diff options
Diffstat (limited to 'fs')
660 files changed, 28478 insertions, 18166 deletions
diff --git a/fs/9p/acl.c b/fs/9p/acl.c index 633da5e37299..ae7e7cf7523a 100644 --- a/fs/9p/acl.c +++ b/fs/9p/acl.c @@ -167,17 +167,11 @@ int v9fs_iop_set_acl(struct mnt_idmap *idmap, struct dentry *dentry, if (retval) goto err_out; - size = posix_acl_xattr_size(acl->a_count); - - value = kzalloc(size, GFP_NOFS); + value = posix_acl_to_xattr(&init_user_ns, acl, &size, GFP_NOFS); if (!value) { retval = -ENOMEM; goto err_out; } - - retval = posix_acl_to_xattr(&init_user_ns, acl, value, size); - if (retval < 0) - goto err_out; } /* @@ -257,13 +251,10 @@ static int v9fs_set_acl(struct p9_fid *fid, int type, struct posix_acl *acl) return 0; /* Set a setxattr request to server */ - size = posix_acl_xattr_size(acl->a_count); - buffer = kmalloc(size, GFP_KERNEL); + buffer = posix_acl_to_xattr(&init_user_ns, acl, &size, GFP_KERNEL); if (!buffer) return -ENOMEM; - retval = posix_acl_to_xattr(&init_user_ns, acl, buffer, size); - if (retval < 0) - goto err_free_out; + switch (type) { case ACL_TYPE_ACCESS: name = XATTR_NAME_POSIX_ACL_ACCESS; @@ -275,7 +266,6 @@ static int v9fs_set_acl(struct p9_fid *fid, int type, struct posix_acl *acl) BUG(); } retval = v9fs_fid_xattr_set(fid, name, buffer, size, 0); -err_free_out: kfree(buffer); return retval; } diff --git a/fs/9p/vfs_file.c b/fs/9p/vfs_file.c index 6f3880208587..c5e73c37baea 100644 --- a/fs/9p/vfs_file.c +++ b/fs/9p/vfs_file.c @@ -517,7 +517,6 @@ const struct file_operations v9fs_file_operations = { .splice_read = v9fs_file_splice_read, .splice_write = iter_file_splice_write, .fsync = v9fs_file_fsync, - .setlease = simple_nosetlease, }; const struct file_operations v9fs_file_operations_dotl = { @@ -532,5 +531,4 @@ const struct file_operations v9fs_file_operations_dotl = { .splice_read = v9fs_file_splice_read, .splice_write = iter_file_splice_write, .fsync = v9fs_file_fsync_dotl, - .setlease = simple_nosetlease, }; diff --git a/fs/Makefile b/fs/Makefile index a04274a3c854..cf4a745e9679 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -16,7 +16,7 @@ obj-y := open.o read_write.o file_table.o super.o \ stack.o fs_struct.o statfs.o fs_pin.o nsfs.o \ fs_dirent.o fs_context.o fs_parser.o fsopen.o init.o \ kernel_read_file.o mnt_idmapping.o remap_range.o pidfs.o \ - file_attr.o + file_attr.o fserror.o nullfs.o obj-$(CONFIG_BUFFER_HEAD) += buffer.o mpage.o obj-$(CONFIG_PROC_FS) += proc_namespace.o diff --git a/fs/adfs/dir.c b/fs/adfs/dir.c index 77fbd196008f..4f9dc276da6f 100644 --- a/fs/adfs/dir.c +++ b/fs/adfs/dir.c @@ -6,6 +6,7 @@ * * Common directory handling for ADFS */ +#include <linux/hex.h> #include <linux/slab.h> #include "adfs.h" diff --git a/fs/affs/dir.c b/fs/affs/dir.c index bd40d5f08810..fe18caaf4d65 100644 --- a/fs/affs/dir.c +++ b/fs/affs/dir.c @@ -15,6 +15,7 @@ */ #include <linux/iversion.h> +#include <linux/filelock.h> #include "affs.h" struct affs_dir_data { @@ -55,6 +56,7 @@ const struct file_operations affs_dir_operations = { .iterate_shared = affs_readdir, .fsync = affs_file_fsync, .release = affs_dir_release, + .setlease = generic_setlease, }; /* diff --git a/fs/affs/file.c b/fs/affs/file.c index 765c3443663e..6c9258359ddb 100644 --- a/fs/affs/file.c +++ b/fs/affs/file.c @@ -15,6 +15,7 @@ #include <linux/uio.h> #include <linux/blkdev.h> +#include <linux/filelock.h> #include <linux/mpage.h> #include "affs.h" @@ -1008,6 +1009,7 @@ const struct file_operations affs_file_operations = { .release = affs_file_release, .fsync = affs_file_fsync, .splice_read = filemap_splice_read, + .setlease = generic_setlease, }; const struct inode_operations affs_file_inode_operations = { diff --git a/fs/attr.c b/fs/attr.c index b9ec6b47bab2..e7d7c6d19fe9 100644 --- a/fs/attr.c +++ b/fs/attr.c @@ -169,7 +169,17 @@ int setattr_prepare(struct mnt_idmap *idmap, struct dentry *dentry, * ATTR_FORCE. */ if (ia_valid & ATTR_SIZE) { - int error = inode_newsize_ok(inode, attr->ia_size); + int error; + + /* + * Verity files are immutable, so deny truncates. This isn't + * covered by the open-time check because sys_truncate() takes a + * path, not an open file. + */ + if (IS_ENABLED(CONFIG_FS_VERITY) && IS_VERITY(inode)) + return -EPERM; + + error = inode_newsize_ok(inode, attr->ia_size); if (error) return error; } diff --git a/fs/bad_inode.c b/fs/bad_inode.c index 0ef9bcb744dd..acf8613f5e36 100644 --- a/fs/bad_inode.c +++ b/fs/bad_inode.c @@ -133,7 +133,8 @@ static int bad_inode_fiemap(struct inode *inode, return -EIO; } -static int bad_inode_update_time(struct inode *inode, int flags) +static int bad_inode_update_time(struct inode *inode, enum fs_update_time type, + unsigned int flags) { return -EIO; } diff --git a/fs/befs/linuxvfs.c b/fs/befs/linuxvfs.c index 9fcfdd6b8189..d7c5d9270387 100644 --- a/fs/befs/linuxvfs.c +++ b/fs/befs/linuxvfs.c @@ -14,6 +14,7 @@ #include <linux/fs_context.h> #include <linux/fs_parser.h> #include <linux/errno.h> +#include <linux/filelock.h> #include <linux/stat.h> #include <linux/nls.h> #include <linux/buffer_head.h> @@ -79,6 +80,7 @@ static const struct file_operations befs_dir_operations = { .read = generic_read_dir, .iterate_shared = befs_readdir, .llseek = generic_file_llseek, + .setlease = generic_setlease, }; static const struct inode_operations befs_dir_inode_operations = { diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c index 8cb1a94339b8..2b772613a74c 100644 --- a/fs/binfmt_misc.c +++ b/fs/binfmt_misc.c @@ -12,6 +12,7 @@ #include <linux/kernel.h> #include <linux/module.h> +#include <linux/hex.h> #include <linux/init.h> #include <linux/sched/mm.h> #include <linux/magic.h> diff --git a/fs/bpf_fs_kfuncs.c b/fs/bpf_fs_kfuncs.c index 5ace2511fec5..e4e51a1d0de2 100644 --- a/fs/bpf_fs_kfuncs.c +++ b/fs/bpf_fs_kfuncs.c @@ -68,10 +68,7 @@ __bpf_kfunc void bpf_put_file(struct file *file) * * Resolve the pathname for the supplied *path* and store it in *buf*. This BPF * kfunc is the safer variant of the legacy bpf_d_path() helper and should be - * used in place of bpf_d_path() whenever possible. It enforces KF_TRUSTED_ARGS - * semantics, meaning that the supplied *path* must itself hold a valid - * reference, or else the BPF program will be outright rejected by the BPF - * verifier. + * used in place of bpf_d_path() whenever possible. * * This BPF kfunc may only be called from BPF LSM programs. * @@ -359,14 +356,13 @@ __bpf_kfunc int bpf_cgroup_read_xattr(struct cgroup *cgroup, const char *name__s __bpf_kfunc_end_defs(); BTF_KFUNCS_START(bpf_fs_kfunc_set_ids) -BTF_ID_FLAGS(func, bpf_get_task_exe_file, - KF_ACQUIRE | KF_TRUSTED_ARGS | KF_RET_NULL) +BTF_ID_FLAGS(func, bpf_get_task_exe_file, KF_ACQUIRE | KF_RET_NULL) BTF_ID_FLAGS(func, bpf_put_file, KF_RELEASE) -BTF_ID_FLAGS(func, bpf_path_d_path, KF_TRUSTED_ARGS) -BTF_ID_FLAGS(func, bpf_get_dentry_xattr, KF_SLEEPABLE | KF_TRUSTED_ARGS) -BTF_ID_FLAGS(func, bpf_get_file_xattr, KF_SLEEPABLE | KF_TRUSTED_ARGS) -BTF_ID_FLAGS(func, bpf_set_dentry_xattr, KF_SLEEPABLE | KF_TRUSTED_ARGS) -BTF_ID_FLAGS(func, bpf_remove_dentry_xattr, KF_SLEEPABLE | KF_TRUSTED_ARGS) +BTF_ID_FLAGS(func, bpf_path_d_path) +BTF_ID_FLAGS(func, bpf_get_dentry_xattr, KF_SLEEPABLE) +BTF_ID_FLAGS(func, bpf_get_file_xattr, KF_SLEEPABLE) +BTF_ID_FLAGS(func, bpf_set_dentry_xattr, KF_SLEEPABLE) +BTF_ID_FLAGS(func, bpf_remove_dentry_xattr, KF_SLEEPABLE) BTF_KFUNCS_END(bpf_fs_kfunc_set_ids) static int bpf_fs_kfuncs_filter(const struct bpf_prog *prog, u32 kfunc_id) @@ -377,9 +373,8 @@ static int bpf_fs_kfuncs_filter(const struct bpf_prog *prog, u32 kfunc_id) return -EACCES; } -/* bpf_[set|remove]_dentry_xattr.* hooks have KF_TRUSTED_ARGS and - * KF_SLEEPABLE, so they are only available to sleepable hooks with - * dentry arguments. +/* bpf_[set|remove]_dentry_xattr.* hooks have KF_SLEEPABLE, so they are only + * available to sleepable hooks with dentry arguments. * * Setting and removing xattr requires exclusive lock on dentry->d_inode. * Some hooks already locked d_inode, while some hooks have not locked diff --git a/fs/btrfs/Kconfig b/fs/btrfs/Kconfig index 6d6fc85835d4..ede184b6eda1 100644 --- a/fs/btrfs/Kconfig +++ b/fs/btrfs/Kconfig @@ -4,11 +4,8 @@ config BTRFS_FS tristate "Btrfs filesystem support" select BLK_CGROUP_PUNT_BIO select CRC32 - select CRYPTO - select CRYPTO_CRC32C - select CRYPTO_XXHASH - select CRYPTO_SHA256 - select CRYPTO_BLAKE2B + select CRYPTO_LIB_BLAKE2B + select CRYPTO_LIB_SHA256 select ZLIB_INFLATE select ZLIB_DEFLATE select LZO_COMPRESS @@ -18,6 +15,7 @@ config BTRFS_FS select FS_IOMAP select RAID6_PQ select XOR_BLOCKS + select XXHASH depends on PAGE_SIZE_LESS_THAN_256KB help @@ -106,9 +104,6 @@ config BTRFS_EXPERIMENTAL - send stream protocol v3 - fs-verity support - - checksum offload mode - sysfs knob to affect when checksums are - calculated (at IO time, or in a thread) - - raid-stripe-tree - additional mapping of extents to devices to support RAID1* profiles on zoned devices, RAID56 not yet supported @@ -121,4 +116,6 @@ config BTRFS_EXPERIMENTAL - asynchronous checksum generation for data writes + - remap-tree - logical address remapping tree + If unsure, say N. diff --git a/fs/btrfs/Makefile b/fs/btrfs/Makefile index 743d7677b175..31b00b932588 100644 --- a/fs/btrfs/Makefile +++ b/fs/btrfs/Makefile @@ -17,7 +17,6 @@ subdir-ccflags-y += $(condflags) # The following turn off the warnings enabled by -Wextra subdir-ccflags-y += -Wno-missing-field-initializers subdir-ccflags-y += -Wno-sign-compare -subdir-ccflags-y += -Wno-type-limits subdir-ccflags-y += -Wno-shift-negative-value obj-$(CONFIG_BTRFS_FS) := btrfs.o @@ -44,4 +43,5 @@ btrfs-$(CONFIG_BTRFS_FS_RUN_SANITY_TESTS) += tests/free-space-tests.o \ tests/extent-buffer-tests.o tests/btrfs-tests.o \ tests/extent-io-tests.o tests/inode-tests.o tests/qgroup-tests.o \ tests/free-space-tree-tests.o tests/extent-map-tests.o \ - tests/raid-stripe-tree-tests.o tests/delayed-refs-tests.o + tests/raid-stripe-tree-tests.o tests/delayed-refs-tests.o \ + tests/chunk-allocation-tests.o diff --git a/fs/btrfs/accessors.h b/fs/btrfs/accessors.h index 78721412951c..8938357fcb40 100644 --- a/fs/btrfs/accessors.h +++ b/fs/btrfs/accessors.h @@ -240,6 +240,26 @@ BTRFS_SETGET_FUNCS(block_group_flags, struct btrfs_block_group_item, flags, 64); BTRFS_SETGET_STACK_FUNCS(stack_block_group_flags, struct btrfs_block_group_item, flags, 64); +/* struct btrfs_block_group_item_v2 */ +BTRFS_SETGET_STACK_FUNCS(stack_block_group_v2_used, struct btrfs_block_group_item_v2, + used, 64); +BTRFS_SETGET_FUNCS(block_group_v2_used, struct btrfs_block_group_item_v2, used, 64); +BTRFS_SETGET_STACK_FUNCS(stack_block_group_v2_chunk_objectid, + struct btrfs_block_group_item_v2, chunk_objectid, 64); +BTRFS_SETGET_FUNCS(block_group_v2_chunk_objectid, + struct btrfs_block_group_item_v2, chunk_objectid, 64); +BTRFS_SETGET_STACK_FUNCS(stack_block_group_v2_flags, + struct btrfs_block_group_item_v2, flags, 64); +BTRFS_SETGET_FUNCS(block_group_v2_flags, struct btrfs_block_group_item_v2, flags, 64); +BTRFS_SETGET_STACK_FUNCS(stack_block_group_v2_remap_bytes, + struct btrfs_block_group_item_v2, remap_bytes, 64); +BTRFS_SETGET_FUNCS(block_group_v2_remap_bytes, struct btrfs_block_group_item_v2, + remap_bytes, 64); +BTRFS_SETGET_STACK_FUNCS(stack_block_group_v2_identity_remap_count, + struct btrfs_block_group_item_v2, identity_remap_count, 32); +BTRFS_SETGET_FUNCS(block_group_v2_identity_remap_count, struct btrfs_block_group_item_v2, + identity_remap_count, 32); + /* struct btrfs_free_space_info */ BTRFS_SETGET_FUNCS(free_space_extent_count, struct btrfs_free_space_info, extent_count, 32); @@ -863,6 +883,12 @@ BTRFS_SETGET_STACK_FUNCS(super_uuid_tree_generation, struct btrfs_super_block, uuid_tree_generation, 64); BTRFS_SETGET_STACK_FUNCS(super_nr_global_roots, struct btrfs_super_block, nr_global_roots, 64); +BTRFS_SETGET_STACK_FUNCS(super_remap_root, struct btrfs_super_block, + remap_root, 64); +BTRFS_SETGET_STACK_FUNCS(super_remap_root_generation, struct btrfs_super_block, + remap_root_generation, 64); +BTRFS_SETGET_STACK_FUNCS(super_remap_root_level, struct btrfs_super_block, + remap_root_level, 8); /* struct btrfs_file_extent_item */ BTRFS_SETGET_STACK_FUNCS(stack_file_extent_type, struct btrfs_file_extent_item, @@ -1010,6 +1036,10 @@ BTRFS_SETGET_STACK_FUNCS(stack_verity_descriptor_encryption, BTRFS_SETGET_STACK_FUNCS(stack_verity_descriptor_size, struct btrfs_verity_descriptor_item, size, 64); +BTRFS_SETGET_FUNCS(remap_address, struct btrfs_remap_item, address, 64); +BTRFS_SETGET_STACK_FUNCS(stack_remap_address, struct btrfs_remap_item, + address, 64); + /* Cast into the data area of the leaf. */ #define btrfs_item_ptr(leaf, slot, type) \ ((type *)(btrfs_item_nr_offset(leaf, 0) + btrfs_item_offset(leaf, slot))) diff --git a/fs/btrfs/acl.c b/fs/btrfs/acl.c index c336e2ab7f8a..e55b686fe1ab 100644 --- a/fs/btrfs/acl.c +++ b/fs/btrfs/acl.c @@ -57,7 +57,8 @@ struct posix_acl *btrfs_get_acl(struct inode *inode, int type, bool rcu) int __btrfs_set_acl(struct btrfs_trans_handle *trans, struct inode *inode, struct posix_acl *acl, int type) { - int ret, size = 0; + int ret; + size_t size = 0; const char *name; char AUTO_KFREE(value); @@ -77,20 +78,15 @@ int __btrfs_set_acl(struct btrfs_trans_handle *trans, struct inode *inode, if (acl) { unsigned int nofs_flag; - size = posix_acl_xattr_size(acl->a_count); /* * We're holding a transaction handle, so use a NOFS memory * allocation context to avoid deadlock if reclaim happens. */ nofs_flag = memalloc_nofs_save(); - value = kmalloc(size, GFP_KERNEL); + value = posix_acl_to_xattr(&init_user_ns, acl, &size, GFP_KERNEL); memalloc_nofs_restore(nofs_flag); if (!value) return -ENOMEM; - - ret = posix_acl_to_xattr(&init_user_ns, acl, value, size); - if (ret < 0) - return ret; } if (trans) diff --git a/fs/btrfs/backref.c b/fs/btrfs/backref.c index 78da47a3d00e..9bb406f7dd30 100644 --- a/fs/btrfs/backref.c +++ b/fs/btrfs/backref.c @@ -3609,10 +3609,8 @@ int btrfs_backref_finish_upper_links(struct btrfs_backref_cache *cache, } rb_node = rb_simple_insert(&cache->rb_root, &upper->simple_node); - if (unlikely(rb_node)) { + if (unlikely(rb_node)) btrfs_backref_panic(cache->fs_info, upper->bytenr, -EEXIST); - return -EUCLEAN; - } list_add_tail(&edge->list[UPPER], &upper->lower); diff --git a/fs/btrfs/bio.c b/fs/btrfs/bio.c index fa1d321a2fb8..0a69e09bfe28 100644 --- a/fs/btrfs/bio.c +++ b/fs/btrfs/bio.c @@ -97,7 +97,13 @@ static struct btrfs_bio *btrfs_split_bio(struct btrfs_fs_info *fs_info, bbio->orig_logical = orig_bbio->orig_logical; orig_bbio->orig_logical += map_length; } + bbio->csum_search_commit_root = orig_bbio->csum_search_commit_root; + bbio->can_use_append = orig_bbio->can_use_append; + bbio->is_scrub = orig_bbio->is_scrub; + bbio->is_remap = orig_bbio->is_remap; + bbio->async_csum = orig_bbio->async_csum; + atomic_inc(&orig_bbio->pending_ios); return bbio; } @@ -480,6 +486,8 @@ static void btrfs_clone_write_end_io(struct bio *bio) static void btrfs_submit_dev_bio(struct btrfs_device *dev, struct bio *bio) { + u64 physical = bio->bi_iter.bi_sector << SECTOR_SHIFT; + if (!dev || !dev->bdev || test_bit(BTRFS_DEV_STATE_MISSING, &dev->dev_state) || (btrfs_op(bio) == BTRFS_MAP_WRITE && @@ -494,12 +502,13 @@ static void btrfs_submit_dev_bio(struct btrfs_device *dev, struct bio *bio) * For zone append writing, bi_sector must point the beginning of the * zone */ - if (bio_op(bio) == REQ_OP_ZONE_APPEND) { - u64 physical = bio->bi_iter.bi_sector << SECTOR_SHIFT; + if (btrfs_bio(bio)->can_use_append && btrfs_dev_is_sequential(dev, physical)) { u64 zone_start = round_down(physical, dev->fs_info->zone_size); ASSERT(btrfs_dev_is_sequential(dev, physical)); bio->bi_iter.bi_sector = zone_start >> SECTOR_SHIFT; + bio->bi_opf &= ~REQ_OP_WRITE; + bio->bi_opf |= REQ_OP_ZONE_APPEND; } btrfs_debug(dev->fs_info, "%s: rw %d 0x%x, sector=%llu, dev=%lu (%s id %llu), size=%u", @@ -662,11 +671,6 @@ static bool should_async_write(struct btrfs_bio *bbio) bool auto_csum_mode = true; #ifdef CONFIG_BTRFS_EXPERIMENTAL - struct btrfs_fs_devices *fs_devices = fs_info->fs_devices; - enum btrfs_offload_csum_mode csum_mode = READ_ONCE(fs_devices->offload_csum_mode); - - if (csum_mode == BTRFS_OFFLOAD_CSUM_FORCE_ON) - return true; /* * Write bios will calculate checksum and submit bio at the same time. * Unless explicitly required don't offload serial csum calculate and bio @@ -747,7 +751,6 @@ static bool btrfs_submit_chunk(struct btrfs_bio *bbio, int mirror_num) u64 logical = bio->bi_iter.bi_sector << SECTOR_SHIFT; u64 length = bio->bi_iter.bi_size; u64 map_length = length; - bool use_append = btrfs_use_zone_append(bbio); struct btrfs_io_context *bioc = NULL; struct btrfs_io_stripe smap; blk_status_t status; @@ -775,8 +778,10 @@ static bool btrfs_submit_chunk(struct btrfs_bio *bbio, int mirror_num) if (bio_op(bio) == REQ_OP_WRITE && is_data_bbio(bbio)) bbio->orig_logical = logical; + bbio->can_use_append = btrfs_use_zone_append(bbio); + map_length = min(map_length, length); - if (use_append) + if (bbio->can_use_append) map_length = btrfs_append_map_length(bbio, map_length); if (map_length < length) { @@ -805,11 +810,6 @@ static bool btrfs_submit_chunk(struct btrfs_bio *bbio, int mirror_num) } if (btrfs_op(bio) == BTRFS_MAP_WRITE) { - if (use_append) { - bio->bi_opf &= ~REQ_OP_WRITE; - bio->bi_opf |= REQ_OP_ZONE_APPEND; - } - if (is_data_bbio(bbio) && bioc && bioc->use_rst) { /* * No locking for the list update, as we only add to @@ -827,7 +827,7 @@ static bool btrfs_submit_chunk(struct btrfs_bio *bbio, int mirror_num) */ if (!(inode->flags & BTRFS_INODE_NODATASUM) && !test_bit(BTRFS_FS_STATE_NO_DATA_CSUMS, &fs_info->fs_state) && - !btrfs_is_data_reloc_root(inode->root)) { + !btrfs_is_data_reloc_root(inode->root) && !bbio->is_remap) { if (should_async_write(bbio) && btrfs_wq_submit_bio(bbio, bioc, &smap, mirror_num)) goto done; @@ -836,9 +836,8 @@ static bool btrfs_submit_chunk(struct btrfs_bio *bbio, int mirror_num) status = errno_to_blk_status(ret); if (status) goto fail; - } else if (use_append || - (btrfs_is_zoned(fs_info) && inode && - inode->flags & BTRFS_INODE_NODATASUM)) { + } else if (bbio->can_use_append || + (btrfs_is_zoned(fs_info) && inode->flags & BTRFS_INODE_NODATASUM)) { ret = btrfs_alloc_dummy_sum(bbio); status = errno_to_blk_status(ret); if (status) diff --git a/fs/btrfs/bio.h b/fs/btrfs/bio.h index 1be74209f0b8..303ed6c7103d 100644 --- a/fs/btrfs/bio.h +++ b/fs/btrfs/bio.h @@ -68,29 +68,36 @@ struct btrfs_bio { struct btrfs_tree_parent_check parent_check; }; + /* For internal use in read end I/O handling */ + struct work_struct end_io_work; + /* End I/O information supplied to btrfs_bio_alloc */ btrfs_bio_end_io_t end_io; void *private; - /* For internal use in read end I/O handling */ - unsigned int mirror_num; atomic_t pending_ios; - struct work_struct end_io_work; + u16 mirror_num; /* Save the first error status of split bio. */ blk_status_t status; /* Use the commit root to look up csums (data read bio only). */ - bool csum_search_commit_root; + bool csum_search_commit_root:1; /* * Since scrub will reuse btree inode, we need this flag to distinguish * scrub bios. */ - bool is_scrub; + bool is_scrub:1; + + /* Whether the bio is coming from copy_remapped_data_io(). */ + bool is_remap:1; /* Whether the csum generation for data write is async. */ - bool async_csum; + bool async_csum:1; + + /* Whether the bio is written using zone append. */ + bool can_use_append:1; /* * This member must come last, bio_alloc_bioset will allocate enough diff --git a/fs/btrfs/block-group.c b/fs/btrfs/block-group.c index 08b14449fabe..3186ed4fd26d 100644 --- a/fs/btrfs/block-group.c +++ b/fs/btrfs/block-group.c @@ -239,7 +239,7 @@ static struct btrfs_block_group *block_group_cache_tree_search( while (n) { cache = rb_entry(n, struct btrfs_block_group, cache_node); - end = cache->start + cache->length - 1; + end = btrfs_block_group_end(cache) - 1; start = cache->start; if (bytenr < start) { @@ -292,7 +292,7 @@ struct btrfs_block_group *btrfs_next_block_group( /* If our block group was removed, we need a full search. */ if (RB_EMPTY_NODE(&cache->cache_node)) { - const u64 next_bytenr = cache->start + cache->length; + const u64 next_bytenr = btrfs_block_group_end(cache); read_unlock(&fs_info->block_group_cache_lock); btrfs_put_block_group(cache); @@ -575,28 +575,28 @@ int btrfs_add_new_free_space(struct btrfs_block_group *block_group, u64 start, /* * Get an arbitrary extent item index / max_index through the block group * - * @block_group the block group to sample from + * @caching_ctl the caching control containing the block group to sample from * @index: the integral step through the block group to grab from * @max_index: the granularity of the sampling * @key: return value parameter for the item we find + * @path: path to use for searching in the extent tree * * Pre-conditions on indices: * 0 <= index <= max_index * 0 < max_index * - * Returns: 0 on success, 1 if the search didn't yield a useful item, negative - * error code on error. + * Returns: 0 on success, 1 if the search didn't yield a useful item. */ static int sample_block_group_extent_item(struct btrfs_caching_control *caching_ctl, - struct btrfs_block_group *block_group, int index, int max_index, - struct btrfs_key *found_key) + struct btrfs_key *found_key, + struct btrfs_path *path) { + struct btrfs_block_group *block_group = caching_ctl->block_group; struct btrfs_fs_info *fs_info = block_group->fs_info; struct btrfs_root *extent_root; u64 search_offset; - u64 search_end = block_group->start + block_group->length; - BTRFS_PATH_AUTO_FREE(path); + const u64 search_end = btrfs_block_group_end(block_group); struct btrfs_key search_key; int ret = 0; @@ -606,16 +606,13 @@ static int sample_block_group_extent_item(struct btrfs_caching_control *caching_ lockdep_assert_held(&caching_ctl->mutex); lockdep_assert_held_read(&fs_info->commit_root_sem); - path = btrfs_alloc_path(); - if (!path) - return -ENOMEM; - - extent_root = btrfs_extent_root(fs_info, max_t(u64, block_group->start, - BTRFS_SUPER_INFO_OFFSET)); - - path->skip_locking = true; - path->search_commit_root = true; - path->reada = READA_FORWARD; + extent_root = btrfs_extent_root(fs_info, block_group->start); + if (unlikely(!extent_root)) { + btrfs_err(fs_info, + "missing extent root for block group at offset %llu", + block_group->start); + return -EUCLEAN; + } search_offset = index * div_u64(block_group->length, max_index); search_key.objectid = block_group->start + search_offset; @@ -673,27 +670,42 @@ static int sample_block_group_extent_item(struct btrfs_caching_control *caching_ * 3, we can either read every file extent, or admit that this is best effort * anyway and try to stay fast. * - * Returns: 0 on success, negative error code on error. + * No errors are returned since failing to determine the size class is not a + * critical error, size classes are just an optimization. */ -static int load_block_group_size_class(struct btrfs_caching_control *caching_ctl, - struct btrfs_block_group *block_group) +static void load_block_group_size_class(struct btrfs_caching_control *caching_ctl) { + BTRFS_PATH_AUTO_RELEASE(path); + struct btrfs_block_group *block_group = caching_ctl->block_group; struct btrfs_fs_info *fs_info = block_group->fs_info; struct btrfs_key key; int i; u64 min_size = block_group->length; enum btrfs_block_group_size_class size_class = BTRFS_BG_SZ_NONE; - int ret; + + /* + * Since we run in workqueue context, we allocate the path on stack to + * avoid memory allocation failure, as the stack in a work queue task + * is not deep. + */ + ASSERT(current_work() == &caching_ctl->work.normal_work); if (!btrfs_block_group_should_use_size_class(block_group)) - return 0; + return; + + path.skip_locking = true; + path.search_commit_root = true; + path.reada = READA_FORWARD; lockdep_assert_held(&caching_ctl->mutex); lockdep_assert_held_read(&fs_info->commit_root_sem); for (i = 0; i < 5; ++i) { - ret = sample_block_group_extent_item(caching_ctl, block_group, i, 5, &key); + int ret; + + ret = sample_block_group_extent_item(caching_ctl, i, 5, &key, &path); if (ret < 0) - goto out; + return; + btrfs_release_path(&path); if (ret > 0) continue; min_size = min_t(u64, min_size, key.offset); @@ -704,13 +716,12 @@ static int load_block_group_size_class(struct btrfs_caching_control *caching_ctl block_group->size_class = size_class; spin_unlock(&block_group->lock); } -out: - return ret; } static int load_extent_tree_free(struct btrfs_caching_control *caching_ctl) { struct btrfs_block_group *block_group = caching_ctl->block_group; + const u64 block_group_end = btrfs_block_group_end(block_group); struct btrfs_fs_info *fs_info = block_group->fs_info; struct btrfs_root *extent_root; BTRFS_PATH_AUTO_FREE(path); @@ -755,13 +766,13 @@ static int load_extent_tree_free(struct btrfs_caching_control *caching_ctl) next: ret = btrfs_search_slot(NULL, extent_root, &key, path, 0, 0); if (ret < 0) - goto out; + return ret; leaf = path->nodes[0]; nritems = btrfs_header_nritems(leaf); while (1) { - if (btrfs_fs_closing(fs_info) > 1) { + if (btrfs_fs_closing_done(fs_info)) { last = (u64)-1; break; } @@ -786,7 +797,7 @@ next: ret = btrfs_next_leaf(extent_root, path); if (ret < 0) - goto out; + return ret; if (ret) break; leaf = path->nodes[0]; @@ -807,7 +818,7 @@ next: continue; } - if (key.objectid >= block_group->start + block_group->length) + if (key.objectid >= block_group_end) break; if (key.type == BTRFS_EXTENT_ITEM_KEY || @@ -817,7 +828,7 @@ next: ret = btrfs_add_new_free_space(block_group, last, key.objectid, &space_added); if (ret) - goto out; + return ret; total_found += space_added; if (key.type == BTRFS_METADATA_ITEM_KEY) last = key.objectid + @@ -836,17 +847,13 @@ next: path->slots[0]++; } - ret = btrfs_add_new_free_space(block_group, last, - block_group->start + block_group->length, - NULL); -out: - return ret; + return btrfs_add_new_free_space(block_group, last, block_group_end, NULL); } static inline void btrfs_free_excluded_extents(const struct btrfs_block_group *bg) { btrfs_clear_extent_bit(&bg->fs_info->excluded_extents, bg->start, - bg->start + bg->length - 1, EXTENT_DIRTY, NULL); + btrfs_block_group_end(bg) - 1, EXTENT_DIRTY, NULL); } static noinline void caching_thread(struct btrfs_work *work) @@ -863,7 +870,7 @@ static noinline void caching_thread(struct btrfs_work *work) mutex_lock(&caching_ctl->mutex); down_read(&fs_info->commit_root_sem); - load_block_group_size_class(caching_ctl, block_group); + load_block_group_size_class(caching_ctl); if (btrfs_test_opt(fs_info, SPACE_CACHE)) { ret = load_free_space_cache(block_group); if (ret == 1) { @@ -933,6 +940,13 @@ int btrfs_cache_block_group(struct btrfs_block_group *cache, bool wait) if (btrfs_is_zoned(fs_info)) return 0; + /* + * No allocations can be done from remapped block groups, so they have + * no entries in the free-space tree. + */ + if (cache->flags & BTRFS_BLOCK_GROUP_REMAPPED) + return 0; + caching_ctl = kzalloc(sizeof(*caching_ctl), GFP_NOFS); if (!caching_ctl) return -ENOMEM; @@ -1057,8 +1071,25 @@ static int remove_block_group_item(struct btrfs_trans_handle *trans, if (ret < 0) return ret; - ret = btrfs_del_item(trans, root, path); - return ret; + return btrfs_del_item(trans, root, path); +} + +void btrfs_remove_bg_from_sinfo(struct btrfs_block_group *bg) +{ + int factor = btrfs_bg_type_to_factor(bg->flags); + + spin_lock(&bg->space_info->lock); + if (btrfs_test_opt(bg->fs_info, ENOSPC_DEBUG)) { + WARN_ON(bg->space_info->total_bytes < bg->length); + WARN_ON(bg->space_info->bytes_readonly < bg->length - bg->zone_unusable); + WARN_ON(bg->space_info->bytes_zone_unusable < bg->zone_unusable); + WARN_ON(bg->space_info->disk_total < bg->length * factor); + } + bg->space_info->total_bytes -= bg->length; + bg->space_info->bytes_readonly -= (bg->length - bg->zone_unusable); + btrfs_space_info_update_bytes_zone_unusable(bg->space_info, -bg->zone_unusable); + bg->space_info->disk_total -= bg->length * factor; + spin_unlock(&bg->space_info->lock); } int btrfs_remove_block_group(struct btrfs_trans_handle *trans, @@ -1072,16 +1103,22 @@ int btrfs_remove_block_group(struct btrfs_trans_handle *trans, struct kobject *kobj = NULL; int ret; int index; - int factor; struct btrfs_caching_control *caching_ctl = NULL; bool remove_map; bool remove_rsv = false; block_group = btrfs_lookup_block_group(fs_info, map->start); - if (!block_group) + if (unlikely(!block_group)) { + btrfs_abort_transaction(trans, -ENOENT); return -ENOENT; + } - BUG_ON(!block_group->ro); + if (unlikely(!block_group->ro && + !(block_group->flags & BTRFS_BLOCK_GROUP_REMAPPED))) { + ret = -EUCLEAN; + btrfs_abort_transaction(trans, ret); + goto out; + } trace_btrfs_remove_block_group(block_group); /* @@ -1093,7 +1130,6 @@ int btrfs_remove_block_group(struct btrfs_trans_handle *trans, block_group->length); index = btrfs_bg_flags_to_raid_index(block_group->flags); - factor = btrfs_bg_type_to_factor(block_group->flags); /* make sure this block group isn't part of an allocation cluster */ cluster = &fs_info->data_alloc_cluster; @@ -1114,8 +1150,9 @@ int btrfs_remove_block_group(struct btrfs_trans_handle *trans, btrfs_clear_data_reloc_bg(block_group); path = btrfs_alloc_path(); - if (!path) { + if (unlikely(!path)) { ret = -ENOMEM; + btrfs_abort_transaction(trans, ret); goto out; } @@ -1151,8 +1188,10 @@ int btrfs_remove_block_group(struct btrfs_trans_handle *trans, mutex_unlock(&trans->transaction->cache_write_mutex); ret = btrfs_remove_free_space_inode(trans, inode, block_group); - if (ret) + if (unlikely(ret)) { + btrfs_abort_transaction(trans, ret); goto out; + } write_lock(&fs_info->block_group_cache_lock); rb_erase_cached(&block_group->cache_node, @@ -1217,26 +1256,11 @@ int btrfs_remove_block_group(struct btrfs_trans_handle *trans, spin_lock(&block_group->space_info->lock); list_del_init(&block_group->ro_list); - - if (btrfs_test_opt(fs_info, ENOSPC_DEBUG)) { - WARN_ON(block_group->space_info->total_bytes - < block_group->length); - WARN_ON(block_group->space_info->bytes_readonly - < block_group->length - block_group->zone_unusable); - WARN_ON(block_group->space_info->bytes_zone_unusable - < block_group->zone_unusable); - WARN_ON(block_group->space_info->disk_total - < block_group->length * factor); - } - block_group->space_info->total_bytes -= block_group->length; - block_group->space_info->bytes_readonly -= - (block_group->length - block_group->zone_unusable); - btrfs_space_info_update_bytes_zone_unusable(block_group->space_info, - -block_group->zone_unusable); - block_group->space_info->disk_total -= block_group->length * factor; - spin_unlock(&block_group->space_info->lock); + if (!(block_group->flags & BTRFS_BLOCK_GROUP_REMAPPED)) + btrfs_remove_bg_from_sinfo(block_group); + /* * Remove the free space for the block group from the free space tree * and the block group's item from the extent tree before marking the @@ -1247,14 +1271,24 @@ int btrfs_remove_block_group(struct btrfs_trans_handle *trans, * deletes the block group item from the extent tree, allowing for * another task to attempt to create another block group with the same * item key (and failing with -EEXIST and a transaction abort). + * + * If the REMAPPED flag has been set the block group's free space + * has already been removed, so we can skip the call to + * btrfs_remove_block_group_free_space(). */ - ret = btrfs_remove_block_group_free_space(trans, block_group); - if (ret) - goto out; + if (!(block_group->flags & BTRFS_BLOCK_GROUP_REMAPPED)) { + ret = btrfs_remove_block_group_free_space(trans, block_group); + if (unlikely(ret)) { + btrfs_abort_transaction(trans, ret); + goto out; + } + } ret = remove_block_group_item(trans, path, block_group); - if (ret < 0) + if (unlikely(ret < 0)) { + btrfs_abort_transaction(trans, ret); goto out; + } spin_lock(&block_group->lock); /* @@ -1377,8 +1411,7 @@ static int inc_block_group_ro(struct btrfs_block_group *cache, bool force) goto out; } - num_bytes = cache->length - cache->reserved - cache->pinned - - cache->bytes_super - cache->zone_unusable - cache->used; + num_bytes = btrfs_block_group_available_space(cache); /* * Data never overcommits, even in mixed mode, so do just the straight @@ -1564,8 +1597,10 @@ void btrfs_delete_unused_bgs(struct btrfs_fs_info *fs_info) spin_lock(&space_info->lock); spin_lock(&block_group->lock); - if (btrfs_is_block_group_used(block_group) || block_group->ro || - list_is_singular(&block_group->list)) { + if (btrfs_is_block_group_used(block_group) || + (block_group->ro && !(block_group->flags & BTRFS_BLOCK_GROUP_REMAPPED)) || + list_is_singular(&block_group->list) || + test_bit(BLOCK_GROUP_FLAG_FULLY_REMAPPED, &block_group->runtime_flags)) { /* * We want to bail if we made new allocations or have * outstanding allocations in this block group. We do @@ -1606,9 +1641,10 @@ void btrfs_delete_unused_bgs(struct btrfs_fs_info *fs_info) * needing to allocate extents from the block group. */ used = btrfs_space_info_used(space_info, true); - if ((space_info->total_bytes - block_group->length < used && - block_group->zone_unusable < block_group->length) || - has_unwritten_metadata(block_group)) { + if (((space_info->total_bytes - block_group->length < used && + block_group->zone_unusable < block_group->length) || + has_unwritten_metadata(block_group)) && + !(block_group->flags & BTRFS_BLOCK_GROUP_REMAPPED)) { /* * Add a reference for the list, compensate for the ref * drop under the "next" label for the @@ -1773,6 +1809,9 @@ void btrfs_mark_bg_unused(struct btrfs_block_group *bg) btrfs_get_block_group(bg); trace_btrfs_add_unused_block_group(bg); list_add_tail(&bg->bg_list, &fs_info->unused_bgs); + } else if (bg->flags & BTRFS_BLOCK_GROUP_REMAPPED && + bg->identity_remap_count == 0) { + /* Leave fully remapped block groups on the fully_remapped_bgs list. */ } else if (!test_bit(BLOCK_GROUP_FLAG_NEW, &bg->runtime_flags)) { /* Pull out the block group from the reclaim_bgs list. */ trace_btrfs_add_unused_block_group(bg); @@ -1805,6 +1844,12 @@ static int reclaim_bgs_cmp(void *unused, const struct list_head *a, static inline bool btrfs_should_reclaim(const struct btrfs_fs_info *fs_info) { + if (!test_bit(BTRFS_FS_OPEN, &fs_info->flags)) + return false; + + if (btrfs_fs_closing(fs_info)) + return false; + if (btrfs_is_zoned(fs_info)) return btrfs_zoned_should_reclaim(fs_info); return true; @@ -1839,12 +1884,6 @@ void btrfs_reclaim_bgs_work(struct work_struct *work) struct btrfs_space_info *space_info; LIST_HEAD(retry_list); - if (!test_bit(BTRFS_FS_OPEN, &fs_info->flags)) - return; - - if (btrfs_fs_closing(fs_info)) - return; - if (!btrfs_should_reclaim(fs_info)) return; @@ -1872,6 +1911,7 @@ void btrfs_reclaim_bgs_work(struct work_struct *work) while (!list_empty(&fs_info->reclaim_bgs)) { u64 used; u64 reserved; + u64 old_total; int ret = 0; bg = list_first_entry(&fs_info->reclaim_bgs, @@ -1937,6 +1977,7 @@ void btrfs_reclaim_bgs_work(struct work_struct *work) } spin_unlock(&bg->lock); + old_total = space_info->total_bytes; spin_unlock(&space_info->lock); /* @@ -1989,14 +2030,14 @@ void btrfs_reclaim_bgs_work(struct work_struct *work) reserved = 0; spin_lock(&space_info->lock); space_info->reclaim_errors++; - if (READ_ONCE(space_info->periodic_reclaim)) - space_info->periodic_reclaim_ready = false; spin_unlock(&space_info->lock); } spin_lock(&space_info->lock); space_info->reclaim_count++; space_info->reclaim_bytes += used; space_info->reclaim_bytes += reserved; + if (space_info->total_bytes < old_total) + btrfs_set_periodic_reclaim_ready(space_info, true); spin_unlock(&space_info->lock); next: @@ -2249,7 +2290,7 @@ static int exclude_super_stripes(struct btrfs_block_group *cache) while (nr--) { u64 len = min_t(u64, stripe_len, - cache->start + cache->length - logical[nr]); + btrfs_block_group_end(cache) - logical[nr]); cache->bytes_super += len; ret = btrfs_set_extent_bit(&fs_info->excluded_extents, @@ -2266,7 +2307,7 @@ static int exclude_super_stripes(struct btrfs_block_group *cache) return 0; } -static struct btrfs_block_group *btrfs_create_block_group_cache( +static struct btrfs_block_group *btrfs_create_block_group( struct btrfs_fs_info *fs_info, u64 start) { struct btrfs_block_group *cache; @@ -2360,7 +2401,7 @@ static int check_chunk_block_group_mappings(struct btrfs_fs_info *fs_info) } static int read_one_block_group(struct btrfs_fs_info *info, - struct btrfs_block_group_item *bgi, + struct btrfs_block_group_item_v2 *bgi, const struct btrfs_key *key, int need_clear) { @@ -2370,16 +2411,21 @@ static int read_one_block_group(struct btrfs_fs_info *info, ASSERT(key->type == BTRFS_BLOCK_GROUP_ITEM_KEY); - cache = btrfs_create_block_group_cache(info, key->objectid); + cache = btrfs_create_block_group(info, key->objectid); if (!cache) return -ENOMEM; cache->length = key->offset; - cache->used = btrfs_stack_block_group_used(bgi); - cache->commit_used = cache->used; - cache->flags = btrfs_stack_block_group_flags(bgi); - cache->global_root_id = btrfs_stack_block_group_chunk_objectid(bgi); + cache->used = btrfs_stack_block_group_v2_used(bgi); + cache->last_used = cache->used; + cache->flags = btrfs_stack_block_group_v2_flags(bgi); + cache->last_flags = cache->flags; + cache->global_root_id = btrfs_stack_block_group_v2_chunk_objectid(bgi); cache->space_info = btrfs_find_space_info(info, cache->flags); + cache->remap_bytes = btrfs_stack_block_group_v2_remap_bytes(bgi); + cache->last_remap_bytes = cache->remap_bytes; + cache->identity_remap_count = btrfs_stack_block_group_v2_identity_remap_count(bgi); + cache->last_identity_remap_count = cache->identity_remap_count; btrfs_set_free_space_tree_thresholds(cache); @@ -2444,10 +2490,10 @@ static int read_one_block_group(struct btrfs_fs_info *info, } else if (cache->length == cache->used) { cache->cached = BTRFS_CACHE_FINISHED; btrfs_free_excluded_extents(cache); - } else if (cache->used == 0) { + } else if (cache->used == 0 && cache->remap_bytes == 0) { cache->cached = BTRFS_CACHE_FINISHED; ret = btrfs_add_new_free_space(cache, cache->start, - cache->start + cache->length, NULL); + btrfs_block_group_end(cache), NULL); btrfs_free_excluded_extents(cache); if (ret) goto error; @@ -2464,7 +2510,7 @@ static int read_one_block_group(struct btrfs_fs_info *info, set_avail_alloc_bits(info, cache->flags); if (btrfs_chunk_writeable(info, cache->start)) { - if (cache->used == 0) { + if (cache->used == 0 && cache->remap_bytes == 0) { ASSERT(list_empty(&cache->bg_list)); if (btrfs_test_opt(info, DISCARD_ASYNC)) btrfs_discard_queue_work(&info->discard_ctl, cache); @@ -2491,7 +2537,7 @@ static int fill_dummy_bgs(struct btrfs_fs_info *fs_info) struct btrfs_block_group *bg; map = rb_entry(node, struct btrfs_chunk_map, rb_node); - bg = btrfs_create_block_group_cache(fs_info, map->start); + bg = btrfs_create_block_group(fs_info, map->start); if (!bg) { ret = -ENOMEM; break; @@ -2568,9 +2614,10 @@ int btrfs_read_block_groups(struct btrfs_fs_info *info) need_clear = 1; while (1) { - struct btrfs_block_group_item bgi; + struct btrfs_block_group_item_v2 bgi; struct extent_buffer *leaf; int slot; + size_t size; ret = find_first_block_group(info, path, &key); if (ret > 0) @@ -2581,8 +2628,16 @@ int btrfs_read_block_groups(struct btrfs_fs_info *info) leaf = path->nodes[0]; slot = path->slots[0]; + if (btrfs_fs_incompat(info, REMAP_TREE)) { + size = sizeof(struct btrfs_block_group_item_v2); + } else { + size = sizeof(struct btrfs_block_group_item); + btrfs_set_stack_block_group_v2_remap_bytes(&bgi, 0); + btrfs_set_stack_block_group_v2_identity_remap_count(&bgi, 0); + } + read_extent_buffer(leaf, &bgi, btrfs_item_ptr_offset(leaf, slot), - sizeof(bgi)); + size); btrfs_item_key_to_cpu(leaf, &key, slot); btrfs_release_path(path); @@ -2652,28 +2707,38 @@ static int insert_block_group_item(struct btrfs_trans_handle *trans, struct btrfs_block_group *block_group) { struct btrfs_fs_info *fs_info = trans->fs_info; - struct btrfs_block_group_item bgi; + struct btrfs_block_group_item_v2 bgi; struct btrfs_root *root = btrfs_block_group_root(fs_info); struct btrfs_key key; - u64 old_commit_used; + u64 old_last_used; + size_t size; int ret; spin_lock(&block_group->lock); - btrfs_set_stack_block_group_used(&bgi, block_group->used); - btrfs_set_stack_block_group_chunk_objectid(&bgi, - block_group->global_root_id); - btrfs_set_stack_block_group_flags(&bgi, block_group->flags); - old_commit_used = block_group->commit_used; - block_group->commit_used = block_group->used; + 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); + btrfs_set_stack_block_group_v2_flags(&bgi, block_group->flags); + btrfs_set_stack_block_group_v2_remap_bytes(&bgi, block_group->remap_bytes); + btrfs_set_stack_block_group_v2_identity_remap_count(&bgi, block_group->identity_remap_count); + old_last_used = block_group->last_used; + block_group->last_used = block_group->used; + block_group->last_remap_bytes = block_group->remap_bytes; + block_group->last_identity_remap_count = block_group->identity_remap_count; + block_group->last_flags = block_group->flags; key.objectid = block_group->start; key.type = BTRFS_BLOCK_GROUP_ITEM_KEY; key.offset = block_group->length; spin_unlock(&block_group->lock); - ret = btrfs_insert_item(trans, root, &key, &bgi, sizeof(bgi)); + if (btrfs_fs_incompat(fs_info, REMAP_TREE)) + size = sizeof(struct btrfs_block_group_item_v2); + else + size = sizeof(struct btrfs_block_group_item); + + ret = btrfs_insert_item(trans, root, &key, &bgi, size); if (ret < 0) { spin_lock(&block_group->lock); - block_group->commit_used = old_commit_used; + block_group->last_used = old_last_used; spin_unlock(&block_group->lock); } @@ -2886,7 +2951,7 @@ struct btrfs_block_group *btrfs_make_block_group(struct btrfs_trans_handle *tran btrfs_set_log_full_commit(trans); - cache = btrfs_create_block_group_cache(fs_info, chunk_offset); + cache = btrfs_create_block_group(fs_info, chunk_offset); if (!cache) return ERR_PTR(-ENOMEM); @@ -3090,7 +3155,6 @@ unlock_out: void btrfs_dec_block_group_ro(struct btrfs_block_group *cache) { struct btrfs_space_info *sinfo = cache->space_info; - u64 num_bytes; BUG_ON(!cache->ro); @@ -3106,10 +3170,7 @@ void btrfs_dec_block_group_ro(struct btrfs_block_group *cache) btrfs_space_info_update_bytes_zone_unusable(sinfo, cache->zone_unusable); sinfo->bytes_readonly -= cache->zone_unusable; } - num_bytes = cache->length - cache->reserved - - cache->pinned - cache->bytes_super - - cache->zone_unusable - cache->used; - sinfo->bytes_readonly -= num_bytes; + sinfo->bytes_readonly -= btrfs_block_group_available_space(cache); list_del_init(&cache->ro_list); } spin_unlock(&cache->lock); @@ -3125,10 +3186,12 @@ static int update_block_group_item(struct btrfs_trans_handle *trans, struct btrfs_root *root = btrfs_block_group_root(fs_info); unsigned long bi; struct extent_buffer *leaf; - struct btrfs_block_group_item bgi; + struct btrfs_block_group_item_v2 bgi; struct btrfs_key key; - u64 old_commit_used; - u64 used; + u64 old_last_used, old_last_remap_bytes; + u32 old_last_identity_remap_count; + u64 used, remap_bytes; + u32 identity_remap_count; /* * Block group items update can be triggered out of commit transaction @@ -3137,14 +3200,24 @@ static int update_block_group_item(struct btrfs_trans_handle *trans, * may be changed. */ spin_lock(&cache->lock); - old_commit_used = cache->commit_used; + old_last_used = cache->last_used; + old_last_remap_bytes = cache->last_remap_bytes; + old_last_identity_remap_count = cache->last_identity_remap_count; used = cache->used; - /* No change in used bytes, can safely skip it. */ - if (cache->commit_used == used) { + remap_bytes = cache->remap_bytes; + identity_remap_count = cache->identity_remap_count; + /* No change in values, can safely skip it. */ + if (cache->last_used == used && + cache->last_remap_bytes == remap_bytes && + cache->last_identity_remap_count == identity_remap_count && + cache->last_flags == cache->flags) { spin_unlock(&cache->lock); return 0; } - cache->commit_used = used; + cache->last_used = used; + cache->last_remap_bytes = remap_bytes; + cache->last_identity_remap_count = identity_remap_count; + cache->last_flags = cache->flags; spin_unlock(&cache->lock); key.objectid = cache->start; @@ -3160,25 +3233,37 @@ static int update_block_group_item(struct btrfs_trans_handle *trans, leaf = path->nodes[0]; bi = btrfs_item_ptr_offset(leaf, path->slots[0]); - btrfs_set_stack_block_group_used(&bgi, used); - btrfs_set_stack_block_group_chunk_objectid(&bgi, - cache->global_root_id); - btrfs_set_stack_block_group_flags(&bgi, cache->flags); - write_extent_buffer(leaf, &bgi, bi, sizeof(bgi)); + btrfs_set_stack_block_group_v2_used(&bgi, used); + btrfs_set_stack_block_group_v2_chunk_objectid(&bgi, cache->global_root_id); + btrfs_set_stack_block_group_v2_flags(&bgi, cache->flags); + + if (btrfs_fs_incompat(fs_info, REMAP_TREE)) { + btrfs_set_stack_block_group_v2_remap_bytes(&bgi, cache->remap_bytes); + btrfs_set_stack_block_group_v2_identity_remap_count(&bgi, + cache->identity_remap_count); + write_extent_buffer(leaf, &bgi, bi, + sizeof(struct btrfs_block_group_item_v2)); + } else { + write_extent_buffer(leaf, &bgi, bi, + sizeof(struct btrfs_block_group_item)); + } + fail: btrfs_release_path(path); /* - * We didn't update the block group item, need to revert commit_used + * We didn't update the block group item, need to revert last_used * unless the block group item didn't exist yet - this is to prevent a * race with a concurrent insertion of the block group item, with * insert_block_group_item(), that happened just after we attempted to - * update. In that case we would reset commit_used to 0 just after the + * update. In that case we would reset last_used to 0 just after the * insertion set it to a value greater than 0 - if the block group later * becomes with 0 used bytes, we would incorrectly skip its update. */ if (ret < 0 && ret != -ENOENT) { spin_lock(&cache->lock); - cache->commit_used = old_commit_used; + cache->last_used = old_last_used; + cache->last_remap_bytes = old_last_remap_bytes; + cache->last_identity_remap_count = old_last_identity_remap_count; spin_unlock(&cache->lock); } return ret; @@ -3701,7 +3786,7 @@ int btrfs_update_block_group(struct btrfs_trans_handle *trans, return -ENOENT; /* An extent can not span multiple block groups. */ - ASSERT(bytenr + num_bytes <= cache->start + cache->length); + ASSERT(bytenr + num_bytes <= btrfs_block_group_end(cache)); space_info = cache->space_info; factor = btrfs_bg_type_to_factor(cache->flags); @@ -4530,6 +4615,13 @@ int btrfs_free_block_groups(struct btrfs_fs_info *info) list_del_init(&block_group->bg_list); btrfs_put_block_group(block_group); } + + while (!list_empty(&info->fully_remapped_bgs)) { + block_group = list_first_entry(&info->fully_remapped_bgs, + struct btrfs_block_group, bg_list); + list_del_init(&block_group->bg_list); + btrfs_put_block_group(block_group); + } spin_unlock(&info->unused_bgs_lock); spin_lock(&info->zone_active_bgs_lock); @@ -4680,6 +4772,7 @@ int btrfs_use_block_group_size_class(struct btrfs_block_group *bg, enum btrfs_block_group_size_class size_class, bool force_wrong_size_class) { + lockdep_assert_held(&bg->lock); ASSERT(size_class != BTRFS_BG_SZ_NONE); /* The new allocation is in the right size class, do nothing */ @@ -4717,3 +4810,103 @@ bool btrfs_block_group_should_use_size_class(const struct btrfs_block_group *bg) return false; return true; } + +void btrfs_mark_bg_fully_remapped(struct btrfs_block_group *bg, + struct btrfs_trans_handle *trans) +{ + struct btrfs_fs_info *fs_info = trans->fs_info; + + + if (btrfs_test_opt(fs_info, DISCARD_ASYNC)) { + spin_lock(&bg->lock); + set_bit(BLOCK_GROUP_FLAG_STRIPE_REMOVAL_PENDING, &bg->runtime_flags); + spin_unlock(&bg->lock); + + btrfs_discard_queue_work(&fs_info->discard_ctl, bg); + } else { + spin_lock(&fs_info->unused_bgs_lock); + /* + * The block group might already be on the unused_bgs list, + * remove it if it is. It'll get readded after + * btrfs_handle_fully_remapped_bgs() finishes. + */ + if (!list_empty(&bg->bg_list)) + list_del(&bg->bg_list); + else + btrfs_get_block_group(bg); + + list_add_tail(&bg->bg_list, &fs_info->fully_remapped_bgs); + spin_unlock(&fs_info->unused_bgs_lock); + } +} + +/* + * Compare the block group and chunk trees, and find any fully-remapped block + * groups which haven't yet had their chunk stripes and device extents removed, + * and put them on the fully_remapped_bgs list so this gets done. + * + * This happens when a block group becomes fully remapped, i.e. its last + * identity mapping is removed, and the volume is unmounted before async + * discard has finished. It's important this gets done as until it is the + * chunk's stripes are dead space. + */ +int btrfs_populate_fully_remapped_bgs_list(struct btrfs_fs_info *fs_info) +{ + struct rb_node *node_bg, *node_chunk; + + node_bg = rb_first_cached(&fs_info->block_group_cache_tree); + node_chunk = rb_first_cached(&fs_info->mapping_tree); + + while (node_bg && node_chunk) { + struct btrfs_block_group *bg; + struct btrfs_chunk_map *map; + + bg = rb_entry(node_bg, struct btrfs_block_group, cache_node); + map = rb_entry(node_chunk, struct btrfs_chunk_map, rb_node); + + ASSERT(bg->start == map->start); + + if (!(bg->flags & BTRFS_BLOCK_GROUP_REMAPPED)) + goto next; + + if (bg->identity_remap_count != 0) + goto next; + + if (map->num_stripes == 0) + goto next; + + spin_lock(&fs_info->unused_bgs_lock); + + if (list_empty(&bg->bg_list)) { + btrfs_get_block_group(bg); + list_add_tail(&bg->bg_list, &fs_info->fully_remapped_bgs); + } else { + list_move_tail(&bg->bg_list, &fs_info->fully_remapped_bgs); + } + + spin_unlock(&fs_info->unused_bgs_lock); + + /* + * Ideally we'd want to call btrfs_discard_queue_work() here, + * but it'd do nothing as the discard worker hasn't been + * started yet. + * + * The block group will get added to the discard list when + * btrfs_handle_fully_remapped_bgs() gets called, when we + * commit the first transaction. + */ + if (btrfs_test_opt(fs_info, DISCARD_ASYNC)) { + spin_lock(&bg->lock); + set_bit(BLOCK_GROUP_FLAG_STRIPE_REMOVAL_PENDING, &bg->runtime_flags); + spin_unlock(&bg->lock); + } + +next: + node_bg = rb_next(node_bg); + node_chunk = rb_next(node_chunk); + } + + ASSERT(!node_bg && !node_chunk); + + return 0; +} diff --git a/fs/btrfs/block-group.h b/fs/btrfs/block-group.h index 5f933455118c..c03e04292900 100644 --- a/fs/btrfs/block-group.h +++ b/fs/btrfs/block-group.h @@ -49,6 +49,7 @@ enum btrfs_discard_state { BTRFS_DISCARD_EXTENTS, BTRFS_DISCARD_BITMAPS, BTRFS_DISCARD_RESET_CURSOR, + BTRFS_DISCARD_FULLY_REMAPPED, }; /* @@ -92,6 +93,8 @@ enum btrfs_block_group_flags { * transaction. */ BLOCK_GROUP_FLAG_NEW, + BLOCK_GROUP_FLAG_FULLY_REMAPPED, + BLOCK_GROUP_FLAG_STRIPE_REMOVAL_PENDING, }; enum btrfs_caching_type { @@ -129,13 +132,22 @@ struct btrfs_block_group { u64 flags; u64 cache_generation; u64 global_root_id; + u64 remap_bytes; + u32 identity_remap_count; /* * The last committed used bytes of this block group, if the above @used - * is still the same as @commit_used, we don't need to update block + * is still the same as @last_used, we don't need to update block * group item of this block group. */ - u64 commit_used; + u64 last_used; + /* The last committed remap_bytes value of this block group. */ + u64 last_remap_bytes; + /* The last commited identity_remap_count value of this block group. */ + u32 last_identity_remap_count; + /* The last committed flags value for this block group. */ + u64 last_flags; + /* * If the free space extent count exceeds this number, convert the block * group to bitmaps. @@ -282,7 +294,8 @@ static inline bool btrfs_is_block_group_used(const struct btrfs_block_group *bg) { lockdep_assert_held(&bg->lock); - return (bg->used > 0 || bg->reserved > 0 || bg->pinned > 0); + return (bg->used > 0 || bg->reserved > 0 || bg->pinned > 0 || + bg->remap_bytes > 0); } static inline bool btrfs_is_block_group_data_only(const struct btrfs_block_group *block_group) @@ -295,6 +308,14 @@ static inline bool btrfs_is_block_group_data_only(const struct btrfs_block_group !(block_group->flags & BTRFS_BLOCK_GROUP_METADATA); } +static inline u64 btrfs_block_group_available_space(const struct btrfs_block_group *bg) +{ + lockdep_assert_held(&bg->lock); + + return (bg->length - bg->used - bg->pinned - bg->reserved - + bg->bytes_super - bg->zone_unusable); +} + #ifdef CONFIG_BTRFS_DEBUG int btrfs_should_fragment_free_space(const struct btrfs_block_group *block_group); #endif @@ -324,6 +345,7 @@ int btrfs_add_new_free_space(struct btrfs_block_group *block_group, struct btrfs_trans_handle *btrfs_start_trans_remove_block_group( struct btrfs_fs_info *fs_info, const u64 chunk_offset); +void btrfs_remove_bg_from_sinfo(struct btrfs_block_group *bg); int btrfs_remove_block_group(struct btrfs_trans_handle *trans, struct btrfs_chunk_map *map); void btrfs_delete_unused_bgs(struct btrfs_fs_info *fs_info); @@ -395,5 +417,8 @@ int btrfs_use_block_group_size_class(struct btrfs_block_group *bg, enum btrfs_block_group_size_class size_class, bool force_wrong_size_class); bool btrfs_block_group_should_use_size_class(const struct btrfs_block_group *bg); +void btrfs_mark_bg_fully_remapped(struct btrfs_block_group *bg, + struct btrfs_trans_handle *trans); +int btrfs_populate_fully_remapped_bgs_list(struct btrfs_fs_info *fs_info); #endif /* BTRFS_BLOCK_GROUP_H */ diff --git a/fs/btrfs/block-rsv.c b/fs/btrfs/block-rsv.c index 96cf7a162987..e823230c09b7 100644 --- a/fs/btrfs/block-rsv.c +++ b/fs/btrfs/block-rsv.c @@ -419,6 +419,9 @@ void btrfs_init_root_block_rsv(struct btrfs_root *root) case BTRFS_TREE_LOG_OBJECTID: root->block_rsv = &fs_info->treelog_rsv; break; + case BTRFS_REMAP_TREE_OBJECTID: + root->block_rsv = &fs_info->remap_block_rsv; + break; default: root->block_rsv = NULL; break; @@ -432,6 +435,9 @@ void btrfs_init_global_block_rsv(struct btrfs_fs_info *fs_info) space_info = btrfs_find_space_info(fs_info, BTRFS_BLOCK_GROUP_SYSTEM); fs_info->chunk_block_rsv.space_info = space_info; + space_info = btrfs_find_space_info(fs_info, BTRFS_BLOCK_GROUP_METADATA_REMAP); + fs_info->remap_block_rsv.space_info = space_info; + space_info = btrfs_find_space_info(fs_info, BTRFS_BLOCK_GROUP_METADATA); fs_info->global_block_rsv.space_info = space_info; fs_info->trans_block_rsv.space_info = space_info; @@ -458,6 +464,8 @@ void btrfs_release_global_block_rsv(struct btrfs_fs_info *fs_info) WARN_ON(fs_info->trans_block_rsv.reserved > 0); WARN_ON(fs_info->chunk_block_rsv.size > 0); WARN_ON(fs_info->chunk_block_rsv.reserved > 0); + WARN_ON(fs_info->remap_block_rsv.size > 0); + WARN_ON(fs_info->remap_block_rsv.reserved > 0); WARN_ON(fs_info->delayed_block_rsv.size > 0); WARN_ON(fs_info->delayed_block_rsv.reserved > 0); WARN_ON(fs_info->delayed_refs_rsv.reserved > 0); diff --git a/fs/btrfs/block-rsv.h b/fs/btrfs/block-rsv.h index 79ae9d05cd91..8359fb96bc3c 100644 --- a/fs/btrfs/block-rsv.h +++ b/fs/btrfs/block-rsv.h @@ -22,6 +22,7 @@ enum btrfs_rsv_type { BTRFS_BLOCK_RSV_DELALLOC, BTRFS_BLOCK_RSV_TRANS, BTRFS_BLOCK_RSV_CHUNK, + BTRFS_BLOCK_RSV_REMAP, BTRFS_BLOCK_RSV_DELOPS, BTRFS_BLOCK_RSV_DELREFS, BTRFS_BLOCK_RSV_TREELOG, diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h index 73602ee8de3f..55c272fe5d92 100644 --- a/fs/btrfs/btrfs_inode.h +++ b/fs/btrfs/btrfs_inode.h @@ -339,10 +339,6 @@ struct btrfs_inode { struct rw_semaphore i_mmap_lock; -#ifdef CONFIG_FS_VERITY - struct fsverity_info *i_verity_info; -#endif - struct inode vfs_inode; }; diff --git a/fs/btrfs/compression.c b/fs/btrfs/compression.c index 6b3357287b42..1e7174ad32e2 100644 --- a/fs/btrfs/compression.c +++ b/fs/btrfs/compression.c @@ -21,7 +21,6 @@ #include <linux/sched/mm.h> #include <linux/log2.h> #include <linux/shrinker.h> -#include <crypto/hash.h> #include "misc.h" #include "ctree.h" #include "fs.h" @@ -87,37 +86,6 @@ bool btrfs_compress_is_valid_type(const char *str, size_t len) return false; } -static int compression_compress_pages(int type, struct list_head *ws, - struct btrfs_inode *inode, u64 start, - struct folio **folios, unsigned long *out_folios, - unsigned long *total_in, unsigned long *total_out) -{ - switch (type) { - case BTRFS_COMPRESS_ZLIB: - return zlib_compress_folios(ws, inode, start, folios, - out_folios, total_in, total_out); - case BTRFS_COMPRESS_LZO: - return lzo_compress_folios(ws, inode, start, folios, - out_folios, total_in, total_out); - case BTRFS_COMPRESS_ZSTD: - return zstd_compress_folios(ws, inode, start, folios, - out_folios, total_in, total_out); - case BTRFS_COMPRESS_NONE: - default: - /* - * This can happen when compression races with remount setting - * it to 'no compress', while caller doesn't call - * inode_need_compress() to check if we really need to - * compress. - * - * Not a big deal, just need to inform caller that we - * haven't allocated any pages yet. - */ - *out_folios = 0; - return -E2BIG; - } -} - static int compression_decompress_bio(struct list_head *ws, struct compressed_bio *cb) { @@ -156,13 +124,6 @@ static int compression_decompress(int type, struct list_head *ws, } } -static void btrfs_free_compressed_folios(struct compressed_bio *cb) -{ - for (unsigned int i = 0; i < cb->nr_folios; i++) - btrfs_free_compr_folio(cb->compressed_folios[i]); - kfree(cb->compressed_folios); -} - static int btrfs_decompress_bio(struct compressed_bio *cb); /* @@ -271,12 +232,14 @@ static void end_bbio_compressed_read(struct btrfs_bio *bbio) { struct compressed_bio *cb = to_compressed_bio(bbio); blk_status_t status = bbio->bio.bi_status; + struct folio_iter fi; if (!status) status = errno_to_blk_status(btrfs_decompress_bio(cb)); - btrfs_free_compressed_folios(cb); btrfs_bio_end_io(cb->orig_bbio, status); + bio_for_each_folio_all(fi, &bbio->bio) + btrfs_free_compr_folio(fi.folio); bio_put(&bbio->bio); } @@ -327,6 +290,7 @@ static noinline void end_compressed_writeback(const struct compressed_bio *cb) static void end_bbio_compressed_write(struct btrfs_bio *bbio) { struct compressed_bio *cb = to_compressed_bio(bbio); + struct folio_iter fi; btrfs_finish_ordered_extent(cb->bbio.ordered, NULL, cb->start, cb->len, cb->bbio.bio.bi_status == BLK_STS_OK); @@ -334,29 +298,11 @@ static void end_bbio_compressed_write(struct btrfs_bio *bbio) if (cb->writeback) end_compressed_writeback(cb); /* Note, our inode could be gone now. */ - btrfs_free_compressed_folios(cb); + bio_for_each_folio_all(fi, &bbio->bio) + btrfs_free_compr_folio(fi.folio); bio_put(&cb->bbio.bio); } -static void btrfs_add_compressed_bio_folios(struct compressed_bio *cb) -{ - struct bio *bio = &cb->bbio.bio; - u32 offset = 0; - unsigned int findex = 0; - - while (offset < cb->compressed_len) { - struct folio *folio = cb->compressed_folios[findex]; - u32 len = min_t(u32, cb->compressed_len - offset, folio_size(folio)); - int ret; - - /* Maximum compressed extent is smaller than bio size limit. */ - ret = bio_add_folio(bio, folio, len, 0); - ASSERT(ret); - offset += len; - findex++; - } -} - /* * worker function to build and submit bios for previously compressed pages. * The corresponding pages in the inode should be marked for writeback @@ -367,35 +313,44 @@ static void btrfs_add_compressed_bio_folios(struct compressed_bio *cb) * the end io hooks. */ void btrfs_submit_compressed_write(struct btrfs_ordered_extent *ordered, - struct folio **compressed_folios, - unsigned int nr_folios, - blk_opf_t write_flags, - bool writeback) + struct compressed_bio *cb) { struct btrfs_inode *inode = ordered->inode; struct btrfs_fs_info *fs_info = inode->root->fs_info; - struct compressed_bio *cb; ASSERT(IS_ALIGNED(ordered->file_offset, fs_info->sectorsize)); ASSERT(IS_ALIGNED(ordered->num_bytes, fs_info->sectorsize)); + ASSERT(cb->writeback); - cb = alloc_compressed_bio(inode, ordered->file_offset, - REQ_OP_WRITE | write_flags, - end_bbio_compressed_write); cb->start = ordered->file_offset; cb->len = ordered->num_bytes; - cb->compressed_folios = compressed_folios; cb->compressed_len = ordered->disk_num_bytes; - cb->writeback = writeback; - cb->nr_folios = nr_folios; cb->bbio.bio.bi_iter.bi_sector = ordered->disk_bytenr >> SECTOR_SHIFT; cb->bbio.ordered = ordered; - btrfs_add_compressed_bio_folios(cb); btrfs_submit_bbio(&cb->bbio, 0); } /* + * Allocate a compressed write bio for @inode file offset @start length @len. + * + * The caller still needs to properly queue all folios and populate involved + * members. + */ +struct compressed_bio *btrfs_alloc_compressed_write(struct btrfs_inode *inode, + u64 start, u64 len) +{ + struct compressed_bio *cb; + + cb = alloc_compressed_bio(inode, start, REQ_OP_WRITE, end_bbio_compressed_write); + cb->start = start; + cb->len = len; + cb->writeback = true; + + return cb; +} + +/* * Add extra pages in the same compressed file extent so that we don't need to * re-read the same extent again and again. * @@ -520,7 +475,7 @@ static noinline int add_ra_bio_pages(struct inode *inode, folio_put(folio); break; } - add_size = min(em->start + em->len, page_end + 1) - cur; + add_size = min(btrfs_extent_map_end(em), page_end + 1) - cur; btrfs_free_extent_map(em); btrfs_unlock_extent(tree, cur, page_end, NULL); @@ -571,13 +526,13 @@ void btrfs_submit_compressed_read(struct btrfs_bio *bbio) struct extent_map_tree *em_tree = &inode->extent_tree; struct compressed_bio *cb; unsigned int compressed_len; + const u32 min_folio_size = btrfs_min_folio_size(fs_info); u64 file_offset = bbio->file_offset; u64 em_len; u64 em_start; struct extent_map *em; unsigned long pflags; int memstall = 0; - blk_status_t status; int ret; /* we need the actual starting offset of this extent in the file */ @@ -585,7 +540,7 @@ void btrfs_submit_compressed_read(struct btrfs_bio *bbio) em = btrfs_lookup_extent_mapping(em_tree, file_offset, fs_info->sectorsize); read_unlock(&em_tree->lock); if (!em) { - status = BLK_STS_IOERR; + ret = -EIO; goto out; } @@ -607,27 +562,30 @@ void btrfs_submit_compressed_read(struct btrfs_bio *bbio) btrfs_free_extent_map(em); - cb->nr_folios = DIV_ROUND_UP(compressed_len, btrfs_min_folio_size(fs_info)); - cb->compressed_folios = kcalloc(cb->nr_folios, sizeof(struct folio *), GFP_NOFS); - if (!cb->compressed_folios) { - status = BLK_STS_RESOURCE; - goto out_free_bio; - } + for (int i = 0; i * min_folio_size < compressed_len; i++) { + struct folio *folio; + u32 cur_len = min(compressed_len - i * min_folio_size, min_folio_size); + + folio = btrfs_alloc_compr_folio(fs_info); + if (!folio) { + ret = -ENOMEM; + goto out_free_bio; + } - ret = btrfs_alloc_folio_array(cb->nr_folios, fs_info->block_min_order, - cb->compressed_folios); - if (ret) { - status = BLK_STS_RESOURCE; - goto out_free_compressed_pages; + ret = bio_add_folio(&cb->bbio.bio, folio, cur_len, 0); + if (unlikely(!ret)) { + folio_put(folio); + ret = -EINVAL; + goto out_free_bio; + } } + ASSERT(cb->bbio.bio.bi_iter.bi_size == compressed_len); add_ra_bio_pages(&inode->vfs_inode, em_start + em_len, cb, &memstall, &pflags); - /* include any pages we added in add_ra-bio_pages */ cb->len = bbio->bio.bi_iter.bi_size; cb->bbio.bio.bi_iter.bi_sector = bbio->bio.bi_iter.bi_sector; - btrfs_add_compressed_bio_folios(cb); if (memstall) psi_memstall_leave(&pflags); @@ -635,12 +593,10 @@ void btrfs_submit_compressed_read(struct btrfs_bio *bbio) btrfs_submit_bbio(&cb->bbio, 0); return; -out_free_compressed_pages: - kfree(cb->compressed_folios); out_free_bio: - bio_put(&cb->bbio.bio); + cleanup_compressed_bio(cb); out: - btrfs_bio_end_io(bbio, status); + btrfs_bio_end_io(bbio, errno_to_blk_status(ret)); } /* @@ -1027,42 +983,71 @@ int btrfs_compress_filemap_get_folio(struct address_space *mapping, u64 start, } /* - * Given an address space and start and length, compress the bytes into @pages - * that are allocated on demand. - * - * @type_level is encoded algorithm and level, where level 0 means whatever - * default the algorithm chooses and is opaque here; - * - compression algo are 0-3 - * - the level are bits 4-7 + * Given an address space and start and length, compress the page cache + * contents into @cb. * - * @out_folios is an in/out parameter, holds maximum number of folios to allocate - * and returns number of actually allocated folios + * @type_level: is encoded algorithm and level, where level 0 means whatever + * default the algorithm chooses and is opaque here; + * - compression algo are 0-3 + * - the level are bits 4-7 * - * @total_in is used to return the number of bytes actually read. It - * may be smaller than the input length if we had to exit early because we - * ran out of room in the folios array or because we cross the - * max_out threshold. + * @cb->bbio.bio.bi_iter.bi_size will indicate the compressed data size. + * The bi_size may not be sectorsize aligned, thus the caller still need + * to do the round up before submission. * - * @total_out is an in/out parameter, must be set to the input length and will - * be also used to return the total number of compressed bytes + * This function will allocate compressed folios with btrfs_alloc_compr_folio(), + * thus callers must make sure the endio function and error handling are using + * btrfs_free_compr_folio() to release those folios. + * This is already done in end_bbio_compressed_write() and cleanup_compressed_bio(). */ -int btrfs_compress_folios(unsigned int type, int level, struct btrfs_inode *inode, - u64 start, struct folio **folios, unsigned long *out_folios, - unsigned long *total_in, unsigned long *total_out) +struct compressed_bio *btrfs_compress_bio(struct btrfs_inode *inode, + u64 start, u32 len, unsigned int type, + int level, blk_opf_t write_flags) { struct btrfs_fs_info *fs_info = inode->root->fs_info; - const unsigned long orig_len = *total_out; struct list_head *workspace; + struct compressed_bio *cb; int ret; + cb = alloc_compressed_bio(inode, start, REQ_OP_WRITE | write_flags, + end_bbio_compressed_write); + cb->start = start; + cb->len = len; + cb->writeback = true; + cb->compress_type = type; + level = btrfs_compress_set_level(type, level); workspace = get_workspace(fs_info, type, level); - ret = compression_compress_pages(type, workspace, inode, start, folios, - out_folios, total_in, total_out); - /* The total read-in bytes should be no larger than the input. */ - ASSERT(*total_in <= orig_len); + switch (type) { + case BTRFS_COMPRESS_ZLIB: + ret = zlib_compress_bio(workspace, cb); + break; + case BTRFS_COMPRESS_LZO: + ret = lzo_compress_bio(workspace, cb); + break; + case BTRFS_COMPRESS_ZSTD: + ret = zstd_compress_bio(workspace, cb); + break; + case BTRFS_COMPRESS_NONE: + default: + /* + * This can happen when compression races with remount setting + * it to 'no compress', while caller doesn't call + * inode_need_compress() to check if we really need to + * compress. + * + * Not a big deal, just need to inform caller that we + * haven't allocated any pages yet. + */ + ret = -E2BIG; + } + put_workspace(fs_info, type, workspace); - return ret; + if (ret < 0) { + cleanup_compressed_bio(cb); + return ERR_PTR(ret); + } + return cb; } static int btrfs_decompress_bio(struct compressed_bio *cb) diff --git a/fs/btrfs/compression.h b/fs/btrfs/compression.h index e0228017e861..65b8bc4bbe0b 100644 --- a/fs/btrfs/compression.h +++ b/fs/btrfs/compression.h @@ -42,12 +42,6 @@ static_assert((BTRFS_MAX_COMPRESSED % PAGE_SIZE) == 0); #define BTRFS_ZLIB_DEFAULT_LEVEL 3 struct compressed_bio { - /* Number of compressed folios in the array. */ - unsigned int nr_folios; - - /* The folios with the compressed data on them. */ - struct folio **compressed_folios; - /* starting offset in the inode for our pages */ u64 start; @@ -91,18 +85,15 @@ int __init btrfs_init_compress(void); void __cold btrfs_exit_compress(void); bool btrfs_compress_level_valid(unsigned int type, int level); -int btrfs_compress_folios(unsigned int type, int level, struct btrfs_inode *inode, - u64 start, struct folio **folios, unsigned long *out_folios, - unsigned long *total_in, unsigned long *total_out); int btrfs_decompress(int type, const u8 *data_in, struct folio *dest_folio, unsigned long dest_pgoff, size_t srclen, size_t destlen); int btrfs_decompress_buf2page(const char *buf, u32 buf_len, struct compressed_bio *cb, u32 decompressed); +struct compressed_bio *btrfs_alloc_compressed_write(struct btrfs_inode *inode, + u64 start, u64 len); void btrfs_submit_compressed_write(struct btrfs_ordered_extent *ordered, - struct folio **compressed_folios, - unsigned int nr_folios, blk_opf_t write_flags, - bool writeback); + struct compressed_bio *cb); void btrfs_submit_compressed_read(struct btrfs_bio *bbio); int btrfs_compress_str2level(unsigned int type, const char *str, int *level_ret); @@ -146,10 +137,21 @@ int btrfs_compress_heuristic(struct btrfs_inode *inode, u64 start, u64 end); int btrfs_compress_filemap_get_folio(struct address_space *mapping, u64 start, struct folio **in_folio_ret); +struct compressed_bio *btrfs_compress_bio(struct btrfs_inode *inode, + u64 start, u32 len, unsigned int type, + int level, blk_opf_t write_flags); + +static inline void cleanup_compressed_bio(struct compressed_bio *cb) +{ + struct bio *bio = &cb->bbio.bio; + struct folio_iter fi; + + bio_for_each_folio_all(fi, bio) + btrfs_free_compr_folio(fi.folio); + bio_put(bio); +} -int zlib_compress_folios(struct list_head *ws, struct btrfs_inode *inode, - u64 start, struct folio **folios, unsigned long *out_folios, - unsigned long *total_in, unsigned long *total_out); +int zlib_compress_bio(struct list_head *ws, struct compressed_bio *cb); int zlib_decompress_bio(struct list_head *ws, struct compressed_bio *cb); int zlib_decompress(struct list_head *ws, const u8 *data_in, struct folio *dest_folio, unsigned long dest_pgoff, size_t srclen, @@ -158,9 +160,7 @@ struct list_head *zlib_alloc_workspace(struct btrfs_fs_info *fs_info, unsigned i void zlib_free_workspace(struct list_head *ws); struct list_head *zlib_get_workspace(struct btrfs_fs_info *fs_info, unsigned int level); -int lzo_compress_folios(struct list_head *ws, struct btrfs_inode *inode, - u64 start, struct folio **folios, unsigned long *out_folios, - unsigned long *total_in, unsigned long *total_out); +int lzo_compress_bio(struct list_head *ws, struct compressed_bio *cb); int lzo_decompress_bio(struct list_head *ws, struct compressed_bio *cb); int lzo_decompress(struct list_head *ws, const u8 *data_in, struct folio *dest_folio, unsigned long dest_pgoff, size_t srclen, @@ -168,9 +168,7 @@ int lzo_decompress(struct list_head *ws, const u8 *data_in, struct list_head *lzo_alloc_workspace(struct btrfs_fs_info *fs_info); void lzo_free_workspace(struct list_head *ws); -int zstd_compress_folios(struct list_head *ws, struct btrfs_inode *inode, - u64 start, struct folio **folios, unsigned long *out_folios, - unsigned long *total_in, unsigned long *total_out); +int zstd_compress_bio(struct list_head *ws, struct compressed_bio *cb); int zstd_decompress_bio(struct list_head *ws, struct compressed_bio *cb); int zstd_decompress(struct list_head *ws, const u8 *data_in, struct folio *dest_folio, unsigned long dest_pgoff, size_t srclen, diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c index a48b4befbee7..7267b2502665 100644 --- a/fs/btrfs/ctree.c +++ b/fs/btrfs/ctree.c @@ -249,6 +249,7 @@ int btrfs_copy_root(struct btrfs_trans_handle *trans, int ret = 0; int level; struct btrfs_disk_key disk_key; + const bool is_reloc_root = (new_root_objectid == BTRFS_TREE_RELOC_OBJECTID); u64 reloc_src_root = 0; WARN_ON(test_bit(BTRFS_ROOT_SHAREABLE, &root->state) && @@ -262,7 +263,7 @@ int btrfs_copy_root(struct btrfs_trans_handle *trans, else btrfs_node_key(buf, &disk_key, 0); - if (new_root_objectid == BTRFS_TREE_RELOC_OBJECTID) + if (is_reloc_root) reloc_src_root = btrfs_header_owner(buf); cow = btrfs_alloc_tree_block(trans, root, 0, new_root_objectid, &disk_key, level, buf->start, 0, @@ -276,7 +277,7 @@ int btrfs_copy_root(struct btrfs_trans_handle *trans, btrfs_set_header_backref_rev(cow, BTRFS_MIXED_BACKREF_REV); btrfs_clear_header_flag(cow, BTRFS_HEADER_FLAG_WRITTEN | BTRFS_HEADER_FLAG_RELOC); - if (new_root_objectid == BTRFS_TREE_RELOC_OBJECTID) + if (is_reloc_root) btrfs_set_header_flag(cow, BTRFS_HEADER_FLAG_RELOC); else btrfs_set_header_owner(cow, new_root_objectid); @@ -291,16 +292,9 @@ int btrfs_copy_root(struct btrfs_trans_handle *trans, return ret; } - if (new_root_objectid == BTRFS_TREE_RELOC_OBJECTID) { - ret = btrfs_inc_ref(trans, root, cow, 1); - if (unlikely(ret)) - btrfs_abort_transaction(trans, ret); - } else { - ret = btrfs_inc_ref(trans, root, cow, 0); - if (unlikely(ret)) - btrfs_abort_transaction(trans, ret); - } - if (ret) { + ret = btrfs_inc_ref(trans, root, cow, is_reloc_root); + if (unlikely(ret)) { + btrfs_abort_transaction(trans, ret); btrfs_tree_unlock(cow); free_extent_buffer(cow); return ret; @@ -362,6 +356,7 @@ static noinline int update_ref_for_cow(struct btrfs_trans_handle *trans, u64 owner; u64 flags; int ret; + const bool is_reloc_root = (btrfs_root_id(root) == BTRFS_TREE_RELOC_OBJECTID); /* * Backrefs update rules: @@ -397,8 +392,7 @@ static noinline int update_ref_for_cow(struct btrfs_trans_handle *trans, } } else { refs = 1; - if (btrfs_root_id(root) == BTRFS_TREE_RELOC_OBJECTID || - btrfs_header_backref_rev(buf) < BTRFS_MIXED_BACKREF_REV) + if (is_reloc_root || btrfs_header_backref_rev(buf) < BTRFS_MIXED_BACKREF_REV) flags = BTRFS_BLOCK_FLAG_FULL_BACKREF; else flags = 0; @@ -417,18 +411,17 @@ static noinline int update_ref_for_cow(struct btrfs_trans_handle *trans, } if (refs > 1) { - if ((owner == btrfs_root_id(root) || - btrfs_root_id(root) == BTRFS_TREE_RELOC_OBJECTID) && + if ((owner == btrfs_root_id(root) || is_reloc_root) && !(flags & BTRFS_BLOCK_FLAG_FULL_BACKREF)) { - ret = btrfs_inc_ref(trans, root, buf, 1); + ret = btrfs_inc_ref(trans, root, buf, true); if (ret) return ret; - if (btrfs_root_id(root) == BTRFS_TREE_RELOC_OBJECTID) { - ret = btrfs_dec_ref(trans, root, buf, 0); + if (is_reloc_root) { + ret = btrfs_dec_ref(trans, root, buf, false); if (ret) return ret; - ret = btrfs_inc_ref(trans, root, cow, 1); + ret = btrfs_inc_ref(trans, root, cow, true); if (ret) return ret; } @@ -437,23 +430,16 @@ static noinline int update_ref_for_cow(struct btrfs_trans_handle *trans, if (ret) return ret; } else { - - if (btrfs_root_id(root) == BTRFS_TREE_RELOC_OBJECTID) - ret = btrfs_inc_ref(trans, root, cow, 1); - else - ret = btrfs_inc_ref(trans, root, cow, 0); + ret = btrfs_inc_ref(trans, root, cow, is_reloc_root); if (ret) return ret; } } else { if (flags & BTRFS_BLOCK_FLAG_FULL_BACKREF) { - if (btrfs_root_id(root) == BTRFS_TREE_RELOC_OBJECTID) - ret = btrfs_inc_ref(trans, root, cow, 1); - else - ret = btrfs_inc_ref(trans, root, cow, 0); + ret = btrfs_inc_ref(trans, root, cow, is_reloc_root); if (ret) return ret; - ret = btrfs_dec_ref(trans, root, buf, 1); + ret = btrfs_dec_ref(trans, root, buf, true); if (ret) return ret; } @@ -4016,8 +4002,7 @@ int btrfs_split_item(struct btrfs_trans_handle *trans, if (ret) return ret; - ret = split_item(trans, path, new_key, split_offset); - return ret; + return split_item(trans, path, new_key, split_offset); } /* diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 692370fc07b2..6de7ad191e04 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -86,6 +86,14 @@ struct btrfs_path { struct btrfs_path *path_name __free(btrfs_free_path) = NULL /* + * This defines an on-stack path that will be auto released when exiting the scope. + * + * It is compatible with any existing manual btrfs_release_path() calls. + */ +#define BTRFS_PATH_AUTO_RELEASE(path_name) \ + struct btrfs_path path_name __free(btrfs_release_path) = { 0 } + +/* * The state of btrfs root */ enum { @@ -601,6 +609,7 @@ void btrfs_release_path(struct btrfs_path *p); struct btrfs_path *btrfs_alloc_path(void); void btrfs_free_path(struct btrfs_path *p); DEFINE_FREE(btrfs_free_path, struct btrfs_path *, btrfs_free_path(_T)) +DEFINE_FREE(btrfs_release_path, struct btrfs_path, btrfs_release_path(&_T)) int btrfs_del_items(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct btrfs_path *path, int slot, int nr); diff --git a/fs/btrfs/defrag.c b/fs/btrfs/defrag.c index b81e224d4a27..ecf05cd64696 100644 --- a/fs/btrfs/defrag.c +++ b/fs/btrfs/defrag.c @@ -609,7 +609,7 @@ static struct extent_map *defrag_get_extent(struct btrfs_inode *inode, { struct btrfs_root *root = inode->root; struct btrfs_file_extent_item *fi; - struct btrfs_path path = { 0 }; + BTRFS_PATH_AUTO_RELEASE(path); struct extent_map *em; struct btrfs_key key; u64 ino = btrfs_ino(inode); @@ -720,16 +720,13 @@ next: if (ret > 0) goto not_found; } - btrfs_release_path(&path); return em; not_found: - btrfs_release_path(&path); btrfs_free_extent_map(em); return NULL; err: - btrfs_release_path(&path); btrfs_free_extent_map(em); return ERR_PTR(ret); } @@ -795,10 +792,11 @@ static bool defrag_check_next_extent(struct inode *inode, struct extent_map *em, { struct btrfs_fs_info *fs_info = inode_to_fs_info(inode); struct extent_map *next; + const u64 em_end = btrfs_extent_map_end(em); bool ret = false; /* This is the last extent */ - if (em->start + em->len >= i_size_read(inode)) + if (em_end >= i_size_read(inode)) return false; /* @@ -807,7 +805,7 @@ static bool defrag_check_next_extent(struct inode *inode, struct extent_map *em, * one will not be a target. * This will just cause extra IO without really reducing the fragments. */ - next = defrag_lookup_extent(inode, em->start + em->len, newer_than, locked); + next = defrag_lookup_extent(inode, em_end, newer_than, locked); /* No more em or hole */ if (!next || next->disk_bytenr >= EXTENT_MAP_LAST_BYTE) goto out; diff --git a/fs/btrfs/delayed-inode.c b/fs/btrfs/delayed-inode.c index 4b7d9015e0da..1739a0b29c49 100644 --- a/fs/btrfs/delayed-inode.c +++ b/fs/btrfs/delayed-inode.c @@ -232,19 +232,19 @@ static void btrfs_dequeue_delayed_node(struct btrfs_delayed_root *root, } static struct btrfs_delayed_node *btrfs_first_delayed_node( - struct btrfs_delayed_root *delayed_root, + struct btrfs_fs_info *fs_info, struct btrfs_ref_tracker *tracker) { struct btrfs_delayed_node *node; - spin_lock(&delayed_root->lock); - node = list_first_entry_or_null(&delayed_root->node_list, + spin_lock(&fs_info->delayed_root.lock); + node = list_first_entry_or_null(&fs_info->delayed_root.node_list, struct btrfs_delayed_node, n_list); if (node) { refcount_inc(&node->refs); btrfs_delayed_node_ref_tracker_alloc(node, tracker, GFP_ATOMIC); } - spin_unlock(&delayed_root->lock); + spin_unlock(&fs_info->delayed_root.lock); return node; } @@ -257,7 +257,7 @@ static struct btrfs_delayed_node *btrfs_next_delayed_node( struct list_head *p; struct btrfs_delayed_node *next = NULL; - delayed_root = node->root->fs_info->delayed_root; + delayed_root = &node->root->fs_info->delayed_root; spin_lock(&delayed_root->lock); if (!test_bit(BTRFS_DELAYED_NODE_IN_LIST, &node->flags)) { /* not in the list */ @@ -287,7 +287,7 @@ static void __btrfs_release_delayed_node( if (!delayed_node) return; - delayed_root = delayed_node->root->fs_info->delayed_root; + delayed_root = &delayed_node->root->fs_info->delayed_root; mutex_lock(&delayed_node->mutex); if (delayed_node->count) @@ -425,7 +425,7 @@ static int __btrfs_add_delayed_item(struct btrfs_delayed_node *delayed_node, delayed_node->index_cnt = ins->index + 1; delayed_node->count++; - atomic_inc(&delayed_node->root->fs_info->delayed_root->items); + atomic_inc(&delayed_node->root->fs_info->delayed_root.items); return 0; } @@ -443,7 +443,6 @@ static void __btrfs_remove_delayed_item(struct btrfs_delayed_item *delayed_item) { struct btrfs_delayed_node *delayed_node = delayed_item->delayed_node; struct rb_root_cached *root; - struct btrfs_delayed_root *delayed_root; /* Not inserted, ignore it. */ if (RB_EMPTY_NODE(&delayed_item->rb_node)) @@ -452,8 +451,6 @@ static void __btrfs_remove_delayed_item(struct btrfs_delayed_item *delayed_item) /* If it's in a rbtree, then we need to have delayed node locked. */ lockdep_assert_held(&delayed_node->mutex); - delayed_root = delayed_node->root->fs_info->delayed_root; - if (delayed_item->type == BTRFS_DELAYED_INSERTION_ITEM) root = &delayed_node->ins_root; else @@ -462,8 +459,7 @@ static void __btrfs_remove_delayed_item(struct btrfs_delayed_item *delayed_item) rb_erase_cached(&delayed_item->rb_node, root); RB_CLEAR_NODE(&delayed_item->rb_node); delayed_node->count--; - - finish_one_item(delayed_root); + finish_one_item(&delayed_node->root->fs_info->delayed_root); } static void btrfs_release_delayed_item(struct btrfs_delayed_item *item) @@ -980,30 +976,21 @@ static int btrfs_delete_delayed_items(struct btrfs_trans_handle *trans, static void btrfs_release_delayed_inode(struct btrfs_delayed_node *delayed_node) { - struct btrfs_delayed_root *delayed_root; - if (delayed_node && test_bit(BTRFS_DELAYED_NODE_INODE_DIRTY, &delayed_node->flags)) { ASSERT(delayed_node->root); clear_bit(BTRFS_DELAYED_NODE_INODE_DIRTY, &delayed_node->flags); delayed_node->count--; - - delayed_root = delayed_node->root->fs_info->delayed_root; - finish_one_item(delayed_root); + finish_one_item(&delayed_node->root->fs_info->delayed_root); } } static void btrfs_release_delayed_iref(struct btrfs_delayed_node *delayed_node) { - if (test_and_clear_bit(BTRFS_DELAYED_NODE_DEL_IREF, &delayed_node->flags)) { - struct btrfs_delayed_root *delayed_root; - ASSERT(delayed_node->root); delayed_node->count--; - - delayed_root = delayed_node->root->fs_info->delayed_root; - finish_one_item(delayed_root); + finish_one_item(&delayed_node->root->fs_info->delayed_root); } } @@ -1137,8 +1124,8 @@ __btrfs_commit_inode_delayed_items(struct btrfs_trans_handle *trans, ret = btrfs_record_root_in_trans(trans, node->root); if (ret) return ret; - ret = btrfs_update_delayed_inode(trans, node->root, path, node); - return ret; + + return btrfs_update_delayed_inode(trans, node->root, path, node); } /* @@ -1150,7 +1137,6 @@ __btrfs_commit_inode_delayed_items(struct btrfs_trans_handle *trans, static int __btrfs_run_delayed_items(struct btrfs_trans_handle *trans, int nr) { struct btrfs_fs_info *fs_info = trans->fs_info; - struct btrfs_delayed_root *delayed_root; struct btrfs_delayed_node *curr_node, *prev_node; struct btrfs_ref_tracker curr_delayed_node_tracker, prev_delayed_node_tracker; struct btrfs_path *path; @@ -1168,9 +1154,7 @@ static int __btrfs_run_delayed_items(struct btrfs_trans_handle *trans, int nr) block_rsv = trans->block_rsv; trans->block_rsv = &fs_info->delayed_block_rsv; - delayed_root = fs_info->delayed_root; - - curr_node = btrfs_first_delayed_node(delayed_root, &curr_delayed_node_tracker); + curr_node = btrfs_first_delayed_node(fs_info, &curr_delayed_node_tracker); while (curr_node && (!count || nr--)) { ret = __btrfs_commit_inode_delayed_items(trans, path, curr_node); @@ -1417,7 +1401,7 @@ void btrfs_assert_delayed_root_empty(struct btrfs_fs_info *fs_info) struct btrfs_ref_tracker delayed_node_tracker; struct btrfs_delayed_node *node; - node = btrfs_first_delayed_node( fs_info->delayed_root, &delayed_node_tracker); + node = btrfs_first_delayed_node(fs_info, &delayed_node_tracker); if (WARN_ON(node)) { btrfs_delayed_node_ref_tracker_free(node, &delayed_node_tracker); @@ -1440,7 +1424,7 @@ static bool could_end_wait(struct btrfs_delayed_root *delayed_root, int seq) void btrfs_balance_delayed_items(struct btrfs_fs_info *fs_info) { - struct btrfs_delayed_root *delayed_root = fs_info->delayed_root; + struct btrfs_delayed_root *delayed_root = &fs_info->delayed_root; if ((atomic_read(&delayed_root->items) < BTRFS_DELAYED_BACKGROUND) || btrfs_workqueue_normal_congested(fs_info->delayed_workers)) @@ -1970,7 +1954,7 @@ int btrfs_delayed_update_inode(struct btrfs_trans_handle *trans, fill_stack_inode_item(trans, &delayed_node->inode_item, inode); set_bit(BTRFS_DELAYED_NODE_INODE_DIRTY, &delayed_node->flags); delayed_node->count++; - atomic_inc(&root->fs_info->delayed_root->items); + atomic_inc(&root->fs_info->delayed_root.items); release_node: mutex_unlock(&delayed_node->mutex); btrfs_release_delayed_node(delayed_node, &delayed_node_tracker); @@ -2012,7 +1996,7 @@ int btrfs_delayed_delete_inode_ref(struct btrfs_inode *inode) mutex_lock(&delayed_node->mutex); if (!test_and_set_bit(BTRFS_DELAYED_NODE_DEL_IREF, &delayed_node->flags)) { delayed_node->count++; - atomic_inc(&fs_info->delayed_root->items); + atomic_inc(&fs_info->delayed_root.items); } mutex_unlock(&delayed_node->mutex); btrfs_release_delayed_node(delayed_node, &delayed_node_tracker); @@ -2118,8 +2102,7 @@ void btrfs_destroy_delayed_inodes(struct btrfs_fs_info *fs_info) struct btrfs_delayed_node *curr_node, *prev_node; struct btrfs_ref_tracker curr_delayed_node_tracker, prev_delayed_node_tracker; - curr_node = btrfs_first_delayed_node(fs_info->delayed_root, - &curr_delayed_node_tracker); + curr_node = btrfs_first_delayed_node(fs_info, &curr_delayed_node_tracker); while (curr_node) { __btrfs_kill_delayed_node(curr_node); diff --git a/fs/btrfs/delayed-inode.h b/fs/btrfs/delayed-inode.h index b09d4ec8c77d..fc752863f89b 100644 --- a/fs/btrfs/delayed-inode.h +++ b/fs/btrfs/delayed-inode.h @@ -30,21 +30,6 @@ enum btrfs_delayed_item_type { BTRFS_DELAYED_DELETION_ITEM }; -struct btrfs_delayed_root { - spinlock_t lock; - struct list_head node_list; - /* - * Used for delayed nodes which is waiting to be dealt with by the - * worker. If the delayed node is inserted into the work queue, we - * drop it from this list. - */ - struct list_head prepare_list; - atomic_t items; /* for delayed items */ - atomic_t items_seq; /* for delayed items */ - int nodes; /* for delayed nodes */ - wait_queue_head_t wait; -}; - struct btrfs_ref_tracker_dir { #ifdef CONFIG_BTRFS_DEBUG struct ref_tracker_dir dir; diff --git a/fs/btrfs/direct-io.c b/fs/btrfs/direct-io.c index 07e19e88ba4b..9a63200d7a53 100644 --- a/fs/btrfs/direct-io.c +++ b/fs/btrfs/direct-io.c @@ -763,7 +763,7 @@ static ssize_t btrfs_dio_read(struct kiocb *iocb, struct iov_iter *iter, struct btrfs_dio_data data = { 0 }; return iomap_dio_rw(iocb, iter, &btrfs_dio_iomap_ops, &btrfs_dio_ops, - IOMAP_DIO_PARTIAL, &data, done_before); + IOMAP_DIO_PARTIAL | IOMAP_DIO_FSBLOCK_ALIGNED, &data, done_before); } static struct iomap_dio *btrfs_dio_write(struct kiocb *iocb, struct iov_iter *iter, @@ -772,7 +772,7 @@ static struct iomap_dio *btrfs_dio_write(struct kiocb *iocb, struct iov_iter *it struct btrfs_dio_data data = { 0 }; return __iomap_dio_rw(iocb, iter, &btrfs_dio_iomap_ops, &btrfs_dio_ops, - IOMAP_DIO_PARTIAL, &data, done_before); + IOMAP_DIO_PARTIAL | IOMAP_DIO_FSBLOCK_ALIGNED, &data, done_before); } static ssize_t check_direct_IO(struct btrfs_fs_info *fs_info, @@ -785,19 +785,6 @@ static ssize_t check_direct_IO(struct btrfs_fs_info *fs_info, if (iov_iter_alignment(iter) & blocksize_mask) return -EINVAL; - - /* - * For bs > ps support, we heavily rely on large folios to make sure no - * block will cross large folio boundaries. - * - * But memory provided by direct IO is only virtually contiguous, not - * physically contiguous, and will break the btrfs' large folio requirement. - * - * So for bs > ps support, all direct IOs should fallback to buffered ones. - */ - if (fs_info->sectorsize > PAGE_SIZE) - return -EINVAL; - return 0; } @@ -814,6 +801,8 @@ ssize_t btrfs_direct_write(struct kiocb *iocb, struct iov_iter *from) ssize_t ret; unsigned int ilock_flags = 0; struct iomap_dio *dio; + const u64 data_profile = btrfs_data_alloc_profile(fs_info) & + BTRFS_BLOCK_GROUP_PROFILE_MASK; if (iocb->ki_flags & IOCB_NOWAIT) ilock_flags |= BTRFS_ILOCK_TRY; @@ -827,6 +816,16 @@ ssize_t btrfs_direct_write(struct kiocb *iocb, struct iov_iter *from) if (iocb->ki_pos + iov_iter_count(from) <= i_size_read(inode) && IS_NOSEC(inode)) ilock_flags |= BTRFS_ILOCK_SHARED; + /* + * If our data profile has duplication (either extra mirrors or RAID56), + * we can not trust the direct IO buffer, the content may change during + * writeback and cause different contents written to different mirrors. + * + * Thus only RAID0 and SINGLE can go true zero-copy direct IO. + */ + if (data_profile != BTRFS_BLOCK_GROUP_RAID0 && data_profile != 0) + goto buffered; + relock: ret = btrfs_inode_lock(BTRFS_I(inode), ilock_flags); if (ret < 0) diff --git a/fs/btrfs/discard.c b/fs/btrfs/discard.c index 89fe85778115..1c304bf473e5 100644 --- a/fs/btrfs/discard.c +++ b/fs/btrfs/discard.c @@ -216,6 +216,25 @@ static struct btrfs_block_group *find_next_block_group( } /* + * Check whether a block group is empty. + * + * "Empty" here means that there are no extents physically located within the + * device extents corresponding to this block group. + * + * For a remapped block group, this means that all of its identity remaps have + * been removed. For a non-remapped block group, this means that no extents + * have an address within its range, and that nothing has been remapped to be + * within it. + */ +static bool block_group_is_empty(const struct btrfs_block_group *bg) +{ + if (bg->flags & BTRFS_BLOCK_GROUP_REMAPPED) + return bg->identity_remap_count == 0; + + return bg->used == 0 && bg->remap_bytes == 0; +} + +/* * Look up next block group and set it for use. * * @discard_ctl: discard control @@ -241,8 +260,10 @@ again: block_group = find_next_block_group(discard_ctl, now); if (block_group && now >= block_group->discard_eligible_time) { + const bool empty = block_group_is_empty(block_group); + if (block_group->discard_index == BTRFS_DISCARD_INDEX_UNUSED && - block_group->used != 0) { + !empty) { if (btrfs_is_block_group_data_only(block_group)) { __add_to_discard_list(discard_ctl, block_group); /* @@ -267,7 +288,12 @@ again: } if (block_group->discard_state == BTRFS_DISCARD_RESET_CURSOR) { block_group->discard_cursor = block_group->start; - block_group->discard_state = BTRFS_DISCARD_EXTENTS; + + if (block_group->flags & BTRFS_BLOCK_GROUP_REMAPPED && empty) { + block_group->discard_state = BTRFS_DISCARD_FULLY_REMAPPED; + } else { + block_group->discard_state = BTRFS_DISCARD_EXTENTS; + } } } if (block_group) { @@ -373,7 +399,7 @@ void btrfs_discard_queue_work(struct btrfs_discard_ctl *discard_ctl, if (!block_group || !btrfs_test_opt(block_group->fs_info, DISCARD_ASYNC)) return; - if (block_group->used == 0) + if (block_group_is_empty(block_group)) add_to_discard_unused_list(discard_ctl, block_group); else add_to_discard_list(discard_ctl, block_group); @@ -470,7 +496,7 @@ static void btrfs_finish_discard_pass(struct btrfs_discard_ctl *discard_ctl, { remove_from_discard_list(discard_ctl, block_group); - if (block_group->used == 0) { + if (block_group_is_empty(block_group)) { if (btrfs_is_free_space_trimmed(block_group)) btrfs_mark_bg_unused(block_group); else @@ -524,7 +550,8 @@ static void btrfs_discard_workfn(struct work_struct *work) /* Perform discarding */ minlen = discard_minlen[discard_index]; - if (discard_state == BTRFS_DISCARD_BITMAPS) { + switch (discard_state) { + case BTRFS_DISCARD_BITMAPS: { u64 maxlen = 0; /* @@ -541,17 +568,28 @@ static void btrfs_discard_workfn(struct work_struct *work) btrfs_block_group_end(block_group), minlen, maxlen, true); discard_ctl->discard_bitmap_bytes += trimmed; - } else { + + break; + } + + case BTRFS_DISCARD_FULLY_REMAPPED: + btrfs_trim_fully_remapped_block_group(block_group); + break; + + default: btrfs_trim_block_group_extents(block_group, &trimmed, block_group->discard_cursor, btrfs_block_group_end(block_group), minlen, true); discard_ctl->discard_extent_bytes += trimmed; + + break; } /* Determine next steps for a block_group */ if (block_group->discard_cursor >= btrfs_block_group_end(block_group)) { - if (discard_state == BTRFS_DISCARD_BITMAPS) { + if (discard_state == BTRFS_DISCARD_BITMAPS || + discard_state == BTRFS_DISCARD_FULLY_REMAPPED) { btrfs_finish_discard_pass(discard_ctl, block_group); } else { block_group->discard_cursor = block_group->start; diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index d8ca5b6e88e0..20c405a4789d 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -18,11 +18,11 @@ #include <linux/crc32c.h> #include <linux/sched/mm.h> #include <linux/unaligned.h> -#include <crypto/hash.h> #include "ctree.h" #include "disk-io.h" #include "transaction.h" #include "btrfs_inode.h" +#include "delayed-inode.h" #include "bio.h" #include "print-tree.h" #include "locking.h" @@ -62,12 +62,6 @@ static int btrfs_cleanup_transaction(struct btrfs_fs_info *fs_info); static void btrfs_error_commit_super(struct btrfs_fs_info *fs_info); -static void btrfs_free_csum_hash(struct btrfs_fs_info *fs_info) -{ - if (fs_info->csum_shash) - crypto_free_shash(fs_info->csum_shash); -} - /* * Compute the csum of a btree block and store the result to provided buffer. */ @@ -76,12 +70,11 @@ static void csum_tree_block(struct extent_buffer *buf, u8 *result) struct btrfs_fs_info *fs_info = buf->fs_info; int num_pages; u32 first_page_part; - SHASH_DESC_ON_STACK(shash, fs_info->csum_shash); + struct btrfs_csum_ctx csum; char *kaddr; int i; - shash->tfm = fs_info->csum_shash; - crypto_shash_init(shash); + btrfs_csum_init(&csum, fs_info->csum_type); if (buf->addr) { /* Pages are contiguous, handle them as a big one. */ @@ -94,21 +87,21 @@ static void csum_tree_block(struct extent_buffer *buf, u8 *result) num_pages = num_extent_pages(buf); } - crypto_shash_update(shash, kaddr + BTRFS_CSUM_SIZE, - first_page_part - BTRFS_CSUM_SIZE); + btrfs_csum_update(&csum, kaddr + BTRFS_CSUM_SIZE, + first_page_part - BTRFS_CSUM_SIZE); /* * Multiple single-page folios case would reach here. * * nodesize <= PAGE_SIZE and large folio all handled by above - * crypto_shash_update() already. + * btrfs_csum_update() already. */ for (i = 1; i < num_pages && INLINE_EXTENT_BUFFER_PAGES > 1; i++) { kaddr = folio_address(buf->folios[i]); - crypto_shash_update(shash, kaddr, PAGE_SIZE); + btrfs_csum_update(&csum, kaddr, PAGE_SIZE); } memset(result, 0, BTRFS_CSUM_SIZE); - crypto_shash_final(shash, result); + btrfs_csum_final(&csum, result); } /* @@ -160,18 +153,15 @@ static bool btrfs_supported_super_csum(u16 csum_type) int btrfs_check_super_csum(struct btrfs_fs_info *fs_info, const struct btrfs_super_block *disk_sb) { - char result[BTRFS_CSUM_SIZE]; - SHASH_DESC_ON_STACK(shash, fs_info->csum_shash); - - shash->tfm = fs_info->csum_shash; + u8 result[BTRFS_CSUM_SIZE]; /* * The super_block structure does not span the whole * BTRFS_SUPER_INFO_SIZE range, we expect that the unused space is * filled with zeros and is included in the checksum. */ - crypto_shash_digest(shash, (const u8 *)disk_sb + BTRFS_CSUM_SIZE, - BTRFS_SUPER_INFO_SIZE - BTRFS_CSUM_SIZE, result); + btrfs_csum(fs_info->csum_type, (const u8 *)disk_sb + BTRFS_CSUM_SIZE, + BTRFS_SUPER_INFO_SIZE - BTRFS_CSUM_SIZE, result); if (memcmp(disk_sb->csum, result, fs_info->csum_size)) return 1; @@ -186,7 +176,6 @@ static int btrfs_repair_eb_io_failure(const struct extent_buffer *eb, const u32 step = min(fs_info->nodesize, PAGE_SIZE); const u32 nr_steps = eb->len / step; phys_addr_t paddrs[BTRFS_MAX_BLOCKSIZE / PAGE_SIZE]; - int ret = 0; if (sb_rdonly(fs_info->sb)) return -EROFS; @@ -208,9 +197,8 @@ static int btrfs_repair_eb_io_failure(const struct extent_buffer *eb, paddrs[i] = page_to_phys(&folio->page) + offset_in_page(eb->start); } - ret = btrfs_repair_io_failure(fs_info, 0, eb->start, eb->len, eb->start, - paddrs, step, mirror_num); - return ret; + return btrfs_repair_io_failure(fs_info, 0, eb->start, eb->len, + eb->start, paddrs, step, mirror_num); } /* @@ -382,22 +370,19 @@ int btrfs_validate_extent_buffer(struct extent_buffer *eb, btrfs_err_rl(fs_info, "bad tree block start, mirror %u want %llu have %llu", eb->read_mirror, eb->start, found_start); - ret = -EIO; - goto out; + return -EIO; } if (unlikely(check_tree_block_fsid(eb))) { btrfs_err_rl(fs_info, "bad fsid on logical %llu mirror %u", eb->start, eb->read_mirror); - ret = -EIO; - goto out; + return -EIO; } found_level = btrfs_header_level(eb); if (unlikely(found_level >= BTRFS_MAX_LEVEL)) { btrfs_err(fs_info, "bad tree block level, mirror %u level %d on logical %llu", eb->read_mirror, btrfs_header_level(eb), eb->start); - ret = -EIO; - goto out; + return -EIO; } csum_tree_block(eb, result); @@ -412,18 +397,15 @@ int btrfs_validate_extent_buffer(struct extent_buffer *eb, BTRFS_CSUM_FMT_VALUE(csum_size, result), btrfs_header_level(eb), ignore_csum ? ", ignored" : ""); - if (unlikely(!ignore_csum)) { - ret = -EUCLEAN; - goto out; - } + if (unlikely(!ignore_csum)) + return -EUCLEAN; } if (unlikely(found_level != check->level)) { btrfs_err(fs_info, "level verify failed on logical %llu mirror %u wanted %u found %u", eb->start, eb->read_mirror, check->level, found_level); - ret = -EIO; - goto out; + return -EIO; } if (unlikely(check->transid && btrfs_header_generation(eb) != check->transid)) { @@ -431,8 +413,7 @@ int btrfs_validate_extent_buffer(struct extent_buffer *eb, "parent transid verify failed on logical %llu mirror %u wanted %llu found %llu", eb->start, eb->read_mirror, check->transid, btrfs_header_generation(eb)); - ret = -EIO; - goto out; + return -EIO; } if (check->has_first_key) { const struct btrfs_key *expect_key = &check->first_key; @@ -450,14 +431,13 @@ int btrfs_validate_extent_buffer(struct extent_buffer *eb, expect_key->type, expect_key->offset, found_key.objectid, found_key.type, found_key.offset); - ret = -EUCLEAN; - goto out; + return -EUCLEAN; } } if (check->owner_root) { ret = btrfs_check_eb_owner(eb, check->owner_root); if (ret < 0) - goto out; + return ret; } /* If this is a leaf block and it is corrupt, just return -EIO. */ @@ -471,7 +451,6 @@ int btrfs_validate_extent_buffer(struct extent_buffer *eb, btrfs_err(fs_info, "read time tree block corruption detected on logical %llu mirror %u", eb->start, eb->read_mirror); -out: return ret; } @@ -498,28 +477,6 @@ static int btree_migrate_folio(struct address_space *mapping, #define btree_migrate_folio NULL #endif -static int btree_writepages(struct address_space *mapping, - struct writeback_control *wbc) -{ - int ret; - - if (wbc->sync_mode == WB_SYNC_NONE) { - struct btrfs_fs_info *fs_info; - - if (wbc->for_kupdate) - return 0; - - fs_info = inode_to_fs_info(mapping->host); - /* this is a bit racy, but that's ok */ - ret = __percpu_counter_compare(&fs_info->dirty_metadata_bytes, - BTRFS_DIRTY_METADATA_THRESH, - fs_info->dirty_metadata_batch); - if (ret < 0) - return 0; - } - return btree_write_cache_pages(mapping, wbc); -} - static bool btree_release_folio(struct folio *folio, gfp_t gfp_flags) { if (folio_test_writeback(folio) || folio_test_dirty(folio)) @@ -837,7 +794,6 @@ struct btrfs_root *btrfs_create_tree(struct btrfs_trans_handle *trans, struct extent_buffer *leaf; struct btrfs_root *tree_root = fs_info->tree_root; struct btrfs_root *root; - struct btrfs_key key; unsigned int nofs_flag; int ret = 0; @@ -886,10 +842,7 @@ struct btrfs_root *btrfs_create_tree(struct btrfs_trans_handle *trans, btrfs_tree_unlock(leaf); - key.objectid = objectid; - key.type = BTRFS_ROOT_ITEM_KEY; - key.offset = 0; - ret = btrfs_insert_root(trans, tree_root, &key, &root->root_item); + ret = btrfs_insert_root(trans, tree_root, &root->root_key, &root->root_item); if (ret) goto fail; @@ -1175,6 +1128,8 @@ static struct btrfs_root *btrfs_get_global_root(struct btrfs_fs_info *fs_info, return btrfs_grab_root(btrfs_global_root(fs_info, &key)); case BTRFS_RAID_STRIPE_TREE_OBJECTID: return btrfs_grab_root(fs_info->stripe_root); + case BTRFS_REMAP_TREE_OBJECTID: + return btrfs_grab_root(fs_info->remap_root); default: return NULL; } @@ -1251,11 +1206,9 @@ void btrfs_free_fs_info(struct btrfs_fs_info *fs_info) ASSERT(percpu_counter_sum_positive(em_counter) == 0); percpu_counter_destroy(em_counter); percpu_counter_destroy(&fs_info->dev_replace.bio_counter); - btrfs_free_csum_hash(fs_info); btrfs_free_stripe_hash_table(fs_info); btrfs_free_ref_cache(fs_info); kfree(fs_info->balance_ctl); - kfree(fs_info->delayed_root); free_global_roots(fs_info); btrfs_put_root(fs_info->tree_root); btrfs_put_root(fs_info->chunk_root); @@ -1266,6 +1219,7 @@ void btrfs_free_fs_info(struct btrfs_fs_info *fs_info) btrfs_put_root(fs_info->data_reloc_root); btrfs_put_root(fs_info->block_group_root); btrfs_put_root(fs_info->stripe_root); + btrfs_put_root(fs_info->remap_root); btrfs_check_leaked_roots(fs_info); btrfs_extent_buffer_leak_debug_check(fs_info); kfree(fs_info->super_copy); @@ -1510,6 +1464,10 @@ static int cleaner_kthread(void *arg) */ btrfs_run_defrag_inodes(fs_info); + if (btrfs_fs_incompat(fs_info, REMAP_TREE) && + !btrfs_test_opt(fs_info, DISCARD_ASYNC)) + btrfs_handle_fully_remapped_bgs(fs_info); + /* * Acquires fs_info->reclaim_bgs_lock to avoid racing * with relocation (btrfs_relocate_chunk) and relocation @@ -1661,7 +1619,7 @@ static void backup_super_roots(struct btrfs_fs_info *info) btrfs_set_backup_chunk_root_level(root_backup, btrfs_header_level(info->chunk_root->node)); - if (!btrfs_fs_compat_ro(info, BLOCK_GROUP_TREE)) { + if (!btrfs_fs_incompat(info, EXTENT_TREE_V2)) { struct btrfs_root *extent_root = btrfs_extent_root(info, 0); struct btrfs_root *csum_root = btrfs_csum_root(info, 0); @@ -1818,6 +1776,7 @@ static void free_root_pointers(struct btrfs_fs_info *info, bool free_chunk_root) free_root_extent_buffers(info->data_reloc_root); free_root_extent_buffers(info->block_group_root); free_root_extent_buffers(info->stripe_root); + free_root_extent_buffers(info->remap_root); if (free_chunk_root) free_root_extent_buffers(info->chunk_root); } @@ -2005,21 +1964,8 @@ static int btrfs_init_workqueues(struct btrfs_fs_info *fs_info) return 0; } -static int btrfs_init_csum_hash(struct btrfs_fs_info *fs_info, u16 csum_type) +static void btrfs_init_csum_hash(struct btrfs_fs_info *fs_info, u16 csum_type) { - struct crypto_shash *csum_shash; - const char *csum_driver = btrfs_super_csum_driver(csum_type); - - csum_shash = crypto_alloc_shash(csum_driver, 0, 0); - - if (IS_ERR(csum_shash)) { - btrfs_err(fs_info, "error allocating %s hash for checksum", - csum_driver); - return PTR_ERR(csum_shash); - } - - fs_info->csum_shash = csum_shash; - /* Check if the checksum implementation is a fast accelerated one. */ switch (csum_type) { case BTRFS_CSUM_TYPE_CRC32: @@ -2033,10 +1979,8 @@ static int btrfs_init_csum_hash(struct btrfs_fs_info *fs_info, u16 csum_type) break; } - btrfs_info(fs_info, "using %s (%s) checksum algorithm", - btrfs_super_csum_name(csum_type), - crypto_shash_driver_name(csum_shash)); - return 0; + btrfs_info(fs_info, "using %s checksum algorithm", + btrfs_super_csum_name(csum_type)); } static int btrfs_replay_log(struct btrfs_fs_info *fs_info, @@ -2194,11 +2138,10 @@ static int load_global_roots(struct btrfs_root *tree_root) return ret; if (!btrfs_fs_compat_ro(tree_root->fs_info, FREE_SPACE_TREE)) return ret; - ret = load_global_roots_objectid(tree_root, path, - BTRFS_FREE_SPACE_TREE_OBJECTID, - "free space"); - return ret; + return load_global_roots_objectid(tree_root, path, + BTRFS_FREE_SPACE_TREE_OBJECTID, + "free space"); } static int btrfs_read_roots(struct btrfs_fs_info *fs_info) @@ -2247,21 +2190,44 @@ static int btrfs_read_roots(struct btrfs_fs_info *fs_info) if (ret) goto out; - /* - * This tree can share blocks with some other fs tree during relocation - * and we need a proper setup by btrfs_get_fs_root - */ - root = btrfs_get_fs_root(tree_root->fs_info, - BTRFS_DATA_RELOC_TREE_OBJECTID, true); - if (IS_ERR(root)) { - if (!btrfs_test_opt(fs_info, IGNOREBADROOTS)) { - location.objectid = BTRFS_DATA_RELOC_TREE_OBJECTID; - ret = PTR_ERR(root); - goto out; + if (btrfs_fs_incompat(fs_info, REMAP_TREE)) { + /* The remap_root has already been loaded in load_important_roots(). */ + root = fs_info->remap_root; + + set_bit(BTRFS_ROOT_TRACK_DIRTY, &root->state); + + root->root_key.objectid = BTRFS_REMAP_TREE_OBJECTID; + root->root_key.type = BTRFS_ROOT_ITEM_KEY; + root->root_key.offset = 0; + + /* Check that data reloc tree doesn't also exist. */ + location.objectid = BTRFS_DATA_RELOC_TREE_OBJECTID; + root = btrfs_read_tree_root(fs_info->tree_root, &location); + if (!IS_ERR(root)) { + btrfs_err(fs_info, "data reloc tree exists when remap-tree enabled"); + btrfs_put_root(root); + return -EIO; + } else if (PTR_ERR(root) != -ENOENT) { + btrfs_warn(fs_info, "error %ld when checking for data reloc tree", + PTR_ERR(root)); } } else { - set_bit(BTRFS_ROOT_TRACK_DIRTY, &root->state); - fs_info->data_reloc_root = root; + /* + * This tree can share blocks with some other fs tree during + * relocation and we need a proper setup by btrfs_get_fs_root(). + */ + root = btrfs_get_fs_root(tree_root->fs_info, + BTRFS_DATA_RELOC_TREE_OBJECTID, true); + if (IS_ERR(root)) { + if (!btrfs_test_opt(fs_info, IGNOREBADROOTS)) { + location.objectid = BTRFS_DATA_RELOC_TREE_OBJECTID; + ret = PTR_ERR(root); + goto out; + } + } else { + set_bit(BTRFS_ROOT_TRACK_DIRTY, &root->state); + fs_info->data_reloc_root = root; + } } location.objectid = BTRFS_QUOTA_TREE_OBJECTID; @@ -2501,6 +2467,35 @@ int btrfs_validate_super(const struct btrfs_fs_info *fs_info, ret = -EINVAL; } + if (btrfs_fs_incompat(fs_info, REMAP_TREE)) { + /* + * Reduce test matrix for remap tree by requiring block-group-tree + * and no-holes. Free-space-tree is a hard requirement. + */ + if (!btrfs_fs_compat_ro(fs_info, FREE_SPACE_TREE_VALID) || + !btrfs_fs_incompat(fs_info, NO_HOLES) || + !btrfs_fs_compat_ro(fs_info, BLOCK_GROUP_TREE)) { + btrfs_err(fs_info, +"remap-tree feature requires free-space-tree, no-holes, and block-group-tree"); + ret = -EINVAL; + } + + if (btrfs_fs_incompat(fs_info, MIXED_GROUPS)) { + btrfs_err(fs_info, "remap-tree not supported with mixed-bg"); + ret = -EINVAL; + } + + if (btrfs_fs_incompat(fs_info, ZONED)) { + btrfs_err(fs_info, "remap-tree not supported with zoned devices"); + ret = -EINVAL; + } + + if (sectorsize > PAGE_SIZE) { + btrfs_err(fs_info, "remap-tree not supported when block size > page size"); + ret = -EINVAL; + } + } + /* * Hint to catch really bogus numbers, bitflips or so, more exact checks are * done later @@ -2659,6 +2654,18 @@ static int load_important_roots(struct btrfs_fs_info *fs_info) btrfs_warn(fs_info, "couldn't read tree root"); return ret; } + + if (btrfs_fs_incompat(fs_info, REMAP_TREE)) { + bytenr = btrfs_super_remap_root(sb); + gen = btrfs_super_remap_root_generation(sb); + level = btrfs_super_remap_root_level(sb); + ret = load_super_root(fs_info->remap_root, bytenr, gen, level); + if (ret) { + btrfs_warn(fs_info, "couldn't read remap root"); + return ret; + } + } + return 0; } @@ -2795,6 +2802,7 @@ void btrfs_init_fs_info(struct btrfs_fs_info *fs_info) INIT_LIST_HEAD(&fs_info->tree_mod_seq_list); INIT_LIST_HEAD(&fs_info->unused_bgs); INIT_LIST_HEAD(&fs_info->reclaim_bgs); + INIT_LIST_HEAD(&fs_info->fully_remapped_bgs); INIT_LIST_HEAD(&fs_info->zone_active_bgs); #ifdef CONFIG_BTRFS_DEBUG INIT_LIST_HEAD(&fs_info->allocated_roots); @@ -2807,6 +2815,7 @@ void btrfs_init_fs_info(struct btrfs_fs_info *fs_info) BTRFS_BLOCK_RSV_GLOBAL); btrfs_init_block_rsv(&fs_info->trans_block_rsv, BTRFS_BLOCK_RSV_TRANS); btrfs_init_block_rsv(&fs_info->chunk_block_rsv, BTRFS_BLOCK_RSV_CHUNK); + btrfs_init_block_rsv(&fs_info->remap_block_rsv, BTRFS_BLOCK_RSV_REMAP); btrfs_init_block_rsv(&fs_info->treelog_rsv, BTRFS_BLOCK_RSV_TREELOG); btrfs_init_block_rsv(&fs_info->empty_block_rsv, BTRFS_BLOCK_RSV_EMPTY); btrfs_init_block_rsv(&fs_info->delayed_block_rsv, @@ -2849,6 +2858,7 @@ void btrfs_init_fs_info(struct btrfs_fs_info *fs_info) mutex_init(&fs_info->chunk_mutex); mutex_init(&fs_info->transaction_kthread_mutex); mutex_init(&fs_info->cleaner_mutex); + mutex_init(&fs_info->remap_mutex); mutex_init(&fs_info->ro_block_group_mutex); init_rwsem(&fs_info->commit_root_sem); init_rwsem(&fs_info->cleanup_work_sem); @@ -2923,11 +2933,7 @@ static int init_mount_fs_info(struct btrfs_fs_info *fs_info, struct super_block if (ret) return ret; - fs_info->delayed_root = kmalloc(sizeof(struct btrfs_delayed_root), - GFP_KERNEL); - if (!fs_info->delayed_root) - return -ENOMEM; - btrfs_init_delayed_root(fs_info->delayed_root); + btrfs_init_delayed_root(&fs_info->delayed_root); if (sb_rdonly(sb)) set_bit(BTRFS_FS_STATE_RO, &fs_info->fs_state); @@ -3040,6 +3046,8 @@ int btrfs_start_pre_rw_mount(struct btrfs_fs_info *fs_info) if (btrfs_fs_incompat(fs_info, EXTENT_TREE_V2)) btrfs_warn(fs_info, "'clear_cache' option is ignored with extent tree v2"); + else if (btrfs_fs_incompat(fs_info, REMAP_TREE)) + btrfs_warn(fs_info, "'clear_cache' option is ignored with remap tree"); else rebuild_free_space_tree = true; } else if (btrfs_fs_compat_ro(fs_info, FREE_SPACE_TREE) && @@ -3054,7 +3062,7 @@ int btrfs_start_pre_rw_mount(struct btrfs_fs_info *fs_info) if (ret) { btrfs_warn(fs_info, "failed to rebuild free space tree: %d", ret); - goto out; + return ret; } } @@ -3065,11 +3073,20 @@ int btrfs_start_pre_rw_mount(struct btrfs_fs_info *fs_info) if (ret) { btrfs_warn(fs_info, "failed to disable free space tree: %d", ret); - goto out; + return ret; } } /* + * Before btrfs-progs v6.16.1 mkfs.btrfs can leave free space entries + * for deleted temporary chunks. Delete them if they exist. + */ + ret = btrfs_delete_orphan_free_space_entries(fs_info); + if (ret < 0) { + btrfs_err(fs_info, "failed to delete orphan free space tree entries: %d", ret); + return ret; + } + /* * btrfs_find_orphan_roots() is responsible for finding all the dead * roots (with 0 refs), flag them with BTRFS_ROOT_DEAD_TREE and load * them into the fs_info->fs_roots_radix tree. This must be done before @@ -3082,17 +3099,17 @@ int btrfs_start_pre_rw_mount(struct btrfs_fs_info *fs_info) */ ret = btrfs_find_orphan_roots(fs_info); if (ret) - goto out; + return ret; ret = btrfs_cleanup_fs_roots(fs_info); if (ret) - goto out; + return ret; down_read(&fs_info->cleanup_work_sem); if ((ret = btrfs_orphan_cleanup(fs_info->fs_root)) || (ret = btrfs_orphan_cleanup(fs_info->tree_root))) { up_read(&fs_info->cleanup_work_sem); - goto out; + return ret; } up_read(&fs_info->cleanup_work_sem); @@ -3101,7 +3118,7 @@ int btrfs_start_pre_rw_mount(struct btrfs_fs_info *fs_info) mutex_unlock(&fs_info->cleaner_mutex); if (ret < 0) { btrfs_warn(fs_info, "failed to recover relocation: %d", ret); - goto out; + return ret; } if (btrfs_test_opt(fs_info, FREE_SPACE_TREE) && @@ -3111,24 +3128,24 @@ int btrfs_start_pre_rw_mount(struct btrfs_fs_info *fs_info) if (ret) { btrfs_warn(fs_info, "failed to create free space tree: %d", ret); - goto out; + return ret; } } if (cache_opt != btrfs_free_space_cache_v1_active(fs_info)) { ret = btrfs_set_free_space_cache_v1_active(fs_info, cache_opt); if (ret) - goto out; + return ret; } ret = btrfs_resume_balance_async(fs_info); if (ret) - goto out; + return ret; ret = btrfs_resume_dev_replace_async(fs_info); if (ret) { btrfs_warn(fs_info, "failed to resume dev_replace"); - goto out; + return ret; } btrfs_qgroup_rescan_resume(fs_info); @@ -3139,12 +3156,11 @@ int btrfs_start_pre_rw_mount(struct btrfs_fs_info *fs_info) if (ret) { btrfs_warn(fs_info, "failed to create the UUID tree %d", ret); - goto out; + return ret; } } -out: - return ret; + return 0; } /* @@ -3255,6 +3271,15 @@ int btrfs_check_features(struct btrfs_fs_info *fs_info, bool is_rw_mount) return 0; } +static bool fs_is_full_ro(const struct btrfs_fs_info *fs_info) +{ + if (!sb_rdonly(fs_info->sb)) + return false; + if (unlikely(fs_info->mount_opt & BTRFS_MOUNT_FULL_RO_MASK)) + return true; + return false; +} + int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_devices) { u32 sectorsize; @@ -3266,6 +3291,7 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device struct btrfs_fs_info *fs_info = btrfs_sb(sb); struct btrfs_root *tree_root; struct btrfs_root *chunk_root; + struct btrfs_root *remap_root; int ret; int level; @@ -3315,12 +3341,9 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device } fs_info->csum_size = btrfs_super_csum_size(disk_super); + fs_info->csum_type = csum_type; - ret = btrfs_init_csum_hash(fs_info, csum_type); - if (ret) { - btrfs_release_disk_super(disk_super); - goto fail_alloc; - } + btrfs_init_csum_hash(fs_info, csum_type); /* * We want to check superblock checksum, the type is stored inside. @@ -3363,6 +3386,10 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device if (btrfs_super_flags(disk_super) & BTRFS_SUPER_FLAG_ERROR) WRITE_ONCE(fs_info->fs_error, -EUCLEAN); + /* If the fs has any rescue options, no transaction is allowed. */ + if (fs_is_full_ro(fs_info)) + WRITE_ONCE(fs_info->fs_error, -EROFS); + /* Set up fs_info before parsing mount options */ nodesize = btrfs_super_nodesize(disk_super); sectorsize = btrfs_super_sectorsize(disk_super); @@ -3399,6 +3426,16 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device if (ret < 0) goto fail_alloc; + if (btrfs_super_incompat_flags(disk_super) & BTRFS_FEATURE_INCOMPAT_REMAP_TREE) { + remap_root = btrfs_alloc_root(fs_info, BTRFS_REMAP_TREE_OBJECTID, + GFP_KERNEL); + fs_info->remap_root = remap_root; + if (!remap_root) { + ret = -ENOMEM; + goto fail_alloc; + } + } + /* * At this point our mount options are validated, if we set ->max_inline * to something non-standard make sure we truncate it to sectorsize. @@ -3489,6 +3526,10 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device fs_info->generation == btrfs_super_uuid_tree_generation(disk_super)) set_bit(BTRFS_FS_UPDATE_UUID_TREE_GEN, &fs_info->flags); + if (unlikely(btrfs_verify_dev_items(fs_info))) { + ret = -EUCLEAN; + goto fail_block_groups; + } ret = btrfs_verify_dev_extents(fs_info); if (ret) { btrfs_err(fs_info, @@ -3546,6 +3587,14 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device goto fail_sysfs; } + if (btrfs_fs_incompat(fs_info, REMAP_TREE)) { + ret = btrfs_populate_fully_remapped_bgs_list(fs_info); + if (ret) { + btrfs_err(fs_info, "failed to populate fully_remapped_bgs list: %d", ret); + goto fail_sysfs; + } + } + btrfs_zoned_reserve_data_reloc_bg(fs_info); btrfs_free_zone_cache(fs_info); @@ -3714,7 +3763,6 @@ static int write_dev_supers(struct btrfs_device *device, { struct btrfs_fs_info *fs_info = device->fs_info; struct address_space *mapping = device->bdev->bd_mapping; - SHASH_DESC_ON_STACK(shash, fs_info->csum_shash); int i; int ret; u64 bytenr, bytenr_orig; @@ -3724,8 +3772,6 @@ static int write_dev_supers(struct btrfs_device *device, if (max_mirrors == 0) max_mirrors = BTRFS_SUPER_MIRROR_MAX; - shash->tfm = fs_info->csum_shash; - for (i = 0; i < max_mirrors; i++) { struct folio *folio; struct bio *bio; @@ -3749,9 +3795,8 @@ static int write_dev_supers(struct btrfs_device *device, btrfs_set_super_bytenr(sb, bytenr_orig); - crypto_shash_digest(shash, (const char *)sb + BTRFS_CSUM_SIZE, - BTRFS_SUPER_INFO_SIZE - BTRFS_CSUM_SIZE, - sb->csum); + btrfs_csum(fs_info->csum_type, (const u8 *)sb + BTRFS_CSUM_SIZE, + BTRFS_SUPER_INFO_SIZE - BTRFS_CSUM_SIZE, sb->csum); folio = __filemap_get_folio(mapping, bytenr >> PAGE_SHIFT, FGP_LOCK | FGP_ACCESSED | FGP_CREAT, @@ -3871,7 +3916,7 @@ static void write_dev_flush(struct btrfs_device *device) { struct bio *bio = &device->flush_bio; - device->last_flush_error = BLK_STS_OK; + clear_bit(BTRFS_DEV_STATE_FLUSH_FAILED, &device->dev_state); bio_init(bio, device->bdev, NULL, 0, REQ_OP_WRITE | REQ_SYNC | REQ_PREFLUSH); @@ -3896,7 +3941,7 @@ static bool wait_dev_flush(struct btrfs_device *device) wait_for_completion_io(&device->flush_wait); if (bio->bi_status) { - device->last_flush_error = bio->bi_status; + set_bit(BTRFS_DEV_STATE_FLUSH_FAILED, &device->dev_state); btrfs_dev_stat_inc_and_print(device, BTRFS_DEV_STAT_FLUSH_ERRS); return true; } @@ -3946,7 +3991,7 @@ static int barrier_all_devices(struct btrfs_fs_info *info) } /* - * Checks last_flush_error of disks in order to determine the device + * Checks flush failure of disks in order to determine the device * state. */ if (unlikely(errors_wait && !btrfs_check_rw_degradable(info, NULL))) diff --git a/fs/btrfs/extent-io-tree.c b/fs/btrfs/extent-io-tree.c index bb2ca1c9c7b0..d0dd50f7d279 100644 --- a/fs/btrfs/extent-io-tree.c +++ b/fs/btrfs/extent-io-tree.c @@ -187,8 +187,6 @@ static int add_extent_changeset(struct extent_state *state, u32 bits, struct extent_changeset *changeset, int set) { - int ret; - if (!changeset) return 0; if (set && (state->state & bits) == bits) @@ -196,9 +194,8 @@ static int add_extent_changeset(struct extent_state *state, u32 bits, if (!set && (state->state & bits) == 0) return 0; changeset->bytes_changed += state->end - state->start + 1; - ret = ulist_add(&changeset->range_changed, state->start, state->end, - GFP_ATOMIC); - return ret; + + return ulist_add(&changeset->range_changed, state->start, state->end, GFP_ATOMIC); } static inline struct extent_state *next_state(struct extent_state *state) diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index e4cae34620d1..03cf9f242c70 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -41,6 +41,7 @@ #include "tree-checker.h" #include "raid-stripe-tree.h" #include "delayed-inode.h" +#include "relocation.h" #undef SCRAMBLE_DELAYED_REFS @@ -476,7 +477,7 @@ again: btrfs_item_key_to_cpu(leaf, &key, path->slots[0]); if (key.objectid != bytenr || key.type != BTRFS_EXTENT_DATA_REF_KEY) - goto fail; + return ret; ref = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_extent_data_ref); @@ -487,12 +488,11 @@ again: btrfs_release_path(path); goto again; } - ret = 0; - break; + return 0; } path->slots[0]++; } -fail: + return ret; } @@ -1380,7 +1380,7 @@ out: } int btrfs_discard_extent(struct btrfs_fs_info *fs_info, u64 bytenr, - u64 num_bytes, u64 *actual_bytes) + u64 num_bytes, u64 *actual_bytes, bool do_remap) { int ret = 0; u64 discarded_bytes = 0; @@ -1398,7 +1398,8 @@ int btrfs_discard_extent(struct btrfs_fs_info *fs_info, u64 bytenr, int i; num_bytes = end - cur; - stripes = btrfs_map_discard(fs_info, cur, &num_bytes, &num_stripes); + stripes = btrfs_map_discard(fs_info, cur, &num_bytes, &num_stripes, + do_remap); if (IS_ERR(stripes)) { ret = PTR_ERR(stripes); if (ret == -EOPNOTSUPP) @@ -1553,6 +1554,28 @@ static void free_head_ref_squota_rsv(struct btrfs_fs_info *fs_info, BTRFS_QGROUP_RSV_DATA); } +static int drop_remap_tree_ref(struct btrfs_trans_handle *trans, + const struct btrfs_delayed_ref_node *node) +{ + u64 bytenr = node->bytenr; + u64 num_bytes = node->num_bytes; + int ret; + + ret = btrfs_add_to_free_space_tree(trans, bytenr, num_bytes); + if (unlikely(ret)) { + btrfs_abort_transaction(trans, ret); + return ret; + } + + ret = btrfs_update_block_group(trans, bytenr, num_bytes, false); + if (unlikely(ret)) { + btrfs_abort_transaction(trans, ret); + return ret; + } + + return 0; +} + static int run_delayed_data_ref(struct btrfs_trans_handle *trans, struct btrfs_delayed_ref_head *href, const struct btrfs_delayed_ref_node *node, @@ -1747,7 +1770,10 @@ static int run_delayed_tree_ref(struct btrfs_trans_handle *trans, } else if (node->action == BTRFS_ADD_DELAYED_REF) { ret = __btrfs_inc_extent_ref(trans, node, extent_op); } else if (node->action == BTRFS_DROP_DELAYED_REF) { - ret = __btrfs_free_extent(trans, href, node, extent_op); + if (node->ref_root == BTRFS_REMAP_TREE_OBJECTID) + ret = drop_remap_tree_ref(trans, node); + else + ret = __btrfs_free_extent(trans, href, node, extent_op); } else { BUG(); } @@ -1761,35 +1787,39 @@ static int run_one_delayed_ref(struct btrfs_trans_handle *trans, struct btrfs_delayed_extent_op *extent_op, bool insert_reserved) { + struct btrfs_fs_info *fs_info = trans->fs_info; int ret = 0; if (TRANS_ABORTED(trans)) { if (insert_reserved) { btrfs_pin_extent(trans, node->bytenr, node->num_bytes); - free_head_ref_squota_rsv(trans->fs_info, href); + free_head_ref_squota_rsv(fs_info, href); } return 0; } if (node->type == BTRFS_TREE_BLOCK_REF_KEY || - node->type == BTRFS_SHARED_BLOCK_REF_KEY) + node->type == BTRFS_SHARED_BLOCK_REF_KEY) { ret = run_delayed_tree_ref(trans, href, node, extent_op, insert_reserved); - else if (node->type == BTRFS_EXTENT_DATA_REF_KEY || - node->type == BTRFS_SHARED_DATA_REF_KEY) + } else if (node->type == BTRFS_EXTENT_DATA_REF_KEY || + node->type == BTRFS_SHARED_DATA_REF_KEY) { ret = run_delayed_data_ref(trans, href, node, extent_op, insert_reserved); - else if (node->type == BTRFS_EXTENT_OWNER_REF_KEY) - ret = 0; - else - BUG(); - if (ret && insert_reserved) - btrfs_pin_extent(trans, node->bytenr, node->num_bytes); - if (ret < 0) - btrfs_err(trans->fs_info, + } else if (unlikely(node->type != BTRFS_EXTENT_OWNER_REF_KEY)) { + ret = -EUCLEAN; + btrfs_err(fs_info, "unexpected delayed ref node type: %u", node->type); + } + + if (unlikely(ret)) { + if (insert_reserved) + btrfs_pin_extent(trans, node->bytenr, node->num_bytes); + btrfs_err(fs_info, "failed to run delayed ref for logical %llu num_bytes %llu type %u action %u ref_mod %d: %d", node->bytenr, node->num_bytes, node->type, node->action, node->ref_mod, ret); + } + return ret; } @@ -2470,7 +2500,7 @@ static int __btrfs_mod_ref(struct btrfs_trans_handle *trans, int i; int action; int level; - int ret = 0; + int ret; if (btrfs_is_testing(fs_info)) return 0; @@ -2522,7 +2552,7 @@ static int __btrfs_mod_ref(struct btrfs_trans_handle *trans, else ret = btrfs_free_extent(trans, &ref); if (ret) - goto fail; + return ret; } else { /* We don't know the owning_root, leave as 0. */ ref.bytenr = btrfs_node_blockptr(buf, i); @@ -2535,12 +2565,10 @@ static int __btrfs_mod_ref(struct btrfs_trans_handle *trans, else ret = btrfs_free_extent(trans, &ref); if (ret) - goto fail; + return ret; } } return 0; -fail: - return ret; } int btrfs_inc_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root, @@ -2559,17 +2587,17 @@ static u64 get_alloc_profile_by_root(struct btrfs_root *root, int data) { struct btrfs_fs_info *fs_info = root->fs_info; u64 flags; - u64 ret; if (data) flags = BTRFS_BLOCK_GROUP_DATA; else if (root == fs_info->chunk_root) flags = BTRFS_BLOCK_GROUP_SYSTEM; + else if (root == fs_info->remap_root) + flags = BTRFS_BLOCK_GROUP_METADATA_REMAP; else flags = BTRFS_BLOCK_GROUP_METADATA; - ret = btrfs_get_alloc_profile(fs_info, flags); - return ret; + return btrfs_get_alloc_profile(fs_info, flags); } static u64 first_logical_byte(struct btrfs_fs_info *fs_info) @@ -2753,8 +2781,7 @@ static int unpin_extent_range(struct btrfs_fs_info *fs_info, u64 len; bool readonly; - if (!cache || - start >= cache->start + cache->length) { + if (!cache || start >= btrfs_block_group_end(cache)) { if (cache) btrfs_put_block_group(cache); total_unpinned = 0; @@ -2770,7 +2797,7 @@ static int unpin_extent_range(struct btrfs_fs_info *fs_info, empty_cluster <<= 1; } - len = cache->start + cache->length - start; + len = btrfs_block_group_end(cache) - start; len = min(len, end + 1 - start); if (return_free_space) @@ -2819,6 +2846,75 @@ static int unpin_extent_range(struct btrfs_fs_info *fs_info, return 0; } +/* + * Complete the remapping of a block group by removing its chunk stripes and + * device extents, and adding it to the unused list if there's no longer any + * extents nominally within it. + */ +int btrfs_complete_bg_remapping(struct btrfs_block_group *bg) +{ + struct btrfs_fs_info *fs_info = bg->fs_info; + struct btrfs_chunk_map *map; + int ret; + + map = btrfs_get_chunk_map(fs_info, bg->start, 1); + if (IS_ERR(map)) + return PTR_ERR(map); + + ret = btrfs_last_identity_remap_gone(map, bg); + if (ret) { + btrfs_free_chunk_map(map); + return ret; + } + + /* + * Set num_stripes to 0, so that btrfs_remove_dev_extents() won't run a + * second time. + */ + map->num_stripes = 0; + + btrfs_free_chunk_map(map); + + if (bg->used == 0) { + spin_lock(&fs_info->unused_bgs_lock); + if (!list_empty(&bg->bg_list)) { + list_del_init(&bg->bg_list); + btrfs_put_block_group(bg); + } + spin_unlock(&fs_info->unused_bgs_lock); + + btrfs_mark_bg_unused(bg); + } + + return 0; +} + +void btrfs_handle_fully_remapped_bgs(struct btrfs_fs_info *fs_info) +{ + struct btrfs_block_group *bg; + int ret; + + spin_lock(&fs_info->unused_bgs_lock); + while (!list_empty(&fs_info->fully_remapped_bgs)) { + bg = list_first_entry(&fs_info->fully_remapped_bgs, + struct btrfs_block_group, bg_list); + list_del_init(&bg->bg_list); + spin_unlock(&fs_info->unused_bgs_lock); + + btrfs_discard_extent(fs_info, bg->start, bg->length, NULL, false); + + ret = btrfs_complete_bg_remapping(bg); + if (ret) { + btrfs_put_block_group(bg); + return; + } + + btrfs_put_block_group(bg); + spin_lock(&fs_info->unused_bgs_lock); + } + spin_unlock(&fs_info->unused_bgs_lock); +} + int btrfs_finish_extent_commit(struct btrfs_trans_handle *trans) { struct btrfs_fs_info *fs_info = trans->fs_info; @@ -2839,7 +2935,7 @@ int btrfs_finish_extent_commit(struct btrfs_trans_handle *trans) if (btrfs_test_opt(fs_info, DISCARD_SYNC)) ret = btrfs_discard_extent(fs_info, start, - end + 1 - start, NULL); + end + 1 - start, NULL, true); next_state = btrfs_next_extent_state(unpin, cached_state); btrfs_clear_extent_dirty(unpin, start, end, &cached_state); @@ -2897,7 +2993,7 @@ int btrfs_finish_extent_commit(struct btrfs_trans_handle *trans) ret = -EROFS; if (!TRANS_ABORTED(trans)) ret = btrfs_discard_extent(fs_info, block_group->start, - block_group->length, NULL); + block_group->length, NULL, true); /* * Not strictly necessary to lock, as the block_group should be @@ -2971,11 +3067,22 @@ u64 btrfs_get_extent_owner_root(struct btrfs_fs_info *fs_info, } static int do_free_extent_accounting(struct btrfs_trans_handle *trans, - u64 bytenr, struct btrfs_squota_delta *delta) + u64 bytenr, struct btrfs_squota_delta *delta, + struct btrfs_path *path) { int ret; + bool remapped = false; u64 num_bytes = delta->num_bytes; + /* Returns 1 on success and 0 on no-op. */ + ret = btrfs_remove_extent_from_remap_tree(trans, path, bytenr, num_bytes); + if (unlikely(ret < 0)) { + btrfs_abort_transaction(trans, ret); + return ret; + } else if (ret == 1) { + remapped = true; + } + if (delta->is_data) { struct btrfs_root *csum_root; @@ -2999,10 +3106,13 @@ static int do_free_extent_accounting(struct btrfs_trans_handle *trans, return ret; } - ret = btrfs_add_to_free_space_tree(trans, bytenr, num_bytes); - if (unlikely(ret)) { - btrfs_abort_transaction(trans, ret); - return ret; + /* If remapped, FST has already been taken care of in remove_range_from_remap_tree(). */ + if (!remapped) { + ret = btrfs_add_to_free_space_tree(trans, bytenr, num_bytes); + if (unlikely(ret)) { + btrfs_abort_transaction(trans, ret); + return ret; + } } ret = btrfs_update_block_group(trans, bytenr, num_bytes, false); @@ -3361,7 +3471,7 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans, } btrfs_release_path(path); - ret = do_free_extent_accounting(trans, bytenr, &delta); + ret = do_free_extent_accounting(trans, bytenr, &delta, path); } btrfs_release_path(path); @@ -3462,12 +3572,12 @@ int btrfs_free_tree_block(struct btrfs_trans_handle *trans, return 0; if (btrfs_header_generation(buf) != trans->transid) - goto out; + return 0; if (root_id != BTRFS_TREE_LOG_OBJECTID) { ret = check_ref_cleanup(trans, buf->start); if (!ret) - goto out; + return 0; } bg = btrfs_lookup_block_group(fs_info, buf->start); @@ -3475,7 +3585,7 @@ int btrfs_free_tree_block(struct btrfs_trans_handle *trans, if (btrfs_header_flag(buf, BTRFS_HEADER_FLAG_WRITTEN)) { pin_down_extent(trans, bg, buf->start, buf->len, true); btrfs_put_block_group(bg); - goto out; + return 0; } /* @@ -3499,7 +3609,7 @@ int btrfs_free_tree_block(struct btrfs_trans_handle *trans, || btrfs_is_zoned(fs_info)) { pin_down_extent(trans, bg, buf->start, buf->len, true); btrfs_put_block_group(bg); - goto out; + return 0; } WARN_ON(test_bit(EXTENT_BUFFER_DIRTY, &buf->bflags)); @@ -3509,7 +3619,6 @@ int btrfs_free_tree_block(struct btrfs_trans_handle *trans, btrfs_put_block_group(bg); trace_btrfs_reserved_extent_free(fs_info, buf->start, buf->len); -out: return 0; } @@ -4191,10 +4300,8 @@ static int find_free_extent_update_loop(struct btrfs_fs_info *fs_info, else trans = btrfs_join_transaction(root); - if (IS_ERR(trans)) { - ret = PTR_ERR(trans); - return ret; - } + if (IS_ERR(trans)) + return PTR_ERR(trans); ret = btrfs_chunk_alloc(trans, space_info, ffe_ctl->flags, CHUNK_ALLOC_FORCE_FOR_EXTENT); @@ -4288,36 +4395,43 @@ static int prepare_allocation_zoned(struct btrfs_fs_info *fs_info, struct find_free_extent_ctl *ffe_ctl, struct btrfs_space_info *space_info) { + struct btrfs_block_group *block_group; + if (ffe_ctl->for_treelog) { spin_lock(&fs_info->treelog_bg_lock); if (fs_info->treelog_bg) ffe_ctl->hint_byte = fs_info->treelog_bg; spin_unlock(&fs_info->treelog_bg_lock); - } else if (ffe_ctl->for_data_reloc) { + return 0; + } + + if (ffe_ctl->for_data_reloc) { spin_lock(&fs_info->relocation_bg_lock); if (fs_info->data_reloc_bg) ffe_ctl->hint_byte = fs_info->data_reloc_bg; spin_unlock(&fs_info->relocation_bg_lock); - } else if (ffe_ctl->flags & BTRFS_BLOCK_GROUP_DATA) { - struct btrfs_block_group *block_group; + return 0; + } - spin_lock(&fs_info->zone_active_bgs_lock); - list_for_each_entry(block_group, &fs_info->zone_active_bgs, active_bg_list) { - /* - * No lock is OK here because avail is monotonically - * decreasing, and this is just a hint. - */ - u64 avail = block_group->zone_capacity - block_group->alloc_offset; + if (!(ffe_ctl->flags & BTRFS_BLOCK_GROUP_DATA)) + return 0; - if (block_group_bits(block_group, ffe_ctl->flags) && - block_group->space_info == space_info && - avail >= ffe_ctl->num_bytes) { - ffe_ctl->hint_byte = block_group->start; - break; - } + spin_lock(&fs_info->zone_active_bgs_lock); + list_for_each_entry(block_group, &fs_info->zone_active_bgs, active_bg_list) { + /* + * No lock is OK here because avail is monotonically + * decreasing, and this is just a hint. + */ + u64 avail = block_group->zone_capacity - block_group->alloc_offset; + + if (block_group_bits(block_group, ffe_ctl->flags) && + block_group->space_info == space_info && + avail >= ffe_ctl->num_bytes) { + ffe_ctl->hint_byte = block_group->start; + break; } - spin_unlock(&fs_info->zone_active_bgs_lock); } + spin_unlock(&fs_info->zone_active_bgs_lock); return 0; } @@ -4441,7 +4555,8 @@ static noinline int find_free_extent(struct btrfs_root *root, block_group->cached != BTRFS_CACHE_NO) { down_read(&space_info->groups_sem); if (list_empty(&block_group->list) || - block_group->ro) { + block_group->ro || + (block_group->flags & BTRFS_BLOCK_GROUP_REMAPPED)) { /* * someone is removing this block group, * we can't jump into the have_block_group @@ -4475,7 +4590,8 @@ search: ffe_ctl->hinted = false; /* If the block group is read-only, we can skip it entirely. */ - if (unlikely(block_group->ro)) { + if (unlikely(block_group->ro || + (block_group->flags & BTRFS_BLOCK_GROUP_REMAPPED))) { if (ffe_ctl->for_treelog) btrfs_clear_treelog_bg(block_group); if (ffe_ctl->for_data_reloc) @@ -4562,7 +4678,7 @@ have_block_group: /* move on to the next group */ if (ffe_ctl->search_start + ffe_ctl->num_bytes > - block_group->start + block_group->length) { + btrfs_block_group_end(block_group)) { btrfs_add_free_space_unused(block_group, ffe_ctl->found_offset, ffe_ctl->num_bytes); @@ -4883,6 +4999,9 @@ static int alloc_reserved_tree_block(struct btrfs_trans_handle *trans, int level = btrfs_delayed_ref_owner(node); bool skinny_metadata = btrfs_fs_incompat(fs_info, SKINNY_METADATA); + if (unlikely(node->ref_root == BTRFS_REMAP_TREE_OBJECTID)) + goto skip; + extent_key.objectid = node->bytenr; if (skinny_metadata) { /* The owner of a tree block is the level. */ @@ -4935,6 +5054,7 @@ static int alloc_reserved_tree_block(struct btrfs_trans_handle *trans, btrfs_free_path(path); +skip: return alloc_reserved_extent(trans, node->bytenr, fs_info->nodesize); } @@ -5263,7 +5383,6 @@ struct walk_control { * @root: the root we are currently deleting * @wc: the walk control for this deletion * @eb: the parent eb that we're currently visiting - * @refs: the number of refs for wc->level - 1 * @flags: the flags for wc->level - 1 * @slot: the slot in the eb that we're currently checking * @@ -5458,12 +5577,12 @@ static noinline int walk_down_proc(struct btrfs_trans_handle *trans, /* wc->stage == UPDATE_BACKREF */ if (!(wc->flags[level] & flag)) { ASSERT(path->locks[level]); - ret = btrfs_inc_ref(trans, root, eb, 1); + ret = btrfs_inc_ref(trans, root, eb, true); if (unlikely(ret)) { btrfs_abort_transaction(trans, ret); return ret; } - ret = btrfs_dec_ref(trans, root, eb, 0); + ret = btrfs_dec_ref(trans, root, eb, false); if (unlikely(ret)) { btrfs_abort_transaction(trans, ret); return ret; @@ -5864,18 +5983,12 @@ static noinline int walk_up_proc(struct btrfs_trans_handle *trans, if (wc->refs[level] == 1) { if (level == 0) { - if (wc->flags[level] & BTRFS_BLOCK_FLAG_FULL_BACKREF) { - ret = btrfs_dec_ref(trans, root, eb, 1); - if (ret) { - btrfs_abort_transaction(trans, ret); - return ret; - } - } else { - ret = btrfs_dec_ref(trans, root, eb, 0); - if (unlikely(ret)) { - btrfs_abort_transaction(trans, ret); - return ret; - } + const bool full_backref = (wc->flags[level] & BTRFS_BLOCK_FLAG_FULL_BACKREF); + + ret = btrfs_dec_ref(trans, root, eb, full_backref); + if (unlikely(ret)) { + btrfs_abort_transaction(trans, ret); + return ret; } if (btrfs_is_fstree(btrfs_root_id(root))) { ret = btrfs_qgroup_trace_leaf_items(trans, eb); @@ -6400,10 +6513,12 @@ void btrfs_error_unpin_extent_range(struct btrfs_fs_info *fs_info, u64 start, u6 * it while performing the free space search since we have already * held back allocations. */ -static int btrfs_trim_free_extents(struct btrfs_device *device, u64 *trimmed) +static int btrfs_trim_free_extents_throttle(struct btrfs_device *device, + u64 *trimmed, u64 pos, u64 *ret_next_pos) { - u64 start = BTRFS_DEVICE_RANGE_RESERVED, len = 0, end = 0; int ret; + u64 start = pos; + u64 trim_len = 0; *trimmed = 0; @@ -6423,15 +6538,20 @@ static int btrfs_trim_free_extents(struct btrfs_device *device, u64 *trimmed) while (1) { struct btrfs_fs_info *fs_info = device->fs_info; + u64 cur_start; + u64 end; + u64 len; u64 bytes; ret = mutex_lock_interruptible(&fs_info->chunk_mutex); if (ret) break; + cur_start = start; btrfs_find_first_clear_extent_bit(&device->alloc_state, start, &start, &end, CHUNK_TRIMMED | CHUNK_ALLOCATED); + start = max(start, cur_start); /* Check if there are any CHUNK_* bits left */ if (start > device->total_bytes) { @@ -6457,6 +6577,7 @@ static int btrfs_trim_free_extents(struct btrfs_device *device, u64 *trimmed) end = min(end, device->total_bytes - 1); len = end - start + 1; + len = min(len, BTRFS_MAX_TRIM_LENGTH); /* We didn't find any extents */ if (!len) { @@ -6477,6 +6598,12 @@ static int btrfs_trim_free_extents(struct btrfs_device *device, u64 *trimmed) start += len; *trimmed += bytes; + trim_len += len; + if (trim_len >= BTRFS_MAX_TRIM_LENGTH) { + *ret_next_pos = start; + ret = -EAGAIN; + break; + } if (btrfs_trim_interrupted()) { ret = -ERESTARTSYS; @@ -6489,20 +6616,134 @@ static int btrfs_trim_free_extents(struct btrfs_device *device, u64 *trimmed) return ret; } +static int btrfs_trim_free_extents(struct btrfs_fs_info *fs_info, u64 *trimmed, + u64 *dev_failed, int *dev_ret) +{ + struct btrfs_device *dev; + struct btrfs_device *working_dev = NULL; + struct btrfs_fs_devices *fs_devices = fs_info->fs_devices; + u8 uuid[BTRFS_UUID_SIZE]; + u64 start = BTRFS_DEVICE_RANGE_RESERVED; + + *trimmed = 0; + *dev_failed = 0; + *dev_ret = 0; + + /* Find the device with the smallest UUID to start. */ + mutex_lock(&fs_devices->device_list_mutex); + list_for_each_entry(dev, &fs_devices->devices, dev_list) { + if (test_bit(BTRFS_DEV_STATE_MISSING, &dev->dev_state)) + continue; + if (!working_dev || + memcmp(dev->uuid, working_dev->uuid, BTRFS_UUID_SIZE) < 0) + working_dev = dev; + } + if (working_dev) + memcpy(uuid, working_dev->uuid, BTRFS_UUID_SIZE); + mutex_unlock(&fs_devices->device_list_mutex); + + if (!working_dev) + return 0; + + while (1) { + u64 group_trimmed = 0; + u64 next_pos = 0; + int ret = 0; + + mutex_lock(&fs_devices->device_list_mutex); + + /* Find and trim the current device. */ + list_for_each_entry(dev, &fs_devices->devices, dev_list) { + if (test_bit(BTRFS_DEV_STATE_MISSING, &dev->dev_state)) + continue; + if (dev == working_dev) { + ret = btrfs_trim_free_extents_throttle(working_dev, + &group_trimmed, start, &next_pos); + break; + } + } + + /* Throttle: continue the same device from the new position. */ + if (ret == -EAGAIN && next_pos > start) { + mutex_unlock(&fs_devices->device_list_mutex); + *trimmed += group_trimmed; + start = next_pos; + cond_resched(); + continue; + } + + /* User interrupted. */ + if (ret == -ERESTARTSYS || ret == -EINTR) { + mutex_unlock(&fs_devices->device_list_mutex); + *trimmed += group_trimmed; + return ret; + } + + /* + * Device completed (ret == 0), failed, or EAGAIN with no progress. + * Record error if any, then move to next device. + */ + if (ret == -EAGAIN) { + /* No progress - log and skip device. */ + btrfs_warn(fs_info, + "trim throttle: no progress, offset=%llu device %s, skipping", + start, btrfs_dev_name(working_dev)); + (*dev_failed)++; + if (!*dev_ret) + *dev_ret = ret; + } else if (ret) { + /* Device failed with error. */ + (*dev_failed)++; + if (!*dev_ret) + *dev_ret = ret; + } + + /* + * Find next device: smallest UUID larger than current. + * Devices added during trim with smaller UUID will be skipped. + */ + working_dev = NULL; + list_for_each_entry(dev, &fs_devices->devices, dev_list) { + if (test_bit(BTRFS_DEV_STATE_MISSING, &dev->dev_state)) + continue; + /* Must larger than current UUID. */ + if (memcmp(dev->uuid, uuid, BTRFS_UUID_SIZE) <= 0) + continue; + /* Find the smallest. */ + if (!working_dev || + memcmp(dev->uuid, working_dev->uuid, BTRFS_UUID_SIZE) < 0) + working_dev = dev; + } + if (working_dev) + memcpy(uuid, working_dev->uuid, BTRFS_UUID_SIZE); + + mutex_unlock(&fs_devices->device_list_mutex); + + *trimmed += group_trimmed; + start = BTRFS_DEVICE_RANGE_RESERVED; + + /* No more devices. */ + if (!working_dev) + break; + + cond_resched(); + } + + return 0; +} + /* * Trim the whole filesystem by: * 1) trimming the free space in each block group * 2) trimming the unallocated space on each device * * This will also continue trimming even if a block group or device encounters - * an error. The return value will be the last error, or 0 if nothing bad + * an error. The return value will be the first error, or 0 if nothing bad * happens. */ int btrfs_trim_fs(struct btrfs_fs_info *fs_info, struct fstrim_range *range) { - struct btrfs_fs_devices *fs_devices = fs_info->fs_devices; struct btrfs_block_group *cache = NULL; - struct btrfs_device *device; u64 group_trimmed; u64 range_end = U64_MAX; u64 start; @@ -6533,14 +6774,15 @@ int btrfs_trim_fs(struct btrfs_fs_info *fs_info, struct fstrim_range *range) } start = max(range->start, cache->start); - end = min(range_end, cache->start + cache->length); + end = min(range_end, btrfs_block_group_end(cache)); if (end - start >= range->minlen) { if (!btrfs_block_group_done(cache)) { ret = btrfs_cache_block_group(cache, true); if (ret) { bg_failed++; - bg_ret = ret; + if (!bg_ret) + bg_ret = ret; continue; } } @@ -6551,9 +6793,14 @@ int btrfs_trim_fs(struct btrfs_fs_info *fs_info, struct fstrim_range *range) range->minlen); trimmed += group_trimmed; + if (ret == -ERESTARTSYS || ret == -EINTR) { + btrfs_put_block_group(cache); + break; + } if (ret) { bg_failed++; - bg_ret = ret; + if (!bg_ret) + bg_ret = ret; continue; } } @@ -6561,30 +6808,22 @@ int btrfs_trim_fs(struct btrfs_fs_info *fs_info, struct fstrim_range *range) if (bg_failed) btrfs_warn(fs_info, - "failed to trim %llu block group(s), last error %d", + "failed to trim %llu block group(s), first error %d", bg_failed, bg_ret); - mutex_lock(&fs_devices->device_list_mutex); - list_for_each_entry(device, &fs_devices->devices, dev_list) { - if (test_bit(BTRFS_DEV_STATE_MISSING, &device->dev_state)) - continue; + if (ret == -ERESTARTSYS || ret == -EINTR) + return ret; - ret = btrfs_trim_free_extents(device, &group_trimmed); - - trimmed += group_trimmed; - if (ret) { - dev_failed++; - dev_ret = ret; - break; - } - } - mutex_unlock(&fs_devices->device_list_mutex); + ret = btrfs_trim_free_extents(fs_info, &group_trimmed, &dev_failed, &dev_ret); + trimmed += group_trimmed; if (dev_failed) btrfs_warn(fs_info, - "failed to trim %llu device(s), last error %d", + "failed to trim %llu device(s), first error %d", dev_failed, dev_ret); range->len = trimmed; + if (ret == -ERESTARTSYS || ret == -EINTR) + return ret; if (bg_ret) return bg_ret; return dev_ret; diff --git a/fs/btrfs/extent-tree.h b/fs/btrfs/extent-tree.h index 71bb8109c969..ff330d4896d6 100644 --- a/fs/btrfs/extent-tree.h +++ b/fs/btrfs/extent-tree.h @@ -161,7 +161,9 @@ int btrfs_drop_subtree(struct btrfs_trans_handle *trans, struct extent_buffer *parent); void btrfs_error_unpin_extent_range(struct btrfs_fs_info *fs_info, u64 start, u64 end); int btrfs_discard_extent(struct btrfs_fs_info *fs_info, u64 bytenr, - u64 num_bytes, u64 *actual_bytes); + u64 num_bytes, u64 *actual_bytes, bool do_remap); int btrfs_trim_fs(struct btrfs_fs_info *fs_info, struct fstrim_range *range); +void btrfs_handle_fully_remapped_bgs(struct btrfs_fs_info *fs_info); +int btrfs_complete_bg_remapping(struct btrfs_block_group *bg); #endif diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index a4b74023618d..744a1fff6eef 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -440,8 +440,7 @@ again: loops = 1; goto again; } else { - found = false; - goto out_failed; + return false; } } @@ -461,7 +460,7 @@ again: } *start = delalloc_start; *end = delalloc_end; -out_failed: + return found; } @@ -476,25 +475,25 @@ void extent_clear_unlock_delalloc(struct btrfs_inode *inode, u64 start, u64 end, end, page_ops); } -static bool btrfs_verify_folio(struct folio *folio, u64 start, u32 len) +static bool btrfs_verify_folio(struct fsverity_info *vi, struct folio *folio, + u64 start, u32 len) { struct btrfs_fs_info *fs_info = folio_to_fs_info(folio); - if (!fsverity_active(folio->mapping->host) || - btrfs_folio_test_uptodate(fs_info, folio, start, len) || - start >= i_size_read(folio->mapping->host)) + if (!vi || btrfs_folio_test_uptodate(fs_info, folio, start, len)) return true; - return fsverity_verify_folio(folio); + return fsverity_verify_folio(vi, folio); } -static void end_folio_read(struct folio *folio, bool uptodate, u64 start, u32 len) +static void end_folio_read(struct fsverity_info *vi, struct folio *folio, + bool uptodate, u64 start, u32 len) { struct btrfs_fs_info *fs_info = folio_to_fs_info(folio); ASSERT(folio_pos(folio) <= start && start + len <= folio_next_pos(folio)); - if (uptodate && btrfs_verify_folio(folio, start, len)) + if (uptodate && btrfs_verify_folio(vi, folio, start, len)) btrfs_folio_set_uptodate(fs_info, folio, start, len); else btrfs_folio_clear_uptodate(fs_info, folio, start, len); @@ -574,14 +573,19 @@ static void begin_folio_read(struct btrfs_fs_info *fs_info, struct folio *folio) static void end_bbio_data_read(struct btrfs_bio *bbio) { struct btrfs_fs_info *fs_info = bbio->inode->root->fs_info; + struct inode *inode = &bbio->inode->vfs_inode; struct bio *bio = &bbio->bio; + struct fsverity_info *vi = NULL; struct folio_iter fi; ASSERT(!bio_flagged(bio, BIO_CLONED)); + + if (bbio->file_offset < i_size_read(inode)) + vi = fsverity_get_info(inode); + bio_for_each_folio_all(fi, &bbio->bio) { bool uptodate = !bio->bi_status; struct folio *folio = fi.folio; - struct inode *inode = folio->mapping->host; u64 start = folio_pos(folio) + fi.offset; btrfs_debug(fs_info, @@ -616,7 +620,7 @@ static void end_bbio_data_read(struct btrfs_bio *bbio) } /* Update page status and unlock. */ - end_folio_read(folio, uptodate, start, fi.length); + end_folio_read(vi, folio, uptodate, start, fi.length); } bio_put(bio); } @@ -970,7 +974,7 @@ static void btrfs_readahead_expand(struct readahead_control *ractl, { const u64 ra_pos = readahead_pos(ractl); const u64 ra_end = ra_pos + readahead_length(ractl); - const u64 em_end = em->start + em->len; + const u64 em_end = btrfs_extent_map_end(em); /* No expansion for holes and inline extents. */ if (em->disk_bytenr > EXTENT_MAP_LAST_BYTE) @@ -991,18 +995,25 @@ static void btrfs_readahead_expand(struct readahead_control *ractl, * return 0 on success, otherwise return error */ static int btrfs_do_readpage(struct folio *folio, struct extent_map **em_cached, - struct btrfs_bio_ctrl *bio_ctrl) + struct btrfs_bio_ctrl *bio_ctrl, + struct fsverity_info *vi) { struct inode *inode = folio->mapping->host; struct btrfs_fs_info *fs_info = inode_to_fs_info(inode); u64 start = folio_pos(folio); const u64 end = start + folio_size(folio) - 1; u64 extent_offset; + u64 locked_end; u64 last_byte = i_size_read(inode); struct extent_map *em; int ret = 0; const size_t blocksize = fs_info->sectorsize; + if (bio_ctrl->ractl) + locked_end = readahead_pos(bio_ctrl->ractl) + readahead_length(bio_ctrl->ractl) - 1; + else + locked_end = end; + ret = set_folio_extent_mapped(folio); if (ret < 0) { folio_unlock(folio); @@ -1029,16 +1040,23 @@ static int btrfs_do_readpage(struct folio *folio, struct extent_map **em_cached, ASSERT(IS_ALIGNED(cur, fs_info->sectorsize)); if (cur >= last_byte) { folio_zero_range(folio, pg_offset, end - cur + 1); - end_folio_read(folio, true, cur, end - cur + 1); + end_folio_read(vi, folio, true, cur, end - cur + 1); break; } if (btrfs_folio_test_uptodate(fs_info, folio, cur, blocksize)) { - end_folio_read(folio, true, cur, blocksize); + end_folio_read(vi, folio, true, cur, blocksize); continue; } - em = get_extent_map(BTRFS_I(inode), folio, cur, end - cur + 1, em_cached); + /* + * Search extent map for the whole locked range. + * This will allow btrfs_get_extent() to return a larger hole + * when possible. + * This can reduce duplicated btrfs_get_extent() calls for large + * holes. + */ + em = get_extent_map(BTRFS_I(inode), folio, cur, locked_end - cur + 1, em_cached); if (IS_ERR(em)) { - end_folio_read(folio, false, cur, end + 1 - cur); + end_folio_read(vi, folio, false, cur, end + 1 - cur); return PTR_ERR(em); } extent_offset = cur - em->start; @@ -1115,12 +1133,12 @@ static int btrfs_do_readpage(struct folio *folio, struct extent_map **em_cached, /* we've found a hole, just zero and go on */ if (block_start == EXTENT_MAP_HOLE) { folio_zero_range(folio, pg_offset, blocksize); - end_folio_read(folio, true, cur, blocksize); + end_folio_read(vi, folio, true, cur, blocksize); continue; } /* the get_extent function already copied into the folio */ if (block_start == EXTENT_MAP_INLINE) { - end_folio_read(folio, true, cur, blocksize); + end_folio_read(vi, folio, true, cur, blocksize); continue; } @@ -1317,7 +1335,8 @@ again: int btrfs_read_folio(struct file *file, struct folio *folio) { - struct btrfs_inode *inode = folio_to_inode(folio); + struct inode *vfs_inode = folio->mapping->host; + struct btrfs_inode *inode = BTRFS_I(vfs_inode); const u64 start = folio_pos(folio); const u64 end = start + folio_size(folio) - 1; struct extent_state *cached_state = NULL; @@ -1326,10 +1345,13 @@ int btrfs_read_folio(struct file *file, struct folio *folio) .last_em_start = U64_MAX, }; struct extent_map *em_cached = NULL; + struct fsverity_info *vi = NULL; int ret; lock_extents_for_read(inode, start, end, &cached_state); - ret = btrfs_do_readpage(folio, &em_cached, &bio_ctrl); + if (folio_pos(folio) < i_size_read(vfs_inode)) + vi = fsverity_get_info(vfs_inode); + ret = btrfs_do_readpage(folio, &em_cached, &bio_ctrl, vi); btrfs_unlock_extent(&inode->io_tree, start, end, &cached_state); btrfs_free_extent_map(em_cached); @@ -1426,8 +1448,9 @@ static noinline_for_stack int writepage_delalloc(struct btrfs_inode *inode, u64 delalloc_start = page_start; u64 delalloc_end = page_end; u64 delalloc_to_write = 0; + unsigned int start_bit; + unsigned int end_bit; int ret = 0; - int bit; /* Save the dirty bitmap as our submission bitmap will be a subset of it. */ if (btrfs_is_subpage(fs_info, folio)) { @@ -1437,10 +1460,12 @@ static noinline_for_stack int writepage_delalloc(struct btrfs_inode *inode, bio_ctrl->submit_bitmap = 1; } - for_each_set_bit(bit, &bio_ctrl->submit_bitmap, blocks_per_folio) { - u64 start = page_start + (bit << fs_info->sectorsize_bits); + for_each_set_bitrange(start_bit, end_bit, &bio_ctrl->submit_bitmap, + blocks_per_folio) { + u64 start = page_start + (start_bit << fs_info->sectorsize_bits); + u32 len = (end_bit - start_bit) << fs_info->sectorsize_bits; - btrfs_folio_set_lock(fs_info, folio, start, fs_info->sectorsize); + btrfs_folio_set_lock(fs_info, folio, start, len); } /* Lock all (subpage) delalloc ranges inside the folio first. */ @@ -1557,10 +1582,13 @@ static noinline_for_stack int writepage_delalloc(struct btrfs_inode *inode, fs_info->sectorsize_bits, blocks_per_folio); - for_each_set_bit(bit, &bio_ctrl->submit_bitmap, bitmap_size) - btrfs_mark_ordered_io_finished(inode, folio, - page_start + (bit << fs_info->sectorsize_bits), - fs_info->sectorsize, false); + for_each_set_bitrange(start_bit, end_bit, &bio_ctrl->submit_bitmap, + bitmap_size) { + u64 start = page_start + (start_bit << fs_info->sectorsize_bits); + u32 len = (end_bit - start_bit) << fs_info->sectorsize_bits; + + btrfs_mark_ordered_io_finished(inode, folio, start, len, false); + } return ret; } out: @@ -1598,7 +1626,7 @@ out: /* * Return 0 if we have submitted or queued the sector for submission. - * Return <0 for critical errors, and the sector will have its dirty flag cleared. + * Return <0 for critical errors, and the involved sector will be cleaned up. * * Caller should make sure filepos < i_size and handle filepos >= i_size case. */ @@ -1623,6 +1651,13 @@ static int submit_one_sector(struct btrfs_inode *inode, em = btrfs_get_extent(inode, NULL, filepos, sectorsize); if (IS_ERR(em)) { /* + * bio_ctrl may contain a bio crossing several folios. + * Submit it immediately so that the bio has a chance + * to finish normally, other than marked as error. + */ + submit_one_bio(bio_ctrl); + + /* * When submission failed, we should still clear the folio dirty. * Or the folio will be written back again but without any * ordered extent. @@ -1630,6 +1665,13 @@ static int submit_one_sector(struct btrfs_inode *inode, btrfs_folio_clear_dirty(fs_info, folio, filepos, sectorsize); btrfs_folio_set_writeback(fs_info, folio, filepos, sectorsize); btrfs_folio_clear_writeback(fs_info, folio, filepos, sectorsize); + + /* + * Since there is no bio submitted to finish the ordered + * extent, we have to manually finish this sector. + */ + btrfs_mark_ordered_io_finished(inode, folio, filepos, + fs_info->sectorsize, false); return PTR_ERR(em); } @@ -1714,8 +1756,8 @@ static noinline_for_stack int extent_writepage_io(struct btrfs_inode *inode, return ret; } - for (cur = start; cur < end; cur += fs_info->sectorsize) - set_bit((cur - folio_start) >> fs_info->sectorsize_bits, &range_bitmap); + bitmap_set(&range_bitmap, (start - folio_pos(folio)) >> fs_info->sectorsize_bits, + len >> fs_info->sectorsize_bits); bitmap_and(&bio_ctrl->submit_bitmap, &bio_ctrl->submit_bitmap, &range_bitmap, blocks_per_folio); @@ -1756,19 +1798,6 @@ static noinline_for_stack int extent_writepage_io(struct btrfs_inode *inode, } ret = submit_one_sector(inode, folio, cur, bio_ctrl, i_size); if (unlikely(ret < 0)) { - /* - * bio_ctrl may contain a bio crossing several folios. - * Submit it immediately so that the bio has a chance - * to finish normally, other than marked as error. - */ - submit_one_bio(bio_ctrl); - /* - * Failed to grab the extent map which should be very rare. - * Since there is no bio submitted to finish the ordered - * extent, we have to manually finish this sector. - */ - btrfs_mark_ordered_io_finished(inode, folio, cur, - fs_info->sectorsize, false); if (!found_error) found_error = ret; continue; @@ -2286,8 +2315,7 @@ void btrfs_btree_wait_writeback_range(struct btrfs_fs_info *fs_info, u64 start, } } -int btree_write_cache_pages(struct address_space *mapping, - struct writeback_control *wbc) +int btree_writepages(struct address_space *mapping, struct writeback_control *wbc) { struct btrfs_eb_write_context ctx = { .wbc = wbc }; struct btrfs_fs_info *fs_info = inode_to_fs_info(mapping->host); @@ -2696,16 +2724,19 @@ void btrfs_readahead(struct readahead_control *rac) .last_em_start = U64_MAX, }; struct folio *folio; - struct btrfs_inode *inode = BTRFS_I(rac->mapping->host); + struct inode *vfs_inode = rac->mapping->host; + struct btrfs_inode *inode = BTRFS_I(vfs_inode); const u64 start = readahead_pos(rac); const u64 end = start + readahead_length(rac) - 1; struct extent_state *cached_state = NULL; struct extent_map *em_cached = NULL; + struct fsverity_info *vi = NULL; lock_extents_for_read(inode, start, end, &cached_state); - + if (start < i_size_read(vfs_inode)) + vi = fsverity_get_info(vfs_inode); while ((folio = readahead_folio(rac)) != NULL) - btrfs_do_readpage(folio, &em_cached, &bio_ctrl); + btrfs_do_readpage(folio, &em_cached, &bio_ctrl, vi); btrfs_unlock_extent(&inode->io_tree, start, end, &cached_state); diff --git a/fs/btrfs/extent_io.h b/fs/btrfs/extent_io.h index 02ebb2f238af..73571d5d3d5a 100644 --- a/fs/btrfs/extent_io.h +++ b/fs/btrfs/extent_io.h @@ -237,8 +237,7 @@ void extent_write_locked_range(struct inode *inode, const struct folio *locked_f u64 start, u64 end, struct writeback_control *wbc, bool pages_dirty); int btrfs_writepages(struct address_space *mapping, struct writeback_control *wbc); -int btree_write_cache_pages(struct address_space *mapping, - struct writeback_control *wbc); +int btree_writepages(struct address_space *mapping, struct writeback_control *wbc); void btrfs_btree_wait_writeback_range(struct btrfs_fs_info *fs_info, u64 start, u64 end); void btrfs_readahead(struct readahead_control *rac); int set_folio_extent_mapped(struct folio *folio); diff --git a/fs/btrfs/extent_map.c b/fs/btrfs/extent_map.c index 7e38c23a0c1c..095a561d733f 100644 --- a/fs/btrfs/extent_map.c +++ b/fs/btrfs/extent_map.c @@ -319,8 +319,15 @@ static void dump_extent_map(struct btrfs_fs_info *fs_info, const char *prefix, /* Internal sanity checks for btrfs debug builds. */ static void validate_extent_map(struct btrfs_fs_info *fs_info, struct extent_map *em) { + const u32 blocksize = fs_info->sectorsize; + if (!IS_ENABLED(CONFIG_BTRFS_DEBUG)) return; + + if (!IS_ALIGNED(em->start, blocksize) || + !IS_ALIGNED(em->len, blocksize)) + dump_extent_map(fs_info, "unaligned start offset or length members", em); + if (em->disk_bytenr < EXTENT_MAP_LAST_BYTE) { if (em->disk_num_bytes == 0) dump_extent_map(fs_info, "zero disk_num_bytes", em); @@ -334,6 +341,11 @@ static void validate_extent_map(struct btrfs_fs_info *fs_info, struct extent_map dump_extent_map(fs_info, "ram_bytes mismatch with disk_num_bytes for non-compressed em", em); + if (!IS_ALIGNED(em->disk_bytenr, blocksize) || + !IS_ALIGNED(em->disk_num_bytes, blocksize) || + !IS_ALIGNED(em->offset, blocksize) || + !IS_ALIGNED(em->ram_bytes, blocksize)) + dump_extent_map(fs_info, "unaligned members", em); } else if (em->offset) { dump_extent_map(fs_info, "non-zero offset for hole/inline", em); } diff --git a/fs/btrfs/file-item.c b/fs/btrfs/file-item.c index 14e5257f0f04..7bd715442f3e 100644 --- a/fs/btrfs/file-item.c +++ b/fs/btrfs/file-item.c @@ -8,7 +8,6 @@ #include <linux/pagemap.h> #include <linux/highmem.h> #include <linux/sched/mm.h> -#include <crypto/hash.h> #include "messages.h" #include "ctree.h" #include "disk-io.h" @@ -769,7 +768,6 @@ static void csum_one_bio(struct btrfs_bio *bbio, struct bvec_iter *src) { struct btrfs_inode *inode = bbio->inode; struct btrfs_fs_info *fs_info = inode->root->fs_info; - SHASH_DESC_ON_STACK(shash, fs_info->csum_shash); struct bio *bio = &bbio->bio; struct btrfs_ordered_sum *sums = bbio->sums; struct bvec_iter iter = *src; @@ -781,8 +779,6 @@ static void csum_one_bio(struct btrfs_bio *bbio, struct bvec_iter *src) u32 offset = 0; int index = 0; - shash->tfm = fs_info->csum_shash; - btrfs_bio_for_each_block(paddr, bio, &iter, step) { paddrs[(offset / step) % nr_steps] = paddr; offset += step; @@ -1138,7 +1134,7 @@ again: } ret = PTR_ERR(item); if (ret != -EFBIG && ret != -ENOENT) - goto out; + return ret; if (ret == -EFBIG) { u32 item_size; @@ -1154,7 +1150,7 @@ again: /* We didn't find a csum item, insert one. */ ret = find_next_csum_offset(root, path, &next_offset); if (ret < 0) - goto out; + return ret; found_next = 1; goto insert; } @@ -1182,7 +1178,7 @@ again: csum_size, 1); path->search_for_extension = false; if (ret < 0) - goto out; + return ret; if (ret > 0) { if (path->slots[0] == 0) @@ -1238,14 +1234,14 @@ extend_csum: btrfs_header_nritems(path->nodes[0])) { ret = find_next_csum_offset(root, path, &next_offset); if (ret < 0) - goto out; + return ret; found_next = 1; goto insert; } ret = find_next_csum_offset(root, path, &next_offset); if (ret < 0) - goto out; + return ret; tmp = (next_offset - bytenr) >> fs_info->sectorsize_bits; if (tmp <= INT_MAX) @@ -1286,7 +1282,7 @@ insert: ret = btrfs_insert_empty_item(trans, root, path, &file_key, ins_size); if (ret < 0) - goto out; + return ret; leaf = path->nodes[0]; csum: item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_csum_item); @@ -1311,8 +1307,8 @@ found: cond_resched(); goto again; } -out: - return ret; + + return 0; } void btrfs_extent_item_to_extent_map(struct btrfs_inode *inode, diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index 1abc7ed2990e..acaa3dbd2b7b 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -10,6 +10,7 @@ #include <linux/string.h> #include <linux/backing-dev.h> #include <linux/falloc.h> +#include <linux/filelock.h> #include <linux/writeback.h> #include <linux/compat.h> #include <linux/slab.h> @@ -565,7 +566,7 @@ int btrfs_mark_extent_written(struct btrfs_trans_handle *trans, int del_nr = 0; int del_slot = 0; int recow; - int ret = 0; + int ret; u64 ino = btrfs_ino(inode); path = btrfs_alloc_path(); @@ -580,7 +581,7 @@ again: ret = btrfs_search_slot(trans, root, &key, path, -1, 1); if (ret < 0) - goto out; + return ret; if (ret > 0 && path->slots[0] > 0) path->slots[0]--; @@ -589,20 +590,20 @@ again: if (unlikely(key.objectid != ino || key.type != BTRFS_EXTENT_DATA_KEY)) { ret = -EINVAL; btrfs_abort_transaction(trans, ret); - goto out; + return ret; } fi = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_file_extent_item); if (unlikely(btrfs_file_extent_type(leaf, fi) != BTRFS_FILE_EXTENT_PREALLOC)) { ret = -EINVAL; btrfs_abort_transaction(trans, ret); - goto out; + return ret; } extent_end = key.offset + btrfs_file_extent_num_bytes(leaf, fi); if (unlikely(key.offset > start || extent_end < end)) { ret = -EINVAL; btrfs_abort_transaction(trans, ret); - goto out; + return ret; } bytenr = btrfs_file_extent_disk_bytenr(leaf, fi); @@ -632,7 +633,7 @@ again: trans->transid); btrfs_set_file_extent_num_bytes(leaf, fi, end - other_start); - goto out; + return 0; } } @@ -660,7 +661,7 @@ again: other_end - start); btrfs_set_file_extent_offset(leaf, fi, start - orig_offset); - goto out; + return 0; } } @@ -676,7 +677,7 @@ again: } if (unlikely(ret < 0)) { btrfs_abort_transaction(trans, ret); - goto out; + return ret; } leaf = path->nodes[0]; @@ -704,7 +705,7 @@ again: ret = btrfs_inc_extent_ref(trans, &ref); if (unlikely(ret)) { btrfs_abort_transaction(trans, ret); - goto out; + return ret; } if (split == start) { @@ -713,7 +714,7 @@ again: if (unlikely(start != key.offset)) { ret = -EINVAL; btrfs_abort_transaction(trans, ret); - goto out; + return ret; } path->slots[0]--; extent_end = end; @@ -744,7 +745,7 @@ again: ret = btrfs_free_extent(trans, &ref); if (unlikely(ret)) { btrfs_abort_transaction(trans, ret); - goto out; + return ret; } } other_start = 0; @@ -762,7 +763,7 @@ again: ret = btrfs_free_extent(trans, &ref); if (unlikely(ret)) { btrfs_abort_transaction(trans, ret); - goto out; + return ret; } } if (del_nr == 0) { @@ -783,11 +784,11 @@ again: ret = btrfs_del_items(trans, root, path, del_slot, del_nr); if (unlikely(ret < 0)) { btrfs_abort_transaction(trans, ret); - goto out; + return ret; } } -out: - return ret; + + return 0; } /* @@ -859,7 +860,7 @@ static noinline int prepare_one_folio(struct inode *inode, struct folio **folio_ fgf_t fgp_flags = (nowait ? FGP_WRITEBEGIN | FGP_NOWAIT : FGP_WRITEBEGIN) | fgf_set_order(write_bytes); struct folio *folio; - int ret = 0; + int ret; again: folio = __filemap_get_folio(inode->i_mapping, index, fgp_flags, mask); @@ -876,10 +877,8 @@ again: if (ret) { /* The folio is already unlocked. */ folio_put(folio); - if (!nowait && ret == -EAGAIN) { - ret = 0; + if (!nowait && ret == -EAGAIN) goto again; - } return ret; } *folio_ret = folio; @@ -1274,8 +1273,7 @@ again: btrfs_delalloc_release_extents(inode, reserved_len); release_space(inode, *data_reserved, reserved_start, reserved_len, only_release_metadata); - ret = extents_locked; - return ret; + return extents_locked; } copied = copy_folio_from_iter_atomic(folio, offset_in_folio(folio, start), @@ -1440,7 +1438,7 @@ ssize_t btrfs_do_write_iter(struct kiocb *iocb, struct iov_iter *from, struct btrfs_inode *inode = BTRFS_I(file_inode(file)); ssize_t num_written, num_sync; - if (unlikely(btrfs_is_shutdown(inode->root->fs_info))) + if (btrfs_is_shutdown(inode->root->fs_info)) return -EIO; /* * If the fs flips readonly due to some impossible error, although we @@ -2045,7 +2043,7 @@ static int btrfs_file_mmap_prepare(struct vm_area_desc *desc) struct file *filp = desc->file; struct address_space *mapping = filp->f_mapping; - if (unlikely(btrfs_is_shutdown(inode_to_fs_info(file_inode(filp))))) + if (btrfs_is_shutdown(inode_to_fs_info(file_inode(filp)))) return -EIO; if (!mapping->a_ops->read_folio) return -ENOEXEC; @@ -2198,10 +2196,11 @@ static int find_first_non_hole(struct btrfs_inode *inode, u64 *start, u64 *len) /* Hole or vacuum extent(only exists in no-hole mode) */ if (em->disk_bytenr == EXTENT_MAP_HOLE) { + const u64 em_end = btrfs_extent_map_end(em); + ret = 1; - *len = em->start + em->len > *start + *len ? - 0 : *start + *len - em->start - em->len; - *start = em->start + em->len; + *len = (em_end > *start + *len) ? 0 : (*start + *len - em_end); + *start = em_end; } btrfs_free_extent_map(em); return ret; @@ -2950,7 +2949,7 @@ static int btrfs_zero_range(struct inode *inode, * new prealloc extent, so that we get a larger contiguous disk extent. */ if (em->start <= alloc_start && (em->flags & EXTENT_FLAG_PREALLOC)) { - const u64 em_end = em->start + em->len; + const u64 em_end = btrfs_extent_map_end(em); if (em_end >= offset + len) { /* @@ -3116,7 +3115,7 @@ static long btrfs_fallocate(struct file *file, int mode, int blocksize = BTRFS_I(inode)->root->fs_info->sectorsize; int ret; - if (unlikely(btrfs_is_shutdown(inode_to_fs_info(inode)))) + if (btrfs_is_shutdown(inode_to_fs_info(inode))) return -EIO; /* Do not allow fallocate in ZONED mode */ @@ -3810,7 +3809,7 @@ static int btrfs_file_open(struct inode *inode, struct file *filp) { int ret; - if (unlikely(btrfs_is_shutdown(inode_to_fs_info(inode)))) + if (btrfs_is_shutdown(inode_to_fs_info(inode))) return -EIO; filp->f_mode |= FMODE_NOWAIT | FMODE_CAN_ODIRECT; @@ -3825,7 +3824,7 @@ static ssize_t btrfs_file_read_iter(struct kiocb *iocb, struct iov_iter *to) { ssize_t ret = 0; - if (unlikely(btrfs_is_shutdown(inode_to_fs_info(file_inode(iocb->ki_filp))))) + if (btrfs_is_shutdown(inode_to_fs_info(file_inode(iocb->ki_filp)))) return -EIO; if (iocb->ki_flags & IOCB_DIRECT) { @@ -3842,7 +3841,7 @@ static ssize_t btrfs_file_splice_read(struct file *in, loff_t *ppos, struct pipe_inode_info *pipe, size_t len, unsigned int flags) { - if (unlikely(btrfs_is_shutdown(inode_to_fs_info(file_inode(in))))) + if (btrfs_is_shutdown(inode_to_fs_info(file_inode(in)))) return -EIO; return filemap_splice_read(in, ppos, pipe, len, flags); @@ -3867,6 +3866,7 @@ const struct file_operations btrfs_file_operations = { .remap_file_range = btrfs_remap_file_range, .uring_cmd = btrfs_uring_cmd, .fop_flags = FOP_BUFFER_RASYNC | FOP_BUFFER_WASYNC, + .setlease = generic_setlease, }; int btrfs_fdatawrite_range(struct btrfs_inode *inode, loff_t start, loff_t end) diff --git a/fs/btrfs/free-space-cache.c b/fs/btrfs/free-space-cache.c index f0f72850fab2..cc075a460a22 100644 --- a/fs/btrfs/free-space-cache.c +++ b/fs/btrfs/free-space-cache.c @@ -29,6 +29,7 @@ #include "file-item.h" #include "file.h" #include "super.h" +#include "relocation.h" #define BITS_PER_BITMAP (PAGE_SIZE * 8UL) #define MAX_CACHE_BYTES_PER_GIG SZ_64K @@ -1079,7 +1080,7 @@ int write_cache_extent_entries(struct btrfs_io_ctl *io_ctl, struct btrfs_trim_range *trim_entry; /* Get the cluster for this block_group if it exists */ - if (block_group && !list_empty(&block_group->cluster_list)) { + if (!list_empty(&block_group->cluster_list)) { cluster = list_first_entry(&block_group->cluster_list, struct btrfs_free_cluster, block_group_list); } @@ -1161,7 +1162,7 @@ update_cache_item(struct btrfs_trans_handle *trans, if (ret < 0) { btrfs_clear_extent_bit(&BTRFS_I(inode)->io_tree, 0, inode->i_size - 1, EXTENT_DELALLOC, NULL); - goto fail; + return ret; } leaf = path->nodes[0]; if (ret > 0) { @@ -1175,7 +1176,7 @@ update_cache_item(struct btrfs_trans_handle *trans, inode->i_size - 1, EXTENT_DELALLOC, NULL); btrfs_release_path(path); - goto fail; + return -ENOENT; } } @@ -1188,9 +1189,6 @@ update_cache_item(struct btrfs_trans_handle *trans, btrfs_release_path(path); return 0; - -fail: - return -1; } static noinline_for_stack int write_pinned_extent_entries( @@ -1200,12 +1198,10 @@ static noinline_for_stack int write_pinned_extent_entries( int *entries) { u64 start, extent_start, extent_end, len; + const u64 block_group_end = btrfs_block_group_end(block_group); struct extent_io_tree *unpin = NULL; int ret; - if (!block_group) - return 0; - /* * We want to add any pinned extents to our free space cache * so we don't leak the space @@ -1217,19 +1213,18 @@ static noinline_for_stack int write_pinned_extent_entries( start = block_group->start; - while (start < block_group->start + block_group->length) { + while (start < block_group_end) { if (!btrfs_find_first_extent_bit(unpin, start, &extent_start, &extent_end, EXTENT_DIRTY, NULL)) return 0; /* This pinned extent is out of our range */ - if (extent_start >= block_group->start + block_group->length) + if (extent_start >= block_group_end) return 0; extent_start = max(extent_start, start); - extent_end = min(block_group->start + block_group->length, - extent_end + 1); + extent_end = min(block_group_end, extent_end + 1); len = extent_end - extent_start; *entries += 1; @@ -1374,9 +1369,9 @@ int btrfs_wait_cache_io(struct btrfs_trans_handle *trans, static int __btrfs_write_out_cache(struct inode *inode, struct btrfs_free_space_ctl *ctl, struct btrfs_block_group *block_group, - struct btrfs_io_ctl *io_ctl, struct btrfs_trans_handle *trans) { + struct btrfs_io_ctl *io_ctl = &block_group->io_ctl; struct extent_state *cached_state = NULL; LIST_HEAD(bitmap_list); int entries = 0; @@ -1393,7 +1388,7 @@ static int __btrfs_write_out_cache(struct inode *inode, if (ret) return ret; - if (block_group && (block_group->flags & BTRFS_BLOCK_GROUP_DATA)) { + if (block_group->flags & BTRFS_BLOCK_GROUP_DATA) { down_write(&block_group->data_rwsem); spin_lock(&block_group->lock); if (block_group->delalloc_bytes) { @@ -1465,7 +1460,7 @@ static int __btrfs_write_out_cache(struct inode *inode, goto out_nospc; } - if (block_group && (block_group->flags & BTRFS_BLOCK_GROUP_DATA)) + if (block_group->flags & BTRFS_BLOCK_GROUP_DATA) up_write(&block_group->data_rwsem); /* * Release the pages and unlock the extent, we will flush @@ -1500,7 +1495,7 @@ out_nospc: cleanup_write_cache_enospc(inode, io_ctl, &cached_state); out_unlock: - if (block_group && (block_group->flags & BTRFS_BLOCK_GROUP_DATA)) + if (block_group->flags & BTRFS_BLOCK_GROUP_DATA) up_write(&block_group->data_rwsem); out: @@ -1536,8 +1531,7 @@ int btrfs_write_out_cache(struct btrfs_trans_handle *trans, if (IS_ERR(inode)) return 0; - ret = __btrfs_write_out_cache(inode, ctl, block_group, - &block_group->io_ctl, trans); + ret = __btrfs_write_out_cache(inode, ctl, block_group, trans); if (ret) { btrfs_debug(fs_info, "failed to write free space cache for block group %llu error %d", @@ -2020,7 +2014,7 @@ find_free_space(struct btrfs_free_space_ctl *ctl, u64 *offset, u64 *bytes, int ret; if (!ctl->free_space_offset.rb_node) - goto out; + return NULL; again: if (use_bytes_index) { node = rb_first_cached(&ctl->free_space_bytes); @@ -2028,7 +2022,7 @@ again: entry = tree_search_offset(ctl, offset_to_bitmap(ctl, *offset), 0, 1); if (!entry) - goto out; + return NULL; node = &entry->offset_index; } @@ -2112,7 +2106,7 @@ again: *bytes = entry->bytes - align_off; return entry; } -out: + return NULL; } @@ -2756,6 +2750,9 @@ int btrfs_add_free_space(struct btrfs_block_group *block_group, { enum btrfs_trim_state trim_state = BTRFS_TRIM_STATE_UNTRIMMED; + if (block_group->flags & BTRFS_BLOCK_GROUP_REMAPPED) + return 0; + if (btrfs_is_zoned(block_group->fs_info)) return __btrfs_add_free_space_zoned(block_group, bytenr, size, true); @@ -2894,7 +2891,7 @@ again: old_end - (offset + bytes), info->trim_state); WARN_ON(ret); - goto out; + return ret; } } @@ -2906,7 +2903,7 @@ again: out_lock: btrfs_discard_update_discardable(block_group); spin_unlock(&ctl->tree_lock); -out: + return ret; } @@ -3063,6 +3060,12 @@ bool btrfs_is_free_space_trimmed(struct btrfs_block_group *block_group) struct rb_node *node; bool ret = true; + if (block_group->flags & BTRFS_BLOCK_GROUP_REMAPPED && + !test_bit(BLOCK_GROUP_FLAG_STRIPE_REMOVAL_PENDING, &block_group->runtime_flags) && + block_group->identity_remap_count == 0) { + return true; + } + spin_lock(&ctl->tree_lock); node = rb_first(&ctl->free_space_offset); @@ -3674,7 +3677,7 @@ static int do_trimming(struct btrfs_block_group *block_group, } spin_unlock(&space_info->lock); - ret = btrfs_discard_extent(fs_info, start, bytes, &trimmed); + ret = btrfs_discard_extent(fs_info, start, bytes, &trimmed, false); if (!ret) { *total_trimmed += trimmed; trim_state = BTRFS_TRIM_STATE_TRIMMED; @@ -3831,6 +3834,50 @@ out_unlock: return ret; } +void btrfs_trim_fully_remapped_block_group(struct btrfs_block_group *bg) +{ + struct btrfs_fs_info *fs_info = bg->fs_info; + struct btrfs_discard_ctl *discard_ctl = &fs_info->discard_ctl; + int ret = 0; + u64 bytes, trimmed; + const u64 max_discard_size = READ_ONCE(discard_ctl->max_discard_size); + u64 end = btrfs_block_group_end(bg); + + if (!test_bit(BLOCK_GROUP_FLAG_STRIPE_REMOVAL_PENDING, &bg->runtime_flags)) { + bg->discard_cursor = end; + + if (bg->used == 0) { + spin_lock(&fs_info->unused_bgs_lock); + if (!list_empty(&bg->bg_list)) { + list_del_init(&bg->bg_list); + btrfs_put_block_group(bg); + } + spin_unlock(&fs_info->unused_bgs_lock); + + btrfs_mark_bg_unused(bg); + } + + return; + } + + bytes = end - bg->discard_cursor; + + if (max_discard_size && + bytes >= (max_discard_size + BTRFS_ASYNC_DISCARD_MIN_FILTER)) + bytes = max_discard_size; + + ret = btrfs_discard_extent(fs_info, bg->discard_cursor, bytes, &trimmed, false); + if (ret) + return; + + bg->discard_cursor += trimmed; + + if (bg->discard_cursor < end) + return; + + btrfs_complete_bg_remapping(bg); +} + /* * If we break out of trimming a bitmap prematurely, we should reset the * trimming bit. In a rather contrived case, it's possible to race here so @@ -3956,7 +4003,7 @@ static int trim_bitmaps(struct btrfs_block_group *block_group, if (async && *total_trimmed) { spin_unlock(&ctl->tree_lock); mutex_unlock(&ctl->cache_writeout_mutex); - goto out; + return ret; } bytes = min(bytes, end - start); @@ -4017,7 +4064,6 @@ next: if (offset >= end) block_group->discard_cursor = end; -out: return ret; } @@ -4110,20 +4156,20 @@ static int cleanup_free_space_cache_v1(struct btrfs_fs_info *fs_info, { struct btrfs_block_group *block_group; struct rb_node *node; - int ret = 0; btrfs_info(fs_info, "cleaning free space cache v1"); node = rb_first_cached(&fs_info->block_group_cache_tree); while (node) { + int ret; + block_group = rb_entry(node, struct btrfs_block_group, cache_node); ret = btrfs_remove_free_space_inode(trans, NULL, block_group); if (ret) - goto out; + return ret; node = rb_next(node); } -out: - return ret; + return 0; } int btrfs_set_free_space_cache_v1_active(struct btrfs_fs_info *fs_info, bool active) diff --git a/fs/btrfs/free-space-cache.h b/fs/btrfs/free-space-cache.h index 9f1dbfdee8ca..33fc3b245648 100644 --- a/fs/btrfs/free-space-cache.h +++ b/fs/btrfs/free-space-cache.h @@ -166,6 +166,7 @@ int btrfs_trim_block_group_extents(struct btrfs_block_group *block_group, int btrfs_trim_block_group_bitmaps(struct btrfs_block_group *block_group, u64 *trimmed, u64 start, u64 end, u64 minlen, u64 maxlen, bool async); +void btrfs_trim_fully_remapped_block_group(struct btrfs_block_group *bg); bool btrfs_free_space_cache_v1_active(struct btrfs_fs_info *fs_info); int btrfs_set_free_space_cache_v1_active(struct btrfs_fs_info *fs_info, bool active); diff --git a/fs/btrfs/free-space-tree.c b/fs/btrfs/free-space-tree.c index 1ad2ad384b9e..ecddfca92b2b 100644 --- a/fs/btrfs/free-space-tree.c +++ b/fs/btrfs/free-space-tree.c @@ -21,8 +21,7 @@ static int __add_block_group_free_space(struct btrfs_trans_handle *trans, struct btrfs_block_group *block_group, struct btrfs_path *path); -static struct btrfs_root *btrfs_free_space_root( - struct btrfs_block_group *block_group) +struct btrfs_root *btrfs_free_space_root(struct btrfs_block_group *block_group) { struct btrfs_key key = { .objectid = BTRFS_FREE_SPACE_TREE_OBJECTID, @@ -93,7 +92,6 @@ static int add_new_free_space_info(struct btrfs_trans_handle *trans, return 0; } -EXPORT_FOR_TESTS struct btrfs_free_space_info *btrfs_search_free_space_info( struct btrfs_trans_handle *trans, struct btrfs_block_group *block_group, @@ -220,7 +218,7 @@ int btrfs_convert_free_space_to_bitmaps(struct btrfs_trans_handle *trans, return 0; start = block_group->start; - end = block_group->start + block_group->length; + end = btrfs_block_group_end(block_group); key.objectid = end - 1; key.type = (u8)-1; @@ -360,7 +358,7 @@ int btrfs_convert_free_space_to_extents(struct btrfs_trans_handle *trans, return 0; start = block_group->start; - end = block_group->start + block_group->length; + end = btrfs_block_group_end(block_group); key.objectid = end - 1; key.type = (u8)-1; @@ -667,7 +665,7 @@ static int modify_free_space_bitmap(struct btrfs_trans_handle *trans, * Read the bit for the block immediately after the extent of space if * that block is within the block group. */ - if (end < block_group->start + block_group->length) { + if (end < btrfs_block_group_end(block_group)) { /* The next block may be in the next bitmap. */ btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]); if (end >= key.objectid + key.offset) { @@ -940,7 +938,7 @@ static int add_free_space_extent(struct btrfs_trans_handle *trans, right: /* Search for a neighbor on the right. */ - if (end == block_group->start + block_group->length) + if (end == btrfs_block_group_end(block_group)) goto insert; key.objectid = end; key.type = (u8)-1; @@ -1106,7 +1104,7 @@ static int populate_free_space_tree(struct btrfs_trans_handle *trans, * highest, block group). */ start = block_group->start; - end = block_group->start + block_group->length; + end = btrfs_block_group_end(block_group); while (ret == 0) { btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]); @@ -1396,9 +1394,9 @@ static int __add_block_group_free_space(struct btrfs_trans_handle *trans, * can use multiple transactions, every time btrfs_end_transaction() is * called at btrfs_rebuild_free_space_tree() we finish the creation of * new block groups by calling btrfs_create_pending_block_groups(), and - * that in turn calls us, through add_block_group_free_space(), to add - * a free space info item and a free space extent item for the block - * group. + * that in turn calls us, through btrfs_add_block_group_free_space(), + * to add a free space info item and a free space extent item for the + * block group. * * Then later btrfs_rebuild_free_space_tree() may find such new block * groups and processes them with populate_free_space_tree(), which can @@ -1479,7 +1477,7 @@ int btrfs_remove_block_group_free_space(struct btrfs_trans_handle *trans, } start = block_group->start; - end = block_group->start + block_group->length; + end = btrfs_block_group_end(block_group); key.objectid = end - 1; key.type = (u8)-1; @@ -1525,33 +1523,28 @@ int btrfs_remove_block_group_free_space(struct btrfs_trans_handle *trans, btrfs_release_path(path); } - ret = 0; - - return ret; + return 0; } static int load_free_space_bitmaps(struct btrfs_caching_control *caching_ctl, struct btrfs_path *path, u32 expected_extent_count) { - struct btrfs_block_group *block_group; - struct btrfs_fs_info *fs_info; + struct btrfs_block_group *block_group = caching_ctl->block_group; + struct btrfs_fs_info *fs_info = block_group->fs_info; struct btrfs_root *root; struct btrfs_key key; bool prev_bit_set = false; /* Initialize to silence GCC. */ u64 extent_start = 0; - u64 end, offset; + const u64 end = btrfs_block_group_end(block_group); + u64 offset; u64 total_found = 0; u32 extent_count = 0; int ret; - block_group = caching_ctl->block_group; - fs_info = block_group->fs_info; root = btrfs_free_space_root(block_group); - end = block_group->start + block_group->length; - while (1) { ret = btrfs_next_item(root, path); if (ret < 0) @@ -1617,21 +1610,17 @@ static int load_free_space_extents(struct btrfs_caching_control *caching_ctl, struct btrfs_path *path, u32 expected_extent_count) { - struct btrfs_block_group *block_group; - struct btrfs_fs_info *fs_info; + struct btrfs_block_group *block_group = caching_ctl->block_group; + struct btrfs_fs_info *fs_info = block_group->fs_info; struct btrfs_root *root; struct btrfs_key key; - u64 end; + const u64 end = btrfs_block_group_end(block_group); u64 total_found = 0; u32 extent_count = 0; int ret; - block_group = caching_ctl->block_group; - fs_info = block_group->fs_info; root = btrfs_free_space_root(block_group); - end = block_group->start + block_group->length; - while (1) { u64 space_added; @@ -1712,3 +1701,106 @@ int btrfs_load_free_space_tree(struct btrfs_caching_control *caching_ctl) else return load_free_space_extents(caching_ctl, path, extent_count); } + +static int delete_orphan_free_space_entries(struct btrfs_root *fst_root, + struct btrfs_path *path, + u64 first_bg_bytenr) +{ + struct btrfs_trans_handle *trans; + int ret; + + trans = btrfs_start_transaction(fst_root, 1); + if (IS_ERR(trans)) + return PTR_ERR(trans); + + while (true) { + struct btrfs_key key = { 0 }; + int i; + + ret = btrfs_search_slot(trans, fst_root, &key, path, -1, 1); + if (ret < 0) + break; + ASSERT(ret > 0); + ret = 0; + for (i = 0; i < btrfs_header_nritems(path->nodes[0]); i++) { + btrfs_item_key_to_cpu(path->nodes[0], &key, i); + if (key.objectid >= first_bg_bytenr) { + /* + * Only break the for() loop and continue to + * delete items. + */ + break; + } + } + /* No items to delete, finished. */ + if (i == 0) + break; + + ret = btrfs_del_items(trans, fst_root, path, 0, i); + if (ret < 0) + break; + btrfs_release_path(path); + } + btrfs_release_path(path); + btrfs_end_transaction(trans); + if (ret == 0) + btrfs_info(fst_root->fs_info, "deleted orphan free space tree entries"); + return ret; +} + +/* Remove any free space entry before the first block group. */ +int btrfs_delete_orphan_free_space_entries(struct btrfs_fs_info *fs_info) +{ + BTRFS_PATH_AUTO_RELEASE(path); + struct btrfs_key key = { + .objectid = BTRFS_FREE_SPACE_TREE_OBJECTID, + .type = BTRFS_ROOT_ITEM_KEY, + .offset = 0, + }; + struct btrfs_root *root; + struct btrfs_block_group *bg; + u64 first_bg_bytenr; + int ret; + + /* + * Extent tree v2 has multiple global roots based on the block group. + * This means we cannot easily grab the global free space tree and locate + * orphan items. Furthermore this is still experimental, all users + * should use the latest btrfs-progs anyway. + */ + if (btrfs_fs_incompat(fs_info, EXTENT_TREE_V2)) + return 0; + if (!btrfs_fs_compat_ro(fs_info, FREE_SPACE_TREE)) + return 0; + root = btrfs_global_root(fs_info, &key); + if (!root) + return 0; + + key.objectid = 0; + key.type = 0; + key.offset = 0; + + bg = btrfs_lookup_first_block_group(fs_info, 0); + if (unlikely(!bg)) { + btrfs_err(fs_info, "no block group found"); + return -EUCLEAN; + } + first_bg_bytenr = bg->start; + btrfs_put_block_group(bg); + + ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0); + if (ret < 0) + return ret; + /* There should not be an all-zero key in fst. */ + ASSERT(ret > 0); + + /* Empty free space tree. */ + if (path.slots[0] >= btrfs_header_nritems(path.nodes[0])) + return 0; + + btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]); + if (key.objectid >= first_bg_bytenr) + return 0; + btrfs_release_path(&path); + return delete_orphan_free_space_entries(root, &path, first_bg_bytenr); +} diff --git a/fs/btrfs/free-space-tree.h b/fs/btrfs/free-space-tree.h index 3d9a5d4477fc..709730e36888 100644 --- a/fs/btrfs/free-space-tree.h +++ b/fs/btrfs/free-space-tree.h @@ -35,12 +35,14 @@ int btrfs_add_to_free_space_tree(struct btrfs_trans_handle *trans, u64 start, u64 size); int btrfs_remove_from_free_space_tree(struct btrfs_trans_handle *trans, u64 start, u64 size); - -#ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS +int btrfs_delete_orphan_free_space_entries(struct btrfs_fs_info *fs_info); struct btrfs_free_space_info * btrfs_search_free_space_info(struct btrfs_trans_handle *trans, struct btrfs_block_group *block_group, struct btrfs_path *path, int cow); +struct btrfs_root *btrfs_free_space_root(struct btrfs_block_group *block_group); + +#ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS int __btrfs_add_to_free_space_tree(struct btrfs_trans_handle *trans, struct btrfs_block_group *block_group, struct btrfs_path *path, u64 start, u64 size); diff --git a/fs/btrfs/fs.c b/fs/btrfs/fs.c index feb0a2faa837..14d83565cdee 100644 --- a/fs/btrfs/fs.c +++ b/fs/btrfs/fs.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 +#include <linux/crc32.h> #include "messages.h" #include "fs.h" #include "accessors.h" @@ -8,13 +9,11 @@ static const struct btrfs_csums { u16 size; const char name[10]; - const char driver[12]; } btrfs_csums[] = { [BTRFS_CSUM_TYPE_CRC32] = { .size = 4, .name = "crc32c" }, [BTRFS_CSUM_TYPE_XXHASH] = { .size = 8, .name = "xxhash64" }, [BTRFS_CSUM_TYPE_SHA256] = { .size = 32, .name = "sha256" }, - [BTRFS_CSUM_TYPE_BLAKE2] = { .size = 32, .name = "blake2b", - .driver = "blake2b-256" }, + [BTRFS_CSUM_TYPE_BLAKE2] = { .size = 32, .name = "blake2b" }, }; /* This exists for btrfs-progs usages. */ @@ -37,21 +36,94 @@ const char *btrfs_super_csum_name(u16 csum_type) return btrfs_csums[csum_type].name; } -/* - * Return driver name if defined, otherwise the name that's also a valid driver - * name. - */ -const char *btrfs_super_csum_driver(u16 csum_type) +size_t __attribute_const__ btrfs_get_num_csums(void) { - /* csum type is validated at mount time */ - return btrfs_csums[csum_type].driver[0] ? - btrfs_csums[csum_type].driver : - btrfs_csums[csum_type].name; + return ARRAY_SIZE(btrfs_csums); } -size_t __attribute_const__ btrfs_get_num_csums(void) +void btrfs_csum(u16 csum_type, const u8 *data, size_t len, u8 *out) { - return ARRAY_SIZE(btrfs_csums); + switch (csum_type) { + case BTRFS_CSUM_TYPE_CRC32: + put_unaligned_le32(~crc32c(~0, data, len), out); + break; + case BTRFS_CSUM_TYPE_XXHASH: + put_unaligned_le64(xxh64(data, len, 0), out); + break; + case BTRFS_CSUM_TYPE_SHA256: + sha256(data, len, out); + break; + case BTRFS_CSUM_TYPE_BLAKE2: + blake2b(NULL, 0, data, len, out, 32); + break; + default: + /* Checksum type is validated at mount time. */ + BUG(); + } +} + +void btrfs_csum_init(struct btrfs_csum_ctx *ctx, u16 csum_type) +{ + ctx->csum_type = csum_type; + switch (ctx->csum_type) { + case BTRFS_CSUM_TYPE_CRC32: + ctx->crc32 = ~0; + break; + case BTRFS_CSUM_TYPE_XXHASH: + xxh64_reset(&ctx->xxh64, 0); + break; + case BTRFS_CSUM_TYPE_SHA256: + sha256_init(&ctx->sha256); + break; + case BTRFS_CSUM_TYPE_BLAKE2: + blake2b_init(&ctx->blake2b, 32); + break; + default: + /* Checksume type is validated at mount time. */ + BUG(); + } +} + +void btrfs_csum_update(struct btrfs_csum_ctx *ctx, const u8 *data, size_t len) +{ + switch (ctx->csum_type) { + case BTRFS_CSUM_TYPE_CRC32: + ctx->crc32 = crc32c(ctx->crc32, data, len); + break; + case BTRFS_CSUM_TYPE_XXHASH: + xxh64_update(&ctx->xxh64, data, len); + break; + case BTRFS_CSUM_TYPE_SHA256: + sha256_update(&ctx->sha256, data, len); + break; + case BTRFS_CSUM_TYPE_BLAKE2: + blake2b_update(&ctx->blake2b, data, len); + break; + default: + /* Checksum type is validated at mount time. */ + BUG(); + } +} + +void btrfs_csum_final(struct btrfs_csum_ctx *ctx, u8 *out) +{ + switch (ctx->csum_type) { + case BTRFS_CSUM_TYPE_CRC32: + put_unaligned_le32(~ctx->crc32, out); + break; + case BTRFS_CSUM_TYPE_XXHASH: + put_unaligned_le64(xxh64_digest(&ctx->xxh64), out); + break; + case BTRFS_CSUM_TYPE_SHA256: + sha256_final(&ctx->sha256, out); + break; + case BTRFS_CSUM_TYPE_BLAKE2: + blake2b_final(&ctx->blake2b, out); + break; + default: + /* Checksum type is validated at mount time. */ + BUG(); + } } /* diff --git a/fs/btrfs/fs.h b/fs/btrfs/fs.h index 0f7e1ef27891..3de3b517810e 100644 --- a/fs/btrfs/fs.h +++ b/fs/btrfs/fs.h @@ -3,6 +3,8 @@ #ifndef BTRFS_FS_H #define BTRFS_FS_H +#include <crypto/blake2b.h> +#include <crypto/sha2.h> #include <linux/blkdev.h> #include <linux/sizes.h> #include <linux/time64.h> @@ -24,6 +26,7 @@ #include <linux/wait_bit.h> #include <linux/sched.h> #include <linux/rbtree.h> +#include <linux/xxhash.h> #include <uapi/linux/btrfs.h> #include <uapi/linux/btrfs_tree.h> #include "extent-io-tree.h" @@ -35,14 +38,12 @@ struct inode; struct super_block; struct kobject; struct reloc_control; -struct crypto_shash; struct ulist; struct btrfs_device; struct btrfs_block_group; struct btrfs_root; struct btrfs_fs_devices; struct btrfs_transaction; -struct btrfs_delayed_root; struct btrfs_balance_control; struct btrfs_subpage_info; struct btrfs_stripe_hash_table; @@ -64,6 +65,12 @@ struct btrfs_space_info; #define BTRFS_MAX_EXTENT_SIZE SZ_128M +/* + * Maximum length to trim in a single iteration to avoid holding device list + * mutex for too long. + */ +#define BTRFS_MAX_TRIM_LENGTH SZ_2G + #define BTRFS_OLDEST_GENERATION 0ULL #define BTRFS_EMPTY_DIR_SIZE 0 @@ -264,6 +271,14 @@ enum { BTRFS_MOUNT_REF_TRACKER = (1ULL << 33), }; +/* These mount options require a full read-only fs, no new transaction is allowed. */ +#define BTRFS_MOUNT_FULL_RO_MASK \ + (BTRFS_MOUNT_NOLOGREPLAY | \ + BTRFS_MOUNT_IGNOREBADROOTS | \ + BTRFS_MOUNT_IGNOREDATACSUMS | \ + BTRFS_MOUNT_IGNOREMETACSUMS | \ + BTRFS_MOUNT_IGNORESUPERFLAGS) + /* * Compat flags that we support. If any incompat flags are set other than the * ones specified below then we will fail to mount @@ -305,7 +320,8 @@ enum { #define BTRFS_FEATURE_INCOMPAT_SUPP \ (BTRFS_FEATURE_INCOMPAT_SUPP_STABLE | \ BTRFS_FEATURE_INCOMPAT_RAID_STRIPE_TREE | \ - BTRFS_FEATURE_INCOMPAT_EXTENT_TREE_V2) + BTRFS_FEATURE_INCOMPAT_EXTENT_TREE_V2 | \ + BTRFS_FEATURE_INCOMPAT_REMAP_TREE) #else @@ -453,6 +469,21 @@ struct btrfs_commit_stats { u64 critical_section_start_time; }; +struct btrfs_delayed_root { + spinlock_t lock; + int nodes; /* for delayed nodes */ + struct list_head node_list; + /* + * Used for delayed nodes which is waiting to be dealt with by the + * worker. If the delayed node is inserted into the work queue, we + * drop it from this list. + */ + struct list_head prepare_list; + atomic_t items; /* for delayed items */ + atomic_t items_seq; /* for delayed items */ + wait_queue_head_t wait; +}; + struct btrfs_fs_info { u8 chunk_tree_uuid[BTRFS_UUID_SIZE]; unsigned long flags; @@ -465,6 +496,7 @@ struct btrfs_fs_info { struct btrfs_root *data_reloc_root; struct btrfs_root *block_group_root; struct btrfs_root *stripe_root; + struct btrfs_root *remap_root; /* The log root tree is a directory of all the other log roots */ struct btrfs_root *log_root_tree; @@ -499,6 +531,8 @@ struct btrfs_fs_info { struct btrfs_block_rsv trans_block_rsv; /* Block reservation for chunk tree */ struct btrfs_block_rsv chunk_block_rsv; + /* Block reservation for remap tree. */ + struct btrfs_block_rsv remap_block_rsv; /* Block reservation for delayed operations */ struct btrfs_block_rsv delayed_block_rsv; /* Block reservation for delayed refs */ @@ -573,6 +607,7 @@ struct btrfs_fs_info { struct mutex transaction_kthread_mutex; struct mutex cleaner_mutex; struct mutex chunk_mutex; + struct mutex remap_mutex; /* * This is taken to make sure we don't set block groups ro after the @@ -802,7 +837,7 @@ struct btrfs_fs_info { /* Filesystem state */ unsigned long fs_state; - struct btrfs_delayed_root *delayed_root; + struct btrfs_delayed_root delayed_root; /* Entries are eb->start >> nodesize_bits */ struct xarray buffer_tree; @@ -826,10 +861,11 @@ struct btrfs_fs_info { struct list_head reclaim_bgs; int bg_reclaim_threshold; - /* Protects the lists unused_bgs and reclaim_bgs. */ + /* Protects the lists unused_bgs, reclaim_bgs, and fully_remapped_bgs. */ spinlock_t unused_bgs_lock; /* Protected by unused_bgs_lock. */ struct list_head unused_bgs; + struct list_head fully_remapped_bgs; struct mutex unused_bg_unpin_mutex; /* Protect block groups that are going to be deleted */ struct mutex reclaim_bgs_lock; @@ -842,9 +878,10 @@ struct btrfs_fs_info { u32 sectorsize_bits; u32 block_min_order; u32 block_max_order; + u32 stripesize; u32 csum_size; u32 csums_per_leaf; - u32 stripesize; + u32 csum_type; /* * Maximum size of an extent. BTRFS_MAX_EXTENT_SIZE on regular @@ -856,8 +893,6 @@ struct btrfs_fs_info { spinlock_t swapfile_pins_lock; struct rb_root swapfile_pins; - struct crypto_shash *csum_shash; - /* Type of exclusive operation running, protected by super_lock */ enum btrfs_exclusive_operation exclusive_operation; @@ -1049,8 +1084,20 @@ int btrfs_check_ioctl_vol_args_path(const struct btrfs_ioctl_vol_args *vol_args) u16 btrfs_csum_type_size(u16 type); int btrfs_super_csum_size(const struct btrfs_super_block *s); const char *btrfs_super_csum_name(u16 csum_type); -const char *btrfs_super_csum_driver(u16 csum_type); size_t __attribute_const__ btrfs_get_num_csums(void); +struct btrfs_csum_ctx { + u16 csum_type; + union { + u32 crc32; + struct xxh64_state xxh64; + struct sha256_ctx sha256; + struct blake2b_ctx blake2b; + }; +}; +void btrfs_csum(u16 csum_type, const u8 *data, size_t len, u8 *out); +void btrfs_csum_init(struct btrfs_csum_ctx *ctx, u16 csum_type); +void btrfs_csum_update(struct btrfs_csum_ctx *ctx, const u8 *data, size_t len); +void btrfs_csum_final(struct btrfs_csum_ctx *ctx, u8 *out); static inline bool btrfs_is_empty_uuid(const u8 *uuid) { @@ -1097,15 +1144,17 @@ void __btrfs_clear_fs_compat_ro(struct btrfs_fs_info *fs_info, u64 flag, #define btrfs_test_opt(fs_info, opt) ((fs_info)->mount_opt & \ BTRFS_MOUNT_##opt) -static inline int btrfs_fs_closing(const struct btrfs_fs_info *fs_info) +static inline bool btrfs_fs_closing(const struct btrfs_fs_info *fs_info) { - /* Do it this way so we only ever do one test_bit in the normal case. */ - if (test_bit(BTRFS_FS_CLOSING_START, &fs_info->flags)) { - if (test_bit(BTRFS_FS_CLOSING_DONE, &fs_info->flags)) - return 2; - return 1; - } - return 0; + return unlikely(test_bit(BTRFS_FS_CLOSING_START, &fs_info->flags)); +} + +static inline bool btrfs_fs_closing_done(const struct btrfs_fs_info *fs_info) +{ + if (btrfs_fs_closing(fs_info) && test_bit(BTRFS_FS_CLOSING_DONE, &fs_info->flags)) + return true; + + return false; } /* @@ -1133,9 +1182,9 @@ static inline void btrfs_wake_unfinished_drop(struct btrfs_fs_info *fs_info) (unlikely(test_bit(BTRFS_FS_STATE_LOG_CLEANUP_ERROR, \ &(fs_info)->fs_state))) -static inline bool btrfs_is_shutdown(struct btrfs_fs_info *fs_info) +static inline bool btrfs_is_shutdown(const struct btrfs_fs_info *fs_info) { - return test_bit(BTRFS_FS_STATE_EMERGENCY_SHUTDOWN, &fs_info->fs_state); + return unlikely(test_bit(BTRFS_FS_STATE_EMERGENCY_SHUTDOWN, &fs_info->fs_state)); } static inline void btrfs_force_shutdown(struct btrfs_fs_info *fs_info) diff --git a/fs/btrfs/inode-item.c b/fs/btrfs/inode-item.c index b73e1dd97208..a864f8c99729 100644 --- a/fs/btrfs/inode-item.c +++ b/fs/btrfs/inode-item.c @@ -371,14 +371,13 @@ int btrfs_insert_empty_inode(struct btrfs_trans_handle *trans, struct btrfs_path *path, u64 objectid) { struct btrfs_key key; - int ret; + key.objectid = objectid; key.type = BTRFS_INODE_ITEM_KEY; key.offset = 0; - ret = btrfs_insert_empty_item(trans, root, path, &key, - sizeof(struct btrfs_inode_item)); - return ret; + return btrfs_insert_empty_item(trans, root, path, &key, + sizeof(struct btrfs_inode_item)); } int btrfs_lookup_inode(struct btrfs_trans_handle *trans, struct btrfs_root diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index a2b5b440637e..845164485a00 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -3,11 +3,11 @@ * Copyright (C) 2007 Oracle. All rights reserved. */ -#include <crypto/hash.h> #include <linux/kernel.h> #include <linux/bio.h> #include <linux/blk-cgroup.h> #include <linux/file.h> +#include <linux/filelock.h> #include <linux/fs.h> #include <linux/fs_struct.h> #include <linux/pagemap.h> @@ -34,7 +34,6 @@ #include <linux/sched/mm.h> #include <linux/iomap.h> #include <linux/unaligned.h> -#include <linux/fsverity.h> #include "misc.h" #include "ctree.h" #include "disk-io.h" @@ -218,7 +217,7 @@ static void print_data_reloc_error(const struct btrfs_inode *inode, u64 file_off int mirror_num) { struct btrfs_fs_info *fs_info = inode->root->fs_info; - struct btrfs_path path = { 0 }; + BTRFS_PATH_AUTO_RELEASE(path); struct btrfs_key found_key = { 0 }; struct extent_buffer *eb; struct btrfs_extent_item *ei; @@ -256,7 +255,6 @@ static void print_data_reloc_error(const struct btrfs_inode *inode, u64 file_off if (ret < 0) { btrfs_err_rl(fs_info, "failed to lookup extent item for logical %llu: %d", logical, ret); - btrfs_release_path(&path); return; } eb = path.nodes[0]; @@ -286,11 +284,14 @@ static void print_data_reloc_error(const struct btrfs_inode *inode, u64 file_off (ref_level ? "node" : "leaf"), ref_level, ref_root); } - btrfs_release_path(&path); } else { struct btrfs_backref_walk_ctx ctx = { 0 }; struct data_reloc_warn reloc_warn = { 0 }; + /* + * Do not hold the path as later iterate_extent_inodes() call + * can be time consuming. + */ btrfs_release_path(&path); ctx.bytenr = found_key.objectid; @@ -506,7 +507,7 @@ static int insert_inline_extent(struct btrfs_trans_handle *trans, ret = btrfs_insert_empty_item(trans, root, path, &key, datasize); if (ret) - goto fail; + return ret; } leaf = path->nodes[0]; ei = btrfs_item_ptr(leaf, path->slots[0], @@ -545,7 +546,7 @@ static int insert_inline_extent(struct btrfs_trans_handle *trans, ret = btrfs_inode_set_file_extent_range(inode, 0, ALIGN(size, root->fs_info->sectorsize)); if (ret) - goto fail; + return ret; /* * We're an inline extent, so nobody can extend the file past i_size @@ -561,8 +562,7 @@ static int insert_inline_extent(struct btrfs_trans_handle *trans, } inode->disk_i_size = i_size; -fail: - return ret; + return 0; } static bool can_cow_file_range_inline(struct btrfs_inode *inode, @@ -689,8 +689,8 @@ out: /* * Don't forget to free the reserved space, as for inlined extent * it won't count as data extent, free them directly here. - * And at reserve time, it's always aligned to page size, so - * just free one page here. + * And at reserve time, it's always aligned to sector size, so + * just free one sector here. * * If we fallback to non-inline (ret == 1) due to -ENOSPC, then we need * to keep the data reservation. @@ -755,10 +755,7 @@ static noinline int cow_file_range_inline(struct btrfs_inode *inode, struct async_extent { u64 start; u64 ram_size; - u64 compressed_size; - struct folio **folios; - unsigned long nr_folios; - int compress_type; + struct compressed_bio *cb; struct list_head list; }; @@ -779,24 +776,18 @@ struct async_cow { struct async_chunk chunks[]; }; -static noinline int add_async_extent(struct async_chunk *cow, - u64 start, u64 ram_size, - u64 compressed_size, - struct folio **folios, - unsigned long nr_folios, - int compress_type) +static int add_async_extent(struct async_chunk *cow, u64 start, u64 ram_size, + struct compressed_bio *cb) { struct async_extent *async_extent; async_extent = kmalloc(sizeof(*async_extent), GFP_NOFS); if (!async_extent) return -ENOMEM; + ASSERT(ram_size < U32_MAX); async_extent->start = start; async_extent->ram_size = ram_size; - async_extent->compressed_size = compressed_size; - async_extent->folios = folios; - async_extent->nr_folios = nr_folios; - async_extent->compress_type = compress_type; + async_extent->cb = cb; list_add_tail(&async_extent->list, &cow->extents); return 0; } @@ -815,6 +806,13 @@ static inline int inode_need_compress(struct btrfs_inode *inode, u64 start, return 0; } + /* + * If the delalloc range is only one fs block and can not be inlined, + * do not even bother try compression, as there will be no space saving + * and will always fallback to regular write later. + */ + if (start != 0 && end + 1 - start <= fs_info->sectorsize) + return 0; /* Defrag ioctl takes precedence over mount options and properties. */ if (inode->defrag_compress == BTRFS_DEFRAG_DONT_COMPRESS) return 0; @@ -863,6 +861,61 @@ static int extent_range_clear_dirty_for_io(struct btrfs_inode *inode, u64 start, return ret; } +static struct folio *compressed_bio_last_folio(struct compressed_bio *cb) +{ + struct bio *bio = &cb->bbio.bio; + struct bio_vec *bvec; + phys_addr_t paddr; + + /* + * Make sure all folios have the same min_folio_size. + * + * Otherwise we cannot simply use offset_in_offset(folio, bi_size) to + * calculate the end of the last folio. + */ + if (IS_ENABLED(CONFIG_BTRFS_ASSERT)) { + struct btrfs_fs_info *fs_info = cb_to_fs_info(cb); + const u32 min_folio_size = btrfs_min_folio_size(fs_info); + struct folio_iter fi; + + bio_for_each_folio_all(fi, bio) + ASSERT(folio_size(fi.folio) == min_folio_size); + } + + /* The bio must not be empty. */ + ASSERT(bio->bi_vcnt); + + bvec = &bio->bi_io_vec[bio->bi_vcnt - 1]; + paddr = page_to_phys(bvec->bv_page) + bvec->bv_offset + bvec->bv_len - 1; + return page_folio(phys_to_page(paddr)); +} + +static void zero_last_folio(struct compressed_bio *cb) +{ + struct bio *bio = &cb->bbio.bio; + struct folio *last_folio = compressed_bio_last_folio(cb); + const u32 bio_size = bio->bi_iter.bi_size; + const u32 foffset = offset_in_folio(last_folio, bio_size); + + folio_zero_range(last_folio, foffset, folio_size(last_folio) - foffset); +} + +static void round_up_last_block(struct compressed_bio *cb, u32 blocksize) +{ + struct bio *bio = &cb->bbio.bio; + struct folio *last_folio = compressed_bio_last_folio(cb); + const u32 bio_size = bio->bi_iter.bi_size; + const u32 foffset = offset_in_folio(last_folio, bio_size); + bool ret; + + if (IS_ALIGNED(bio_size, blocksize)) + return; + + ret = bio_add_folio(bio, last_folio, round_up(foffset, blocksize) - foffset, foffset); + /* The remaining part should be merged thus never fail. */ + ASSERT(ret); +} + /* * Work queue call back to started compression on a file and pages. * @@ -883,24 +936,22 @@ static void compress_file_range(struct btrfs_work *work) struct btrfs_inode *inode = async_chunk->inode; struct btrfs_fs_info *fs_info = inode->root->fs_info; struct address_space *mapping = inode->vfs_inode.i_mapping; - const u32 min_folio_shift = PAGE_SHIFT + fs_info->block_min_order; + struct compressed_bio *cb = NULL; const u32 min_folio_size = btrfs_min_folio_size(fs_info); u64 blocksize = fs_info->sectorsize; u64 start = async_chunk->start; u64 end = async_chunk->end; u64 actual_end; u64 i_size; + u32 cur_len; int ret = 0; - struct folio **folios = NULL; - unsigned long nr_folios; unsigned long total_compressed = 0; unsigned long total_in = 0; unsigned int loff; - int i; int compress_type = fs_info->compress_type; int compress_level = fs_info->compress_level; - if (unlikely(btrfs_is_shutdown(fs_info))) + if (btrfs_is_shutdown(fs_info)) goto cleanup_and_bail_uncompressed; inode_should_defrag(inode, start, end, end - start + 1, SZ_16K); @@ -915,7 +966,7 @@ static void compress_file_range(struct btrfs_work *work) /* * All the folios should have been locked thus no failure. * - * And even if some folios are missing, btrfs_compress_folios() + * And even if some folios are missing, btrfs_compress_bio() * would handle them correctly, so here just do an ASSERT() check for * early logic errors. */ @@ -935,9 +986,10 @@ static void compress_file_range(struct btrfs_work *work) barrier(); actual_end = min_t(u64, i_size, end + 1); again: - folios = NULL; - nr_folios = (end >> min_folio_shift) - (start >> min_folio_shift) + 1; - nr_folios = min_t(unsigned long, nr_folios, BTRFS_MAX_COMPRESSED >> min_folio_shift); + total_in = 0; + cur_len = min(end + 1 - start, BTRFS_MAX_UNCOMPRESSED); + ret = 0; + cb = NULL; /* * we don't want to send crud past the end of i_size through @@ -952,21 +1004,6 @@ again: if (actual_end <= start) goto cleanup_and_bail_uncompressed; - total_compressed = actual_end - start; - - /* - * Skip compression for a small file range(<=blocksize) that - * isn't an inline extent, since it doesn't save disk space at all. - */ - if (total_compressed <= blocksize && - (start > 0 || end + 1 < inode->disk_i_size)) - goto cleanup_and_bail_uncompressed; - - total_compressed = min_t(unsigned long, total_compressed, - BTRFS_MAX_UNCOMPRESSED); - total_in = 0; - ret = 0; - /* * We do compression for mount -o compress and when the inode has not * been flagged as NOCOMPRESS. This flag can change at any time if we @@ -975,15 +1012,6 @@ again: if (!inode_need_compress(inode, start, end)) goto cleanup_and_bail_uncompressed; - folios = kcalloc(nr_folios, sizeof(struct folio *), GFP_NOFS); - if (!folios) { - /* - * Memory allocation failure is not a fatal error, we can fall - * back to uncompressed code. - */ - goto cleanup_and_bail_uncompressed; - } - if (0 < inode->defrag_compress && inode->defrag_compress < BTRFS_NR_COMPRESS_TYPES) { compress_type = inode->defrag_compress; compress_level = inode->defrag_compress_level; @@ -992,11 +1020,15 @@ again: } /* Compression level is applied here. */ - ret = btrfs_compress_folios(compress_type, compress_level, - inode, start, folios, &nr_folios, &total_in, - &total_compressed); - if (ret) + cb = btrfs_compress_bio(inode, start, cur_len, compress_type, + compress_level, async_chunk->write_flags); + if (IS_ERR(cb)) { + cb = NULL; goto mark_incompressible; + } + + total_compressed = cb->bbio.bio.bi_iter.bi_size; + total_in = cur_len; /* * Zero the tail end of the last folio, as we might be sending it down @@ -1004,7 +1036,7 @@ again: */ loff = (total_compressed & (min_folio_size - 1)); if (loff) - folio_zero_range(folios[nr_folios - 1], loff, min_folio_size - loff); + zero_last_folio(cb); /* * Try to create an inline extent. @@ -1020,11 +1052,13 @@ again: BTRFS_COMPRESS_NONE, NULL, false); else ret = cow_file_range_inline(inode, NULL, start, end, total_compressed, - compress_type, folios[0], false); + compress_type, + bio_first_folio_all(&cb->bbio.bio), false); if (ret <= 0) { + cleanup_compressed_bio(cb); if (ret < 0) mapping_set_error(mapping, -EIO); - goto free_pages; + return; } /* @@ -1032,6 +1066,7 @@ again: * block size boundary so the allocator does sane things. */ total_compressed = ALIGN(total_compressed, blocksize); + round_up_last_block(cb, blocksize); /* * One last check to make sure the compression is really a win, compare @@ -1042,12 +1077,12 @@ again: if (total_compressed + blocksize > total_in) goto mark_incompressible; + /* * The async work queues will take care of doing actual allocation on * disk for these compressed pages, and will submit the bios. */ - ret = add_async_extent(async_chunk, start, total_in, total_compressed, folios, - nr_folios, compress_type); + ret = add_async_extent(async_chunk, start, total_in, cb); BUG_ON(ret); if (start + total_in < end) { start += total_in; @@ -1060,33 +1095,10 @@ mark_incompressible: if (!btrfs_test_opt(fs_info, FORCE_COMPRESS) && !inode->prop_compress) inode->flags |= BTRFS_INODE_NOCOMPRESS; cleanup_and_bail_uncompressed: - ret = add_async_extent(async_chunk, start, end - start + 1, 0, NULL, 0, - BTRFS_COMPRESS_NONE); + ret = add_async_extent(async_chunk, start, end - start + 1, NULL); BUG_ON(ret); -free_pages: - if (folios) { - for (i = 0; i < nr_folios; i++) { - WARN_ON(folios[i]->mapping); - btrfs_free_compr_folio(folios[i]); - } - kfree(folios); - } -} - -static void free_async_extent_pages(struct async_extent *async_extent) -{ - int i; - - if (!async_extent->folios) - return; - - for (i = 0; i < async_extent->nr_folios; i++) { - WARN_ON(async_extent->folios[i]->mapping); - btrfs_free_compr_folio(async_extent->folios[i]); - } - kfree(async_extent->folios); - async_extent->nr_folios = 0; - async_extent->folios = NULL; + if (cb) + cleanup_compressed_bio(cb); } static void submit_uncompressed_range(struct btrfs_inode *inode, @@ -1133,7 +1145,7 @@ static void submit_one_async_extent(struct async_chunk *async_chunk, struct extent_state *cached = NULL; struct extent_map *em; int ret = 0; - bool free_pages = false; + u32 compressed_size; u64 start = async_extent->start; u64 end = async_extent->start + async_extent->ram_size - 1; @@ -1153,17 +1165,14 @@ static void submit_one_async_extent(struct async_chunk *async_chunk, locked_folio = async_chunk->locked_folio; } - if (async_extent->compress_type == BTRFS_COMPRESS_NONE) { - ASSERT(!async_extent->folios); - ASSERT(async_extent->nr_folios == 0); + if (!async_extent->cb) { submit_uncompressed_range(inode, async_extent, locked_folio); - free_pages = true; goto done; } + compressed_size = async_extent->cb->bbio.bio.bi_iter.bi_size; ret = btrfs_reserve_extent(root, async_extent->ram_size, - async_extent->compressed_size, - async_extent->compressed_size, + compressed_size, compressed_size, 0, *alloc_hint, &ins, true, true); if (ret) { /* @@ -1173,7 +1182,8 @@ static void submit_one_async_extent(struct async_chunk *async_chunk, * fall back to uncompressed. */ submit_uncompressed_range(inode, async_extent, locked_folio); - free_pages = true; + cleanup_compressed_bio(async_extent->cb); + async_extent->cb = NULL; goto done; } @@ -1185,7 +1195,9 @@ static void submit_one_async_extent(struct async_chunk *async_chunk, file_extent.ram_bytes = async_extent->ram_size; file_extent.num_bytes = async_extent->ram_size; file_extent.offset = 0; - file_extent.compression = async_extent->compress_type; + file_extent.compression = async_extent->cb->compress_type; + + async_extent->cb->bbio.bio.bi_iter.bi_sector = ins.objectid >> SECTOR_SHIFT; em = btrfs_create_io_em(inode, start, &file_extent, BTRFS_ORDERED_COMPRESSED); if (IS_ERR(em)) { @@ -1201,22 +1213,20 @@ static void submit_one_async_extent(struct async_chunk *async_chunk, ret = PTR_ERR(ordered); goto out_free_reserve; } + async_extent->cb->bbio.ordered = ordered; btrfs_dec_block_group_reservations(fs_info, ins.objectid); /* Clear dirty, set writeback and unlock the pages. */ extent_clear_unlock_delalloc(inode, start, end, NULL, &cached, EXTENT_LOCKED | EXTENT_DELALLOC, PAGE_UNLOCK | PAGE_START_WRITEBACK); - btrfs_submit_compressed_write(ordered, - async_extent->folios, /* compressed_folios */ - async_extent->nr_folios, - async_chunk->write_flags, true); + btrfs_submit_bbio(&async_extent->cb->bbio, 0); + async_extent->cb = NULL; + *alloc_hint = ins.objectid + ins.offset; done: if (async_chunk->blkcg_css) kthread_associate_blkcg(NULL); - if (free_pages) - free_async_extent_pages(async_extent); kfree(async_extent); return; @@ -1231,7 +1241,8 @@ out_free_reserve: EXTENT_DEFRAG | EXTENT_DO_ACCOUNTING, PAGE_UNLOCK | PAGE_START_WRITEBACK | PAGE_END_WRITEBACK); - free_async_extent_pages(async_extent); + if (async_extent->cb) + cleanup_compressed_bio(async_extent->cb); if (async_chunk->blkcg_css) kthread_associate_blkcg(NULL); btrfs_debug(fs_info, @@ -1274,6 +1285,133 @@ u64 btrfs_get_extent_allocation_hint(struct btrfs_inode *inode, u64 start, } /* + * Handle COW for one range. + * + * @ins: The key representing the allocated range. + * @file_offset: The file offset of the COW range + * @num_bytes: The expected length of the COW range + * The actually allocated length can be smaller than it. + * @min_alloc_size: The minimal extent size. + * @alloc_hint: The hint for the extent allocator. + * @ret_alloc_size: The COW range handles by this function. + * + * Return 0 if everything is fine and update @ret_alloc_size updated. The + * range is still locked, and caller should unlock the range after everything + * is done or for error handling. + * + * Return <0 for error and @is updated for where the extra cleanup should + * happen. The range [file_offset, file_offset + ret_alloc_size) will be + * cleaned up by this function. + */ +static int cow_one_range(struct btrfs_inode *inode, struct folio *locked_folio, + struct btrfs_key *ins, struct extent_state **cached, + u64 file_offset, u32 num_bytes, u32 min_alloc_size, + u64 alloc_hint, u32 *ret_alloc_size) +{ + struct btrfs_root *root = inode->root; + struct btrfs_fs_info *fs_info = root->fs_info; + struct btrfs_ordered_extent *ordered; + struct btrfs_file_extent file_extent; + struct extent_map *em; + u32 cur_len = 0; + u64 cur_end; + int ret; + + ret = btrfs_reserve_extent(root, num_bytes, num_bytes, min_alloc_size, + 0, alloc_hint, ins, true, true); + if (ret < 0) { + *ret_alloc_size = cur_len; + return ret; + } + + cur_len = ins->offset; + cur_end = file_offset + cur_len - 1; + + file_extent.disk_bytenr = ins->objectid; + file_extent.disk_num_bytes = ins->offset; + file_extent.num_bytes = ins->offset; + file_extent.ram_bytes = ins->offset; + file_extent.offset = 0; + file_extent.compression = BTRFS_COMPRESS_NONE; + + /* + * Locked range will be released either during error clean up (inside + * this function or by the caller for previously successful ranges) or + * after the whole range is finished. + */ + btrfs_lock_extent(&inode->io_tree, file_offset, cur_end, cached); + em = btrfs_create_io_em(inode, file_offset, &file_extent, BTRFS_ORDERED_REGULAR); + if (IS_ERR(em)) { + ret = PTR_ERR(em); + goto free_reserved; + } + btrfs_free_extent_map(em); + + ordered = btrfs_alloc_ordered_extent(inode, file_offset, &file_extent, + 1U << BTRFS_ORDERED_REGULAR); + if (IS_ERR(ordered)) { + btrfs_drop_extent_map_range(inode, file_offset, cur_end, false); + ret = PTR_ERR(ordered); + goto free_reserved; + } + + if (btrfs_is_data_reloc_root(root)) { + ret = btrfs_reloc_clone_csums(ordered); + + /* + * Only drop cache here, and process as normal. + * + * We must not allow extent_clear_unlock_delalloc() at + * free_reserved label to free meta of this ordered extent, as + * its meta should be freed by btrfs_finish_ordered_io(). + * + * So we must continue until @start is increased to + * skip current ordered extent. + */ + if (ret) + btrfs_drop_extent_map_range(inode, file_offset, + cur_end, false); + } + btrfs_put_ordered_extent(ordered); + btrfs_dec_block_group_reservations(fs_info, ins->objectid); + /* + * Error handling for btrfs_reloc_clone_csums(). + * + * Treat the range as finished, thus only clear EXTENT_LOCKED | EXTENT_DELALLOC. + * The accounting will be done by ordered extents. + */ + if (unlikely(ret < 0)) { + btrfs_cleanup_ordered_extents(inode, file_offset, cur_len); + extent_clear_unlock_delalloc(inode, file_offset, cur_end, locked_folio, cached, + EXTENT_LOCKED | EXTENT_DELALLOC, + PAGE_UNLOCK | PAGE_START_WRITEBACK | + PAGE_END_WRITEBACK); + mapping_set_error(inode->vfs_inode.i_mapping, -EIO); + } + *ret_alloc_size = cur_len; + return ret; + +free_reserved: + extent_clear_unlock_delalloc(inode, file_offset, cur_end, locked_folio, cached, + EXTENT_LOCKED | EXTENT_DELALLOC | + EXTENT_DELALLOC_NEW | + EXTENT_DEFRAG | EXTENT_DO_ACCOUNTING, + PAGE_UNLOCK | PAGE_START_WRITEBACK | + PAGE_END_WRITEBACK); + btrfs_qgroup_free_data(inode, NULL, file_offset, cur_len, NULL); + btrfs_dec_block_group_reservations(fs_info, ins->objectid); + btrfs_free_reserved_extent(fs_info, ins->objectid, ins->offset, true); + mapping_set_error(inode->vfs_inode.i_mapping, -EIO); + *ret_alloc_size = cur_len; + /* + * We should not return -EAGAIN where it's a special return code for + * zoned to catch btrfs_reserved_extent(). + */ + ASSERT(ret != -EAGAIN); + return ret; +} + +/* * when extent_io.c finds a delayed allocation range in the file, * the call backs end up in this code. The basic idea is to * allocate extents on disk for the range, and create ordered data structs @@ -1309,16 +1447,15 @@ static noinline int cow_file_range(struct btrfs_inode *inode, u64 alloc_hint = 0; u64 orig_start = start; u64 num_bytes; - u64 cur_alloc_size = 0; - u64 min_alloc_size; - u64 blocksize = fs_info->sectorsize; + u32 min_alloc_size; + u32 blocksize = fs_info->sectorsize; + u32 cur_alloc_size = 0; struct btrfs_key ins; - struct extent_map *em; unsigned clear_bits; unsigned long page_ops; int ret = 0; - if (unlikely(btrfs_is_shutdown(fs_info))) { + if (btrfs_is_shutdown(fs_info)) { ret = -EIO; goto out_unlock; } @@ -1382,16 +1519,14 @@ static noinline int cow_file_range(struct btrfs_inode *inode, min_alloc_size = fs_info->sectorsize; while (num_bytes > 0) { - struct btrfs_ordered_extent *ordered; - struct btrfs_file_extent file_extent; + ret = cow_one_range(inode, locked_folio, &ins, &cached, start, + num_bytes, min_alloc_size, alloc_hint, &cur_alloc_size); - ret = btrfs_reserve_extent(root, num_bytes, num_bytes, - min_alloc_size, 0, alloc_hint, - &ins, true, true); if (ret == -EAGAIN) { /* - * btrfs_reserve_extent only returns -EAGAIN for zoned - * file systems, which is an indication that there are + * cow_one_range() only returns -EAGAIN for zoned + * file systems (from btrfs_reserve_extent()), which + * is an indication that there are * no active zones to allocate from at the moment. * * If this is the first loop iteration, wait for at @@ -1420,79 +1555,14 @@ static noinline int cow_file_range(struct btrfs_inode *inode, } if (ret < 0) goto out_unlock; - cur_alloc_size = ins.offset; - - file_extent.disk_bytenr = ins.objectid; - file_extent.disk_num_bytes = ins.offset; - file_extent.num_bytes = ins.offset; - file_extent.ram_bytes = ins.offset; - file_extent.offset = 0; - file_extent.compression = BTRFS_COMPRESS_NONE; - - /* - * Locked range will be released either during error clean up or - * after the whole range is finished. - */ - btrfs_lock_extent(&inode->io_tree, start, start + cur_alloc_size - 1, - &cached); - - em = btrfs_create_io_em(inode, start, &file_extent, - BTRFS_ORDERED_REGULAR); - if (IS_ERR(em)) { - btrfs_unlock_extent(&inode->io_tree, start, - start + cur_alloc_size - 1, &cached); - ret = PTR_ERR(em); - goto out_reserve; - } - btrfs_free_extent_map(em); - ordered = btrfs_alloc_ordered_extent(inode, start, &file_extent, - 1U << BTRFS_ORDERED_REGULAR); - if (IS_ERR(ordered)) { - btrfs_unlock_extent(&inode->io_tree, start, - start + cur_alloc_size - 1, &cached); - ret = PTR_ERR(ordered); - goto out_drop_extent_cache; - } - - if (btrfs_is_data_reloc_root(root)) { - ret = btrfs_reloc_clone_csums(ordered); + /* We should not allocate an extent larger than requested.*/ + ASSERT(cur_alloc_size <= num_bytes); - /* - * Only drop cache here, and process as normal. - * - * We must not allow extent_clear_unlock_delalloc() - * at out_unlock label to free meta of this ordered - * extent, as its meta should be freed by - * btrfs_finish_ordered_io(). - * - * So we must continue until @start is increased to - * skip current ordered extent. - */ - if (ret) - btrfs_drop_extent_map_range(inode, start, - start + cur_alloc_size - 1, - false); - } - btrfs_put_ordered_extent(ordered); - - btrfs_dec_block_group_reservations(fs_info, ins.objectid); - - if (num_bytes < cur_alloc_size) - num_bytes = 0; - else - num_bytes -= cur_alloc_size; + num_bytes -= cur_alloc_size; alloc_hint = ins.objectid + ins.offset; start += cur_alloc_size; cur_alloc_size = 0; - - /* - * btrfs_reloc_clone_csums() error, since start is increased - * extent_clear_unlock_delalloc() at out_unlock label won't - * free metadata of current ordered extent, we're OK to exit. - */ - if (ret) - goto out_unlock; } extent_clear_unlock_delalloc(inode, orig_start, end, locked_folio, &cached, EXTENT_LOCKED | EXTENT_DELALLOC, page_ops); @@ -1501,11 +1571,6 @@ done: *done_offset = end; return ret; -out_drop_extent_cache: - btrfs_drop_extent_map_range(inode, start, start + cur_alloc_size - 1, false); -out_reserve: - btrfs_dec_block_group_reservations(fs_info, ins.objectid); - btrfs_free_reserved_extent(fs_info, ins.objectid, ins.offset, true); out_unlock: /* * Now, we have three regions to clean up: @@ -1542,24 +1607,9 @@ out_unlock: page_ops = PAGE_UNLOCK | PAGE_START_WRITEBACK | PAGE_END_WRITEBACK; /* - * For the range (2). If we reserved an extent for our delalloc range - * (or a subrange) and failed to create the respective ordered extent, - * then it means that when we reserved the extent we decremented the - * extent's size from the data space_info's bytes_may_use counter and - * incremented the space_info's bytes_reserved counter by the same - * amount. We must make sure extent_clear_unlock_delalloc() does not try - * to decrement again the data space_info's bytes_may_use counter, - * therefore we do not pass it the flag EXTENT_CLEAR_DATA_RESV. - */ - if (cur_alloc_size) { - extent_clear_unlock_delalloc(inode, start, - start + cur_alloc_size - 1, - locked_folio, &cached, clear_bits, - page_ops); - btrfs_qgroup_free_data(inode, NULL, start, cur_alloc_size, NULL); - } - - /* + * For the range (2) the error handling is done by cow_one_range() itself. + * Nothing needs to be done. + * * For the range (3). We never touched the region. In addition to the * clear_bits above, we add EXTENT_CLEAR_DATA_RESV to release the data * space_info's bytes_may_use counter, reserved in @@ -1574,7 +1624,7 @@ out_unlock: end - start - cur_alloc_size + 1, NULL); } btrfs_err(fs_info, -"%s failed, root=%llu inode=%llu start=%llu len=%llu cur_offset=%llu cur_alloc_size=%llu: %d", +"%s failed, root=%llu inode=%llu start=%llu len=%llu cur_offset=%llu cur_alloc_size=%u: %d", __func__, btrfs_root_id(inode->root), btrfs_ino(inode), orig_start, end + 1 - orig_start, start, cur_alloc_size, ret); @@ -2071,7 +2121,7 @@ static noinline int run_delalloc_nocow(struct btrfs_inode *inode, */ ASSERT(!btrfs_is_zoned(fs_info) || btrfs_is_data_reloc_root(root)); - if (unlikely(btrfs_is_shutdown(fs_info))) { + if (btrfs_is_shutdown(fs_info)) { ret = -EIO; goto error; } @@ -2371,7 +2421,6 @@ int btrfs_run_delalloc_range(struct btrfs_inode *inode, struct folio *locked_fol u64 start, u64 end, struct writeback_control *wbc) { const bool zoned = btrfs_is_zoned(inode->root->fs_info); - int ret; /* * The range must cover part of the @locked_folio, or a return of 1 @@ -2380,10 +2429,8 @@ int btrfs_run_delalloc_range(struct btrfs_inode *inode, struct folio *locked_fol ASSERT(!(end <= folio_pos(locked_folio) || start >= folio_next_pos(locked_folio))); - if (should_nocow(inode, start, end)) { - ret = run_delalloc_nocow(inode, locked_folio, start, end); - return ret; - } + if (should_nocow(inode, start, end)) + return run_delalloc_nocow(inode, locked_folio, start, end); if (btrfs_inode_can_compress(inode) && inode_need_compress(inode, start, end) && @@ -2391,11 +2438,9 @@ int btrfs_run_delalloc_range(struct btrfs_inode *inode, struct folio *locked_fol return 1; if (zoned) - ret = run_delalloc_cow(inode, locked_folio, start, end, wbc, - true); + return run_delalloc_cow(inode, locked_folio, start, end, wbc, true); else - ret = cow_file_range(inode, locked_folio, start, end, NULL, 0); - return ret; + return cow_file_range(inode, locked_folio, start, end, NULL, 0); } void btrfs_split_delalloc_extent(struct btrfs_inode *inode, @@ -3006,7 +3051,7 @@ static int insert_reserved_file_extent(struct btrfs_trans_handle *trans, drop_args.extent_item_size = sizeof(*stack_fi); ret = btrfs_drop_extents(trans, root, inode, &drop_args); if (ret) - goto out; + return ret; if (!drop_args.extent_inserted) { ins.objectid = btrfs_ino(inode); @@ -3016,7 +3061,7 @@ static int insert_reserved_file_extent(struct btrfs_trans_handle *trans, ret = btrfs_insert_empty_item(trans, root, path, &ins, sizeof(*stack_fi)); if (ret) - goto out; + return ret; } leaf = path->nodes[0]; btrfs_set_stack_file_extent_generation(stack_fi, trans->transid); @@ -3051,13 +3096,11 @@ static int insert_reserved_file_extent(struct btrfs_trans_handle *trans, ret = btrfs_inode_set_file_extent_range(inode, file_pos, ram_bytes); if (ret) - goto out; + return ret; - ret = btrfs_alloc_reserved_file_extent(trans, root, btrfs_ino(inode), - file_pos - offset, - qgroup_reserved, &ins); -out: - return ret; + return btrfs_alloc_reserved_file_extent(trans, root, btrfs_ino(inode), + file_pos - offset, + qgroup_reserved, &ins); } static void btrfs_release_delalloc_bytes(struct btrfs_fs_info *fs_info, @@ -3225,19 +3268,21 @@ int btrfs_finish_one_ordered(struct btrfs_ordered_extent *ordered_extent) logical_len); btrfs_zoned_release_data_reloc_bg(fs_info, ordered_extent->disk_bytenr, ordered_extent->disk_num_bytes); + if (unlikely(ret < 0)) { + btrfs_abort_transaction(trans, ret); + goto out; + } } else { BUG_ON(root == fs_info->tree_root); ret = insert_ordered_extent_file_extent(trans, ordered_extent); - if (!ret) { - clear_reserved_extent = false; - btrfs_release_delalloc_bytes(fs_info, - ordered_extent->disk_bytenr, - ordered_extent->disk_num_bytes); + if (unlikely(ret < 0)) { + btrfs_abort_transaction(trans, ret); + goto out; } - } - if (unlikely(ret < 0)) { - btrfs_abort_transaction(trans, ret); - goto out; + clear_reserved_extent = false; + btrfs_release_delalloc_bytes(fs_info, + ordered_extent->disk_bytenr, + ordered_extent->disk_num_bytes); } ret = btrfs_unpin_extent_cache(inode, ordered_extent->file_offset, @@ -3335,7 +3380,7 @@ out: btrfs_discard_extent(fs_info, ordered_extent->disk_bytenr, ordered_extent->disk_num_bytes, - NULL); + NULL, true); btrfs_free_reserved_extent(fs_info, ordered_extent->disk_bytenr, ordered_extent->disk_num_bytes, true); @@ -3417,20 +3462,19 @@ void btrfs_calculate_block_csum_pages(struct btrfs_fs_info *fs_info, const u32 blocksize = fs_info->sectorsize; const u32 step = min(blocksize, PAGE_SIZE); const u32 nr_steps = blocksize / step; - SHASH_DESC_ON_STACK(shash, fs_info->csum_shash); + struct btrfs_csum_ctx csum; - shash->tfm = fs_info->csum_shash; - crypto_shash_init(shash); + btrfs_csum_init(&csum, fs_info->csum_type); for (int i = 0; i < nr_steps; i++) { const phys_addr_t paddr = paddrs[i]; void *kaddr; ASSERT(offset_in_page(paddr) + step <= PAGE_SIZE); kaddr = kmap_local_page(phys_to_page(paddr)) + offset_in_page(paddr); - crypto_shash_update(shash, kaddr, step); + btrfs_csum_update(&csum, kaddr, step); kunmap_local(kaddr); } - crypto_shash_final(shash, dest); + btrfs_csum_final(&csum, dest); } /* @@ -5571,11 +5615,8 @@ void btrfs_evict_inode(struct inode *inode) trace_btrfs_inode_evict(inode); - if (!root) { - fsverity_cleanup_inode(inode); - clear_inode(inode); - return; - } + if (!root) + goto clear_inode; fs_info = inode_to_fs_info(inode); evict_inode_truncate_pages(inode); @@ -5675,7 +5716,7 @@ out: * to retry these periodically in the future. */ btrfs_remove_delayed_node(BTRFS_I(inode)); - fsverity_cleanup_inode(inode); +clear_inode: clear_inode(inode); } @@ -6386,16 +6427,21 @@ static int btrfs_dirty_inode(struct btrfs_inode *inode) * We need our own ->update_time so that we can return error on ENOSPC for * updating the inode in the case of file write and mmap writes. */ -static int btrfs_update_time(struct inode *inode, int flags) +static int btrfs_update_time(struct inode *inode, enum fs_update_time type, + unsigned int flags) { struct btrfs_root *root = BTRFS_I(inode)->root; - bool dirty; + int dirty; if (btrfs_root_readonly(root)) return -EROFS; + if (flags & IOCB_NOWAIT) + return -EAGAIN; - dirty = inode_update_timestamps(inode, flags); - return dirty ? btrfs_dirty_inode(BTRFS_I(inode)) : 0; + dirty = inode_update_time(inode, type, flags); + if (dirty <= 0) + return dirty; + return btrfs_dirty_inode(BTRFS_I(inode)); } /* @@ -7131,7 +7177,7 @@ struct extent_map *btrfs_get_extent(struct btrfs_inode *inode, read_unlock(&em_tree->lock); if (em) { - if (em->start > start || em->start + em->len <= start) + if (em->start > start || btrfs_extent_map_end(em) <= start) btrfs_free_extent_map(em); else if (em->disk_bytenr == EXTENT_MAP_INLINE && folio) btrfs_free_extent_map(em); @@ -8101,9 +8147,6 @@ static void init_once(void *foo) struct btrfs_inode *ei = foo; inode_init_once(&ei->vfs_inode); -#ifdef CONFIG_FS_VERITY - ei->i_verity_info = NULL; -#endif } void __cold btrfs_destroy_cachep(void) @@ -9784,12 +9827,12 @@ ssize_t btrfs_do_encoded_write(struct kiocb *iocb, struct iov_iter *from, struct extent_state *cached_state = NULL; struct btrfs_ordered_extent *ordered; struct btrfs_file_extent file_extent; + struct compressed_bio *cb = NULL; int compression; size_t orig_count; + const u32 min_folio_size = btrfs_min_folio_size(fs_info); u64 start, end; u64 num_bytes, ram_bytes, disk_num_bytes; - unsigned long nr_folios, i; - struct folio **folios; struct btrfs_key ins; bool extent_reserved = false; struct extent_map *em; @@ -9878,39 +9921,46 @@ ssize_t btrfs_do_encoded_write(struct kiocb *iocb, struct iov_iter *from, * isn't. */ disk_num_bytes = ALIGN(orig_count, fs_info->sectorsize); - nr_folios = DIV_ROUND_UP(disk_num_bytes, PAGE_SIZE); - folios = kvcalloc(nr_folios, sizeof(struct folio *), GFP_KERNEL_ACCOUNT); - if (!folios) - return -ENOMEM; - for (i = 0; i < nr_folios; i++) { - size_t bytes = min_t(size_t, PAGE_SIZE, iov_iter_count(from)); + + cb = btrfs_alloc_compressed_write(inode, start, num_bytes); + for (int i = 0; i * min_folio_size < disk_num_bytes; i++) { + struct folio *folio; + size_t bytes = min(min_folio_size, iov_iter_count(from)); char *kaddr; - folios[i] = folio_alloc(GFP_KERNEL_ACCOUNT, 0); - if (!folios[i]) { + folio = btrfs_alloc_compr_folio(fs_info); + if (!folio) { ret = -ENOMEM; - goto out_folios; + goto out_cb; } - kaddr = kmap_local_folio(folios[i], 0); - if (copy_from_iter(kaddr, bytes, from) != bytes) { - kunmap_local(kaddr); + kaddr = kmap_local_folio(folio, 0); + ret = copy_from_iter(kaddr, bytes, from); + kunmap_local(kaddr); + if (ret != bytes) { + folio_put(folio); ret = -EFAULT; - goto out_folios; + 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 (unlikely(!ret)) { + folio_put(folio); + ret = -EINVAL; + goto out_cb; } - if (bytes < PAGE_SIZE) - memset(kaddr + bytes, 0, PAGE_SIZE - bytes); - kunmap_local(kaddr); } + ASSERT(cb->bbio.bio.bi_iter.bi_size == disk_num_bytes); for (;;) { ret = btrfs_wait_ordered_range(inode, start, num_bytes); if (ret) - goto out_folios; + goto out_cb; ret = invalidate_inode_pages2_range(inode->vfs_inode.i_mapping, start >> PAGE_SHIFT, end >> PAGE_SHIFT); if (ret) - goto out_folios; + goto out_cb; btrfs_lock_extent(io_tree, start, end, &cached_state); ordered = btrfs_lookup_ordered_range(inode, start, num_bytes); if (!ordered && @@ -9942,7 +9992,8 @@ ssize_t btrfs_do_encoded_write(struct kiocb *iocb, struct iov_iter *from, encoded->unencoded_offset == 0 && can_cow_file_range_inline(inode, start, encoded->len, orig_count)) { ret = __cow_file_range_inline(inode, encoded->len, - orig_count, compression, folios[0], + orig_count, compression, + bio_first_folio_all(&cb->bbio.bio), true); if (ret <= 0) { if (ret == 0) @@ -9987,7 +10038,7 @@ ssize_t btrfs_do_encoded_write(struct kiocb *iocb, struct iov_iter *from, btrfs_delalloc_release_extents(inode, num_bytes); - btrfs_submit_compressed_write(ordered, folios, nr_folios, 0, false); + btrfs_submit_compressed_write(ordered, cb); ret = orig_count; goto out; @@ -10009,12 +10060,9 @@ out_free_data_space: btrfs_free_reserved_data_space_noquota(inode, disk_num_bytes); out_unlock: btrfs_unlock_extent(io_tree, start, end, &cached_state); -out_folios: - for (i = 0; i < nr_folios; i++) { - if (folios[i]) - folio_put(folios[i]); - } - kvfree(folios); +out_cb: + if (cb) + cleanup_compressed_bio(cb); out: if (ret >= 0) iocb->ki_pos += encoded->len; @@ -10605,6 +10653,7 @@ static const struct file_operations btrfs_dir_file_operations = { #endif .release = btrfs_release_file, .fsync = btrfs_sync_file, + .setlease = generic_setlease, }; /* diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index acb484546b1d..a6cc2d3b414c 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -815,75 +815,6 @@ free_pending: return ret; } -/* copy of may_delete in fs/namei.c() - * Check whether we can remove a link victim from directory dir, check - * whether the type of victim is right. - * 1. We can't do it if dir is read-only (done in permission()) - * 2. We should have write and exec permissions on dir - * 3. We can't remove anything from append-only dir - * 4. We can't do anything with immutable dir (done in permission()) - * 5. If the sticky bit on dir is set we should either - * a. be owner of dir, or - * b. be owner of victim, or - * c. have CAP_FOWNER capability - * 6. If the victim is append-only or immutable we can't do anything with - * links pointing to it. - * 7. If we were asked to remove a directory and victim isn't one - ENOTDIR. - * 8. If we were asked to remove a non-directory and victim isn't one - EISDIR. - * 9. We can't remove a root or mountpoint. - * 10. We don't allow removal of NFS sillyrenamed files; it's handled by - * nfs_async_unlink(). - */ - -static int btrfs_may_delete(struct mnt_idmap *idmap, - struct inode *dir, struct dentry *victim, int isdir) -{ - int ret; - - if (d_really_is_negative(victim)) - return -ENOENT; - - /* The @victim is not inside @dir. */ - if (d_inode(victim->d_parent) != dir) - return -EINVAL; - audit_inode_child(dir, victim, AUDIT_TYPE_CHILD_DELETE); - - ret = inode_permission(idmap, dir, MAY_WRITE | MAY_EXEC); - if (ret) - return ret; - if (IS_APPEND(dir)) - return -EPERM; - if (check_sticky(idmap, dir, d_inode(victim)) || - IS_APPEND(d_inode(victim)) || IS_IMMUTABLE(d_inode(victim)) || - IS_SWAPFILE(d_inode(victim))) - return -EPERM; - if (isdir) { - if (!d_is_dir(victim)) - return -ENOTDIR; - if (IS_ROOT(victim)) - return -EBUSY; - } else if (d_is_dir(victim)) - return -EISDIR; - if (IS_DEADDIR(dir)) - return -ENOENT; - if (victim->d_flags & DCACHE_NFSFS_RENAMED) - return -EBUSY; - return 0; -} - -/* copy of may_create in fs/namei.c() */ -static inline int btrfs_may_create(struct mnt_idmap *idmap, - struct inode *dir, const struct dentry *child) -{ - if (d_really_is_positive(child)) - return -EEXIST; - if (IS_DEADDIR(dir)) - return -ENOENT; - if (!fsuidgid_has_mapping(dir->i_sb, idmap)) - return -EOVERFLOW; - return inode_permission(idmap, dir, MAY_WRITE | MAY_EXEC); -} - /* * Create a new subvolume below @parent. This is largely modeled after * sys_mkdirat and vfs_mkdir, but we only do a single component lookup @@ -905,7 +836,7 @@ static noinline int btrfs_mksubvol(struct dentry *parent, if (IS_ERR(dentry)) return PTR_ERR(dentry); - ret = btrfs_may_create(idmap, dir, dentry); + ret = may_create_dentry(idmap, dir, dentry); if (ret) goto out_dput; @@ -1176,7 +1107,7 @@ static noinline int __btrfs_ioctl_snap_create(struct file *file, bool readonly, struct btrfs_qgroup_inherit *inherit) { - int ret = 0; + int ret; struct qstr qname = QSTR_INIT(name, strlen(name)); if (!S_ISDIR(file_inode(file)->i_mode)) @@ -1184,7 +1115,7 @@ static noinline int __btrfs_ioctl_snap_create(struct file *file, ret = mnt_want_write_file(file); if (ret) - goto out; + return ret; if (strchr(name, '/')) { ret = -EINVAL; @@ -1236,7 +1167,6 @@ static noinline int __btrfs_ioctl_snap_create(struct file *file, } out_drop_write: mnt_drop_write_file(file); -out: return ret; } @@ -1352,14 +1282,14 @@ static noinline int btrfs_ioctl_subvol_setflags(struct file *file, struct btrfs_trans_handle *trans; u64 root_flags; u64 flags; - int ret = 0; + int ret; if (!inode_owner_or_capable(file_mnt_idmap(file), inode)) return -EPERM; ret = mnt_want_write_file(file); if (ret) - goto out; + return ret; if (btrfs_ino(BTRFS_I(inode)) != BTRFS_FIRST_FREE_OBJECTID) { ret = -EINVAL; @@ -1428,7 +1358,6 @@ out_drop_sem: up_write(&fs_info->subvol_sem); out_drop_write: mnt_drop_write_file(file); -out: return ret; } @@ -1494,10 +1423,8 @@ static noinline int copy_to_sk(struct btrfs_path *path, continue; if (sizeof(sh) + item_len > *buf_size) { - if (*num_found) { - ret = 1; - goto out; - } + if (*num_found) + return 1; /* * return one empty item back for v1, which does not @@ -1509,10 +1436,8 @@ static noinline int copy_to_sk(struct btrfs_path *path, ret = -EOVERFLOW; } - if (sizeof(sh) + item_len + *sk_offset > *buf_size) { - ret = 1; - goto out; - } + if (sizeof(sh) + item_len + *sk_offset > *buf_size) + return 1; sh.objectid = key->objectid; sh.type = key->type; @@ -1526,10 +1451,8 @@ static noinline int copy_to_sk(struct btrfs_path *path, * problem. Otherwise we'll fault and then copy the buffer in * properly this next time through */ - if (copy_to_user_nofault(ubuf + *sk_offset, &sh, sizeof(sh))) { - ret = 0; - goto out; - } + if (copy_to_user_nofault(ubuf + *sk_offset, &sh, sizeof(sh))) + return 0; *sk_offset += sizeof(sh); @@ -1541,22 +1464,20 @@ static noinline int copy_to_sk(struct btrfs_path *path, */ if (read_extent_buffer_to_user_nofault(leaf, up, item_off, item_len)) { - ret = 0; *sk_offset -= sizeof(sh); - goto out; + return 0; } *sk_offset += item_len; } (*num_found)++; - if (ret) /* -EOVERFLOW from above */ - goto out; + /* -EOVERFLOW from above. */ + if (ret) + return ret; - if (*num_found >= sk->nr_items) { - ret = 1; - goto out; - } + if (*num_found >= sk->nr_items) + return 1; } advance_key: ret = 0; @@ -1576,7 +1497,7 @@ advance_key: key->objectid++; } else ret = 1; -out: + /* * 0: all items from this leaf copied, continue with next * 1: * more items can be copied, but unused buffer is too small @@ -2420,7 +2341,7 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file, } /* check if subvolume may be deleted by a user */ - ret = btrfs_may_delete(idmap, dir, dentry, 1); + ret = may_delete_dentry(idmap, dir, dentry, true); if (ret) goto out_end_removing; @@ -5000,7 +4921,7 @@ out_acct: int btrfs_uring_cmd(struct io_uring_cmd *cmd, unsigned int issue_flags) { - if (unlikely(btrfs_is_shutdown(inode_to_fs_info(file_inode(cmd->file))))) + if (btrfs_is_shutdown(inode_to_fs_info(file_inode(cmd->file)))) return -EIO; switch (cmd->cmd_op) { diff --git a/fs/btrfs/locking.c b/fs/btrfs/locking.c index 0035851d72b0..e3df5ca0b552 100644 --- a/fs/btrfs/locking.c +++ b/fs/btrfs/locking.c @@ -73,6 +73,7 @@ static struct btrfs_lockdep_keyset { { .id = BTRFS_FREE_SPACE_TREE_OBJECTID, DEFINE_NAME("free-space") }, { .id = BTRFS_BLOCK_GROUP_TREE_OBJECTID, DEFINE_NAME("block-group") }, { .id = BTRFS_RAID_STRIPE_TREE_OBJECTID, DEFINE_NAME("raid-stripe") }, + { .id = BTRFS_REMAP_TREE_OBJECTID, DEFINE_NAME("remap") }, { .id = 0, DEFINE_NAME("tree") }, }; diff --git a/fs/btrfs/lzo.c b/fs/btrfs/lzo.c index 4758f66da449..8e20497afffe 100644 --- a/fs/btrfs/lzo.c +++ b/fs/btrfs/lzo.c @@ -123,126 +123,188 @@ static inline size_t read_compress_length(const char *buf) } /* + * Write data into @out_folio and queue it into @out_bio. + * + * Return 0 if everything is fine and @total_out will be increased. + * Return <0 for error. + * + * The @out_folio can be NULL after a full folio is queued. + * Thus the caller should check and allocate a new folio when needed. + */ +static int write_and_queue_folio(struct bio *out_bio, struct folio **out_folio, + u32 *total_out, u32 write_len) +{ + const u32 fsize = folio_size(*out_folio); + const u32 foffset = offset_in_folio(*out_folio, *total_out); + + ASSERT(out_folio && *out_folio); + /* Should not cross folio boundary. */ + ASSERT(foffset + write_len <= fsize); + + /* We can not use bio_add_folio_nofail() which doesn't do any merge. */ + if (!bio_add_folio(out_bio, *out_folio, write_len, foffset)) { + /* + * We have allocated a bio that havs BTRFS_MAX_COMPRESSED_PAGES + * vecs, and all ranges inside the same folio should have been + * merged. If bio_add_folio() still failed, that means we have + * reached the bvec limits. + * + * This should only happen at the beginning of a folio, and + * caller is responsible for releasing the folio, since it's + * not yet queued into the bio. + */ + ASSERT(IS_ALIGNED(*total_out, fsize)); + return -E2BIG; + } + + *total_out += write_len; + /* + * The full folio has been filled and queued, reset @out_folio to NULL, + * so that error handling is fully handled by the bio. + */ + if (IS_ALIGNED(*total_out, fsize)) + *out_folio = NULL; + return 0; +} + +/* + * Copy compressed data to bio. + * + * @out_bio: The bio that will contain all the compressed data. + * @compressed_data: The compressed data of this segment. + * @compressed_size: The size of the compressed data. + * @out_folio: The current output folio, will be updated if a new + * folio is allocated. + * @total_out: The total bytes of current output. + * @max_out: The maximum size of the compressed data. + * * Will do: * * - Write a segment header into the destination * - Copy the compressed buffer into the destination * - Make sure we have enough space in the last sector to fit a segment header * If not, we will pad at most (LZO_LEN (4)) - 1 bytes of zeros. + * - If a full folio is filled, it will be queued into @out_bio, and @out_folio + * will be updated. * * Will allocate new pages when needed. */ -static int copy_compressed_data_to_page(struct btrfs_fs_info *fs_info, - char *compressed_data, - size_t compressed_size, - struct folio **out_folios, - unsigned long max_nr_folio, - u32 *cur_out) +static int copy_compressed_data_to_bio(struct btrfs_fs_info *fs_info, + struct bio *out_bio, + const char *compressed_data, + size_t compressed_size, + struct folio **out_folio, + u32 *total_out, u32 max_out) { const u32 sectorsize = fs_info->sectorsize; - const u32 min_folio_shift = PAGE_SHIFT + fs_info->block_min_order; + const u32 sectorsize_bits = fs_info->sectorsize_bits; + const u32 fsize = btrfs_min_folio_size(fs_info); + const u32 old_size = out_bio->bi_iter.bi_size; + u32 copy_start; u32 sector_bytes_left; - u32 orig_out; - struct folio *cur_folio; char *kaddr; + int ret; - if ((*cur_out >> min_folio_shift) >= max_nr_folio) - return -E2BIG; + ASSERT(out_folio); + + /* There should be at least a lzo header queued. */ + ASSERT(old_size); + ASSERT(old_size == *total_out); /* * We never allow a segment header crossing sector boundary, previous * run should ensure we have enough space left inside the sector. */ - ASSERT((*cur_out / sectorsize) == (*cur_out + LZO_LEN - 1) / sectorsize); + ASSERT((old_size >> sectorsize_bits) == (old_size + LZO_LEN - 1) >> sectorsize_bits); - cur_folio = out_folios[*cur_out >> min_folio_shift]; - /* Allocate a new page */ - if (!cur_folio) { - cur_folio = btrfs_alloc_compr_folio(fs_info); - if (!cur_folio) + if (!*out_folio) { + *out_folio = btrfs_alloc_compr_folio(fs_info); + if (!*out_folio) return -ENOMEM; - out_folios[*cur_out >> min_folio_shift] = cur_folio; } - kaddr = kmap_local_folio(cur_folio, offset_in_folio(cur_folio, *cur_out)); + /* Write the segment header first. */ + kaddr = kmap_local_folio(*out_folio, offset_in_folio(*out_folio, *total_out)); write_compress_length(kaddr, compressed_size); - *cur_out += LZO_LEN; - - orig_out = *cur_out; + kunmap_local(kaddr); + ret = write_and_queue_folio(out_bio, out_folio, total_out, LZO_LEN); + if (ret < 0) + return ret; - /* Copy compressed data */ - while (*cur_out - orig_out < compressed_size) { - u32 copy_len = min_t(u32, sectorsize - *cur_out % sectorsize, - orig_out + compressed_size - *cur_out); + copy_start = *total_out; - kunmap_local(kaddr); + /* Copy compressed data. */ + while (*total_out - copy_start < compressed_size) { + u32 copy_len = min_t(u32, sectorsize - *total_out % sectorsize, + copy_start + compressed_size - *total_out); + u32 foffset = *total_out & (fsize - 1); - if ((*cur_out >> min_folio_shift) >= max_nr_folio) + /* With the range copied, we're larger than the original range. */ + if (((*total_out + copy_len) >> sectorsize_bits) >= + max_out >> sectorsize_bits) return -E2BIG; - cur_folio = out_folios[*cur_out >> min_folio_shift]; - /* Allocate a new page */ - if (!cur_folio) { - cur_folio = btrfs_alloc_compr_folio(fs_info); - if (!cur_folio) + if (!*out_folio) { + *out_folio = btrfs_alloc_compr_folio(fs_info); + if (!*out_folio) return -ENOMEM; - out_folios[*cur_out >> min_folio_shift] = cur_folio; } - kaddr = kmap_local_folio(cur_folio, 0); - memcpy(kaddr + offset_in_folio(cur_folio, *cur_out), - compressed_data + *cur_out - orig_out, copy_len); - - *cur_out += copy_len; + kaddr = kmap_local_folio(*out_folio, foffset); + memcpy(kaddr, compressed_data + *total_out - copy_start, copy_len); + kunmap_local(kaddr); + ret = write_and_queue_folio(out_bio, out_folio, total_out, copy_len); + if (ret < 0) + return ret; } /* * Check if we can fit the next segment header into the remaining space * of the sector. */ - sector_bytes_left = round_up(*cur_out, sectorsize) - *cur_out; + sector_bytes_left = round_up(*total_out, sectorsize) - *total_out; if (sector_bytes_left >= LZO_LEN || sector_bytes_left == 0) - goto out; + return 0; - /* The remaining size is not enough, pad it with zeros */ - memset(kaddr + offset_in_page(*cur_out), 0, - sector_bytes_left); - *cur_out += sector_bytes_left; + ASSERT(*out_folio); -out: - kunmap_local(kaddr); - return 0; + /* The remaining size is not enough, pad it with zeros */ + folio_zero_range(*out_folio, offset_in_folio(*out_folio, *total_out), sector_bytes_left); + return write_and_queue_folio(out_bio, out_folio, total_out, sector_bytes_left); } -int lzo_compress_folios(struct list_head *ws, struct btrfs_inode *inode, - u64 start, struct folio **folios, unsigned long *out_folios, - unsigned long *total_in, unsigned long *total_out) +int lzo_compress_bio(struct list_head *ws, struct compressed_bio *cb) { + struct btrfs_inode *inode = cb->bbio.inode; struct btrfs_fs_info *fs_info = inode->root->fs_info; struct workspace *workspace = list_entry(ws, struct workspace, list); + struct bio *bio = &cb->bbio.bio; + const u64 start = cb->start; + const u32 len = cb->len; const u32 sectorsize = fs_info->sectorsize; const u32 min_folio_size = btrfs_min_folio_size(fs_info); struct address_space *mapping = inode->vfs_inode.i_mapping; struct folio *folio_in = NULL; + struct folio *folio_out = NULL; char *sizes_ptr; - const unsigned long max_nr_folio = *out_folios; int ret = 0; - /* Points to the file offset of input data */ + /* Points to the file offset of input data. */ u64 cur_in = start; - /* Points to the current output byte */ - u32 cur_out = 0; - u32 len = *total_out; + /* Points to the current output byte. */ + u32 total_out = 0; - ASSERT(max_nr_folio > 0); - *out_folios = 0; - *total_out = 0; - *total_in = 0; + ASSERT(bio->bi_iter.bi_size == 0); + ASSERT(len); + + folio_out = btrfs_alloc_compr_folio(fs_info); + if (!folio_out) + return -ENOMEM; + + /* Queue a segment header first. */ + ret = write_and_queue_folio(bio, &folio_out, &total_out, LZO_LEN); + /* The first header should not fail. */ + ASSERT(ret == 0); - /* - * Skip the header for now, we will later come back and write the total - * compressed size - */ - cur_out += LZO_LEN; while (cur_in < start + len) { char *data_in; const u32 sectorsize_mask = sectorsize - 1; @@ -250,19 +312,18 @@ int lzo_compress_folios(struct list_head *ws, struct btrfs_inode *inode, u32 in_len; size_t out_len; - /* Get the input page first */ + /* Get the input page first. */ if (!folio_in) { ret = btrfs_compress_filemap_get_folio(mapping, cur_in, &folio_in); if (ret < 0) goto out; } - /* Compress at most one sector of data each time */ + /* Compress at most one sector of data each time. */ in_len = min_t(u32, start + len - cur_in, sectorsize - sector_off); ASSERT(in_len); data_in = kmap_local_folio(folio_in, offset_in_folio(folio_in, cur_in)); - ret = lzo1x_1_compress(data_in, in_len, - workspace->cbuf, &out_len, + ret = lzo1x_1_compress(data_in, in_len, workspace->cbuf, &out_len, workspace->mem); kunmap_local(data_in); if (unlikely(ret < 0)) { @@ -271,9 +332,8 @@ int lzo_compress_folios(struct list_head *ws, struct btrfs_inode *inode, goto out; } - ret = copy_compressed_data_to_page(fs_info, workspace->cbuf, out_len, - folios, max_nr_folio, - &cur_out); + ret = copy_compressed_data_to_bio(fs_info, bio, workspace->cbuf, out_len, + &folio_out, &total_out, len); if (ret < 0) goto out; @@ -283,50 +343,80 @@ int lzo_compress_folios(struct list_head *ws, struct btrfs_inode *inode, * Check if we're making it bigger after two sectors. And if * it is so, give up. */ - if (cur_in - start > sectorsize * 2 && cur_in - start < cur_out) { + if (cur_in - start > sectorsize * 2 && cur_in - start < total_out) { ret = -E2BIG; goto out; } - /* Check if we have reached folio boundary. */ + /* Check if we have reached input folio boundary. */ if (IS_ALIGNED(cur_in, min_folio_size)) { folio_put(folio_in); folio_in = NULL; } } + /* + * The last folio is already queued. Bio is responsible for freeing + * those folios now. + */ + folio_out = NULL; /* Store the size of all chunks of compressed data */ - sizes_ptr = kmap_local_folio(folios[0], 0); - write_compress_length(sizes_ptr, cur_out); + sizes_ptr = kmap_local_folio(bio_first_folio_all(bio), 0); + write_compress_length(sizes_ptr, total_out); kunmap_local(sizes_ptr); - - ret = 0; - *total_out = cur_out; - *total_in = cur_in - start; out: + /* + * We can only free the folio that has no part queued into the bio. + * + * As any folio that is already queued into bio will be released by + * the endio function of bio. + */ + if (folio_out && IS_ALIGNED(total_out, min_folio_size)) { + btrfs_free_compr_folio(folio_out); + folio_out = NULL; + } if (folio_in) folio_put(folio_in); - *out_folios = DIV_ROUND_UP(cur_out, min_folio_size); return ret; } +static struct folio *get_current_folio(struct compressed_bio *cb, struct folio_iter *fi, + u32 *cur_folio_index, u32 cur_in) +{ + struct btrfs_fs_info *fs_info = cb_to_fs_info(cb); + const u32 min_folio_shift = PAGE_SHIFT + fs_info->block_min_order; + + ASSERT(cur_folio_index); + + /* Need to switch to the next folio. */ + if (cur_in >> min_folio_shift != *cur_folio_index) { + /* We can only do the switch one folio a time. */ + ASSERT(cur_in >> min_folio_shift == *cur_folio_index + 1); + + bio_next_folio(fi, &cb->bbio.bio); + (*cur_folio_index)++; + } + return fi->folio; +} + /* * Copy the compressed segment payload into @dest. * * For the payload there will be no padding, just need to do page switching. */ static void copy_compressed_segment(struct compressed_bio *cb, + struct folio_iter *fi, u32 *cur_folio_index, char *dest, u32 len, u32 *cur_in) { - struct btrfs_fs_info *fs_info = cb_to_fs_info(cb); - const u32 min_folio_shift = PAGE_SHIFT + fs_info->block_min_order; u32 orig_in = *cur_in; while (*cur_in < orig_in + len) { - struct folio *cur_folio = cb->compressed_folios[*cur_in >> min_folio_shift]; - u32 copy_len = min_t(u32, orig_in + len - *cur_in, - folio_size(cur_folio) - offset_in_folio(cur_folio, *cur_in)); + struct folio *cur_folio = get_current_folio(cb, fi, cur_folio_index, *cur_in); + u32 copy_len; + ASSERT(cur_folio); + copy_len = min_t(u32, orig_in + len - *cur_in, + folio_size(cur_folio) - offset_in_folio(cur_folio, *cur_in)); ASSERT(copy_len); memcpy_from_folio(dest + *cur_in - orig_in, cur_folio, @@ -341,7 +431,7 @@ 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; const u32 sectorsize = fs_info->sectorsize; - const u32 min_folio_shift = PAGE_SHIFT + fs_info->block_min_order; + struct folio_iter fi; char *kaddr; int ret; /* Compressed data length, can be unaligned */ @@ -350,8 +440,15 @@ int lzo_decompress_bio(struct list_head *ws, struct compressed_bio *cb) u32 cur_in = 0; /* Bytes decompressed so far */ u32 cur_out = 0; - - kaddr = kmap_local_folio(cb->compressed_folios[0], 0); + /* The current folio index number inside the bio. */ + u32 cur_folio_index = 0; + + bio_first_folio(&fi, &cb->bbio.bio, 0); + /* There must be a compressed folio and matches the sectorsize. */ + if (unlikely(!fi.folio)) + return -EINVAL; + ASSERT(folio_size(fi.folio) == sectorsize); + kaddr = kmap_local_folio(fi.folio, 0); len_in = read_compress_length(kaddr); kunmap_local(kaddr); cur_in += LZO_LEN; @@ -388,7 +485,7 @@ int lzo_decompress_bio(struct list_head *ws, struct compressed_bio *cb) */ ASSERT(cur_in / sectorsize == (cur_in + LZO_LEN - 1) / sectorsize); - cur_folio = cb->compressed_folios[cur_in >> min_folio_shift]; + cur_folio = get_current_folio(cb, &fi, &cur_folio_index, cur_in); ASSERT(cur_folio); kaddr = kmap_local_folio(cur_folio, 0); seg_len = read_compress_length(kaddr + offset_in_folio(cur_folio, cur_in)); @@ -410,7 +507,8 @@ int lzo_decompress_bio(struct list_head *ws, struct compressed_bio *cb) } /* Copy the compressed segment payload into workspace */ - copy_compressed_segment(cb, workspace->cbuf, seg_len, &cur_in); + copy_compressed_segment(cb, &fi, &cur_folio_index, workspace->cbuf, + seg_len, &cur_in); /* Decompress the data */ ret = lzo1x_decompress_safe(workspace->cbuf, seg_len, @@ -456,7 +554,7 @@ int lzo_decompress(struct list_head *ws, const u8 *data_in, size_t in_len; size_t out_len; size_t max_segment_len = workspace_buf_length(fs_info); - int ret = 0; + int ret; if (unlikely(srclen < LZO_LEN || srclen > max_segment_len + LZO_LEN * 2)) return -EUCLEAN; @@ -467,10 +565,8 @@ int lzo_decompress(struct list_head *ws, const u8 *data_in, data_in += LZO_LEN; in_len = read_compress_length(data_in); - if (unlikely(in_len != srclen - LZO_LEN * 2)) { - ret = -EUCLEAN; - goto out; - } + if (unlikely(in_len != srclen - LZO_LEN * 2)) + return -EUCLEAN; data_in += LZO_LEN; out_len = sectorsize; @@ -482,19 +578,18 @@ int lzo_decompress(struct list_head *ws, const u8 *data_in, "lzo decompression failed, error %d root %llu inode %llu offset %llu", ret, btrfs_root_id(inode->root), btrfs_ino(inode), folio_pos(dest_folio)); - ret = -EIO; - goto out; + return -EIO; } ASSERT(out_len <= sectorsize); memcpy_to_folio(dest_folio, dest_pgoff, workspace->buf, out_len); /* Early end, considered as an error. */ if (unlikely(out_len < destlen)) { - ret = -EIO; folio_zero_range(dest_folio, dest_pgoff + out_len, destlen - out_len); + return -EIO; } -out: - return ret; + + return 0; } const struct btrfs_compress_levels btrfs_lzo_compress = { diff --git a/fs/btrfs/messages.c b/fs/btrfs/messages.c index 2f853de44473..6190777924bf 100644 --- a/fs/btrfs/messages.c +++ b/fs/btrfs/messages.c @@ -211,33 +211,19 @@ static struct ratelimit_state printk_limits[] = { RATELIMIT_STATE_INIT(printk_limits[7], DEFAULT_RATELIMIT_INTERVAL, 100), }; -void __cold _btrfs_printk(const struct btrfs_fs_info *fs_info, const char *fmt, ...) +__printf(3, 4) __cold +void _btrfs_printk(const struct btrfs_fs_info *fs_info, unsigned int level, const char *fmt, ...) { - char lvl[PRINTK_MAX_SINGLE_HEADER_LEN + 1] = "\0"; struct va_format vaf; va_list args; - int kern_level; - const char *type = logtypes[4]; - struct ratelimit_state *ratelimit = &printk_limits[4]; + const char *type = logtypes[level]; + struct ratelimit_state *ratelimit = &printk_limits[level]; #ifdef CONFIG_PRINTK_INDEX printk_index_subsys_emit("%sBTRFS %s (device %s): ", NULL, fmt); #endif va_start(args, fmt); - - while ((kern_level = printk_get_level(fmt)) != 0) { - size_t size = printk_skip_level(fmt) - fmt; - - if (kern_level >= '0' && kern_level <= '7') { - memcpy(lvl, fmt, size); - lvl[size] = '\0'; - type = logtypes[kern_level - '0']; - ratelimit = &printk_limits[kern_level - '0']; - } - fmt += size; - } - vaf.fmt = fmt; vaf.va = &args; @@ -247,10 +233,10 @@ void __cold _btrfs_printk(const struct btrfs_fs_info *fs_info, const char *fmt, char statestr[STATE_STRING_BUF_LEN]; btrfs_state_to_string(fs_info, statestr); - _printk("%sBTRFS %s (device %s%s): %pV\n", lvl, type, + _printk(KERN_SOH "%dBTRFS %s (device %s%s): %pV\n", level, type, fs_info->sb->s_id, statestr, &vaf); } else { - _printk("%sBTRFS %s: %pV\n", lvl, type, &vaf); + _printk(KERN_SOH "%dBTRFS %s: %pV\n", level, type, &vaf); } } diff --git a/fs/btrfs/messages.h b/fs/btrfs/messages.h index d8c0bd17dcda..943e53980945 100644 --- a/fs/btrfs/messages.h +++ b/fs/btrfs/messages.h @@ -23,69 +23,74 @@ void btrfs_no_printk(const struct btrfs_fs_info *fs_info, const char *fmt, ...) #ifdef CONFIG_PRINTK -#define btrfs_printk(fs_info, fmt, args...) \ - _btrfs_printk(fs_info, fmt, ##args) - -__printf(2, 3) -__cold -void _btrfs_printk(const struct btrfs_fs_info *fs_info, const char *fmt, ...); +__printf(3, 4) __cold +void _btrfs_printk(const struct btrfs_fs_info *fs_info, unsigned int level, const char *fmt, ...); #else -#define btrfs_printk(fs_info, fmt, args...) \ +#define btrfs_printk_in_rcu(fs_info, level, fmt, args...) \ + btrfs_no_printk(fs_info, fmt, ##args) + +#define btrfs_printk_in_rcu(fs_info, level, fmt, args...) \ btrfs_no_printk(fs_info, fmt, ##args) + +#define btrfs_printk_rl_in_rcu(fs_info, level, fmt, args...) \ + btrfs_no_printk(fs_info, fmt, ##args) + #endif /* * Print a message with filesystem info, enclosed in RCU protection. */ #define btrfs_crit(fs_info, fmt, args...) \ - btrfs_printk_in_rcu(fs_info, KERN_CRIT fmt, ##args) + btrfs_printk_in_rcu(fs_info, LOGLEVEL_CRIT, fmt, ##args) #define btrfs_err(fs_info, fmt, args...) \ - btrfs_printk_in_rcu(fs_info, KERN_ERR fmt, ##args) + btrfs_printk_in_rcu(fs_info, LOGLEVEL_ERR, fmt, ##args) #define btrfs_warn(fs_info, fmt, args...) \ - btrfs_printk_in_rcu(fs_info, KERN_WARNING fmt, ##args) + btrfs_printk_in_rcu(fs_info, LOGLEVEL_WARNING, fmt, ##args) #define btrfs_info(fs_info, fmt, args...) \ - btrfs_printk_in_rcu(fs_info, KERN_INFO fmt, ##args) + btrfs_printk_in_rcu(fs_info, LOGLEVEL_INFO, fmt, ##args) /* * Wrappers that use a ratelimited printk */ #define btrfs_crit_rl(fs_info, fmt, args...) \ - btrfs_printk_rl_in_rcu(fs_info, KERN_CRIT fmt, ##args) + btrfs_printk_rl_in_rcu(fs_info, LOGLEVEL_CRIT, fmt, ##args) #define btrfs_err_rl(fs_info, fmt, args...) \ - btrfs_printk_rl_in_rcu(fs_info, KERN_ERR fmt, ##args) + btrfs_printk_rl_in_rcu(fs_info, LOGLEVEL_ERR, fmt, ##args) #define btrfs_warn_rl(fs_info, fmt, args...) \ - btrfs_printk_rl_in_rcu(fs_info, KERN_WARNING fmt, ##args) + btrfs_printk_rl_in_rcu(fs_info, LOGLEVEL_WARNING, fmt, ##args) #define btrfs_info_rl(fs_info, fmt, args...) \ - btrfs_printk_rl_in_rcu(fs_info, KERN_INFO fmt, ##args) + btrfs_printk_rl_in_rcu(fs_info, LOGLEVEL_INFO, fmt, ##args) #if defined(CONFIG_DYNAMIC_DEBUG) #define btrfs_debug(fs_info, fmt, args...) \ _dynamic_func_call_no_desc(fmt, btrfs_printk_in_rcu, \ - fs_info, KERN_DEBUG fmt, ##args) + fs_info, LOGLEVEL_DEBUG, fmt, ##args) #define btrfs_debug_rl(fs_info, fmt, args...) \ _dynamic_func_call_no_desc(fmt, btrfs_printk_rl_in_rcu, \ - fs_info, KERN_DEBUG fmt, ##args) + fs_info, LOGLEVEL_DEBUG, fmt, ##args) #elif defined(DEBUG) #define btrfs_debug(fs_info, fmt, args...) \ - btrfs_printk_in_rcu(fs_info, KERN_DEBUG fmt, ##args) + btrfs_printk_in_rcu(fs_info, LOGLEVEL_DEBUG, fmt, ##args) #define btrfs_debug_rl(fs_info, fmt, args...) \ - btrfs_printk_rl_in_rcu(fs_info, KERN_DEBUG fmt, ##args) + btrfs_printk_rl_in_rcu(fs_info, LOGLEVEl_DEBUG, fmt, ##args) #else /* When printk() is no_printk(), expand to no-op. */ #define btrfs_debug(fs_info, fmt, args...) do { (void)(fs_info); } while(0) #define btrfs_debug_rl(fs_info, fmt, args...) do { (void)(fs_info); } while(0) #endif -#define btrfs_printk_in_rcu(fs_info, fmt, args...) \ -do { \ - rcu_read_lock(); \ - btrfs_printk(fs_info, fmt, ##args); \ - rcu_read_unlock(); \ +#ifdef CONFIG_PRINTK + +#define btrfs_printk_in_rcu(fs_info, level, fmt, args...) \ +do { \ + rcu_read_lock(); \ + _btrfs_printk(fs_info, level, fmt, ##args); \ + rcu_read_unlock(); \ } while (0) -#define btrfs_printk_rl_in_rcu(fs_info, fmt, args...) \ +#define btrfs_printk_rl_in_rcu(fs_info, level, fmt, args...) \ do { \ static DEFINE_RATELIMIT_STATE(_rs, \ DEFAULT_RATELIMIT_INTERVAL, \ @@ -93,10 +98,12 @@ do { \ \ rcu_read_lock(); \ if (__ratelimit(&_rs)) \ - btrfs_printk(fs_info, fmt, ##args); \ + _btrfs_printk(fs_info, level, fmt, ##args); \ rcu_read_unlock(); \ } while (0) +#endif + #ifdef CONFIG_BTRFS_ASSERT __printf(1, 2) @@ -113,7 +120,6 @@ static inline void verify_assert_printk_format(const char *fmt, ...) { */ #define __REST_ARGS(_, ... ) __VA_OPT__(,) __VA_ARGS__ -#if defined(CONFIG_CC_IS_CLANG) || GCC_VERSION >= 80000 /* * Assertion with optional printk() format. * @@ -152,22 +158,6 @@ do { \ } while(0) #else - -/* For GCC < 8.x only the simple output. */ - -#define ASSERT(cond, args...) \ -do { \ - verify_assert_printk_format("check the format string" args); \ - if (!likely(cond)) { \ - pr_err("assertion failed: %s :: %ld, in %s:%d\n", \ - #cond, (long)(cond), __FILE__, __LINE__); \ - BUG(); \ - } \ -} while(0) - -#endif - -#else /* Compile check the @cond expression but don't generate any code. */ #define ASSERT(cond, args...) BUILD_BUG_ON_INVALID(cond) #endif diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c index 206587820fec..f53c313ab6e4 100644 --- a/fs/btrfs/qgroup.c +++ b/fs/btrfs/qgroup.c @@ -346,6 +346,42 @@ int btrfs_verify_qgroup_counts(const struct btrfs_fs_info *fs_info, u64 qgroupid } #endif +static bool squota_check_parent_usage(struct btrfs_fs_info *fs_info, struct btrfs_qgroup *parent) +{ + u64 excl_sum = 0; + u64 rfer_sum = 0; + u64 excl_cmpr_sum = 0; + u64 rfer_cmpr_sum = 0; + struct btrfs_qgroup_list *glist; + int nr_members = 0; + bool mismatch; + + if (btrfs_qgroup_mode(fs_info) != BTRFS_QGROUP_MODE_SIMPLE) + return false; + if (btrfs_qgroup_level(parent->qgroupid) == 0) + return false; + + /* Eligible parent qgroup. Squota; level > 0; empty members list. */ + list_for_each_entry(glist, &parent->members, next_member) { + excl_sum += glist->member->excl; + rfer_sum += glist->member->rfer; + excl_cmpr_sum += glist->member->excl_cmpr; + rfer_cmpr_sum += glist->member->rfer_cmpr; + nr_members++; + } + mismatch = (parent->excl != excl_sum || parent->rfer != rfer_sum || + parent->excl_cmpr != excl_cmpr_sum || parent->rfer_cmpr != excl_cmpr_sum); + + WARN(mismatch, + "parent squota qgroup %hu/%llu has mismatched usage from its %d members. " + "%llu %llu %llu %llu vs %llu %llu %llu %llu\n", + btrfs_qgroup_level(parent->qgroupid), + btrfs_qgroup_subvolid(parent->qgroupid), nr_members, parent->excl, + parent->rfer, parent->excl_cmpr, parent->rfer_cmpr, excl_sum, + rfer_sum, excl_cmpr_sum, rfer_cmpr_sum); + return mismatch; +} + __printf(2, 3) static void qgroup_mark_inconsistent(struct btrfs_fs_info *fs_info, const char *fmt, ...) { @@ -658,7 +694,6 @@ void btrfs_free_qgroup_config(struct btrfs_fs_info *fs_info) static int add_qgroup_relation_item(struct btrfs_trans_handle *trans, u64 src, u64 dst) { - int ret; struct btrfs_root *quota_root = trans->fs_info->quota_root; BTRFS_PATH_AUTO_FREE(path); struct btrfs_key key; @@ -671,8 +706,7 @@ static int add_qgroup_relation_item(struct btrfs_trans_handle *trans, u64 src, key.type = BTRFS_QGROUP_RELATION_KEY; key.offset = dst; - ret = btrfs_insert_empty_item(trans, quota_root, path, &key, 0); - return ret; + return btrfs_insert_empty_item(trans, quota_root, path, &key, 0); } static int del_qgroup_relation_item(struct btrfs_trans_handle *trans, u64 src, @@ -797,9 +831,7 @@ static int del_qgroup_item(struct btrfs_trans_handle *trans, u64 qgroupid) if (ret > 0) return -ENOENT; - ret = btrfs_del_item(trans, quota_root, path); - - return ret; + return btrfs_del_item(trans, quota_root, path); } static int update_qgroup_limit_item(struct btrfs_trans_handle *trans, @@ -1562,6 +1594,7 @@ int btrfs_add_qgroup_relation(struct btrfs_trans_handle *trans, u64 src, u64 dst goto out; } ret = quick_update_accounting(fs_info, src, dst, 1); + squota_check_parent_usage(fs_info, parent); spin_unlock(&fs_info->qgroup_lock); out: kfree(prealloc); @@ -1580,10 +1613,8 @@ static int __del_qgroup_relation(struct btrfs_trans_handle *trans, u64 src, int ret = 0; int ret2; - if (!fs_info->quota_root) { - ret = -ENOTCONN; - goto out; - } + if (!fs_info->quota_root) + return -ENOTCONN; member = find_qgroup_rb(fs_info, src); parent = find_qgroup_rb(fs_info, dst); @@ -1605,10 +1636,10 @@ static int __del_qgroup_relation(struct btrfs_trans_handle *trans, u64 src, delete_item: ret = del_qgroup_relation_item(trans, src, dst); if (ret < 0 && ret != -ENOENT) - goto out; + return ret; ret2 = del_qgroup_relation_item(trans, dst, src); if (ret2 < 0 && ret2 != -ENOENT) - goto out; + return ret2; /* At least one deletion succeeded, return 0 */ if (!ret || !ret2) @@ -1618,9 +1649,11 @@ delete_item: spin_lock(&fs_info->qgroup_lock); del_relation_rb(fs_info, src, dst); ret = quick_update_accounting(fs_info, src, dst, -1); + ASSERT(parent); + squota_check_parent_usage(fs_info, parent); spin_unlock(&fs_info->qgroup_lock); } -out: + return ret; } @@ -1679,6 +1712,36 @@ out: return ret; } +static bool can_delete_parent_qgroup(struct btrfs_qgroup *qgroup) + +{ + ASSERT(btrfs_qgroup_level(qgroup->qgroupid)); + return list_empty(&qgroup->members); +} + +/* + * Return true if we can delete the squota qgroup and false otherwise. + * + * Rules for whether we can delete: + * + * A subvolume qgroup can be removed iff the subvolume is fully deleted, which + * is iff there is 0 usage in the qgroup. + * + * A higher level qgroup can be removed iff it has no members. + * Note: We audit its usage to warn on inconsitencies without blocking deletion. + */ +static bool can_delete_squota_qgroup(struct btrfs_fs_info *fs_info, struct btrfs_qgroup *qgroup) +{ + ASSERT(btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_SIMPLE); + + if (btrfs_qgroup_level(qgroup->qgroupid) > 0) { + squota_check_parent_usage(fs_info, qgroup); + return can_delete_parent_qgroup(qgroup); + } + + return !(qgroup->rfer || qgroup->excl || qgroup->rfer_cmpr || qgroup->excl_cmpr); +} + /* * Return 0 if we can not delete the qgroup (not empty or has children etc). * Return >0 if we can delete the qgroup. @@ -1689,23 +1752,13 @@ static int can_delete_qgroup(struct btrfs_fs_info *fs_info, struct btrfs_qgroup struct btrfs_key key; BTRFS_PATH_AUTO_FREE(path); - /* - * Squota would never be inconsistent, but there can still be case - * where a dropped subvolume still has qgroup numbers, and squota - * relies on such qgroup for future accounting. - * - * So for squota, do not allow dropping any non-zero qgroup. - */ - if (btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_SIMPLE && - (qgroup->rfer || qgroup->excl || qgroup->excl_cmpr || qgroup->rfer_cmpr)) - return 0; + /* Since squotas cannot be inconsistent, they have special rules for deletion. */ + if (btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_SIMPLE) + return can_delete_squota_qgroup(fs_info, qgroup); /* For higher level qgroup, we can only delete it if it has no child. */ - if (btrfs_qgroup_level(qgroup->qgroupid)) { - if (!list_empty(&qgroup->members)) - return 0; - return 1; - } + if (btrfs_qgroup_level(qgroup->qgroupid)) + return can_delete_parent_qgroup(qgroup); /* * For level-0 qgroups, we can only delete it if it has no subvolume @@ -2433,13 +2486,11 @@ static int qgroup_trace_new_subtree_blocks(struct btrfs_trans_handle* trans, /* This node is old, no need to trace */ if (child_gen < last_snapshot) - goto out; + return ret; eb = btrfs_read_node_slot(eb, parent_slot); - if (IS_ERR(eb)) { - ret = PTR_ERR(eb); - goto out; - } + if (IS_ERR(eb)) + return PTR_ERR(eb); dst_path->nodes[cur_level] = eb; dst_path->slots[cur_level] = 0; @@ -2484,7 +2535,7 @@ cleanup: dst_path->slots[cur_level] = 0; dst_path->locks[cur_level] = 0; } -out: + return ret; } @@ -2596,10 +2647,8 @@ int btrfs_qgroup_trace_subtree(struct btrfs_trans_handle *trans, return ret; } - if (root_level == 0) { - ret = btrfs_qgroup_trace_leaf_items(trans, root_eb); - return ret; - } + if (root_level == 0) + return btrfs_qgroup_trace_leaf_items(trans, root_eb); path = btrfs_alloc_path(); if (!path) diff --git a/fs/btrfs/raid56.c b/fs/btrfs/raid56.c index f38d8305e46d..baadaaa189c0 100644 --- a/fs/btrfs/raid56.c +++ b/fs/btrfs/raid56.c @@ -150,6 +150,7 @@ static void scrub_rbio_work_locked(struct work_struct *work); static void free_raid_bio_pointers(struct btrfs_raid_bio *rbio) { bitmap_free(rbio->error_bitmap); + bitmap_free(rbio->stripe_uptodate_bitmap); kfree(rbio->stripe_pages); kfree(rbio->bio_paddrs); kfree(rbio->stripe_paddrs); diff --git a/fs/btrfs/reflink.c b/fs/btrfs/reflink.c index 58dc3e5057ce..314cb95ba846 100644 --- a/fs/btrfs/reflink.c +++ b/fs/btrfs/reflink.c @@ -754,8 +754,13 @@ static noinline int btrfs_clone_files(struct file *file, struct file *file_src, /* * We may have copied an inline extent into a page of the destination - * range, so wait for writeback to complete before invalidating pages - * from the page cache. This is a rare case. + * range. So flush delalloc and wait for ordered extent completion. + * This is to ensure the invalidation below does not fail, as if for + * example it finds a dirty folio, our folio release callback + * (btrfs_release_folio()) returns false, which makes the invalidation + * return an -EBUSY error. We can't ignore such failures since they + * could come from some range other than the copied inline extent's + * destination range and we have no way to know that. */ ret = btrfs_wait_ordered_range(BTRFS_I(inode), destoff, len); if (ret < 0) @@ -873,7 +878,7 @@ loff_t btrfs_remap_file_range(struct file *src_file, loff_t off, bool same_inode = dst_inode == src_inode; int ret; - if (unlikely(btrfs_is_shutdown(inode_to_fs_info(file_inode(src_file))))) + if (btrfs_is_shutdown(inode_to_fs_info(file_inode(src_file)))) return -EIO; if (remap_flags & ~(REMAP_FILE_DEDUP | REMAP_FILE_ADVISORY)) diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c index 5bfefc3e9c06..fcd0a2ba3554 100644 --- a/fs/btrfs/relocation.c +++ b/fs/btrfs/relocation.c @@ -37,6 +37,7 @@ #include "super.h" #include "tree-checker.h" #include "raid-stripe-tree.h" +#include "free-space-tree.h" /* * Relocation overview @@ -3254,7 +3255,6 @@ static int delete_v1_space_cache(struct extent_buffer *leaf, struct btrfs_key key; bool found = false; int i; - int ret; if (btrfs_header_owner(leaf) != BTRFS_ROOT_TREE_OBJECTID) return 0; @@ -3278,8 +3278,8 @@ static int delete_v1_space_cache(struct extent_buffer *leaf, } if (!found) return -ENOENT; - ret = delete_block_group_cache(block_group, NULL, space_cache_ino); - return ret; + + return delete_block_group_cache(block_group, NULL, space_cache_ino); } /* @@ -3616,7 +3616,7 @@ restart: btrfs_btree_balance_dirty(fs_info); } - if (!err) { + if (!err && !btrfs_fs_incompat(fs_info, REMAP_TREE)) { ret = relocate_file_extent_cluster(rc); if (ret < 0) err = ret; @@ -3860,6 +3860,1433 @@ static const char *stage_to_string(enum reloc_stage stage) return "unknown"; } +static int add_remap_tree_entries(struct btrfs_trans_handle *trans, struct btrfs_path *path, + struct btrfs_key *entries, unsigned int num_entries) +{ + int ret; + struct btrfs_fs_info *fs_info = trans->fs_info; + struct btrfs_item_batch batch; + u32 *data_sizes; + u32 max_items; + + max_items = BTRFS_LEAF_DATA_SIZE(trans->fs_info) / sizeof(struct btrfs_item); + + data_sizes = kzalloc(sizeof(u32) * min_t(u32, num_entries, max_items), GFP_NOFS); + if (!data_sizes) + return -ENOMEM; + + while (true) { + batch.keys = entries; + batch.data_sizes = data_sizes; + batch.total_data_size = 0; + batch.nr = min_t(u32, num_entries, max_items); + + ret = btrfs_insert_empty_items(trans, fs_info->remap_root, path, &batch); + btrfs_release_path(path); + + if (num_entries <= max_items) + break; + + num_entries -= max_items; + entries += max_items; + } + + kfree(data_sizes); + + return ret; +} + +struct space_run { + u64 start; + u64 end; +}; + +static void parse_bitmap(u64 block_size, const unsigned long *bitmap, + unsigned long size, u64 address, struct space_run *space_runs, + unsigned int *num_space_runs) +{ + unsigned long pos, end; + u64 run_start, run_length; + + pos = find_first_bit(bitmap, size); + if (pos == size) + return; + + while (true) { + end = find_next_zero_bit(bitmap, size, pos); + + run_start = address + (pos * block_size); + run_length = (end - pos) * block_size; + + if (*num_space_runs != 0 && + space_runs[*num_space_runs - 1].end == run_start) { + space_runs[*num_space_runs - 1].end += run_length; + } else { + space_runs[*num_space_runs].start = run_start; + space_runs[*num_space_runs].end = run_start + run_length; + + (*num_space_runs)++; + } + + if (end == size) + break; + + pos = find_next_bit(bitmap, size, end + 1); + if (pos == size) + break; + } +} + +static void adjust_block_group_remap_bytes(struct btrfs_trans_handle *trans, + struct btrfs_block_group *bg, s64 diff) +{ + struct btrfs_fs_info *fs_info = trans->fs_info; + bool bg_already_dirty = true; + bool mark_unused = false; + + spin_lock(&bg->lock); + bg->remap_bytes += diff; + if (bg->used == 0 && bg->remap_bytes == 0) + mark_unused = true; + spin_unlock(&bg->lock); + + if (mark_unused) + btrfs_mark_bg_unused(bg); + + spin_lock(&trans->transaction->dirty_bgs_lock); + if (list_empty(&bg->dirty_list)) { + list_add_tail(&bg->dirty_list, &trans->transaction->dirty_bgs); + bg_already_dirty = false; + btrfs_get_block_group(bg); + } + spin_unlock(&trans->transaction->dirty_bgs_lock); + + /* Modified block groups are accounted for in the delayed_refs_rsv. */ + if (!bg_already_dirty) + btrfs_inc_delayed_refs_rsv_bg_updates(fs_info); +} + +/* Private structure for I/O from copy_remapped_data(). */ +struct reloc_io_private { + struct completion done; + refcount_t pending_refs; + blk_status_t status; +}; + +static void reloc_endio(struct btrfs_bio *bbio) +{ + struct reloc_io_private *priv = bbio->private; + + if (bbio->bio.bi_status) + WRITE_ONCE(priv->status, bbio->bio.bi_status); + + if (refcount_dec_and_test(&priv->pending_refs)) + complete(&priv->done); + + bio_put(&bbio->bio); +} + +static int copy_remapped_data_io(struct btrfs_fs_info *fs_info, + struct reloc_io_private *priv, + struct page **pages, u64 addr, u64 length, + blk_opf_t op) +{ + struct btrfs_bio *bbio; + int i; + + init_completion(&priv->done); + refcount_set(&priv->pending_refs, 1); + priv->status = 0; + + bbio = btrfs_bio_alloc(BIO_MAX_VECS, op, BTRFS_I(fs_info->btree_inode), + addr, reloc_endio, priv); + bbio->bio.bi_iter.bi_sector = (addr >> SECTOR_SHIFT); + bbio->is_remap = true; + + i = 0; + do { + size_t bytes = min_t(u64, length, PAGE_SIZE); + + if (bio_add_page(&bbio->bio, pages[i], bytes, 0) < bytes) { + refcount_inc(&priv->pending_refs); + btrfs_submit_bbio(bbio, 0); + + bbio = btrfs_bio_alloc(BIO_MAX_VECS, op, + BTRFS_I(fs_info->btree_inode), + addr, reloc_endio, priv); + bbio->bio.bi_iter.bi_sector = (addr >> SECTOR_SHIFT); + bbio->is_remap = true; + continue; + } + + i++; + addr += bytes; + length -= bytes; + } while (length); + + refcount_inc(&priv->pending_refs); + btrfs_submit_bbio(bbio, 0); + + if (!refcount_dec_and_test(&priv->pending_refs)) + wait_for_completion_io(&priv->done); + + return blk_status_to_errno(READ_ONCE(priv->status)); +} + +static int copy_remapped_data(struct btrfs_fs_info *fs_info, u64 old_addr, + u64 new_addr, u64 length) +{ + int ret; + u64 copy_len = min_t(u64, length, SZ_1M); + struct page **pages; + struct reloc_io_private priv; + unsigned int nr_pages = DIV_ROUND_UP(length, PAGE_SIZE); + + pages = kcalloc(nr_pages, sizeof(struct page *), GFP_NOFS); + if (!pages) + return -ENOMEM; + + ret = btrfs_alloc_page_array(nr_pages, pages, 0); + if (ret) { + ret = -ENOMEM; + goto end; + } + + /* Copy 1MB at a time, to avoid using too much memory. */ + do { + u64 to_copy = min_t(u64, length, copy_len); + + /* Limit to one bio. */ + to_copy = min_t(u64, to_copy, BIO_MAX_VECS << PAGE_SHIFT); + + ret = copy_remapped_data_io(fs_info, &priv, pages, old_addr, + to_copy, REQ_OP_READ); + if (ret) + goto end; + + ret = copy_remapped_data_io(fs_info, &priv, pages, new_addr, + to_copy, REQ_OP_WRITE); + if (ret) + goto end; + + if (to_copy == length) + break; + + old_addr += to_copy; + new_addr += to_copy; + length -= to_copy; + } while (true); + + ret = 0; +end: + for (int i = 0; i < nr_pages; i++) { + if (pages[i]) + __free_page(pages[i]); + } + kfree(pages); + + return ret; +} + +static int add_remap_item(struct btrfs_trans_handle *trans, + struct btrfs_path *path, u64 new_addr, u64 length, + u64 old_addr) +{ + struct btrfs_fs_info *fs_info = trans->fs_info; + struct btrfs_remap_item remap = { 0 }; + struct btrfs_key key; + struct extent_buffer *leaf; + int ret; + + key.objectid = old_addr; + key.type = BTRFS_REMAP_KEY; + key.offset = length; + + ret = btrfs_insert_empty_item(trans, fs_info->remap_root, path, + &key, sizeof(struct btrfs_remap_item)); + if (ret) + return ret; + + leaf = path->nodes[0]; + btrfs_set_stack_remap_address(&remap, new_addr); + write_extent_buffer(leaf, &remap, btrfs_item_ptr_offset(leaf, path->slots[0]), + sizeof(struct btrfs_remap_item)); + + btrfs_release_path(path); + + return 0; +} + +static int add_remap_backref_item(struct btrfs_trans_handle *trans, + struct btrfs_path *path, u64 new_addr, + u64 length, u64 old_addr) +{ + struct btrfs_fs_info *fs_info = trans->fs_info; + struct btrfs_remap_item remap = { 0 }; + struct btrfs_key key; + struct extent_buffer *leaf; + int ret; + + key.objectid = new_addr; + key.type = BTRFS_REMAP_BACKREF_KEY; + key.offset = length; + + ret = btrfs_insert_empty_item(trans, fs_info->remap_root, path, &key, + sizeof(struct btrfs_remap_item)); + if (ret) + return ret; + + leaf = path->nodes[0]; + btrfs_set_stack_remap_address(&remap, old_addr); + write_extent_buffer(leaf, &remap, btrfs_item_ptr_offset(leaf, path->slots[0]), + sizeof(struct btrfs_remap_item)); + + btrfs_release_path(path); + + return 0; +} + +static int move_existing_remap(struct btrfs_fs_info *fs_info, + struct btrfs_path *path, + struct btrfs_block_group *bg, u64 new_addr, + u64 length, u64 old_addr) +{ + struct btrfs_trans_handle *trans; + struct extent_buffer *leaf; + struct btrfs_remap_item *remap_ptr; + struct btrfs_remap_item remap = { 0 }; + struct btrfs_key key, ins; + u64 dest_addr, dest_length, min_size; + struct btrfs_block_group *dest_bg; + int ret; + const bool is_data = (bg->flags & BTRFS_BLOCK_GROUP_DATA); + struct btrfs_space_info *sinfo = bg->space_info; + bool mutex_taken = false; + bool bg_needs_free_space; + + spin_lock(&sinfo->lock); + btrfs_space_info_update_bytes_may_use(sinfo, length); + spin_unlock(&sinfo->lock); + + if (is_data) + min_size = fs_info->sectorsize; + else + min_size = fs_info->nodesize; + + ret = btrfs_reserve_extent(fs_info->fs_root, length, length, min_size, + 0, 0, &ins, is_data, false); + if (unlikely(ret)) { + spin_lock(&sinfo->lock); + btrfs_space_info_update_bytes_may_use(sinfo, -length); + spin_unlock(&sinfo->lock); + return ret; + } + + dest_addr = ins.objectid; + dest_length = ins.offset; + + if (!is_data && !IS_ALIGNED(dest_length, fs_info->nodesize)) { + u64 new_length = ALIGN_DOWN(dest_length, fs_info->nodesize); + + btrfs_free_reserved_extent(fs_info, dest_addr + new_length, + dest_length - new_length, 0); + + dest_length = new_length; + } + + trans = btrfs_join_transaction(fs_info->remap_root); + if (IS_ERR(trans)) { + ret = PTR_ERR(trans); + trans = NULL; + goto end; + } + + mutex_lock(&fs_info->remap_mutex); + mutex_taken = true; + + /* Find old remap entry. */ + key.objectid = old_addr; + key.type = BTRFS_REMAP_KEY; + key.offset = length; + + ret = btrfs_search_slot(trans, fs_info->remap_root, &key, path, 0, 1); + if (ret == 1) { + /* + * Not a problem if the remap entry wasn't found: that means + * that another transaction has deallocated the data. + * move_existing_remaps() loops until the BG contains no + * remaps, so we can just return 0 in this case. + */ + btrfs_release_path(path); + ret = 0; + goto end; + } else if (unlikely(ret)) { + goto end; + } + + ret = copy_remapped_data(fs_info, new_addr, dest_addr, dest_length); + if (unlikely(ret)) + goto end; + + /* Change data of old remap entry. */ + leaf = path->nodes[0]; + remap_ptr = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_remap_item); + btrfs_set_remap_address(leaf, remap_ptr, dest_addr); + btrfs_mark_buffer_dirty(trans, leaf); + + if (dest_length != length) { + key.offset = dest_length; + btrfs_set_item_key_safe(trans, path, &key); + } + + btrfs_release_path(path); + + if (dest_length != length) { + /* Add remap item for remainder. */ + ret = add_remap_item(trans, path, new_addr + dest_length, + length - dest_length, old_addr + dest_length); + if (unlikely(ret)) + goto end; + } + + /* Change or remove old backref. */ + key.objectid = new_addr; + key.type = BTRFS_REMAP_BACKREF_KEY; + key.offset = length; + + ret = btrfs_search_slot(trans, fs_info->remap_root, &key, path, -1, 1); + if (unlikely(ret)) { + if (ret == 1) { + btrfs_release_path(path); + ret = -ENOENT; + } + goto end; + } + + leaf = path->nodes[0]; + + if (dest_length == length) { + ret = btrfs_del_item(trans, fs_info->remap_root, path); + if (unlikely(ret)) { + btrfs_release_path(path); + goto end; + } + } else { + key.objectid += dest_length; + key.offset -= dest_length; + btrfs_set_item_key_safe(trans, path, &key); + btrfs_set_stack_remap_address(&remap, old_addr + dest_length); + + write_extent_buffer(leaf, &remap, + btrfs_item_ptr_offset(leaf, path->slots[0]), + sizeof(struct btrfs_remap_item)); + } + + btrfs_release_path(path); + + /* Add new backref. */ + ret = add_remap_backref_item(trans, path, dest_addr, dest_length, old_addr); + if (unlikely(ret)) + goto end; + + adjust_block_group_remap_bytes(trans, bg, -dest_length); + + ret = btrfs_add_to_free_space_tree(trans, new_addr, dest_length); + 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); + if (unlikely(ret)) + goto end; + } + + ret = btrfs_remove_from_free_space_tree(trans, dest_addr, dest_length); + if (unlikely(ret)) { + btrfs_remove_from_free_space_tree(trans, new_addr, dest_length); + goto end; + } + + ret = 0; + +end: + if (mutex_taken) + mutex_unlock(&fs_info->remap_mutex); + + btrfs_dec_block_group_reservations(fs_info, dest_addr); + + if (unlikely(ret)) { + btrfs_free_reserved_extent(fs_info, dest_addr, dest_length, 0); + + if (trans) { + btrfs_abort_transaction(trans, ret); + 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); + } + + return ret; +} + +static int move_existing_remaps(struct btrfs_fs_info *fs_info, + struct btrfs_block_group *bg, + struct btrfs_path *path) +{ + int ret; + struct btrfs_key key; + struct extent_buffer *leaf; + struct btrfs_remap_item *remap; + u64 old_addr; + + /* Look for backrefs in remap tree. */ + while (bg->remap_bytes > 0) { + key.objectid = bg->start; + key.type = BTRFS_REMAP_BACKREF_KEY; + key.offset = 0; + + ret = btrfs_search_slot(NULL, fs_info->remap_root, &key, path, 0, 0); + if (ret < 0) + return ret; + + leaf = path->nodes[0]; + + if (path->slots[0] >= btrfs_header_nritems(leaf)) { + ret = btrfs_next_leaf(fs_info->remap_root, path); + if (ret < 0) { + btrfs_release_path(path); + return ret; + } + + if (ret) { + btrfs_release_path(path); + break; + } + + leaf = path->nodes[0]; + } + + btrfs_item_key_to_cpu(leaf, &key, path->slots[0]); + + if (key.type != BTRFS_REMAP_BACKREF_KEY) { + path->slots[0]++; + + if (path->slots[0] >= btrfs_header_nritems(leaf)) { + ret = btrfs_next_leaf(fs_info->remap_root, path); + if (ret < 0) { + btrfs_release_path(path); + return ret; + } + + if (ret) { + btrfs_release_path(path); + break; + } + + leaf = path->nodes[0]; + } + } + + remap = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_remap_item); + old_addr = btrfs_remap_address(leaf, remap); + + btrfs_release_path(path); + + ret = move_existing_remap(fs_info, path, bg, key.objectid, + key.offset, old_addr); + if (ret) + return ret; + } + + ASSERT(bg->remap_bytes == 0); + + return 0; +} + +static int create_remap_tree_entries(struct btrfs_trans_handle *trans, + struct btrfs_path *path, + struct btrfs_block_group *bg) +{ + struct btrfs_fs_info *fs_info = trans->fs_info; + struct btrfs_free_space_info *fsi; + struct btrfs_key key, found_key; + struct extent_buffer *leaf; + struct btrfs_root *space_root; + u32 extent_count; + struct space_run *space_runs = NULL; + unsigned int num_space_runs = 0; + struct btrfs_key *entries = NULL; + unsigned int max_entries, num_entries; + int ret; + + mutex_lock(&bg->free_space_lock); + + if (test_bit(BLOCK_GROUP_FLAG_NEEDS_FREE_SPACE, &bg->runtime_flags)) { + mutex_unlock(&bg->free_space_lock); + + ret = btrfs_add_block_group_free_space(trans, bg); + if (ret) + return ret; + + mutex_lock(&bg->free_space_lock); + } + + fsi = btrfs_search_free_space_info(trans, bg, path, 0); + if (IS_ERR(fsi)) { + mutex_unlock(&bg->free_space_lock); + return PTR_ERR(fsi); + } + + extent_count = btrfs_free_space_extent_count(path->nodes[0], fsi); + + btrfs_release_path(path); + + space_runs = kmalloc(sizeof(*space_runs) * extent_count, GFP_NOFS); + if (!space_runs) { + mutex_unlock(&bg->free_space_lock); + return -ENOMEM; + } + + key.objectid = bg->start; + key.type = 0; + key.offset = 0; + + space_root = btrfs_free_space_root(bg); + + ret = btrfs_search_slot(trans, space_root, &key, path, 0, 0); + if (ret < 0) { + mutex_unlock(&bg->free_space_lock); + goto out; + } + + ret = 0; + + while (true) { + leaf = path->nodes[0]; + + btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]); + + if (found_key.objectid >= bg->start + bg->length) + break; + + if (found_key.type == BTRFS_FREE_SPACE_EXTENT_KEY) { + if (num_space_runs != 0 && + space_runs[num_space_runs - 1].end == found_key.objectid) { + space_runs[num_space_runs - 1].end = + found_key.objectid + found_key.offset; + } else { + ASSERT(num_space_runs < extent_count); + + space_runs[num_space_runs].start = found_key.objectid; + space_runs[num_space_runs].end = + found_key.objectid + found_key.offset; + + num_space_runs++; + } + } else if (found_key.type == BTRFS_FREE_SPACE_BITMAP_KEY) { + void *bitmap; + unsigned long offset; + u32 data_size; + + offset = btrfs_item_ptr_offset(leaf, path->slots[0]); + data_size = btrfs_item_size(leaf, path->slots[0]); + + if (data_size != 0) { + bitmap = kmalloc(data_size, GFP_NOFS); + if (!bitmap) { + mutex_unlock(&bg->free_space_lock); + ret = -ENOMEM; + goto out; + } + + read_extent_buffer(leaf, bitmap, offset, data_size); + + parse_bitmap(fs_info->sectorsize, bitmap, + data_size * BITS_PER_BYTE, + found_key.objectid, space_runs, + &num_space_runs); + + ASSERT(num_space_runs <= extent_count); + + kfree(bitmap); + } + } + + path->slots[0]++; + + if (path->slots[0] >= btrfs_header_nritems(leaf)) { + ret = btrfs_next_leaf(space_root, path); + if (ret != 0) { + if (ret == 1) + ret = 0; + break; + } + leaf = path->nodes[0]; + } + } + + btrfs_release_path(path); + + mutex_unlock(&bg->free_space_lock); + + max_entries = extent_count + 2; + entries = kmalloc(sizeof(*entries) * max_entries, GFP_NOFS); + if (!entries) { + ret = -ENOMEM; + goto out; + } + + num_entries = 0; + + if (num_space_runs == 0) { + entries[num_entries].objectid = bg->start; + entries[num_entries].type = BTRFS_IDENTITY_REMAP_KEY; + entries[num_entries].offset = bg->length; + num_entries++; + } else { + if (space_runs[0].start > bg->start) { + entries[num_entries].objectid = bg->start; + entries[num_entries].type = BTRFS_IDENTITY_REMAP_KEY; + entries[num_entries].offset = space_runs[0].start - bg->start; + num_entries++; + } + + for (unsigned int i = 1; i < num_space_runs; i++) { + entries[num_entries].objectid = space_runs[i - 1].end; + entries[num_entries].type = BTRFS_IDENTITY_REMAP_KEY; + entries[num_entries].offset = + space_runs[i].start - space_runs[i - 1].end; + num_entries++; + } + + if (space_runs[num_space_runs - 1].end < bg->start + bg->length) { + entries[num_entries].objectid = + space_runs[num_space_runs - 1].end; + entries[num_entries].type = BTRFS_IDENTITY_REMAP_KEY; + entries[num_entries].offset = + bg->start + bg->length - space_runs[num_space_runs - 1].end; + num_entries++; + } + + if (num_entries == 0) + goto out; + } + + bg->identity_remap_count = num_entries; + + ret = add_remap_tree_entries(trans, path, entries, num_entries); + +out: + kfree(entries); + kfree(space_runs); + + return ret; +} + +static int find_next_identity_remap(struct btrfs_trans_handle *trans, + struct btrfs_path *path, u64 bg_end, + u64 last_start, u64 *start, u64 *length) +{ + int ret; + struct btrfs_key key, found_key; + struct btrfs_root *remap_root = trans->fs_info->remap_root; + struct extent_buffer *leaf; + + key.objectid = last_start; + key.type = BTRFS_IDENTITY_REMAP_KEY; + key.offset = 0; + + ret = btrfs_search_slot(trans, remap_root, &key, path, 0, 0); + if (ret < 0) + goto out; + + leaf = path->nodes[0]; + while (true) { + if (path->slots[0] >= btrfs_header_nritems(leaf)) { + ret = btrfs_next_leaf(remap_root, path); + + if (ret != 0) { + if (ret == 1) + ret = -ENOENT; + goto out; + } + + leaf = path->nodes[0]; + } + + btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]); + + if (found_key.objectid >= bg_end) { + ret = -ENOENT; + goto out; + } + + if (found_key.type == BTRFS_IDENTITY_REMAP_KEY) { + *start = found_key.objectid; + *length = found_key.offset; + ret = 0; + goto out; + } + + path->slots[0]++; + } + +out: + btrfs_release_path(path); + + return ret; +} + +static int remove_chunk_stripes(struct btrfs_trans_handle *trans, + struct btrfs_chunk_map *chunk_map, + struct btrfs_path *path) +{ + struct btrfs_fs_info *fs_info = trans->fs_info; + struct btrfs_key key; + struct extent_buffer *leaf; + struct btrfs_chunk *chunk; + int ret; + + key.objectid = BTRFS_FIRST_CHUNK_TREE_OBJECTID; + key.type = BTRFS_CHUNK_ITEM_KEY; + key.offset = chunk_map->start; + + btrfs_reserve_chunk_metadata(trans, false); + + ret = btrfs_search_slot(trans, fs_info->chunk_root, &key, path, 0, 1); + if (ret) { + if (ret == 1) { + btrfs_release_path(path); + ret = -ENOENT; + } + btrfs_trans_release_chunk_metadata(trans); + return ret; + } + + leaf = path->nodes[0]; + + chunk = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_chunk); + btrfs_set_chunk_num_stripes(leaf, chunk, 0); + btrfs_set_chunk_sub_stripes(leaf, chunk, 0); + + btrfs_truncate_item(trans, path, offsetof(struct btrfs_chunk, stripe), 1); + + btrfs_mark_buffer_dirty(trans, leaf); + + btrfs_release_path(path); + btrfs_trans_release_chunk_metadata(trans); + + return 0; +} + +int btrfs_last_identity_remap_gone(struct btrfs_chunk_map *chunk_map, + struct btrfs_block_group *bg) +{ + struct btrfs_fs_info *fs_info = bg->fs_info; + struct btrfs_trans_handle *trans; + int ret; + unsigned int num_items; + BTRFS_PATH_AUTO_FREE(path); + + path = btrfs_alloc_path(); + if (!path) + return -ENOMEM; + + /* + * One item for each entry we're removing in the dev extents tree, and + * another for each device. DUP chunks are all on one device, + * everything else has one device per stripe. + */ + if (bg->flags & BTRFS_BLOCK_GROUP_DUP) + num_items = chunk_map->num_stripes + 1; + else + num_items = 2 * chunk_map->num_stripes; + + trans = btrfs_start_transaction_fallback_global_rsv(fs_info->tree_root, num_items); + if (IS_ERR(trans)) + return PTR_ERR(trans); + + ret = btrfs_remove_dev_extents(trans, chunk_map); + if (unlikely(ret)) { + btrfs_abort_transaction(trans, ret); + return ret; + } + + mutex_lock(&trans->fs_info->chunk_mutex); + for (unsigned int i = 0; i < chunk_map->num_stripes; i++) { + ret = btrfs_update_device(trans, chunk_map->stripes[i].dev); + if (unlikely(ret)) { + mutex_unlock(&trans->fs_info->chunk_mutex); + btrfs_abort_transaction(trans, ret); + return ret; + } + } + mutex_unlock(&trans->fs_info->chunk_mutex); + + write_lock(&trans->fs_info->mapping_tree_lock); + btrfs_chunk_map_device_clear_bits(chunk_map, CHUNK_ALLOCATED); + write_unlock(&trans->fs_info->mapping_tree_lock); + + btrfs_remove_bg_from_sinfo(bg); + + spin_lock(&bg->lock); + clear_bit(BLOCK_GROUP_FLAG_STRIPE_REMOVAL_PENDING, &bg->runtime_flags); + spin_unlock(&bg->lock); + + ret = remove_chunk_stripes(trans, chunk_map, path); + if (unlikely(ret)) { + btrfs_abort_transaction(trans, ret); + return ret; + } + + ret = btrfs_commit_transaction(trans); + if (ret) + return ret; + + return 0; +} + +static void adjust_identity_remap_count(struct btrfs_trans_handle *trans, + struct btrfs_block_group *bg, int delta) +{ + struct btrfs_fs_info *fs_info = trans->fs_info; + bool bg_already_dirty = true; + bool mark_fully_remapped = false; + + WARN_ON(delta < 0 && -delta > bg->identity_remap_count); + + spin_lock(&bg->lock); + + bg->identity_remap_count += delta; + + if (bg->identity_remap_count == 0 && + !test_bit(BLOCK_GROUP_FLAG_FULLY_REMAPPED, &bg->runtime_flags)) { + set_bit(BLOCK_GROUP_FLAG_FULLY_REMAPPED, &bg->runtime_flags); + mark_fully_remapped = true; + } + + spin_unlock(&bg->lock); + + spin_lock(&trans->transaction->dirty_bgs_lock); + if (list_empty(&bg->dirty_list)) { + list_add_tail(&bg->dirty_list, &trans->transaction->dirty_bgs); + bg_already_dirty = false; + btrfs_get_block_group(bg); + } + spin_unlock(&trans->transaction->dirty_bgs_lock); + + /* Modified block groups are accounted for in the delayed_refs_rsv. */ + if (!bg_already_dirty) + btrfs_inc_delayed_refs_rsv_bg_updates(fs_info); + + if (mark_fully_remapped) + btrfs_mark_bg_fully_remapped(bg, trans); +} + +static int add_remap_entry(struct btrfs_trans_handle *trans, + struct btrfs_path *path, + struct btrfs_block_group *src_bg, u64 old_addr, + u64 new_addr, u64 length) +{ + struct btrfs_fs_info *fs_info = trans->fs_info; + struct btrfs_key key, new_key; + int ret; + int identity_count_delta = 0; + + key.objectid = old_addr; + key.type = (u8)-1; + key.offset = (u64)-1; + + ret = btrfs_search_slot(trans, fs_info->remap_root, &key, path, -1, 1); + if (ret < 0) + goto end; + + if (path->slots[0] == 0) { + ret = -ENOENT; + goto end; + } + + path->slots[0]--; + + btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]); + + if (key.type != BTRFS_IDENTITY_REMAP_KEY || + key.objectid > old_addr || + key.objectid + key.offset <= old_addr) { + ret = -ENOENT; + goto end; + } + + /* Shorten or delete identity mapping entry. */ + if (key.objectid == old_addr) { + ret = btrfs_del_item(trans, fs_info->remap_root, path); + if (ret) + goto end; + + identity_count_delta--; + } else { + new_key.objectid = key.objectid; + new_key.type = BTRFS_IDENTITY_REMAP_KEY; + new_key.offset = old_addr - key.objectid; + + btrfs_set_item_key_safe(trans, path, &new_key); + } + + btrfs_release_path(path); + + /* Create new remap entry. */ + ret = add_remap_item(trans, path, new_addr, length, old_addr); + if (ret) + goto end; + + /* Add entry for remainder of identity mapping, if necessary. */ + if (key.objectid + key.offset != old_addr + length) { + new_key.objectid = old_addr + length; + new_key.type = BTRFS_IDENTITY_REMAP_KEY; + new_key.offset = key.objectid + key.offset - old_addr - length; + + ret = btrfs_insert_empty_item(trans, fs_info->remap_root, + path, &new_key, 0); + if (ret) + goto end; + + btrfs_release_path(path); + + identity_count_delta++; + } + + /* Add backref. */ + ret = add_remap_backref_item(trans, path, new_addr, length, old_addr); + if (ret) + goto end; + + if (identity_count_delta != 0) + adjust_identity_remap_count(trans, src_bg, identity_count_delta); + +end: + btrfs_release_path(path); + + return ret; +} + +static int mark_chunk_remapped(struct btrfs_trans_handle *trans, + struct btrfs_path *path, u64 start) +{ + struct btrfs_fs_info *fs_info = trans->fs_info; + struct btrfs_chunk_map *chunk_map; + struct btrfs_key key; + u64 type; + int ret; + struct extent_buffer *leaf; + struct btrfs_chunk *chunk; + + read_lock(&fs_info->mapping_tree_lock); + + chunk_map = btrfs_find_chunk_map_nolock(fs_info, start, 1); + if (!chunk_map) { + read_unlock(&fs_info->mapping_tree_lock); + return -ENOENT; + } + + chunk_map->type |= BTRFS_BLOCK_GROUP_REMAPPED; + type = chunk_map->type; + + read_unlock(&fs_info->mapping_tree_lock); + + key.objectid = BTRFS_FIRST_CHUNK_TREE_OBJECTID; + key.type = BTRFS_CHUNK_ITEM_KEY; + key.offset = start; + + ret = btrfs_search_slot(trans, fs_info->chunk_root, &key, path, 0, 1); + if (ret == 1) { + ret = -ENOENT; + goto end; + } else if (ret < 0) + goto end; + + leaf = path->nodes[0]; + + chunk = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_chunk); + btrfs_set_chunk_type(leaf, chunk, type); + btrfs_mark_buffer_dirty(trans, leaf); + + ret = 0; +end: + btrfs_free_chunk_map(chunk_map); + btrfs_release_path(path); + + return ret; +} + +static int do_remap_reloc_trans(struct btrfs_fs_info *fs_info, + struct btrfs_block_group *src_bg, + struct btrfs_path *path, u64 *last_start) +{ + struct btrfs_trans_handle *trans; + struct btrfs_root *extent_root; + struct btrfs_key ins; + struct btrfs_block_group *dest_bg = NULL; + u64 start = 0, remap_length = 0; + u64 length, new_addr, min_size; + int ret; + const bool is_data = (src_bg->flags & BTRFS_BLOCK_GROUP_DATA); + bool no_more = false; + bool made_reservation = false, bg_needs_free_space; + struct btrfs_space_info *sinfo = src_bg->space_info; + + extent_root = btrfs_extent_root(fs_info, src_bg->start); + + trans = btrfs_start_transaction(extent_root, 0); + if (IS_ERR(trans)) + return PTR_ERR(trans); + + mutex_lock(&fs_info->remap_mutex); + + ret = find_next_identity_remap(trans, path, src_bg->start + src_bg->length, + *last_start, &start, &remap_length); + if (ret == -ENOENT) { + no_more = true; + goto next; + } else if (ret) { + mutex_unlock(&fs_info->remap_mutex); + btrfs_end_transaction(trans); + return ret; + } + + /* Try to reserve enough space for block. */ + spin_lock(&sinfo->lock); + btrfs_space_info_update_bytes_may_use(sinfo, remap_length); + spin_unlock(&sinfo->lock); + + if (is_data) + min_size = fs_info->sectorsize; + else + min_size = fs_info->nodesize; + + /* + * We're using btrfs_reserve_extent() to allocate a contiguous + * logical address range, but this will become a remap item rather than + * an extent in the extent tree. + * + * Short allocations are fine: it means that we chop off the beginning + * of the identity remap that we're processing, and will tackle the + * rest of it the next time round. + */ + ret = btrfs_reserve_extent(fs_info->fs_root, remap_length, remap_length, + min_size, 0, 0, &ins, is_data, false); + if (ret) { + spin_lock(&sinfo->lock); + btrfs_space_info_update_bytes_may_use(sinfo, -remap_length); + spin_unlock(&sinfo->lock); + + mutex_unlock(&fs_info->remap_mutex); + btrfs_end_transaction(trans); + return ret; + } + + made_reservation = true; + + new_addr = ins.objectid; + length = ins.offset; + + if (!is_data && !IS_ALIGNED(length, fs_info->nodesize)) { + u64 new_length = ALIGN_DOWN(length, fs_info->nodesize); + + btrfs_free_reserved_extent(fs_info, new_addr + new_length, + length - new_length, 0); + + length = new_length; + } + + dest_bg = btrfs_lookup_block_group(fs_info, new_addr); + + 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); + + if (bg_needs_free_space) { + ret = btrfs_add_block_group_free_space(trans, dest_bg); + if (ret) + goto fail; + } + + ret = copy_remapped_data(fs_info, start, new_addr, length); + if (ret) + goto fail; + + ret = btrfs_remove_from_free_space_tree(trans, new_addr, length); + if (ret) + goto fail; + + ret = add_remap_entry(trans, path, src_bg, start, new_addr, length); + if (ret) { + btrfs_add_to_free_space_tree(trans, new_addr, length); + goto fail; + } + + adjust_block_group_remap_bytes(trans, dest_bg, length); + btrfs_free_reserved_bytes(dest_bg, length, 0); + + spin_lock(&sinfo->lock); + sinfo->bytes_readonly += length; + spin_unlock(&sinfo->lock); + +next: + if (dest_bg) + btrfs_put_block_group(dest_bg); + + if (made_reservation) + btrfs_dec_block_group_reservations(fs_info, new_addr); + + mutex_unlock(&fs_info->remap_mutex); + + if (src_bg->identity_remap_count == 0) { + bool mark_fully_remapped = false; + + spin_lock(&src_bg->lock); + if (!test_bit(BLOCK_GROUP_FLAG_FULLY_REMAPPED, &src_bg->runtime_flags)) { + mark_fully_remapped = true; + set_bit(BLOCK_GROUP_FLAG_FULLY_REMAPPED, &src_bg->runtime_flags); + } + spin_unlock(&src_bg->lock); + + if (mark_fully_remapped) + btrfs_mark_bg_fully_remapped(src_bg, trans); + } + + ret = btrfs_end_transaction(trans); + if (ret) + return ret; + + if (no_more) + return 1; + + *last_start = start; + + return 0; + +fail: + if (dest_bg) + btrfs_put_block_group(dest_bg); + + btrfs_free_reserved_extent(fs_info, new_addr, length, 0); + + mutex_unlock(&fs_info->remap_mutex); + btrfs_end_transaction(trans); + + return ret; +} + +static int do_remap_reloc(struct btrfs_fs_info *fs_info, struct btrfs_path *path, + struct btrfs_block_group *bg) +{ + u64 last_start = bg->start; + int ret; + + while (true) { + ret = do_remap_reloc_trans(fs_info, bg, path, &last_start); + if (ret) { + if (ret == 1) + ret = 0; + break; + } + } + + return ret; +} + +int btrfs_translate_remap(struct btrfs_fs_info *fs_info, u64 *logical, u64 *length) +{ + int ret; + struct btrfs_key key, found_key; + struct extent_buffer *leaf; + struct btrfs_remap_item *remap; + BTRFS_PATH_AUTO_FREE(path); + + path = btrfs_alloc_path(); + if (!path) + return -ENOMEM; + + key.objectid = *logical; + key.type = (u8)-1; + key.offset = (u64)-1; + + ret = btrfs_search_slot(NULL, fs_info->remap_root, &key, path, 0, 0); + if (ret < 0) + return ret; + + leaf = path->nodes[0]; + if (path->slots[0] == 0) + return -ENOENT; + + path->slots[0]--; + + btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]); + + if (found_key.type != BTRFS_REMAP_KEY && + found_key.type != BTRFS_IDENTITY_REMAP_KEY) { + return -ENOENT; + } + + if (found_key.objectid > *logical || + found_key.objectid + found_key.offset <= *logical) { + return -ENOENT; + } + + if (*logical + *length > found_key.objectid + found_key.offset) + *length = found_key.objectid + found_key.offset - *logical; + + if (found_key.type == BTRFS_IDENTITY_REMAP_KEY) + return 0; + + remap = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_remap_item); + *logical += btrfs_remap_address(leaf, remap) - found_key.objectid; + + return 0; +} + +static int start_block_group_remapping(struct btrfs_fs_info *fs_info, + struct btrfs_path *path, + struct btrfs_block_group *bg) +{ + struct btrfs_trans_handle *trans; + bool bg_already_dirty = true; + int ret, ret2; + + ret = btrfs_cache_block_group(bg, true); + if (ret) + return ret; + + trans = btrfs_start_transaction(fs_info->remap_root, 0); + if (IS_ERR(trans)) + return PTR_ERR(trans); + + /* We need to run delayed refs, to make sure FST is up to date. */ + ret = btrfs_run_delayed_refs(trans, U64_MAX); + if (ret) { + btrfs_end_transaction(trans); + return ret; + } + + mutex_lock(&fs_info->remap_mutex); + + if (bg->flags & BTRFS_BLOCK_GROUP_REMAPPED) { + ret = 0; + goto end; + } + + ret = create_remap_tree_entries(trans, path, bg); + if (unlikely(ret)) { + btrfs_abort_transaction(trans, ret); + goto end; + } + + spin_lock(&bg->lock); + bg->flags |= BTRFS_BLOCK_GROUP_REMAPPED; + spin_unlock(&bg->lock); + + spin_lock(&trans->transaction->dirty_bgs_lock); + if (list_empty(&bg->dirty_list)) { + list_add_tail(&bg->dirty_list, &trans->transaction->dirty_bgs); + bg_already_dirty = false; + btrfs_get_block_group(bg); + } + spin_unlock(&trans->transaction->dirty_bgs_lock); + + /* Modified block groups are accounted for in the delayed_refs_rsv. */ + if (!bg_already_dirty) + btrfs_inc_delayed_refs_rsv_bg_updates(fs_info); + + ret = mark_chunk_remapped(trans, path, bg->start); + if (unlikely(ret)) { + btrfs_abort_transaction(trans, ret); + goto end; + } + + ret = btrfs_remove_block_group_free_space(trans, bg); + if (unlikely(ret)) { + btrfs_abort_transaction(trans, ret); + goto end; + } + + btrfs_remove_free_space_cache(bg); + +end: + mutex_unlock(&fs_info->remap_mutex); + + ret2 = btrfs_end_transaction(trans); + if (!ret) + ret = ret2; + + return ret; +} + +static int do_nonremap_reloc(struct btrfs_fs_info *fs_info, bool verbose, + struct reloc_control *rc) +{ + int ret; + + while (1) { + enum reloc_stage finishes_stage; + + mutex_lock(&fs_info->cleaner_mutex); + ret = relocate_block_group(rc); + mutex_unlock(&fs_info->cleaner_mutex); + + finishes_stage = rc->stage; + /* + * We may have gotten ENOSPC after we already dirtied some + * extents. If writeout happens while we're relocating a + * different block group we could end up hitting the + * BUG_ON(rc->stage == UPDATE_DATA_PTRS) in + * btrfs_reloc_cow_block. Make sure we write everything out + * properly so we don't trip over this problem, and then break + * out of the loop if we hit an error. + */ + if (rc->stage == MOVE_DATA_EXTENTS && rc->found_file_extent) { + int wb_ret; + + wb_ret = btrfs_wait_ordered_range(BTRFS_I(rc->data_inode), + 0, (u64)-1); + if (wb_ret && ret == 0) + ret = wb_ret; + invalidate_mapping_pages(rc->data_inode->i_mapping, 0, -1); + rc->stage = UPDATE_DATA_PTRS; + } + + if (ret < 0) + return ret; + + if (rc->extents_found == 0) + break; + + if (verbose) + btrfs_info(fs_info, "found %llu extents, stage: %s", + rc->extents_found, stage_to_string(finishes_stage)); + } + + WARN_ON(rc->block_group->pinned > 0); + WARN_ON(rc->block_group->reserved > 0); + WARN_ON(rc->block_group->used > 0); + + return 0; +} + /* * function to relocate all extents in a block group. */ @@ -3870,7 +5297,7 @@ int btrfs_relocate_block_group(struct btrfs_fs_info *fs_info, u64 group_start, struct btrfs_root *extent_root = btrfs_extent_root(fs_info, group_start); struct reloc_control *rc; struct inode *inode; - struct btrfs_path *path; + struct btrfs_path *path = NULL; int ret; bool bg_is_ro = false; @@ -3932,7 +5359,7 @@ int btrfs_relocate_block_group(struct btrfs_fs_info *fs_info, u64 group_start, } inode = lookup_free_space_inode(rc->block_group, path); - btrfs_free_path(path); + btrfs_release_path(path); if (!IS_ERR(inode)) ret = delete_block_group_cache(rc->block_group, inode, 0); @@ -3942,11 +5369,13 @@ int btrfs_relocate_block_group(struct btrfs_fs_info *fs_info, u64 group_start, if (ret && ret != -ENOENT) goto out; - rc->data_inode = create_reloc_inode(rc->block_group); - if (IS_ERR(rc->data_inode)) { - ret = PTR_ERR(rc->data_inode); - rc->data_inode = NULL; - goto out; + if (!btrfs_fs_incompat(fs_info, REMAP_TREE)) { + rc->data_inode = create_reloc_inode(rc->block_group); + if (IS_ERR(rc->data_inode)) { + ret = PTR_ERR(rc->data_inode); + rc->data_inode = NULL; + goto out; + } } if (verbose) @@ -3959,54 +5388,31 @@ int btrfs_relocate_block_group(struct btrfs_fs_info *fs_info, u64 group_start, ret = btrfs_zone_finish(rc->block_group); WARN_ON(ret && ret != -EAGAIN); - while (1) { - enum reloc_stage finishes_stage; - - mutex_lock(&fs_info->cleaner_mutex); - ret = relocate_block_group(rc); - mutex_unlock(&fs_info->cleaner_mutex); - - finishes_stage = rc->stage; - /* - * We may have gotten ENOSPC after we already dirtied some - * extents. If writeout happens while we're relocating a - * different block group we could end up hitting the - * BUG_ON(rc->stage == UPDATE_DATA_PTRS) in - * btrfs_reloc_cow_block. Make sure we write everything out - * properly so we don't trip over this problem, and then break - * out of the loop if we hit an error. - */ - if (rc->stage == MOVE_DATA_EXTENTS && rc->found_file_extent) { - int wb_ret; - - wb_ret = btrfs_wait_ordered_range(BTRFS_I(rc->data_inode), 0, - (u64)-1); - if (wb_ret && ret == 0) - ret = wb_ret; - invalidate_mapping_pages(rc->data_inode->i_mapping, - 0, -1); - rc->stage = UPDATE_DATA_PTRS; + if (should_relocate_using_remap_tree(bg)) { + if (bg->remap_bytes != 0) { + ret = move_existing_remaps(fs_info, bg, path); + if (ret) + goto out; } - - if (ret < 0) + ret = start_block_group_remapping(fs_info, path, bg); + if (ret) goto out; - if (rc->extents_found == 0) - break; + ret = do_remap_reloc(fs_info, path, rc->block_group); + if (ret) + goto out; - if (verbose) - btrfs_info(fs_info, "found %llu extents, stage: %s", - rc->extents_found, - stage_to_string(finishes_stage)); + btrfs_delete_unused_bgs(fs_info); + } else { + ret = do_nonremap_reloc(fs_info, verbose, rc); } - WARN_ON(rc->block_group->pinned > 0); - WARN_ON(rc->block_group->reserved > 0); - WARN_ON(rc->block_group->used > 0); out: if (ret && bg_is_ro) btrfs_dec_block_group_ro(rc->block_group); - iput(rc->data_inode); + if (!btrfs_fs_incompat(fs_info, REMAP_TREE)) + iput(rc->data_inode); + btrfs_free_path(path); reloc_chunk_end(fs_info); out_put_bg: btrfs_put_block_group(bg); @@ -4200,7 +5606,7 @@ out: btrfs_free_path(path); - if (ret == 0) { + if (ret == 0 && !btrfs_fs_incompat(fs_info, REMAP_TREE)) { /* cleanup orphan inode in data relocation tree */ fs_root = btrfs_grab_root(fs_info->data_reloc_root); ASSERT(fs_root); @@ -4414,3 +5820,260 @@ u64 btrfs_get_reloc_bg_bytenr(const struct btrfs_fs_info *fs_info) logical = fs_info->reloc_ctl->block_group->start; return logical; } + +static int insert_remap_item(struct btrfs_trans_handle *trans, struct btrfs_path *path, + u64 old_addr, u64 length, u64 new_addr) +{ + int ret; + struct btrfs_fs_info *fs_info = trans->fs_info; + struct btrfs_key key; + struct btrfs_remap_item remap = { 0 }; + + if (old_addr == new_addr) { + /* Add new identity remap item. */ + key.objectid = old_addr; + key.type = BTRFS_IDENTITY_REMAP_KEY; + key.offset = length; + + ret = btrfs_insert_empty_item(trans, fs_info->remap_root, path, + &key, 0); + if (ret) + return ret; + } else { + /* Add new remap item. */ + key.objectid = old_addr; + key.type = BTRFS_REMAP_KEY; + key.offset = length; + + ret = btrfs_insert_empty_item(trans, fs_info->remap_root, + path, &key, sizeof(struct btrfs_remap_item)); + if (ret) + return ret; + + btrfs_set_stack_remap_address(&remap, new_addr); + + write_extent_buffer(path->nodes[0], &remap, + btrfs_item_ptr_offset(path->nodes[0], path->slots[0]), + sizeof(struct btrfs_remap_item)); + + btrfs_release_path(path); + + /* Add new backref item. */ + key.objectid = new_addr; + key.type = BTRFS_REMAP_BACKREF_KEY; + key.offset = length; + + ret = btrfs_insert_empty_item(trans, fs_info->remap_root, + path, &key, + sizeof(struct btrfs_remap_item)); + if (ret) + return ret; + + btrfs_set_stack_remap_address(&remap, old_addr); + + write_extent_buffer(path->nodes[0], &remap, + btrfs_item_ptr_offset(path->nodes[0], path->slots[0]), + sizeof(struct btrfs_remap_item)); + } + + btrfs_release_path(path); + + return 0; +} + +/* + * Punch a hole in the remap item or identity remap item pointed to by path, + * for the range [hole_start, hole_start + hole_length). + */ +static int remove_range_from_remap_tree(struct btrfs_trans_handle *trans, + struct btrfs_path *path, + struct btrfs_block_group *bg, + u64 hole_start, u64 hole_length) +{ + int ret; + struct btrfs_fs_info *fs_info = trans->fs_info; + struct extent_buffer *leaf = path->nodes[0]; + struct btrfs_key key; + u64 hole_end, new_addr, remap_start, remap_length, remap_end; + u64 overlap_length; + bool is_identity_remap; + int identity_count_delta = 0; + + hole_end = hole_start + hole_length; + + btrfs_item_key_to_cpu(leaf, &key, path->slots[0]); + + is_identity_remap = (key.type == BTRFS_IDENTITY_REMAP_KEY); + + remap_start = key.objectid; + remap_length = key.offset; + remap_end = remap_start + remap_length; + + if (is_identity_remap) { + new_addr = remap_start; + } else { + struct btrfs_remap_item *remap_ptr; + + remap_ptr = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_remap_item); + new_addr = btrfs_remap_address(leaf, remap_ptr); + } + + /* Delete old item. */ + ret = btrfs_del_item(trans, fs_info->remap_root, path); + btrfs_release_path(path); + if (ret) + return ret; + + if (is_identity_remap) { + identity_count_delta = -1; + } else { + /* Remove backref. */ + key.objectid = new_addr; + key.type = BTRFS_REMAP_BACKREF_KEY; + key.offset = remap_length; + + ret = btrfs_search_slot(trans, fs_info->remap_root, &key, path, -1, 1); + if (ret) { + if (ret == 1) { + btrfs_release_path(path); + ret = -ENOENT; + } + return ret; + } + + ret = btrfs_del_item(trans, fs_info->remap_root, path); + + btrfs_release_path(path); + + if (ret) + return ret; + } + + /* If hole_start > remap_start, re-add the start of the remap item. */ + if (hole_start > remap_start) { + ret = insert_remap_item(trans, path, remap_start, + hole_start - remap_start, new_addr); + if (ret) + return ret; + + if (is_identity_remap) + identity_count_delta++; + } + + /* If hole_end < remap_end, re-add the end of the remap item. */ + if (hole_end < remap_end) { + ret = insert_remap_item(trans, path, hole_end, + remap_end - hole_end, + hole_end - remap_start + new_addr); + if (ret) + return ret; + + if (is_identity_remap) + identity_count_delta++; + } + + if (identity_count_delta != 0) + adjust_identity_remap_count(trans, bg, identity_count_delta); + + overlap_length = min_t(u64, hole_end, remap_end) - + max_t(u64, hole_start, remap_start); + + if (!is_identity_remap) { + struct btrfs_block_group *dest_bg; + + dest_bg = btrfs_lookup_block_group(fs_info, new_addr); + adjust_block_group_remap_bytes(trans, dest_bg, -overlap_length); + btrfs_put_block_group(dest_bg); + ret = btrfs_add_to_free_space_tree(trans, + hole_start - remap_start + new_addr, + overlap_length); + if (ret) + return ret; + } + + ret = overlap_length; + + return ret; +} + +/* + * Return 1 if remove_range_from_remap_tree() has been called successfully, + * 0 if block group wasn't remapped, and a negative number on error. + */ +int btrfs_remove_extent_from_remap_tree(struct btrfs_trans_handle *trans, + struct btrfs_path *path, + u64 bytenr, u64 num_bytes) +{ + struct btrfs_fs_info *fs_info = trans->fs_info; + struct btrfs_key key, found_key; + struct extent_buffer *leaf; + struct btrfs_block_group *bg; + int ret, length; + + if (!(btrfs_super_incompat_flags(fs_info->super_copy) & + BTRFS_FEATURE_INCOMPAT_REMAP_TREE)) + return 0; + + bg = btrfs_lookup_block_group(fs_info, bytenr); + if (!bg) + return 0; + + mutex_lock(&fs_info->remap_mutex); + + if (!(bg->flags & BTRFS_BLOCK_GROUP_REMAPPED)) { + mutex_unlock(&fs_info->remap_mutex); + btrfs_put_block_group(bg); + return 0; + } + + do { + key.objectid = bytenr; + key.type = (u8)-1; + key.offset = (u64)-1; + + ret = btrfs_search_slot(trans, fs_info->remap_root, &key, path, -1, 1); + if (ret < 0) + goto end; + + leaf = path->nodes[0]; + if (path->slots[0] == 0) { + ret = -ENOENT; + goto end; + } + + path->slots[0]--; + + btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]); + + if (found_key.type != BTRFS_IDENTITY_REMAP_KEY && + found_key.type != BTRFS_REMAP_KEY) { + ret = -ENOENT; + goto end; + } + + if (bytenr < found_key.objectid || + bytenr >= found_key.objectid + found_key.offset) { + ret = -ENOENT; + goto end; + } + + length = remove_range_from_remap_tree(trans, path, bg, bytenr, num_bytes); + if (length < 0) { + ret = length; + goto end; + } + + bytenr += length; + num_bytes -= length; + } while (num_bytes > 0); + + ret = 1; + +end: + mutex_unlock(&fs_info->remap_mutex); + + btrfs_put_block_group(bg); + btrfs_release_path(path); + + return ret; +} diff --git a/fs/btrfs/relocation.h b/fs/btrfs/relocation.h index 5c36b3f84b57..d647823b5d13 100644 --- a/fs/btrfs/relocation.h +++ b/fs/btrfs/relocation.h @@ -12,6 +12,17 @@ struct btrfs_trans_handle; struct btrfs_ordered_extent; struct btrfs_pending_snapshot; +static inline bool should_relocate_using_remap_tree(const struct btrfs_block_group *bg) +{ + if (!btrfs_fs_incompat(bg->fs_info, REMAP_TREE)) + return false; + + if (bg->flags & (BTRFS_BLOCK_GROUP_SYSTEM | BTRFS_BLOCK_GROUP_METADATA_REMAP)) + return false; + + return true; +} + int btrfs_relocate_block_group(struct btrfs_fs_info *fs_info, u64 group_start, bool verbose); int btrfs_init_reloc_root(struct btrfs_trans_handle *trans, struct btrfs_root *root); @@ -31,5 +42,11 @@ int btrfs_should_cancel_balance(const struct btrfs_fs_info *fs_info); struct btrfs_root *find_reloc_root(struct btrfs_fs_info *fs_info, u64 bytenr); bool btrfs_should_ignore_reloc_root(const struct btrfs_root *root); u64 btrfs_get_reloc_bg_bytenr(const struct btrfs_fs_info *fs_info); +int btrfs_translate_remap(struct btrfs_fs_info *fs_info, u64 *logical, u64 *length); +int btrfs_remove_extent_from_remap_tree(struct btrfs_trans_handle *trans, + struct btrfs_path *path, + u64 bytenr, u64 num_bytes); +int btrfs_last_identity_remap_gone(struct btrfs_chunk_map *chunk_map, + struct btrfs_block_group *bg); #endif diff --git a/fs/btrfs/root-tree.c b/fs/btrfs/root-tree.c index 6a7e297ab0a7..37a4173c0a0b 100644 --- a/fs/btrfs/root-tree.c +++ b/fs/btrfs/root-tree.c @@ -217,8 +217,6 @@ int btrfs_find_orphan_roots(struct btrfs_fs_info *fs_info) BTRFS_PATH_AUTO_FREE(path); struct btrfs_key key; struct btrfs_root *root; - int err = 0; - int ret; path = btrfs_alloc_path(); if (!path) @@ -230,20 +228,19 @@ int btrfs_find_orphan_roots(struct btrfs_fs_info *fs_info) while (1) { u64 root_objectid; + int ret; ret = btrfs_search_slot(NULL, tree_root, &key, path, 0, 0); - if (ret < 0) { - err = ret; - break; - } + if (ret < 0) + return ret; leaf = path->nodes[0]; if (path->slots[0] >= btrfs_header_nritems(leaf)) { ret = btrfs_next_leaf(tree_root, path); if (ret < 0) - err = ret; - if (ret != 0) - break; + return ret; + else if (ret > 0) + return 0; leaf = path->nodes[0]; } @@ -252,34 +249,32 @@ int btrfs_find_orphan_roots(struct btrfs_fs_info *fs_info) if (key.objectid != BTRFS_ORPHAN_OBJECTID || key.type != BTRFS_ORPHAN_ITEM_KEY) - break; + return 0; root_objectid = key.offset; key.offset++; root = btrfs_get_fs_root(fs_info, root_objectid, false); - err = PTR_ERR_OR_ZERO(root); - if (err && err != -ENOENT) { + ret = PTR_ERR_OR_ZERO(root); + if (ret && ret != -ENOENT) { break; - } else if (err == -ENOENT) { + } else if (ret == -ENOENT) { struct btrfs_trans_handle *trans; - btrfs_release_path(path); - trans = btrfs_join_transaction(tree_root); if (IS_ERR(trans)) { - err = PTR_ERR(trans); - btrfs_handle_fs_error(fs_info, err, - "Failed to start trans to delete orphan item"); - break; + ret = PTR_ERR(trans); + btrfs_err(fs_info, + "failed to join transaction to delete orphan item: %d", + ret); + return ret; } - err = btrfs_del_orphan_item(trans, tree_root, - root_objectid); + ret = btrfs_del_orphan_item(trans, tree_root, root_objectid); btrfs_end_transaction(trans); - if (err) { - btrfs_handle_fs_error(fs_info, err, - "Failed to delete root orphan item"); - break; + if (ret) { + btrfs_err(fs_info, + "failed to delete root orphan item: %d", ret); + return ret; } continue; } @@ -307,7 +302,7 @@ int btrfs_find_orphan_roots(struct btrfs_fs_info *fs_info) btrfs_put_root(root); } - return err; + return 0; } /* drop the root item for 'key' from the tree root */ diff --git a/fs/btrfs/scrub.c b/fs/btrfs/scrub.c index a40ee41f42c6..2a64e2d50ced 100644 --- a/fs/btrfs/scrub.c +++ b/fs/btrfs/scrub.c @@ -6,7 +6,6 @@ #include <linux/blkdev.h> #include <linux/ratelimit.h> #include <linux/sched/mm.h> -#include <crypto/hash.h> #include "ctree.h" #include "discard.h" #include "volumes.h" @@ -718,7 +717,7 @@ static void scrub_verify_one_metadata(struct scrub_stripe *stripe, int sector_nr const u64 logical = stripe->logical + (sector_nr << fs_info->sectorsize_bits); void *first_kaddr = scrub_stripe_get_kaddr(stripe, sector_nr); struct btrfs_header *header = first_kaddr; - SHASH_DESC_ON_STACK(shash, fs_info->csum_shash); + struct btrfs_csum_ctx csum; u8 on_disk_csum[BTRFS_CSUM_SIZE]; u8 calculated_csum[BTRFS_CSUM_SIZE]; @@ -760,17 +759,16 @@ static void scrub_verify_one_metadata(struct scrub_stripe *stripe, int sector_nr } /* Now check tree block csum. */ - shash->tfm = fs_info->csum_shash; - crypto_shash_init(shash); - crypto_shash_update(shash, first_kaddr + BTRFS_CSUM_SIZE, - fs_info->sectorsize - BTRFS_CSUM_SIZE); + btrfs_csum_init(&csum, fs_info->csum_type); + btrfs_csum_update(&csum, first_kaddr + BTRFS_CSUM_SIZE, + fs_info->sectorsize - BTRFS_CSUM_SIZE); for (int i = sector_nr + 1; i < sector_nr + sectors_per_tree; i++) { - crypto_shash_update(shash, scrub_stripe_get_kaddr(stripe, i), - fs_info->sectorsize); + btrfs_csum_update(&csum, scrub_stripe_get_kaddr(stripe, i), + fs_info->sectorsize); } - crypto_shash_final(shash, calculated_csum); + btrfs_csum_final(&csum, calculated_csum); if (memcmp(calculated_csum, on_disk_csum, fs_info->csum_size) != 0) { scrub_bitmap_set_meta_error(stripe, sector_nr, sectors_per_tree); scrub_bitmap_set_error(stripe, sector_nr, sectors_per_tree); @@ -1690,15 +1688,15 @@ static int scrub_find_fill_first_stripe(struct btrfs_block_group *bg, scrub_stripe_reset_bitmaps(stripe); /* The range must be inside the bg. */ - ASSERT(logical_start >= bg->start && logical_end <= bg->start + bg->length, + ASSERT(logical_start >= bg->start && logical_end <= btrfs_block_group_end(bg), "bg->start=%llu logical_start=%llu logical_end=%llu end=%llu", - bg->start, logical_start, logical_end, bg->start + bg->length); + bg->start, logical_start, logical_end, btrfs_block_group_end(bg)); ret = find_first_extent_item(extent_root, extent_path, logical_start, logical_len); /* Either error or not found. */ if (ret) - goto out; + return ret; get_extent_info(extent_path, &extent_start, &extent_len, &extent_flags, &extent_gen); if (extent_flags & BTRFS_EXTENT_FLAG_TREE_BLOCK) @@ -1731,7 +1729,7 @@ static int scrub_find_fill_first_stripe(struct btrfs_block_group *bg, ret = find_first_extent_item(extent_root, extent_path, cur_logical, stripe_end - cur_logical + 1); if (ret < 0) - goto out; + return ret; if (ret > 0) { ret = 0; break; @@ -1765,7 +1763,7 @@ static int scrub_find_fill_first_stripe(struct btrfs_block_group *bg, stripe->logical, stripe_end, stripe->csums, &csum_bitmap); if (ret < 0) - goto out; + return ret; if (ret > 0) ret = 0; @@ -1775,7 +1773,7 @@ static int scrub_find_fill_first_stripe(struct btrfs_block_group *bg, } } set_bit(SCRUB_STRIPE_FLAG_INITIALIZED, &stripe->state); -out: + return ret; } @@ -2173,8 +2171,8 @@ static int scrub_raid56_parity_stripe(struct scrub_ctx *sctx, u64 full_stripe_start) { struct btrfs_fs_info *fs_info = sctx->fs_info; - struct btrfs_path extent_path = { 0 }; - struct btrfs_path csum_path = { 0 }; + BTRFS_PATH_AUTO_RELEASE(extent_path); + BTRFS_PATH_AUTO_RELEASE(csum_path); struct scrub_stripe *stripe; bool all_empty = true; const int data_stripes = nr_data_stripes(map); @@ -2226,7 +2224,7 @@ static int scrub_raid56_parity_stripe(struct scrub_ctx *sctx, full_stripe_start + btrfs_stripe_nr_to_offset(i), BTRFS_STRIPE_LEN, stripe); if (ret < 0) - goto out; + return ret; /* * No extent in this data stripe, need to manually mark them * initialized to make later read submission happy. @@ -2248,10 +2246,8 @@ static int scrub_raid56_parity_stripe(struct scrub_ctx *sctx, break; } } - if (all_empty) { - ret = 0; - goto out; - } + if (all_empty) + return 0; for (int i = 0; i < data_stripes; i++) { stripe = &sctx->raid56_data_stripes[i]; @@ -2292,20 +2288,15 @@ static int scrub_raid56_parity_stripe(struct scrub_ctx *sctx, "scrub: unrepaired sectors detected, full stripe %llu data stripe %u errors %*pbl", full_stripe_start, i, stripe->nr_sectors, &error); - ret = -EIO; - goto out; + return ret; } bitmap_or(&extent_bitmap, &extent_bitmap, &has_extent, stripe->nr_sectors); } /* Now we can check and regenerate the P/Q stripe. */ - ret = scrub_raid56_cached_parity(sctx, scrub_dev, map, full_stripe_start, - &extent_bitmap); -out: - btrfs_release_path(&extent_path); - btrfs_release_path(&csum_path); - return ret; + return scrub_raid56_cached_parity(sctx, scrub_dev, map, full_stripe_start, + &extent_bitmap); } /* @@ -2328,7 +2319,7 @@ static int scrub_simple_mirror(struct scrub_ctx *sctx, int ret = 0; /* The range must be inside the bg */ - ASSERT(logical_start >= bg->start && logical_end <= bg->start + bg->length); + ASSERT(logical_start >= bg->start && logical_end <= btrfs_block_group_end(bg)); /* Go through each extent items inside the logical range */ while (cur_logical < logical_end) { @@ -2420,12 +2411,13 @@ static int scrub_simple_stripe(struct scrub_ctx *sctx, const u64 logical_increment = simple_stripe_full_stripe_len(map); const u64 orig_logical = simple_stripe_get_logical(map, bg, stripe_index); const u64 orig_physical = map->stripes[stripe_index].physical; + const u64 end = btrfs_block_group_end(bg); const int mirror_num = simple_stripe_mirror_num(map, stripe_index); u64 cur_logical = orig_logical; u64 cur_physical = orig_physical; int ret = 0; - while (cur_logical < bg->start + bg->length) { + while (cur_logical < end) { /* * Inside each stripe, RAID0 is just SINGLE, and RAID10 is * just RAID1, so we can reuse scrub_simple_mirror() to scrub diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c index d8127a7120c2..3dcfdba018b5 100644 --- a/fs/btrfs/send.c +++ b/fs/btrfs/send.c @@ -6449,11 +6449,9 @@ static int process_extent(struct send_ctx *sctx, if (sctx->parent_root && !sctx->cur_inode_new) { ret = is_extent_unchanged(sctx, path, key); if (ret < 0) - goto out; - if (ret) { - ret = 0; + return ret; + if (ret) goto out_hole; - } } else { struct btrfs_file_extent_item *ei; u8 type; @@ -6469,31 +6467,25 @@ static int process_extent(struct send_ctx *sctx, * we have enough commands queued up to justify rev'ing * the send spec. */ - if (type == BTRFS_FILE_EXTENT_PREALLOC) { - ret = 0; - goto out; - } + if (type == BTRFS_FILE_EXTENT_PREALLOC) + return 0; /* Have a hole, just skip it. */ - if (btrfs_file_extent_disk_bytenr(path->nodes[0], ei) == 0) { - ret = 0; - goto out; - } + if (btrfs_file_extent_disk_bytenr(path->nodes[0], ei) == 0) + return 0; } } ret = find_extent_clone(sctx, path, key->objectid, key->offset, sctx->cur_inode_size, &found_clone); if (ret != -ENOENT && ret < 0) - goto out; + return ret; ret = send_write_or_clone(sctx, path, key, found_clone); if (ret) - goto out; + return ret; out_hole: - ret = maybe_send_hole(sctx, path, key); -out: - return ret; + return maybe_send_hole(sctx, path, key); } static int process_all_extents(struct send_ctx *sctx) @@ -6535,23 +6527,24 @@ static int process_recorded_refs_if_needed(struct send_ctx *sctx, bool at_end, int *pending_move, int *refs_processed) { - int ret = 0; + int ret; if (sctx->cur_ino == 0) - goto out; + return 0; + if (!at_end && sctx->cur_ino == sctx->cmp_key->objectid && sctx->cmp_key->type <= BTRFS_INODE_EXTREF_KEY) - goto out; + return 0; + if (list_empty(&sctx->new_refs) && list_empty(&sctx->deleted_refs)) - goto out; + return 0; ret = process_recorded_refs(sctx, pending_move); if (ret < 0) - goto out; + return ret; *refs_processed = 1; -out: - return ret; + return 0; } static int finish_inode_if_needed(struct send_ctx *sctx, bool at_end) @@ -6768,7 +6761,7 @@ static void close_current_inode(struct send_ctx *sctx) static int changed_inode(struct send_ctx *sctx, enum btrfs_compare_tree_result result) { - int ret = 0; + int ret; struct btrfs_key *key = sctx->cmp_key; struct btrfs_inode_item *left_ii = NULL; struct btrfs_inode_item *right_ii = NULL; @@ -6860,7 +6853,7 @@ static int changed_inode(struct send_ctx *sctx, if (result == BTRFS_COMPARE_TREE_NEW) { if (btrfs_inode_nlink(sctx->left_path->nodes[0], left_ii) == 0) { sctx->ignore_cur_inode = true; - goto out; + return 0; } sctx->cur_inode_gen = left_gen; sctx->cur_inode_new = true; @@ -6888,7 +6881,7 @@ static int changed_inode(struct send_ctx *sctx, old_nlinks = btrfs_inode_nlink(sctx->right_path->nodes[0], right_ii); if (new_nlinks == 0 && old_nlinks == 0) { sctx->ignore_cur_inode = true; - goto out; + return 0; } else if (new_nlinks == 0 || old_nlinks == 0) { sctx->cur_inode_new_gen = 1; } @@ -6914,7 +6907,7 @@ static int changed_inode(struct send_ctx *sctx, ret = process_all_refs(sctx, BTRFS_COMPARE_TREE_DELETED); if (ret < 0) - goto out; + return ret; } /* @@ -6935,11 +6928,11 @@ static int changed_inode(struct send_ctx *sctx, left_ii); ret = send_create_inode_if_needed(sctx); if (ret < 0) - goto out; + return ret; ret = process_all_refs(sctx, BTRFS_COMPARE_TREE_NEW); if (ret < 0) - goto out; + return ret; /* * Advance send_progress now as we did not get * into process_recorded_refs_if_needed in the @@ -6953,10 +6946,10 @@ static int changed_inode(struct send_ctx *sctx, */ ret = process_all_extents(sctx); if (ret < 0) - goto out; + return ret; ret = process_all_new_xattrs(sctx); if (ret < 0) - goto out; + return ret; } } else { sctx->cur_inode_gen = left_gen; @@ -6970,8 +6963,7 @@ static int changed_inode(struct send_ctx *sctx, } } -out: - return ret; + return 0; } /* @@ -7104,20 +7096,20 @@ static int compare_refs(struct send_ctx *sctx, struct btrfs_path *path, u32 item_size; u32 cur_offset = 0; int ref_name_len; - int ret = 0; /* Easy case, just check this one dirid */ if (key->type == BTRFS_INODE_REF_KEY) { dirid = key->offset; - ret = dir_changed(sctx, dirid); - goto out; + return dir_changed(sctx, dirid); } leaf = path->nodes[0]; item_size = btrfs_item_size(leaf, path->slots[0]); ptr = btrfs_item_ptr_offset(leaf, path->slots[0]); while (cur_offset < item_size) { + int ret; + extref = (struct btrfs_inode_extref *)(ptr + cur_offset); dirid = btrfs_inode_extref_parent(leaf, extref); @@ -7127,11 +7119,10 @@ static int compare_refs(struct send_ctx *sctx, struct btrfs_path *path, continue; ret = dir_changed(sctx, dirid); if (ret) - break; + return ret; last_dirid = dirid; } -out: - return ret; + return 0; } /* @@ -7212,12 +7203,12 @@ static int changed_cb(struct btrfs_path *left_path, ret = finish_inode_if_needed(sctx, 0); if (ret < 0) - goto out; + return ret; /* Ignore non-FS objects */ if (key->objectid == BTRFS_FREE_INO_OBJECTID || key->objectid == BTRFS_FREE_SPACE_OBJECTID) - goto out; + return 0; if (key->type == BTRFS_INODE_ITEM_KEY) { ret = changed_inode(sctx, result); @@ -7234,7 +7225,6 @@ static int changed_cb(struct btrfs_path *left_path, ret = changed_verity(sctx, result); } -out: return ret; } diff --git a/fs/btrfs/space-info.c b/fs/btrfs/space-info.c index 3f08e450f796..bb5aac7ee9d2 100644 --- a/fs/btrfs/space-info.c +++ b/fs/btrfs/space-info.c @@ -215,7 +215,7 @@ static u64 calc_chunk_size(const struct btrfs_fs_info *fs_info, u64 flags) if (flags & BTRFS_BLOCK_GROUP_DATA) return BTRFS_MAX_DATA_CHUNK_SIZE; - else if (flags & BTRFS_BLOCK_GROUP_SYSTEM) + else if (flags & (BTRFS_BLOCK_GROUP_SYSTEM | BTRFS_BLOCK_GROUP_METADATA_REMAP)) return SZ_32M; /* Handle BTRFS_BLOCK_GROUP_METADATA */ @@ -329,7 +329,7 @@ int btrfs_init_space_info(struct btrfs_fs_info *fs_info) struct btrfs_super_block *disk_super; u64 features; u64 flags; - int mixed = 0; + bool mixed = false; int ret; disk_super = fs_info->super_copy; @@ -338,26 +338,35 @@ int btrfs_init_space_info(struct btrfs_fs_info *fs_info) features = btrfs_super_incompat_flags(disk_super); if (features & BTRFS_FEATURE_INCOMPAT_MIXED_GROUPS) - mixed = 1; + mixed = true; flags = BTRFS_BLOCK_GROUP_SYSTEM; ret = create_space_info(fs_info, flags); if (ret) - goto out; + return ret; if (mixed) { flags = BTRFS_BLOCK_GROUP_METADATA | BTRFS_BLOCK_GROUP_DATA; ret = create_space_info(fs_info, flags); + if (ret) + return ret; } else { flags = BTRFS_BLOCK_GROUP_METADATA; ret = create_space_info(fs_info, flags); if (ret) - goto out; + return ret; flags = BTRFS_BLOCK_GROUP_DATA; ret = create_space_info(fs_info, flags); + if (ret) + return ret; + } + + if (features & BTRFS_FEATURE_INCOMPAT_REMAP_TREE) { + flags = BTRFS_BLOCK_GROUP_METADATA_REMAP; + ret = create_space_info(fs_info, flags); } -out: + return ret; } @@ -370,8 +379,13 @@ void btrfs_add_bg_to_space_info(struct btrfs_fs_info *info, factor = btrfs_bg_type_to_factor(block_group->flags); spin_lock(&space_info->lock); - space_info->total_bytes += block_group->length; - space_info->disk_total += block_group->length * factor; + + if (!(block_group->flags & BTRFS_BLOCK_GROUP_REMAPPED) || + block_group->identity_remap_count != 0) { + space_info->total_bytes += block_group->length; + space_info->disk_total += block_group->length * factor; + } + space_info->bytes_used += block_group->used; space_info->disk_used += block_group->used * factor; space_info->bytes_readonly += block_group->bytes_super; @@ -606,27 +620,12 @@ do { \ spin_unlock(&__rsv->lock); \ } while (0) -static const char *space_info_flag_to_str(const struct btrfs_space_info *space_info) -{ - switch (space_info->flags) { - case BTRFS_BLOCK_GROUP_SYSTEM: - return "SYSTEM"; - case BTRFS_BLOCK_GROUP_METADATA | BTRFS_BLOCK_GROUP_DATA: - return "DATA+METADATA"; - case BTRFS_BLOCK_GROUP_DATA: - return "DATA"; - case BTRFS_BLOCK_GROUP_METADATA: - return "METADATA"; - default: - return "UNKNOWN"; - } -} - static void dump_global_block_rsv(struct btrfs_fs_info *fs_info) { DUMP_BLOCK_RSV(fs_info, global_block_rsv); DUMP_BLOCK_RSV(fs_info, trans_block_rsv); DUMP_BLOCK_RSV(fs_info, chunk_block_rsv); + DUMP_BLOCK_RSV(fs_info, remap_block_rsv); DUMP_BLOCK_RSV(fs_info, delayed_block_rsv); DUMP_BLOCK_RSV(fs_info, delayed_refs_rsv); } @@ -634,7 +633,7 @@ static void dump_global_block_rsv(struct btrfs_fs_info *fs_info) static void __btrfs_dump_space_info(const struct btrfs_space_info *info) { const struct btrfs_fs_info *fs_info = info->fs_info; - const char *flag_str = space_info_flag_to_str(info); + const char *flag_str = btrfs_space_info_type_str(info); lockdep_assert_held(&info->lock); /* The free space could be negative in case of overcommit */ @@ -672,8 +671,7 @@ again: u64 avail; spin_lock(&cache->lock); - avail = cache->length - cache->used - cache->pinned - - cache->reserved - cache->bytes_super - cache->zone_unusable; + avail = btrfs_block_group_available_space(cache); btrfs_info(fs_info, "block group %llu has %llu bytes, %llu used %llu pinned %llu reserved %llu delalloc %llu super %llu zone_unusable (%llu bytes available) %s", cache->start, cache->length, cache->used, cache->pinned, @@ -2099,11 +2097,11 @@ static bool is_reclaim_urgent(struct btrfs_space_info *space_info) return unalloc < data_chunk_size; } -static void do_reclaim_sweep(struct btrfs_space_info *space_info, int raid) +static bool do_reclaim_sweep(struct btrfs_space_info *space_info, int raid) { struct btrfs_block_group *bg; int thresh_pct; - bool try_again = true; + bool will_reclaim = false; bool urgent; spin_lock(&space_info->lock); @@ -2121,7 +2119,7 @@ again: spin_lock(&bg->lock); thresh = mult_perc(bg->length, thresh_pct); if (bg->used < thresh && bg->reclaim_mark) { - try_again = false; + will_reclaim = true; reclaim = true; } bg->reclaim_mark++; @@ -2138,12 +2136,13 @@ again: * If we have any staler groups, we don't touch the fresher ones, but if we * really need a block group, do take a fresh one. */ - if (try_again && urgent) { - try_again = false; + if (!will_reclaim && urgent) { + urgent = false; goto again; } up_read(&space_info->groups_sem); + return will_reclaim; } void btrfs_space_info_update_reclaimable(struct btrfs_space_info *space_info, s64 bytes) @@ -2153,7 +2152,8 @@ void btrfs_space_info_update_reclaimable(struct btrfs_space_info *space_info, s6 lockdep_assert_held(&space_info->lock); space_info->reclaimable_bytes += bytes; - if (space_info->reclaimable_bytes >= chunk_sz) + if (space_info->reclaimable_bytes > 0 && + space_info->reclaimable_bytes >= chunk_sz) btrfs_set_periodic_reclaim_ready(space_info, true); } @@ -2180,7 +2180,6 @@ static bool btrfs_should_periodic_reclaim(struct btrfs_space_info *space_info) spin_lock(&space_info->lock); ret = space_info->periodic_reclaim_ready; - btrfs_set_periodic_reclaim_ready(space_info, false); spin_unlock(&space_info->lock); return ret; @@ -2194,8 +2193,10 @@ void btrfs_reclaim_sweep(const struct btrfs_fs_info *fs_info) list_for_each_entry(space_info, &fs_info->space_info, list) { if (!btrfs_should_periodic_reclaim(space_info)) continue; - for (raid = 0; raid < BTRFS_NR_RAID_TYPES; raid++) - do_reclaim_sweep(space_info, raid); + for (raid = 0; raid < BTRFS_NR_RAID_TYPES; raid++) { + if (do_reclaim_sweep(space_info, raid)) + btrfs_set_periodic_reclaim_ready(space_info, false); + } } } diff --git a/fs/btrfs/space-info.h b/fs/btrfs/space-info.h index 446c0614ad4a..0703f24b23f7 100644 --- a/fs/btrfs/space-info.h +++ b/fs/btrfs/space-info.h @@ -307,4 +307,20 @@ int btrfs_calc_reclaim_threshold(const struct btrfs_space_info *space_info); void btrfs_reclaim_sweep(const struct btrfs_fs_info *fs_info); void btrfs_return_free_space(struct btrfs_space_info *space_info, u64 len); +static inline const char *btrfs_space_info_type_str(const struct btrfs_space_info *space_info) +{ + switch (space_info->flags) { + case BTRFS_BLOCK_GROUP_SYSTEM: + return "SYSTEM"; + case BTRFS_BLOCK_GROUP_METADATA | BTRFS_BLOCK_GROUP_DATA: + return "DATA+METADATA"; + case BTRFS_BLOCK_GROUP_DATA: + return "DATA"; + case BTRFS_BLOCK_GROUP_METADATA: + return "METADATA"; + default: + return "UNKNOWN"; + } +} + #endif /* BTRFS_SPACE_INFO_H */ diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index af56fdbba65d..d64d303b6edc 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -2483,6 +2483,18 @@ static void btrfs_shutdown(struct super_block *sb) } #endif +static int btrfs_show_stats(struct seq_file *seq, struct dentry *root) +{ + struct btrfs_fs_info *fs_info = btrfs_sb(root->d_sb); + + if (btrfs_is_zoned(fs_info)) { + btrfs_show_zoned_stats(fs_info, seq); + return 0; + } + + return 0; +} + static const struct super_operations btrfs_super_ops = { .drop_inode = btrfs_drop_inode, .evict_inode = btrfs_evict_inode, @@ -2498,6 +2510,7 @@ static const struct super_operations btrfs_super_ops = { .unfreeze_fs = btrfs_unfreeze, .nr_cached_objects = btrfs_nr_cached_objects, .free_cached_objects = btrfs_free_cached_objects, + .show_stats = btrfs_show_stats, #ifdef CONFIG_BTRFS_EXPERIMENTAL .remove_bdev = btrfs_remove_bdev, .shutdown = btrfs_shutdown, @@ -2700,7 +2713,3 @@ module_exit(exit_btrfs_fs) MODULE_DESCRIPTION("B-Tree File System (BTRFS)"); MODULE_LICENSE("GPL"); -MODULE_SOFTDEP("pre: crc32c"); -MODULE_SOFTDEP("pre: xxhash64"); -MODULE_SOFTDEP("pre: sha256"); -MODULE_SOFTDEP("pre: blake2b-256"); diff --git a/fs/btrfs/sysfs.c b/fs/btrfs/sysfs.c index 4b3c2acac51a..27bfb7b55ec4 100644 --- a/fs/btrfs/sysfs.c +++ b/fs/btrfs/sysfs.c @@ -11,7 +11,6 @@ #include <linux/bug.h> #include <linux/list.h> #include <linux/string_choices.h> -#include <crypto/hash.h> #include "messages.h" #include "ctree.h" #include "discard.h" @@ -300,6 +299,8 @@ BTRFS_FEAT_ATTR_INCOMPAT(zoned, ZONED); BTRFS_FEAT_ATTR_INCOMPAT(extent_tree_v2, EXTENT_TREE_V2); /* Remove once support for raid stripe tree is feature complete. */ BTRFS_FEAT_ATTR_INCOMPAT(raid_stripe_tree, RAID_STRIPE_TREE); +/* Remove once support for remap tree is feature complete. */ +BTRFS_FEAT_ATTR_INCOMPAT(remap_tree, REMAP_TREE); #endif #ifdef CONFIG_FS_VERITY BTRFS_FEAT_ATTR_COMPAT_RO(verity, VERITY); @@ -332,6 +333,7 @@ static struct attribute *btrfs_supported_feature_attrs[] = { #ifdef CONFIG_BTRFS_EXPERIMENTAL BTRFS_FEAT_ATTR_PTR(extent_tree_v2), BTRFS_FEAT_ATTR_PTR(raid_stripe_tree), + BTRFS_FEAT_ATTR_PTR(remap_tree), #endif #ifdef CONFIG_FS_VERITY BTRFS_FEAT_ATTR_PTR(verity), @@ -1253,10 +1255,9 @@ static ssize_t btrfs_checksum_show(struct kobject *kobj, { struct btrfs_fs_info *fs_info = to_fs_info(kobj); u16 csum_type = btrfs_super_csum_type(fs_info->super_copy); + const char *csum_name = btrfs_super_csum_name(csum_type); - return sysfs_emit(buf, "%s (%s)\n", - btrfs_super_csum_name(csum_type), - crypto_shash_driver_name(fs_info->csum_shash)); + return sysfs_emit(buf, "%s (%s-lib)\n", csum_name, csum_name); } BTRFS_ATTR(, checksum, btrfs_checksum_show); @@ -1540,47 +1541,6 @@ static ssize_t btrfs_bg_reclaim_threshold_store(struct kobject *kobj, BTRFS_ATTR_RW(, bg_reclaim_threshold, btrfs_bg_reclaim_threshold_show, btrfs_bg_reclaim_threshold_store); -#ifdef CONFIG_BTRFS_EXPERIMENTAL -static ssize_t btrfs_offload_csum_show(struct kobject *kobj, - struct kobj_attribute *a, char *buf) -{ - struct btrfs_fs_devices *fs_devices = to_fs_devs(kobj); - - switch (READ_ONCE(fs_devices->offload_csum_mode)) { - case BTRFS_OFFLOAD_CSUM_AUTO: - return sysfs_emit(buf, "auto\n"); - case BTRFS_OFFLOAD_CSUM_FORCE_ON: - return sysfs_emit(buf, "1\n"); - case BTRFS_OFFLOAD_CSUM_FORCE_OFF: - return sysfs_emit(buf, "0\n"); - default: - WARN_ON(1); - return -EINVAL; - } -} - -static ssize_t btrfs_offload_csum_store(struct kobject *kobj, - struct kobj_attribute *a, const char *buf, - size_t len) -{ - struct btrfs_fs_devices *fs_devices = to_fs_devs(kobj); - int ret; - bool val; - - ret = kstrtobool(buf, &val); - if (ret == 0) - WRITE_ONCE(fs_devices->offload_csum_mode, - val ? BTRFS_OFFLOAD_CSUM_FORCE_ON : BTRFS_OFFLOAD_CSUM_FORCE_OFF); - else if (ret == -EINVAL && sysfs_streq(buf, "auto")) - WRITE_ONCE(fs_devices->offload_csum_mode, BTRFS_OFFLOAD_CSUM_AUTO); - else - return -EINVAL; - - return len; -} -BTRFS_ATTR_RW(, offload_csum, btrfs_offload_csum_show, btrfs_offload_csum_store); -#endif - /* * Per-filesystem information and stats. * @@ -1600,9 +1560,6 @@ static const struct attribute *btrfs_attrs[] = { BTRFS_ATTR_PTR(, bg_reclaim_threshold), BTRFS_ATTR_PTR(, commit_stats), BTRFS_ATTR_PTR(, temp_fsid), -#ifdef CONFIG_BTRFS_EXPERIMENTAL - BTRFS_ATTR_PTR(, offload_csum), -#endif NULL, }; @@ -1972,6 +1929,8 @@ static const char *alloc_name(struct btrfs_space_info *space_info) case BTRFS_BLOCK_GROUP_SYSTEM: ASSERT(space_info->subgroup_id == BTRFS_SUB_GROUP_PRIMARY); return "system"; + case BTRFS_BLOCK_GROUP_METADATA_REMAP: + return "metadata-remap"; default: WARN_ON(1); return "invalid-combination"; diff --git a/fs/btrfs/tests/btrfs-tests.c b/fs/btrfs/tests/btrfs-tests.c index b576897d71cc..7f13c05d3736 100644 --- a/fs/btrfs/tests/btrfs-tests.c +++ b/fs/btrfs/tests/btrfs-tests.c @@ -301,6 +301,9 @@ int btrfs_run_sanity_tests(void) ret = btrfs_test_delayed_refs(sectorsize, nodesize); if (ret) goto out; + ret = btrfs_test_chunk_allocation(sectorsize, nodesize); + if (ret) + goto out; } } ret = btrfs_test_extent_map(); diff --git a/fs/btrfs/tests/btrfs-tests.h b/fs/btrfs/tests/btrfs-tests.h index 4307bdaa6749..b03d85a6e5ef 100644 --- a/fs/btrfs/tests/btrfs-tests.h +++ b/fs/btrfs/tests/btrfs-tests.h @@ -7,8 +7,10 @@ #define BTRFS_TESTS_H #include <linux/types.h> +#include <linux/cleanup.h> #ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS + int btrfs_run_sanity_tests(void); #define test_msg(fmt, ...) pr_info("BTRFS: selftest: " fmt "\n", ##__VA_ARGS__) @@ -45,13 +47,18 @@ int btrfs_test_free_space_tree(u32 sectorsize, u32 nodesize); int btrfs_test_raid_stripe_tree(u32 sectorsize, u32 nodesize); int btrfs_test_extent_map(void); int btrfs_test_delayed_refs(u32 sectorsize, u32 nodesize); +int btrfs_test_chunk_allocation(u32 sectorsize, u32 nodesize); struct inode *btrfs_new_test_inode(void); struct btrfs_fs_info *btrfs_alloc_dummy_fs_info(u32 nodesize, u32 sectorsize); void btrfs_free_dummy_fs_info(struct btrfs_fs_info *fs_info); +DEFINE_FREE(btrfs_free_dummy_fs_info, struct btrfs_fs_info *, + btrfs_free_dummy_fs_info(_T)) void btrfs_free_dummy_root(struct btrfs_root *root); struct btrfs_block_group * btrfs_alloc_dummy_block_group(struct btrfs_fs_info *fs_info, unsigned long length); void btrfs_free_dummy_block_group(struct btrfs_block_group *cache); +DEFINE_FREE(btrfs_free_dummy_block_group, struct btrfs_block_group *, + btrfs_free_dummy_block_group(_T)); void btrfs_init_dummy_trans(struct btrfs_trans_handle *trans, struct btrfs_fs_info *fs_info); void btrfs_init_dummy_transaction(struct btrfs_transaction *trans, struct btrfs_fs_info *fs_info); diff --git a/fs/btrfs/tests/chunk-allocation-tests.c b/fs/btrfs/tests/chunk-allocation-tests.c new file mode 100644 index 000000000000..9beb0602fc8c --- /dev/null +++ b/fs/btrfs/tests/chunk-allocation-tests.c @@ -0,0 +1,476 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2026 Meta. All rights reserved. + */ + +#include <linux/sizes.h> +#include "btrfs-tests.h" +#include "../volumes.h" +#include "../disk-io.h" +#include "../extent-io-tree.h" + +/* + * Tests for chunk allocator pending extent internals. + * These two functions form the core of searching the chunk allocation pending + * extent bitmap and have relatively easily definable semantics, so unit + * testing them can help ensure the correctness of chunk allocation. + */ + +/* + * Describes the inputs to the system and expected results + * when testing btrfs_find_hole_in_pending_extents(). + */ +struct pending_extent_test_case { + const char *name; + /* Input range to search. */ + u64 hole_start; + u64 hole_len; + /* The size of hole we are searching for. */ + u64 min_hole_size; + /* + * Pending extents to set up (up to 2 for up to 3 holes) + * If len == 0, then it is skipped. + */ + struct { + u64 start; + u64 len; + } pending_extents[2]; + /* Expected outputs. */ + bool expected_found; + u64 expected_start; + u64 expected_len; +}; + +static const struct pending_extent_test_case find_hole_tests[] = { + { + .name = "no pending extents", + .hole_start = 0, + .hole_len = 10ULL * SZ_1G, + .min_hole_size = SZ_1G, + .pending_extents = { }, + .expected_found = true, + .expected_start = 0, + .expected_len = 10ULL * SZ_1G, + }, + { + .name = "pending extent at start of range", + .hole_start = 0, + .hole_len = 10ULL * SZ_1G, + .min_hole_size = SZ_1G, + .pending_extents = { + { .start = 0, .len = SZ_1G }, + }, + .expected_found = true, + .expected_start = SZ_1G, + .expected_len = 9ULL * SZ_1G, + }, + { + .name = "pending extent overlapping start of range", + .hole_start = SZ_1G, + .hole_len = 9ULL * SZ_1G, + .min_hole_size = SZ_1G, + .pending_extents = { + { .start = 0, .len = SZ_2G }, + }, + .expected_found = true, + .expected_start = SZ_2G, + .expected_len = 8ULL * SZ_1G, + }, + { + .name = "two holes; first hole is exactly big enough", + .hole_start = 0, + .hole_len = 10ULL * SZ_1G, + .min_hole_size = SZ_1G, + .pending_extents = { + { .start = SZ_1G, .len = SZ_1G }, + }, + .expected_found = true, + .expected_start = 0, + .expected_len = SZ_1G, + }, + { + .name = "two holes; first hole is big enough", + .hole_start = 0, + .hole_len = 10ULL * SZ_1G, + .min_hole_size = SZ_1G, + .pending_extents = { + { .start = SZ_2G, .len = SZ_1G }, + }, + .expected_found = true, + .expected_start = 0, + .expected_len = SZ_2G, + }, + { + .name = "two holes; second hole is big enough", + .hole_start = 0, + .hole_len = 10ULL * SZ_1G, + .min_hole_size = SZ_2G, + .pending_extents = { + { .start = SZ_1G, .len = SZ_1G }, + }, + .expected_found = true, + .expected_start = SZ_2G, + .expected_len = 8ULL * SZ_1G, + }, + { + .name = "three holes; first hole big enough", + .hole_start = 0, + .hole_len = 10ULL * SZ_1G, + .min_hole_size = SZ_2G, + .pending_extents = { + { .start = SZ_2G, .len = SZ_1G }, + { .start = 4ULL * SZ_1G, .len = SZ_1G }, + }, + .expected_found = true, + .expected_start = 0, + .expected_len = SZ_2G, + }, + { + .name = "three holes; second hole big enough", + .hole_start = 0, + .hole_len = 10ULL * SZ_1G, + .min_hole_size = SZ_2G, + .pending_extents = { + { .start = SZ_1G, .len = SZ_1G }, + { .start = 5ULL * SZ_1G, .len = SZ_1G }, + }, + .expected_found = true, + .expected_start = SZ_2G, + .expected_len = 3ULL * SZ_1G, + }, + { + .name = "three holes; third hole big enough", + .hole_start = 0, + .hole_len = 10ULL * SZ_1G, + .min_hole_size = SZ_2G, + .pending_extents = { + { .start = SZ_1G, .len = SZ_1G }, + { .start = 3ULL * SZ_1G, .len = 5ULL * SZ_1G }, + }, + .expected_found = true, + .expected_start = 8ULL * SZ_1G, + .expected_len = SZ_2G, + }, + { + .name = "three holes; all holes too small", + .hole_start = 0, + .hole_len = 10ULL * SZ_1G, + .min_hole_size = SZ_2G, + .pending_extents = { + { .start = SZ_1G, .len = SZ_1G }, + { .start = 3ULL * SZ_1G, .len = 6ULL * SZ_1G }, + }, + .expected_found = false, + .expected_start = 0, + .expected_len = SZ_1G, + }, + { + .name = "three holes; all holes too small; first biggest", + .hole_start = 0, + .hole_len = 10ULL * SZ_1G, + .min_hole_size = 3ULL * SZ_1G, + .pending_extents = { + { .start = SZ_2G, .len = SZ_1G }, + { .start = 4ULL * SZ_1G, .len = 5ULL * SZ_1G }, + }, + .expected_found = false, + .expected_start = 0, + .expected_len = SZ_2G, + }, + { + .name = "three holes; all holes too small; second biggest", + .hole_start = 0, + .hole_len = 10ULL * SZ_1G, + .min_hole_size = 3ULL * SZ_1G, + .pending_extents = { + { .start = SZ_1G, .len = SZ_1G }, + { .start = 4ULL * SZ_1G, .len = 5ULL * SZ_1G }, + }, + .expected_found = false, + .expected_start = SZ_2G, + .expected_len = SZ_2G, + }, + { + .name = "three holes; all holes too small; third biggest", + .hole_start = 0, + .hole_len = 10ULL * SZ_1G, + .min_hole_size = 3ULL * SZ_1G, + .pending_extents = { + { .start = SZ_1G, .len = SZ_1G }, + { .start = 3ULL * SZ_1G, .len = 5ULL * SZ_1G }, + }, + .expected_found = false, + .expected_start = 8ULL * SZ_1G, + .expected_len = SZ_2G, + }, + { + .name = "hole entirely allocated by pending", + .hole_start = 0, + .hole_len = 10ULL * SZ_1G, + .min_hole_size = SZ_1G, + .pending_extents = { + { .start = 0, .len = 10ULL * SZ_1G }, + }, + .expected_found = false, + .expected_start = 10ULL * SZ_1G, + .expected_len = 0, + }, + { + .name = "pending extent at end of range", + .hole_start = 0, + .hole_len = 10ULL * SZ_1G, + .min_hole_size = SZ_1G, + .pending_extents = { + { .start = 9ULL * SZ_1G, .len = SZ_2G }, + }, + .expected_found = true, + .expected_start = 0, + .expected_len = 9ULL * SZ_1G, + }, + { + .name = "zero length input", + .hole_start = SZ_1G, + .hole_len = 0, + .min_hole_size = SZ_1G, + .pending_extents = { }, + .expected_found = false, + .expected_start = SZ_1G, + .expected_len = 0, + }, +}; + +static int test_find_hole_in_pending(u32 sectorsize, u32 nodesize) +{ + struct btrfs_fs_info *fs_info; + struct btrfs_device *device; + int ret = 0; + + test_msg("running find_hole_in_pending_extents tests"); + + fs_info = btrfs_alloc_dummy_fs_info(nodesize, sectorsize); + if (!fs_info) { + test_std_err(TEST_ALLOC_FS_INFO); + return -ENOMEM; + } + + device = btrfs_alloc_dummy_device(fs_info); + if (IS_ERR(device)) { + test_err("failed to allocate dummy device"); + ret = PTR_ERR(device); + goto out_free_fs_info; + } + device->fs_info = fs_info; + + for (int i = 0; i < ARRAY_SIZE(find_hole_tests); i++) { + const struct pending_extent_test_case *test_case = &find_hole_tests[i]; + u64 hole_start = test_case->hole_start; + u64 hole_len = test_case->hole_len; + bool found; + + for (int j = 0; j < ARRAY_SIZE(test_case->pending_extents); j++) { + u64 start = test_case->pending_extents[j].start; + u64 len = test_case->pending_extents[j].len; + + if (!len) + continue; + btrfs_set_extent_bit(&device->alloc_state, + start, start + len - 1, + CHUNK_ALLOCATED, NULL); + } + + mutex_lock(&fs_info->chunk_mutex); + found = btrfs_find_hole_in_pending_extents(device, &hole_start, &hole_len, + test_case->min_hole_size); + mutex_unlock(&fs_info->chunk_mutex); + + if (found != test_case->expected_found) { + test_err("%s: expected found=%d, got found=%d", + test_case->name, test_case->expected_found, found); + ret = -EINVAL; + goto out_clear_pending_extents; + } + if (hole_start != test_case->expected_start || + hole_len != test_case->expected_len) { + test_err("%s: expected [%llu, %llu), got [%llu, %llu)", + test_case->name, test_case->expected_start, + test_case->expected_start + + test_case->expected_len, + hole_start, hole_start + hole_len); + ret = -EINVAL; + goto out_clear_pending_extents; + } +out_clear_pending_extents: + btrfs_clear_extent_bit(&device->alloc_state, 0, (u64)-1, + CHUNK_ALLOCATED, NULL); + if (ret) + break; + } + +out_free_fs_info: + btrfs_free_dummy_fs_info(fs_info); + return ret; +} + +/* + * Describes the inputs to the system and expected results + * when testing btrfs_first_pending_extent(). + */ +struct first_pending_test_case { + const char *name; + /* The range to look for a pending extent in. */ + u64 hole_start; + u64 hole_len; + /* The pending extent to look for. */ + struct { + u64 start; + u64 len; + } pending_extent; + /* Expected outputs. */ + bool expected_found; + u64 expected_pending_start; + u64 expected_pending_end; +}; + +static const struct first_pending_test_case first_pending_tests[] = { + { + .name = "no pending extent", + .hole_start = 0, + .hole_len = 10ULL * SZ_1G, + .pending_extent = { 0, 0 }, + .expected_found = false, + }, + { + .name = "pending extent at search start", + .hole_start = SZ_1G, + .hole_len = 9ULL * SZ_1G, + .pending_extent = { SZ_1G, SZ_1G }, + .expected_found = true, + .expected_pending_start = SZ_1G, + .expected_pending_end = SZ_2G - 1, + }, + { + .name = "pending extent overlapping search start", + .hole_start = SZ_1G, + .hole_len = 9ULL * SZ_1G, + .pending_extent = { 0, SZ_2G }, + .expected_found = true, + .expected_pending_start = 0, + .expected_pending_end = SZ_2G - 1, + }, + { + .name = "pending extent inside search range", + .hole_start = 0, + .hole_len = 10ULL * SZ_1G, + .pending_extent = { SZ_2G, SZ_1G }, + .expected_found = true, + .expected_pending_start = SZ_2G, + .expected_pending_end = 3ULL * SZ_1G - 1, + }, + { + .name = "pending extent outside search range", + .hole_start = 0, + .hole_len = SZ_1G, + .pending_extent = { SZ_2G, SZ_1G }, + .expected_found = false, + }, + { + .name = "pending extent overlapping end of search range", + .hole_start = 0, + .hole_len = SZ_2G, + .pending_extent = { SZ_1G, SZ_2G }, + .expected_found = true, + .expected_pending_start = SZ_1G, + .expected_pending_end = 3ULL * SZ_1G - 1, + }, +}; + +static int test_first_pending_extent(u32 sectorsize, u32 nodesize) +{ + struct btrfs_fs_info *fs_info; + struct btrfs_device *device; + int ret = 0; + + test_msg("running first_pending_extent tests"); + + fs_info = btrfs_alloc_dummy_fs_info(nodesize, sectorsize); + if (!fs_info) { + test_std_err(TEST_ALLOC_FS_INFO); + return -ENOMEM; + } + + device = btrfs_alloc_dummy_device(fs_info); + if (IS_ERR(device)) { + test_err("failed to allocate dummy device"); + ret = PTR_ERR(device); + goto out_free_fs_info; + } + + device->fs_info = fs_info; + + for (int i = 0; i < ARRAY_SIZE(first_pending_tests); i++) { + const struct first_pending_test_case *test_case = &first_pending_tests[i]; + u64 start = test_case->pending_extent.start; + u64 len = test_case->pending_extent.len; + u64 pending_start, pending_end; + bool found; + + if (len) { + btrfs_set_extent_bit(&device->alloc_state, + start, start + len - 1, + CHUNK_ALLOCATED, NULL); + } + + mutex_lock(&fs_info->chunk_mutex); + found = btrfs_first_pending_extent(device, test_case->hole_start, + test_case->hole_len, + &pending_start, &pending_end); + mutex_unlock(&fs_info->chunk_mutex); + + if (found != test_case->expected_found) { + test_err("%s: expected found=%d, got found=%d", + test_case->name, test_case->expected_found, found); + ret = -EINVAL; + goto out_clear_pending_extents; + } + if (!found) + goto out_clear_pending_extents; + + if (pending_start != test_case->expected_pending_start || + pending_end != test_case->expected_pending_end) { + test_err("%s: expected pending [%llu, %llu], got [%llu, %llu]", + test_case->name, + test_case->expected_pending_start, + test_case->expected_pending_end, + pending_start, pending_end); + ret = -EINVAL; + goto out_clear_pending_extents; + } + +out_clear_pending_extents: + btrfs_clear_extent_bit(&device->alloc_state, 0, (u64)-1, + CHUNK_ALLOCATED, NULL); + if (ret) + break; + } + +out_free_fs_info: + btrfs_free_dummy_fs_info(fs_info); + return ret; +} + +int btrfs_test_chunk_allocation(u32 sectorsize, u32 nodesize) +{ + int ret; + + test_msg("running chunk allocation tests"); + + ret = test_first_pending_extent(sectorsize, nodesize); + if (ret) + return ret; + + ret = test_find_hole_in_pending(sectorsize, nodesize); + if (ret) + return ret; + + return 0; +} diff --git a/fs/btrfs/tests/extent-map-tests.c b/fs/btrfs/tests/extent-map-tests.c index aabf825e8d7b..811f36d41101 100644 --- a/fs/btrfs/tests/extent-map-tests.c +++ b/fs/btrfs/tests/extent-map-tests.c @@ -173,9 +173,12 @@ static int test_case_2(struct btrfs_fs_info *fs_info, struct btrfs_inode *inode) return -ENOMEM; } - /* Add [0, 1K) */ + /* + * Add [0, 1K) which is inlined. And the extent map length must + * be one block. + */ em->start = 0; - em->len = SZ_1K; + em->len = SZ_4K; em->disk_bytenr = EXTENT_MAP_INLINE; em->disk_num_bytes = 0; em->ram_bytes = SZ_1K; @@ -219,7 +222,7 @@ static int test_case_2(struct btrfs_fs_info *fs_info, struct btrfs_inode *inode) /* Add [0, 1K) */ em->start = 0; - em->len = SZ_1K; + em->len = SZ_4K; em->disk_bytenr = EXTENT_MAP_INLINE; em->disk_num_bytes = 0; em->ram_bytes = SZ_1K; @@ -235,7 +238,7 @@ static int test_case_2(struct btrfs_fs_info *fs_info, struct btrfs_inode *inode) ret = -ENOENT; goto out; } - if (em->start != 0 || btrfs_extent_map_end(em) != SZ_1K || + if (em->start != 0 || btrfs_extent_map_end(em) != SZ_4K || em->disk_bytenr != EXTENT_MAP_INLINE) { test_err( "case2 [0 1K]: ret %d return a wrong em (start %llu len %llu disk_bytenr %llu", @@ -1131,8 +1134,11 @@ int btrfs_test_extent_map(void) /* * Note: the fs_info is not set up completely, we only need * fs_info::fsid for the tracepoint. + * + * And all the immediate numbers are based on 4K blocksize, + * thus we have to use 4K as sectorsize no matter the page size. */ - fs_info = btrfs_alloc_dummy_fs_info(PAGE_SIZE, PAGE_SIZE); + fs_info = btrfs_alloc_dummy_fs_info(SZ_4K, SZ_4K); if (!fs_info) { test_std_err(TEST_ALLOC_FS_INFO); return -ENOMEM; diff --git a/fs/btrfs/tests/free-space-tree-tests.c b/fs/btrfs/tests/free-space-tree-tests.c index c8822edd32e2..8dee057f41fd 100644 --- a/fs/btrfs/tests/free-space-tree-tests.c +++ b/fs/btrfs/tests/free-space-tree-tests.c @@ -49,7 +49,7 @@ static int __check_free_space_extents(struct btrfs_trans_handle *trans, if (flags & BTRFS_FREE_SPACE_USING_BITMAPS) { if (path->slots[0] != 0) goto invalid; - end = cache->start + cache->length; + end = btrfs_block_group_end(cache); i = 0; while (++path->slots[0] < btrfs_header_nritems(path->nodes[0])) { btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]); @@ -216,7 +216,7 @@ static int test_remove_end(struct btrfs_trans_handle *trans, int ret; ret = __btrfs_remove_from_free_space_tree(trans, cache, path, - cache->start + cache->length - alignment, + btrfs_block_group_end(cache) - alignment, alignment); if (ret) { test_err("could not remove free space"); diff --git a/fs/btrfs/tests/inode-tests.c b/fs/btrfs/tests/inode-tests.c index a4c2b7748b95..b04fbcaf0a1d 100644 --- a/fs/btrfs/tests/inode-tests.c +++ b/fs/btrfs/tests/inode-tests.c @@ -81,17 +81,20 @@ static void insert_inode_item_key(struct btrfs_root *root) * diagram of how the extents will look though this may not be possible we still * want to make sure everything acts normally (the last number is not inclusive) * - * [0 - 6][ 6 - 4096 ][ 4096 - 4100][4100 - 8195][8195 - 12291] - * [inline][hole but no extent][ hole ][ regular ][regular1 split] + * The numbers are using 4K fs block size as an example, the real test will scale + * all the extent maps (except the inlined one) according to the block size. * - * [12291 - 16387][16387 - 24579][24579 - 28675][ 28675 - 32771][32771 - 36867 ] - * [ hole ][regular1 split][ prealloc ][ prealloc1 ][prealloc1 written] + * [ 0 - 6 ][ 6 - 4K ][ 4K - 8K ][ 8K - 12K ] + * [ inline ][ implied hole ][ regular ][ regular1 split ] * - * [36867 - 45059][45059 - 53251][53251 - 57347][57347 - 61443][61443- 69635] - * [ prealloc1 ][ compressed ][ compressed1 ][ regular ][ compressed1] + * [ 12K - 16K ][ 16K - 24K ][ 24K - 28K ][ 28K - 32K ][ 32K - 36K ] + * [ hole ][ regular1 split ][ prealloc ][ prealloc1 ][ prealloc1 written ] * - * [69635-73731][ 73731 - 86019 ][86019-90115] - * [ regular ][ hole but no extent][ regular ] + * [ 36K - 44K ][ 44K - 52K ][ 52K - 56K ][ 56K - 60K ][ 60K - 68 K ] + * [ prealloc1 ][ compressed ][ compressed1 ][ regular ][ compressed1 ] + * + * [ 68K - 72K ][ 72K - 84K ][ 84K - 88K ] + * [ regular ][ hole but no extent ][ regular ] */ static void setup_file_extents(struct btrfs_root *root, u32 sectorsize) { @@ -100,6 +103,8 @@ static void setup_file_extents(struct btrfs_root *root, u32 sectorsize) u64 offset = 0; /* + * Start 0, length 6, inlined. + * * Tree-checker has strict limits on inline extents that they can only * exist at file offset 0, thus we can only have one inline file extent * at most. @@ -109,20 +114,18 @@ static void setup_file_extents(struct btrfs_root *root, u32 sectorsize) slot++; offset = sectorsize; - /* Now another hole */ - insert_extent(root, offset, 4, 4, 0, 0, 0, BTRFS_FILE_EXTENT_REG, 0, - slot); + /* Start 1 * blocksize, length 1 * blocksize, regular. */ + insert_extent(root, offset, sectorsize, sectorsize, 0, + disk_bytenr, sectorsize, BTRFS_FILE_EXTENT_REG, 0, slot); slot++; - offset += 4; - /* Now for a regular extent */ - insert_extent(root, offset, sectorsize - 1, sectorsize - 1, 0, - disk_bytenr, sectorsize - 1, BTRFS_FILE_EXTENT_REG, 0, slot); - slot++; - disk_bytenr += sectorsize; - offset += sectorsize - 1; + /* We don't want the regular em merged with the next one. */ + disk_bytenr += 2 * sectorsize; + offset += sectorsize; /* + * Start 2 * blocksize, length 1 * blocksize, regular. + * * Now for 3 extents that were split from a hole punch so we test * offsets properly. */ @@ -130,10 +133,14 @@ static void setup_file_extents(struct btrfs_root *root, u32 sectorsize) 4 * sectorsize, BTRFS_FILE_EXTENT_REG, 0, slot); slot++; offset += sectorsize; + + /* Start 3 * blocksize, length 1 * blocksize, regular, explicit hole. */ insert_extent(root, offset, sectorsize, sectorsize, 0, 0, 0, BTRFS_FILE_EXTENT_REG, 0, slot); slot++; offset += sectorsize; + + /* Start 4 * blocksize, length 2 * blocksize, regular. */ insert_extent(root, offset, 2 * sectorsize, 4 * sectorsize, 2 * sectorsize, disk_bytenr, 4 * sectorsize, BTRFS_FILE_EXTENT_REG, 0, slot); @@ -141,7 +148,7 @@ static void setup_file_extents(struct btrfs_root *root, u32 sectorsize) offset += 2 * sectorsize; disk_bytenr += 4 * sectorsize; - /* Now for a unwritten prealloc extent */ + /* Start 6 * blocksize, length 1 * blocksize, preallocated. */ insert_extent(root, offset, sectorsize, sectorsize, 0, disk_bytenr, sectorsize, BTRFS_FILE_EXTENT_PREALLOC, 0, slot); slot++; @@ -154,6 +161,8 @@ static void setup_file_extents(struct btrfs_root *root, u32 sectorsize) disk_bytenr += 2 * sectorsize; /* + * Start 7 * blocksize, length 1 * blocksize, prealloc. + * * Now for a partially written prealloc extent, basically the same as * the hole punch example above. Ram_bytes never changes when you mark * extents written btw. @@ -162,11 +171,15 @@ static void setup_file_extents(struct btrfs_root *root, u32 sectorsize) 4 * sectorsize, BTRFS_FILE_EXTENT_PREALLOC, 0, slot); slot++; offset += sectorsize; + + /* Start 8 * blocksize, length 1 * blocksize, regular. */ insert_extent(root, offset, sectorsize, 4 * sectorsize, sectorsize, disk_bytenr, 4 * sectorsize, BTRFS_FILE_EXTENT_REG, 0, slot); slot++; offset += sectorsize; + + /* Start 9 * blocksize, length 2 * blocksize, prealloc. */ insert_extent(root, offset, 2 * sectorsize, 4 * sectorsize, 2 * sectorsize, disk_bytenr, 4 * sectorsize, BTRFS_FILE_EXTENT_PREALLOC, 0, slot); @@ -174,7 +187,7 @@ static void setup_file_extents(struct btrfs_root *root, u32 sectorsize) offset += 2 * sectorsize; disk_bytenr += 4 * sectorsize; - /* Now a normal compressed extent */ + /* Start 11 * blocksize, length 2 * blocksize, regular. */ insert_extent(root, offset, 2 * sectorsize, 2 * sectorsize, 0, disk_bytenr, sectorsize, BTRFS_FILE_EXTENT_REG, BTRFS_COMPRESS_ZLIB, slot); @@ -183,17 +196,21 @@ static void setup_file_extents(struct btrfs_root *root, u32 sectorsize) /* No merges */ disk_bytenr += 2 * sectorsize; - /* Now a split compressed extent */ + /* Start 13 * blocksize, length 1 * blocksize, regular. */ insert_extent(root, offset, sectorsize, 4 * sectorsize, 0, disk_bytenr, sectorsize, BTRFS_FILE_EXTENT_REG, BTRFS_COMPRESS_ZLIB, slot); slot++; offset += sectorsize; + + /* Start 14 * blocksize, length 1 * blocksize, regular. */ insert_extent(root, offset, sectorsize, sectorsize, 0, disk_bytenr + sectorsize, sectorsize, BTRFS_FILE_EXTENT_REG, 0, slot); slot++; offset += sectorsize; + + /* Start 15 * blocksize, length 2 * blocksize, regular. */ insert_extent(root, offset, 2 * sectorsize, 4 * sectorsize, 2 * sectorsize, disk_bytenr, sectorsize, BTRFS_FILE_EXTENT_REG, BTRFS_COMPRESS_ZLIB, slot); @@ -201,12 +218,19 @@ static void setup_file_extents(struct btrfs_root *root, u32 sectorsize) offset += 2 * sectorsize; disk_bytenr += 2 * sectorsize; - /* Now extents that have a hole but no hole extent */ + /* Start 17 * blocksize, length 1 * blocksize, regular. */ insert_extent(root, offset, sectorsize, sectorsize, 0, disk_bytenr, sectorsize, BTRFS_FILE_EXTENT_REG, 0, slot); slot++; offset += 4 * sectorsize; disk_bytenr += sectorsize; + + /* + * Start 18 * blocksize, length 3 * blocksize, implied hole (aka no + * file extent item). + * + * Start 21 * blocksize, length 1 * blocksize, regular. + */ insert_extent(root, offset, sectorsize, sectorsize, 0, disk_bytenr, sectorsize, BTRFS_FILE_EXTENT_REG, 0, slot); } @@ -313,29 +337,7 @@ static noinline int test_btrfs_get_extent(u32 sectorsize, u32 nodesize) * unless we have a page for it to write into. Maybe we should change * this? */ - offset = em->start + em->len; - btrfs_free_extent_map(em); - - em = btrfs_get_extent(BTRFS_I(inode), NULL, offset, sectorsize); - if (IS_ERR(em)) { - test_err("got an error when we shouldn't have"); - goto out; - } - if (em->disk_bytenr != EXTENT_MAP_HOLE) { - test_err("expected a hole, got %llu", em->disk_bytenr); - goto out; - } - if (em->start != offset || em->len != 4) { - test_err( - "unexpected extent wanted start %llu len 4, got start %llu len %llu", - offset, em->start, em->len); - goto out; - } - if (em->flags != 0) { - test_err("unexpected flags set, want 0 have %u", em->flags); - goto out; - } - offset = em->start + em->len; + offset = btrfs_extent_map_end(em); btrfs_free_extent_map(em); /* Regular extent */ @@ -348,10 +350,10 @@ static noinline int test_btrfs_get_extent(u32 sectorsize, u32 nodesize) test_err("expected a real extent, got %llu", em->disk_bytenr); goto out; } - if (em->start != offset || em->len != sectorsize - 1) { + if (em->start != offset || em->len != sectorsize) { test_err( - "unexpected extent wanted start %llu len 4095, got start %llu len %llu", - offset, em->start, em->len); + "unexpected extent wanted start %llu len %u, got start %llu len %llu", + offset, sectorsize, em->start, em->len); goto out; } if (em->flags != 0) { @@ -362,7 +364,7 @@ static noinline int test_btrfs_get_extent(u32 sectorsize, u32 nodesize) test_err("wrong offset, want 0, have %llu", em->offset); goto out; } - offset = em->start + em->len; + offset = btrfs_extent_map_end(em); btrfs_free_extent_map(em); /* The next 3 are split extents */ @@ -391,7 +393,7 @@ static noinline int test_btrfs_get_extent(u32 sectorsize, u32 nodesize) } disk_bytenr = btrfs_extent_map_block_start(em); orig_start = em->start; - offset = em->start + em->len; + offset = btrfs_extent_map_end(em); btrfs_free_extent_map(em); em = btrfs_get_extent(BTRFS_I(inode), NULL, offset, sectorsize); @@ -413,7 +415,7 @@ static noinline int test_btrfs_get_extent(u32 sectorsize, u32 nodesize) test_err("unexpected flags set, want 0 have %u", em->flags); goto out; } - offset = em->start + em->len; + offset = btrfs_extent_map_end(em); btrfs_free_extent_map(em); em = btrfs_get_extent(BTRFS_I(inode), NULL, offset, sectorsize); @@ -446,7 +448,7 @@ static noinline int test_btrfs_get_extent(u32 sectorsize, u32 nodesize) disk_bytenr, btrfs_extent_map_block_start(em)); goto out; } - offset = em->start + em->len; + offset = btrfs_extent_map_end(em); btrfs_free_extent_map(em); /* Prealloc extent */ @@ -474,7 +476,7 @@ static noinline int test_btrfs_get_extent(u32 sectorsize, u32 nodesize) test_err("wrong offset, want 0, have %llu", em->offset); goto out; } - offset = em->start + em->len; + offset = btrfs_extent_map_end(em); btrfs_free_extent_map(em); /* The next 3 are a half written prealloc extent */ @@ -504,7 +506,7 @@ static noinline int test_btrfs_get_extent(u32 sectorsize, u32 nodesize) } disk_bytenr = btrfs_extent_map_block_start(em); orig_start = em->start; - offset = em->start + em->len; + offset = btrfs_extent_map_end(em); btrfs_free_extent_map(em); em = btrfs_get_extent(BTRFS_I(inode), NULL, offset, sectorsize); @@ -536,7 +538,7 @@ static noinline int test_btrfs_get_extent(u32 sectorsize, u32 nodesize) disk_bytenr + em->offset, btrfs_extent_map_block_start(em)); goto out; } - offset = em->start + em->len; + offset = btrfs_extent_map_end(em); btrfs_free_extent_map(em); em = btrfs_get_extent(BTRFS_I(inode), NULL, offset, sectorsize); @@ -569,7 +571,7 @@ static noinline int test_btrfs_get_extent(u32 sectorsize, u32 nodesize) disk_bytenr + em->offset, btrfs_extent_map_block_start(em)); goto out; } - offset = em->start + em->len; + offset = btrfs_extent_map_end(em); btrfs_free_extent_map(em); /* Now for the compressed extent */ @@ -602,7 +604,7 @@ static noinline int test_btrfs_get_extent(u32 sectorsize, u32 nodesize) BTRFS_COMPRESS_ZLIB, btrfs_extent_map_compression(em)); goto out; } - offset = em->start + em->len; + offset = btrfs_extent_map_end(em); btrfs_free_extent_map(em); /* Split compressed extent */ @@ -637,7 +639,7 @@ static noinline int test_btrfs_get_extent(u32 sectorsize, u32 nodesize) } disk_bytenr = btrfs_extent_map_block_start(em); orig_start = em->start; - offset = em->start + em->len; + offset = btrfs_extent_map_end(em); btrfs_free_extent_map(em); em = btrfs_get_extent(BTRFS_I(inode), NULL, offset, sectorsize); @@ -663,7 +665,7 @@ static noinline int test_btrfs_get_extent(u32 sectorsize, u32 nodesize) test_err("wrong offset, want 0, have %llu", em->offset); goto out; } - offset = em->start + em->len; + offset = btrfs_extent_map_end(em); btrfs_free_extent_map(em); em = btrfs_get_extent(BTRFS_I(inode), NULL, offset, sectorsize); @@ -697,7 +699,7 @@ static noinline int test_btrfs_get_extent(u32 sectorsize, u32 nodesize) BTRFS_COMPRESS_ZLIB, btrfs_extent_map_compression(em)); goto out; } - offset = em->start + em->len; + offset = btrfs_extent_map_end(em); btrfs_free_extent_map(em); /* A hole between regular extents but no hole extent */ @@ -724,7 +726,7 @@ static noinline int test_btrfs_get_extent(u32 sectorsize, u32 nodesize) test_err("wrong offset, want 0, have %llu", em->offset); goto out; } - offset = em->start + em->len; + offset = btrfs_extent_map_end(em); btrfs_free_extent_map(em); em = btrfs_get_extent(BTRFS_I(inode), NULL, offset, SZ_4M); @@ -756,7 +758,7 @@ static noinline int test_btrfs_get_extent(u32 sectorsize, u32 nodesize) test_err("wrong offset, want 0, have %llu", em->offset); goto out; } - offset = em->start + em->len; + offset = btrfs_extent_map_end(em); btrfs_free_extent_map(em); em = btrfs_get_extent(BTRFS_I(inode), NULL, offset, sectorsize); diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c index bd03f465e2d3..0b2498749b1e 100644 --- a/fs/btrfs/transaction.c +++ b/fs/btrfs/transaction.c @@ -950,7 +950,7 @@ int btrfs_wait_for_commit(struct btrfs_fs_info *fs_info, u64 transid) if (transid) { if (transid <= btrfs_get_last_trans_committed(fs_info)) - goto out; + return 0; /* find specified transaction */ spin_lock(&fs_info->trans_lock); @@ -975,7 +975,7 @@ int btrfs_wait_for_commit(struct btrfs_fs_info *fs_info, u64 transid) if (!cur_trans) { if (transid > btrfs_get_last_trans_committed(fs_info)) ret = -EINVAL; - goto out; + return ret; } } else { /* find newest transaction that is committing | committed */ @@ -991,14 +991,15 @@ int btrfs_wait_for_commit(struct btrfs_fs_info *fs_info, u64 transid) } } spin_unlock(&fs_info->trans_lock); + /* Nothing committing or committed. */ if (!cur_trans) - goto out; /* nothing committing|committed */ + return ret; } wait_for_commit(cur_trans, TRANS_STATE_COMPLETED); ret = cur_trans->aborted; btrfs_put_transaction(cur_trans); -out: + return ret; } @@ -1515,7 +1516,7 @@ static noinline int commit_fs_roots(struct btrfs_trans_handle *trans) btrfs_free_log(trans, root); ret2 = btrfs_update_reloc_root(trans, root); - if (ret2) + if (unlikely(ret2)) return ret2; /* see comments in should_cow_block() */ @@ -1532,7 +1533,7 @@ static noinline int commit_fs_roots(struct btrfs_trans_handle *trans) ret2 = btrfs_update_root(trans, fs_info->tree_root, &root->root_key, &root->root_item); - if (ret2) + if (unlikely(ret2)) return ret2; spin_lock(&fs_info->fs_roots_radix_lock); } @@ -1621,9 +1622,9 @@ static int qgroup_account_snapshot(struct btrfs_trans_handle *trans, goto out; switch_commit_roots(trans); ret = btrfs_write_and_wait_transaction(trans); - if (ret) - btrfs_handle_fs_error(fs_info, ret, - "Error while writing out transaction for qgroup"); + if (unlikely(ret)) + btrfs_err(fs_info, +"error while writing out transaction during qgroup snapshot accounting: %d", ret); out: /* @@ -1687,11 +1688,11 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans, &pending->dentry->d_name, 0, &fname); memalloc_nofs_restore(nofs_flags); - if (pending->error) + if (unlikely(pending->error)) goto free_pending; pending->error = btrfs_get_free_objectid(tree_root, &objectid); - if (pending->error) + if (unlikely(pending->error)) goto free_fname; /* @@ -1707,7 +1708,7 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans, &pending->block_rsv, to_reserve, BTRFS_RESERVE_NO_FLUSH); - if (pending->error) + if (unlikely(pending->error)) goto clear_skip_qgroup; } @@ -1719,7 +1720,7 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans, trans->bytes_reserved, 1); parent_root = parent_inode->root; ret = record_root_in_trans(trans, parent_root, 0); - if (ret) + if (unlikely(ret)) goto fail; cur_time = current_time(&parent_inode->vfs_inode); @@ -1736,7 +1737,7 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans, dir_item = btrfs_lookup_dir_item(NULL, parent_root, path, btrfs_ino(parent_inode), &fname.disk_name, 0); - if (dir_item != NULL && !IS_ERR(dir_item)) { + if (unlikely(dir_item != NULL && !IS_ERR(dir_item))) { pending->error = -EEXIST; goto dir_item_existed; } else if (IS_ERR(dir_item)) { @@ -1873,7 +1874,7 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans, else if (btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_SIMPLE) ret = btrfs_qgroup_inherit(trans, btrfs_root_id(root), objectid, btrfs_root_id(parent_root), pending->inherit); - if (ret < 0) + if (unlikely(ret < 0)) goto fail; ret = btrfs_insert_dir_item(trans, &fname.disk_name, @@ -1939,7 +1940,7 @@ static noinline int create_pending_snapshots(struct btrfs_trans_handle *trans) list_for_each_entry_safe(pending, next, head, list) { list_del(&pending->list); ret = create_pending_snapshot(trans, pending); - if (ret) + if (unlikely(ret)) break; } return ret; @@ -1967,6 +1968,13 @@ static void update_super_roots(struct btrfs_fs_info *fs_info) super->cache_generation = 0; if (test_bit(BTRFS_FS_UPDATE_UUID_TREE_GEN, &fs_info->flags)) super->uuid_tree_generation = root_item->generation; + + if (btrfs_fs_incompat(fs_info, REMAP_TREE)) { + root_item = &fs_info->remap_root->root_item; + super->remap_root = root_item->bytenr; + super->remap_root_generation = root_item->generation; + super->remap_root_level = root_item->level; + } } int btrfs_transaction_blocked(struct btrfs_fs_info *info) @@ -2258,7 +2266,7 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans) if (run_it) { ret = btrfs_start_dirty_block_groups(trans); - if (ret) + if (unlikely(ret)) goto lockdep_trans_commit_start_release; } } @@ -2308,7 +2316,7 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans) ret = READ_ONCE(prev_trans->aborted); btrfs_put_transaction(prev_trans); - if (ret) + if (unlikely(ret)) goto lockdep_release; spin_lock(&fs_info->trans_lock); } @@ -2338,11 +2346,11 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans) extwriter_counter_dec(cur_trans, trans->type); ret = btrfs_start_delalloc_flush(fs_info); - if (ret) + if (unlikely(ret)) goto lockdep_release; ret = btrfs_run_delayed_items(trans); - if (ret) + if (unlikely(ret)) goto lockdep_release; /* @@ -2357,7 +2365,7 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans) /* some pending stuffs might be added after the previous flush. */ ret = btrfs_run_delayed_items(trans); - if (ret) { + if (unlikely(ret)) { btrfs_lockdep_release(fs_info, btrfs_trans_num_writers); goto cleanup_transaction; } @@ -2429,7 +2437,7 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans) * core function of the snapshot creation. */ ret = create_pending_snapshots(trans); - if (ret) + if (unlikely(ret)) goto unlock_reloc; /* @@ -2443,11 +2451,11 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans) * the nodes and leaves. */ ret = btrfs_run_delayed_items(trans); - if (ret) + if (unlikely(ret)) goto unlock_reloc; ret = btrfs_run_delayed_refs(trans, U64_MAX); - if (ret) + if (unlikely(ret)) goto unlock_reloc; /* @@ -2459,7 +2467,7 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans) WARN_ON(cur_trans != trans->transaction); ret = commit_fs_roots(trans); - if (ret) + if (unlikely(ret)) goto unlock_reloc; /* commit_fs_roots gets rid of all the tree log roots, it is now @@ -2472,11 +2480,11 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans) * new_roots. So let's do quota accounting. */ ret = btrfs_qgroup_account_extents(trans); - if (ret < 0) + if (unlikely(ret < 0)) goto unlock_reloc; ret = commit_cowonly_roots(trans); - if (ret) + if (unlikely(ret)) goto unlock_reloc; /* @@ -2500,13 +2508,6 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans) list_add_tail(&fs_info->chunk_root->dirty_list, &cur_trans->switch_commits); - if (btrfs_fs_incompat(fs_info, EXTENT_TREE_V2)) { - btrfs_set_root_node(&fs_info->block_group_root->root_item, - fs_info->block_group_root->node); - list_add_tail(&fs_info->block_group_root->dirty_list, - &cur_trans->switch_commits); - } - switch_commit_roots(trans); ASSERT(list_empty(&cur_trans->dirty_bgs)); @@ -2550,9 +2551,8 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans) wake_up_process(fs_info->cleaner_kthread); ret = btrfs_write_and_wait_transaction(trans); - if (ret) { - btrfs_handle_fs_error(fs_info, ret, - "Error while writing out transaction"); + if (unlikely(ret)) { + btrfs_err(fs_info, "error while writing out transaction: %d", ret); mutex_unlock(&fs_info->tree_log_mutex); goto scrub_continue; } @@ -2563,7 +2563,7 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans) * to go about their business */ mutex_unlock(&fs_info->tree_log_mutex); - if (ret) + if (unlikely(ret)) goto scrub_continue; update_commit_stats(fs_info); @@ -2576,7 +2576,7 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans) btrfs_trans_state_lockdep_release(fs_info, BTRFS_LOCKDEP_TRANS_SUPER_COMMITTED); ret = btrfs_finish_extent_commit(trans); - if (ret) + if (unlikely(ret)) goto scrub_continue; if (test_bit(BTRFS_TRANS_HAVE_FREE_BGS, &cur_trans->flags)) diff --git a/fs/btrfs/tree-checker.c b/fs/btrfs/tree-checker.c index c21c21adf61e..452394b34d01 100644 --- a/fs/btrfs/tree-checker.c +++ b/fs/btrfs/tree-checker.c @@ -688,6 +688,7 @@ static int check_block_group_item(struct extent_buffer *leaf, u64 chunk_objectid; u64 flags; u64 type; + size_t exp_size; /* * Here we don't really care about alignment since extent allocator can @@ -699,10 +700,15 @@ static int check_block_group_item(struct extent_buffer *leaf, return -EUCLEAN; } - if (unlikely(item_size != sizeof(bgi))) { + if (btrfs_fs_incompat(fs_info, REMAP_TREE)) + exp_size = sizeof(struct btrfs_block_group_item_v2); + else + exp_size = sizeof(struct btrfs_block_group_item); + + if (unlikely(item_size != exp_size)) { block_group_err(leaf, slot, "invalid item size, have %u expect %zu", - item_size, sizeof(bgi)); + item_size, exp_size); return -EUCLEAN; } @@ -748,17 +754,26 @@ static int check_block_group_item(struct extent_buffer *leaf, return -EUCLEAN; } + if (unlikely(flags & BTRFS_BLOCK_GROUP_METADATA_REMAP && + !btrfs_fs_incompat(fs_info, REMAP_TREE))) { + block_group_err(leaf, slot, +"invalid flags, have 0x%llx (METADATA_REMAP flag set) but no remap-tree incompat flag", + flags); + return -EUCLEAN; + } + type = flags & BTRFS_BLOCK_GROUP_TYPE_MASK; if (unlikely(type != BTRFS_BLOCK_GROUP_DATA && type != BTRFS_BLOCK_GROUP_METADATA && type != BTRFS_BLOCK_GROUP_SYSTEM && + type != BTRFS_BLOCK_GROUP_METADATA_REMAP && type != (BTRFS_BLOCK_GROUP_METADATA | BTRFS_BLOCK_GROUP_DATA))) { block_group_err(leaf, slot, -"invalid type, have 0x%llx (%lu bits set) expect either 0x%llx, 0x%llx, 0x%llx or 0x%llx", +"invalid type, have 0x%llx (%lu bits set) expect either 0x%llx, 0x%llx, 0x%llx, 0x%llx or 0x%llx", type, hweight64(type), BTRFS_BLOCK_GROUP_DATA, BTRFS_BLOCK_GROUP_METADATA, - BTRFS_BLOCK_GROUP_SYSTEM, + BTRFS_BLOCK_GROUP_SYSTEM, BTRFS_BLOCK_GROUP_METADATA_REMAP, BTRFS_BLOCK_GROUP_METADATA | BTRFS_BLOCK_GROUP_DATA); return -EUCLEAN; } @@ -807,6 +822,32 @@ static void chunk_err(const struct btrfs_fs_info *fs_info, va_end(args); } +static bool valid_stripe_count(u64 profile, u16 num_stripes, u16 sub_stripes) +{ + switch (profile) { + case BTRFS_BLOCK_GROUP_RAID0: + return true; + case BTRFS_BLOCK_GROUP_RAID10: + return sub_stripes == btrfs_raid_array[BTRFS_RAID_RAID10].sub_stripes; + case BTRFS_BLOCK_GROUP_RAID1: + return num_stripes == btrfs_raid_array[BTRFS_RAID_RAID1].devs_min; + case BTRFS_BLOCK_GROUP_RAID1C3: + return num_stripes == btrfs_raid_array[BTRFS_RAID_RAID1C3].devs_min; + case BTRFS_BLOCK_GROUP_RAID1C4: + return num_stripes == btrfs_raid_array[BTRFS_RAID_RAID1C4].devs_min; + case BTRFS_BLOCK_GROUP_RAID5: + return num_stripes >= btrfs_raid_array[BTRFS_RAID_RAID5].devs_min; + case BTRFS_BLOCK_GROUP_RAID6: + return num_stripes >= btrfs_raid_array[BTRFS_RAID_RAID6].devs_min; + case BTRFS_BLOCK_GROUP_DUP: + return num_stripes == btrfs_raid_array[BTRFS_RAID_DUP].dev_stripes; + case 0: /* SINGLE */ + return num_stripes == btrfs_raid_array[BTRFS_RAID_SINGLE].dev_stripes; + default: + BUG(); + } +} + /* * The common chunk check which could also work on super block sys chunk array. * @@ -830,6 +871,7 @@ int btrfs_check_chunk_valid(const struct btrfs_fs_info *fs_info, u64 features; u32 chunk_sector_size; bool mixed = false; + bool remapped; int raid_index; int nparity; int ncopies; @@ -852,13 +894,14 @@ int btrfs_check_chunk_valid(const struct btrfs_fs_info *fs_info, raid_index = btrfs_bg_flags_to_raid_index(type); ncopies = btrfs_raid_array[raid_index].ncopies; nparity = btrfs_raid_array[raid_index].nparity; + remapped = (type & BTRFS_BLOCK_GROUP_REMAPPED); - if (unlikely(!num_stripes)) { + if (unlikely(!remapped && !num_stripes)) { chunk_err(fs_info, leaf, chunk, logical, "invalid chunk num_stripes, have %u", num_stripes); return -EUCLEAN; } - if (unlikely(num_stripes < ncopies)) { + if (unlikely(num_stripes != 0 && num_stripes < ncopies)) { chunk_err(fs_info, leaf, chunk, logical, "invalid chunk num_stripes < ncopies, have %u < %d", num_stripes, ncopies); @@ -913,12 +956,10 @@ int btrfs_check_chunk_valid(const struct btrfs_fs_info *fs_info, length, btrfs_stripe_nr_to_offset(U32_MAX)); return -EUCLEAN; } - if (unlikely(type & ~(BTRFS_BLOCK_GROUP_TYPE_MASK | - BTRFS_BLOCK_GROUP_PROFILE_MASK))) { + if (unlikely(type & ~BTRFS_BLOCK_GROUP_VALID)) { chunk_err(fs_info, leaf, chunk, logical, "unrecognized chunk type: 0x%llx", - ~(BTRFS_BLOCK_GROUP_TYPE_MASK | - BTRFS_BLOCK_GROUP_PROFILE_MASK) & type); + type & ~BTRFS_BLOCK_GROUP_VALID); return -EUCLEAN; } @@ -958,22 +999,9 @@ int btrfs_check_chunk_valid(const struct btrfs_fs_info *fs_info, } } - if (unlikely((type & BTRFS_BLOCK_GROUP_RAID10 && - sub_stripes != btrfs_raid_array[BTRFS_RAID_RAID10].sub_stripes) || - (type & BTRFS_BLOCK_GROUP_RAID1 && - num_stripes != btrfs_raid_array[BTRFS_RAID_RAID1].devs_min) || - (type & BTRFS_BLOCK_GROUP_RAID1C3 && - num_stripes != btrfs_raid_array[BTRFS_RAID_RAID1C3].devs_min) || - (type & BTRFS_BLOCK_GROUP_RAID1C4 && - num_stripes != btrfs_raid_array[BTRFS_RAID_RAID1C4].devs_min) || - (type & BTRFS_BLOCK_GROUP_RAID5 && - num_stripes < btrfs_raid_array[BTRFS_RAID_RAID5].devs_min) || - (type & BTRFS_BLOCK_GROUP_RAID6 && - num_stripes < btrfs_raid_array[BTRFS_RAID_RAID6].devs_min) || - (type & BTRFS_BLOCK_GROUP_DUP && - num_stripes != btrfs_raid_array[BTRFS_RAID_DUP].dev_stripes) || - ((type & BTRFS_BLOCK_GROUP_PROFILE_MASK) == 0 && - num_stripes != btrfs_raid_array[BTRFS_RAID_SINGLE].dev_stripes))) { + if (!remapped && + !valid_stripe_count(type & BTRFS_BLOCK_GROUP_PROFILE_MASK, + num_stripes, sub_stripes)) { chunk_err(fs_info, leaf, chunk, logical, "invalid num_stripes:sub_stripes %u:%u for profile %llu", num_stripes, sub_stripes, @@ -997,11 +1025,11 @@ static int check_leaf_chunk_item(struct extent_buffer *leaf, struct btrfs_fs_info *fs_info = leaf->fs_info; int num_stripes; - if (unlikely(btrfs_item_size(leaf, slot) < sizeof(struct btrfs_chunk))) { + if (unlikely(btrfs_item_size(leaf, slot) < offsetof(struct btrfs_chunk, stripe))) { chunk_err(fs_info, leaf, chunk, key->offset, "invalid chunk item size: have %u expect [%zu, %u)", btrfs_item_size(leaf, slot), - sizeof(struct btrfs_chunk), + offsetof(struct btrfs_chunk, stripe), BTRFS_LEAF_DATA_SIZE(fs_info)); return -EUCLEAN; } diff --git a/fs/btrfs/tree-checker.h b/fs/btrfs/tree-checker.h index eb201f4ec3c7..833e2fd989eb 100644 --- a/fs/btrfs/tree-checker.h +++ b/fs/btrfs/tree-checker.h @@ -57,6 +57,11 @@ enum btrfs_tree_block_status { BTRFS_TREE_BLOCK_WRITTEN_NOT_SET, }; + +#define BTRFS_BLOCK_GROUP_VALID (BTRFS_BLOCK_GROUP_TYPE_MASK | \ + BTRFS_BLOCK_GROUP_PROFILE_MASK | \ + BTRFS_BLOCK_GROUP_REMAPPED) + /* * Exported simply for btrfs-progs which wants to have the * btrfs_tree_block_status return codes. diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index 2d9d38b82daa..e1bd03ebfd98 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -2798,7 +2798,7 @@ static int replay_one_buffer(struct extent_buffer *eb, nritems = btrfs_header_nritems(eb); for (wc->log_slot = 0; wc->log_slot < nritems; wc->log_slot++) { - struct btrfs_inode_item *inode_item; + struct btrfs_inode_item *inode_item = NULL; btrfs_item_key_to_cpu(eb, &wc->log_key, wc->log_slot); @@ -5160,7 +5160,7 @@ static int log_one_extent(struct btrfs_trans_handle *trans, if (ctx->logged_before) { drop_args.path = path; drop_args.start = em->start; - drop_args.end = em->start + em->len; + drop_args.end = btrfs_extent_map_end(em); drop_args.replace_extent = true; drop_args.extent_item_size = sizeof(fi); ret = btrfs_drop_extents(trans, log, inode, &drop_args); diff --git a/fs/btrfs/uuid-tree.c b/fs/btrfs/uuid-tree.c index e3a1310fa7d5..f24c14b9bb2f 100644 --- a/fs/btrfs/uuid-tree.c +++ b/fs/btrfs/uuid-tree.c @@ -207,15 +207,11 @@ static int btrfs_uuid_iter_rem(struct btrfs_root *uuid_root, u8 *uuid, u8 type, /* 1 - for the uuid item */ trans = btrfs_start_transaction(uuid_root, 1); - if (IS_ERR(trans)) { - ret = PTR_ERR(trans); - goto out; - } + if (IS_ERR(trans)) + return PTR_ERR(trans); ret = btrfs_uuid_tree_remove(trans, uuid, type, subid); btrfs_end_transaction(trans); - -out: return ret; } @@ -235,14 +231,14 @@ static int btrfs_check_uuid_tree_entry(struct btrfs_fs_info *fs_info, if (type != BTRFS_UUID_KEY_SUBVOL && type != BTRFS_UUID_KEY_RECEIVED_SUBVOL) - goto out; + return 0; subvol_root = btrfs_get_fs_root(fs_info, subvolid, true); if (IS_ERR(subvol_root)) { ret = PTR_ERR(subvol_root); if (ret == -ENOENT) - ret = 1; - goto out; + return 1; + return ret; } switch (type) { @@ -257,7 +253,7 @@ static int btrfs_check_uuid_tree_entry(struct btrfs_fs_info *fs_info, break; } btrfs_put_root(subvol_root); -out: + return ret; } diff --git a/fs/btrfs/verity.c b/fs/btrfs/verity.c index a2ac3fb68bc8..d12537a212e9 100644 --- a/fs/btrfs/verity.c +++ b/fs/btrfs/verity.c @@ -525,23 +525,21 @@ static int finish_verity(struct btrfs_inode *inode, const void *desc, ret = write_key_bytes(inode, BTRFS_VERITY_DESC_ITEM_KEY, 0, (const char *)&item, sizeof(item)); if (ret) - goto out; + return ret; /* Write out the descriptor itself */ ret = write_key_bytes(inode, BTRFS_VERITY_DESC_ITEM_KEY, 1, desc, desc_size); if (ret) - goto out; + return ret; /* * 1 for updating the inode flag * 1 for deleting the orphan */ trans = btrfs_start_transaction(root, 2); - if (IS_ERR(trans)) { - ret = PTR_ERR(trans); - goto out; - } + if (IS_ERR(trans)) + return PTR_ERR(trans); inode->ro_flags |= BTRFS_INODE_RO_VERITY; btrfs_sync_inode_flags_to_i_flags(inode); ret = btrfs_update_inode(trans, inode); @@ -554,8 +552,7 @@ static int finish_verity(struct btrfs_inode *inode, const void *desc, btrfs_set_fs_compat_ro(root->fs_info, VERITY); end_trans: btrfs_end_transaction(trans); -out: - return ret; + return 0; } @@ -697,7 +694,6 @@ int btrfs_get_verity_descriptor(struct inode *inode, void *buf, size_t buf_size) * * @inode: inode to read a merkle tree page for * @index: page index relative to the start of the merkle tree - * @num_ra_pages: number of pages to readahead. Optional, we ignore it * * The Merkle tree is stored in the filesystem btree, but its pages are cached * with a logical position past EOF in the inode's mapping. @@ -705,8 +701,7 @@ int btrfs_get_verity_descriptor(struct inode *inode, void *buf, size_t buf_size) * Returns the page we read, or an ERR_PTR on error. */ static struct page *btrfs_read_merkle_tree_page(struct inode *inode, - pgoff_t index, - unsigned long num_ra_pages) + pgoff_t index) { struct folio *folio; u64 off = (u64)index << PAGE_SHIFT; @@ -774,16 +769,17 @@ out: /* * fsverity op that writes a Merkle tree block into the btree. * - * @inode: inode to write a Merkle tree block for + * @file: file to write a Merkle tree block for * @buf: Merkle tree block to write * @pos: the position of the block in the Merkle tree (in bytes) * @size: the Merkle tree block size (in bytes) * * Returns 0 on success or negative error code on failure */ -static int btrfs_write_merkle_tree_block(struct inode *inode, const void *buf, +static int btrfs_write_merkle_tree_block(struct file *file, const void *buf, u64 pos, unsigned int size) { + struct inode *inode = file_inode(file); loff_t merkle_pos = merkle_file_pos(inode); if (merkle_pos < 0) @@ -796,8 +792,6 @@ static int btrfs_write_merkle_tree_block(struct inode *inode, const void *buf, } const struct fsverity_operations btrfs_verityops = { - .inode_info_offs = (int)offsetof(struct btrfs_inode, i_verity_info) - - (int)offsetof(struct btrfs_inode, vfs_inode), .begin_enable_verity = btrfs_begin_enable_verity, .end_enable_verity = btrfs_end_enable_verity, .get_verity_descriptor = btrfs_get_verity_descriptor, diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 13c514684cfb..f281d113519b 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -231,6 +231,10 @@ void btrfs_describe_block_groups(u64 bg_flags, char *buf, u32 size_buf) DESCRIBE_FLAG(BTRFS_BLOCK_GROUP_DATA, "data"); DESCRIBE_FLAG(BTRFS_BLOCK_GROUP_SYSTEM, "system"); DESCRIBE_FLAG(BTRFS_BLOCK_GROUP_METADATA, "metadata"); + /* Block groups containing the remap tree. */ + DESCRIBE_FLAG(BTRFS_BLOCK_GROUP_METADATA_REMAP, "metadata-remap"); + /* Block group that has been remapped. */ + DESCRIBE_FLAG(BTRFS_BLOCK_GROUP_REMAPPED, "remapped"); DESCRIBE_FLAG(BTRFS_AVAIL_ALLOC_BIT_SINGLE, "single"); for (i = 0; i < BTRFS_NR_RAID_TYPES; i++) @@ -1169,7 +1173,7 @@ static void btrfs_close_one_device(struct btrfs_device *device) * any transaction and set the error state, guaranteeing no commits of * unsafe super blocks. */ - device->last_flush_error = 0; + clear_bit(BTRFS_DEV_STATE_FLUSH_FAILED, &device->dev_state); /* Verify the device is back in a pristine state */ WARN_ON(test_bit(BTRFS_DEV_STATE_FLUSH_SENT, &device->dev_state)); @@ -1364,7 +1368,9 @@ struct btrfs_super_block *btrfs_read_disk_super(struct block_device *bdev, (bytenr + BTRFS_SUPER_INFO_SIZE) >> PAGE_SHIFT); } + filemap_invalidate_lock(mapping); page = read_cache_page_gfp(mapping, bytenr >> PAGE_SHIFT, GFP_NOFS); + filemap_invalidate_unlock(mapping); if (IS_ERR(page)) return ERR_CAST(page); @@ -1503,30 +1509,158 @@ error_bdev_put: } /* - * Try to find a chunk that intersects [start, start + len] range and when one - * such is found, record the end of it in *start + * Find the first pending extent intersecting a range. + * + * @device: the device to search + * @start: start of the range to check + * @len: length of the range to check + * @pending_start: output pointer for the start of the found pending extent + * @pending_end: output pointer for the end of the found pending extent (inclusive) + * + * Search for a pending chunk allocation that intersects the half-open range + * [start, start + len). + * + * Return: true if a pending extent was found, false otherwise. + * If the return value is true, store the first pending extent in + * [*pending_start, *pending_end]. Otherwise, the two output variables + * may still be modified, to something outside the range and should not + * be used. */ -static bool contains_pending_extent(struct btrfs_device *device, u64 *start, - u64 len) +bool btrfs_first_pending_extent(struct btrfs_device *device, u64 start, u64 len, + u64 *pending_start, u64 *pending_end) { - u64 physical_start, physical_end; - lockdep_assert_held(&device->fs_info->chunk_mutex); - if (btrfs_find_first_extent_bit(&device->alloc_state, *start, - &physical_start, &physical_end, + if (btrfs_find_first_extent_bit(&device->alloc_state, start, + pending_start, pending_end, CHUNK_ALLOCATED, NULL)) { - if (in_range(physical_start, *start, len) || - in_range(*start, physical_start, - physical_end + 1 - physical_start)) { - *start = physical_end + 1; + if (in_range(*pending_start, start, len) || + in_range(start, *pending_start, *pending_end + 1 - *pending_start)) { return true; } } return false; } +/* + * Find the first real hole accounting for pending extents. + * + * @device: the device containing the candidate hole + * @start: input/output pointer for the hole start position + * @len: input/output pointer for the hole length + * @min_hole_size: the size of hole we are looking for + * + * Given a potential hole specified by [*start, *start + *len), check for pending + * chunk allocations within that range. If pending extents are found, the hole is + * adjusted to represent the first true free space that is large enough when + * accounting for pending chunks. + * + * Note that this function must handle various cases involving non consecutive + * pending extents. + * + * Returns: true if a suitable hole was found and false otherwise. + * If the return value is true, then *start and *len are set to represent the hole. + * If the return value is false, then *start is set to the largest hole we + * found and *len is set to its length. + * If there are no holes at all, then *start is set to the end of the range and + * *len is set to 0. + */ +bool btrfs_find_hole_in_pending_extents(struct btrfs_device *device, u64 *start, + u64 *len, u64 min_hole_size) +{ + u64 pending_start, pending_end; + u64 end; + u64 max_hole_start = 0; + u64 max_hole_len = 0; + + lockdep_assert_held(&device->fs_info->chunk_mutex); + + if (*len == 0) + return false; + + end = *start + *len - 1; + + /* + * Loop until we either see a large enough hole or check every pending + * extent overlapping the candidate hole. + * At every hole that we observe, record it if it is the new max. + * At the end of the iteration, set the output variables to the max hole. + */ + while (true) { + if (btrfs_first_pending_extent(device, *start, *len, &pending_start, &pending_end)) { + /* + * Case 1: the pending extent overlaps the start of + * candidate hole. That means the true hole is after the + * pending extent, but we need to find the next pending + * extent to properly size the hole. In the next loop, + * we will reduce to case 2 or 3. + * e.g., + * + * |----pending A----| real hole |----pending B----| + * | candidate hole | + * *start end + */ + if (pending_start <= *start) { + *start = pending_end + 1; + goto next; + } + /* + * Case 2: The pending extent starts after *start (and overlaps + * [*start, end), so the first hole just goes up to the start + * of the pending extent. + * e.g., + * + * | real hole |----pending A----| + * | candidate hole | + * *start end + */ + *len = pending_start - *start; + if (*len > max_hole_len) { + max_hole_start = *start; + max_hole_len = *len; + } + if (*len >= min_hole_size) + break; + /* + * If the hole wasn't big enough, then we advance past + * the pending extent and keep looking. + */ + *start = pending_end + 1; + goto next; + } else { + /* + * Case 3: There is no pending extent overlapping the + * range [*start, *start + *len - 1], so the only remaining + * hole is the remaining range. + * e.g., + * + * | candidate hole | + * | real hole | + * *start end + */ + + if (*len > max_hole_len) { + max_hole_start = *start; + max_hole_len = *len; + } + break; + } +next: + if (*start > end) + break; + *len = end - *start + 1; + } + if (max_hole_len) { + *start = max_hole_start; + *len = max_hole_len; + } else { + *start = end + 1; + *len = 0; + } + return max_hole_len >= min_hole_size; +} + static u64 dev_extent_search_start(struct btrfs_device *device) { switch (device->fs_devices->chunk_alloc_policy) { @@ -1591,59 +1725,57 @@ static bool dev_extent_hole_check_zoned(struct btrfs_device *device, } /* - * Check if specified hole is suitable for allocation. + * Validate and adjust a hole for chunk allocation * - * @device: the device which we have the hole - * @hole_start: starting position of the hole - * @hole_size: the size of the hole - * @num_bytes: the size of the free space that we need + * @device: the device containing the candidate hole + * @hole_start: input/output pointer for the hole start position + * @hole_size: input/output pointer for the hole size + * @num_bytes: minimum allocation size required * - * This function may modify @hole_start and @hole_size to reflect the suitable - * position for allocation. Returns 1 if hole position is updated, 0 otherwise. + * Check if the specified hole is suitable for allocation and adjust it if + * necessary. The hole may be modified to skip over pending chunk allocations + * and to satisfy stricter zoned requirements on zoned filesystems. + * + * For regular (non-zoned) allocation, if the hole after adjustment is smaller + * than @num_bytes, the search continues past additional pending extents until + * either a sufficiently large hole is found or no more pending extents exist. + * + * Return: true if a suitable hole was found and false otherwise. + * If the return value is true, then *hole_start and *hole_size are set to + * represent the hole we found. + * If the return value is false, then *hole_start is set to the largest + * hole we found and *hole_size is set to its length. + * If there are no holes at all, then *hole_start is set to the end of the range + * and *hole_size is set to 0. */ static bool dev_extent_hole_check(struct btrfs_device *device, u64 *hole_start, u64 *hole_size, u64 num_bytes) { - bool changed = false; - u64 hole_end = *hole_start + *hole_size; + bool found = false; + const u64 hole_end = *hole_start + *hole_size - 1; - for (;;) { - /* - * Check before we set max_hole_start, otherwise we could end up - * sending back this offset anyway. - */ - if (contains_pending_extent(device, hole_start, *hole_size)) { - if (hole_end >= *hole_start) - *hole_size = hole_end - *hole_start; - else - *hole_size = 0; - changed = true; - } + ASSERT(*hole_size > 0); - switch (device->fs_devices->chunk_alloc_policy) { - default: - btrfs_warn_unknown_chunk_allocation(device->fs_devices->chunk_alloc_policy); - fallthrough; - case BTRFS_CHUNK_ALLOC_REGULAR: - /* No extra check */ - break; - case BTRFS_CHUNK_ALLOC_ZONED: - if (dev_extent_hole_check_zoned(device, hole_start, - hole_size, num_bytes)) { - changed = true; - /* - * The changed hole can contain pending extent. - * Loop again to check that. - */ - continue; - } - break; - } +again: + *hole_size = hole_end - *hole_start + 1; + found = btrfs_find_hole_in_pending_extents(device, hole_start, hole_size, num_bytes); + if (!found) + return found; + ASSERT(*hole_size >= num_bytes); + switch (device->fs_devices->chunk_alloc_policy) { + default: + btrfs_warn_unknown_chunk_allocation(device->fs_devices->chunk_alloc_policy); + fallthrough; + case BTRFS_CHUNK_ALLOC_REGULAR: + return found; + case BTRFS_CHUNK_ALLOC_ZONED: + if (dev_extent_hole_check_zoned(device, hole_start, hole_size, num_bytes)) + goto again; break; } - return changed; + return found; } /* @@ -1702,7 +1834,7 @@ static int find_free_dev_extent(struct btrfs_device *device, u64 num_bytes, ret = -ENOMEM; goto out; } -again: + if (search_start >= search_end || test_bit(BTRFS_DEV_STATE_REPLACE_TGT, &device->dev_state)) { ret = -ENOSPC; @@ -1789,11 +1921,7 @@ next: */ if (search_end > search_start) { hole_size = search_end - search_start; - if (dev_extent_hole_check(device, &search_start, &hole_size, - num_bytes)) { - btrfs_release_path(path); - goto again; - } + dev_extent_hole_check(device, &search_start, &hole_size, num_bytes); if (hole_size > max_hole_size) { max_hole_start = search_start; @@ -2314,9 +2442,7 @@ int btrfs_rm_device(struct btrfs_fs_info *fs_info, free_fs_devices(cur_devices); } - ret = btrfs_commit_transaction(trans); - - return ret; + return btrfs_commit_transaction(trans); error_undo: if (test_bit(BTRFS_DEV_STATE_WRITEABLE, &device->dev_state)) { @@ -2921,8 +3047,7 @@ error: return ret; } -static noinline int btrfs_update_device(struct btrfs_trans_handle *trans, - struct btrfs_device *device) +int btrfs_update_device(struct btrfs_trans_handle *trans, struct btrfs_device *device) { int ret; BTRFS_PATH_AUTO_FREE(path); @@ -3220,25 +3345,12 @@ static int remove_chunk_item(struct btrfs_trans_handle *trans, return btrfs_free_chunk(trans, chunk_offset); } -int btrfs_remove_chunk(struct btrfs_trans_handle *trans, u64 chunk_offset) +int btrfs_remove_dev_extents(struct btrfs_trans_handle *trans, struct btrfs_chunk_map *map) { struct btrfs_fs_info *fs_info = trans->fs_info; - struct btrfs_chunk_map *map; + struct btrfs_fs_devices *fs_devices = fs_info->fs_devices; u64 dev_extent_len = 0; int i, ret = 0; - struct btrfs_fs_devices *fs_devices = fs_info->fs_devices; - - map = btrfs_get_chunk_map(fs_info, chunk_offset, 1); - if (IS_ERR(map)) { - /* - * This is a logic error, but we don't want to just rely on the - * user having built with ASSERT enabled, so if ASSERT doesn't - * do anything we still error out. - */ - DEBUG_WARN("errr %ld reading chunk map at offset %llu", - PTR_ERR(map), chunk_offset); - return PTR_ERR(map); - } /* * First delete the device extent items from the devices btree. @@ -3259,7 +3371,7 @@ int btrfs_remove_chunk(struct btrfs_trans_handle *trans, u64 chunk_offset) if (unlikely(ret)) { mutex_unlock(&fs_devices->device_list_mutex); btrfs_abort_transaction(trans, ret); - goto out; + return ret; } if (device->bytes_used > 0) { @@ -3279,6 +3391,26 @@ int btrfs_remove_chunk(struct btrfs_trans_handle *trans, u64 chunk_offset) } mutex_unlock(&fs_devices->device_list_mutex); + return 0; +} + +int btrfs_remove_chunk(struct btrfs_trans_handle *trans, u64 chunk_offset) +{ + struct btrfs_fs_info *fs_info = trans->fs_info; + struct btrfs_chunk_map *map; + int ret; + + map = btrfs_get_chunk_map(fs_info, chunk_offset, 1); + if (IS_ERR(map)) { + DEBUG_WARN("errr %ld reading chunk map at offset %llu", + PTR_ERR(map), chunk_offset); + return PTR_ERR(map); + } + + ret = btrfs_remove_dev_extents(trans, map); + if (ret) + goto out; + /* * We acquire fs_info->chunk_mutex for 2 reasons: * @@ -3374,11 +3506,10 @@ int btrfs_remove_chunk(struct btrfs_trans_handle *trans, u64 chunk_offset) */ btrfs_trans_release_chunk_metadata(trans); + /* On error, btrfs_remove_block_group() aborts the transaction. */ ret = btrfs_remove_block_group(trans, map); - if (unlikely(ret)) { - btrfs_abort_transaction(trans, ret); - goto out; - } + if (unlikely(ret)) + ASSERT(BTRFS_FS_ERROR(fs_info) != 0); out: if (trans->removing_chunk) { @@ -3390,15 +3521,50 @@ out: return ret; } -int btrfs_relocate_chunk(struct btrfs_fs_info *fs_info, u64 chunk_offset, - bool verbose) +static int btrfs_relocate_chunk_finish(struct btrfs_fs_info *fs_info, + struct btrfs_block_group *bg) { struct btrfs_root *root = fs_info->chunk_root; struct btrfs_trans_handle *trans; - struct btrfs_block_group *block_group; u64 length; int ret; + btrfs_discard_cancel_work(&fs_info->discard_ctl, bg); + length = bg->length; + btrfs_put_block_group(bg); + + /* + * On a zoned file system, discard the whole block group, this will + * trigger a REQ_OP_ZONE_RESET operation on the device zone. If + * resetting the zone fails, don't treat it as a fatal problem from the + * filesystem's point of view. + */ + if (btrfs_is_zoned(fs_info)) { + ret = btrfs_discard_extent(fs_info, bg->start, length, NULL, true); + if (ret) + btrfs_info(fs_info, "failed to reset zone %llu after relocation", + bg->start); + } + + trans = btrfs_start_trans_remove_block_group(root->fs_info, bg->start); + if (IS_ERR(trans)) { + ret = PTR_ERR(trans); + btrfs_handle_fs_error(root->fs_info, ret, NULL); + return ret; + } + + /* Step two, delete the device extents and the chunk tree entries. */ + ret = btrfs_remove_chunk(trans, bg->start); + btrfs_end_transaction(trans); + + return ret; +} + +int btrfs_relocate_chunk(struct btrfs_fs_info *fs_info, u64 chunk_offset, bool verbose) +{ + struct btrfs_block_group *block_group; + int ret; + if (btrfs_fs_incompat(fs_info, EXTENT_TREE_V2)) { btrfs_err(fs_info, "relocate: not supported on extent tree v2 yet"); @@ -3436,38 +3602,15 @@ int btrfs_relocate_chunk(struct btrfs_fs_info *fs_info, u64 chunk_offset, block_group = btrfs_lookup_block_group(fs_info, chunk_offset); if (!block_group) return -ENOENT; - btrfs_discard_cancel_work(&fs_info->discard_ctl, block_group); - length = block_group->length; - btrfs_put_block_group(block_group); - /* - * On a zoned file system, discard the whole block group, this will - * trigger a REQ_OP_ZONE_RESET operation on the device zone. If - * resetting the zone fails, don't treat it as a fatal problem from the - * filesystem's point of view. - */ - if (btrfs_is_zoned(fs_info)) { - ret = btrfs_discard_extent(fs_info, chunk_offset, length, NULL); - if (ret) - btrfs_info(fs_info, - "failed to reset zone %llu after relocation", - chunk_offset); - } - - trans = btrfs_start_trans_remove_block_group(root->fs_info, - chunk_offset); - if (IS_ERR(trans)) { - ret = PTR_ERR(trans); - btrfs_handle_fs_error(root->fs_info, ret, NULL); - return ret; + if (should_relocate_using_remap_tree(block_group)) { + /* If we're relocating using the remap tree we're now done. */ + btrfs_put_block_group(block_group); + ret = 0; + } else { + ret = btrfs_relocate_chunk_finish(fs_info, block_group); } - /* - * step two, delete the device extents and the - * chunk tree entries - */ - ret = btrfs_remove_chunk(trans, chunk_offset); - btrfs_end_transaction(trans); return ret; } @@ -3644,7 +3787,7 @@ static int insert_balance_item(struct btrfs_fs_info *fs_info, struct btrfs_path *path; struct extent_buffer *leaf; struct btrfs_key key; - int ret, err; + int ret; path = btrfs_alloc_path(); if (!path) @@ -3679,9 +3822,11 @@ static int insert_balance_item(struct btrfs_fs_info *fs_info, btrfs_set_balance_flags(leaf, item, bctl->flags); out: btrfs_free_path(path); - err = btrfs_commit_transaction(trans); - if (err && !ret) - ret = err; + if (ret == 0) + ret = btrfs_commit_transaction(trans); + else + btrfs_end_transaction(trans); + return ret; } @@ -3691,7 +3836,7 @@ static int del_balance_item(struct btrfs_fs_info *fs_info) struct btrfs_trans_handle *trans; struct btrfs_path *path; struct btrfs_key key; - int ret, err; + int ret; path = btrfs_alloc_path(); if (!path) @@ -3718,9 +3863,11 @@ static int del_balance_item(struct btrfs_fs_info *fs_info) ret = btrfs_del_item(trans, root, path); out: btrfs_free_path(path); - err = btrfs_commit_transaction(trans); - if (err && !ret) - ret = err; + if (ret == 0) + ret = btrfs_commit_transaction(trans); + else + btrfs_end_transaction(trans); + return ret; } @@ -3964,6 +4111,12 @@ static bool should_balance_chunk(struct extent_buffer *leaf, struct btrfs_chunk struct btrfs_balance_args *bargs = NULL; u64 chunk_type = btrfs_chunk_type(leaf, chunk); + /* Treat METADATA_REMAP chunks as METADATA. */ + if (chunk_type & BTRFS_BLOCK_GROUP_METADATA_REMAP) { + chunk_type &= ~BTRFS_BLOCK_GROUP_METADATA_REMAP; + chunk_type |= BTRFS_BLOCK_GROUP_METADATA; + } + /* type filter */ if (!((chunk_type & BTRFS_BLOCK_GROUP_TYPE_MASK) & (bctl->flags & BTRFS_BALANCE_TYPE_MASK))) { @@ -4045,6 +4198,107 @@ static bool should_balance_chunk(struct extent_buffer *leaf, struct btrfs_chunk return true; } +struct remap_chunk_info { + struct list_head list; + u64 offset; + struct btrfs_block_group *bg; + bool made_ro; +}; + +static int cow_remap_tree(struct btrfs_trans_handle *trans, struct btrfs_path *path) +{ + struct btrfs_fs_info *fs_info = trans->fs_info; + struct btrfs_key key = { 0 }; + int ret; + + ret = btrfs_search_slot(trans, fs_info->remap_root, &key, path, 0, 1); + if (ret < 0) + return ret; + + while (true) { + ret = btrfs_next_leaf(fs_info->remap_root, path); + if (ret < 0) { + return ret; + } else if (ret > 0) { + ret = 0; + break; + } + + btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]); + + btrfs_release_path(path); + + ret = btrfs_search_slot(trans, fs_info->remap_root, &key, path, 0, 1); + if (ret < 0) + break; + } + + return ret; +} + +static int balance_remap_chunks(struct btrfs_fs_info *fs_info, struct btrfs_path *path, + struct list_head *chunks) +{ + struct remap_chunk_info *rci, *tmp; + struct btrfs_trans_handle *trans; + int ret; + + list_for_each_entry_safe(rci, tmp, chunks, list) { + rci->bg = btrfs_lookup_block_group(fs_info, rci->offset); + if (!rci->bg) { + list_del(&rci->list); + kfree(rci); + continue; + } + + ret = btrfs_inc_block_group_ro(rci->bg, false); + if (ret) + goto end; + + rci->made_ro = true; + } + + if (list_empty(chunks)) + return 0; + + trans = btrfs_start_transaction(fs_info->remap_root, 0); + if (IS_ERR(trans)) { + ret = PTR_ERR(trans); + goto end; + } + + mutex_lock(&fs_info->remap_mutex); + ret = cow_remap_tree(trans, path); + mutex_unlock(&fs_info->remap_mutex); + + btrfs_release_path(path); + btrfs_commit_transaction(trans); + +end: + while (!list_empty(chunks)) { + bool is_unused; + + 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); + + if (is_unused) + btrfs_mark_bg_unused(rci->bg); + + if (rci->made_ro) + btrfs_dec_block_group_ro(rci->bg); + + btrfs_put_block_group(rci->bg); + + list_del(&rci->list); + kfree(rci); + } + + return ret; +} + static int __btrfs_balance(struct btrfs_fs_info *fs_info) { struct btrfs_balance_control *bctl = fs_info->balance_ctl; @@ -4067,6 +4321,9 @@ static int __btrfs_balance(struct btrfs_fs_info *fs_info) u32 count_meta = 0; u32 count_sys = 0; int chunk_reserved = 0; + struct remap_chunk_info *rci; + unsigned int num_remap_chunks = 0; + LIST_HEAD(remap_chunks); path = btrfs_alloc_path(); if (!path) { @@ -4133,6 +4390,14 @@ again: chunk = btrfs_item_ptr(leaf, slot, struct btrfs_chunk); chunk_type = btrfs_chunk_type(leaf, chunk); + /* Check if chunk has already been fully relocated. */ + if (chunk_type & BTRFS_BLOCK_GROUP_REMAPPED && + btrfs_chunk_num_stripes(leaf, chunk) == 0) { + btrfs_release_path(path); + mutex_unlock(&fs_info->reclaim_bgs_lock); + goto loop; + } + if (!counting) { spin_lock(&fs_info->balance_lock); bctl->stat.considered++; @@ -4157,7 +4422,8 @@ again: count_data++; else if (chunk_type & BTRFS_BLOCK_GROUP_SYSTEM) count_sys++; - else if (chunk_type & BTRFS_BLOCK_GROUP_METADATA) + else if (chunk_type & (BTRFS_BLOCK_GROUP_METADATA | + BTRFS_BLOCK_GROUP_METADATA_REMAP)) count_meta++; goto loop; @@ -4177,6 +4443,29 @@ again: goto loop; } + /* + * Balancing METADATA_REMAP chunks takes place separately - add + * the details to a list so it can be processed later. + */ + if (chunk_type & BTRFS_BLOCK_GROUP_METADATA_REMAP) { + mutex_unlock(&fs_info->reclaim_bgs_lock); + + rci = kmalloc(sizeof(struct remap_chunk_info), GFP_NOFS); + if (!rci) { + ret = -ENOMEM; + goto error; + } + + rci->offset = found_key.offset; + rci->bg = NULL; + rci->made_ro = false; + list_add_tail(&rci->list, &remap_chunks); + + num_remap_chunks++; + + goto loop; + } + if (!chunk_reserved) { /* * We may be relocating the only data chunk we have, @@ -4216,11 +4505,24 @@ loop: key.offset = found_key.offset - 1; } + btrfs_release_path(path); + if (counting) { - btrfs_release_path(path); counting = false; goto again; } + + if (!list_empty(&remap_chunks)) { + ret = balance_remap_chunks(fs_info, path, &remap_chunks); + if (ret == -ENOSPC) + enospc_errors++; + + if (!ret) { + spin_lock(&fs_info->balance_lock); + bctl->stat.completed += num_remap_chunks; + spin_unlock(&fs_info->balance_lock); + } + } error: if (enospc_errors) { btrfs_info(fs_info, "%d enospc errors during balance", @@ -4842,6 +5144,7 @@ int btrfs_shrink_device(struct btrfs_device *device, u64 new_size) u64 diff; u64 start; u64 free_diff = 0; + u64 pending_start, pending_end; new_size = round_down(new_size, fs_info->sectorsize); start = new_size; @@ -4887,7 +5190,7 @@ int btrfs_shrink_device(struct btrfs_device *device, u64 new_size) * in-memory chunks are synced to disk so that the loop below sees them * and relocates them accordingly. */ - if (contains_pending_extent(device, &start, diff)) { + if (btrfs_first_pending_extent(device, start, diff, &pending_start, &pending_end)) { mutex_unlock(&fs_info->chunk_mutex); ret = btrfs_commit_transaction(trans); if (ret) @@ -5408,7 +5711,7 @@ static void chunk_map_device_set_bits(struct btrfs_chunk_map *map, unsigned int } } -static void chunk_map_device_clear_bits(struct btrfs_chunk_map *map, unsigned int bits) +void btrfs_chunk_map_device_clear_bits(struct btrfs_chunk_map *map, unsigned int bits) { for (int i = 0; i < map->num_stripes; i++) { struct btrfs_io_stripe *stripe = &map->stripes[i]; @@ -5425,7 +5728,7 @@ void btrfs_remove_chunk_map(struct btrfs_fs_info *fs_info, struct btrfs_chunk_ma write_lock(&fs_info->mapping_tree_lock); rb_erase_cached(&map->rb_node, &fs_info->mapping_tree); RB_CLEAR_NODE(&map->rb_node); - chunk_map_device_clear_bits(map, CHUNK_ALLOCATED); + btrfs_chunk_map_device_clear_bits(map, CHUNK_ALLOCATED); write_unlock(&fs_info->mapping_tree_lock); /* Once for the tree reference. */ @@ -5461,7 +5764,7 @@ int btrfs_add_chunk_map(struct btrfs_fs_info *fs_info, struct btrfs_chunk_map *m return -EEXIST; } chunk_map_device_set_bits(map, CHUNK_ALLOCATED); - chunk_map_device_clear_bits(map, CHUNK_TRIMMED); + btrfs_chunk_map_device_clear_bits(map, CHUNK_TRIMMED); write_unlock(&fs_info->mapping_tree_lock); return 0; @@ -5817,7 +6120,7 @@ void btrfs_mapping_tree_free(struct btrfs_fs_info *fs_info) map = rb_entry(node, struct btrfs_chunk_map, rb_node); rb_erase_cached(&map->rb_node, &fs_info->mapping_tree); RB_CLEAR_NODE(&map->rb_node); - chunk_map_device_clear_bits(map, CHUNK_ALLOCATED); + btrfs_chunk_map_device_clear_bits(map, CHUNK_ALLOCATED); /* Once for the tree ref. */ btrfs_free_chunk_map(map); cond_resched_rwlock_write(&fs_info->mapping_tree_lock); @@ -6064,7 +6367,7 @@ void btrfs_put_bioc(struct btrfs_io_context *bioc) */ struct btrfs_discard_stripe *btrfs_map_discard(struct btrfs_fs_info *fs_info, u64 logical, u64 *length_ret, - u32 *num_stripes) + u32 *num_stripes, bool do_remap) { struct btrfs_chunk_map *map; struct btrfs_discard_stripe *stripes; @@ -6088,6 +6391,24 @@ struct btrfs_discard_stripe *btrfs_map_discard(struct btrfs_fs_info *fs_info, if (IS_ERR(map)) return ERR_CAST(map); + if (do_remap && (map->type & BTRFS_BLOCK_GROUP_REMAPPED)) { + u64 new_logical = logical; + + ret = btrfs_translate_remap(fs_info, &new_logical, &length); + if (ret) + goto out_free_map; + + if (new_logical != logical) { + btrfs_free_chunk_map(map); + + map = btrfs_get_chunk_map(fs_info, new_logical, length); + if (IS_ERR(map)) + return ERR_CAST(map); + + logical = new_logical; + } + } + /* we don't discard raid56 yet */ if (map->type & BTRFS_BLOCK_GROUP_RAID56_MASK) { ret = -EOPNOTSUPP; @@ -6575,6 +6896,24 @@ int btrfs_map_block(struct btrfs_fs_info *fs_info, enum btrfs_map_op op, if (IS_ERR(map)) return PTR_ERR(map); + if (map->type & BTRFS_BLOCK_GROUP_REMAPPED) { + u64 new_logical = logical; + + ret = btrfs_translate_remap(fs_info, &new_logical, length); + if (ret) + return ret; + + if (new_logical != logical) { + btrfs_free_chunk_map(map); + + map = btrfs_get_chunk_map(fs_info, new_logical, *length); + if (IS_ERR(map)) + return PTR_ERR(map); + + logical = new_logical; + } + } + num_copies = btrfs_chunk_map_num_copies(map); if (io_geom.mirror_num > num_copies) return -EINVAL; @@ -7039,7 +7378,12 @@ static int read_one_chunk(struct btrfs_key *key, struct extent_buffer *leaf, */ map->sub_stripes = btrfs_raid_array[index].sub_stripes; map->verified_stripes = 0; - map->stripe_size = btrfs_calc_stripe_length(map); + + if (num_stripes > 0) + map->stripe_size = btrfs_calc_stripe_length(map); + else + map->stripe_size = 0; + for (i = 0; i < num_stripes; i++) { map->stripes[i].physical = btrfs_stripe_offset_nr(leaf, chunk, i); @@ -7165,7 +7509,6 @@ static int read_one_dev(struct extent_buffer *leaf, struct btrfs_fs_devices *fs_devices = fs_info->fs_devices; struct btrfs_device *device; u64 devid; - int ret; u8 fs_uuid[BTRFS_FSID_SIZE]; u8 dev_uuid[BTRFS_UUID_SIZE]; @@ -7257,6 +7600,7 @@ static int read_one_dev(struct extent_buffer *leaf, return -EINVAL; } } + set_bit(BTRFS_DEV_STATE_ITEM_FOUND, &device->dev_state); set_bit(BTRFS_DEV_STATE_IN_FS_METADATA, &device->dev_state); if (test_bit(BTRFS_DEV_STATE_WRITEABLE, &device->dev_state) && !test_bit(BTRFS_DEV_STATE_REPLACE_TGT, &device->dev_state)) { @@ -7264,8 +7608,8 @@ static int read_one_dev(struct extent_buffer *leaf, atomic64_add(device->total_bytes - device->bytes_used, &fs_info->free_chunk_space); } - ret = 0; - return ret; + + return 0; } int btrfs_read_sys_array(struct btrfs_fs_info *fs_info) @@ -7354,10 +7698,9 @@ bool btrfs_check_rw_degradable(struct btrfs_fs_info *fs_info, map = btrfs_find_chunk_map(fs_info, 0, U64_MAX); /* No chunk at all? Return false anyway */ - if (!map) { - ret = false; - goto out; - } + if (!map) + return false; + while (map) { int missing = 0; int max_tolerated; @@ -7371,7 +7714,7 @@ bool btrfs_check_rw_degradable(struct btrfs_fs_info *fs_info, if (!dev || !dev->bdev || test_bit(BTRFS_DEV_STATE_MISSING, &dev->dev_state) || - dev->last_flush_error) + test_bit(BTRFS_DEV_STATE_FLUSH_FAILED, &dev->dev_state)) missing++; else if (failing_dev && failing_dev == dev) missing++; @@ -7382,15 +7725,14 @@ bool btrfs_check_rw_degradable(struct btrfs_fs_info *fs_info, "chunk %llu missing %d devices, max tolerance is %d for writable mount", map->start, missing, max_tolerated); btrfs_free_chunk_map(map); - ret = false; - goto out; + return false; } next_start = map->start + map->chunk_len; btrfs_free_chunk_map(map); map = btrfs_find_chunk_map(fs_info, next_start, U64_MAX - next_start); } -out: + return ret; } @@ -8022,7 +8364,7 @@ int btrfs_verify_dev_extents(struct btrfs_fs_info *fs_info) if (!path) return -ENOMEM; - path->reada = READA_FORWARD; + path->reada = READA_FORWARD_ALWAYS; ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); if (ret < 0) return ret; @@ -8083,6 +8425,45 @@ int btrfs_verify_dev_extents(struct btrfs_fs_info *fs_info) } /* + * Ensure that all devices registered in the fs have their device items in the + * chunk tree. + * + * Return true if unexpected device is found. + * Return false otherwise. + */ +bool btrfs_verify_dev_items(const struct btrfs_fs_info *fs_info) +{ + struct btrfs_fs_devices *seed_devs; + struct btrfs_device *dev; + bool ret = false; + + mutex_lock(&uuid_mutex); + list_for_each_entry(dev, &fs_info->fs_devices->devices, dev_list) { + if (!test_bit(BTRFS_DEV_STATE_ITEM_FOUND, &dev->dev_state)) { + btrfs_err(fs_info, + "devid %llu path %s is registered but not found in chunk tree", + dev->devid, btrfs_dev_name(dev)); + ret = true; + } + } + list_for_each_entry(seed_devs, &fs_info->fs_devices->seed_list, seed_list) { + list_for_each_entry(dev, &seed_devs->devices, dev_list) { + if (!test_bit(BTRFS_DEV_STATE_ITEM_FOUND, &dev->dev_state)) { + btrfs_err(fs_info, + "devid %llu path %s is registered but not found in chunk tree", + dev->devid, btrfs_dev_name(dev)); + ret = true; + } + } + } + mutex_unlock(&uuid_mutex); + if (ret) + btrfs_err(fs_info, +"remove the above devices or use 'btrfs device scan --forget <dev>' to unregister them before mount"); + return ret; +} + +/* * Check whether the given block group or device is pinned by any inode being * used as a swapfile. */ diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h index 34b854c1a303..8288d79372a5 100644 --- a/fs/btrfs/volumes.h +++ b/fs/btrfs/volumes.h @@ -30,6 +30,7 @@ struct btrfs_block_group; struct btrfs_trans_handle; struct btrfs_transaction; struct btrfs_zoned_device_info; +struct btrfs_space_info; #define BTRFS_MAX_DATA_CHUNK_SIZE (10ULL * SZ_1G) @@ -58,7 +59,6 @@ static_assert(ilog2(BTRFS_STRIPE_LEN) == BTRFS_STRIPE_LEN_SHIFT); */ static_assert(const_ffs(BTRFS_BLOCK_GROUP_RAID0) < const_ffs(BTRFS_BLOCK_GROUP_PROFILE_MASK & ~BTRFS_BLOCK_GROUP_RAID0)); -static_assert(ilog2(BTRFS_BLOCK_GROUP_RAID0) > ilog2(BTRFS_BLOCK_GROUP_TYPE_MASK)); /* ilog2() can handle both constants and variables */ #define BTRFS_BG_FLAG_TO_INDEX(profile) \ @@ -80,6 +80,15 @@ enum btrfs_raid_types { BTRFS_NR_RAID_TYPES }; +static_assert(BTRFS_RAID_RAID0 == 1); +static_assert(BTRFS_RAID_RAID1 == 2); +static_assert(BTRFS_RAID_DUP == 3); +static_assert(BTRFS_RAID_RAID10 == 4); +static_assert(BTRFS_RAID_RAID5 == 5); +static_assert(BTRFS_RAID_RAID6 == 6); +static_assert(BTRFS_RAID_RAID1C3 == 7); +static_assert(BTRFS_RAID_RAID1C4 == 8); + /* * Use sequence counter to get consistent device stat data on * 32-bit processors. @@ -99,6 +108,10 @@ enum btrfs_raid_types { #define BTRFS_DEV_STATE_REPLACE_TGT (3) #define BTRFS_DEV_STATE_FLUSH_SENT (4) #define BTRFS_DEV_STATE_NO_READA (5) +#define BTRFS_DEV_STATE_FLUSH_FAILED (6) + +/* Set when the device item is found in chunk tree, used to catch unexpected registered device. */ +#define BTRFS_DEV_STATE_ITEM_FOUND (7) /* Special value encoding failure to write primary super block. */ #define BTRFS_SUPER_PRIMARY_WRITE_ERROR (INT_MAX / 2) @@ -122,13 +135,7 @@ struct btrfs_device { struct btrfs_zoned_device_info *zone_info; - /* - * Device's major-minor number. Must be set even if the device is not - * opened (bdev == NULL), unless the device is missing. - */ - dev_t devt; unsigned long dev_state; - blk_status_t last_flush_error; #ifdef __BTRFS_NEED_DEVICE_DATA_ORDERED seqcount_t data_seqcount; @@ -192,6 +199,12 @@ struct btrfs_device { atomic_t dev_stats_ccnt; atomic_t dev_stat_values[BTRFS_DEV_STAT_VALUES_MAX]; + /* + * Device's major-minor number. Must be set even if the device is not + * opened (bdev == NULL), unless the device is missing. + */ + dev_t devt; + struct extent_io_tree alloc_state; struct completion kobj_unregister; @@ -318,25 +331,6 @@ enum btrfs_read_policy { BTRFS_NR_READ_POLICY, }; -#ifdef CONFIG_BTRFS_EXPERIMENTAL -/* - * Checksum mode - offload it to workqueues or do it synchronously in - * btrfs_submit_chunk(). - */ -enum btrfs_offload_csum_mode { - /* - * Choose offloading checksum or do it synchronously automatically. - * Do it synchronously if the checksum is fast, or offload to workqueues - * otherwise. - */ - BTRFS_OFFLOAD_CSUM_AUTO, - /* Always offload checksum to workqueues. */ - BTRFS_OFFLOAD_CSUM_FORCE_ON, - /* Never offload checksum to workqueues. */ - BTRFS_OFFLOAD_CSUM_FORCE_OFF, -}; -#endif - struct btrfs_fs_devices { u8 fsid[BTRFS_FSID_SIZE]; /* FS specific uuid */ @@ -463,9 +457,6 @@ struct btrfs_fs_devices { /* Device to be used for reading in case of RAID1. */ u64 read_devid; - - /* Checksum mode - offload it or do it synchronously. */ - enum btrfs_offload_csum_mode offload_csum_mode; #endif }; @@ -643,6 +634,7 @@ static inline void btrfs_free_chunk_map(struct btrfs_chunk_map *map) kfree(map); } } +DEFINE_FREE(btrfs_free_chunk_map, struct btrfs_chunk_map *, btrfs_free_chunk_map(_T)) struct btrfs_balance_control { struct btrfs_balance_args data; @@ -724,7 +716,7 @@ int btrfs_map_repair_block(struct btrfs_fs_info *fs_info, u32 length, int mirror_num); struct btrfs_discard_stripe *btrfs_map_discard(struct btrfs_fs_info *fs_info, u64 logical, u64 *length_ret, - u32 *num_stripes); + u32 *num_stripes, bool do_remap); int btrfs_read_sys_array(struct btrfs_fs_info *fs_info); int btrfs_read_chunk_tree(struct btrfs_fs_info *fs_info); struct btrfs_block_group *btrfs_create_chunk(struct btrfs_trans_handle *trans, @@ -786,6 +778,7 @@ u64 btrfs_calc_stripe_length(const struct btrfs_chunk_map *map); int btrfs_nr_parity_stripes(u64 type); int btrfs_chunk_alloc_add_chunk_item(struct btrfs_trans_handle *trans, struct btrfs_block_group *bg); +int btrfs_remove_dev_extents(struct btrfs_trans_handle *trans, struct btrfs_chunk_map *map); int btrfs_remove_chunk(struct btrfs_trans_handle *trans, u64 chunk_offset); #ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS @@ -893,10 +886,18 @@ enum btrfs_raid_types __attribute_const__ btrfs_bg_flags_to_raid_index(u64 flags int btrfs_bg_type_to_factor(u64 flags); const char *btrfs_bg_type_to_raid_name(u64 flags); int btrfs_verify_dev_extents(struct btrfs_fs_info *fs_info); +bool btrfs_verify_dev_items(const struct btrfs_fs_info *fs_info); bool btrfs_repair_one_zone(struct btrfs_fs_info *fs_info, u64 logical); bool btrfs_pinned_by_swapfile(struct btrfs_fs_info *fs_info, void *ptr); const u8 *btrfs_sb_fsid_ptr(const struct btrfs_super_block *sb); +int btrfs_update_device(struct btrfs_trans_handle *trans, struct btrfs_device *device); +void btrfs_chunk_map_device_clear_bits(struct btrfs_chunk_map *map, unsigned int bits); + +bool btrfs_first_pending_extent(struct btrfs_device *device, u64 start, u64 len, + u64 *pending_start, u64 *pending_end); +bool btrfs_find_hole_in_pending_extents(struct btrfs_device *device, + u64 *start, u64 *len, u64 min_hole_size); #ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS struct btrfs_io_context *alloc_btrfs_io_context(struct btrfs_fs_info *fs_info, diff --git a/fs/btrfs/zlib.c b/fs/btrfs/zlib.c index 6caba8be7c84..0a8fcee16428 100644 --- a/fs/btrfs/zlib.c +++ b/fs/btrfs/zlib.c @@ -139,36 +139,30 @@ static int copy_data_into_buffer(struct address_space *mapping, data_in = kmap_local_folio(folio, offset); memcpy(workspace->buf + cur - filepos, data_in, copy_length); kunmap_local(data_in); + folio_put(folio); cur += copy_length; } return 0; } -int zlib_compress_folios(struct list_head *ws, struct btrfs_inode *inode, - u64 start, struct folio **folios, unsigned long *out_folios, - unsigned long *total_in, unsigned long *total_out) +int zlib_compress_bio(struct list_head *ws, struct compressed_bio *cb) { + struct btrfs_inode *inode = cb->bbio.inode; struct btrfs_fs_info *fs_info = inode->root->fs_info; struct workspace *workspace = list_entry(ws, struct workspace, list); struct address_space *mapping = inode->vfs_inode.i_mapping; - const u32 min_folio_shift = PAGE_SHIFT + fs_info->block_min_order; + struct bio *bio = &cb->bbio.bio; + u64 start = cb->start; + u32 len = cb->len; const u32 min_folio_size = btrfs_min_folio_size(fs_info); int ret; char *data_in = NULL; char *cfolio_out; - int nr_folios = 0; struct folio *in_folio = NULL; struct folio *out_folio = NULL; - unsigned long len = *total_out; - unsigned long nr_dest_folios = *out_folios; - const unsigned long max_out = nr_dest_folios << min_folio_shift; const u32 blocksize = fs_info->sectorsize; const u64 orig_end = start + len; - *out_folios = 0; - *total_out = 0; - *total_in = 0; - ret = zlib_deflateInit(&workspace->strm, workspace->level); if (unlikely(ret != Z_OK)) { btrfs_err(fs_info, @@ -187,8 +181,6 @@ int zlib_compress_folios(struct list_head *ws, struct btrfs_inode *inode, goto out; } cfolio_out = folio_address(out_folio); - folios[0] = out_folio; - nr_folios = 1; workspace->strm.next_in = workspace->buf; workspace->strm.avail_in = 0; @@ -197,8 +189,8 @@ int zlib_compress_folios(struct list_head *ws, struct btrfs_inode *inode, while (workspace->strm.total_in < len) { /* - * Get next input pages and copy the contents to - * the workspace buffer if required. + * Get next input pages and copy the contents to the workspace + * buffer if required. */ if (workspace->strm.avail_in == 0) { unsigned long bytes_left = len - workspace->strm.total_in; @@ -249,40 +241,39 @@ int zlib_compress_folios(struct list_head *ws, struct btrfs_inode *inode, goto out; } - /* we're making it bigger, give up */ + /* We're making it bigger, give up. */ if (workspace->strm.total_in > blocksize * 2 && - workspace->strm.total_in < - workspace->strm.total_out) { + workspace->strm.total_in < workspace->strm.total_out) { ret = -E2BIG; goto out; } - /* we need another page for writing out. Test this - * before the total_in so we will pull in a new page for - * the stream end if required - */ + if (workspace->strm.total_out >= len) { + ret = -E2BIG; + goto out; + } + /* Queue the full folio and allocate a new one. */ if (workspace->strm.avail_out == 0) { - if (nr_folios == nr_dest_folios) { + if (!bio_add_folio(bio, out_folio, folio_size(out_folio), 0)) { ret = -E2BIG; goto out; } + out_folio = btrfs_alloc_compr_folio(fs_info); if (out_folio == NULL) { ret = -ENOMEM; goto out; } cfolio_out = folio_address(out_folio); - folios[nr_folios] = out_folio; - nr_folios++; workspace->strm.avail_out = min_folio_size; workspace->strm.next_out = cfolio_out; } - /* we're all done */ + /* We're all done. */ if (workspace->strm.total_in >= len) break; - if (workspace->strm.total_out > max_out) - break; } + workspace->strm.avail_in = 0; + /* * Call deflate with Z_FINISH flush parameter providing more output * space but no more input data, until it returns with Z_STREAM_END. @@ -296,23 +287,39 @@ int zlib_compress_folios(struct list_head *ws, struct btrfs_inode *inode, ret = -EIO; goto out; } else if (workspace->strm.avail_out == 0) { - /* Get another folio for the stream end. */ - if (nr_folios == nr_dest_folios) { + if (workspace->strm.total_out >= len) { + ret = -E2BIG; + goto out; + } + if (!bio_add_folio(bio, out_folio, folio_size(out_folio), 0)) { ret = -E2BIG; goto out; } + /* Get another folio for the stream end. */ out_folio = btrfs_alloc_compr_folio(fs_info); if (out_folio == NULL) { ret = -ENOMEM; goto out; } cfolio_out = folio_address(out_folio); - folios[nr_folios] = out_folio; - nr_folios++; workspace->strm.avail_out = min_folio_size; workspace->strm.next_out = cfolio_out; } } + /* Queue the remaining part of the folio. */ + if (workspace->strm.total_out > bio->bi_iter.bi_size) { + u32 cur_len = offset_in_folio(out_folio, workspace->strm.total_out); + + if (!bio_add_folio(bio, out_folio, cur_len, 0)) { + ret = -E2BIG; + goto out; + } + } else { + /* The last folio hasn't' been utilized. */ + btrfs_free_compr_folio(out_folio); + } + out_folio = NULL; + ASSERT(bio->bi_iter.bi_size == workspace->strm.total_out); zlib_deflateEnd(&workspace->strm); if (workspace->strm.total_out >= workspace->strm.total_in) { @@ -321,10 +328,9 @@ int zlib_compress_folios(struct list_head *ws, struct btrfs_inode *inode, } ret = 0; - *total_out = workspace->strm.total_out; - *total_in = workspace->strm.total_in; out: - *out_folios = nr_folios; + if (out_folio) + btrfs_free_compr_folio(out_folio); if (data_in) { kunmap_local(data_in); folio_put(in_folio); @@ -337,18 +343,23 @@ int zlib_decompress_bio(struct list_head *ws, struct compressed_bio *cb) { struct btrfs_fs_info *fs_info = cb_to_fs_info(cb); struct workspace *workspace = list_entry(ws, struct workspace, list); + struct folio_iter fi; const u32 min_folio_size = btrfs_min_folio_size(fs_info); int ret = 0, ret2; int wbits = MAX_WBITS; char *data_in; size_t total_out = 0; - unsigned long folio_in_index = 0; size_t srclen = cb->compressed_len; - unsigned long total_folios_in = DIV_ROUND_UP(srclen, min_folio_size); unsigned long buf_start; - struct folio **folios_in = cb->compressed_folios; - data_in = kmap_local_folio(folios_in[folio_in_index], 0); + bio_first_folio(&fi, &cb->bbio.bio, 0); + + /* We must have at least one folio here, that has the correct size. */ + if (unlikely(!fi.folio)) + return -EINVAL; + ASSERT(folio_size(fi.folio) == min_folio_size); + + data_in = kmap_local_folio(fi.folio, 0); workspace->strm.next_in = data_in; workspace->strm.avail_in = min_t(size_t, srclen, min_folio_size); workspace->strm.total_in = 0; @@ -403,12 +414,13 @@ int zlib_decompress_bio(struct list_head *ws, struct compressed_bio *cb) if (workspace->strm.avail_in == 0) { unsigned long tmp; kunmap_local(data_in); - folio_in_index++; - if (folio_in_index >= total_folios_in) { + bio_next_folio(&fi, &cb->bbio.bio); + if (!fi.folio) { data_in = NULL; break; } - data_in = kmap_local_folio(folios_in[folio_in_index], 0); + ASSERT(folio_size(fi.folio) == min_folio_size); + data_in = kmap_local_folio(fi.folio, 0); workspace->strm.next_in = data_in; tmp = srclen - workspace->strm.total_in; workspace->strm.avail_in = min(tmp, min_folio_size); diff --git a/fs/btrfs/zoned.c b/fs/btrfs/zoned.c index 359a98e6de85..ad8621587fd2 100644 --- a/fs/btrfs/zoned.c +++ b/fs/btrfs/zoned.c @@ -1231,6 +1231,7 @@ static int calculate_alloc_pointer(struct btrfs_block_group *cache, BTRFS_PATH_AUTO_FREE(path); struct btrfs_key key; struct btrfs_key found_key; + const u64 bg_end = btrfs_block_group_end(cache); int ret; u64 length; @@ -1253,7 +1254,7 @@ static int calculate_alloc_pointer(struct btrfs_block_group *cache, if (!path) return -ENOMEM; - key.objectid = cache->start + cache->length; + key.objectid = bg_end; key.type = 0; key.offset = 0; @@ -1282,7 +1283,7 @@ static int calculate_alloc_pointer(struct btrfs_block_group *cache, length = fs_info->nodesize; if (unlikely(!(found_key.objectid >= cache->start && - found_key.objectid + length <= cache->start + cache->length))) { + found_key.objectid + length <= bg_end))) { return -EUCLEAN; } *offset_ret = found_key.objectid + length - cache->start; @@ -1437,18 +1438,32 @@ static int btrfs_load_block_group_dup(struct btrfs_block_group *bg, bg->zone_capacity = min_not_zero(zone_info[0].capacity, zone_info[1].capacity); if (unlikely(zone_info[0].alloc_offset == WP_MISSING_DEV)) { - btrfs_err(bg->fs_info, + btrfs_err(fs_info, "zoned: cannot recover write pointer for zone %llu", zone_info[0].physical); return -EIO; } if (unlikely(zone_info[1].alloc_offset == WP_MISSING_DEV)) { - btrfs_err(bg->fs_info, + btrfs_err(fs_info, "zoned: cannot recover write pointer for zone %llu", zone_info[1].physical); return -EIO; } + /* + * When the last extent is removed, last_alloc can be smaller than the other write + * pointer. In that case, last_alloc should be moved to the corresponding write + * pointer position. + */ + for (int i = 0; i < map->num_stripes; i++) { + if (zone_info[i].alloc_offset == WP_CONVENTIONAL) + continue; + if (last_alloc <= zone_info[i].alloc_offset) { + last_alloc = zone_info[i].alloc_offset; + break; + } + } + if (zone_info[0].alloc_offset == WP_CONVENTIONAL) zone_info[0].alloc_offset = last_alloc; @@ -1456,7 +1471,7 @@ static int btrfs_load_block_group_dup(struct btrfs_block_group *bg, zone_info[1].alloc_offset = last_alloc; if (unlikely(zone_info[0].alloc_offset != zone_info[1].alloc_offset)) { - btrfs_err(bg->fs_info, + btrfs_err(fs_info, "zoned: write pointer offset mismatch of zones in DUP profile"); return -EIO; } @@ -1490,6 +1505,21 @@ static int btrfs_load_block_group_raid1(struct btrfs_block_group *bg, /* In case a device is missing we have a cap of 0, so don't use it. */ bg->zone_capacity = min_not_zero(zone_info[0].capacity, zone_info[1].capacity); + /* + * When the last extent is removed, last_alloc can be smaller than the other write + * pointer. In that case, last_alloc should be moved to the corresponding write + * pointer position. + */ + for (i = 0; i < map->num_stripes; i++) { + if (zone_info[i].alloc_offset == WP_MISSING_DEV || + zone_info[i].alloc_offset == WP_CONVENTIONAL) + continue; + if (last_alloc <= zone_info[i].alloc_offset) { + last_alloc = zone_info[i].alloc_offset; + break; + } + } + for (i = 0; i < map->num_stripes; i++) { if (zone_info[i].alloc_offset == WP_MISSING_DEV) continue; @@ -1531,7 +1561,9 @@ static int btrfs_load_block_group_raid0(struct btrfs_block_group *bg, { struct btrfs_fs_info *fs_info = bg->fs_info; u64 stripe_nr = 0, stripe_offset = 0; + u64 prev_offset = 0; u32 stripe_index = 0; + bool has_partial = false, has_conventional = false; if ((map->type & BTRFS_BLOCK_GROUP_DATA) && !fs_info->stripe_root) { btrfs_err(fs_info, "zoned: data %s needs raid-stripe-tree", @@ -1539,6 +1571,35 @@ static int btrfs_load_block_group_raid0(struct btrfs_block_group *bg, return -EINVAL; } + /* + * When the last extent is removed, last_alloc can be smaller than the other write + * pointer. In that case, last_alloc should be moved to the corresponding write + * pointer position. + */ + for (int i = 0; i < map->num_stripes; i++) { + u64 alloc; + + if (zone_info[i].alloc_offset == WP_MISSING_DEV || + zone_info[i].alloc_offset == WP_CONVENTIONAL) + continue; + + stripe_nr = zone_info[i].alloc_offset >> BTRFS_STRIPE_LEN_SHIFT; + stripe_offset = zone_info[i].alloc_offset & BTRFS_STRIPE_LEN_MASK; + if (stripe_offset == 0 && stripe_nr > 0) { + stripe_nr--; + stripe_offset = BTRFS_STRIPE_LEN; + } + alloc = ((stripe_nr * map->num_stripes + i) << BTRFS_STRIPE_LEN_SHIFT) + + stripe_offset; + last_alloc = max(last_alloc, alloc); + + /* Partially written stripe found. It should be last. */ + if (zone_info[i].alloc_offset & BTRFS_STRIPE_LEN_MASK) + break; + } + stripe_nr = 0; + stripe_offset = 0; + if (last_alloc) { u32 factor = map->num_stripes; @@ -1552,7 +1613,7 @@ static int btrfs_load_block_group_raid0(struct btrfs_block_group *bg, continue; if (zone_info[i].alloc_offset == WP_CONVENTIONAL) { - + has_conventional = true; zone_info[i].alloc_offset = btrfs_stripe_nr_to_offset(stripe_nr); if (stripe_index > i) @@ -1561,6 +1622,28 @@ static int btrfs_load_block_group_raid0(struct btrfs_block_group *bg, zone_info[i].alloc_offset += stripe_offset; } + /* Verification */ + if (i != 0) { + if (unlikely(prev_offset < zone_info[i].alloc_offset)) { + btrfs_err(fs_info, + "zoned: stripe position disorder found in block group %llu", + bg->start); + return -EIO; + } + + if (unlikely(has_partial && + (zone_info[i].alloc_offset & BTRFS_STRIPE_LEN_MASK))) { + btrfs_err(fs_info, + "zoned: multiple partial written stripe found in block group %llu", + bg->start); + return -EIO; + } + } + prev_offset = zone_info[i].alloc_offset; + + if ((zone_info[i].alloc_offset & BTRFS_STRIPE_LEN_MASK) != 0) + has_partial = true; + if (test_bit(0, active) != test_bit(i, active)) { if (unlikely(!btrfs_zone_activate(bg))) return -EIO; @@ -1572,6 +1655,19 @@ static int btrfs_load_block_group_raid0(struct btrfs_block_group *bg, bg->alloc_offset += zone_info[i].alloc_offset; } + /* Check if all devices stay in the same stripe row. */ + if (unlikely(zone_info[0].alloc_offset - + zone_info[map->num_stripes - 1].alloc_offset > BTRFS_STRIPE_LEN)) { + btrfs_err(fs_info, "zoned: stripe gap too large in block group %llu", bg->start); + return -EIO; + } + + if (unlikely(has_conventional && bg->alloc_offset < last_alloc)) { + btrfs_err(fs_info, "zoned: allocated extent stays beyond write pointers %llu %llu", + bg->alloc_offset, last_alloc); + return -EIO; + } + return 0; } @@ -1582,8 +1678,11 @@ static int btrfs_load_block_group_raid10(struct btrfs_block_group *bg, u64 last_alloc) { struct btrfs_fs_info *fs_info = bg->fs_info; + u64 AUTO_KFREE(raid0_allocs); u64 stripe_nr = 0, stripe_offset = 0; u32 stripe_index = 0; + bool has_partial = false, has_conventional = false; + u64 prev_offset = 0; if ((map->type & BTRFS_BLOCK_GROUP_DATA) && !fs_info->stripe_root) { btrfs_err(fs_info, "zoned: data %s needs raid-stripe-tree", @@ -1591,6 +1690,60 @@ static int btrfs_load_block_group_raid10(struct btrfs_block_group *bg, return -EINVAL; } + raid0_allocs = kcalloc(map->num_stripes / map->sub_stripes, sizeof(*raid0_allocs), + GFP_NOFS); + if (!raid0_allocs) + return -ENOMEM; + + /* + * When the last extent is removed, last_alloc can be smaller than the other write + * pointer. In that case, last_alloc should be moved to the corresponding write + * pointer position. + */ + for (int i = 0; i < map->num_stripes; i += map->sub_stripes) { + u64 alloc = zone_info[i].alloc_offset; + + for (int j = 1; j < map->sub_stripes; j++) { + int idx = i + j; + + if (zone_info[idx].alloc_offset == WP_MISSING_DEV || + zone_info[idx].alloc_offset == WP_CONVENTIONAL) + continue; + if (alloc == WP_MISSING_DEV || alloc == WP_CONVENTIONAL) { + alloc = zone_info[idx].alloc_offset; + } else if (unlikely(zone_info[idx].alloc_offset != alloc)) { + btrfs_err(fs_info, + "zoned: write pointer mismatch found in block group %llu", + bg->start); + return -EIO; + } + } + + raid0_allocs[i / map->sub_stripes] = alloc; + if (alloc == WP_CONVENTIONAL) + continue; + if (unlikely(alloc == WP_MISSING_DEV)) { + btrfs_err(fs_info, + "zoned: cannot recover write pointer of block group %llu due to missing device", + bg->start); + return -EIO; + } + + stripe_nr = alloc >> BTRFS_STRIPE_LEN_SHIFT; + stripe_offset = alloc & BTRFS_STRIPE_LEN_MASK; + if (stripe_offset == 0 && stripe_nr > 0) { + stripe_nr--; + stripe_offset = BTRFS_STRIPE_LEN; + } + + alloc = ((stripe_nr * (map->num_stripes / map->sub_stripes) + + (i / map->sub_stripes)) << + BTRFS_STRIPE_LEN_SHIFT) + stripe_offset; + last_alloc = max(last_alloc, alloc); + } + stripe_nr = 0; + stripe_offset = 0; + if (last_alloc) { u32 factor = map->num_stripes / map->sub_stripes; @@ -1600,24 +1753,51 @@ static int btrfs_load_block_group_raid10(struct btrfs_block_group *bg, } for (int i = 0; i < map->num_stripes; i++) { - if (zone_info[i].alloc_offset == WP_MISSING_DEV) - continue; + int idx = i / map->sub_stripes; - if (test_bit(0, active) != test_bit(i, active)) { - if (unlikely(!btrfs_zone_activate(bg))) - return -EIO; - } else { - if (test_bit(0, active)) - set_bit(BLOCK_GROUP_FLAG_ZONE_IS_ACTIVE, &bg->runtime_flags); + if (raid0_allocs[idx] == WP_CONVENTIONAL) { + has_conventional = true; + raid0_allocs[idx] = btrfs_stripe_nr_to_offset(stripe_nr); + + if (stripe_index > idx) + raid0_allocs[idx] += BTRFS_STRIPE_LEN; + else if (stripe_index == idx) + raid0_allocs[idx] += stripe_offset; } - if (zone_info[i].alloc_offset == WP_CONVENTIONAL) { - zone_info[i].alloc_offset = btrfs_stripe_nr_to_offset(stripe_nr); + if ((i % map->sub_stripes) == 0) { + /* Verification */ + if (i != 0) { + if (unlikely(prev_offset < raid0_allocs[idx])) { + btrfs_err(fs_info, + "zoned: stripe position disorder found in block group %llu", + bg->start); + return -EIO; + } - if (stripe_index > (i / map->sub_stripes)) - zone_info[i].alloc_offset += BTRFS_STRIPE_LEN; - else if (stripe_index == (i / map->sub_stripes)) - zone_info[i].alloc_offset += stripe_offset; + if (unlikely(has_partial && + (raid0_allocs[idx] & BTRFS_STRIPE_LEN_MASK))) { + btrfs_err(fs_info, + "zoned: multiple partial written stripe found in block group %llu", + bg->start); + return -EIO; + } + } + prev_offset = raid0_allocs[idx]; + + if ((raid0_allocs[idx] & BTRFS_STRIPE_LEN_MASK) != 0) + has_partial = true; + } + + if (zone_info[i].alloc_offset == WP_MISSING_DEV || + zone_info[i].alloc_offset == WP_CONVENTIONAL) + zone_info[i].alloc_offset = raid0_allocs[idx]; + + if (test_bit(0, active) != test_bit(i, active)) { + if (unlikely(!btrfs_zone_activate(bg))) + return -EIO; + } else if (test_bit(0, active)) { + set_bit(BLOCK_GROUP_FLAG_ZONE_IS_ACTIVE, &bg->runtime_flags); } if ((i % map->sub_stripes) == 0) { @@ -1626,9 +1806,79 @@ static int btrfs_load_block_group_raid10(struct btrfs_block_group *bg, } } + /* Check if all devices stay in the same stripe row. */ + if (unlikely(zone_info[0].alloc_offset - + zone_info[map->num_stripes - 1].alloc_offset > BTRFS_STRIPE_LEN)) { + btrfs_err(fs_info, "zoned: stripe gap too large in block group %llu", + bg->start); + return -EIO; + } + + if (unlikely(has_conventional && bg->alloc_offset < last_alloc)) { + btrfs_err(fs_info, "zoned: allocated extent stays beyond write pointers %llu %llu", + bg->alloc_offset, last_alloc); + return -EIO; + } + return 0; } +EXPORT_FOR_TESTS +int btrfs_load_block_group_by_raid_type(struct btrfs_block_group *bg, + struct btrfs_chunk_map *map, + struct zone_info *zone_info, + unsigned long *active, u64 last_alloc) +{ + struct btrfs_fs_info *fs_info = bg->fs_info; + u64 profile; + int ret; + + profile = map->type & BTRFS_BLOCK_GROUP_PROFILE_MASK; + switch (profile) { + case 0: /* single */ + ret = btrfs_load_block_group_single(bg, &zone_info[0], active); + break; + case BTRFS_BLOCK_GROUP_DUP: + ret = btrfs_load_block_group_dup(bg, map, zone_info, active, last_alloc); + break; + case BTRFS_BLOCK_GROUP_RAID1: + case BTRFS_BLOCK_GROUP_RAID1C3: + case BTRFS_BLOCK_GROUP_RAID1C4: + ret = btrfs_load_block_group_raid1(bg, map, zone_info, active, last_alloc); + break; + case BTRFS_BLOCK_GROUP_RAID0: + ret = btrfs_load_block_group_raid0(bg, map, zone_info, active, last_alloc); + break; + case BTRFS_BLOCK_GROUP_RAID10: + ret = btrfs_load_block_group_raid10(bg, map, zone_info, active, last_alloc); + break; + case BTRFS_BLOCK_GROUP_RAID5: + case BTRFS_BLOCK_GROUP_RAID6: + default: + btrfs_err(fs_info, "zoned: profile %s not yet supported", + btrfs_bg_type_to_raid_name(map->type)); + return -EINVAL; + } + + if (ret == -EIO && profile != 0 && profile != BTRFS_BLOCK_GROUP_RAID0 && + profile != BTRFS_BLOCK_GROUP_RAID10) { + /* + * Detected broken write pointer. Make this block group + * unallocatable by setting the allocation pointer at the end of + * allocatable region. Relocating this block group will fix the + * mismatch. + * + * Currently, we cannot handle RAID0 or RAID10 case like this + * because we don't have a proper zone_capacity value. But, + * reading from this block group won't work anyway by a missing + * stripe. + */ + bg->alloc_offset = bg->zone_capacity; + } + + return ret; +} + int btrfs_load_block_group_zone_info(struct btrfs_block_group *cache, bool new) { struct btrfs_fs_info *fs_info = cache->fs_info; @@ -1641,7 +1891,6 @@ int btrfs_load_block_group_zone_info(struct btrfs_block_group *cache, bool new) unsigned long *active = NULL; u64 last_alloc = 0; u32 num_sequential = 0, num_conventional = 0; - u64 profile; if (!btrfs_is_zoned(fs_info)) return 0; @@ -1701,53 +1950,7 @@ int btrfs_load_block_group_zone_info(struct btrfs_block_group *cache, bool new) } } - profile = map->type & BTRFS_BLOCK_GROUP_PROFILE_MASK; - switch (profile) { - case 0: /* single */ - ret = btrfs_load_block_group_single(cache, &zone_info[0], active); - break; - case BTRFS_BLOCK_GROUP_DUP: - ret = btrfs_load_block_group_dup(cache, map, zone_info, active, - last_alloc); - break; - case BTRFS_BLOCK_GROUP_RAID1: - case BTRFS_BLOCK_GROUP_RAID1C3: - case BTRFS_BLOCK_GROUP_RAID1C4: - ret = btrfs_load_block_group_raid1(cache, map, zone_info, - active, last_alloc); - break; - case BTRFS_BLOCK_GROUP_RAID0: - ret = btrfs_load_block_group_raid0(cache, map, zone_info, - active, last_alloc); - break; - case BTRFS_BLOCK_GROUP_RAID10: - ret = btrfs_load_block_group_raid10(cache, map, zone_info, - active, last_alloc); - break; - case BTRFS_BLOCK_GROUP_RAID5: - case BTRFS_BLOCK_GROUP_RAID6: - default: - btrfs_err(fs_info, "zoned: profile %s not yet supported", - btrfs_bg_type_to_raid_name(map->type)); - ret = -EINVAL; - goto out; - } - - if (ret == -EIO && profile != 0 && profile != BTRFS_BLOCK_GROUP_RAID0 && - profile != BTRFS_BLOCK_GROUP_RAID10) { - /* - * Detected broken write pointer. Make this block group - * unallocatable by setting the allocation pointer at the end of - * allocatable region. Relocating this block group will fix the - * mismatch. - * - * Currently, we cannot handle RAID0 or RAID10 case like this - * because we don't have a proper zone_capacity value. But, - * reading from this block group won't work anyway by a missing - * stripe. - */ - cache->alloc_offset = cache->zone_capacity; - } + ret = btrfs_load_block_group_by_raid_type(cache, map, zone_info, active, last_alloc); out: /* Reject non SINGLE data profiles without RST */ @@ -2028,7 +2231,7 @@ int btrfs_check_meta_write_pointer(struct btrfs_fs_info *fs_info, if (block_group) { if (block_group->start > eb->start || - block_group->start + block_group->length <= eb->start) { + btrfs_block_group_end(block_group) <= eb->start) { btrfs_put_block_group(block_group); block_group = NULL; ctx->zoned_bg = NULL; @@ -2248,7 +2451,7 @@ out_unlock: static void wait_eb_writebacks(struct btrfs_block_group *block_group) { struct btrfs_fs_info *fs_info = block_group->fs_info; - const u64 end = block_group->start + block_group->length; + const u64 end = btrfs_block_group_end(block_group); struct extent_buffer *eb; unsigned long index, start = (block_group->start >> fs_info->nodesize_bits); @@ -2984,3 +3187,58 @@ int btrfs_reset_unused_block_groups(struct btrfs_space_info *space_info, u64 num return 0; } + +void btrfs_show_zoned_stats(struct btrfs_fs_info *fs_info, struct seq_file *seq) +{ + struct btrfs_block_group *bg; + u64 data_reloc_bg; + u64 treelog_bg; + + seq_puts(seq, "\n zoned statistics:\n"); + + spin_lock(&fs_info->zone_active_bgs_lock); + seq_printf(seq, "\tactive block-groups: %zu\n", + list_count_nodes(&fs_info->zone_active_bgs)); + spin_unlock(&fs_info->zone_active_bgs_lock); + + spin_lock(&fs_info->unused_bgs_lock); + seq_printf(seq, "\t reclaimable: %zu\n", + list_count_nodes(&fs_info->reclaim_bgs)); + seq_printf(seq, "\t unused: %zu\n", list_count_nodes(&fs_info->unused_bgs)); + spin_unlock(&fs_info->unused_bgs_lock); + + seq_printf(seq,"\t need reclaim: %s\n", + str_true_false(btrfs_zoned_should_reclaim(fs_info))); + + data_reloc_bg = data_race(fs_info->data_reloc_bg); + if (data_reloc_bg) + seq_printf(seq, "\tdata relocation block-group: %llu\n", + data_reloc_bg); + treelog_bg = data_race(fs_info->treelog_bg); + if (treelog_bg) + seq_printf(seq, "\ttree-log block-group: %llu\n", treelog_bg); + + spin_lock(&fs_info->zone_active_bgs_lock); + seq_puts(seq, "\tactive zones:\n"); + list_for_each_entry(bg, &fs_info->zone_active_bgs, active_bg_list) { + u64 start; + u64 alloc_offset; + u64 used; + u64 reserved; + u64 zone_unusable; + const char *typestr = btrfs_space_info_type_str(bg->space_info); + + spin_lock(&bg->lock); + start = bg->start; + alloc_offset = bg->alloc_offset; + used = bg->used; + reserved = bg->reserved; + zone_unusable = bg->zone_unusable; + spin_unlock(&bg->lock); + + seq_printf(seq, + "\t start: %llu, wp: %llu used: %llu, reserved: %llu, unusable: %llu (%s)\n", + start, alloc_offset, used, reserved, zone_unusable, typestr); + } + spin_unlock(&fs_info->zone_active_bgs_lock); +} diff --git a/fs/btrfs/zoned.h b/fs/btrfs/zoned.h index 5cefdeb08b7b..8e21a836f858 100644 --- a/fs/btrfs/zoned.h +++ b/fs/btrfs/zoned.h @@ -10,6 +10,7 @@ #include <linux/errno.h> #include <linux/spinlock.h> #include <linux/mutex.h> +#include <linux/seq_file.h> #include "messages.h" #include "volumes.h" #include "disk-io.h" @@ -96,6 +97,17 @@ int btrfs_zone_finish_one_bg(struct btrfs_fs_info *fs_info); int btrfs_zoned_activate_one_bg(struct btrfs_space_info *space_info, bool do_finish); void btrfs_check_active_zone_reservation(struct btrfs_fs_info *fs_info); int btrfs_reset_unused_block_groups(struct btrfs_space_info *space_info, u64 num_bytes); +void btrfs_show_zoned_stats(struct btrfs_fs_info *fs_info, struct seq_file *seq); + +#ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS +struct zone_info; + +int btrfs_load_block_group_by_raid_type(struct btrfs_block_group *bg, + struct btrfs_chunk_map *map, + struct zone_info *zone_info, + unsigned long *active, u64 last_alloc); +#endif + #else /* CONFIG_BLK_DEV_ZONED */ static inline int btrfs_get_dev_zone_info_all_devices(struct btrfs_fs_info *fs_info) @@ -275,6 +287,11 @@ static inline int btrfs_reset_unused_block_groups(struct btrfs_space_info *space return 0; } +static inline int btrfs_show_zoned_stats(struct btrfs_fs_info *fs_info, struct seq_file *seq) +{ + return 0; +} + #endif static inline bool btrfs_dev_is_sequential(struct btrfs_device *device, u64 pos) diff --git a/fs/btrfs/zstd.c b/fs/btrfs/zstd.c index c9cddcfa337b..32fd7f5454d3 100644 --- a/fs/btrfs/zstd.c +++ b/fs/btrfs/zstd.c @@ -396,36 +396,31 @@ fail: return ERR_PTR(-ENOMEM); } -int zstd_compress_folios(struct list_head *ws, struct btrfs_inode *inode, - u64 start, struct folio **folios, unsigned long *out_folios, - unsigned long *total_in, unsigned long *total_out) +int zstd_compress_bio(struct list_head *ws, struct compressed_bio *cb) { + struct btrfs_inode *inode = cb->bbio.inode; struct btrfs_fs_info *fs_info = inode->root->fs_info; struct workspace *workspace = list_entry(ws, struct workspace, list); struct address_space *mapping = inode->vfs_inode.i_mapping; + struct bio *bio = &cb->bbio.bio; zstd_cstream *stream; int ret = 0; - int nr_folios = 0; - struct folio *in_folio = NULL; /* The current folio to read. */ - struct folio *out_folio = NULL; /* The current folio to write to. */ + /* The current folio to read. */ + struct folio *in_folio = NULL; + /* The current folio to write to. */ + struct folio *out_folio = NULL; unsigned long tot_in = 0; unsigned long tot_out = 0; - unsigned long len = *total_out; - const unsigned long nr_dest_folios = *out_folios; - const u64 orig_end = start + len; + const u64 start = cb->start; + const u32 len = cb->len; + const u64 end = start + len; const u32 blocksize = fs_info->sectorsize; const u32 min_folio_size = btrfs_min_folio_size(fs_info); - unsigned long max_out = nr_dest_folios * min_folio_size; - unsigned int cur_len; workspace->params = zstd_get_btrfs_parameters(workspace->req_level, len); - *out_folios = 0; - *total_out = 0; - *total_in = 0; - /* Initialize the stream */ - stream = zstd_init_cstream(&workspace->params, len, workspace->mem, - workspace->size); + /* Initialize the stream. */ + stream = zstd_init_cstream(&workspace->params, len, workspace->mem, workspace->size); if (unlikely(!stream)) { btrfs_err(fs_info, "zstd compression init level %d failed, root %llu inode %llu offset %llu", @@ -435,99 +430,95 @@ int zstd_compress_folios(struct list_head *ws, struct btrfs_inode *inode, goto out; } - /* map in the first page of input data */ + /* Map in the first page of input data. */ ret = btrfs_compress_filemap_get_folio(mapping, start, &in_folio); if (ret < 0) goto out; - cur_len = btrfs_calc_input_length(in_folio, orig_end, start); workspace->in_buf.src = kmap_local_folio(in_folio, offset_in_folio(in_folio, start)); workspace->in_buf.pos = 0; - workspace->in_buf.size = cur_len; + workspace->in_buf.size = btrfs_calc_input_length(in_folio, end, start); - /* Allocate and map in the output buffer */ + /* Allocate and map in the output buffer. */ out_folio = btrfs_alloc_compr_folio(fs_info); if (out_folio == NULL) { ret = -ENOMEM; goto out; } - folios[nr_folios++] = out_folio; workspace->out_buf.dst = folio_address(out_folio); workspace->out_buf.pos = 0; - workspace->out_buf.size = min_t(size_t, max_out, min_folio_size); + workspace->out_buf.size = min_folio_size; while (1) { size_t ret2; - ret2 = zstd_compress_stream(stream, &workspace->out_buf, - &workspace->in_buf); + ret2 = zstd_compress_stream(stream, &workspace->out_buf, &workspace->in_buf); if (unlikely(zstd_is_error(ret2))) { btrfs_warn(fs_info, "zstd compression level %d failed, error %d root %llu inode %llu offset %llu", workspace->req_level, zstd_get_error_code(ret2), btrfs_root_id(inode->root), btrfs_ino(inode), - start); + start + tot_in); ret = -EIO; goto out; } - /* Check to see if we are making it bigger */ + /* Check to see if we are making it bigger. */ if (tot_in + workspace->in_buf.pos > blocksize * 2 && - tot_in + workspace->in_buf.pos < - tot_out + workspace->out_buf.pos) { + tot_in + workspace->in_buf.pos < tot_out + workspace->out_buf.pos) { ret = -E2BIG; goto out; } - /* We've reached the end of our output range */ - if (workspace->out_buf.pos >= max_out) { - tot_out += workspace->out_buf.pos; - ret = -E2BIG; - goto out; - } - - /* Check if we need more output space */ - if (workspace->out_buf.pos == workspace->out_buf.size) { + /* Check if we need more output space. */ + if (workspace->out_buf.pos >= workspace->out_buf.size) { tot_out += min_folio_size; - max_out -= min_folio_size; - if (nr_folios == nr_dest_folios) { + if (tot_out >= len) { ret = -E2BIG; goto out; } + /* Queue the current foliot into the bio. */ + if (!bio_add_folio(bio, out_folio, folio_size(out_folio), 0)) { + ret = -E2BIG; + goto out; + } + out_folio = btrfs_alloc_compr_folio(fs_info); if (out_folio == NULL) { ret = -ENOMEM; goto out; } - folios[nr_folios++] = out_folio; workspace->out_buf.dst = folio_address(out_folio); workspace->out_buf.pos = 0; - workspace->out_buf.size = min_t(size_t, max_out, min_folio_size); + workspace->out_buf.size = min_folio_size; } - /* We've reached the end of the input */ - if (workspace->in_buf.pos >= len) { + /* We've reached the end of the input. */ + if (tot_in + workspace->in_buf.pos >= len) { tot_in += workspace->in_buf.pos; break; } - /* Check if we need more input */ - if (workspace->in_buf.pos == workspace->in_buf.size) { + /* Check if we need more input. */ + if (workspace->in_buf.pos >= workspace->in_buf.size) { + u64 cur; + tot_in += workspace->in_buf.size; + cur = start + tot_in; + kunmap_local(workspace->in_buf.src); workspace->in_buf.src = NULL; folio_put(in_folio); - start += cur_len; - len -= cur_len; - ret = btrfs_compress_filemap_get_folio(mapping, start, &in_folio); + + ret = btrfs_compress_filemap_get_folio(mapping, cur, &in_folio); if (ret < 0) goto out; - cur_len = btrfs_calc_input_length(in_folio, orig_end, start); workspace->in_buf.src = kmap_local_folio(in_folio, - offset_in_folio(in_folio, start)); + offset_in_folio(in_folio, cur)); workspace->in_buf.pos = 0; - workspace->in_buf.size = cur_len; + workspace->in_buf.size = btrfs_calc_input_length(in_folio, end, cur); } } + while (1) { size_t ret2; @@ -537,23 +528,30 @@ int zstd_compress_folios(struct list_head *ws, struct btrfs_inode *inode, "zstd compression end level %d failed, error %d root %llu inode %llu offset %llu", workspace->req_level, zstd_get_error_code(ret2), btrfs_root_id(inode->root), btrfs_ino(inode), - start); + start + tot_in); ret = -EIO; goto out; } + /* Queue the remaining part of the output folio into bio. */ if (ret2 == 0) { tot_out += workspace->out_buf.pos; + if (tot_out >= len) { + ret = -E2BIG; + goto out; + } + if (!bio_add_folio(bio, out_folio, workspace->out_buf.pos, 0)) { + ret = -E2BIG; + goto out; + } + out_folio = NULL; break; } - if (workspace->out_buf.pos >= max_out) { - tot_out += workspace->out_buf.pos; + tot_out += min_folio_size; + if (tot_out >= len) { ret = -E2BIG; goto out; } - - tot_out += min_folio_size; - max_out -= min_folio_size; - if (nr_folios == nr_dest_folios) { + if (!bio_add_folio(bio, out_folio, folio_size(out_folio), 0)) { ret = -E2BIG; goto out; } @@ -562,10 +560,9 @@ int zstd_compress_folios(struct list_head *ws, struct btrfs_inode *inode, ret = -ENOMEM; goto out; } - folios[nr_folios++] = out_folio; workspace->out_buf.dst = folio_address(out_folio); workspace->out_buf.pos = 0; - workspace->out_buf.size = min_t(size_t, max_out, min_folio_size); + workspace->out_buf.size = min_folio_size; } if (tot_out >= tot_in) { @@ -574,10 +571,10 @@ int zstd_compress_folios(struct list_head *ws, struct btrfs_inode *inode, } ret = 0; - *total_in = tot_in; - *total_out = tot_out; + ASSERT(tot_out == bio->bi_iter.bi_size); out: - *out_folios = nr_folios; + if (out_folio) + btrfs_free_compr_folio(out_folio); if (workspace->in_buf.src) { kunmap_local(workspace->in_buf.src); folio_put(in_folio); @@ -589,7 +586,7 @@ int zstd_decompress_bio(struct list_head *ws, struct compressed_bio *cb) { struct btrfs_fs_info *fs_info = cb_to_fs_info(cb); struct workspace *workspace = list_entry(ws, struct workspace, list); - struct folio **folios_in = cb->compressed_folios; + struct folio_iter fi; size_t srclen = cb->compressed_len; zstd_dstream *stream; int ret = 0; @@ -600,6 +597,11 @@ int zstd_decompress_bio(struct list_head *ws, struct compressed_bio *cb) unsigned long buf_start; unsigned long total_out = 0; + bio_first_folio(&fi, &cb->bbio.bio, 0); + if (unlikely(!fi.folio)) + return -EINVAL; + ASSERT(folio_size(fi.folio) == blocksize); + stream = zstd_init_dstream( ZSTD_BTRFS_MAX_INPUT, workspace->mem, workspace->size); if (unlikely(!stream)) { @@ -612,7 +614,7 @@ int zstd_decompress_bio(struct list_head *ws, struct compressed_bio *cb) goto done; } - workspace->in_buf.src = kmap_local_folio(folios_in[folio_in_index], 0); + workspace->in_buf.src = kmap_local_folio(fi.folio, 0); workspace->in_buf.pos = 0; workspace->in_buf.size = min_t(size_t, srclen, min_folio_size); @@ -660,8 +662,9 @@ int zstd_decompress_bio(struct list_head *ws, struct compressed_bio *cb) goto done; } srclen -= min_folio_size; - workspace->in_buf.src = - kmap_local_folio(folios_in[folio_in_index], 0); + bio_next_folio(&fi, &cb->bbio.bio); + ASSERT(fi.folio); + workspace->in_buf.src = kmap_local_folio(fi.folio, 0); workspace->in_buf.pos = 0; workspace->in_buf.size = min_t(size_t, srclen, min_folio_size); } diff --git a/fs/buffer.c b/fs/buffer.c index 838c0c571022..ed724a902657 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -29,6 +29,7 @@ #include <linux/slab.h> #include <linux/capability.h> #include <linux/blkdev.h> +#include <linux/blk-crypto.h> #include <linux/file.h> #include <linux/quotaops.h> #include <linux/highmem.h> @@ -302,6 +303,7 @@ still_busy: struct postprocess_bh_ctx { struct work_struct work; struct buffer_head *bh; + struct fsverity_info *vi; }; static void verify_bh(struct work_struct *work) @@ -311,21 +313,12 @@ static void verify_bh(struct work_struct *work) struct buffer_head *bh = ctx->bh; bool valid; - valid = fsverity_verify_blocks(bh->b_folio, bh->b_size, bh_offset(bh)); + valid = fsverity_verify_blocks(ctx->vi, bh->b_folio, bh->b_size, + bh_offset(bh)); end_buffer_async_read(bh, valid); kfree(ctx); } -static bool need_fsverity(struct buffer_head *bh) -{ - struct folio *folio = bh->b_folio; - struct inode *inode = folio->mapping->host; - - return fsverity_active(inode) && - /* needed by ext4 */ - folio->index < DIV_ROUND_UP(inode->i_size, PAGE_SIZE); -} - static void decrypt_bh(struct work_struct *work) { struct postprocess_bh_ctx *ctx = @@ -335,7 +328,7 @@ static void decrypt_bh(struct work_struct *work) err = fscrypt_decrypt_pagecache_blocks(bh->b_folio, bh->b_size, bh_offset(bh)); - if (err == 0 && need_fsverity(bh)) { + if (err == 0 && ctx->vi) { /* * We use different work queues for decryption and for verity * because verity may require reading metadata pages that need @@ -357,15 +350,20 @@ static void end_buffer_async_read_io(struct buffer_head *bh, int uptodate) { struct inode *inode = bh->b_folio->mapping->host; bool decrypt = fscrypt_inode_uses_fs_layer_crypto(inode); - bool verify = need_fsverity(bh); + struct fsverity_info *vi = NULL; + + /* needed by ext4 */ + if (bh->b_folio->index < DIV_ROUND_UP(inode->i_size, PAGE_SIZE)) + vi = fsverity_get_info(inode); /* Decrypt (with fscrypt) and/or verify (with fsverity) if needed. */ - if (uptodate && (decrypt || verify)) { + if (uptodate && (decrypt || vi)) { struct postprocess_bh_ctx *ctx = kmalloc(sizeof(*ctx), GFP_ATOMIC); if (ctx) { ctx->bh = bh; + ctx->vi = vi; if (decrypt) { INIT_WORK(&ctx->work, decrypt_bh); fscrypt_enqueue_decrypt_work(&ctx->work); @@ -2354,7 +2352,7 @@ bool block_is_partially_uptodate(struct folio *folio, size_t from, size_t count) if (!head) return false; blocksize = head->b_size; - to = min_t(unsigned, folio_size(folio) - from, count); + to = min(folio_size(folio) - from, count); to = from + to; if (from < blocksize && to > folio_size(folio) - blocksize) return false; @@ -2821,7 +2819,7 @@ static void submit_bh_wbc(blk_opf_t opf, struct buffer_head *bh, wbc_account_cgroup_owner(wbc, bh->b_folio, bh->b_size); } - submit_bio(bio); + blk_crypto_submit_bio(bio); } void submit_bh(blk_opf_t opf, struct buffer_head *bh) @@ -2948,6 +2946,10 @@ bool try_to_free_buffers(struct folio *folio) if (folio_test_writeback(folio)) return false; + /* Misconfigured folio check */ + if (WARN_ON_ONCE(!folio_buffers(folio))) + return true; + if (mapping == NULL) { /* can this still happen? */ ret = drop_buffers(folio, &buffers_to_free); goto out; diff --git a/fs/ceph/acl.c b/fs/ceph/acl.c index 1564eacc253d..85d3dd48b167 100644 --- a/fs/ceph/acl.c +++ b/fs/ceph/acl.c @@ -90,7 +90,8 @@ retry: int ceph_set_acl(struct mnt_idmap *idmap, struct dentry *dentry, struct posix_acl *acl, int type) { - int ret = 0, size = 0; + int ret = 0; + size_t size = 0; const char *name = NULL; char *value = NULL; struct iattr newattrs; @@ -126,16 +127,11 @@ int ceph_set_acl(struct mnt_idmap *idmap, struct dentry *dentry, } if (acl) { - size = posix_acl_xattr_size(acl->a_count); - value = kmalloc(size, GFP_NOFS); + value = posix_acl_to_xattr(&init_user_ns, acl, &size, GFP_NOFS); if (!value) { ret = -ENOMEM; goto out; } - - ret = posix_acl_to_xattr(&init_user_ns, acl, value, size); - if (ret < 0) - goto out_free; } if (new_mode != old_mode) { @@ -172,7 +168,7 @@ int ceph_pre_init_acls(struct inode *dir, umode_t *mode, struct posix_acl *acl, *default_acl; size_t val_size1 = 0, val_size2 = 0; struct ceph_pagelist *pagelist = NULL; - void *tmp_buf = NULL; + void *tmp_buf1 = NULL, *tmp_buf2 = NULL; int err; err = posix_acl_create(dir, mode, &default_acl, &acl); @@ -192,15 +188,7 @@ int ceph_pre_init_acls(struct inode *dir, umode_t *mode, if (!default_acl && !acl) return 0; - if (acl) - val_size1 = posix_acl_xattr_size(acl->a_count); - if (default_acl) - val_size2 = posix_acl_xattr_size(default_acl->a_count); - err = -ENOMEM; - tmp_buf = kmalloc(max(val_size1, val_size2), GFP_KERNEL); - if (!tmp_buf) - goto out_err; pagelist = ceph_pagelist_alloc(GFP_KERNEL); if (!pagelist) goto out_err; @@ -213,34 +201,39 @@ int ceph_pre_init_acls(struct inode *dir, umode_t *mode, if (acl) { size_t len = strlen(XATTR_NAME_POSIX_ACL_ACCESS); + + err = -ENOMEM; + tmp_buf1 = posix_acl_to_xattr(&init_user_ns, acl, + &val_size1, GFP_KERNEL); + if (!tmp_buf1) + goto out_err; err = ceph_pagelist_reserve(pagelist, len + val_size1 + 8); if (err) goto out_err; ceph_pagelist_encode_string(pagelist, XATTR_NAME_POSIX_ACL_ACCESS, len); - err = posix_acl_to_xattr(&init_user_ns, acl, - tmp_buf, val_size1); - if (err < 0) - goto out_err; ceph_pagelist_encode_32(pagelist, val_size1); - ceph_pagelist_append(pagelist, tmp_buf, val_size1); + ceph_pagelist_append(pagelist, tmp_buf1, val_size1); } if (default_acl) { size_t len = strlen(XATTR_NAME_POSIX_ACL_DEFAULT); + + err = -ENOMEM; + tmp_buf2 = posix_acl_to_xattr(&init_user_ns, default_acl, + &val_size2, GFP_KERNEL); + if (!tmp_buf2) + goto out_err; err = ceph_pagelist_reserve(pagelist, len + val_size2 + 8); if (err) goto out_err; ceph_pagelist_encode_string(pagelist, XATTR_NAME_POSIX_ACL_DEFAULT, len); - err = posix_acl_to_xattr(&init_user_ns, default_acl, - tmp_buf, val_size2); - if (err < 0) - goto out_err; ceph_pagelist_encode_32(pagelist, val_size2); - ceph_pagelist_append(pagelist, tmp_buf, val_size2); + ceph_pagelist_append(pagelist, tmp_buf2, val_size2); } - kfree(tmp_buf); + kfree(tmp_buf1); + kfree(tmp_buf2); as_ctx->acl = acl; as_ctx->default_acl = default_acl; @@ -250,7 +243,8 @@ int ceph_pre_init_acls(struct inode *dir, umode_t *mode, out_err: posix_acl_release(acl); posix_acl_release(default_acl); - kfree(tmp_buf); + kfree(tmp_buf1); + kfree(tmp_buf2); if (pagelist) ceph_pagelist_release(pagelist); return err; diff --git a/fs/ceph/crypto.c b/fs/ceph/crypto.c index 0ea4db650f85..9a115282f67d 100644 --- a/fs/ceph/crypto.c +++ b/fs/ceph/crypto.c @@ -166,12 +166,13 @@ static struct inode *parse_longname(const struct inode *parent, struct ceph_vino vino = { .snap = CEPH_NOSNAP }; char *name_end, *inode_number; int ret = -EIO; - /* NUL-terminate */ - char *str __free(kfree) = kmemdup_nul(name, *name_len, GFP_KERNEL); + /* Snapshot name must start with an underscore */ + if (*name_len <= 0 || name[0] != '_') + return ERR_PTR(-EIO); + /* Skip initial '_' and NUL-terminate */ + char *str __free(kfree) = kmemdup_nul(name + 1, *name_len - 1, GFP_KERNEL); if (!str) return ERR_PTR(-ENOMEM); - /* Skip initial '_' */ - str++; name_end = strrchr(str, '_'); if (!name_end) { doutc(cl, "failed to parse long snapshot name: %s\n", str); diff --git a/fs/ceph/file.c b/fs/ceph/file.c index 983390069f73..31b691b2aea2 100644 --- a/fs/ceph/file.c +++ b/fs/ceph/file.c @@ -3169,7 +3169,6 @@ const struct file_operations ceph_file_fops = { .mmap_prepare = ceph_mmap_prepare, .fsync = ceph_fsync, .lock = ceph_lock, - .setlease = simple_nosetlease, .flock = ceph_flock, .splice_read = ceph_splice_read, .splice_write = iter_file_splice_write, diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index 7e4eab824dae..c45bd19d4b1c 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -5671,7 +5671,7 @@ static int ceph_mds_auth_match(struct ceph_mds_client *mdsc, u32 caller_uid = from_kuid(&init_user_ns, cred->fsuid); u32 caller_gid = from_kgid(&init_user_ns, cred->fsgid); struct ceph_client *cl = mdsc->fsc->client; - const char *fs_name = mdsc->fsc->mount_options->mds_namespace; + const char *fs_name = mdsc->mdsmap->m_fs_name; const char *spath = mdsc->fsc->mount_options->server_path; bool gid_matched = false; u32 gid, tlen, len; @@ -5679,7 +5679,8 @@ static int ceph_mds_auth_match(struct ceph_mds_client *mdsc, doutc(cl, "fsname check fs_name=%s match.fs_name=%s\n", fs_name, auth->match.fs_name ? auth->match.fs_name : ""); - if (auth->match.fs_name && strcmp(auth->match.fs_name, fs_name)) { + + if (!ceph_namespace_match(auth->match.fs_name, fs_name)) { /* fsname mismatch, try next one */ return 0; } diff --git a/fs/ceph/mdsmap.c b/fs/ceph/mdsmap.c index 2c7b151a7c95..b228e5ecfb92 100644 --- a/fs/ceph/mdsmap.c +++ b/fs/ceph/mdsmap.c @@ -353,22 +353,33 @@ struct ceph_mdsmap *ceph_mdsmap_decode(struct ceph_mds_client *mdsc, void **p, __decode_and_drop_type(p, end, u8, bad_ext); } if (mdsmap_ev >= 8) { - u32 fsname_len; + size_t fsname_len; + /* enabled */ ceph_decode_8_safe(p, end, m->m_enabled, bad_ext); + /* fs_name */ - ceph_decode_32_safe(p, end, fsname_len, bad_ext); + m->m_fs_name = ceph_extract_encoded_string(p, end, + &fsname_len, + GFP_NOFS); + if (IS_ERR(m->m_fs_name)) { + m->m_fs_name = NULL; + goto nomem; + } /* validate fsname against mds_namespace */ - if (!namespace_equals(mdsc->fsc->mount_options, *p, + if (!namespace_equals(mdsc->fsc->mount_options, m->m_fs_name, fsname_len)) { - pr_warn_client(cl, "fsname %*pE doesn't match mds_namespace %s\n", - (int)fsname_len, (char *)*p, + pr_warn_client(cl, "fsname %s doesn't match mds_namespace %s\n", + m->m_fs_name, mdsc->fsc->mount_options->mds_namespace); goto bad; } - /* skip fsname after validation */ - ceph_decode_skip_n(p, end, fsname_len, bad); + } else { + m->m_enabled = false; + m->m_fs_name = kstrdup(CEPH_OLD_FS_NAME, GFP_NOFS); + if (!m->m_fs_name) + goto nomem; } /* damaged */ if (mdsmap_ev >= 9) { @@ -430,6 +441,7 @@ void ceph_mdsmap_destroy(struct ceph_mdsmap *m) kfree(m->m_info); } kfree(m->m_data_pg_pools); + kfree(m->m_fs_name); kfree(m); } diff --git a/fs/ceph/mdsmap.h b/fs/ceph/mdsmap.h index 1f2171dd01bf..d48d07c3516d 100644 --- a/fs/ceph/mdsmap.h +++ b/fs/ceph/mdsmap.h @@ -45,6 +45,7 @@ struct ceph_mdsmap { bool m_enabled; bool m_damaged; int m_num_laggy; + char *m_fs_name; }; static inline struct ceph_entity_addr * diff --git a/fs/ceph/super.h b/fs/ceph/super.h index a1f781c46b41..29a980e22dc2 100644 --- a/fs/ceph/super.h +++ b/fs/ceph/super.h @@ -104,14 +104,26 @@ struct ceph_mount_options { struct fscrypt_dummy_policy dummy_enc_policy; }; +#define CEPH_NAMESPACE_WILDCARD "*" + +static inline bool ceph_namespace_match(const char *pattern, + const char *target) +{ + if (!pattern || !pattern[0] || + !strcmp(pattern, CEPH_NAMESPACE_WILDCARD)) + return true; + + return !strcmp(pattern, target); +} + /* * Check if the mds namespace in ceph_mount_options matches * the passed in namespace string. First time match (when * ->mds_namespace is NULL) is treated specially, since * ->mds_namespace needs to be initialized by the caller. */ -static inline int namespace_equals(struct ceph_mount_options *fsopt, - const char *namespace, size_t len) +static inline bool namespace_equals(struct ceph_mount_options *fsopt, + const char *namespace, size_t len) { return !(fsopt->mds_namespace && (strlen(fsopt->mds_namespace) != len || diff --git a/fs/char_dev.c b/fs/char_dev.c index c2ddb998f3c9..bf7b32650e54 100644 --- a/fs/char_dev.c +++ b/fs/char_dev.c @@ -10,6 +10,7 @@ #include <linux/kdev_t.h> #include <linux/slab.h> #include <linux/string.h> +#include <linux/cleanup.h> #include <linux/major.h> #include <linux/errno.h> @@ -97,7 +98,8 @@ static struct char_device_struct * __register_chrdev_region(unsigned int major, unsigned int baseminor, int minorct, const char *name) { - struct char_device_struct *cd, *curr, *prev = NULL; + struct char_device_struct *cd __free(kfree) = NULL; + struct char_device_struct *curr, *prev = NULL; int ret; int i; @@ -117,14 +119,14 @@ __register_chrdev_region(unsigned int major, unsigned int baseminor, if (cd == NULL) return ERR_PTR(-ENOMEM); - mutex_lock(&chrdevs_lock); + guard(mutex)(&chrdevs_lock); if (major == 0) { ret = find_dynamic_major(); if (ret < 0) { pr_err("CHRDEV \"%s\" dynamic allocation region is full\n", name); - goto out; + return ERR_PTR(ret); } major = ret; } @@ -144,7 +146,7 @@ __register_chrdev_region(unsigned int major, unsigned int baseminor, if (curr->baseminor >= baseminor + minorct) break; - goto out; + return ERR_PTR(ret); } cd->major = major; @@ -160,12 +162,7 @@ __register_chrdev_region(unsigned int major, unsigned int baseminor, prev->next = cd; } - mutex_unlock(&chrdevs_lock); - return cd; -out: - mutex_unlock(&chrdevs_lock); - kfree(cd); - return ERR_PTR(ret); + return_ptr(cd); } static struct char_device_struct * @@ -343,7 +340,7 @@ void __unregister_chrdev(unsigned int major, unsigned int baseminor, kfree(cd); } -static DEFINE_SPINLOCK(cdev_lock); +static __cacheline_aligned_in_smp DEFINE_SPINLOCK(cdev_lock); static struct kobject *cdev_get(struct cdev *p) { diff --git a/fs/coredump.c b/fs/coredump.c index 8feb9c1cf83d..4ce7c80b39c8 100644 --- a/fs/coredump.c +++ b/fs/coredump.c @@ -92,7 +92,7 @@ enum coredump_type_t { }; struct core_name { - char *corename; + char *corename __counted_by_ptr(size); int used, size; unsigned int core_pipe_limit; bool core_dumped; @@ -106,15 +106,15 @@ static int expand_corename(struct core_name *cn, int size) size = kmalloc_size_roundup(size); corename = krealloc(cn->corename, size, GFP_KERNEL); - if (!corename) return -ENOMEM; + cn->corename = corename; + cn->size = size; + if (size > core_name_size) /* racy but harmless */ core_name_size = size; - cn->size = size; - cn->corename = corename; return 0; } @@ -895,11 +895,12 @@ static bool coredump_file(struct core_name *cn, struct coredump_params *cprm, * privs and don't want to unlink another user's coredump. */ if (!coredump_force_suid_safe(cprm)) { + CLASS(filename_kernel, name)(cn->corename); /* * If it doesn't exist, that's fine. If there's some * other problem, we'll catch it at the filp_open(). */ - do_unlinkat(AT_FDCWD, getname_kernel(cn->corename)); + filename_unlinkat(AT_FDCWD, name); } /* diff --git a/fs/cramfs/inode.c b/fs/cramfs/inode.c index e54ebe402df7..41b1a869cf13 100644 --- a/fs/cramfs/inode.c +++ b/fs/cramfs/inode.c @@ -16,6 +16,7 @@ #include <linux/module.h> #include <linux/fs.h> #include <linux/file.h> +#include <linux/filelock.h> #include <linux/pagemap.h> #include <linux/ramfs.h> #include <linux/init.h> @@ -938,6 +939,7 @@ static const struct file_operations cramfs_directory_operations = { .llseek = generic_file_llseek, .read = generic_read_dir, .iterate_shared = cramfs_readdir, + .setlease = generic_setlease, }; static const struct inode_operations cramfs_dir_inode_operations = { diff --git a/fs/crypto/bio.c b/fs/crypto/bio.c index 5f5599020e94..6da683ea69dc 100644 --- a/fs/crypto/bio.c +++ b/fs/crypto/bio.c @@ -47,50 +47,71 @@ bool fscrypt_decrypt_bio(struct bio *bio) } EXPORT_SYMBOL(fscrypt_decrypt_bio); +struct fscrypt_zero_done { + atomic_t pending; + blk_status_t status; + struct completion done; +}; + +static void fscrypt_zeroout_range_done(struct fscrypt_zero_done *done) +{ + if (atomic_dec_and_test(&done->pending)) + complete(&done->done); +} + +static void fscrypt_zeroout_range_end_io(struct bio *bio) +{ + struct fscrypt_zero_done *done = bio->bi_private; + + if (bio->bi_status) + cmpxchg(&done->status, 0, bio->bi_status); + fscrypt_zeroout_range_done(done); + bio_put(bio); +} + static int fscrypt_zeroout_range_inline_crypt(const struct inode *inode, - pgoff_t lblk, sector_t pblk, + pgoff_t lblk, sector_t sector, unsigned int len) { const unsigned int blockbits = inode->i_blkbits; const unsigned int blocks_per_page = 1 << (PAGE_SHIFT - blockbits); - struct bio *bio; - int ret, err = 0; - int num_pages = 0; - - /* This always succeeds since __GFP_DIRECT_RECLAIM is set. */ - bio = bio_alloc(inode->i_sb->s_bdev, BIO_MAX_VECS, REQ_OP_WRITE, - GFP_NOFS); + struct fscrypt_zero_done done = { + .pending = ATOMIC_INIT(1), + .done = COMPLETION_INITIALIZER_ONSTACK(done.done), + }; while (len) { - unsigned int blocks_this_page = min(len, blocks_per_page); - unsigned int bytes_this_page = blocks_this_page << blockbits; + struct bio *bio; + unsigned int n; - if (num_pages == 0) { - fscrypt_set_bio_crypt_ctx(bio, inode, lblk, GFP_NOFS); - bio->bi_iter.bi_sector = - pblk << (blockbits - SECTOR_SHIFT); - } - ret = bio_add_page(bio, ZERO_PAGE(0), bytes_this_page, 0); - if (WARN_ON_ONCE(ret != bytes_this_page)) { - err = -EIO; - goto out; - } - num_pages++; - len -= blocks_this_page; - lblk += blocks_this_page; - pblk += blocks_this_page; - if (num_pages == BIO_MAX_VECS || !len || - !fscrypt_mergeable_bio(bio, inode, lblk)) { - err = submit_bio_wait(bio); - if (err) - goto out; - bio_reset(bio, inode->i_sb->s_bdev, REQ_OP_WRITE); - num_pages = 0; + bio = bio_alloc(inode->i_sb->s_bdev, BIO_MAX_VECS, REQ_OP_WRITE, + GFP_NOFS); + bio->bi_iter.bi_sector = sector; + bio->bi_private = &done; + bio->bi_end_io = fscrypt_zeroout_range_end_io; + fscrypt_set_bio_crypt_ctx(bio, inode, lblk, GFP_NOFS); + + for (n = 0; n < BIO_MAX_VECS; n++) { + unsigned int blocks_this_page = + min(len, blocks_per_page); + unsigned int bytes_this_page = blocks_this_page << blockbits; + + __bio_add_page(bio, ZERO_PAGE(0), bytes_this_page, 0); + len -= blocks_this_page; + lblk += blocks_this_page; + sector += (bytes_this_page >> SECTOR_SHIFT); + if (!len || !fscrypt_mergeable_bio(bio, inode, lblk)) + break; } + + atomic_inc(&done.pending); + blk_crypto_submit_bio(bio); } -out: - bio_put(bio); - return err; + + fscrypt_zeroout_range_done(&done); + + wait_for_completion(&done.done); + return blk_status_to_errno(done.status); } /** @@ -132,7 +153,7 @@ int fscrypt_zeroout_range(const struct inode *inode, pgoff_t lblk, return 0; if (fscrypt_inode_uses_inline_crypto(inode)) - return fscrypt_zeroout_range_inline_crypt(inode, lblk, pblk, + return fscrypt_zeroout_range_inline_crypt(inode, lblk, sector, len); BUILD_BUG_ON(ARRAY_SIZE(pages) > BIO_MAX_VECS); diff --git a/fs/dcache.c b/fs/dcache.c index dc2fff4811d1..2758fe7322d4 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -1104,6 +1104,16 @@ struct dentry *d_find_alias_rcu(struct inode *inode) return de; } +/** + * d_dispose_if_unused - move unreferenced dentries to shrink list + * @dentry: dentry in question + * @dispose: head of shrink list + * + * If dentry has no external references, move it to shrink list. + * + * NOTE!!! The caller is responsible for preventing eviction of the dentry by + * holding dentry->d_inode->i_lock or equivalent. + */ void d_dispose_if_unused(struct dentry *dentry, struct list_head *dispose) { spin_lock(&dentry->d_lock); @@ -3227,10 +3237,7 @@ EXPORT_SYMBOL(d_parent_ino); static __initdata unsigned long dhash_entries; static int __init set_dhash_entries(char *str) { - if (!str) - return 0; - dhash_entries = simple_strtoul(str, &str, 0); - return 1; + return kstrtoul(str, 0, &dhash_entries) == 0; } __setup("dhash_entries=", set_dhash_entries); @@ -3290,10 +3297,6 @@ static void __init dcache_init(void) runtime_const_init(ptr, dentry_hashtable); } -/* SLAB cache for __getname() consumers */ -struct kmem_cache *names_cachep __ro_after_init; -EXPORT_SYMBOL(names_cachep); - void __init vfs_caches_init_early(void) { int i; @@ -3307,9 +3310,7 @@ void __init vfs_caches_init_early(void) void __init vfs_caches_init(void) { - names_cachep = kmem_cache_create_usercopy("names_cache", PATH_MAX, 0, - SLAB_HWCACHE_ALIGN|SLAB_PANIC, 0, PATH_MAX, NULL); - + filename_init(); dcache_init(); inode_init(); files_init(); diff --git a/fs/dlm/config.c b/fs/dlm/config.c index a0d75b5c83c6..82cc3215663f 100644 --- a/fs/dlm/config.c +++ b/fs/dlm/config.c @@ -324,39 +324,39 @@ struct dlm_member_gone { struct list_head list; /* space->members_gone */ }; -static struct configfs_group_operations clusters_ops = { +static const struct configfs_group_operations clusters_ops = { .make_group = make_cluster, .drop_item = drop_cluster, }; -static struct configfs_item_operations cluster_ops = { +static const struct configfs_item_operations cluster_ops = { .release = release_cluster, }; -static struct configfs_group_operations spaces_ops = { +static const struct configfs_group_operations spaces_ops = { .make_group = make_space, .drop_item = drop_space, }; -static struct configfs_item_operations space_ops = { +static const struct configfs_item_operations space_ops = { .release = release_space, }; -static struct configfs_group_operations comms_ops = { +static const struct configfs_group_operations comms_ops = { .make_item = make_comm, .drop_item = drop_comm, }; -static struct configfs_item_operations comm_ops = { +static const struct configfs_item_operations comm_ops = { .release = release_comm, }; -static struct configfs_group_operations nodes_ops = { +static const struct configfs_group_operations nodes_ops = { .make_item = make_node, .drop_item = drop_node, }; -static struct configfs_item_operations node_ops = { +static const struct configfs_item_operations node_ops = { .release = release_node, }; diff --git a/fs/dlm/dir.c b/fs/dlm/dir.c index b1ab0adbd9d0..01c292379f5b 100644 --- a/fs/dlm/dir.c +++ b/fs/dlm/dir.c @@ -62,7 +62,7 @@ int dlm_recover_directory(struct dlm_ls *ls, uint64_t seq) char *b, *last_name = NULL; int error = -ENOMEM, last_len, nodeid, result; uint16_t namelen; - unsigned int count = 0, count_match = 0, count_bad = 0, count_add = 0; + unsigned int count = 0, count_bad = 0, count_add = 0; log_rinfo(ls, "dlm_recover_directory"); @@ -157,12 +157,12 @@ int dlm_recover_directory(struct dlm_ls *ls, uint64_t seq) } /* The name was found in rsbtbl, and the - * master nodeid matches memb->nodeid. */ + * master nodeid matches memb->nodeid. if (result == DLM_LU_MATCH && nodeid == memb->nodeid) { count_match++; - } + }*/ /* The name was not found in rsbtbl and was * added with memb->nodeid as the master. */ diff --git a/fs/dlm/dlm_internal.h b/fs/dlm/dlm_internal.h index d534a4bc162b..9df842421ae0 100644 --- a/fs/dlm/dlm_internal.h +++ b/fs/dlm/dlm_internal.h @@ -606,7 +606,6 @@ struct dlm_ls { struct dlm_rsb ls_local_rsb; /* for returning errors */ struct dlm_lkb ls_local_lkb; /* for returning errors */ - struct dlm_message ls_local_ms; /* for faking a reply */ struct dentry *ls_debug_rsb_dentry; /* debugfs */ struct dentry *ls_debug_waiters_dentry; /* debugfs */ @@ -665,6 +664,9 @@ struct dlm_ls { int ls_namelen; char ls_name[DLM_LOCKSPACE_LEN + 1]; + + /* Must be last --ends in a flexible-array member.*/ + struct dlm_message ls_local_ms; /* for faking a reply */ }; /* diff --git a/fs/dlm/lock.c b/fs/dlm/lock.c index be938fdf17d9..47a09d6748cb 100644 --- a/fs/dlm/lock.c +++ b/fs/dlm/lock.c @@ -343,7 +343,7 @@ void dlm_hold_rsb(struct dlm_rsb *r) /* TODO move this to lib/refcount.c */ static __must_check bool dlm_refcount_dec_and_write_lock_bh(refcount_t *r, rwlock_t *lock) -__cond_acquires(lock) + __cond_acquires(true, lock) { if (refcount_dec_not_one(r)) return false; @@ -626,7 +626,8 @@ int dlm_search_rsb_tree(struct rhashtable *rhash, const void *name, int len, struct dlm_rsb **r_ret) { char key[DLM_RESNAME_MAXLEN] = {}; - + if (len > DLM_RESNAME_MAXLEN) + return -EINVAL; memcpy(key, name, len); *r_ret = rhashtable_lookup_fast(rhash, &key, dlm_rhash_rsb_params); if (*r_ret) @@ -5014,25 +5015,8 @@ void dlm_receive_buffer(const union dlm_packet *p, int nodeid) static void recover_convert_waiter(struct dlm_ls *ls, struct dlm_lkb *lkb, struct dlm_message *ms_local) { - if (middle_conversion(lkb)) { - log_rinfo(ls, "%s %x middle convert in progress", __func__, - lkb->lkb_id); - - /* We sent this lock to the new master. The new master will - * tell us when it's granted. We no longer need a reply, so - * use a fake reply to put the lkb into the right state. - */ - hold_lkb(lkb); - memset(ms_local, 0, sizeof(struct dlm_message)); - ms_local->m_type = cpu_to_le32(DLM_MSG_CONVERT_REPLY); - ms_local->m_result = cpu_to_le32(to_dlm_errno(-EINPROGRESS)); - ms_local->m_header.h_nodeid = cpu_to_le32(lkb->lkb_nodeid); - _receive_convert_reply(lkb, ms_local, true); - unhold_lkb(lkb); - - } else if (lkb->lkb_rqmode >= lkb->lkb_grmode) { + if (middle_conversion(lkb) || lkb->lkb_rqmode >= lkb->lkb_grmode) set_bit(DLM_IFL_RESEND_BIT, &lkb->lkb_iflags); - } /* lkb->lkb_rqmode < lkb->lkb_grmode shouldn't happen since down conversions are async; there's no reply from the remote master */ diff --git a/fs/dlm/member.c b/fs/dlm/member.c index c0f557a80a75..c1b5598997b7 100644 --- a/fs/dlm/member.c +++ b/fs/dlm/member.c @@ -299,11 +299,7 @@ static void add_ordered_member(struct dlm_ls *ls, struct dlm_member *new) if (!memb) list_add_tail(newlist, head); else { - /* FIXME: can use list macro here */ - newlist->prev = tmp->prev; - newlist->next = tmp; - tmp->prev->next = newlist; - tmp->prev = newlist; + list_add_tail(newlist, tmp); } } diff --git a/fs/ecryptfs/ecryptfs_kernel.h b/fs/ecryptfs/ecryptfs_kernel.h index 62a2ea7f59ed..509293745ce9 100644 --- a/fs/ecryptfs/ecryptfs_kernel.h +++ b/fs/ecryptfs/ecryptfs_kernel.h @@ -21,6 +21,7 @@ #include <linux/kernel.h> #include <linux/fs.h> #include <linux/fs_stack.h> +#include <linux/hex.h> #include <linux/namei.h> #include <linux/scatterlist.h> #include <linux/hash.h> @@ -359,7 +360,7 @@ struct ecryptfs_message { /* Inherits from msg_ctx->index */ u32 index; u32 data_len; - u8 data[]; + u8 data[] __counted_by(data_len); }; struct ecryptfs_msg_ctx { diff --git a/fs/efivarfs/vars.c b/fs/efivarfs/vars.c index 6edc10958ecf..6833c3d24b54 100644 --- a/fs/efivarfs/vars.c +++ b/fs/efivarfs/vars.c @@ -9,6 +9,7 @@ #include <linux/capability.h> #include <linux/types.h> #include <linux/errno.h> +#include <linux/hex.h> #include <linux/init.h> #include <linux/mm.h> #include <linux/module.h> @@ -552,7 +553,7 @@ int efivar_entry_get(struct efivar_entry *entry, u32 *attributes, err = __efivar_entry_get(entry, attributes, size, data); efivar_unlock(); - return 0; + return err; } /** diff --git a/fs/efs/dir.c b/fs/efs/dir.c index f892ac7c2a35..35ad0092c115 100644 --- a/fs/efs/dir.c +++ b/fs/efs/dir.c @@ -6,6 +6,7 @@ */ #include <linux/buffer_head.h> +#include <linux/filelock.h> #include "efs.h" static int efs_readdir(struct file *, struct dir_context *); @@ -14,6 +15,7 @@ const struct file_operations efs_dir_operations = { .llseek = generic_file_llseek, .read = generic_read_dir, .iterate_shared = efs_readdir, + .setlease = generic_setlease, }; const struct inode_operations efs_dir_inode_operations = { diff --git a/fs/erofs/Kconfig b/fs/erofs/Kconfig index d81f3318417d..a9f645f57bb2 100644 --- a/fs/erofs/Kconfig +++ b/fs/erofs/Kconfig @@ -112,13 +112,14 @@ config EROFS_FS_ZIP config EROFS_FS_ZIP_LZMA bool "EROFS LZMA compressed data support" depends on EROFS_FS_ZIP + default y help Saying Y here includes support for reading EROFS file systems containing LZMA compressed data, specifically called microLZMA. It gives better compression ratios than the default LZ4 format, at the expense of more CPU overhead. - If unsure, say N. + Say N if you want to disable LZMA compression support. config EROFS_FS_ZIP_DEFLATE bool "EROFS DEFLATE compressed data support" @@ -129,9 +130,6 @@ config EROFS_FS_ZIP_DEFLATE ratios than the default LZ4 format, while it costs more CPU overhead. - DEFLATE support is an experimental feature for now and so most - file systems will be readable without selecting this option. - If unsure, say N. config EROFS_FS_ZIP_ZSTD @@ -141,10 +139,7 @@ config EROFS_FS_ZIP_ZSTD Saying Y here includes support for reading EROFS file systems containing Zstandard compressed data. It gives better compression ratios than the default LZ4 format, while it costs more CPU - overhead. - - Zstandard support is an experimental feature for now and so most - file systems will be readable without selecting this option. + overhead and memory footprint. If unsure, say N. @@ -194,3 +189,12 @@ config EROFS_FS_PCPU_KTHREAD_HIPRI at higher priority. If unsure, say N. + +config EROFS_FS_PAGE_CACHE_SHARE + bool "EROFS page cache share support (experimental)" + depends on EROFS_FS && EROFS_FS_XATTR && !EROFS_FS_ONDEMAND + help + This enables page cache sharing among inodes with identical + content fingerprints on the same machine. + + If unsure, say N. diff --git a/fs/erofs/Makefile b/fs/erofs/Makefile index 549abc424763..a80e1762b607 100644 --- a/fs/erofs/Makefile +++ b/fs/erofs/Makefile @@ -10,3 +10,4 @@ erofs-$(CONFIG_EROFS_FS_ZIP_ZSTD) += decompressor_zstd.o erofs-$(CONFIG_EROFS_FS_ZIP_ACCEL) += decompressor_crypto.o erofs-$(CONFIG_EROFS_FS_BACKED_BY_FILE) += fileio.o erofs-$(CONFIG_EROFS_FS_ONDEMAND) += fscache.o +erofs-$(CONFIG_EROFS_FS_PAGE_CACHE_SHARE) += ishare.o diff --git a/fs/erofs/data.c b/fs/erofs/data.c index bb13c4cb8455..a28084ef796b 100644 --- a/fs/erofs/data.c +++ b/fs/erofs/data.c @@ -5,6 +5,7 @@ * Copyright (C) 2021, Alibaba Cloud */ #include "internal.h" +#include <linux/filelock.h> #include <linux/sched/mm.h> #include <trace/events/erofs.h> @@ -266,31 +267,39 @@ void erofs_onlinefolio_end(struct folio *folio, int err, bool dirty) folio_end_read(folio, !(v & BIT(EROFS_ONLINEFOLIO_EIO))); } +struct erofs_iomap_iter_ctx { + struct page *page; + void *base; + struct inode *realinode; +}; + static int erofs_iomap_begin(struct inode *inode, loff_t offset, loff_t length, unsigned int flags, struct iomap *iomap, struct iomap *srcmap) { - int ret; - struct super_block *sb = inode->i_sb; + struct iomap_iter *iter = container_of(iomap, struct iomap_iter, iomap); + struct erofs_iomap_iter_ctx *ctx = iter->private; + struct inode *realinode = ctx ? ctx->realinode : inode; + struct super_block *sb = realinode->i_sb; struct erofs_map_blocks map; struct erofs_map_dev mdev; + int ret; map.m_la = offset; map.m_llen = length; - ret = erofs_map_blocks(inode, &map); + ret = erofs_map_blocks(realinode, &map); if (ret < 0) return ret; iomap->offset = map.m_la; iomap->length = map.m_llen; iomap->flags = 0; - iomap->private = NULL; iomap->addr = IOMAP_NULL_ADDR; if (!(map.m_flags & EROFS_MAP_MAPPED)) { iomap->type = IOMAP_HOLE; return 0; } - if (!(map.m_flags & EROFS_MAP_META) || !erofs_inode_in_metabox(inode)) { + if (!(map.m_flags & EROFS_MAP_META) || !erofs_inode_in_metabox(realinode)) { mdev = (struct erofs_map_dev) { .m_deviceid = map.m_deviceid, .m_pa = map.m_pa, @@ -309,16 +318,20 @@ static int erofs_iomap_begin(struct inode *inode, loff_t offset, loff_t length, } if (map.m_flags & EROFS_MAP_META) { - void *ptr; - struct erofs_buf buf = __EROFS_BUF_INITIALIZER; - iomap->type = IOMAP_INLINE; - ptr = erofs_read_metabuf(&buf, sb, map.m_pa, - erofs_inode_in_metabox(inode)); - if (IS_ERR(ptr)) - return PTR_ERR(ptr); - iomap->inline_data = ptr; - iomap->private = buf.base; + /* read context should read the inlined data */ + if (ctx) { + struct erofs_buf buf = __EROFS_BUF_INITIALIZER; + void *ptr; + + ptr = erofs_read_metabuf(&buf, sb, map.m_pa, + erofs_inode_in_metabox(realinode)); + if (IS_ERR(ptr)) + return PTR_ERR(ptr); + iomap->inline_data = ptr; + ctx->page = buf.page; + ctx->base = buf.base; + } } else { iomap->type = IOMAP_MAPPED; } @@ -328,18 +341,18 @@ static int erofs_iomap_begin(struct inode *inode, loff_t offset, loff_t length, static int erofs_iomap_end(struct inode *inode, loff_t pos, loff_t length, ssize_t written, unsigned int flags, struct iomap *iomap) { - void *ptr = iomap->private; + struct iomap_iter *iter = container_of(iomap, struct iomap_iter, iomap); + struct erofs_iomap_iter_ctx *ctx = iter->private; - if (ptr) { + if (ctx && ctx->base) { struct erofs_buf buf = { - .page = kmap_to_page(ptr), - .base = ptr, + .page = ctx->page, + .base = ctx->base, }; DBG_BUGON(iomap->type != IOMAP_INLINE); erofs_put_metabuf(&buf); - } else { - DBG_BUGON(iomap->type == IOMAP_INLINE); + ctx->base = NULL; } return written; } @@ -353,12 +366,10 @@ int erofs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, u64 start, u64 len) { if (erofs_inode_is_data_compressed(EROFS_I(inode)->datalayout)) { -#ifdef CONFIG_EROFS_FS_ZIP + if (!IS_ENABLED(CONFIG_EROFS_FS_ZIP)) + return -EOPNOTSUPP; return iomap_fiemap(inode, fieinfo, start, len, &z_erofs_iomap_report_ops); -#else - return -EOPNOTSUPP; -#endif } return iomap_fiemap(inode, fieinfo, start, len, &erofs_iomap_ops); } @@ -369,18 +380,38 @@ int erofs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, */ static int erofs_read_folio(struct file *file, struct folio *folio) { - trace_erofs_read_folio(folio, true); - - iomap_bio_read_folio(folio, &erofs_iomap_ops); + struct iomap_read_folio_ctx read_ctx = { + .ops = &iomap_bio_read_ops, + .cur_folio = folio, + }; + bool need_iput; + struct erofs_iomap_iter_ctx iter_ctx = { + .realinode = erofs_real_inode(folio_inode(folio), &need_iput), + }; + + trace_erofs_read_folio(iter_ctx.realinode, folio, true); + iomap_read_folio(&erofs_iomap_ops, &read_ctx, &iter_ctx); + if (need_iput) + iput(iter_ctx.realinode); return 0; } static void erofs_readahead(struct readahead_control *rac) { - trace_erofs_readahead(rac->mapping->host, readahead_index(rac), - readahead_count(rac), true); - - iomap_bio_readahead(rac, &erofs_iomap_ops); + struct iomap_read_folio_ctx read_ctx = { + .ops = &iomap_bio_read_ops, + .rac = rac, + }; + bool need_iput; + struct erofs_iomap_iter_ctx iter_ctx = { + .realinode = erofs_real_inode(rac->mapping->host, &need_iput), + }; + + trace_erofs_readahead(iter_ctx.realinode, readahead_index(rac), + readahead_count(rac), true); + iomap_readahead(&erofs_iomap_ops, &read_ctx, &iter_ctx); + if (need_iput) + iput(iter_ctx.realinode); } static sector_t erofs_bmap(struct address_space *mapping, sector_t block) @@ -396,13 +427,17 @@ static ssize_t erofs_file_read_iter(struct kiocb *iocb, struct iov_iter *to) if (!iov_iter_count(to)) return 0; -#ifdef CONFIG_FS_DAX - if (IS_DAX(inode)) + if (IS_ENABLED(CONFIG_FS_DAX) && IS_DAX(inode)) return dax_iomap_rw(iocb, to, &erofs_iomap_ops); -#endif - if ((iocb->ki_flags & IOCB_DIRECT) && inode->i_sb->s_bdev) + + if ((iocb->ki_flags & IOCB_DIRECT) && inode->i_sb->s_bdev) { + struct erofs_iomap_iter_ctx iter_ctx = { + .realinode = inode, + }; + return iomap_dio_rw(iocb, to, &erofs_iomap_ops, - NULL, 0, NULL, 0); + NULL, 0, &iter_ctx, 0); + } return filemap_read(iocb, to, 0); } @@ -454,12 +489,11 @@ static loff_t erofs_file_llseek(struct file *file, loff_t offset, int whence) struct inode *inode = file->f_mapping->host; const struct iomap_ops *ops = &erofs_iomap_ops; - if (erofs_inode_is_data_compressed(EROFS_I(inode)->datalayout)) -#ifdef CONFIG_EROFS_FS_ZIP + if (erofs_inode_is_data_compressed(EROFS_I(inode)->datalayout)) { + if (!IS_ENABLED(CONFIG_EROFS_FS_ZIP)) + return generic_file_llseek(file, offset, whence); ops = &z_erofs_iomap_report_ops; -#else - return generic_file_llseek(file, offset, whence); -#endif + } if (whence == SEEK_HOLE) offset = iomap_seek_hole(inode, offset, ops); @@ -483,4 +517,5 @@ const struct file_operations erofs_file_fops = { .mmap_prepare = erofs_file_mmap_prepare, .get_unmapped_area = thp_get_unmapped_area, .splice_read = filemap_splice_read, + .setlease = generic_setlease, }; diff --git a/fs/erofs/decompressor.c b/fs/erofs/decompressor.c index d5d090276391..3c54e95964c9 100644 --- a/fs/erofs/decompressor.c +++ b/fs/erofs/decompressor.c @@ -34,7 +34,10 @@ static int z_erofs_load_lz4_config(struct super_block *sb, } } else { distance = le16_to_cpu(dsb->u1.lz4_max_distance); + if (!distance && !erofs_sb_has_lz4_0padding(sbi)) + return 0; sbi->lz4.max_pclusterblks = 1; + sbi->available_compr_algs = 1 << Z_EROFS_COMPRESSION_LZ4; } sbi->lz4.max_distance_pages = distance ? @@ -195,55 +198,47 @@ const char *z_erofs_fixup_insize(struct z_erofs_decompress_req *rq, return NULL; } -static int z_erofs_lz4_decompress_mem(struct z_erofs_decompress_req *rq, u8 *dst) +static const char *__z_erofs_lz4_decompress(struct z_erofs_decompress_req *rq, + u8 *dst) { - bool support_0padding = false, may_inplace = false; + bool may_inplace = false; unsigned int inputmargin; u8 *out, *headpage, *src; const char *reason; int ret, maptype; - DBG_BUGON(*rq->in == NULL); headpage = kmap_local_page(*rq->in); - - /* LZ4 decompression inplace is only safe if zero_padding is enabled */ - if (erofs_sb_has_zero_padding(EROFS_SB(rq->sb))) { - support_0padding = true; - reason = z_erofs_fixup_insize(rq, headpage + rq->pageofs_in, - min_t(unsigned int, rq->inputsize, - rq->sb->s_blocksize - rq->pageofs_in)); - if (reason) { - kunmap_local(headpage); - return IS_ERR(reason) ? PTR_ERR(reason) : -EFSCORRUPTED; - } - may_inplace = !((rq->pageofs_in + rq->inputsize) & - (rq->sb->s_blocksize - 1)); + reason = z_erofs_fixup_insize(rq, headpage + rq->pageofs_in, + min_t(unsigned int, rq->inputsize, + rq->sb->s_blocksize - rq->pageofs_in)); + if (reason) { + kunmap_local(headpage); + return reason; } + may_inplace = !((rq->pageofs_in + rq->inputsize) & + (rq->sb->s_blocksize - 1)); inputmargin = rq->pageofs_in; src = z_erofs_lz4_handle_overlap(rq, headpage, dst, &inputmargin, &maptype, may_inplace); if (IS_ERR(src)) - return PTR_ERR(src); + return ERR_CAST(src); out = dst + rq->pageofs_out; - /* legacy format could compress extra data in a pcluster. */ - if (rq->partial_decoding || !support_0padding) + if (rq->partial_decoding) ret = LZ4_decompress_safe_partial(src + inputmargin, out, rq->inputsize, rq->outputsize, rq->outputsize); else ret = LZ4_decompress_safe(src + inputmargin, out, rq->inputsize, rq->outputsize); + if (ret == rq->outputsize) + reason = NULL; + else if (ret < 0) + reason = "corrupted compressed data"; + else + reason = "unexpected end of stream"; - if (ret != rq->outputsize) { - if (ret >= 0) - memset(out + ret, 0, rq->outputsize - ret); - ret = -EFSCORRUPTED; - } else { - ret = 0; - } - - if (maptype == 0) { + if (!maptype) { kunmap_local(headpage); } else if (maptype == 1) { vm_unmap_ram(src, rq->inpages); @@ -251,15 +246,16 @@ static int z_erofs_lz4_decompress_mem(struct z_erofs_decompress_req *rq, u8 *dst z_erofs_put_gbuf(src); } else if (maptype != 3) { DBG_BUGON(1); - return -EFAULT; + return ERR_PTR(-EFAULT); } - return ret; + return reason; } static const char *z_erofs_lz4_decompress(struct z_erofs_decompress_req *rq, struct page **pagepool) { unsigned int dst_maptype; + const char *reason; void *dst; int ret; @@ -283,12 +279,12 @@ static const char *z_erofs_lz4_decompress(struct z_erofs_decompress_req *rq, dst_maptype = 2; } } - ret = z_erofs_lz4_decompress_mem(rq, dst); + reason = __z_erofs_lz4_decompress(rq, dst); if (!dst_maptype) kunmap_local(dst); else if (dst_maptype == 2) vm_unmap_ram(dst, rq->outpages); - return ERR_PTR(ret); + return reason; } static const char *z_erofs_transform_plain(struct z_erofs_decompress_req *rq, @@ -452,42 +448,37 @@ int z_erofs_parse_cfgs(struct super_block *sb, struct erofs_super_block *dsb) { struct erofs_sb_info *sbi = EROFS_SB(sb); struct erofs_buf buf = __EROFS_BUF_INITIALIZER; - unsigned int algs, alg; + unsigned long algs, alg; erofs_off_t offset; int size, ret = 0; - if (!erofs_sb_has_compr_cfgs(sbi)) { - sbi->available_compr_algs = 1 << Z_EROFS_COMPRESSION_LZ4; + if (!erofs_sb_has_compr_cfgs(sbi)) return z_erofs_load_lz4_config(sb, dsb, NULL, 0); - } - sbi->available_compr_algs = le16_to_cpu(dsb->u1.available_compr_algs); - if (sbi->available_compr_algs & ~Z_EROFS_ALL_COMPR_ALGS) { - erofs_err(sb, "unidentified algorithms %x, please upgrade kernel", - sbi->available_compr_algs & ~Z_EROFS_ALL_COMPR_ALGS); + algs = le16_to_cpu(dsb->u1.available_compr_algs); + sbi->available_compr_algs = algs; + if (algs & ~Z_EROFS_ALL_COMPR_ALGS) { + erofs_err(sb, "unidentified algorithms %lx, please upgrade kernel", + algs & ~Z_EROFS_ALL_COMPR_ALGS); return -EOPNOTSUPP; } (void)erofs_init_metabuf(&buf, sb, false); offset = EROFS_SUPER_OFFSET + sbi->sb_size; - alg = 0; - for (algs = sbi->available_compr_algs; algs; algs >>= 1, ++alg) { + for_each_set_bit(alg, &algs, Z_EROFS_COMPRESSION_MAX) { const struct z_erofs_decompressor *dec = z_erofs_decomp[alg]; void *data; - if (!(algs & 1)) - continue; - data = erofs_read_metadata(sb, &buf, &offset, &size); if (IS_ERR(data)) { ret = PTR_ERR(data); break; } - if (alg < Z_EROFS_COMPRESSION_MAX && dec && dec->config) { + if (dec && dec->config) { ret = dec->config(sb, dsb, data, size); } else { - erofs_err(sb, "algorithm %d isn't enabled on this kernel", + erofs_err(sb, "algorithm %ld isn't enabled on this kernel", alg); ret = -EOPNOTSUPP; } diff --git a/fs/erofs/decompressor_crypto.c b/fs/erofs/decompressor_crypto.c index 5ef6f71d3b7f..77c6bd535df3 100644 --- a/fs/erofs/decompressor_crypto.c +++ b/fs/erofs/decompressor_crypto.c @@ -62,7 +62,7 @@ struct z_erofs_crypto_engine { struct crypto_acomp *tfm; }; -struct z_erofs_crypto_engine *z_erofs_crypto[Z_EROFS_COMPRESSION_MAX] = { +static struct z_erofs_crypto_engine *z_erofs_crypto[Z_EROFS_COMPRESSION_MAX] = { [Z_EROFS_COMPRESSION_LZ4] = (struct z_erofs_crypto_engine[]) { {}, }, diff --git a/fs/erofs/decompressor_deflate.c b/fs/erofs/decompressor_deflate.c index 3fb73000ed27..4f26ab767645 100644 --- a/fs/erofs/decompressor_deflate.c +++ b/fs/erofs/decompressor_deflate.c @@ -89,7 +89,6 @@ static int z_erofs_load_deflate_config(struct super_block *sb, inited = true; } mutex_unlock(&deflate_resize_mutex); - erofs_info(sb, "EXPERIMENTAL DEFLATE feature in use. Use at your own risk!"); return 0; failed: mutex_unlock(&deflate_resize_mutex); diff --git a/fs/erofs/dir.c b/fs/erofs/dir.c index 32b4f5aa60c9..e5132575b9d3 100644 --- a/fs/erofs/dir.c +++ b/fs/erofs/dir.c @@ -5,6 +5,7 @@ * Copyright (C) 2022, Alibaba Cloud */ #include "internal.h" +#include <linux/filelock.h> static int erofs_fill_dentries(struct inode *dir, struct dir_context *ctx, void *dentry_blk, struct erofs_dirent *de, @@ -127,4 +128,5 @@ const struct file_operations erofs_dir_fops = { #ifdef CONFIG_COMPAT .compat_ioctl = erofs_compat_ioctl, #endif + .setlease = generic_setlease, }; diff --git a/fs/erofs/erofs_fs.h b/fs/erofs/erofs_fs.h index e24268acdd62..b80c6bb33a58 100644 --- a/fs/erofs/erofs_fs.h +++ b/fs/erofs/erofs_fs.h @@ -17,13 +17,13 @@ #define EROFS_FEATURE_COMPAT_XATTR_FILTER 0x00000004 #define EROFS_FEATURE_COMPAT_SHARED_EA_IN_METABOX 0x00000008 #define EROFS_FEATURE_COMPAT_PLAIN_XATTR_PFX 0x00000010 - +#define EROFS_FEATURE_COMPAT_ISHARE_XATTRS 0x00000020 /* * Any bits that aren't in EROFS_ALL_FEATURE_INCOMPAT should * be incompatible with this kernel version. */ -#define EROFS_FEATURE_INCOMPAT_ZERO_PADDING 0x00000001 +#define EROFS_FEATURE_INCOMPAT_LZ4_0PADDING 0x00000001 #define EROFS_FEATURE_INCOMPAT_COMPR_CFGS 0x00000002 #define EROFS_FEATURE_INCOMPAT_BIG_PCLUSTER 0x00000002 #define EROFS_FEATURE_INCOMPAT_CHUNKED_FILE 0x00000004 @@ -83,7 +83,8 @@ struct erofs_super_block { __le32 xattr_prefix_start; /* start of long xattr prefixes */ __le64 packed_nid; /* nid of the special packed inode */ __u8 xattr_filter_reserved; /* reserved for xattr name filter */ - __u8 reserved[3]; + __u8 ishare_xattr_prefix_id; + __u8 reserved[2]; __le32 build_time; /* seconds added to epoch for mkfs time */ __le64 rootnid_8b; /* (48BIT on) nid of root directory */ __le64 reserved2; diff --git a/fs/erofs/fileio.c b/fs/erofs/fileio.c index 932e8b353ba1..4d5054dcac95 100644 --- a/fs/erofs/fileio.c +++ b/fs/erofs/fileio.c @@ -10,6 +10,7 @@ struct erofs_fileio_rq { struct bio bio; struct kiocb iocb; struct super_block *sb; + refcount_t ref; }; struct erofs_fileio { @@ -24,31 +25,28 @@ static void erofs_fileio_ki_complete(struct kiocb *iocb, long ret) container_of(iocb, struct erofs_fileio_rq, iocb); struct folio_iter fi; - if (ret > 0) { - if (ret != rq->bio.bi_iter.bi_size) { - bio_advance(&rq->bio, ret); - zero_fill_bio(&rq->bio); - } - ret = 0; + if (ret >= 0 && ret != rq->bio.bi_iter.bi_size) { + bio_advance(&rq->bio, ret); + zero_fill_bio(&rq->bio); } - if (rq->bio.bi_end_io) { - if (ret < 0 && !rq->bio.bi_status) - rq->bio.bi_status = errno_to_blk_status(ret); - } else { + if (!rq->bio.bi_end_io) { bio_for_each_folio_all(fi, &rq->bio) { DBG_BUGON(folio_test_uptodate(fi.folio)); - erofs_onlinefolio_end(fi.folio, ret, false); + erofs_onlinefolio_end(fi.folio, ret < 0, false); } + } else if (ret < 0 && !rq->bio.bi_status) { + rq->bio.bi_status = errno_to_blk_status(ret); } bio_endio(&rq->bio); bio_uninit(&rq->bio); - kfree(rq); + if (refcount_dec_and_test(&rq->ref)) + kfree(rq); } static void erofs_fileio_rq_submit(struct erofs_fileio_rq *rq) { struct iov_iter iter; - int ret; + ssize_t ret; if (!rq) return; @@ -64,6 +62,8 @@ static void erofs_fileio_rq_submit(struct erofs_fileio_rq *rq) ret = vfs_iocb_iter_read(rq->iocb.ki_filp, &rq->iocb, &iter); if (ret != -EIOCBQUEUED) erofs_fileio_ki_complete(&rq->iocb, ret); + if (refcount_dec_and_test(&rq->ref)) + kfree(rq); } static struct erofs_fileio_rq *erofs_fileio_rq_alloc(struct erofs_map_dev *mdev) @@ -74,6 +74,7 @@ static struct erofs_fileio_rq *erofs_fileio_rq_alloc(struct erofs_map_dev *mdev) bio_init(&rq->bio, NULL, rq->bvecs, ARRAY_SIZE(rq->bvecs), REQ_OP_READ); rq->iocb.ki_filp = mdev->m_dif->file; rq->sb = mdev->m_sb; + refcount_set(&rq->ref, 2); return rq; } @@ -88,9 +89,9 @@ void erofs_fileio_submit_bio(struct bio *bio) bio)); } -static int erofs_fileio_scan_folio(struct erofs_fileio *io, struct folio *folio) +static int erofs_fileio_scan_folio(struct erofs_fileio *io, + struct inode *inode, struct folio *folio) { - struct inode *inode = folio_inode(folio); struct erofs_map_blocks *map = &io->map; unsigned int cur = 0, end = folio_size(folio), len, attached = 0; loff_t pos = folio_pos(folio), ofs; @@ -158,31 +159,38 @@ io_retry: static int erofs_fileio_read_folio(struct file *file, struct folio *folio) { + bool need_iput; + struct inode *realinode = erofs_real_inode(folio_inode(folio), &need_iput); struct erofs_fileio io = {}; int err; - trace_erofs_read_folio(folio, true); - err = erofs_fileio_scan_folio(&io, folio); + trace_erofs_read_folio(realinode, folio, true); + err = erofs_fileio_scan_folio(&io, realinode, folio); erofs_fileio_rq_submit(io.rq); + if (need_iput) + iput(realinode); return err; } static void erofs_fileio_readahead(struct readahead_control *rac) { - struct inode *inode = rac->mapping->host; + bool need_iput; + struct inode *realinode = erofs_real_inode(rac->mapping->host, &need_iput); struct erofs_fileio io = {}; struct folio *folio; int err; - trace_erofs_readahead(inode, readahead_index(rac), + trace_erofs_readahead(realinode, readahead_index(rac), readahead_count(rac), true); while ((folio = readahead_folio(rac))) { - err = erofs_fileio_scan_folio(&io, folio); + err = erofs_fileio_scan_folio(&io, realinode, folio); if (err && err != -EINTR) - erofs_err(inode->i_sb, "readahead error at folio %lu @ nid %llu", - folio->index, EROFS_I(inode)->nid); + erofs_err(realinode->i_sb, "readahead error at folio %lu @ nid %llu", + folio->index, EROFS_I(realinode)->nid); } erofs_fileio_rq_submit(io.rq); + if (need_iput) + iput(realinode); } const struct address_space_operations erofs_fileio_aops = { diff --git a/fs/erofs/fscache.c b/fs/erofs/fscache.c index 7a346e20f7b7..a2cc0f3fa9d0 100644 --- a/fs/erofs/fscache.c +++ b/fs/erofs/fscache.c @@ -3,7 +3,6 @@ * Copyright (C) 2022, Alibaba Cloud * Copyright (C) 2022, Bytedance Inc. All rights reserved. */ -#include <linux/pseudo_fs.h> #include <linux/fscache.h> #include "internal.h" @@ -13,18 +12,6 @@ static LIST_HEAD(erofs_domain_list); static LIST_HEAD(erofs_domain_cookies_list); static struct vfsmount *erofs_pseudo_mnt; -static int erofs_anon_init_fs_context(struct fs_context *fc) -{ - return init_pseudo(fc, EROFS_SUPER_MAGIC) ? 0 : -ENOMEM; -} - -static struct file_system_type erofs_anon_fs_type = { - .owner = THIS_MODULE, - .name = "pseudo_erofs", - .init_fs_context = erofs_anon_init_fs_context, - .kill_sb = kill_anon_super, -}; - struct erofs_fscache_io { struct netfs_cache_resources cres; struct iov_iter iter; @@ -392,7 +379,7 @@ static void erofs_fscache_domain_put(struct erofs_domain *domain) } fscache_relinquish_volume(domain->volume, NULL, false); mutex_unlock(&erofs_domain_list_lock); - kfree(domain->domain_id); + kfree_sensitive(domain->domain_id); kfree(domain); return; } @@ -459,7 +446,7 @@ static int erofs_fscache_init_domain(struct super_block *sb) sbi->domain = domain; return 0; out: - kfree(domain->domain_id); + kfree_sensitive(domain->domain_id); kfree(domain); return err; } diff --git a/fs/erofs/inode.c b/fs/erofs/inode.c index bce98c845a18..4f86169c23f1 100644 --- a/fs/erofs/inode.c +++ b/fs/erofs/inode.c @@ -8,21 +8,29 @@ #include <linux/compat.h> #include <trace/events/erofs.h> -static int erofs_fill_symlink(struct inode *inode, void *kaddr, - unsigned int m_pofs) +static int erofs_fill_symlink(struct inode *inode, void *bptr, unsigned int ofs) { struct erofs_inode *vi = EROFS_I(inode); - loff_t off; - - m_pofs += vi->xattr_isize; - /* check if it cannot be handled with fast symlink scheme */ - if (vi->datalayout != EROFS_INODE_FLAT_INLINE || - check_add_overflow(m_pofs, inode->i_size, &off) || - off > i_blocksize(inode)) - return 0; - - inode->i_link = kmemdup_nul(kaddr + m_pofs, inode->i_size, GFP_KERNEL); - return inode->i_link ? 0 : -ENOMEM; + char *link; + loff_t end; + + ofs += vi->xattr_isize; + /* check whether the symlink data is small enough to be inlined */ + if (vi->datalayout == EROFS_INODE_FLAT_INLINE && + !check_add_overflow(ofs, inode->i_size, &end) && + end <= i_blocksize(inode)) { + link = kmemdup_nul(bptr + ofs, inode->i_size, GFP_KERNEL); + if (!link) + return -ENOMEM; + if (unlikely(!inode->i_size || strlen(link) != inode->i_size)) { + erofs_err(inode->i_sb, "invalid fast symlink size %llu @ nid %llu", + inode->i_size | 0ULL, vi->nid); + kfree(link); + return -EFSCORRUPTED; + } + inode_set_cached_link(inode, link, inode->i_size); + } + return 0; } static int erofs_read_inode(struct inode *inode) @@ -137,6 +145,11 @@ static int erofs_read_inode(struct inode *inode) err = -EFSCORRUPTED; goto err_out; } + + if (IS_ENABLED(CONFIG_EROFS_FS_POSIX_ACL) && + erofs_inode_has_noacl(inode, ptr, ofs)) + cache_no_acl(inode); + switch (inode->i_mode & S_IFMT) { case S_IFDIR: vi->dot_omitted = (ifmt >> EROFS_I_DOT_OMITTED_BIT) & 1; @@ -170,11 +183,17 @@ static int erofs_read_inode(struct inode *inode) goto err_out; } - if (erofs_inode_is_data_compressed(vi->datalayout)) - inode->i_blocks = le32_to_cpu(copied.i_u.blocks_lo) << - (sb->s_blocksize_bits - 9); - else + if (!erofs_inode_is_data_compressed(vi->datalayout)) { inode->i_blocks = round_up(inode->i_size, sb->s_blocksize) >> 9; + } else if (!IS_ENABLED(CONFIG_EROFS_FS_ZIP) || !sbi->available_compr_algs) { + erofs_err(sb, "compressed inode (nid %llu) is invalid in a plain filesystem", + vi->nid); + err = -EFSCORRUPTED; + goto err_out; + } else { + inode->i_blocks = le32_to_cpu(copied.i_u.blocks_lo) << + (sb->s_blocksize_bits - 9); + } if (vi->datalayout == EROFS_INODE_CHUNK_BASED) { /* fill chunked inode summary info */ @@ -203,7 +222,6 @@ err_out: static int erofs_fill_inode(struct inode *inode) { - struct erofs_inode *vi = EROFS_I(inode); int err; trace_erofs_fill_inode(inode); @@ -214,7 +232,8 @@ static int erofs_fill_inode(struct inode *inode) switch (inode->i_mode & S_IFMT) { case S_IFREG: inode->i_op = &erofs_generic_iops; - inode->i_fop = &erofs_file_fops; + inode->i_fop = erofs_ishare_fill_inode(inode) ? + &erofs_ishare_fops : &erofs_file_fops; break; case S_IFDIR: inode->i_op = &erofs_dir_iops; @@ -235,28 +254,7 @@ static int erofs_fill_inode(struct inode *inode) } mapping_set_large_folios(inode->i_mapping); - if (erofs_inode_is_data_compressed(vi->datalayout)) { -#ifdef CONFIG_EROFS_FS_ZIP - DO_ONCE_LITE_IF(inode->i_blkbits != PAGE_SHIFT, - erofs_info, inode->i_sb, - "EXPERIMENTAL EROFS subpage compressed block support in use. Use at your own risk!"); - inode->i_mapping->a_ops = &z_erofs_aops; -#else - err = -EOPNOTSUPP; -#endif - } else { - inode->i_mapping->a_ops = &erofs_aops; -#ifdef CONFIG_EROFS_FS_ONDEMAND - if (erofs_is_fscache_mode(inode->i_sb)) - inode->i_mapping->a_ops = &erofs_fscache_access_aops; -#endif -#ifdef CONFIG_EROFS_FS_BACKED_BY_FILE - if (erofs_is_fileio_mode(EROFS_SB(inode->i_sb))) - inode->i_mapping->a_ops = &erofs_fileio_aops; -#endif - } - - return err; + return erofs_inode_set_aops(inode, inode, false); } /* diff --git a/fs/erofs/internal.h b/fs/erofs/internal.h index f7f622836198..d1634455e389 100644 --- a/fs/erofs/internal.h +++ b/fs/erofs/internal.h @@ -59,10 +59,6 @@ enum { struct erofs_mount_opts { /* current strategy of how to use managed cache */ unsigned char cache_strategy; - /* strategy of sync decompression (0 - auto, 1 - force on, 2 - force off) */ - unsigned int sync_decompress; - /* threshold for decompression synchronously */ - unsigned int max_sync_decompress_pages; unsigned int mount_opt; }; @@ -116,8 +112,8 @@ struct erofs_sb_info { /* managed XArray arranged in physical block number */ struct xarray managed_pslots; + unsigned int sync_decompress; /* strategy for sync decompression */ unsigned int shrinker_run_no; - u16 available_compr_algs; /* pseudo inode to manage cached pages */ struct inode *managed_cache; @@ -134,6 +130,7 @@ struct erofs_sb_info { u32 xattr_blkaddr; u32 xattr_prefix_start; u8 xattr_prefix_count; + u8 ishare_xattr_prefix_id; struct erofs_xattr_prefix_item *xattr_prefixes; unsigned int xattr_filter_reserved; #endif @@ -156,6 +153,7 @@ struct erofs_sb_info { char *volume_name; u32 feature_compat; u32 feature_incompat; + u16 available_compr_algs; /* sysfs support */ struct kobject s_kobj; /* /sys/fs/erofs/<devname> */ @@ -178,6 +176,7 @@ struct erofs_sb_info { #define EROFS_MOUNT_DAX_ALWAYS 0x00000040 #define EROFS_MOUNT_DAX_NEVER 0x00000080 #define EROFS_MOUNT_DIRECT_IO 0x00000100 +#define EROFS_MOUNT_INODE_SHARE 0x00000200 #define clear_opt(opt, option) ((opt)->mount_opt &= ~EROFS_MOUNT_##option) #define set_opt(opt, option) ((opt)->mount_opt |= EROFS_MOUNT_##option) @@ -188,6 +187,8 @@ static inline bool erofs_is_fileio_mode(struct erofs_sb_info *sbi) return IS_ENABLED(CONFIG_EROFS_FS_BACKED_BY_FILE) && sbi->dif0.file; } +extern struct file_system_type erofs_anon_fs_type; + static inline bool erofs_is_fscache_mode(struct super_block *sb) { return IS_ENABLED(CONFIG_EROFS_FS_ONDEMAND) && @@ -220,7 +221,7 @@ static inline bool erofs_sb_has_##name(struct erofs_sb_info *sbi) \ return sbi->feature_##compat & EROFS_FEATURE_##feature; \ } -EROFS_FEATURE_FUNCS(zero_padding, incompat, INCOMPAT_ZERO_PADDING) +EROFS_FEATURE_FUNCS(lz4_0padding, incompat, INCOMPAT_LZ4_0PADDING) EROFS_FEATURE_FUNCS(compr_cfgs, incompat, INCOMPAT_COMPR_CFGS) EROFS_FEATURE_FUNCS(big_pcluster, incompat, INCOMPAT_BIG_PCLUSTER) EROFS_FEATURE_FUNCS(chunked_file, incompat, INCOMPAT_CHUNKED_FILE) @@ -236,6 +237,7 @@ EROFS_FEATURE_FUNCS(sb_chksum, compat, COMPAT_SB_CHKSUM) EROFS_FEATURE_FUNCS(xattr_filter, compat, COMPAT_XATTR_FILTER) EROFS_FEATURE_FUNCS(shared_ea_in_metabox, compat, COMPAT_SHARED_EA_IN_METABOX) EROFS_FEATURE_FUNCS(plain_xattr_pfx, compat, COMPAT_PLAIN_XATTR_PFX) +EROFS_FEATURE_FUNCS(ishare_xattrs, compat, COMPAT_ISHARE_XATTRS) static inline u64 erofs_nid_to_ino64(struct erofs_sb_info *sbi, erofs_nid_t nid) { @@ -265,6 +267,11 @@ static inline u64 erofs_nid_to_ino64(struct erofs_sb_info *sbi, erofs_nid_t nid) /* default readahead size of directories */ #define EROFS_DIR_RA_BYTES 16384 +struct erofs_inode_fingerprint { + u8 *opaque; + int size; +}; + struct erofs_inode { erofs_nid_t nid; @@ -300,6 +307,18 @@ struct erofs_inode { }; #endif /* CONFIG_EROFS_FS_ZIP */ }; +#ifdef CONFIG_EROFS_FS_PAGE_CACHE_SHARE + struct list_head ishare_list; + union { + /* for each anon shared inode */ + struct { + struct erofs_inode_fingerprint fingerprint; + spinlock_t ishare_lock; + }; + /* for each real inode */ + struct inode *sharedinode; + }; +#endif /* the corresponding vfs inode */ struct inode vfs_inode; }; @@ -406,6 +425,7 @@ extern const struct inode_operations erofs_dir_iops; extern const struct file_operations erofs_file_fops; extern const struct file_operations erofs_dir_fops; +extern const struct file_operations erofs_ishare_fops; extern const struct iomap_ops z_erofs_iomap_report_ops; @@ -451,6 +471,28 @@ static inline void *erofs_vm_map_ram(struct page **pages, unsigned int count) return NULL; } +static inline int erofs_inode_set_aops(struct inode *inode, + struct inode *realinode, bool no_fscache) +{ + if (erofs_inode_is_data_compressed(EROFS_I(realinode)->datalayout)) { + if (!IS_ENABLED(CONFIG_EROFS_FS_ZIP)) + return -EOPNOTSUPP; + DO_ONCE_LITE_IF(realinode->i_blkbits != PAGE_SHIFT, + erofs_info, realinode->i_sb, + "EXPERIMENTAL EROFS subpage compressed block support in use. Use at your own risk!"); + inode->i_mapping->a_ops = &z_erofs_aops; + return 0; + } + inode->i_mapping->a_ops = &erofs_aops; + if (IS_ENABLED(CONFIG_EROFS_FS_ONDEMAND) && !no_fscache && + erofs_is_fscache_mode(realinode->i_sb)) + inode->i_mapping->a_ops = &erofs_fscache_access_aops; + if (IS_ENABLED(CONFIG_EROFS_FS_BACKED_BY_FILE) && + erofs_is_fileio_mode(EROFS_SB(realinode->i_sb))) + inode->i_mapping->a_ops = &erofs_fileio_aops; + return 0; +} + int erofs_register_sysfs(struct super_block *sb); void erofs_unregister_sysfs(struct super_block *sb); int __init erofs_init_sysfs(void); @@ -488,7 +530,6 @@ void z_erofs_put_gbuf(void *ptr); int z_erofs_gbuf_growsize(unsigned int nrpages); int __init z_erofs_gbuf_init(void); void z_erofs_gbuf_exit(void); -int z_erofs_parse_cfgs(struct super_block *sb, struct erofs_super_block *dsb); #else static inline void erofs_shrinker_register(struct super_block *sb) {} static inline void erofs_shrinker_unregister(struct super_block *sb) {} @@ -498,6 +539,7 @@ static inline int z_erofs_init_subsystem(void) { return 0; } static inline void z_erofs_exit_subsystem(void) {} static inline int z_erofs_init_super(struct super_block *sb) { return 0; } #endif /* !CONFIG_EROFS_FS_ZIP */ +int z_erofs_parse_cfgs(struct super_block *sb, struct erofs_super_block *dsb); #ifdef CONFIG_EROFS_FS_BACKED_BY_FILE struct bio *erofs_fileio_bio_alloc(struct erofs_map_dev *mdev); @@ -537,10 +579,26 @@ static inline struct bio *erofs_fscache_bio_alloc(struct erofs_map_dev *mdev) { static inline void erofs_fscache_submit_bio(struct bio *bio) {} #endif +#ifdef CONFIG_EROFS_FS_PAGE_CACHE_SHARE +int __init erofs_init_ishare(void); +void erofs_exit_ishare(void); +bool erofs_ishare_fill_inode(struct inode *inode); +void erofs_ishare_free_inode(struct inode *inode); +struct inode *erofs_real_inode(struct inode *inode, bool *need_iput); +#else +static inline int erofs_init_ishare(void) { return 0; } +static inline void erofs_exit_ishare(void) {} +static inline bool erofs_ishare_fill_inode(struct inode *inode) { return false; } +static inline void erofs_ishare_free_inode(struct inode *inode) {} +static inline struct inode *erofs_real_inode(struct inode *inode, bool *need_iput) +{ + *need_iput = false; + return inode; +} +#endif + long erofs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); long erofs_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); -#define EFSCORRUPTED EUCLEAN /* Filesystem is corrupted */ - #endif /* __EROFS_INTERNAL_H */ diff --git a/fs/erofs/ishare.c b/fs/erofs/ishare.c new file mode 100644 index 000000000000..ce980320a8b9 --- /dev/null +++ b/fs/erofs/ishare.c @@ -0,0 +1,206 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2024, Alibaba Cloud + */ +#include <linux/xxhash.h> +#include <linux/mount.h> +#include "internal.h" +#include "xattr.h" + +#include "../internal.h" + +static struct vfsmount *erofs_ishare_mnt; + +static inline bool erofs_is_ishare_inode(struct inode *inode) +{ + /* assumed FS_ONDEMAND is excluded with FS_PAGE_CACHE_SHARE feature */ + return inode->i_sb->s_type == &erofs_anon_fs_type; +} + +static int erofs_ishare_iget5_eq(struct inode *inode, void *data) +{ + struct erofs_inode_fingerprint *fp1 = &EROFS_I(inode)->fingerprint; + struct erofs_inode_fingerprint *fp2 = data; + + return fp1->size == fp2->size && + !memcmp(fp1->opaque, fp2->opaque, fp2->size); +} + +static int erofs_ishare_iget5_set(struct inode *inode, void *data) +{ + struct erofs_inode *vi = EROFS_I(inode); + + vi->fingerprint = *(struct erofs_inode_fingerprint *)data; + INIT_LIST_HEAD(&vi->ishare_list); + spin_lock_init(&vi->ishare_lock); + return 0; +} + +bool erofs_ishare_fill_inode(struct inode *inode) +{ + struct erofs_sb_info *sbi = EROFS_SB(inode->i_sb); + struct erofs_inode *vi = EROFS_I(inode); + struct erofs_inode_fingerprint fp; + struct inode *sharedinode; + unsigned long hash; + + if (erofs_xattr_fill_inode_fingerprint(&fp, inode, sbi->domain_id)) + return false; + hash = xxh32(fp.opaque, fp.size, 0); + sharedinode = iget5_locked(erofs_ishare_mnt->mnt_sb, hash, + erofs_ishare_iget5_eq, erofs_ishare_iget5_set, + &fp); + if (!sharedinode) { + kfree(fp.opaque); + return false; + } + + if (inode_state_read_once(sharedinode) & I_NEW) { + if (erofs_inode_set_aops(sharedinode, inode, true)) { + iget_failed(sharedinode); + kfree(fp.opaque); + return false; + } + sharedinode->i_size = vi->vfs_inode.i_size; + unlock_new_inode(sharedinode); + } else { + kfree(fp.opaque); + if (sharedinode->i_size != vi->vfs_inode.i_size) { + _erofs_printk(inode->i_sb, KERN_WARNING + "size(%lld:%lld) not matches for the same fingerprint\n", + vi->vfs_inode.i_size, sharedinode->i_size); + iput(sharedinode); + return false; + } + } + vi->sharedinode = sharedinode; + INIT_LIST_HEAD(&vi->ishare_list); + spin_lock(&EROFS_I(sharedinode)->ishare_lock); + list_add(&vi->ishare_list, &EROFS_I(sharedinode)->ishare_list); + spin_unlock(&EROFS_I(sharedinode)->ishare_lock); + return true; +} + +void erofs_ishare_free_inode(struct inode *inode) +{ + struct erofs_inode *vi = EROFS_I(inode); + struct inode *sharedinode = vi->sharedinode; + + if (!sharedinode) + return; + spin_lock(&EROFS_I(sharedinode)->ishare_lock); + list_del(&vi->ishare_list); + spin_unlock(&EROFS_I(sharedinode)->ishare_lock); + iput(sharedinode); + vi->sharedinode = NULL; +} + +static int erofs_ishare_file_open(struct inode *inode, struct file *file) +{ + struct inode *sharedinode = EROFS_I(inode)->sharedinode; + struct file *realfile; + + if (file->f_flags & O_DIRECT) + return -EINVAL; + realfile = alloc_empty_backing_file(O_RDONLY|O_NOATIME, current_cred()); + if (IS_ERR(realfile)) + return PTR_ERR(realfile); + ihold(sharedinode); + realfile->f_op = &erofs_file_fops; + realfile->f_inode = sharedinode; + realfile->f_mapping = sharedinode->i_mapping; + path_get(&file->f_path); + backing_file_set_user_path(realfile, &file->f_path); + + file_ra_state_init(&realfile->f_ra, file->f_mapping); + realfile->private_data = EROFS_I(inode); + file->private_data = realfile; + return 0; +} + +static int erofs_ishare_file_release(struct inode *inode, struct file *file) +{ + struct file *realfile = file->private_data; + + iput(realfile->f_inode); + fput(realfile); + file->private_data = NULL; + return 0; +} + +static ssize_t erofs_ishare_file_read_iter(struct kiocb *iocb, + struct iov_iter *to) +{ + struct file *realfile = iocb->ki_filp->private_data; + struct kiocb dedup_iocb; + ssize_t nread; + + if (!iov_iter_count(to)) + return 0; + kiocb_clone(&dedup_iocb, iocb, realfile); + nread = filemap_read(&dedup_iocb, to, 0); + iocb->ki_pos = dedup_iocb.ki_pos; + return nread; +} + +static int erofs_ishare_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct file *realfile = file->private_data; + + vma_set_file(vma, realfile); + return generic_file_readonly_mmap(file, vma); +} + +static int erofs_ishare_fadvise(struct file *file, loff_t offset, + loff_t len, int advice) +{ + return vfs_fadvise(file->private_data, offset, len, advice); +} + +const struct file_operations erofs_ishare_fops = { + .open = erofs_ishare_file_open, + .llseek = generic_file_llseek, + .read_iter = erofs_ishare_file_read_iter, + .mmap = erofs_ishare_mmap, + .release = erofs_ishare_file_release, + .get_unmapped_area = thp_get_unmapped_area, + .splice_read = filemap_splice_read, + .fadvise = erofs_ishare_fadvise, +}; + +struct inode *erofs_real_inode(struct inode *inode, bool *need_iput) +{ + struct erofs_inode *vi, *vi_share; + struct inode *realinode; + + *need_iput = false; + if (!erofs_is_ishare_inode(inode)) + return inode; + + vi_share = EROFS_I(inode); + spin_lock(&vi_share->ishare_lock); + /* fetch any one as real inode */ + DBG_BUGON(list_empty(&vi_share->ishare_list)); + list_for_each_entry(vi, &vi_share->ishare_list, ishare_list) { + realinode = igrab(&vi->vfs_inode); + if (realinode) { + *need_iput = true; + break; + } + } + spin_unlock(&vi_share->ishare_lock); + + DBG_BUGON(!realinode); + return realinode; +} + +int __init erofs_init_ishare(void) +{ + erofs_ishare_mnt = kern_mount(&erofs_anon_fs_type); + return PTR_ERR_OR_ZERO(erofs_ishare_mnt); +} + +void erofs_exit_ishare(void) +{ + kern_unmount(erofs_ishare_mnt); +} diff --git a/fs/erofs/super.c b/fs/erofs/super.c index 5136cda5972a..7827e61424b7 100644 --- a/fs/erofs/super.c +++ b/fs/erofs/super.c @@ -11,6 +11,7 @@ #include <linux/fs_parser.h> #include <linux/exportfs.h> #include <linux/backing-dev.h> +#include <linux/pseudo_fs.h> #include "xattr.h" #define CREATE_TRACE_POINTS @@ -121,18 +122,6 @@ void *erofs_read_metadata(struct super_block *sb, struct erofs_buf *buf, return buffer; } -#ifndef CONFIG_EROFS_FS_ZIP -static int z_erofs_parse_cfgs(struct super_block *sb, - struct erofs_super_block *dsb) -{ - if (!dsb->u1.available_compr_algs) - return 0; - - erofs_err(sb, "compression disabled, unable to mount compressed EROFS"); - return -EOPNOTSUPP; -} -#endif - static int erofs_init_device(struct erofs_buf *buf, struct super_block *sb, struct erofs_device_info *dif, erofs_off_t *pos) { @@ -319,6 +308,15 @@ static int erofs_read_superblock(struct super_block *sb) sbi->xattr_prefix_start = le32_to_cpu(dsb->xattr_prefix_start); sbi->xattr_prefix_count = dsb->xattr_prefix_count; sbi->xattr_filter_reserved = dsb->xattr_filter_reserved; + if (erofs_sb_has_ishare_xattrs(sbi)) { + if (dsb->ishare_xattr_prefix_id >= sbi->xattr_prefix_count) { + erofs_err(sb, "invalid ishare xattr prefix id %u", + dsb->ishare_xattr_prefix_id); + ret = -EFSCORRUPTED; + goto out; + } + sbi->ishare_xattr_prefix_id = dsb->ishare_xattr_prefix_id; + } #endif sbi->islotbits = ilog2(sizeof(struct erofs_inode_compact)); if (erofs_sb_has_48bit(sbi) && dsb->rootnid_8b) { @@ -330,12 +328,13 @@ static int erofs_read_superblock(struct super_block *sb) } sbi->packed_nid = le64_to_cpu(dsb->packed_nid); if (erofs_sb_has_metabox(sbi)) { + ret = -EFSCORRUPTED; if (sbi->sb_size <= offsetof(struct erofs_super_block, metabox_nid)) - return -EFSCORRUPTED; + goto out; sbi->metabox_nid = le64_to_cpu(dsb->metabox_nid); if (sbi->metabox_nid & BIT_ULL(EROFS_DIRENT_NID_METABOX_BIT)) - return -EFSCORRUPTED; /* self-loop detection */ + goto out; /* self-loop detection */ } sbi->inos = le64_to_cpu(dsb->inos); @@ -346,14 +345,22 @@ static int erofs_read_superblock(struct super_block *sb) if (dsb->volume_name[0]) { sbi->volume_name = kstrndup(dsb->volume_name, sizeof(dsb->volume_name), GFP_KERNEL); - if (!sbi->volume_name) - return -ENOMEM; + if (!sbi->volume_name) { + ret = -ENOMEM; + goto out; + } } - /* parse on-disk compression configurations */ - ret = z_erofs_parse_cfgs(sb, dsb); - if (ret < 0) + if (IS_ENABLED(CONFIG_EROFS_FS_ZIP)) { + ret = z_erofs_parse_cfgs(sb, dsb); + if (ret < 0) + goto out; + } else if (dsb->u1.available_compr_algs || + erofs_sb_has_lz4_0padding(sbi)) { + erofs_err(sb, "compression disabled, unable to mount compressed EROFS"); + ret = -EOPNOTSUPP; goto out; + } ret = erofs_scan_devices(sb, dsb); @@ -372,20 +379,18 @@ static void erofs_default_options(struct erofs_sb_info *sbi) { #ifdef CONFIG_EROFS_FS_ZIP sbi->opt.cache_strategy = EROFS_ZIP_CACHE_READAROUND; - sbi->opt.max_sync_decompress_pages = 3; - sbi->opt.sync_decompress = EROFS_SYNC_DECOMPRESS_AUTO; -#endif -#ifdef CONFIG_EROFS_FS_XATTR - set_opt(&sbi->opt, XATTR_USER); -#endif -#ifdef CONFIG_EROFS_FS_POSIX_ACL - set_opt(&sbi->opt, POSIX_ACL); + sbi->sync_decompress = EROFS_SYNC_DECOMPRESS_AUTO; #endif + if (IS_ENABLED(CONFIG_EROFS_FS_XATTR)) + set_opt(&sbi->opt, XATTR_USER); + if (IS_ENABLED(CONFIG_EROFS_FS_POSIX_ACL)) + set_opt(&sbi->opt, POSIX_ACL); } enum { Opt_user_xattr, Opt_acl, Opt_cache_strategy, Opt_dax, Opt_dax_enum, Opt_device, Opt_fsid, Opt_domain_id, Opt_directio, Opt_fsoffset, + Opt_inode_share, }; static const struct constant_table erofs_param_cache_strategy[] = { @@ -413,6 +418,7 @@ static const struct fs_parameter_spec erofs_fs_parameters[] = { fsparam_string("domain_id", Opt_domain_id), fsparam_flag_no("directio", Opt_directio), fsparam_u64("fsoffset", Opt_fsoffset), + fsparam_flag("inode_share", Opt_inode_share), {} }; @@ -514,11 +520,11 @@ static int erofs_fc_parse_param(struct fs_context *fc, if (!sbi->fsid) return -ENOMEM; break; +#endif +#if defined(CONFIG_EROFS_FS_ONDEMAND) || defined(CONFIG_EROFS_FS_PAGE_CACHE_SHARE) case Opt_domain_id: - kfree(sbi->domain_id); - sbi->domain_id = kstrdup(param->string, GFP_KERNEL); - if (!sbi->domain_id) - return -ENOMEM; + kfree_sensitive(sbi->domain_id); + sbi->domain_id = no_free_ptr(param->string); break; #else case Opt_fsid: @@ -539,6 +545,13 @@ static int erofs_fc_parse_param(struct fs_context *fc, case Opt_fsoffset: sbi->dif0.fsoff = result.uint_64; break; + case Opt_inode_share: +#ifdef CONFIG_EROFS_FS_PAGE_CACHE_SHARE + set_opt(&sbi->opt, INODE_SHARE); +#else + errorfc(fc, "%s option not supported", erofs_fs_parameters[opt].name); +#endif + break; } return 0; } @@ -614,7 +627,7 @@ static void erofs_set_sysfs_name(struct super_block *sb) { struct erofs_sb_info *sbi = EROFS_SB(sb); - if (sbi->domain_id) + if (sbi->domain_id && sbi->fsid) super_set_sysfs_name_generic(sb, "%s,%s", sbi->domain_id, sbi->fsid); else if (sbi->fsid) @@ -637,6 +650,15 @@ static int erofs_fc_fill_super(struct super_block *sb, struct fs_context *fc) sb->s_maxbytes = MAX_LFS_FILESIZE; sb->s_op = &erofs_sops; + if (!sbi->domain_id && test_opt(&sbi->opt, INODE_SHARE)) { + errorfc(fc, "domain_id is needed when inode_ishare is on"); + return -EINVAL; + } + if (test_opt(&sbi->opt, DAX_ALWAYS) && test_opt(&sbi->opt, INODE_SHARE)) { + errorfc(fc, "FSDAX is not allowed when inode_ishare is on"); + return -EINVAL; + } + sbi->blkszbits = PAGE_SHIFT; if (!sb->s_bdev) { /* @@ -714,6 +736,12 @@ static int erofs_fc_fill_super(struct super_block *sb, struct fs_context *fc) erofs_info(sb, "unsupported blocksize for DAX"); clear_opt(&sbi->opt, DAX_ALWAYS); } + if (test_opt(&sbi->opt, INODE_SHARE) && !erofs_sb_has_ishare_xattrs(sbi)) { + erofs_info(sb, "on-disk ishare xattrs not found. Turning off inode_share."); + clear_opt(&sbi->opt, INODE_SHARE); + } + if (test_opt(&sbi->opt, INODE_SHARE)) + erofs_info(sb, "EXPERIMENTAL EROFS page cache share support in use. Use at your own risk!"); sb->s_time_gran = 1; sb->s_xattr = erofs_xattr_handlers; @@ -849,7 +877,7 @@ static void erofs_sb_free(struct erofs_sb_info *sbi) { erofs_free_dev_context(sbi->devs); kfree(sbi->fsid); - kfree(sbi->domain_id); + kfree_sensitive(sbi->domain_id); if (sbi->dif0.file) fput(sbi->dif0.file); kfree(sbi->volume_name); @@ -943,6 +971,41 @@ static struct file_system_type erofs_fs_type = { }; MODULE_ALIAS_FS("erofs"); +#if defined(CONFIG_EROFS_FS_ONDEMAND) || defined(CONFIG_EROFS_FS_PAGE_CACHE_SHARE) +static void erofs_free_anon_inode(struct inode *inode) +{ + struct erofs_inode *vi = EROFS_I(inode); + +#ifdef CONFIG_EROFS_FS_PAGE_CACHE_SHARE + kfree(vi->fingerprint.opaque); +#endif + kmem_cache_free(erofs_inode_cachep, vi); +} + +static const struct super_operations erofs_anon_sops = { + .alloc_inode = erofs_alloc_inode, + .drop_inode = inode_just_drop, + .free_inode = erofs_free_anon_inode, +}; + +static int erofs_anon_init_fs_context(struct fs_context *fc) +{ + struct pseudo_fs_context *ctx; + + ctx = init_pseudo(fc, EROFS_SUPER_MAGIC); + if (!ctx) + return -ENOMEM; + ctx->ops = &erofs_anon_sops; + return 0; +} + +struct file_system_type erofs_anon_fs_type = { + .name = "pseudo_erofs", + .init_fs_context = erofs_anon_init_fs_context, + .kill_sb = kill_anon_super, +}; +#endif + static int __init erofs_module_init(void) { int err; @@ -968,6 +1031,10 @@ static int __init erofs_module_init(void) if (err) goto sysfs_err; + err = erofs_init_ishare(); + if (err) + goto ishare_err; + err = register_filesystem(&erofs_fs_type); if (err) goto fs_err; @@ -975,6 +1042,8 @@ static int __init erofs_module_init(void) return 0; fs_err: + erofs_exit_ishare(); +ishare_err: erofs_exit_sysfs(); sysfs_err: z_erofs_exit_subsystem(); @@ -992,6 +1061,7 @@ static void __exit erofs_module_exit(void) /* Ensure all RCU free inodes / pclusters are safe to be destroyed. */ rcu_barrier(); + erofs_exit_ishare(); erofs_exit_sysfs(); z_erofs_exit_subsystem(); erofs_exit_shrinker(); @@ -1046,16 +1116,16 @@ static int erofs_show_options(struct seq_file *seq, struct dentry *root) #endif if (sbi->dif0.fsoff) seq_printf(seq, ",fsoffset=%llu", sbi->dif0.fsoff); + if (test_opt(opt, INODE_SHARE)) + seq_puts(seq, ",inode_share"); return 0; } static void erofs_evict_inode(struct inode *inode) { -#ifdef CONFIG_FS_DAX if (IS_DAX(inode)) dax_break_layout_final(inode); -#endif - + erofs_ishare_free_inode(inode); truncate_inode_pages_final(&inode->i_data); clear_inode(inode); } diff --git a/fs/erofs/sysfs.c b/fs/erofs/sysfs.c index 1e0658a1d95b..6734483a440f 100644 --- a/fs/erofs/sysfs.c +++ b/fs/erofs/sysfs.c @@ -59,7 +59,7 @@ static struct erofs_attr erofs_attr_##_name = { \ #define ATTR_LIST(name) (&erofs_attr_##name.attr) #ifdef CONFIG_EROFS_FS_ZIP -EROFS_ATTR_RW_UI(sync_decompress, erofs_mount_opts); +EROFS_ATTR_RW_UI(sync_decompress, erofs_sb_info); EROFS_ATTR_FUNC(drop_caches, 0200); #endif #ifdef CONFIG_EROFS_FS_ZIP_ACCEL @@ -86,7 +86,6 @@ static struct attribute *erofs_attrs[] = { ATTRIBUTE_GROUPS(erofs); /* Features this copy of erofs supports */ -EROFS_ATTR_FEATURE(zero_padding); EROFS_ATTR_FEATURE(compr_cfgs); EROFS_ATTR_FEATURE(big_pcluster); EROFS_ATTR_FEATURE(chunked_file); @@ -100,7 +99,6 @@ EROFS_ATTR_FEATURE(48bit); EROFS_ATTR_FEATURE(metabox); static struct attribute *erofs_feat_attrs[] = { - ATTR_LIST(zero_padding), ATTR_LIST(compr_cfgs), ATTR_LIST(big_pcluster), ATTR_LIST(chunked_file), @@ -170,11 +168,10 @@ static ssize_t erofs_attr_store(struct kobject *kobj, struct attribute *attr, return ret; if (t != (unsigned int)t) return -ERANGE; -#ifdef CONFIG_EROFS_FS_ZIP - if (!strcmp(a->attr.name, "sync_decompress") && + if (IS_ENABLED(CONFIG_EROFS_FS_ZIP) && + !strcmp(a->attr.name, "sync_decompress") && (t > EROFS_SYNC_DECOMPRESS_FORCE_OFF)) return -EINVAL; -#endif *(unsigned int *)ptr = t; return len; case attr_pointer_bool: diff --git a/fs/erofs/xattr.c b/fs/erofs/xattr.c index 396536d9a862..b7da1ed83160 100644 --- a/fs/erofs/xattr.c +++ b/fs/erofs/xattr.c @@ -25,15 +25,22 @@ struct erofs_xattr_iter { struct dentry *dentry; }; +static const char *erofs_xattr_prefix(unsigned int idx, struct dentry *dentry); + static int erofs_init_inode_xattrs(struct inode *inode) { - struct erofs_inode *const vi = EROFS_I(inode); - struct erofs_xattr_iter it; - unsigned int i; - struct erofs_xattr_ibody_header *ih; + struct erofs_buf buf = __EROFS_BUF_INITIALIZER; + struct erofs_inode *vi = EROFS_I(inode); struct super_block *sb = inode->i_sb; + const struct erofs_xattr_ibody_header *ih; + __le32 *xattr_id; + erofs_off_t pos; + unsigned int i; int ret = 0; + if (!vi->xattr_isize) + return -ENODATA; + /* the most case is that xattrs of this inode are initialized. */ if (test_bit(EROFS_I_EA_INITED_BIT, &vi->flags)) { /* @@ -43,7 +50,6 @@ static int erofs_init_inode_xattrs(struct inode *inode) smp_mb(); return 0; } - if (wait_on_bit_lock(&vi->flags, EROFS_I_BL_XATTR_BIT, TASK_KILLABLE)) return -ERESTARTSYS; @@ -60,139 +66,69 @@ static int erofs_init_inode_xattrs(struct inode *inode) * undefined right now (maybe use later with some new sb feature). */ if (vi->xattr_isize == sizeof(struct erofs_xattr_ibody_header)) { - erofs_err(sb, - "xattr_isize %d of nid %llu is not supported yet", + erofs_err(sb, "xattr_isize %d of nid %llu is not supported yet", vi->xattr_isize, vi->nid); ret = -EOPNOTSUPP; goto out_unlock; } else if (vi->xattr_isize < sizeof(struct erofs_xattr_ibody_header)) { - if (vi->xattr_isize) { - erofs_err(sb, "bogus xattr ibody @ nid %llu", vi->nid); - DBG_BUGON(1); - ret = -EFSCORRUPTED; - goto out_unlock; /* xattr ondisk layout error */ - } - ret = -ENODATA; + erofs_err(sb, "bogus xattr ibody @ nid %llu", vi->nid); + DBG_BUGON(1); + ret = -EFSCORRUPTED; goto out_unlock; } - it.buf = __EROFS_BUF_INITIALIZER; - ret = erofs_init_metabuf(&it.buf, sb, erofs_inode_in_metabox(inode)); - if (ret) - goto out_unlock; - it.pos = erofs_iloc(inode) + vi->inode_isize; - - /* read in shared xattr array (non-atomic, see kmalloc below) */ - it.kaddr = erofs_bread(&it.buf, it.pos, true); - if (IS_ERR(it.kaddr)) { - ret = PTR_ERR(it.kaddr); + pos = erofs_iloc(inode) + vi->inode_isize; + ih = erofs_read_metabuf(&buf, sb, pos, erofs_inode_in_metabox(inode)); + if (IS_ERR(ih)) { + ret = PTR_ERR(ih); goto out_unlock; } - - ih = it.kaddr; vi->xattr_name_filter = le32_to_cpu(ih->h_name_filter); vi->xattr_shared_count = ih->h_shared_count; vi->xattr_shared_xattrs = kmalloc_array(vi->xattr_shared_count, sizeof(uint), GFP_KERNEL); if (!vi->xattr_shared_xattrs) { - erofs_put_metabuf(&it.buf); + erofs_put_metabuf(&buf); ret = -ENOMEM; goto out_unlock; } - /* let's skip ibody header */ - it.pos += sizeof(struct erofs_xattr_ibody_header); - + /* skip the ibody header and read the shared xattr array */ + pos += sizeof(struct erofs_xattr_ibody_header); for (i = 0; i < vi->xattr_shared_count; ++i) { - it.kaddr = erofs_bread(&it.buf, it.pos, true); - if (IS_ERR(it.kaddr)) { + xattr_id = erofs_bread(&buf, pos + i * sizeof(__le32), true); + if (IS_ERR(xattr_id)) { kfree(vi->xattr_shared_xattrs); vi->xattr_shared_xattrs = NULL; - ret = PTR_ERR(it.kaddr); + ret = PTR_ERR(xattr_id); goto out_unlock; } - vi->xattr_shared_xattrs[i] = le32_to_cpu(*(__le32 *)it.kaddr); - it.pos += sizeof(__le32); + vi->xattr_shared_xattrs[i] = le32_to_cpu(*xattr_id); } - erofs_put_metabuf(&it.buf); + erofs_put_metabuf(&buf); /* paired with smp_mb() at the beginning of the function. */ smp_mb(); set_bit(EROFS_I_EA_INITED_BIT, &vi->flags); - out_unlock: clear_and_wake_up_bit(EROFS_I_BL_XATTR_BIT, &vi->flags); return ret; } -static bool erofs_xattr_user_list(struct dentry *dentry) -{ - return test_opt(&EROFS_SB(dentry->d_sb)->opt, XATTR_USER); -} - -static bool erofs_xattr_trusted_list(struct dentry *dentry) -{ - return capable(CAP_SYS_ADMIN); -} - -static int erofs_xattr_generic_get(const struct xattr_handler *handler, - struct dentry *unused, struct inode *inode, - const char *name, void *buffer, size_t size) -{ - if (handler->flags == EROFS_XATTR_INDEX_USER && - !test_opt(&EROFS_I_SB(inode)->opt, XATTR_USER)) - return -EOPNOTSUPP; - - return erofs_getxattr(inode, handler->flags, name, buffer, size); -} - -const struct xattr_handler erofs_xattr_user_handler = { - .prefix = XATTR_USER_PREFIX, - .flags = EROFS_XATTR_INDEX_USER, - .list = erofs_xattr_user_list, - .get = erofs_xattr_generic_get, -}; - -const struct xattr_handler erofs_xattr_trusted_handler = { - .prefix = XATTR_TRUSTED_PREFIX, - .flags = EROFS_XATTR_INDEX_TRUSTED, - .list = erofs_xattr_trusted_list, - .get = erofs_xattr_generic_get, -}; - -#ifdef CONFIG_EROFS_FS_SECURITY -const struct xattr_handler __maybe_unused erofs_xattr_security_handler = { - .prefix = XATTR_SECURITY_PREFIX, - .flags = EROFS_XATTR_INDEX_SECURITY, - .get = erofs_xattr_generic_get, -}; -#endif - -const struct xattr_handler * const erofs_xattr_handlers[] = { - &erofs_xattr_user_handler, - &erofs_xattr_trusted_handler, -#ifdef CONFIG_EROFS_FS_SECURITY - &erofs_xattr_security_handler, -#endif - NULL, -}; - static int erofs_xattr_copy_to_buffer(struct erofs_xattr_iter *it, unsigned int len) { unsigned int slice, processed; struct super_block *sb = it->sb; - void *src; for (processed = 0; processed < len; processed += slice) { it->kaddr = erofs_bread(&it->buf, it->pos, true); if (IS_ERR(it->kaddr)) return PTR_ERR(it->kaddr); - src = it->kaddr; slice = min_t(unsigned int, sb->s_blocksize - erofs_blkoff(sb, it->pos), len - processed); - memcpy(it->buffer + it->buffer_ofs, src, slice); + memcpy(it->buffer + it->buffer_ofs, it->kaddr, slice); it->buffer_ofs += slice; it->pos += slice; } @@ -391,8 +327,8 @@ static int erofs_xattr_iter_shared(struct erofs_xattr_iter *it, return i ? ret : -ENODATA; } -int erofs_getxattr(struct inode *inode, int index, const char *name, - void *buffer, size_t buffer_size) +static int erofs_getxattr(struct inode *inode, int index, const char *name, + void *buffer, size_t buffer_size) { int ret; unsigned int hashbit; @@ -462,6 +398,81 @@ ssize_t erofs_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size) return ret ? ret : it.buffer_ofs; } +static bool erofs_xattr_user_list(struct dentry *dentry) +{ + return test_opt(&EROFS_SB(dentry->d_sb)->opt, XATTR_USER); +} + +static bool erofs_xattr_trusted_list(struct dentry *dentry) +{ + return capable(CAP_SYS_ADMIN); +} + +static int erofs_xattr_generic_get(const struct xattr_handler *handler, + struct dentry *unused, struct inode *inode, + const char *name, void *buffer, size_t size) +{ + if (handler->flags == EROFS_XATTR_INDEX_USER && + !test_opt(&EROFS_I_SB(inode)->opt, XATTR_USER)) + return -EOPNOTSUPP; + + return erofs_getxattr(inode, handler->flags, name, buffer, size); +} + +static const struct xattr_handler erofs_xattr_user_handler = { + .prefix = XATTR_USER_PREFIX, + .flags = EROFS_XATTR_INDEX_USER, + .list = erofs_xattr_user_list, + .get = erofs_xattr_generic_get, +}; + +static const struct xattr_handler erofs_xattr_trusted_handler = { + .prefix = XATTR_TRUSTED_PREFIX, + .flags = EROFS_XATTR_INDEX_TRUSTED, + .list = erofs_xattr_trusted_list, + .get = erofs_xattr_generic_get, +}; + +#ifdef CONFIG_EROFS_FS_SECURITY +static const struct xattr_handler erofs_xattr_security_handler = { + .prefix = XATTR_SECURITY_PREFIX, + .flags = EROFS_XATTR_INDEX_SECURITY, + .get = erofs_xattr_generic_get, +}; +#endif + +const struct xattr_handler * const erofs_xattr_handlers[] = { + &erofs_xattr_user_handler, + &erofs_xattr_trusted_handler, +#ifdef CONFIG_EROFS_FS_SECURITY + &erofs_xattr_security_handler, +#endif + NULL, +}; + +static const char *erofs_xattr_prefix(unsigned int idx, struct dentry *dentry) +{ + static const struct xattr_handler * const xattr_handler_map[] = { + [EROFS_XATTR_INDEX_USER] = &erofs_xattr_user_handler, +#ifdef CONFIG_EROFS_FS_POSIX_ACL + [EROFS_XATTR_INDEX_POSIX_ACL_ACCESS] = &nop_posix_acl_access, + [EROFS_XATTR_INDEX_POSIX_ACL_DEFAULT] = &nop_posix_acl_default, +#endif + [EROFS_XATTR_INDEX_TRUSTED] = &erofs_xattr_trusted_handler, +#ifdef CONFIG_EROFS_FS_SECURITY + [EROFS_XATTR_INDEX_SECURITY] = &erofs_xattr_security_handler, +#endif + }; + const struct xattr_handler *handler = NULL; + + if (idx && idx < ARRAY_SIZE(xattr_handler_map)) { + handler = xattr_handler_map[idx]; + if (xattr_handler_can_list(handler, dentry)) + return xattr_prefix(handler); + } + return NULL; +} + void erofs_xattr_prefixes_cleanup(struct super_block *sb) { struct erofs_sb_info *sbi = EROFS_SB(sb); @@ -519,6 +530,19 @@ int erofs_xattr_prefixes_init(struct super_block *sb) } erofs_put_metabuf(&buf); + if (!ret && erofs_sb_has_ishare_xattrs(sbi)) { + struct erofs_xattr_prefix_item *pf = pfs + sbi->ishare_xattr_prefix_id; + struct erofs_xattr_long_prefix *newpfx; + + newpfx = krealloc(pf->prefix, + sizeof(*newpfx) + pf->infix_len + 1, GFP_KERNEL); + if (newpfx) { + newpfx->infix[pf->infix_len] = '\0'; + pf->prefix = newpfx; + } else { + ret = -ENOMEM; + } + } sbi->xattr_prefixes = pfs; if (ret) erofs_xattr_prefixes_cleanup(sb); @@ -563,4 +587,58 @@ struct posix_acl *erofs_get_acl(struct inode *inode, int type, bool rcu) kfree(value); return acl; } + +bool erofs_inode_has_noacl(struct inode *inode, void *kaddr, unsigned int ofs) +{ + static const unsigned int bitmask = + BIT(21) | /* system.posix_acl_default */ + BIT(30); /* system.posix_acl_access */ + struct erofs_sb_info *sbi = EROFS_I_SB(inode); + const struct erofs_xattr_ibody_header *ih = kaddr + ofs; + + if (EROFS_I(inode)->xattr_isize < sizeof(*ih)) + return true; + + if (erofs_sb_has_xattr_filter(sbi) && !sbi->xattr_filter_reserved && + !check_add_overflow(ofs, sizeof(*ih), &ofs) && + ofs <= i_blocksize(inode)) { + if ((le32_to_cpu(ih->h_name_filter) & bitmask) == bitmask) + return true; + } + return false; +} +#endif + +#ifdef CONFIG_EROFS_FS_PAGE_CACHE_SHARE +int erofs_xattr_fill_inode_fingerprint(struct erofs_inode_fingerprint *fp, + struct inode *inode, const char *domain_id) +{ + struct erofs_sb_info *sbi = EROFS_SB(inode->i_sb); + struct erofs_xattr_prefix_item *prefix; + const char *infix; + int valuelen, base_index; + + if (!test_opt(&sbi->opt, INODE_SHARE)) + return -EOPNOTSUPP; + if (!sbi->xattr_prefixes) + return -EINVAL; + prefix = sbi->xattr_prefixes + sbi->ishare_xattr_prefix_id; + infix = prefix->prefix->infix; + base_index = prefix->prefix->base_index; + valuelen = erofs_getxattr(inode, base_index, infix, NULL, 0); + if (valuelen <= 0 || valuelen > (1 << sbi->blkszbits)) + return -EFSCORRUPTED; + fp->size = valuelen + (domain_id ? strlen(domain_id) : 0); + fp->opaque = kmalloc(fp->size, GFP_KERNEL); + if (!fp->opaque) + return -ENOMEM; + if (valuelen != erofs_getxattr(inode, base_index, infix, + fp->opaque, valuelen)) { + kfree(fp->opaque); + fp->opaque = NULL; + return -EFSCORRUPTED; + } + memcpy(fp->opaque + valuelen, domain_id, fp->size - valuelen); + return 0; +} #endif diff --git a/fs/erofs/xattr.h b/fs/erofs/xattr.h index 6317caa8413e..4465f7018a41 100644 --- a/fs/erofs/xattr.h +++ b/fs/erofs/xattr.h @@ -11,51 +11,14 @@ #include <linux/xattr.h> #ifdef CONFIG_EROFS_FS_XATTR -extern const struct xattr_handler erofs_xattr_user_handler; -extern const struct xattr_handler erofs_xattr_trusted_handler; -extern const struct xattr_handler erofs_xattr_security_handler; - -static inline const char *erofs_xattr_prefix(unsigned int idx, - struct dentry *dentry) -{ - const struct xattr_handler *handler = NULL; - - static const struct xattr_handler * const xattr_handler_map[] = { - [EROFS_XATTR_INDEX_USER] = &erofs_xattr_user_handler, -#ifdef CONFIG_EROFS_FS_POSIX_ACL - [EROFS_XATTR_INDEX_POSIX_ACL_ACCESS] = &nop_posix_acl_access, - [EROFS_XATTR_INDEX_POSIX_ACL_DEFAULT] = &nop_posix_acl_default, -#endif - [EROFS_XATTR_INDEX_TRUSTED] = &erofs_xattr_trusted_handler, -#ifdef CONFIG_EROFS_FS_SECURITY - [EROFS_XATTR_INDEX_SECURITY] = &erofs_xattr_security_handler, -#endif - }; - - if (idx && idx < ARRAY_SIZE(xattr_handler_map)) - handler = xattr_handler_map[idx]; - - if (!xattr_handler_can_list(handler, dentry)) - return NULL; - - return xattr_prefix(handler); -} - extern const struct xattr_handler * const erofs_xattr_handlers[]; int erofs_xattr_prefixes_init(struct super_block *sb); void erofs_xattr_prefixes_cleanup(struct super_block *sb); -int erofs_getxattr(struct inode *, int, const char *, void *, size_t); ssize_t erofs_listxattr(struct dentry *, char *, size_t); #else static inline int erofs_xattr_prefixes_init(struct super_block *sb) { return 0; } static inline void erofs_xattr_prefixes_cleanup(struct super_block *sb) {} -static inline int erofs_getxattr(struct inode *inode, int index, - const char *name, void *buffer, - size_t buffer_size) -{ - return -EOPNOTSUPP; -} #define erofs_listxattr (NULL) #define erofs_xattr_handlers (NULL) @@ -67,4 +30,7 @@ struct posix_acl *erofs_get_acl(struct inode *inode, int type, bool rcu); #define erofs_get_acl (NULL) #endif +int erofs_xattr_fill_inode_fingerprint(struct erofs_inode_fingerprint *fp, + struct inode *inode, const char *domain_id); +bool erofs_inode_has_noacl(struct inode *inode, void *kaddr, unsigned int ofs); #endif diff --git a/fs/erofs/zdata.c b/fs/erofs/zdata.c index 3d31f7840ca0..ea9d32e9cb12 100644 --- a/fs/erofs/zdata.c +++ b/fs/erofs/zdata.c @@ -9,6 +9,7 @@ #include <linux/cpuhotplug.h> #include <trace/events/erofs.h> +#define Z_EROFS_MAX_SYNC_DECOMPRESS_BYTES 12288 #define Z_EROFS_PCLUSTER_MAX_PAGES (Z_EROFS_PCLUSTER_MAX_SIZE / PAGE_SIZE) #define Z_EROFS_INLINE_BVECS 2 @@ -493,7 +494,7 @@ enum z_erofs_pclustermode { }; struct z_erofs_frontend { - struct inode *const inode; + struct inode *inode, *sharedinode; struct erofs_map_blocks map; struct z_erofs_bvec_iter biter; @@ -508,8 +509,8 @@ struct z_erofs_frontend { unsigned int icur; }; -#define Z_EROFS_DEFINE_FRONTEND(fe, i, ho) struct z_erofs_frontend fe = { \ - .inode = i, .head = Z_EROFS_PCLUSTER_TAIL, \ +#define Z_EROFS_DEFINE_FRONTEND(fe, i, si, ho) struct z_erofs_frontend fe = { \ + .inode = i, .sharedinode = si, .head = Z_EROFS_PCLUSTER_TAIL, \ .mode = Z_EROFS_PCLUSTER_FOLLOWED, .headoffset = ho } static bool z_erofs_should_alloc_cache(struct z_erofs_frontend *fe) @@ -805,14 +806,26 @@ static int z_erofs_pcluster_begin(struct z_erofs_frontend *fe) struct erofs_map_blocks *map = &fe->map; struct super_block *sb = fe->inode->i_sb; struct z_erofs_pcluster *pcl = NULL; - void *ptr; + void *ptr = NULL; int ret; DBG_BUGON(fe->pcl); /* must be Z_EROFS_PCLUSTER_TAIL or pointed to previous pcluster */ DBG_BUGON(!fe->head); - if (!(map->m_flags & EROFS_MAP_META)) { + if (map->m_flags & EROFS_MAP_META) { + ret = erofs_init_metabuf(&map->buf, sb, + erofs_inode_in_metabox(fe->inode)); + if (ret) + return ret; + ptr = erofs_bread(&map->buf, map->m_pa, false); + if (IS_ERR(ptr)) { + erofs_err(sb, "failed to read inline data %pe @ pa %llu of nid %llu", + ptr, map->m_pa, EROFS_I(fe->inode)->nid); + return PTR_ERR(ptr); + } + ptr = map->buf.page; + } else { while (1) { rcu_read_lock(); pcl = xa_load(&EROFS_SB(sb)->managed_pslots, map->m_pa); @@ -852,18 +865,8 @@ static int z_erofs_pcluster_begin(struct z_erofs_frontend *fe) /* bind cache first when cached decompression is preferred */ z_erofs_bind_cache(fe); } else { - ret = erofs_init_metabuf(&map->buf, sb, - erofs_inode_in_metabox(fe->inode)); - if (ret) - return ret; - ptr = erofs_bread(&map->buf, map->m_pa, false); - if (IS_ERR(ptr)) { - ret = PTR_ERR(ptr); - erofs_err(sb, "failed to get inline folio %d", ret); - return ret; - } - folio_get(page_folio(map->buf.page)); - WRITE_ONCE(fe->pcl->compressed_bvecs[0].page, map->buf.page); + folio_get(page_folio((struct page *)ptr)); + WRITE_ONCE(fe->pcl->compressed_bvecs[0].page, ptr); fe->pcl->pageofs_in = map->m_pa & ~PAGE_MASK; fe->mode = Z_EROFS_PCLUSTER_FOLLOWED_NOINPLACE; } @@ -1095,21 +1098,6 @@ static int z_erofs_scan_folio(struct z_erofs_frontend *f, return err; } -static bool z_erofs_is_sync_decompress(struct erofs_sb_info *sbi, - unsigned int readahead_pages) -{ - /* auto: enable for read_folio, disable for readahead */ - if ((sbi->opt.sync_decompress == EROFS_SYNC_DECOMPRESS_AUTO) && - !readahead_pages) - return true; - - if ((sbi->opt.sync_decompress == EROFS_SYNC_DECOMPRESS_FORCE_ON) && - (readahead_pages <= sbi->opt.max_sync_decompress_pages)) - return true; - - return false; -} - static bool z_erofs_page_is_invalidated(struct page *page) { return !page_folio(page)->mapping && !z_erofs_is_shortlived_page(page); @@ -1324,9 +1312,10 @@ static int z_erofs_decompress_pcluster(struct z_erofs_backend *be, bool eio) GFP_NOWAIT | __GFP_NORETRY }, be->pagepool); if (IS_ERR(reason)) { - erofs_err(be->sb, "failed to decompress (%s) %ld @ pa %llu size %u => %u", - alg->name, PTR_ERR(reason), pcl->pos, - pcl->pclustersize, pcl->length); + if (pcl->besteffort || reason != ERR_PTR(-ENOMEM)) + erofs_err(be->sb, "failed to decompress (%s) %pe @ pa %llu size %u => %u", + alg->name, reason, pcl->pos, + pcl->pclustersize, pcl->length); err = PTR_ERR(reason); } else if (unlikely(reason)) { erofs_err(be->sb, "failed to decompress (%s) %s @ pa %llu size %u => %u", @@ -1483,9 +1472,9 @@ static void z_erofs_decompress_kickoff(struct z_erofs_decompressqueue *io, #else queue_work(z_erofs_workqueue, &io->u.work); #endif - /* enable sync decompression for readahead */ - if (sbi->opt.sync_decompress == EROFS_SYNC_DECOMPRESS_AUTO) - sbi->opt.sync_decompress = EROFS_SYNC_DECOMPRESS_FORCE_ON; + /* See `sync_decompress` in sysfs-fs-erofs for more details */ + if (sbi->sync_decompress == EROFS_SYNC_DECOMPRESS_AUTO) + sbi->sync_decompress = EROFS_SYNC_DECOMPRESS_FORCE_ON; return; } z_erofs_decompressqueue_work(&io->u.work); @@ -1802,16 +1791,21 @@ drain_io: z_erofs_decompress_kickoff(q[JQ_SUBMIT], nr_bios); } -static int z_erofs_runqueue(struct z_erofs_frontend *f, unsigned int rapages) +static int z_erofs_runqueue(struct z_erofs_frontend *f, unsigned int rabytes) { struct z_erofs_decompressqueue io[NR_JOBQUEUES]; struct erofs_sb_info *sbi = EROFS_I_SB(f->inode); - bool force_fg = z_erofs_is_sync_decompress(sbi, rapages); + int syncmode = sbi->sync_decompress; + bool force_fg; int err; + force_fg = (syncmode == EROFS_SYNC_DECOMPRESS_AUTO && !rabytes) || + (syncmode == EROFS_SYNC_DECOMPRESS_FORCE_ON && + (rabytes <= Z_EROFS_MAX_SYNC_DECOMPRESS_BYTES)); + if (f->head == Z_EROFS_PCLUSTER_TAIL) return 0; - z_erofs_submit_queue(f, io, &force_fg, !!rapages); + z_erofs_submit_queue(f, io, &force_fg, !!rabytes); /* handle bypass queue (no i/o pclusters) immediately */ err = z_erofs_decompress_queue(&io[JQ_BYPASS], &f->pagepool); @@ -1866,7 +1860,7 @@ static void z_erofs_pcluster_readmore(struct z_erofs_frontend *f, pgoff_t index = cur >> PAGE_SHIFT; struct folio *folio; - folio = erofs_grab_folio_nowait(inode->i_mapping, index); + folio = erofs_grab_folio_nowait(f->sharedinode->i_mapping, index); if (!IS_ERR_OR_NULL(folio)) { if (folio_test_uptodate(folio)) folio_unlock(folio); @@ -1883,11 +1877,13 @@ static void z_erofs_pcluster_readmore(struct z_erofs_frontend *f, static int z_erofs_read_folio(struct file *file, struct folio *folio) { - struct inode *const inode = folio->mapping->host; - Z_EROFS_DEFINE_FRONTEND(f, inode, folio_pos(folio)); + struct inode *sharedinode = folio->mapping->host; + bool need_iput; + struct inode *realinode = erofs_real_inode(sharedinode, &need_iput); + Z_EROFS_DEFINE_FRONTEND(f, realinode, sharedinode, folio_pos(folio)); int err; - trace_erofs_read_folio(folio, false); + trace_erofs_read_folio(realinode, folio, false); z_erofs_pcluster_readmore(&f, NULL, true); err = z_erofs_scan_folio(&f, folio, false); z_erofs_pcluster_readmore(&f, NULL, false); @@ -1896,23 +1892,28 @@ static int z_erofs_read_folio(struct file *file, struct folio *folio) /* if some pclusters are ready, need submit them anyway */ err = z_erofs_runqueue(&f, 0) ?: err; if (err && err != -EINTR) - erofs_err(inode->i_sb, "read error %d @ %lu of nid %llu", - err, folio->index, EROFS_I(inode)->nid); + erofs_err(realinode->i_sb, "read error %d @ %lu of nid %llu", + err, folio->index, EROFS_I(realinode)->nid); erofs_put_metabuf(&f.map.buf); erofs_release_pages(&f.pagepool); + + if (need_iput) + iput(realinode); return err; } static void z_erofs_readahead(struct readahead_control *rac) { - struct inode *const inode = rac->mapping->host; - Z_EROFS_DEFINE_FRONTEND(f, inode, readahead_pos(rac)); + struct inode *sharedinode = rac->mapping->host; + bool need_iput; + struct inode *realinode = erofs_real_inode(sharedinode, &need_iput); + Z_EROFS_DEFINE_FRONTEND(f, realinode, sharedinode, readahead_pos(rac)); unsigned int nrpages = readahead_count(rac); struct folio *head = NULL, *folio; int err; - trace_erofs_readahead(inode, readahead_index(rac), nrpages, false); + trace_erofs_readahead(realinode, readahead_index(rac), nrpages, false); z_erofs_pcluster_readmore(&f, rac, true); while ((folio = readahead_folio(rac))) { folio->private = head; @@ -1926,15 +1927,18 @@ static void z_erofs_readahead(struct readahead_control *rac) err = z_erofs_scan_folio(&f, folio, true); if (err && err != -EINTR) - erofs_err(inode->i_sb, "readahead error at folio %lu @ nid %llu", - folio->index, EROFS_I(inode)->nid); + erofs_err(realinode->i_sb, "readahead error at folio %lu @ nid %llu", + folio->index, EROFS_I(realinode)->nid); } z_erofs_pcluster_readmore(&f, rac, false); z_erofs_pcluster_end(&f); - (void)z_erofs_runqueue(&f, nrpages); + (void)z_erofs_runqueue(&f, nrpages << PAGE_SHIFT); erofs_put_metabuf(&f.map.buf); erofs_release_pages(&f.pagepool); + + if (need_iput) + iput(realinode); } const struct address_space_operations z_erofs_aops = { diff --git a/fs/exec.c b/fs/exec.c index 9d5ebc9d15b0..2e3a6593c6fd 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -555,7 +555,7 @@ int copy_string_kernel(const char *arg, struct linux_binprm *bprm) return -E2BIG; while (len > 0) { - unsigned int bytes_to_copy = min_t(unsigned int, len, + unsigned int bytes_to_copy = min(len, min_not_zero(offset_in_page(pos), PAGE_SIZE)); struct page *page; @@ -777,10 +777,8 @@ static struct file *do_open_execat(int fd, struct filename *name, int flags) return ERR_PTR(-EINVAL); if (flags & AT_SYMLINK_NOFOLLOW) open_exec_flags.lookup_flags &= ~LOOKUP_FOLLOW; - if (flags & AT_EMPTY_PATH) - open_exec_flags.lookup_flags |= LOOKUP_EMPTY; - file = do_filp_open(fd, name, &open_exec_flags); + file = do_file_open(fd, name, &open_exec_flags); if (IS_ERR(file)) return file; @@ -815,14 +813,8 @@ static struct file *do_open_execat(int fd, struct filename *name, int flags) */ struct file *open_exec(const char *name) { - struct filename *filename = getname_kernel(name); - struct file *f = ERR_CAST(filename); - - if (!IS_ERR(filename)) { - f = do_open_execat(AT_FDCWD, filename, 0); - putname(filename); - } - return f; + CLASS(filename_kernel, filename)(name); + return do_open_execat(AT_FDCWD, filename, 0); } EXPORT_SYMBOL(open_exec); @@ -1471,6 +1463,9 @@ out_free: return ERR_PTR(retval); } +DEFINE_CLASS(bprm, struct linux_binprm *, if (!IS_ERR(_T)) free_bprm(_T), + alloc_bprm(fd, name, flags), int fd, struct filename *name, int flags) + int bprm_change_interp(const char *interp, struct linux_binprm *bprm) { /* If a binfmt changed the interp, free it first. */ @@ -1785,12 +1780,8 @@ static int do_execveat_common(int fd, struct filename *filename, struct user_arg_ptr envp, int flags) { - struct linux_binprm *bprm; int retval; - if (IS_ERR(filename)) - return PTR_ERR(filename); - /* * We move the actual failure in case of RLIMIT_NPROC excess from * set*uid() to execve() because too many poorly written programs @@ -1798,47 +1789,43 @@ static int do_execveat_common(int fd, struct filename *filename, * whether NPROC limit is still exceeded. */ if ((current->flags & PF_NPROC_EXCEEDED) && - is_rlimit_overlimit(current_ucounts(), UCOUNT_RLIMIT_NPROC, rlimit(RLIMIT_NPROC))) { - retval = -EAGAIN; - goto out_ret; - } + is_rlimit_overlimit(current_ucounts(), UCOUNT_RLIMIT_NPROC, rlimit(RLIMIT_NPROC))) + return -EAGAIN; /* We're below the limit (still or again), so we don't want to make * further execve() calls fail. */ current->flags &= ~PF_NPROC_EXCEEDED; - bprm = alloc_bprm(fd, filename, flags); - if (IS_ERR(bprm)) { - retval = PTR_ERR(bprm); - goto out_ret; - } + CLASS(bprm, bprm)(fd, filename, flags); + if (IS_ERR(bprm)) + return PTR_ERR(bprm); retval = count(argv, MAX_ARG_STRINGS); if (retval < 0) - goto out_free; + return retval; bprm->argc = retval; retval = count(envp, MAX_ARG_STRINGS); if (retval < 0) - goto out_free; + return retval; bprm->envc = retval; retval = bprm_stack_limits(bprm); if (retval < 0) - goto out_free; + return retval; retval = copy_string_kernel(bprm->filename, bprm); if (retval < 0) - goto out_free; + return retval; bprm->exec = bprm->p; retval = copy_strings(bprm->envc, envp, bprm); if (retval < 0) - goto out_free; + return retval; retval = copy_strings(bprm->argc, argv, bprm); if (retval < 0) - goto out_free; + return retval; /* * When argv is empty, add an empty string ("") as argv[0] to @@ -1849,133 +1836,61 @@ static int do_execveat_common(int fd, struct filename *filename, if (bprm->argc == 0) { retval = copy_string_kernel("", bprm); if (retval < 0) - goto out_free; + return retval; bprm->argc = 1; pr_warn_once("process '%s' launched '%s' with NULL argv: empty string added\n", current->comm, bprm->filename); } - retval = bprm_execve(bprm); -out_free: - free_bprm(bprm); - -out_ret: - putname(filename); - return retval; + return bprm_execve(bprm); } int kernel_execve(const char *kernel_filename, const char *const *argv, const char *const *envp) { - struct filename *filename; - struct linux_binprm *bprm; - int fd = AT_FDCWD; int retval; /* It is non-sense for kernel threads to call execve */ if (WARN_ON_ONCE(current->flags & PF_KTHREAD)) return -EINVAL; - filename = getname_kernel(kernel_filename); - if (IS_ERR(filename)) - return PTR_ERR(filename); - - bprm = alloc_bprm(fd, filename, 0); - if (IS_ERR(bprm)) { - retval = PTR_ERR(bprm); - goto out_ret; - } + CLASS(filename_kernel, filename)(kernel_filename); + CLASS(bprm, bprm)(AT_FDCWD, filename, 0); + if (IS_ERR(bprm)) + return PTR_ERR(bprm); retval = count_strings_kernel(argv); if (WARN_ON_ONCE(retval == 0)) - retval = -EINVAL; + return -EINVAL; if (retval < 0) - goto out_free; + return retval; bprm->argc = retval; retval = count_strings_kernel(envp); if (retval < 0) - goto out_free; + return retval; bprm->envc = retval; retval = bprm_stack_limits(bprm); if (retval < 0) - goto out_free; + return retval; retval = copy_string_kernel(bprm->filename, bprm); if (retval < 0) - goto out_free; + return retval; bprm->exec = bprm->p; retval = copy_strings_kernel(bprm->envc, envp, bprm); if (retval < 0) - goto out_free; + return retval; retval = copy_strings_kernel(bprm->argc, argv, bprm); if (retval < 0) - goto out_free; - - retval = bprm_execve(bprm); -out_free: - free_bprm(bprm); -out_ret: - putname(filename); - return retval; -} - -static int do_execve(struct filename *filename, - const char __user *const __user *__argv, - const char __user *const __user *__envp) -{ - struct user_arg_ptr argv = { .ptr.native = __argv }; - struct user_arg_ptr envp = { .ptr.native = __envp }; - return do_execveat_common(AT_FDCWD, filename, argv, envp, 0); -} - -static int do_execveat(int fd, struct filename *filename, - const char __user *const __user *__argv, - const char __user *const __user *__envp, - int flags) -{ - struct user_arg_ptr argv = { .ptr.native = __argv }; - struct user_arg_ptr envp = { .ptr.native = __envp }; - - return do_execveat_common(fd, filename, argv, envp, flags); -} - -#ifdef CONFIG_COMPAT -static int compat_do_execve(struct filename *filename, - const compat_uptr_t __user *__argv, - const compat_uptr_t __user *__envp) -{ - struct user_arg_ptr argv = { - .is_compat = true, - .ptr.compat = __argv, - }; - struct user_arg_ptr envp = { - .is_compat = true, - .ptr.compat = __envp, - }; - return do_execveat_common(AT_FDCWD, filename, argv, envp, 0); -} + return retval; -static int compat_do_execveat(int fd, struct filename *filename, - const compat_uptr_t __user *__argv, - const compat_uptr_t __user *__envp, - int flags) -{ - struct user_arg_ptr argv = { - .is_compat = true, - .ptr.compat = __argv, - }; - struct user_arg_ptr envp = { - .is_compat = true, - .ptr.compat = __envp, - }; - return do_execveat_common(fd, filename, argv, envp, flags); + return bprm_execve(bprm); } -#endif void set_binfmt(struct linux_binfmt *new) { @@ -2001,12 +1916,19 @@ void set_dumpable(struct mm_struct *mm, int value) __mm_flags_set_mask_dumpable(mm, value); } +static inline struct user_arg_ptr native_arg(const char __user *const __user *p) +{ + return (struct user_arg_ptr){.ptr.native = p}; +} + SYSCALL_DEFINE3(execve, const char __user *, filename, const char __user *const __user *, argv, const char __user *const __user *, envp) { - return do_execve(getname(filename), argv, envp); + CLASS(filename, name)(filename); + return do_execveat_common(AT_FDCWD, name, + native_arg(argv), native_arg(envp), 0); } SYSCALL_DEFINE5(execveat, @@ -2015,17 +1937,25 @@ SYSCALL_DEFINE5(execveat, const char __user *const __user *, envp, int, flags) { - return do_execveat(fd, - getname_uflags(filename, flags), - argv, envp, flags); + CLASS(filename_uflags, name)(filename, flags); + return do_execveat_common(fd, name, + native_arg(argv), native_arg(envp), flags); } #ifdef CONFIG_COMPAT + +static inline struct user_arg_ptr compat_arg(const compat_uptr_t __user *p) +{ + return (struct user_arg_ptr){.is_compat = true, .ptr.compat = p}; +} + COMPAT_SYSCALL_DEFINE3(execve, const char __user *, filename, const compat_uptr_t __user *, argv, const compat_uptr_t __user *, envp) { - return compat_do_execve(getname(filename), argv, envp); + CLASS(filename, name)(filename); + return do_execveat_common(AT_FDCWD, name, + compat_arg(argv), compat_arg(envp), 0); } COMPAT_SYSCALL_DEFINE5(execveat, int, fd, @@ -2034,9 +1964,9 @@ COMPAT_SYSCALL_DEFINE5(execveat, int, fd, const compat_uptr_t __user *, envp, int, flags) { - return compat_do_execveat(fd, - getname_uflags(filename, flags), - argv, envp, flags); + CLASS(filename_uflags, name)(filename, flags); + return do_execveat_common(fd, name, + compat_arg(argv), compat_arg(envp), flags); } #endif diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c index 3045a58e124a..2dbf335eafef 100644 --- a/fs/exfat/dir.c +++ b/fs/exfat/dir.c @@ -7,6 +7,7 @@ #include <linux/compat.h> #include <linux/bio.h> #include <linux/buffer_head.h> +#include <linux/filelock.h> #include "exfat_raw.h" #include "exfat_fs.h" @@ -298,6 +299,7 @@ const struct file_operations exfat_dir_operations = { .compat_ioctl = exfat_compat_ioctl, #endif .fsync = exfat_file_fsync, + .setlease = generic_setlease, }; int exfat_alloc_new_dir(struct inode *inode, struct exfat_chain *clu) diff --git a/fs/exfat/file.c b/fs/exfat/file.c index 536c8078f0c1..b60ee0e1bec9 100644 --- a/fs/exfat/file.c +++ b/fs/exfat/file.c @@ -12,6 +12,7 @@ #include <linux/security.h> #include <linux/msdos_fs.h> #include <linux/writeback.h> +#include <linux/filelock.h> #include "exfat_raw.h" #include "exfat_fs.h" @@ -772,6 +773,7 @@ const struct file_operations exfat_file_operations = { .fsync = exfat_file_fsync, .splice_read = exfat_splice_read, .splice_write = iter_file_splice_write, + .setlease = generic_setlease, }; const struct inode_operations exfat_file_inode_operations = { diff --git a/fs/ext2/dir.c b/fs/ext2/dir.c index b07b3b369710..395fc36c089b 100644 --- a/fs/ext2/dir.c +++ b/fs/ext2/dir.c @@ -24,6 +24,7 @@ #include "ext2.h" #include <linux/buffer_head.h> +#include <linux/filelock.h> #include <linux/pagemap.h> #include <linux/swap.h> #include <linux/iversion.h> @@ -734,4 +735,5 @@ const struct file_operations ext2_dir_operations = { .compat_ioctl = ext2_compat_ioctl, #endif .fsync = ext2_fsync, + .setlease = generic_setlease, }; diff --git a/fs/ext2/ext2.h b/fs/ext2/ext2.h index cf97b76e9fd3..5e0c6c5fcb6c 100644 --- a/fs/ext2/ext2.h +++ b/fs/ext2/ext2.h @@ -357,7 +357,6 @@ struct ext2_inode { */ #define EXT2_VALID_FS 0x0001 /* Unmounted cleanly */ #define EXT2_ERROR_FS 0x0002 /* Errors detected */ -#define EFSCORRUPTED EUCLEAN /* Filesystem is corrupted */ /* * Mount flags diff --git a/fs/ext2/file.c b/fs/ext2/file.c index 76bddce462fc..ebe356a38b18 100644 --- a/fs/ext2/file.c +++ b/fs/ext2/file.c @@ -22,6 +22,7 @@ #include <linux/time.h> #include <linux/pagemap.h> #include <linux/dax.h> +#include <linux/filelock.h> #include <linux/quotaops.h> #include <linux/iomap.h> #include <linux/uio.h> @@ -325,6 +326,7 @@ const struct file_operations ext2_file_operations = { .get_unmapped_area = thp_get_unmapped_area, .splice_read = filemap_splice_read, .splice_write = iter_file_splice_write, + .setlease = generic_setlease, }; const struct inode_operations ext2_file_inode_operations = { diff --git a/fs/ext4/dir.c b/fs/ext4/dir.c index 256fe2c1d4c1..00c4b3c82b65 100644 --- a/fs/ext4/dir.c +++ b/fs/ext4/dir.c @@ -24,6 +24,7 @@ #include <linux/fs.h> #include <linux/buffer_head.h> +#include <linux/filelock.h> #include <linux/slab.h> #include <linux/iversion.h> #include <linux/unicode.h> @@ -690,4 +691,5 @@ const struct file_operations ext4_dir_operations = { #endif .fsync = ext4_sync_file, .release = ext4_release_dir, + .setlease = generic_setlease, }; diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 56112f201cac..293f698b7042 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -707,15 +707,6 @@ enum { * found an unwritten extent, we need to split it. */ #define EXT4_GET_BLOCKS_SPLIT_NOMERGE 0x0008 - /* - * Caller is from the dio or dioread_nolock buffered IO, reqest to - * create an unwritten extent if it does not exist or split the - * found unwritten extent. Also do not merge the newly created - * unwritten extent, io end will convert unwritten to written, - * and try to merge the written extent. - */ -#define EXT4_GET_BLOCKS_IO_CREATE_EXT (EXT4_GET_BLOCKS_SPLIT_NOMERGE|\ - EXT4_GET_BLOCKS_CREATE_UNWRIT_EXT) /* Convert unwritten extent to initialized. */ #define EXT4_GET_BLOCKS_CONVERT 0x0010 /* Eventual metadata allocation (due to growing extent tree) @@ -1205,10 +1196,6 @@ struct ext4_inode_info { #ifdef CONFIG_FS_ENCRYPTION struct fscrypt_inode_info *i_crypt_info; #endif - -#ifdef CONFIG_FS_VERITY - struct fsverity_info *i_verity_info; -#endif }; /* @@ -1692,6 +1679,8 @@ struct ext4_sb_info { /* timer for periodic error stats printing */ struct timer_list s_err_report; + /* timeout in seconds for s_err_report; 0 disables the timer. */ + unsigned long s_err_report_sec; /* Lazy inode table initialization info */ struct ext4_li_request *s_li_request; @@ -1795,6 +1784,10 @@ struct ext4_sb_info { * Main fast commit lock. This lock protects accesses to the * following fields: * ei->i_fc_list, s_fc_dentry_q, s_fc_q, s_fc_bytes, s_fc_bh. + * + * s_fc_lock can be taken from reclaim context (inode eviction) and is + * thus reclaim unsafe. Use ext4_fc_lock()/ext4_fc_unlock() helpers + * when acquiring / releasing the lock. */ struct mutex s_fc_lock; struct buffer_head *s_fc_bh; @@ -1839,6 +1832,18 @@ static inline void ext4_writepages_up_write(struct super_block *sb, int ctx) percpu_up_write(&EXT4_SB(sb)->s_writepages_rwsem); } +static inline int ext4_fc_lock(struct super_block *sb) +{ + mutex_lock(&EXT4_SB(sb)->s_fc_lock); + return memalloc_nofs_save(); +} + +static inline void ext4_fc_unlock(struct super_block *sb, int ctx) +{ + memalloc_nofs_restore(ctx); + mutex_unlock(&EXT4_SB(sb)->s_fc_lock); +} + static inline int ext4_valid_inum(struct super_block *sb, unsigned long ino) { return ino == EXT4_ROOT_INO || @@ -2373,7 +2378,6 @@ static inline int ext4_emergency_state(struct super_block *sb) #define EXT4_DEF_SB_UPDATE_INTERVAL_SEC (3600) /* seconds (1 hour) */ #define EXT4_DEF_SB_UPDATE_INTERVAL_KB (16384) /* kilobytes (16MB) */ - /* * Minimum number of groups in a flexgroup before we separate out * directories into the first block group of a flexgroup @@ -3199,6 +3203,7 @@ extern void ext4_mark_group_bitmap_corrupted(struct super_block *sb, unsigned int flags); extern unsigned int ext4_num_base_meta_blocks(struct super_block *sb, ext4_group_t block_group); +extern void print_daily_error_info(struct timer_list *t); extern __printf(7, 8) void __ext4_error(struct super_block *, const char *, unsigned int, bool, @@ -3735,8 +3740,8 @@ static inline void ext4_set_de_type(struct super_block *sb, } /* readpages.c */ -extern int ext4_mpage_readpages(struct inode *inode, - struct readahead_control *rac, struct folio *folio); +int ext4_read_folio(struct file *file, struct folio *folio); +void ext4_readahead(struct readahead_control *rac); extern int __init ext4_init_post_read_processing(void); extern void ext4_exit_post_read_processing(void); @@ -3795,6 +3800,10 @@ extern int ext4_convert_unwritten_io_end_vec(handle_t *handle, ext4_io_end_t *io_end); extern int ext4_map_blocks(handle_t *handle, struct inode *inode, struct ext4_map_blocks *map, int flags); +extern int ext4_map_query_blocks(handle_t *handle, struct inode *inode, + struct ext4_map_blocks *map, int flags); +extern int ext4_map_create_blocks(handle_t *handle, struct inode *inode, + struct ext4_map_blocks *map, int flags); extern int ext4_ext_calc_credits_for_single_extent(struct inode *inode, int num, struct ext4_ext_path *path); @@ -3909,7 +3918,6 @@ static inline void ext4_clear_io_unwritten_flag(ext4_io_end_t *io_end) } extern const struct iomap_ops ext4_iomap_ops; -extern const struct iomap_ops ext4_iomap_overwrite_ops; extern const struct iomap_ops ext4_iomap_report_ops; static inline int ext4_buffer_uptodate(struct buffer_head *bh) @@ -3938,7 +3946,4 @@ extern int ext4_block_write_begin(handle_t *handle, struct folio *folio, get_block_t *get_block); #endif /* __KERNEL__ */ -#define EFSBADCRC EBADMSG /* Bad CRC detected */ -#define EFSCORRUPTED EUCLEAN /* Filesystem is corrupted */ - #endif /* _EXT4_H */ diff --git a/fs/ext4/extents-test.c b/fs/ext4/extents-test.c new file mode 100644 index 000000000000..4879e68e465d --- /dev/null +++ b/fs/ext4/extents-test.c @@ -0,0 +1,1027 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Written by Ojaswin Mujoo <ojaswin@linux.ibm.com> (IBM) + * + * These Kunit tests are designed to test the functionality of + * extent split and conversion in ext4. + * + * Currently, ext4 can split extents in 2 ways: + * 1. By splitting the extents in the extent tree and optionally converting them + * to written or unwritten based on flags passed. + * 2. In case 1 encounters an error, ext4 instead zerooes out the unwritten + * areas of the extent and marks the complete extent written. + * + * The primary function that handles this is ext4_split_convert_extents(). + * + * We test both of the methods of split. The behavior we try to enforce is: + * 1. When passing EXT4_GET_BLOCKS_CONVERT flag to ext4_split_convert_extents(), + * the split extent should be converted to initialized. + * 2. When passing EXT4_GET_BLOCKS_CONVERT_UNWRITTEN flag to + * ext4_split_convert_extents(), the split extent should be converted to + * uninitialized. + * 3. In case we use the zeroout method, then we should correctly write zeroes + * to the unwritten areas of the extent and we should not corrupt/leak any + * data. + * + * Enforcing 1 and 2 is straight forward, we just setup a minimal inode with + * extent tree, call ext4_split_convert_extents() and check the final state of + * the extent tree. + * + * For zeroout testing, we maintain a separate buffer which represents the disk + * data corresponding to the extents. We then override ext4's zeroout functions + * to instead write zeroes to our buffer. Then, we override + * ext4_ext_insert_extent() to return -ENOSPC, which triggers the zeroout. + * Finally, we check the state of the extent tree and zeroout buffer to confirm + * everything went well. + */ + +#include <kunit/test.h> +#include <kunit/static_stub.h> +#include <linux/gfp_types.h> +#include <linux/stddef.h> + +#include "ext4.h" +#include "ext4_extents.h" + +#define EXT_DATA_PBLK 100 +#define EXT_DATA_LBLK 10 +#define EXT_DATA_LEN 3 + +struct kunit_ctx { + /* + * Ext4 inode which has only 1 unwrit extent + */ + struct ext4_inode_info *k_ei; + /* + * Represents the underlying data area (used for zeroout testing) + */ + char *k_data; +} k_ctx; + +/* + * describes the state of an expected extent in extent tree. + */ +struct kunit_ext_state { + ext4_lblk_t ex_lblk; + ext4_lblk_t ex_len; + bool is_unwrit; +}; + +/* + * describes the state of the data area of a writ extent. Used for testing + * correctness of zeroout. + */ +struct kunit_ext_data_state { + char exp_char; + ext4_lblk_t off_blk; + ext4_lblk_t len_blk; +}; + +enum kunit_test_types { + TEST_SPLIT_CONVERT, + TEST_CREATE_BLOCKS, +}; + +struct kunit_ext_test_param { + /* description of test */ + char *desc; + + /* determines which function will be tested */ + int type; + + /* is extent unwrit at beginning of test */ + bool is_unwrit_at_start; + + /* flags to pass while splitting */ + int split_flags; + + /* map describing range to split */ + struct ext4_map_blocks split_map; + + /* disable zeroout */ + bool disable_zeroout; + + /* no of extents expected after split */ + int nr_exp_ext; + + /* + * expected state of extents after split. We will never split into more + * than 3 extents + */ + struct kunit_ext_state exp_ext_state[3]; + + /* Below fields used for zeroout tests */ + + bool is_zeroout_test; + /* + * no of expected data segments (zeroout tests). Example, if we expect + * data to be 4kb 0s, followed by 8kb non-zero, then nr_exp_data_segs==2 + */ + int nr_exp_data_segs; + + /* + * expected state of data area after zeroout. + */ + struct kunit_ext_data_state exp_data_state[3]; +}; + +static void ext_kill_sb(struct super_block *sb) +{ + generic_shutdown_super(sb); +} + +static int ext_set(struct super_block *sb, void *data) +{ + return 0; +} + +static struct file_system_type ext_fs_type = { + .name = "extents test", + .kill_sb = ext_kill_sb, +}; + +static void extents_kunit_exit(struct kunit *test) +{ + struct ext4_sb_info *sbi = k_ctx.k_ei->vfs_inode.i_sb->s_fs_info; + + kfree(sbi); + kfree(k_ctx.k_ei); + kfree(k_ctx.k_data); +} + +static int __ext4_ext_dirty_stub(const char *where, unsigned int line, + handle_t *handle, struct inode *inode, + struct ext4_ext_path *path) +{ + return 0; +} + +static struct ext4_ext_path * +ext4_ext_insert_extent_stub(handle_t *handle, struct inode *inode, + struct ext4_ext_path *path, + struct ext4_extent *newext, int gb_flags) +{ + return ERR_PTR(-ENOSPC); +} + +/* + * We will zeroout the equivalent range in the data area + */ +static int ext4_ext_zeroout_stub(struct inode *inode, struct ext4_extent *ex) +{ + ext4_lblk_t ee_block, off_blk; + loff_t ee_len; + loff_t off_bytes; + struct kunit *test = kunit_get_current_test(); + + ee_block = le32_to_cpu(ex->ee_block); + ee_len = ext4_ext_get_actual_len(ex); + + KUNIT_EXPECT_EQ_MSG(test, 1, ee_block >= EXT_DATA_LBLK, "ee_block=%d", + ee_block); + KUNIT_EXPECT_EQ(test, 1, + ee_block + ee_len <= EXT_DATA_LBLK + EXT_DATA_LEN); + + off_blk = ee_block - EXT_DATA_LBLK; + off_bytes = off_blk << inode->i_sb->s_blocksize_bits; + memset(k_ctx.k_data + off_bytes, 0, + ee_len << inode->i_sb->s_blocksize_bits); + + return 0; +} + +static int ext4_issue_zeroout_stub(struct inode *inode, ext4_lblk_t lblk, + ext4_fsblk_t pblk, ext4_lblk_t len) +{ + ext4_lblk_t off_blk; + loff_t off_bytes; + struct kunit *test = kunit_get_current_test(); + + kunit_log(KERN_ALERT, test, + "%s: lblk=%u pblk=%llu len=%u", __func__, lblk, pblk, len); + KUNIT_EXPECT_EQ(test, 1, lblk >= EXT_DATA_LBLK); + KUNIT_EXPECT_EQ(test, 1, lblk + len <= EXT_DATA_LBLK + EXT_DATA_LEN); + KUNIT_EXPECT_EQ(test, 1, lblk - EXT_DATA_LBLK == pblk - EXT_DATA_PBLK); + + off_blk = lblk - EXT_DATA_LBLK; + off_bytes = off_blk << inode->i_sb->s_blocksize_bits; + memset(k_ctx.k_data + off_bytes, 0, + len << inode->i_sb->s_blocksize_bits); + + return 0; +} + +static int extents_kunit_init(struct kunit *test) +{ + struct ext4_extent_header *eh = NULL; + struct ext4_inode_info *ei; + struct inode *inode; + struct super_block *sb; + struct ext4_sb_info *sbi = NULL; + struct kunit_ext_test_param *param = + (struct kunit_ext_test_param *)(test->param_value); + int err; + + sb = sget(&ext_fs_type, NULL, ext_set, 0, NULL); + if (IS_ERR(sb)) + return PTR_ERR(sb); + + sb->s_blocksize = 4096; + sb->s_blocksize_bits = 12; + + sbi = kzalloc(sizeof(struct ext4_sb_info), GFP_KERNEL); + if (sbi == NULL) + return -ENOMEM; + + sbi->s_sb = sb; + sb->s_fs_info = sbi; + + if (!param || !param->disable_zeroout) + sbi->s_extent_max_zeroout_kb = 32; + + /* setup the mock inode */ + k_ctx.k_ei = kzalloc(sizeof(struct ext4_inode_info), GFP_KERNEL); + if (k_ctx.k_ei == NULL) + return -ENOMEM; + ei = k_ctx.k_ei; + inode = &ei->vfs_inode; + + err = ext4_es_register_shrinker(sbi); + if (err) + return err; + + ext4_es_init_tree(&ei->i_es_tree); + rwlock_init(&ei->i_es_lock); + INIT_LIST_HEAD(&ei->i_es_list); + ei->i_es_all_nr = 0; + ei->i_es_shk_nr = 0; + ei->i_es_shrink_lblk = 0; + + ei->i_disksize = (EXT_DATA_LBLK + EXT_DATA_LEN + 10) + << sb->s_blocksize_bits; + ei->i_flags = 0; + ext4_set_inode_flag(inode, EXT4_INODE_EXTENTS); + inode->i_sb = sb; + + k_ctx.k_data = kzalloc(EXT_DATA_LEN * 4096, GFP_KERNEL); + if (k_ctx.k_data == NULL) + return -ENOMEM; + + /* + * set the data area to a junk value + */ + memset(k_ctx.k_data, 'X', EXT_DATA_LEN * 4096); + + /* create a tree with depth 0 */ + eh = (struct ext4_extent_header *)k_ctx.k_ei->i_data; + + /* Fill extent header */ + eh = ext_inode_hdr(&k_ctx.k_ei->vfs_inode); + eh->eh_depth = 0; + eh->eh_entries = cpu_to_le16(1); + eh->eh_magic = EXT4_EXT_MAGIC; + eh->eh_max = + cpu_to_le16(ext4_ext_space_root_idx(&k_ctx.k_ei->vfs_inode, 0)); + eh->eh_generation = 0; + + /* + * add 1 extent in leaf node covering: + * - lblks: [EXT_DATA_LBLK, EXT_DATA_LBLK * + EXT_DATA_LEN) + * - pblks: [EXT_DATA_PBLK, EXT_DATA_PBLK + EXT_DATA_LEN) + */ + EXT_FIRST_EXTENT(eh)->ee_block = cpu_to_le32(EXT_DATA_LBLK); + EXT_FIRST_EXTENT(eh)->ee_len = cpu_to_le16(EXT_DATA_LEN); + ext4_ext_store_pblock(EXT_FIRST_EXTENT(eh), EXT_DATA_PBLK); + if (!param || param->is_unwrit_at_start) + ext4_ext_mark_unwritten(EXT_FIRST_EXTENT(eh)); + + ext4_es_insert_extent(inode, EXT_DATA_LBLK, EXT_DATA_LEN, EXT_DATA_PBLK, + ext4_ext_is_unwritten(EXT_FIRST_EXTENT(eh)) ? + EXTENT_STATUS_UNWRITTEN : + EXTENT_STATUS_WRITTEN, + 0); + + /* Add stubs */ + kunit_activate_static_stub(test, __ext4_ext_dirty, + __ext4_ext_dirty_stub); + kunit_activate_static_stub(test, ext4_ext_zeroout, ext4_ext_zeroout_stub); + kunit_activate_static_stub(test, ext4_issue_zeroout, + ext4_issue_zeroout_stub); + return 0; +} + +/* + * Return 1 if all bytes in the buf equal to c, else return the offset of first mismatch + */ +static int check_buffer(char *buf, int c, int size) +{ + void *ret = NULL; + + ret = memchr_inv(buf, c, size); + if (ret == NULL) + return 0; + + kunit_log(KERN_ALERT, kunit_get_current_test(), + "# %s: wrong char found at offset %u (expected:%d got:%d)", __func__, + (u32)((char *)ret - buf), c, *((char *)ret)); + return 1; +} + +/* + * Simulate a map block call by first calling ext4_map_query_blocks() to + * correctly populate map flags and pblk and then call the + * ext4_map_create_blocks() to do actual split and conversion. This is easier + * than calling ext4_map_blocks() because that needs mocking a lot of unrelated + * functions. + */ +static void ext4_map_create_blocks_helper(struct kunit *test, + struct inode *inode, + struct ext4_map_blocks *map, + int flags) +{ + int retval = 0; + + retval = ext4_map_query_blocks(NULL, inode, map, flags); + if (retval < 0) { + KUNIT_FAIL(test, + "ext4_map_query_blocks() failed. Cannot proceed\n"); + return; + } + + ext4_map_create_blocks(NULL, inode, map, flags); +} + +static void test_split_convert(struct kunit *test) +{ + struct ext4_ext_path *path; + struct inode *inode = &k_ctx.k_ei->vfs_inode; + struct ext4_extent *ex; + struct ext4_map_blocks map; + const struct kunit_ext_test_param *param = + (const struct kunit_ext_test_param *)(test->param_value); + int blkbits = inode->i_sb->s_blocksize_bits; + + if (param->is_zeroout_test) + /* + * Force zeroout by making ext4_ext_insert_extent return ENOSPC + */ + kunit_activate_static_stub(test, ext4_ext_insert_extent, + ext4_ext_insert_extent_stub); + + path = ext4_find_extent(inode, EXT_DATA_LBLK, NULL, EXT4_EX_NOCACHE); + ex = path->p_ext; + KUNIT_EXPECT_EQ(test, EXT_DATA_LBLK, le32_to_cpu(ex->ee_block)); + KUNIT_EXPECT_EQ(test, EXT_DATA_LEN, ext4_ext_get_actual_len(ex)); + KUNIT_EXPECT_EQ(test, param->is_unwrit_at_start, + ext4_ext_is_unwritten(ex)); + if (param->is_zeroout_test) + KUNIT_EXPECT_EQ(test, 0, + check_buffer(k_ctx.k_data, 'X', + EXT_DATA_LEN << blkbits)); + + map.m_lblk = param->split_map.m_lblk; + map.m_len = param->split_map.m_len; + + switch (param->type) { + case TEST_SPLIT_CONVERT: + path = ext4_split_convert_extents(NULL, inode, &map, path, + param->split_flags, NULL); + break; + case TEST_CREATE_BLOCKS: + ext4_map_create_blocks_helper(test, inode, &map, param->split_flags); + break; + default: + KUNIT_FAIL(test, "param->type %d not support.", param->type); + } + + path = ext4_find_extent(inode, EXT_DATA_LBLK, NULL, EXT4_EX_NOCACHE); + ex = path->p_ext; + + for (int i = 0; i < param->nr_exp_ext; i++) { + struct kunit_ext_state exp_ext = param->exp_ext_state[i]; + bool es_check_needed = param->type != TEST_SPLIT_CONVERT; + struct extent_status es; + int contains_ex, ex_end, es_end, es_pblk; + + KUNIT_EXPECT_EQ(test, exp_ext.ex_lblk, + le32_to_cpu(ex->ee_block)); + KUNIT_EXPECT_EQ(test, exp_ext.ex_len, + ext4_ext_get_actual_len(ex)); + KUNIT_EXPECT_EQ(test, exp_ext.is_unwrit, + ext4_ext_is_unwritten(ex)); + /* + * Confirm extent cache is in sync. Note that es cache can be + * merged even when on-disk extents are not so take that into + * account. + * + * Also, ext4_split_convert_extents() forces EXT4_EX_NOCACHE hence + * es status are ignored for that case. + */ + if (es_check_needed) { + ext4_es_lookup_extent(inode, le32_to_cpu(ex->ee_block), + NULL, &es, NULL); + + ex_end = exp_ext.ex_lblk + exp_ext.ex_len; + es_end = es.es_lblk + es.es_len; + contains_ex = es.es_lblk <= exp_ext.ex_lblk && + es_end >= ex_end; + es_pblk = ext4_es_pblock(&es) + + (exp_ext.ex_lblk - es.es_lblk); + + KUNIT_EXPECT_EQ(test, contains_ex, 1); + KUNIT_EXPECT_EQ(test, ext4_ext_pblock(ex), es_pblk); + KUNIT_EXPECT_EQ(test, 1, + (exp_ext.is_unwrit && + ext4_es_is_unwritten(&es)) || + (!exp_ext.is_unwrit && + ext4_es_is_written(&es))); + } + + /* Only printed on failure */ + kunit_log(KERN_INFO, test, + "# [extent %d] exp: lblk:%d len:%d unwrit:%d \n", i, + exp_ext.ex_lblk, exp_ext.ex_len, exp_ext.is_unwrit); + kunit_log(KERN_INFO, test, + "# [extent %d] got: lblk:%d len:%d unwrit:%d\n", i, + le32_to_cpu(ex->ee_block), + ext4_ext_get_actual_len(ex), + ext4_ext_is_unwritten(ex)); + if (es_check_needed) + kunit_log( + KERN_INFO, test, + "# [extent %d] es: lblk:%d len:%d pblk:%lld type:0x%x\n", + i, es.es_lblk, es.es_len, ext4_es_pblock(&es), + ext4_es_type(&es)); + kunit_log(KERN_INFO, test, "------------------\n"); + + ex = ex + 1; + } + + if (!param->is_zeroout_test) + return; + + /* + * Check that then data area has been zeroed out correctly + */ + for (int i = 0; i < param->nr_exp_data_segs; i++) { + loff_t off, len; + struct kunit_ext_data_state exp_data_seg = param->exp_data_state[i]; + + off = exp_data_seg.off_blk << blkbits; + len = exp_data_seg.len_blk << blkbits; + KUNIT_EXPECT_EQ_MSG(test, 0, + check_buffer(k_ctx.k_data + off, + exp_data_seg.exp_char, len), + "# corruption in byte range [%lld, %lld)", + off, len); + } + + return; +} + +static const struct kunit_ext_test_param test_split_convert_params[] = { + /* unwrit to writ splits */ + { .desc = "split unwrit extent to 2 extents and convert 1st half writ", + .type = TEST_SPLIT_CONVERT, + .is_unwrit_at_start = 1, + .split_flags = EXT4_GET_BLOCKS_CONVERT, + .split_map = { .m_lblk = EXT_DATA_LBLK, .m_len = 1 }, + .nr_exp_ext = 2, + .exp_ext_state = { { .ex_lblk = EXT_DATA_LBLK, + .ex_len = 1, + .is_unwrit = 0 }, + { .ex_lblk = EXT_DATA_LBLK + 1, + .ex_len = EXT_DATA_LEN - 1, + .is_unwrit = 1 } }, + .is_zeroout_test = 0 }, + { .desc = "split unwrit extent to 2 extents and convert 2nd half writ", + .type = TEST_SPLIT_CONVERT, + .is_unwrit_at_start = 1, + .split_flags = EXT4_GET_BLOCKS_CONVERT, + .split_map = { .m_lblk = EXT_DATA_LBLK + 1, .m_len = EXT_DATA_LEN - 1 }, + .nr_exp_ext = 2, + .exp_ext_state = { { .ex_lblk = EXT_DATA_LBLK, + .ex_len = 1, + .is_unwrit = 1 }, + { .ex_lblk = EXT_DATA_LBLK + 1, + .ex_len = EXT_DATA_LEN - 1, + .is_unwrit = 0 } }, + .is_zeroout_test = 0 }, + { .desc = "split unwrit extent to 3 extents and convert 2nd half to writ", + .type = TEST_SPLIT_CONVERT, + .is_unwrit_at_start = 1, + .split_flags = EXT4_GET_BLOCKS_CONVERT, + .split_map = { .m_lblk = EXT_DATA_LBLK + 1, .m_len = EXT_DATA_LEN - 2 }, + .nr_exp_ext = 3, + .exp_ext_state = { { .ex_lblk = EXT_DATA_LBLK, + .ex_len = 1, + .is_unwrit = 1 }, + { .ex_lblk = EXT_DATA_LBLK + 1, + .ex_len = EXT_DATA_LEN - 2, + .is_unwrit = 0 }, + { .ex_lblk = EXT_DATA_LBLK + 1 + (EXT_DATA_LEN - 2), + .ex_len = 1, + .is_unwrit = 1 } }, + .is_zeroout_test = 0 }, + + /* writ to unwrit splits */ + { .desc = "split writ extent to 2 extents and convert 1st half unwrit", + .type = TEST_SPLIT_CONVERT, + .is_unwrit_at_start = 0, + .split_flags = EXT4_GET_BLOCKS_CONVERT_UNWRITTEN, + .split_map = { .m_lblk = EXT_DATA_LBLK, .m_len = 1 }, + .nr_exp_ext = 2, + .exp_ext_state = { { .ex_lblk = EXT_DATA_LBLK, + .ex_len = 1, + .is_unwrit = 1 }, + { .ex_lblk = EXT_DATA_LBLK + 1, + .ex_len = EXT_DATA_LEN - 1, + .is_unwrit = 0 } }, + .is_zeroout_test = 0 }, + { .desc = "split writ extent to 2 extents and convert 2nd half unwrit", + .type = TEST_SPLIT_CONVERT, + .is_unwrit_at_start = 0, + .split_flags = EXT4_GET_BLOCKS_CONVERT_UNWRITTEN, + .split_map = { .m_lblk = EXT_DATA_LBLK + 1, .m_len = EXT_DATA_LEN - 1 }, + .nr_exp_ext = 2, + .exp_ext_state = { { .ex_lblk = EXT_DATA_LBLK, + .ex_len = 1, + .is_unwrit = 0 }, + { .ex_lblk = EXT_DATA_LBLK + 1, + .ex_len = EXT_DATA_LEN - 1, + .is_unwrit = 1 } }, + .is_zeroout_test = 0 }, + { .desc = "split writ extent to 3 extents and convert 2nd half to unwrit", + .type = TEST_SPLIT_CONVERT, + .is_unwrit_at_start = 0, + .split_flags = EXT4_GET_BLOCKS_CONVERT_UNWRITTEN, + .split_map = { .m_lblk = EXT_DATA_LBLK + 1, .m_len = EXT_DATA_LEN - 2 }, + .nr_exp_ext = 3, + .exp_ext_state = { { .ex_lblk = EXT_DATA_LBLK, + .ex_len = 1, + .is_unwrit = 0 }, + { .ex_lblk = EXT_DATA_LBLK + 1, + .ex_len = EXT_DATA_LEN - 2, + .is_unwrit = 1 }, + { .ex_lblk = EXT_DATA_LBLK + 1 + (EXT_DATA_LEN - 2), + .ex_len = 1, + .is_unwrit = 0 } }, + .is_zeroout_test = 0 }, + + /* + * ***** zeroout tests ***** + */ + /* unwrit to writ splits */ + { .desc = "split unwrit extent to 2 extents and convert 1st half writ (zeroout)", + .type = TEST_SPLIT_CONVERT, + .is_unwrit_at_start = 1, + .split_flags = EXT4_GET_BLOCKS_CONVERT, + .split_map = { .m_lblk = EXT_DATA_LBLK, .m_len = 1 }, + .nr_exp_ext = 1, + .exp_ext_state = { { .ex_lblk = EXT_DATA_LBLK, + .ex_len = EXT_DATA_LEN, + .is_unwrit = 0 } }, + .is_zeroout_test = 1, + .nr_exp_data_segs = 2, + .exp_data_state = { { .exp_char = 'X', .off_blk = 0, .len_blk = 1 }, + { .exp_char = 0, + .off_blk = 1, + .len_blk = EXT_DATA_LEN - 1 } } }, + { .desc = "split unwrit extent to 2 extents and convert 2nd half writ (zeroout)", + .type = TEST_SPLIT_CONVERT, + .is_unwrit_at_start = 1, + .split_flags = EXT4_GET_BLOCKS_CONVERT, + .split_map = { .m_lblk = EXT_DATA_LBLK + 1, .m_len = EXT_DATA_LEN - 1 }, + .nr_exp_ext = 1, + .exp_ext_state = { { .ex_lblk = EXT_DATA_LBLK, + .ex_len = EXT_DATA_LEN, + .is_unwrit = 0 } }, + .is_zeroout_test = 1, + .nr_exp_data_segs = 2, + .exp_data_state = { { .exp_char = 0, .off_blk = 0, .len_blk = 1 }, + { .exp_char = 'X', + .off_blk = 1, + .len_blk = EXT_DATA_LEN - 1 } } }, + { .desc = "split unwrit extent to 3 extents and convert 2nd half writ (zeroout)", + .type = TEST_SPLIT_CONVERT, + .is_unwrit_at_start = 1, + .split_flags = EXT4_GET_BLOCKS_CONVERT, + .split_map = { .m_lblk = EXT_DATA_LBLK + 1, .m_len = EXT_DATA_LEN - 2 }, + .nr_exp_ext = 1, + .exp_ext_state = { { .ex_lblk = EXT_DATA_LBLK, + .ex_len = EXT_DATA_LEN, + .is_unwrit = 0 } }, + .is_zeroout_test = 1, + .nr_exp_data_segs = 3, + .exp_data_state = { { .exp_char = 0, .off_blk = 0, .len_blk = 1 }, + { .exp_char = 'X', .off_blk = 1, .len_blk = EXT_DATA_LEN - 2 }, + { .exp_char = 0, .off_blk = EXT_DATA_LEN - 1, .len_blk = 1 } } }, + + /* writ to unwrit splits */ + { .desc = "split writ extent to 2 extents and convert 1st half unwrit (zeroout)", + .type = TEST_SPLIT_CONVERT, + .is_unwrit_at_start = 0, + .split_flags = EXT4_GET_BLOCKS_CONVERT_UNWRITTEN, + .split_map = { .m_lblk = EXT_DATA_LBLK, .m_len = 1 }, + .nr_exp_ext = 1, + .exp_ext_state = { { .ex_lblk = EXT_DATA_LBLK, + .ex_len = EXT_DATA_LEN, + .is_unwrit = 0 } }, + .is_zeroout_test = 1, + .nr_exp_data_segs = 2, + .exp_data_state = { { .exp_char = 0, .off_blk = 0, .len_blk = 1 }, + { .exp_char = 'X', + .off_blk = 1, + .len_blk = EXT_DATA_LEN - 1 } } }, + { .desc = "split writ extent to 2 extents and convert 2nd half unwrit (zeroout)", + .type = TEST_SPLIT_CONVERT, + .is_unwrit_at_start = 0, + .split_flags = EXT4_GET_BLOCKS_CONVERT_UNWRITTEN, + .split_map = { .m_lblk = EXT_DATA_LBLK + 1, .m_len = EXT_DATA_LEN - 1 }, + .nr_exp_ext = 1, + .exp_ext_state = { { .ex_lblk = EXT_DATA_LBLK, + .ex_len = EXT_DATA_LEN, + .is_unwrit = 0 } }, + .is_zeroout_test = 1, + .nr_exp_data_segs = 2, + .exp_data_state = { { .exp_char = 'X', .off_blk = 0, .len_blk = 1 }, + { .exp_char = 0, + .off_blk = 1, + .len_blk = EXT_DATA_LEN - 1 } } }, + { .desc = "split writ extent to 3 extents and convert 2nd half unwrit (zeroout)", + .type = TEST_SPLIT_CONVERT, + .is_unwrit_at_start = 0, + .split_flags = EXT4_GET_BLOCKS_CONVERT_UNWRITTEN, + .split_map = { .m_lblk = EXT_DATA_LBLK + 1, .m_len = EXT_DATA_LEN - 2 }, + .nr_exp_ext = 1, + .exp_ext_state = { { .ex_lblk = EXT_DATA_LBLK, + .ex_len = EXT_DATA_LEN, + .is_unwrit = 0 } }, + .is_zeroout_test = 1, + .nr_exp_data_segs = 3, + .exp_data_state = { { .exp_char = 'X', .off_blk = 0, .len_blk = 1 }, + { .exp_char = 0, + .off_blk = 1, + .len_blk = EXT_DATA_LEN - 2 }, + { .exp_char = 'X', + .off_blk = EXT_DATA_LEN - 1, + .len_blk = 1 } } }, +}; + +/* Tests to trigger ext4_ext_map_blocks() -> convert_initialized_extent() */ +static const struct kunit_ext_test_param test_convert_initialized_params[] = { + /* writ to unwrit splits */ + { .desc = "split writ extent to 2 extents and convert 1st half unwrit", + .type = TEST_CREATE_BLOCKS, + .split_flags = EXT4_GET_BLOCKS_CONVERT_UNWRITTEN, + .is_unwrit_at_start = 0, + .split_map = { .m_lblk = EXT_DATA_LBLK, .m_len = 1 }, + .nr_exp_ext = 2, + .exp_ext_state = { { .ex_lblk = EXT_DATA_LBLK, + .ex_len = 1, + .is_unwrit = 1 }, + { .ex_lblk = EXT_DATA_LBLK + 1, + .ex_len = EXT_DATA_LEN - 1, + .is_unwrit = 0 } }, + .is_zeroout_test = 0 }, + { .desc = "split writ extent to 2 extents and convert 2nd half unwrit", + .type = TEST_CREATE_BLOCKS, + .split_flags = EXT4_GET_BLOCKS_CONVERT_UNWRITTEN, + .is_unwrit_at_start = 0, + .split_map = { .m_lblk = EXT_DATA_LBLK + 1, .m_len = EXT_DATA_LEN - 1 }, + .nr_exp_ext = 2, + .exp_ext_state = { { .ex_lblk = EXT_DATA_LBLK, + .ex_len = 1, + .is_unwrit = 0 }, + { .ex_lblk = EXT_DATA_LBLK + 1, + .ex_len = EXT_DATA_LEN - 1, + .is_unwrit = 1 } }, + .is_zeroout_test = 0 }, + { .desc = "split writ extent to 3 extents and convert 2nd half to unwrit", + .type = TEST_CREATE_BLOCKS, + .split_flags = EXT4_GET_BLOCKS_CONVERT_UNWRITTEN, + .is_unwrit_at_start = 0, + .split_map = { .m_lblk = EXT_DATA_LBLK + 1, .m_len = EXT_DATA_LEN - 2 }, + .nr_exp_ext = 3, + .exp_ext_state = { { .ex_lblk = EXT_DATA_LBLK, + .ex_len = 1, + .is_unwrit = 0 }, + { .ex_lblk = EXT_DATA_LBLK + 1, + .ex_len = EXT_DATA_LEN - 2, + .is_unwrit = 1 }, + { .ex_lblk = EXT_DATA_LBLK + 1 + (EXT_DATA_LEN - 2), + .ex_len = 1, + .is_unwrit = 0 } }, + .is_zeroout_test = 0 }, + + /* writ to unwrit splits (zeroout) */ + { .desc = "split writ extent to 2 extents and convert 1st half unwrit (zeroout)", + .type = TEST_CREATE_BLOCKS, + .is_unwrit_at_start = 0, + .split_flags = EXT4_GET_BLOCKS_CONVERT_UNWRITTEN, + .split_map = { .m_lblk = EXT_DATA_LBLK, .m_len = 1 }, + .nr_exp_ext = 1, + .exp_ext_state = { { .ex_lblk = EXT_DATA_LBLK, + .ex_len = EXT_DATA_LEN, + .is_unwrit = 0 } }, + .is_zeroout_test = 1, + .nr_exp_data_segs = 2, + .exp_data_state = { { .exp_char = 0, .off_blk = 0, .len_blk = 1 }, + { .exp_char = 'X', + .off_blk = 1, + .len_blk = EXT_DATA_LEN - 1 } } }, + { .desc = "split writ extent to 2 extents and convert 2nd half unwrit (zeroout)", + .type = TEST_CREATE_BLOCKS, + .is_unwrit_at_start = 0, + .split_flags = EXT4_GET_BLOCKS_CONVERT_UNWRITTEN, + .split_map = { .m_lblk = EXT_DATA_LBLK + 1, .m_len = EXT_DATA_LEN - 1 }, + .nr_exp_ext = 1, + .exp_ext_state = { { .ex_lblk = EXT_DATA_LBLK, + .ex_len = EXT_DATA_LEN, + .is_unwrit = 0 } }, + .is_zeroout_test = 1, + .nr_exp_data_segs = 2, + .exp_data_state = { { .exp_char = 'X', .off_blk = 0, .len_blk = 1 }, + { .exp_char = 0, + .off_blk = 1, + .len_blk = EXT_DATA_LEN - 1 } } }, + { .desc = "split writ extent to 3 extents and convert 2nd half unwrit (zeroout)", + .type = TEST_CREATE_BLOCKS, + .is_unwrit_at_start = 0, + .split_flags = EXT4_GET_BLOCKS_CONVERT_UNWRITTEN, + .split_map = { .m_lblk = EXT_DATA_LBLK + 1, .m_len = EXT_DATA_LEN - 2 }, + .nr_exp_ext = 1, + .exp_ext_state = { { .ex_lblk = EXT_DATA_LBLK, + .ex_len = EXT_DATA_LEN, + .is_unwrit = 0 } }, + .is_zeroout_test = 1, + .nr_exp_data_segs = 3, + .exp_data_state = { { .exp_char = 'X', .off_blk = 0, .len_blk = 1 }, + { .exp_char = 0, + .off_blk = 1, + .len_blk = EXT_DATA_LEN - 2 }, + { .exp_char = 'X', + .off_blk = EXT_DATA_LEN - 1, + .len_blk = 1 } } }, +}; + +/* Tests to trigger ext4_ext_map_blocks() -> ext4_ext_handle_unwritten_exntents() */ +static const struct kunit_ext_test_param test_handle_unwritten_params[] = { + /* unwrit to writ splits via endio path */ + { .desc = "split unwrit extent to 2 extents and convert 1st half writ (endio)", + .type = TEST_CREATE_BLOCKS, + .is_unwrit_at_start = 1, + .split_flags = EXT4_GET_BLOCKS_CONVERT, + .split_map = { .m_lblk = EXT_DATA_LBLK, .m_len = 1 }, + .nr_exp_ext = 2, + .exp_ext_state = { { .ex_lblk = EXT_DATA_LBLK, + .ex_len = 1, + .is_unwrit = 0 }, + { .ex_lblk = EXT_DATA_LBLK + 1, + .ex_len = EXT_DATA_LEN - 1, + .is_unwrit = 1 } }, + .is_zeroout_test = 0 }, + { .desc = "split unwrit extent to 2 extents and convert 2nd half writ (endio)", + .type = TEST_CREATE_BLOCKS, + .is_unwrit_at_start = 1, + .split_flags = EXT4_GET_BLOCKS_CONVERT, + .split_map = { .m_lblk = EXT_DATA_LBLK + 1, .m_len = EXT_DATA_LEN - 1 }, + .nr_exp_ext = 2, + .exp_ext_state = { { .ex_lblk = EXT_DATA_LBLK, + .ex_len = 1, + .is_unwrit = 1 }, + { .ex_lblk = EXT_DATA_LBLK + 1, + .ex_len = EXT_DATA_LEN - 1, + .is_unwrit = 0 } }, + .is_zeroout_test = 0 }, + { .desc = "split unwrit extent to 3 extents and convert 2nd half to writ (endio)", + .type = TEST_CREATE_BLOCKS, + .is_unwrit_at_start = 1, + .split_flags = EXT4_GET_BLOCKS_CONVERT, + .split_map = { .m_lblk = EXT_DATA_LBLK + 1, .m_len = EXT_DATA_LEN - 2 }, + .nr_exp_ext = 3, + .exp_ext_state = { { .ex_lblk = EXT_DATA_LBLK, + .ex_len = 1, + .is_unwrit = 1 }, + { .ex_lblk = EXT_DATA_LBLK + 1, + .ex_len = EXT_DATA_LEN - 2, + .is_unwrit = 0 }, + { .ex_lblk = EXT_DATA_LBLK + 1 + (EXT_DATA_LEN - 2), + .ex_len = 1, + .is_unwrit = 1 } }, + .is_zeroout_test = 0 }, + + /* unwrit to writ splits via non-endio path */ + { .desc = "split unwrit extent to 2 extents and convert 1st half writ (non endio)", + .type = TEST_CREATE_BLOCKS, + .is_unwrit_at_start = 1, + .split_flags = EXT4_GET_BLOCKS_CREATE, + .split_map = { .m_lblk = EXT_DATA_LBLK, .m_len = 1 }, + .nr_exp_ext = 2, + .disable_zeroout = true, + .exp_ext_state = { { .ex_lblk = EXT_DATA_LBLK, + .ex_len = 1, + .is_unwrit = 0 }, + { .ex_lblk = EXT_DATA_LBLK + 1, + .ex_len = EXT_DATA_LEN - 1, + .is_unwrit = 1 } }, + .is_zeroout_test = 0 }, + { .desc = "split unwrit extent to 2 extents and convert 2nd half writ (non endio)", + .type = TEST_CREATE_BLOCKS, + .is_unwrit_at_start = 1, + .split_flags = EXT4_GET_BLOCKS_CREATE, + .split_map = { .m_lblk = EXT_DATA_LBLK + 1, .m_len = EXT_DATA_LEN - 1 }, + .nr_exp_ext = 2, + .disable_zeroout = true, + .exp_ext_state = { { .ex_lblk = EXT_DATA_LBLK, + .ex_len = 1, + .is_unwrit = 1 }, + { .ex_lblk = EXT_DATA_LBLK + 1, + .ex_len = EXT_DATA_LEN - 1, + .is_unwrit = 0 } }, + .is_zeroout_test = 0 }, + { .desc = "split unwrit extent to 3 extents and convert 2nd half to writ (non endio)", + .type = TEST_CREATE_BLOCKS, + .is_unwrit_at_start = 1, + .split_flags = EXT4_GET_BLOCKS_CREATE, + .split_map = { .m_lblk = EXT_DATA_LBLK + 1, .m_len = EXT_DATA_LEN - 2 }, + .nr_exp_ext = 3, + .disable_zeroout = true, + .exp_ext_state = { { .ex_lblk = EXT_DATA_LBLK, + .ex_len = 1, + .is_unwrit = 1 }, + { .ex_lblk = EXT_DATA_LBLK + 1, + .ex_len = EXT_DATA_LEN - 2, + .is_unwrit = 0 }, + { .ex_lblk = EXT_DATA_LBLK + 1 + (EXT_DATA_LEN - 2), + .ex_len = 1, + .is_unwrit = 1 } }, + .is_zeroout_test = 0 }, + + /* + * ***** zeroout tests ***** + */ + /* unwrit to writ splits (endio)*/ + { .desc = "split unwrit extent to 2 extents and convert 1st half writ (endio, zeroout)", + .type = TEST_CREATE_BLOCKS, + .is_unwrit_at_start = 1, + .split_flags = EXT4_GET_BLOCKS_CONVERT, + .split_map = { .m_lblk = EXT_DATA_LBLK, .m_len = 1 }, + .nr_exp_ext = 1, + .exp_ext_state = { { .ex_lblk = EXT_DATA_LBLK, + .ex_len = EXT_DATA_LEN, + .is_unwrit = 0 } }, + .is_zeroout_test = 1, + .nr_exp_data_segs = 2, + .exp_data_state = { { .exp_char = 'X', .off_blk = 0, .len_blk = 1 }, + { .exp_char = 0, + .off_blk = 1, + .len_blk = EXT_DATA_LEN - 1 } } }, + { .desc = "split unwrit extent to 2 extents and convert 2nd half writ (endio, zeroout)", + .type = TEST_CREATE_BLOCKS, + .is_unwrit_at_start = 1, + .split_flags = EXT4_GET_BLOCKS_CONVERT, + .split_map = { .m_lblk = EXT_DATA_LBLK + 1, .m_len = EXT_DATA_LEN - 1 }, + .nr_exp_ext = 1, + .exp_ext_state = { { .ex_lblk = EXT_DATA_LBLK, + .ex_len = EXT_DATA_LEN, + .is_unwrit = 0 } }, + .is_zeroout_test = 1, + .nr_exp_data_segs = 2, + .exp_data_state = { { .exp_char = 0, .off_blk = 0, .len_blk = 1 }, + { .exp_char = 'X', + .off_blk = 1, + .len_blk = EXT_DATA_LEN - 1 } } }, + { .desc = "split unwrit extent to 3 extents and convert 2nd half writ (endio, zeroout)", + .type = TEST_CREATE_BLOCKS, + .is_unwrit_at_start = 1, + .split_flags = EXT4_GET_BLOCKS_CONVERT, + .split_map = { .m_lblk = EXT_DATA_LBLK + 1, .m_len = EXT_DATA_LEN - 2 }, + .nr_exp_ext = 1, + .exp_ext_state = { { .ex_lblk = EXT_DATA_LBLK, + .ex_len = EXT_DATA_LEN, + .is_unwrit = 0 } }, + .is_zeroout_test = 1, + .nr_exp_data_segs = 3, + .exp_data_state = { { .exp_char = 0, .off_blk = 0, .len_blk = 1 }, + { .exp_char = 'X', + .off_blk = 1, + .len_blk = EXT_DATA_LEN - 2 }, + { .exp_char = 0, + .off_blk = EXT_DATA_LEN - 1, + .len_blk = 1 } } }, + + /* unwrit to writ splits (non-endio)*/ + { .desc = "split unwrit extent to 2 extents and convert 1st half writ (non-endio, zeroout)", + .type = TEST_CREATE_BLOCKS, + .is_unwrit_at_start = 1, + .split_flags = EXT4_GET_BLOCKS_CREATE, + .split_map = { .m_lblk = EXT_DATA_LBLK, .m_len = 1 }, + .nr_exp_ext = 1, + .exp_ext_state = { { .ex_lblk = EXT_DATA_LBLK, + .ex_len = EXT_DATA_LEN, + .is_unwrit = 0 } }, + .is_zeroout_test = 1, + .nr_exp_data_segs = 2, + .exp_data_state = { { .exp_char = 'X', .off_blk = 0, .len_blk = 1 }, + { .exp_char = 0, + .off_blk = 1, + .len_blk = EXT_DATA_LEN - 1 } } }, + { .desc = "split unwrit extent to 2 extents and convert 2nd half writ (non-endio, zeroout)", + .type = TEST_CREATE_BLOCKS, + .is_unwrit_at_start = 1, + .split_flags = EXT4_GET_BLOCKS_CREATE, + .split_map = { .m_lblk = EXT_DATA_LBLK + 1, .m_len = EXT_DATA_LEN - 1 }, + .nr_exp_ext = 1, + .exp_ext_state = { { .ex_lblk = EXT_DATA_LBLK, + .ex_len = EXT_DATA_LEN, + .is_unwrit = 0 } }, + .is_zeroout_test = 1, + .nr_exp_data_segs = 2, + .exp_data_state = { { .exp_char = 0, .off_blk = 0, .len_blk = 1 }, + { .exp_char = 'X', + .off_blk = 1, + .len_blk = EXT_DATA_LEN - 1 } } }, + { .desc = "split unwrit extent to 3 extents and convert 2nd half writ (non-endio, zeroout)", + .type = TEST_CREATE_BLOCKS, + .is_unwrit_at_start = 1, + .split_flags = EXT4_GET_BLOCKS_CREATE, + .split_map = { .m_lblk = EXT_DATA_LBLK + 1, .m_len = EXT_DATA_LEN - 2 }, + .nr_exp_ext = 1, + .exp_ext_state = { { .ex_lblk = EXT_DATA_LBLK, + .ex_len = EXT_DATA_LEN, + .is_unwrit = 0 } }, + .is_zeroout_test = 1, + .nr_exp_data_segs = 3, + .exp_data_state = { { .exp_char = 0, .off_blk = 0, .len_blk = 1 }, + { .exp_char = 'X', + .off_blk = 1, + .len_blk = EXT_DATA_LEN - 2 }, + { .exp_char = 0, + .off_blk = EXT_DATA_LEN - 1, + .len_blk = 1 } } }, +}; + +static void ext_get_desc(struct kunit *test, const void *p, char *desc) + +{ + struct kunit_ext_test_param *param = (struct kunit_ext_test_param *)p; + + snprintf(desc, KUNIT_PARAM_DESC_SIZE, "%s %s\n", param->desc, + (param->type & TEST_CREATE_BLOCKS) ? "(highlevel)" : ""); +} + +static int test_split_convert_param_init(struct kunit *test) +{ + size_t arr_size = ARRAY_SIZE(test_split_convert_params); + + kunit_register_params_array(test, test_split_convert_params, arr_size, + ext_get_desc); + return 0; +} + +static int test_convert_initialized_param_init(struct kunit *test) +{ + size_t arr_size = ARRAY_SIZE(test_convert_initialized_params); + + kunit_register_params_array(test, test_convert_initialized_params, + arr_size, ext_get_desc); + return 0; +} + +static int test_handle_unwritten_init(struct kunit *test) +{ + size_t arr_size = ARRAY_SIZE(test_handle_unwritten_params); + + kunit_register_params_array(test, test_handle_unwritten_params, + arr_size, ext_get_desc); + return 0; +} + +/* + * Note that we use KUNIT_CASE_PARAM_WITH_INIT() instead of the more compact + * KUNIT_ARRAY_PARAM() because the later currently has a limitation causing the + * output parsing to be prone to error. For more context: + * + * https://lore.kernel.org/linux-kselftest/aULJpTvJDw9ctUDe@li-dc0c254c-257c-11b2-a85c-98b6c1322444.ibm.com/ + */ +static struct kunit_case extents_test_cases[] = { + KUNIT_CASE_PARAM_WITH_INIT(test_split_convert, kunit_array_gen_params, + test_split_convert_param_init, NULL), + KUNIT_CASE_PARAM_WITH_INIT(test_split_convert, kunit_array_gen_params, + test_convert_initialized_param_init, NULL), + KUNIT_CASE_PARAM_WITH_INIT(test_split_convert, kunit_array_gen_params, + test_handle_unwritten_init, NULL), + {} +}; + +static struct kunit_suite extents_test_suite = { + .name = "ext4_extents_test", + .init = extents_kunit_init, + .exit = extents_kunit_exit, + .test_cases = extents_test_cases, +}; + +kunit_test_suites(&extents_test_suite); + +MODULE_LICENSE("GPL"); diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index 2cf5759ba689..3630b27e4fd7 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c @@ -32,6 +32,7 @@ #include "ext4_jbd2.h" #include "ext4_extents.h" #include "xattr.h" +#include <kunit/static_stub.h> #include <trace/events/ext4.h> @@ -40,11 +41,9 @@ */ #define EXT4_EXT_MAY_ZEROOUT 0x1 /* safe to zeroout if split fails \ due to ENOSPC */ -#define EXT4_EXT_MARK_UNWRIT1 0x2 /* mark first half unwritten */ -#define EXT4_EXT_MARK_UNWRIT2 0x4 /* mark second half unwritten */ - -#define EXT4_EXT_DATA_VALID1 0x8 /* first half contains valid data */ -#define EXT4_EXT_DATA_VALID2 0x10 /* second half contains valid data */ +static struct ext4_ext_path *ext4_split_convert_extents( + handle_t *handle, struct inode *inode, struct ext4_map_blocks *map, + struct ext4_ext_path *path, int flags, unsigned int *allocated); static __le32 ext4_extent_block_csum(struct inode *inode, struct ext4_extent_header *eh) @@ -86,8 +85,7 @@ static void ext4_extent_block_csum_set(struct inode *inode, static struct ext4_ext_path *ext4_split_extent_at(handle_t *handle, struct inode *inode, struct ext4_ext_path *path, - ext4_lblk_t split, - int split_flag, int flags); + ext4_lblk_t split, int flags); static int ext4_ext_trunc_restart_fn(struct inode *inode, int *dropped) { @@ -192,6 +190,9 @@ static int __ext4_ext_dirty(const char *where, unsigned int line, { int err; + KUNIT_STATIC_STUB_REDIRECT(__ext4_ext_dirty, where, line, handle, inode, + path); + WARN_ON(!rwsem_is_locked(&EXT4_I(inode)->i_data_sem)); if (path->p_bh) { ext4_extent_block_csum_set(inode, ext_block_hdr(path->p_bh)); @@ -332,15 +333,12 @@ ext4_force_split_extent_at(handle_t *handle, struct inode *inode, struct ext4_ext_path *path, ext4_lblk_t lblk, int nofail) { - int unwritten = ext4_ext_is_unwritten(path[path->p_depth].p_ext); int flags = EXT4_EX_NOCACHE | EXT4_GET_BLOCKS_SPLIT_NOMERGE; if (nofail) flags |= EXT4_GET_BLOCKS_METADATA_NOFAIL | EXT4_EX_NOFAIL; - return ext4_split_extent_at(handle, inode, path, lblk, unwritten ? - EXT4_EXT_MARK_UNWRIT1|EXT4_EXT_MARK_UNWRIT2 : 0, - flags); + return ext4_split_extent_at(handle, inode, path, lblk, flags); } static int @@ -530,6 +528,8 @@ static void ext4_cache_extents(struct inode *inode, ext4_lblk_t prev = 0; int i; + KUNIT_STATIC_STUB_REDIRECT(ext4_cache_extents, inode, eh); + for (i = le16_to_cpu(eh->eh_entries); i > 0; i--, ex++) { unsigned int status = EXTENT_STATUS_WRITTEN; ext4_lblk_t lblk = le32_to_cpu(ex->ee_block); @@ -893,6 +893,8 @@ ext4_find_extent(struct inode *inode, ext4_lblk_t block, int ret; gfp_t gfp_flags = GFP_NOFS; + KUNIT_STATIC_STUB_REDIRECT(ext4_find_extent, inode, block, path, flags); + if (flags & EXT4_EX_NOFAIL) gfp_flags |= __GFP_NOFAIL; @@ -1985,6 +1987,9 @@ ext4_ext_insert_extent(handle_t *handle, struct inode *inode, ext4_lblk_t next; int mb_flags = 0, unwritten; + KUNIT_STATIC_STUB_REDIRECT(ext4_ext_insert_extent, handle, inode, path, + newext, gb_flags); + if (gb_flags & EXT4_GET_BLOCKS_DELALLOC_RESERVE) mb_flags |= EXT4_MB_DELALLOC_RESERVED; if (unlikely(ext4_ext_get_actual_len(newext) == 0)) { @@ -2944,10 +2949,6 @@ again: } else { path = kcalloc(depth + 1, sizeof(struct ext4_ext_path), GFP_NOFS | __GFP_NOFAIL); - if (path == NULL) { - ext4_journal_stop(handle); - return -ENOMEM; - } path[0].p_maxdepth = path[0].p_depth = depth; path[0].p_hdr = ext_inode_hdr(inode); i = 0; @@ -3133,8 +3134,8 @@ static void ext4_zeroout_es(struct inode *inode, struct ext4_extent *ex) ext4_fsblk_t ee_pblock; unsigned int ee_len; - ee_block = le32_to_cpu(ex->ee_block); - ee_len = ext4_ext_get_actual_len(ex); + ee_block = le32_to_cpu(ex->ee_block); + ee_len = ext4_ext_get_actual_len(ex); ee_pblock = ext4_ext_pblock(ex); if (ee_len == 0) @@ -3150,6 +3151,8 @@ static int ext4_ext_zeroout(struct inode *inode, struct ext4_extent *ex) ext4_fsblk_t ee_pblock; unsigned int ee_len; + KUNIT_STATIC_STUB_REDIRECT(ext4_ext_zeroout, inode, ex); + ee_len = ext4_ext_get_actual_len(ex); ee_pblock = ext4_ext_pblock(ex); return ext4_issue_zeroout(inode, le32_to_cpu(ex->ee_block), ee_pblock, @@ -3163,35 +3166,30 @@ static int ext4_ext_zeroout(struct inode *inode, struct ext4_extent *ex) * @inode: the file inode * @path: the path to the extent * @split: the logical block where the extent is splitted. - * @split_flags: indicates if the extent could be zeroout if split fails, and - * the states(init or unwritten) of new extents. * @flags: flags used to insert new extent to extent tree. * * * Splits extent [a, b] into two extents [a, @split) and [@split, b], states - * of which are determined by split_flag. + * of which are same as the original extent. No conversion is performed. * - * There are two cases: - * a> the extent are splitted into two extent. - * b> split is not needed, and just mark the extent. - * - * Return an extent path pointer on success, or an error pointer on failure. + * Return an extent path pointer on success, or an error pointer on failure. On + * failure, the extent is restored to original state. */ static struct ext4_ext_path *ext4_split_extent_at(handle_t *handle, struct inode *inode, struct ext4_ext_path *path, ext4_lblk_t split, - int split_flag, int flags) + int flags) { ext4_fsblk_t newblock; ext4_lblk_t ee_block; - struct ext4_extent *ex, newex, orig_ex, zero_ex; + struct ext4_extent *ex, newex, orig_ex; struct ext4_extent *ex2 = NULL; unsigned int ee_len, depth; - int err = 0; + int err = 0, insert_err = 0, is_unwrit = 0; - BUG_ON((split_flag & (EXT4_EXT_DATA_VALID1 | EXT4_EXT_DATA_VALID2)) == - (EXT4_EXT_DATA_VALID1 | EXT4_EXT_DATA_VALID2)); + /* Do not cache extents that are in the process of being modified. */ + flags |= EXT4_EX_NOCACHE; ext_debug(inode, "logical block %llu\n", (unsigned long long)split); @@ -3202,39 +3200,24 @@ static struct ext4_ext_path *ext4_split_extent_at(handle_t *handle, ee_block = le32_to_cpu(ex->ee_block); ee_len = ext4_ext_get_actual_len(ex); newblock = split - ee_block + ext4_ext_pblock(ex); + is_unwrit = ext4_ext_is_unwritten(ex); BUG_ON(split < ee_block || split >= (ee_block + ee_len)); - BUG_ON(!ext4_ext_is_unwritten(ex) && - split_flag & (EXT4_EXT_MAY_ZEROOUT | - EXT4_EXT_MARK_UNWRIT1 | - EXT4_EXT_MARK_UNWRIT2)); - err = ext4_ext_get_access(handle, inode, path + depth); - if (err) + /* + * No split needed + */ + if (split == ee_block) goto out; - if (split == ee_block) { - /* - * case b: block @split is the block that the extent begins with - * then we just change the state of the extent, and splitting - * is not needed. - */ - if (split_flag & EXT4_EXT_MARK_UNWRIT2) - ext4_ext_mark_unwritten(ex); - else - ext4_ext_mark_initialized(ex); - - if (!(flags & EXT4_GET_BLOCKS_SPLIT_NOMERGE)) - ext4_ext_try_to_merge(handle, inode, path, ex); - - err = ext4_ext_dirty(handle, inode, path + path->p_depth); + err = ext4_ext_get_access(handle, inode, path + depth); + if (err) goto out; - } /* case a */ memcpy(&orig_ex, ex, sizeof(orig_ex)); ex->ee_len = cpu_to_le16(split - ee_block); - if (split_flag & EXT4_EXT_MARK_UNWRIT1) + if (is_unwrit) ext4_ext_mark_unwritten(ex); /* @@ -3249,17 +3232,16 @@ static struct ext4_ext_path *ext4_split_extent_at(handle_t *handle, ex2->ee_block = cpu_to_le32(split); ex2->ee_len = cpu_to_le16(ee_len - (split - ee_block)); ext4_ext_store_pblock(ex2, newblock); - if (split_flag & EXT4_EXT_MARK_UNWRIT2) + if (is_unwrit) ext4_ext_mark_unwritten(ex2); path = ext4_ext_insert_extent(handle, inode, path, &newex, flags); if (!IS_ERR(path)) - goto out; - - err = PTR_ERR(path); - if (err != -ENOSPC && err != -EDQUOT && err != -ENOMEM) return path; + insert_err = PTR_ERR(path); + err = 0; + /* * Get a new path to try to zeroout or fix the extent length. * Using EXT4_EX_NOFAIL guarantees that ext4_find_extent() @@ -3272,70 +3254,124 @@ static struct ext4_ext_path *ext4_split_extent_at(handle_t *handle, if (IS_ERR(path)) { EXT4_ERROR_INODE(inode, "Failed split extent on %u, err %ld", split, PTR_ERR(path)); - return path; + goto out_path; } - depth = ext_depth(inode); - ex = path[depth].p_ext; - if (EXT4_EXT_MAY_ZEROOUT & split_flag) { - if (split_flag & (EXT4_EXT_DATA_VALID1|EXT4_EXT_DATA_VALID2)) { - if (split_flag & EXT4_EXT_DATA_VALID1) { - err = ext4_ext_zeroout(inode, ex2); - zero_ex.ee_block = ex2->ee_block; - zero_ex.ee_len = cpu_to_le16( - ext4_ext_get_actual_len(ex2)); - ext4_ext_store_pblock(&zero_ex, - ext4_ext_pblock(ex2)); - } else { - err = ext4_ext_zeroout(inode, ex); - zero_ex.ee_block = ex->ee_block; - zero_ex.ee_len = cpu_to_le16( - ext4_ext_get_actual_len(ex)); - ext4_ext_store_pblock(&zero_ex, - ext4_ext_pblock(ex)); - } - } else { - err = ext4_ext_zeroout(inode, &orig_ex); - zero_ex.ee_block = orig_ex.ee_block; - zero_ex.ee_len = cpu_to_le16( - ext4_ext_get_actual_len(&orig_ex)); - ext4_ext_store_pblock(&zero_ex, - ext4_ext_pblock(&orig_ex)); - } + err = ext4_ext_get_access(handle, inode, path + depth); + if (err) + goto out; - if (!err) { - /* update the extent length and mark as initialized */ - ex->ee_len = cpu_to_le16(ee_len); - ext4_ext_try_to_merge(handle, inode, path, ex); - err = ext4_ext_dirty(handle, inode, path + path->p_depth); - if (!err) - /* update extent status tree */ - ext4_zeroout_es(inode, &zero_ex); - /* If we failed at this point, we don't know in which - * state the extent tree exactly is so don't try to fix - * length of the original extent as it may do even more - * damage. - */ - goto out; - } - } + depth = ext_depth(inode); + ex = path[depth].p_ext; fix_extent_len: ex->ee_len = orig_ex.ee_len; - /* - * Ignore ext4_ext_dirty return value since we are already in error path - * and err is a non-zero error code. - */ - ext4_ext_dirty(handle, inode, path + path->p_depth); + err = ext4_ext_dirty(handle, inode, path + path->p_depth); out: - if (err) { + if (err || insert_err) { ext4_free_ext_path(path); - path = ERR_PTR(err); + path = err ? ERR_PTR(err) : ERR_PTR(insert_err); } +out_path: + if (IS_ERR(path)) + /* Remove all remaining potentially stale extents. */ + ext4_es_remove_extent(inode, ee_block, ee_len); ext4_ext_show_leaf(inode, path); return path; } +static int ext4_split_extent_zeroout(handle_t *handle, struct inode *inode, + struct ext4_ext_path *path, + struct ext4_map_blocks *map, int flags) +{ + struct ext4_extent *ex; + unsigned int ee_len, depth; + ext4_lblk_t ee_block; + uint64_t lblk, pblk, len; + int is_unwrit; + int err = 0; + + depth = ext_depth(inode); + ex = path[depth].p_ext; + ee_block = le32_to_cpu(ex->ee_block); + ee_len = ext4_ext_get_actual_len(ex); + is_unwrit = ext4_ext_is_unwritten(ex); + + if (flags & EXT4_GET_BLOCKS_CONVERT) { + /* + * EXT4_GET_BLOCKS_CONVERT: Caller wants the range specified by + * map to be initialized. Zeroout everything except the map + * range. + */ + + loff_t map_end = (loff_t) map->m_lblk + map->m_len; + loff_t ex_end = (loff_t) ee_block + ee_len; + + if (!is_unwrit) + /* Shouldn't happen. Just exit */ + return -EINVAL; + + /* zeroout left */ + if (map->m_lblk > ee_block) { + lblk = ee_block; + len = map->m_lblk - ee_block; + pblk = ext4_ext_pblock(ex); + err = ext4_issue_zeroout(inode, lblk, pblk, len); + if (err) + /* ZEROOUT failed, just return original error */ + return err; + } + + /* zeroout right */ + if (map_end < ex_end) { + lblk = map_end; + len = ex_end - map_end; + pblk = ext4_ext_pblock(ex) + (map_end - ee_block); + err = ext4_issue_zeroout(inode, lblk, pblk, len); + if (err) + /* ZEROOUT failed, just return original error */ + return err; + } + } else if (flags & EXT4_GET_BLOCKS_CONVERT_UNWRITTEN) { + /* + * EXT4_GET_BLOCKS_CONVERT_UNWRITTEN: Caller wants the + * range specified by map to be marked unwritten. + * Zeroout the map range leaving rest as it is. + */ + + if (is_unwrit) + /* Shouldn't happen. Just exit */ + return -EINVAL; + + lblk = map->m_lblk; + len = map->m_len; + pblk = ext4_ext_pblock(ex) + (map->m_lblk - ee_block); + err = ext4_issue_zeroout(inode, lblk, pblk, len); + if (err) + /* ZEROOUT failed, just return original error */ + return err; + } else { + /* + * We no longer perform unwritten to unwritten splits in IO paths. + * Hence this should not happen. + */ + WARN_ON_ONCE(true); + return -EINVAL; + } + + err = ext4_ext_get_access(handle, inode, path + depth); + if (err) + return err; + + ext4_ext_mark_initialized(ex); + + ext4_ext_dirty(handle, inode, path + depth); + if (err) + return err; + + return 0; +} + /* * ext4_split_extent() splits an extent and mark extent which is covered * by @map as split_flags indicates @@ -3352,13 +3388,13 @@ static struct ext4_ext_path *ext4_split_extent(handle_t *handle, struct ext4_ext_path *path, struct ext4_map_blocks *map, int split_flag, int flags, - unsigned int *allocated) + unsigned int *allocated, bool *did_zeroout) { - ext4_lblk_t ee_block; + ext4_lblk_t ee_block, orig_ee_block; struct ext4_extent *ex; - unsigned int ee_len, depth; - int unwritten; - int split_flag1, flags1; + unsigned int ee_len, orig_ee_len, depth; + int unwritten, orig_unwritten; + int orig_err = 0; depth = ext_depth(inode); ex = path[depth].p_ext; @@ -3366,25 +3402,27 @@ static struct ext4_ext_path *ext4_split_extent(handle_t *handle, ee_len = ext4_ext_get_actual_len(ex); unwritten = ext4_ext_is_unwritten(ex); + orig_ee_block = ee_block; + orig_ee_len = ee_len; + orig_unwritten = unwritten; + + /* Do not cache extents that are in the process of being modified. */ + flags |= EXT4_EX_NOCACHE; + if (map->m_lblk + map->m_len < ee_block + ee_len) { - split_flag1 = split_flag & EXT4_EXT_MAY_ZEROOUT; - flags1 = flags | EXT4_GET_BLOCKS_SPLIT_NOMERGE; - if (unwritten) - split_flag1 |= EXT4_EXT_MARK_UNWRIT1 | - EXT4_EXT_MARK_UNWRIT2; - if (split_flag & EXT4_EXT_DATA_VALID2) - split_flag1 |= EXT4_EXT_DATA_VALID1; path = ext4_split_extent_at(handle, inode, path, - map->m_lblk + map->m_len, split_flag1, flags1); + map->m_lblk + map->m_len, flags); if (IS_ERR(path)) - return path; + goto try_zeroout; + /* * Update path is required because previous ext4_split_extent_at * may result in split of original leaf or extent zeroout. */ path = ext4_find_extent(inode, map->m_lblk, path, flags); if (IS_ERR(path)) - return path; + goto try_zeroout; + depth = ext_depth(inode); ex = path[depth].p_ext; if (!ex) { @@ -3393,22 +3431,69 @@ static struct ext4_ext_path *ext4_split_extent(handle_t *handle, ext4_free_ext_path(path); return ERR_PTR(-EFSCORRUPTED); } - unwritten = ext4_ext_is_unwritten(ex); + + /* extent would have changed so update original values */ + orig_ee_block = le32_to_cpu(ex->ee_block); + orig_ee_len = ext4_ext_get_actual_len(ex); + orig_unwritten = ext4_ext_is_unwritten(ex); } if (map->m_lblk >= ee_block) { - split_flag1 = split_flag & EXT4_EXT_DATA_VALID2; - if (unwritten) { - split_flag1 |= EXT4_EXT_MARK_UNWRIT1; - split_flag1 |= split_flag & (EXT4_EXT_MAY_ZEROOUT | - EXT4_EXT_MARK_UNWRIT2); - } - path = ext4_split_extent_at(handle, inode, path, - map->m_lblk, split_flag1, flags); + path = ext4_split_extent_at(handle, inode, path, map->m_lblk, + flags); if (IS_ERR(path)) - return path; + goto try_zeroout; + } + + goto success; + +try_zeroout: + /* + * There was an error in splitting the extent. So instead, just zeroout + * unwritten portions and convert it to initialized as a last resort. If + * there is any failure here we just return the original error + */ + + orig_err = PTR_ERR(path); + if (orig_err != -ENOSPC && orig_err != -EDQUOT && orig_err != -ENOMEM) + goto out_orig_err; + + /* we can't zeroout? just return the original err */ + if (!(split_flag & EXT4_EXT_MAY_ZEROOUT)) + goto out_orig_err; + + if (flags & EXT4_GET_BLOCKS_CONVERT_UNWRITTEN) { + int max_zeroout_blks = + EXT4_SB(inode->i_sb)->s_extent_max_zeroout_kb >> + (inode->i_sb->s_blocksize_bits - 10); + + if (map->m_len > max_zeroout_blks) + goto out_orig_err; } + path = ext4_find_extent(inode, map->m_lblk, NULL, flags); + if (IS_ERR(path)) + goto out_orig_err; + + depth = ext_depth(inode); + ex = path[depth].p_ext; + ee_block = le32_to_cpu(ex->ee_block); + ee_len = ext4_ext_get_actual_len(ex); + unwritten = ext4_ext_is_unwritten(ex); + + /* extent to zeroout should have been unchanged but its not */ + if (WARN_ON(ee_block != orig_ee_block || ee_len != orig_ee_len || + unwritten != orig_unwritten)) + goto out_free_path; + + if (ext4_split_extent_zeroout(handle, inode, path, map, flags)) + goto out_free_path; + + /* zeroout succeeded */ + if (did_zeroout) + *did_zeroout = true; + +success: if (allocated) { if (map->m_lblk + map->m_len > ee_block + ee_len) *allocated = ee_len - (map->m_lblk - ee_block); @@ -3417,6 +3502,12 @@ static struct ext4_ext_path *ext4_split_extent(handle_t *handle, } ext4_ext_show_leaf(inode, path); return path; + +out_free_path: + ext4_free_ext_path(path); +out_orig_err: + return ERR_PTR(orig_err); + } /* @@ -3452,7 +3543,6 @@ ext4_ext_convert_to_initialized(handle_t *handle, struct inode *inode, ext4_lblk_t ee_block, eof_block; unsigned int ee_len, depth, map_len = map->m_len; int err = 0; - int split_flag = EXT4_EXT_DATA_VALID2; unsigned int max_zeroout = 0; ext_debug(inode, "logical block %llu, max_blocks %u\n", @@ -3604,9 +3694,7 @@ ext4_ext_convert_to_initialized(handle_t *handle, struct inode *inode, * It is safe to convert extent to initialized via explicit * zeroout only if extent is fully inside i_size or new_size. */ - split_flag |= ee_block + ee_len <= eof_block ? EXT4_EXT_MAY_ZEROOUT : 0; - - if (EXT4_EXT_MAY_ZEROOUT & split_flag) + if (ee_block + ee_len <= eof_block) max_zeroout = sbi->s_extent_max_zeroout_kb >> (inode->i_sb->s_blocksize_bits - 10); @@ -3661,8 +3749,8 @@ ext4_ext_convert_to_initialized(handle_t *handle, struct inode *inode, } fallback: - path = ext4_split_extent(handle, inode, path, &split_map, split_flag, - flags, NULL); + path = ext4_split_convert_extents(handle, inode, &split_map, path, + flags | EXT4_GET_BLOCKS_CONVERT, NULL); if (IS_ERR(path)) return path; out: @@ -3712,7 +3800,8 @@ static struct ext4_ext_path *ext4_split_convert_extents(handle_t *handle, ext4_lblk_t ee_block; struct ext4_extent *ex; unsigned int ee_len; - int split_flag = 0, depth; + int split_flag = 0, depth, err = 0; + bool did_zeroout = false; ext_debug(inode, "logical block %llu, max_blocks %u\n", (unsigned long long)map->m_lblk, map->m_len); @@ -3726,34 +3815,87 @@ static struct ext4_ext_path *ext4_split_convert_extents(handle_t *handle, ee_block = le32_to_cpu(ex->ee_block); ee_len = ext4_ext_get_actual_len(ex); - /* Convert to unwritten */ - if (flags & EXT4_GET_BLOCKS_CONVERT_UNWRITTEN) { - split_flag |= EXT4_EXT_DATA_VALID1; - /* Convert to initialized */ - } else if (flags & EXT4_GET_BLOCKS_CONVERT) { - /* - * It is safe to convert extent to initialized via explicit - * zeroout only if extent is fully inside i_size or new_size. - */ - split_flag |= ee_block + ee_len <= eof_block ? - EXT4_EXT_MAY_ZEROOUT : 0; - split_flag |= (EXT4_EXT_MARK_UNWRIT2 | EXT4_EXT_DATA_VALID2); + /* No split needed */ + if (ee_block == map->m_lblk && ee_len == map->m_len) + goto convert; + + /* + * It is only safe to convert extent to initialized via explicit + * zeroout only if extent is fully inside i_size or new_size. + */ + split_flag |= ee_block + ee_len <= eof_block ? EXT4_EXT_MAY_ZEROOUT : 0; + + /* + * pass SPLIT_NOMERGE explicitly so we don't end up merging extents we + * just split. + */ + path = ext4_split_extent(handle, inode, path, map, split_flag, + flags | EXT4_GET_BLOCKS_SPLIT_NOMERGE, + allocated, &did_zeroout); + if (IS_ERR(path)) + return path; + +convert: + path = ext4_find_extent(inode, map->m_lblk, path, flags); + if (IS_ERR(path)) + return path; + + depth = ext_depth(inode); + ex = path[depth].p_ext; + + /* + * Conversion is already handled in case of zeroout + */ + if (!did_zeroout) { + err = ext4_ext_get_access(handle, inode, path + depth); + if (err) + goto err; + + if (flags & EXT4_GET_BLOCKS_CONVERT) + ext4_ext_mark_initialized(ex); + else if (flags & EXT4_GET_BLOCKS_CONVERT_UNWRITTEN) + ext4_ext_mark_unwritten(ex); + + if (!(flags & EXT4_GET_BLOCKS_SPLIT_NOMERGE)) + /* + * note: ext4_ext_correct_indexes() isn't needed here because + * borders are not changed + */ + ext4_ext_try_to_merge(handle, inode, path, ex); + + err = ext4_ext_dirty(handle, inode, path + depth); + if (err) + goto err; + } + + /* Lets update the extent status tree after conversion */ + if (!(flags & EXT4_EX_NOCACHE)) + ext4_es_insert_extent(inode, le32_to_cpu(ex->ee_block), + ext4_ext_get_actual_len(ex), + ext4_ext_pblock(ex), + ext4_ext_is_unwritten(ex) ? + EXTENT_STATUS_UNWRITTEN : + EXTENT_STATUS_WRITTEN, + false); + +err: + if (err) { + ext4_free_ext_path(path); + return ERR_PTR(err); } - flags |= EXT4_GET_BLOCKS_SPLIT_NOMERGE; - return ext4_split_extent(handle, inode, path, map, split_flag, flags, - allocated); + + return path; } static struct ext4_ext_path * ext4_convert_unwritten_extents_endio(handle_t *handle, struct inode *inode, struct ext4_map_blocks *map, - struct ext4_ext_path *path) + struct ext4_ext_path *path, int flags) { struct ext4_extent *ex; ext4_lblk_t ee_block; unsigned int ee_len; int depth; - int err = 0; depth = ext_depth(inode); ex = path[depth].p_ext; @@ -3763,66 +3905,21 @@ ext4_convert_unwritten_extents_endio(handle_t *handle, struct inode *inode, ext_debug(inode, "logical block %llu, max_blocks %u\n", (unsigned long long)ee_block, ee_len); - /* If extent is larger than requested it is a clear sign that we still - * have some extent state machine issues left. So extent_split is still - * required. - * TODO: Once all related issues will be fixed this situation should be - * illegal. - */ - if (ee_block != map->m_lblk || ee_len > map->m_len) { -#ifdef CONFIG_EXT4_DEBUG - ext4_warning(inode->i_sb, "Inode (%ld) finished: extent logical block %llu," - " len %u; IO logical block %llu, len %u", - inode->i_ino, (unsigned long long)ee_block, ee_len, - (unsigned long long)map->m_lblk, map->m_len); -#endif - path = ext4_split_convert_extents(handle, inode, map, path, - EXT4_GET_BLOCKS_CONVERT, NULL); - if (IS_ERR(path)) - return path; - - path = ext4_find_extent(inode, map->m_lblk, path, 0); - if (IS_ERR(path)) - return path; - depth = ext_depth(inode); - ex = path[depth].p_ext; - } - - err = ext4_ext_get_access(handle, inode, path + depth); - if (err) - goto errout; - /* first mark the extent as initialized */ - ext4_ext_mark_initialized(ex); - - /* note: ext4_ext_correct_indexes() isn't needed here because - * borders are not changed - */ - ext4_ext_try_to_merge(handle, inode, path, ex); - - /* Mark modified extent as dirty */ - err = ext4_ext_dirty(handle, inode, path + path->p_depth); - if (err) - goto errout; - - ext4_ext_show_leaf(inode, path); - return path; - -errout: - ext4_free_ext_path(path); - return ERR_PTR(err); + return ext4_split_convert_extents(handle, inode, map, path, flags, + NULL); } static struct ext4_ext_path * convert_initialized_extent(handle_t *handle, struct inode *inode, struct ext4_map_blocks *map, struct ext4_ext_path *path, + int flags, unsigned int *allocated) { struct ext4_extent *ex; ext4_lblk_t ee_block; unsigned int ee_len; int depth; - int err = 0; /* * Make sure that the extent is no bigger than we support with @@ -3839,53 +3936,33 @@ convert_initialized_extent(handle_t *handle, struct inode *inode, ext_debug(inode, "logical block %llu, max_blocks %u\n", (unsigned long long)ee_block, ee_len); - if (ee_block != map->m_lblk || ee_len > map->m_len) { - path = ext4_split_convert_extents(handle, inode, map, path, - EXT4_GET_BLOCKS_CONVERT_UNWRITTEN, NULL); - if (IS_ERR(path)) - return path; + path = ext4_split_convert_extents(handle, inode, map, path, flags, + NULL); + if (IS_ERR(path)) + return path; - path = ext4_find_extent(inode, map->m_lblk, path, 0); - if (IS_ERR(path)) - return path; - depth = ext_depth(inode); - ex = path[depth].p_ext; - if (!ex) { - EXT4_ERROR_INODE(inode, "unexpected hole at %lu", - (unsigned long) map->m_lblk); - err = -EFSCORRUPTED; - goto errout; - } - } + ext4_ext_show_leaf(inode, path); - err = ext4_ext_get_access(handle, inode, path + depth); - if (err) - goto errout; - /* first mark the extent as unwritten */ - ext4_ext_mark_unwritten(ex); + ext4_update_inode_fsync_trans(handle, inode, 1); - /* note: ext4_ext_correct_indexes() isn't needed here because - * borders are not changed + /* + * The extent might be initialized in case of zeroout. */ - ext4_ext_try_to_merge(handle, inode, path, ex); - - /* Mark modified extent as dirty */ - err = ext4_ext_dirty(handle, inode, path + path->p_depth); - if (err) - goto errout; - ext4_ext_show_leaf(inode, path); + path = ext4_find_extent(inode, map->m_lblk, path, flags); + if (IS_ERR(path)) + return path; - ext4_update_inode_fsync_trans(handle, inode, 1); + depth = ext_depth(inode); + ex = path[depth].p_ext; - map->m_flags |= EXT4_MAP_UNWRITTEN; + if (ext4_ext_is_unwritten(ex)) + map->m_flags |= EXT4_MAP_UNWRITTEN; + else + map->m_flags |= EXT4_MAP_MAPPED; if (*allocated > map->m_len) *allocated = map->m_len; map->m_len = *allocated; return path; - -errout: - ext4_free_ext_path(path); - return ERR_PTR(err); } static struct ext4_ext_path * @@ -3910,30 +3987,10 @@ ext4_ext_handle_unwritten_extents(handle_t *handle, struct inode *inode, trace_ext4_ext_handle_unwritten_extents(inode, map, flags, *allocated, newblock); - /* get_block() before submitting IO, split the extent */ - if (flags & EXT4_GET_BLOCKS_SPLIT_NOMERGE) { - path = ext4_split_convert_extents(handle, inode, map, path, - flags | EXT4_GET_BLOCKS_CONVERT, allocated); - if (IS_ERR(path)) - return path; - /* - * shouldn't get a 0 allocated when splitting an extent unless - * m_len is 0 (bug) or extent has been corrupted - */ - if (unlikely(*allocated == 0)) { - EXT4_ERROR_INODE(inode, - "unexpected allocated == 0, m_len = %u", - map->m_len); - err = -EFSCORRUPTED; - goto errout; - } - map->m_flags |= EXT4_MAP_UNWRITTEN; - goto out; - } /* IO end_io complete, convert the filled extent to written */ if (flags & EXT4_GET_BLOCKS_CONVERT) { path = ext4_convert_unwritten_extents_endio(handle, inode, - map, path); + map, path, flags); if (IS_ERR(path)) return path; ext4_update_inode_fsync_trans(handle, inode, 1); @@ -3983,7 +4040,6 @@ ext4_ext_handle_unwritten_extents(handle_t *handle, struct inode *inode, goto errout; } -out: map->m_flags |= EXT4_MAP_NEW; map_out: map->m_flags |= EXT4_MAP_MAPPED; @@ -4160,8 +4216,7 @@ again: insert_hole: /* Put just found gap into cache to speed up subsequent requests */ ext_debug(inode, " -> %u:%u\n", hole_start, len); - ext4_es_insert_extent(inode, hole_start, len, ~0, - EXTENT_STATUS_HOLE, false); + ext4_es_cache_extent(inode, hole_start, len, ~0, EXTENT_STATUS_HOLE); /* Update hole_len to reflect hole size after lblk */ if (hole_start != lblk) @@ -4257,7 +4312,7 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode, if ((!ext4_ext_is_unwritten(ex)) && (flags & EXT4_GET_BLOCKS_CONVERT_UNWRITTEN)) { path = convert_initialized_extent(handle, - inode, map, path, &allocated); + inode, map, path, flags, &allocated); if (IS_ERR(path)) err = PTR_ERR(path); goto out; @@ -5375,7 +5430,8 @@ again: if (!extent) { EXT4_ERROR_INODE(inode, "unexpected hole at %lu", (unsigned long) *iterator); - return -EFSCORRUPTED; + ret = -EFSCORRUPTED; + goto out; } if (SHIFT == SHIFT_LEFT && *iterator > le32_to_cpu(extent->ee_block)) { @@ -5541,7 +5597,7 @@ static int ext4_insert_range(struct file *file, loff_t offset, loff_t len) struct ext4_extent *extent; ext4_lblk_t start_lblk, len_lblk, ee_start_lblk = 0; unsigned int credits, ee_len; - int ret, depth, split_flag = 0; + int ret, depth; loff_t start; trace_ext4_insert_range(inode, offset, len); @@ -5612,12 +5668,8 @@ static int ext4_insert_range(struct file *file, loff_t offset, loff_t len) */ if ((start_lblk > ee_start_lblk) && (start_lblk < (ee_start_lblk + ee_len))) { - if (ext4_ext_is_unwritten(extent)) - split_flag = EXT4_EXT_MARK_UNWRIT1 | - EXT4_EXT_MARK_UNWRIT2; path = ext4_split_extent_at(handle, inode, path, - start_lblk, split_flag, - EXT4_EX_NOCACHE | + start_lblk, EXT4_EX_NOCACHE | EXT4_GET_BLOCKS_SPLIT_NOMERGE | EXT4_GET_BLOCKS_METADATA_NOFAIL); } @@ -6187,3 +6239,7 @@ out: ext4_free_ext_path(path); return 0; } + +#ifdef CONFIG_EXT4_KUNIT_TESTS +#include "extents-test.c" +#endif diff --git a/fs/ext4/extents_status.c b/fs/ext4/extents_status.c index e04fbf10fe4f..a1538bac51c6 100644 --- a/fs/ext4/extents_status.c +++ b/fs/ext4/extents_status.c @@ -16,6 +16,7 @@ #include "ext4.h" #include <trace/events/ext4.h> +#include <kunit/static_stub.h> /* * According to previous discussion in Ext4 Developer Workshop, we @@ -178,7 +179,8 @@ static struct kmem_cache *ext4_pending_cachep; static int __es_insert_extent(struct inode *inode, struct extent_status *newes, struct extent_status *prealloc); static int __es_remove_extent(struct inode *inode, ext4_lblk_t lblk, - ext4_lblk_t end, int *reserved, + ext4_lblk_t end, unsigned int status, + int *reserved, struct extent_status *res, struct extent_status *prealloc); static int es_reclaim_extents(struct ext4_inode_info *ei, int *nr_to_scan); static int __es_shrink(struct ext4_sb_info *sbi, int nr_to_scan, @@ -242,6 +244,21 @@ static inline void ext4_es_inc_seq(struct inode *inode) WRITE_ONCE(ei->i_es_seq, ei->i_es_seq + 1); } +static inline int __es_check_extent_status(struct extent_status *es, + unsigned int status, + struct extent_status *res) +{ + if (ext4_es_type(es) & status) + return 0; + + if (res) { + res->es_lblk = es->es_lblk; + res->es_len = es->es_len; + res->es_pblk = es->es_pblk; + } + return -EINVAL; +} + /* * search through the tree for an delayed extent with a given offset. If * it can't be found, try to find next extent. @@ -882,7 +899,8 @@ out: /* * ext4_es_insert_extent() adds information to an inode's extent - * status tree. + * status tree. This interface is used for modifying extents. To cache + * on-disk extents, use ext4_es_cache_extent() instead. */ void ext4_es_insert_extent(struct inode *inode, ext4_lblk_t lblk, ext4_lblk_t len, ext4_fsblk_t pblk, @@ -929,7 +947,7 @@ retry: pr = __alloc_pending(true); write_lock(&EXT4_I(inode)->i_es_lock); - err1 = __es_remove_extent(inode, lblk, end, &resv_used, es1); + err1 = __es_remove_extent(inode, lblk, end, 0, &resv_used, NULL, es1); if (err1 != 0) goto error; /* Free preallocated extent if it didn't get used. */ @@ -961,10 +979,6 @@ retry: } pending = err3; } - /* - * TODO: For cache on-disk extents, there is no need to increment - * the sequence counter, this requires future optimization. - */ ext4_es_inc_seq(inode); error: write_unlock(&EXT4_I(inode)->i_es_lock); @@ -998,17 +1012,24 @@ error: } /* - * ext4_es_cache_extent() inserts information into the extent status - * tree if and only if there isn't information about the range in - * question already. + * ext4_es_cache_extent() inserts information into the extent status tree + * only if there is no existing information about the specified range or + * if the existing extents have the same status. + * + * Note that this interface is only used for caching on-disk extent + * information and cannot be used to convert existing extents in the extent + * status tree. To convert existing extents, use ext4_es_insert_extent() + * instead. */ void ext4_es_cache_extent(struct inode *inode, ext4_lblk_t lblk, ext4_lblk_t len, ext4_fsblk_t pblk, unsigned int status) { struct extent_status *es; - struct extent_status newes; + struct extent_status chkes, newes; ext4_lblk_t end = lblk + len - 1; + bool conflict = false; + int err; if (EXT4_SB(inode->i_sb)->s_mount_state & EXT4_FC_REPLAY) return; @@ -1016,7 +1037,6 @@ void ext4_es_cache_extent(struct inode *inode, ext4_lblk_t lblk, newes.es_lblk = lblk; newes.es_len = len; ext4_es_store_pblock_status(&newes, pblk, status); - trace_ext4_es_cache_extent(inode, &newes); if (!len) return; @@ -1024,11 +1044,42 @@ void ext4_es_cache_extent(struct inode *inode, ext4_lblk_t lblk, BUG_ON(end < lblk); write_lock(&EXT4_I(inode)->i_es_lock); - es = __es_tree_search(&EXT4_I(inode)->i_es_tree.root, lblk); - if (!es || es->es_lblk > end) - __es_insert_extent(inode, &newes, NULL); + if (es && es->es_lblk <= end) { + /* Found an extent that covers the entire range. */ + if (es->es_lblk <= lblk && es->es_lblk + es->es_len > end) { + if (__es_check_extent_status(es, status, &chkes)) + conflict = true; + goto unlock; + } + /* Check and remove all extents in range. */ + err = __es_remove_extent(inode, lblk, end, status, NULL, + &chkes, NULL); + if (err) { + if (err == -EINVAL) + conflict = true; + goto unlock; + } + } + __es_insert_extent(inode, &newes, NULL); + trace_ext4_es_cache_extent(inode, &newes); + ext4_es_print_tree(inode); +unlock: write_unlock(&EXT4_I(inode)->i_es_lock); + if (!conflict) + return; + /* + * A hole in the on-disk extent but a delayed extent in the extent + * status tree, is allowed. + */ + if (status == EXTENT_STATUS_HOLE && + ext4_es_type(&chkes) == EXTENT_STATUS_DELAYED) + return; + + ext4_warning_inode(inode, + "ES cache extent failed: add [%d,%d,%llu,0x%x] conflict with existing [%d,%d,%llu,0x%x]\n", + lblk, len, pblk, status, chkes.es_lblk, chkes.es_len, + ext4_es_pblock(&chkes), ext4_es_status(&chkes)); } /* @@ -1409,23 +1460,27 @@ static unsigned int get_rsvd(struct inode *inode, ext4_lblk_t end, return rc->ndelayed; } - /* * __es_remove_extent - removes block range from extent status tree * * @inode - file containing range * @lblk - first block in range * @end - last block in range + * @status - the extent status to be checked * @reserved - number of cluster reservations released + * @res - return the extent if the status is not match * @prealloc - pre-allocated es to avoid memory allocation failures * * If @reserved is not NULL and delayed allocation is enabled, counts * block/cluster reservations freed by removing range and if bigalloc - * enabled cancels pending reservations as needed. Returns 0 on success, - * error code on failure. + * enabled cancels pending reservations as needed. If @status is not + * zero, check extent status type while removing extent, return -EINVAL + * and pass out the extent through @res if not match. Returns 0 on + * success, error code on failure. */ static int __es_remove_extent(struct inode *inode, ext4_lblk_t lblk, - ext4_lblk_t end, int *reserved, + ext4_lblk_t end, unsigned int status, + int *reserved, struct extent_status *res, struct extent_status *prealloc) { struct ext4_es_tree *tree = &EXT4_I(inode)->i_es_tree; @@ -1434,18 +1489,24 @@ static int __es_remove_extent(struct inode *inode, ext4_lblk_t lblk, struct extent_status orig_es; ext4_lblk_t len1, len2; ext4_fsblk_t block; - int err = 0; + int err; bool count_reserved = true; struct rsvd_count rc; if (reserved == NULL || !test_opt(inode->i_sb, DELALLOC)) count_reserved = false; + if (status == 0) + status = ES_TYPE_MASK; es = __es_tree_search(&tree->root, lblk); if (!es) - goto out; + return 0; if (es->es_lblk > end) - goto out; + return 0; + + err = __es_check_extent_status(es, status, res); + if (err) + return err; /* Simply invalidate cache_es. */ tree->cache_es = NULL; @@ -1480,7 +1541,7 @@ static int __es_remove_extent(struct inode *inode, ext4_lblk_t lblk, es->es_lblk = orig_es.es_lblk; es->es_len = orig_es.es_len; - goto out; + return err; } } else { es->es_lblk = end + 1; @@ -1494,7 +1555,7 @@ static int __es_remove_extent(struct inode *inode, ext4_lblk_t lblk, if (count_reserved) count_rsvd(inode, orig_es.es_lblk + len1, orig_es.es_len - len1 - len2, &orig_es, &rc); - goto out_get_reserved; + goto out; } if (len1 > 0) { @@ -1509,6 +1570,9 @@ static int __es_remove_extent(struct inode *inode, ext4_lblk_t lblk, } while (es && ext4_es_end(es) <= end) { + err = __es_check_extent_status(es, status, res); + if (err) + return err; if (count_reserved) count_rsvd(inode, es->es_lblk, es->es_len, es, &rc); node = rb_next(&es->rb_node); @@ -1524,6 +1588,10 @@ static int __es_remove_extent(struct inode *inode, ext4_lblk_t lblk, if (es && es->es_lblk < end + 1) { ext4_lblk_t orig_len = es->es_len; + err = __es_check_extent_status(es, status, res); + if (err) + return err; + len1 = ext4_es_end(es) - end; if (count_reserved) count_rsvd(inode, es->es_lblk, orig_len - len1, @@ -1536,11 +1604,10 @@ static int __es_remove_extent(struct inode *inode, ext4_lblk_t lblk, } } -out_get_reserved: +out: if (count_reserved) *reserved = get_rsvd(inode, end, es, &rc); -out: - return err; + return 0; } /* @@ -1582,7 +1649,7 @@ retry: * is reclaimed. */ write_lock(&EXT4_I(inode)->i_es_lock); - err = __es_remove_extent(inode, lblk, end, &reserved, es); + err = __es_remove_extent(inode, lblk, end, 0, &reserved, NULL, es); if (err) goto error; /* Free preallocated extent if it didn't get used. */ @@ -2174,7 +2241,7 @@ retry: } write_lock(&EXT4_I(inode)->i_es_lock); - err1 = __es_remove_extent(inode, lblk, end, NULL, es1); + err1 = __es_remove_extent(inode, lblk, end, 0, NULL, NULL, es1); if (err1 != 0) goto error; /* Free preallocated extent if it didn't get used. */ diff --git a/fs/ext4/fast_commit.c b/fs/ext4/fast_commit.c index fa66b08de999..f575751f1cae 100644 --- a/fs/ext4/fast_commit.c +++ b/fs/ext4/fast_commit.c @@ -231,16 +231,16 @@ static bool ext4_fc_disabled(struct super_block *sb) void ext4_fc_del(struct inode *inode) { struct ext4_inode_info *ei = EXT4_I(inode); - struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); struct ext4_fc_dentry_update *fc_dentry; wait_queue_head_t *wq; + int alloc_ctx; if (ext4_fc_disabled(inode->i_sb)) return; - mutex_lock(&sbi->s_fc_lock); + alloc_ctx = ext4_fc_lock(inode->i_sb); if (list_empty(&ei->i_fc_list) && list_empty(&ei->i_fc_dilist)) { - mutex_unlock(&sbi->s_fc_lock); + ext4_fc_unlock(inode->i_sb, alloc_ctx); return; } @@ -275,9 +275,9 @@ void ext4_fc_del(struct inode *inode) #endif prepare_to_wait(wq, &wait.wq_entry, TASK_UNINTERRUPTIBLE); if (ext4_test_inode_state(inode, EXT4_STATE_FC_FLUSHING_DATA)) { - mutex_unlock(&sbi->s_fc_lock); + ext4_fc_unlock(inode->i_sb, alloc_ctx); schedule(); - mutex_lock(&sbi->s_fc_lock); + alloc_ctx = ext4_fc_lock(inode->i_sb); } finish_wait(wq, &wait.wq_entry); } @@ -288,7 +288,7 @@ void ext4_fc_del(struct inode *inode) * dentry create references, since it is not needed to log it anyways. */ if (list_empty(&ei->i_fc_dilist)) { - mutex_unlock(&sbi->s_fc_lock); + ext4_fc_unlock(inode->i_sb, alloc_ctx); return; } @@ -298,7 +298,7 @@ void ext4_fc_del(struct inode *inode) list_del_init(&fc_dentry->fcd_dilist); WARN_ON(!list_empty(&ei->i_fc_dilist)); - mutex_unlock(&sbi->s_fc_lock); + ext4_fc_unlock(inode->i_sb, alloc_ctx); release_dentry_name_snapshot(&fc_dentry->fcd_name); kmem_cache_free(ext4_fc_dentry_cachep, fc_dentry); @@ -315,6 +315,7 @@ void ext4_fc_mark_ineligible(struct super_block *sb, int reason, handle_t *handl tid_t tid; bool has_transaction = true; bool is_ineligible; + int alloc_ctx; if (ext4_fc_disabled(sb)) return; @@ -329,12 +330,12 @@ void ext4_fc_mark_ineligible(struct super_block *sb, int reason, handle_t *handl has_transaction = false; read_unlock(&sbi->s_journal->j_state_lock); } - mutex_lock(&sbi->s_fc_lock); + alloc_ctx = ext4_fc_lock(sb); is_ineligible = ext4_test_mount_flag(sb, EXT4_MF_FC_INELIGIBLE); if (has_transaction && (!is_ineligible || tid_gt(tid, sbi->s_fc_ineligible_tid))) sbi->s_fc_ineligible_tid = tid; ext4_set_mount_flag(sb, EXT4_MF_FC_INELIGIBLE); - mutex_unlock(&sbi->s_fc_lock); + ext4_fc_unlock(sb, alloc_ctx); WARN_ON(reason >= EXT4_FC_REASON_MAX); sbi->s_fc_stats.fc_ineligible_reason_count[reason]++; } @@ -358,6 +359,7 @@ static int ext4_fc_track_template( struct ext4_inode_info *ei = EXT4_I(inode); struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); tid_t tid = 0; + int alloc_ctx; int ret; tid = handle->h_transaction->t_tid; @@ -373,14 +375,14 @@ static int ext4_fc_track_template( if (!enqueue) return ret; - mutex_lock(&sbi->s_fc_lock); + alloc_ctx = ext4_fc_lock(inode->i_sb); if (list_empty(&EXT4_I(inode)->i_fc_list)) list_add_tail(&EXT4_I(inode)->i_fc_list, (sbi->s_journal->j_flags & JBD2_FULL_COMMIT_ONGOING || sbi->s_journal->j_flags & JBD2_FAST_COMMIT_ONGOING) ? &sbi->s_fc_q[FC_Q_STAGING] : &sbi->s_fc_q[FC_Q_MAIN]); - mutex_unlock(&sbi->s_fc_lock); + ext4_fc_unlock(inode->i_sb, alloc_ctx); return ret; } @@ -402,6 +404,7 @@ static int __track_dentry_update(handle_t *handle, struct inode *inode, struct inode *dir = dentry->d_parent->d_inode; struct super_block *sb = inode->i_sb; struct ext4_sb_info *sbi = EXT4_SB(sb); + int alloc_ctx; spin_unlock(&ei->i_fc_lock); @@ -425,7 +428,7 @@ static int __track_dentry_update(handle_t *handle, struct inode *inode, take_dentry_name_snapshot(&node->fcd_name, dentry); INIT_LIST_HEAD(&node->fcd_dilist); INIT_LIST_HEAD(&node->fcd_list); - mutex_lock(&sbi->s_fc_lock); + alloc_ctx = ext4_fc_lock(sb); if (sbi->s_journal->j_flags & JBD2_FULL_COMMIT_ONGOING || sbi->s_journal->j_flags & JBD2_FAST_COMMIT_ONGOING) list_add_tail(&node->fcd_list, @@ -446,7 +449,7 @@ static int __track_dentry_update(handle_t *handle, struct inode *inode, WARN_ON(!list_empty(&ei->i_fc_dilist)); list_add_tail(&node->fcd_dilist, &ei->i_fc_dilist); } - mutex_unlock(&sbi->s_fc_lock); + ext4_fc_unlock(sb, alloc_ctx); spin_lock(&ei->i_fc_lock); return 0; @@ -1046,18 +1049,19 @@ static int ext4_fc_perform_commit(journal_t *journal) struct blk_plug plug; int ret = 0; u32 crc = 0; + int alloc_ctx; /* * Step 1: Mark all inodes on s_fc_q[MAIN] with * EXT4_STATE_FC_FLUSHING_DATA. This prevents these inodes from being * freed until the data flush is over. */ - mutex_lock(&sbi->s_fc_lock); + alloc_ctx = ext4_fc_lock(sb); list_for_each_entry(iter, &sbi->s_fc_q[FC_Q_MAIN], i_fc_list) { ext4_set_inode_state(&iter->vfs_inode, EXT4_STATE_FC_FLUSHING_DATA); } - mutex_unlock(&sbi->s_fc_lock); + ext4_fc_unlock(sb, alloc_ctx); /* Step 2: Flush data for all the eligible inodes. */ ret = ext4_fc_flush_data(journal); @@ -1067,7 +1071,7 @@ static int ext4_fc_perform_commit(journal_t *journal) * any error from step 2. This ensures that waiters waiting on * EXT4_STATE_FC_FLUSHING_DATA can resume. */ - mutex_lock(&sbi->s_fc_lock); + alloc_ctx = ext4_fc_lock(sb); list_for_each_entry(iter, &sbi->s_fc_q[FC_Q_MAIN], i_fc_list) { ext4_clear_inode_state(&iter->vfs_inode, EXT4_STATE_FC_FLUSHING_DATA); @@ -1084,7 +1088,7 @@ static int ext4_fc_perform_commit(journal_t *journal) * prepare_to_wait() in ext4_fc_del(). */ smp_mb(); - mutex_unlock(&sbi->s_fc_lock); + ext4_fc_unlock(sb, alloc_ctx); /* * If we encountered error in Step 2, return it now after clearing @@ -1101,12 +1105,12 @@ static int ext4_fc_perform_commit(journal_t *journal) * previous handles are now drained. We now mark the inodes on the * commit queue as being committed. */ - mutex_lock(&sbi->s_fc_lock); + alloc_ctx = ext4_fc_lock(sb); list_for_each_entry(iter, &sbi->s_fc_q[FC_Q_MAIN], i_fc_list) { ext4_set_inode_state(&iter->vfs_inode, EXT4_STATE_FC_COMMITTING); } - mutex_unlock(&sbi->s_fc_lock); + ext4_fc_unlock(sb, alloc_ctx); jbd2_journal_unlock_updates(journal); /* @@ -1117,6 +1121,7 @@ static int ext4_fc_perform_commit(journal_t *journal) blkdev_issue_flush(journal->j_fs_dev); blk_start_plug(&plug); + alloc_ctx = ext4_fc_lock(sb); /* Step 6: Write fast commit blocks to disk. */ if (sbi->s_fc_bytes == 0) { /* @@ -1134,7 +1139,6 @@ static int ext4_fc_perform_commit(journal_t *journal) } /* Step 6.2: Now write all the dentry updates. */ - mutex_lock(&sbi->s_fc_lock); ret = ext4_fc_commit_dentry_updates(journal, &crc); if (ret) goto out; @@ -1156,7 +1160,7 @@ static int ext4_fc_perform_commit(journal_t *journal) ret = ext4_fc_write_tail(sb, crc); out: - mutex_unlock(&sbi->s_fc_lock); + ext4_fc_unlock(sb, alloc_ctx); blk_finish_plug(&plug); return ret; } @@ -1290,6 +1294,7 @@ static void ext4_fc_cleanup(journal_t *journal, int full, tid_t tid) struct ext4_sb_info *sbi = EXT4_SB(sb); struct ext4_inode_info *ei; struct ext4_fc_dentry_update *fc_dentry; + int alloc_ctx; if (full && sbi->s_fc_bh) sbi->s_fc_bh = NULL; @@ -1297,7 +1302,7 @@ static void ext4_fc_cleanup(journal_t *journal, int full, tid_t tid) trace_ext4_fc_cleanup(journal, full, tid); jbd2_fc_release_bufs(journal); - mutex_lock(&sbi->s_fc_lock); + alloc_ctx = ext4_fc_lock(sb); while (!list_empty(&sbi->s_fc_q[FC_Q_MAIN])) { ei = list_first_entry(&sbi->s_fc_q[FC_Q_MAIN], struct ext4_inode_info, @@ -1356,7 +1361,7 @@ static void ext4_fc_cleanup(journal_t *journal, int full, tid_t tid) if (full) sbi->s_fc_bytes = 0; - mutex_unlock(&sbi->s_fc_lock); + ext4_fc_unlock(sb, alloc_ctx); trace_ext4_fc_stats(sb); } @@ -2302,6 +2307,9 @@ static const char * const fc_ineligible_reasons[] = { [EXT4_FC_REASON_FALLOC_RANGE] = "Falloc range op", [EXT4_FC_REASON_INODE_JOURNAL_DATA] = "Data journalling", [EXT4_FC_REASON_ENCRYPTED_FILENAME] = "Encrypted filename", + [EXT4_FC_REASON_MIGRATE] = "Inode format migration", + [EXT4_FC_REASON_VERITY] = "fs-verity enable", + [EXT4_FC_REASON_MOVE_EXT] = "Move extents", }; int ext4_fc_info_show(struct seq_file *seq, void *v) diff --git a/fs/ext4/fast_commit.h b/fs/ext4/fast_commit.h index 3bd534e4dbbf..2f77a37fb101 100644 --- a/fs/ext4/fast_commit.h +++ b/fs/ext4/fast_commit.h @@ -97,6 +97,9 @@ enum { EXT4_FC_REASON_FALLOC_RANGE, EXT4_FC_REASON_INODE_JOURNAL_DATA, EXT4_FC_REASON_ENCRYPTED_FILENAME, + EXT4_FC_REASON_MIGRATE, + EXT4_FC_REASON_VERITY, + EXT4_FC_REASON_MOVE_EXT, EXT4_FC_REASON_MAX }; diff --git a/fs/ext4/file.c b/fs/ext4/file.c index 7a8b30932189..4320ebff74f3 100644 --- a/fs/ext4/file.c +++ b/fs/ext4/file.c @@ -25,6 +25,7 @@ #include <linux/mount.h> #include <linux/path.h> #include <linux/dax.h> +#include <linux/filelock.h> #include <linux/quotaops.h> #include <linux/pagevec.h> #include <linux/uio.h> @@ -418,22 +419,20 @@ static const struct iomap_dio_ops ext4_dio_write_ops = { * updating inode i_disksize and/or orphan handling with exclusive lock. * * - shared locking will only be true mostly with overwrites, including - * initialized blocks and unwritten blocks. For overwrite unwritten blocks - * we protect splitting extents by i_data_sem in ext4_inode_info, so we can - * also release exclusive i_rwsem lock. + * initialized blocks and unwritten blocks. * * - Otherwise we will switch to exclusive i_rwsem lock. */ static ssize_t ext4_dio_write_checks(struct kiocb *iocb, struct iov_iter *from, bool *ilock_shared, bool *extend, - bool *unwritten, int *dio_flags) + int *dio_flags) { struct file *file = iocb->ki_filp; struct inode *inode = file_inode(file); loff_t offset; size_t count; ssize_t ret; - bool overwrite, unaligned_io; + bool overwrite, unaligned_io, unwritten; restart: ret = ext4_generic_write_checks(iocb, from); @@ -445,7 +444,7 @@ restart: unaligned_io = ext4_unaligned_io(inode, from, offset); *extend = ext4_extending_io(inode, offset, count); - overwrite = ext4_overwrite_io(inode, offset, count, unwritten); + overwrite = ext4_overwrite_io(inode, offset, count, &unwritten); /* * Determine whether we need to upgrade to an exclusive lock. This is @@ -460,7 +459,7 @@ restart: */ if (*ilock_shared && ((!IS_NOSEC(inode) || *extend || !overwrite || - (unaligned_io && *unwritten)))) { + (unaligned_io && unwritten)))) { if (iocb->ki_flags & IOCB_NOWAIT) { ret = -EAGAIN; goto out; @@ -483,7 +482,7 @@ restart: ret = -EAGAIN; goto out; } - if (unaligned_io && (!overwrite || *unwritten)) + if (unaligned_io && (!overwrite || unwritten)) inode_dio_wait(inode); *dio_flags = IOMAP_DIO_FORCE_WAIT; } @@ -508,8 +507,7 @@ static ssize_t ext4_dio_write_iter(struct kiocb *iocb, struct iov_iter *from) struct inode *inode = file_inode(iocb->ki_filp); loff_t offset = iocb->ki_pos; size_t count = iov_iter_count(from); - const struct iomap_ops *iomap_ops = &ext4_iomap_ops; - bool extend = false, unwritten = false; + bool extend = false; bool ilock_shared = true; int dio_flags = 0; @@ -555,7 +553,7 @@ static ssize_t ext4_dio_write_iter(struct kiocb *iocb, struct iov_iter *from) ext4_clear_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA); ret = ext4_dio_write_checks(iocb, from, &ilock_shared, &extend, - &unwritten, &dio_flags); + &dio_flags); if (ret <= 0) return ret; @@ -575,9 +573,7 @@ static ssize_t ext4_dio_write_iter(struct kiocb *iocb, struct iov_iter *from) goto out; } - if (ilock_shared && !unwritten) - iomap_ops = &ext4_iomap_overwrite_ops; - ret = iomap_dio_rw(iocb, from, iomap_ops, &ext4_dio_write_ops, + ret = iomap_dio_rw(iocb, from, &ext4_iomap_ops, &ext4_dio_write_ops, dio_flags, NULL, 0); if (ret == -ENOTBLK) ret = 0; @@ -858,7 +854,6 @@ static int ext4_sample_last_mounted(struct super_block *sb, * when trying to sort through large numbers of block * devices or filesystem images. */ - memset(buf, 0, sizeof(buf)); path.mnt = mnt; path.dentry = mnt->mnt_root; cp = d_path(&path, buf, sizeof(buf)); @@ -980,6 +975,7 @@ const struct file_operations ext4_file_operations = { .fop_flags = FOP_MMAP_SYNC | FOP_BUFFER_RASYNC | FOP_DIO_PARALLEL_WRITE | FOP_DONTCACHE, + .setlease = generic_setlease, }; const struct inode_operations ext4_file_inode_operations = { diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 0c466ccbed69..396dc3a5d16b 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -48,6 +48,8 @@ #include "acl.h" #include "truncate.h" +#include <kunit/static_stub.h> + #include <trace/events/ext4.h> static void ext4_journalled_zero_new_buffers(handle_t *handle, @@ -400,6 +402,8 @@ int ext4_issue_zeroout(struct inode *inode, ext4_lblk_t lblk, ext4_fsblk_t pblk, { int ret; + KUNIT_STATIC_STUB_REDIRECT(ext4_issue_zeroout, inode, lblk, pblk, len); + if (IS_ENCRYPTED(inode) && S_ISREG(inode->i_mode)) return fscrypt_zeroout_range(inode, lblk, pblk, len); @@ -503,8 +507,8 @@ static int ext4_map_query_blocks_next_in_leaf(handle_t *handle, retval = ext4_ext_map_blocks(handle, inode, &map2, 0); if (retval <= 0) { - ext4_es_insert_extent(inode, map->m_lblk, map->m_len, - map->m_pblk, status, false); + ext4_es_cache_extent(inode, map->m_lblk, map->m_len, + map->m_pblk, status); return map->m_len; } @@ -525,20 +529,20 @@ static int ext4_map_query_blocks_next_in_leaf(handle_t *handle, */ if (map->m_pblk + map->m_len == map2.m_pblk && status == status2) { - ext4_es_insert_extent(inode, map->m_lblk, - map->m_len + map2.m_len, map->m_pblk, - status, false); + ext4_es_cache_extent(inode, map->m_lblk, + map->m_len + map2.m_len, map->m_pblk, + status); map->m_len += map2.m_len; } else { - ext4_es_insert_extent(inode, map->m_lblk, map->m_len, - map->m_pblk, status, false); + ext4_es_cache_extent(inode, map->m_lblk, map->m_len, + map->m_pblk, status); } return map->m_len; } -static int ext4_map_query_blocks(handle_t *handle, struct inode *inode, - struct ext4_map_blocks *map, int flags) +int ext4_map_query_blocks(handle_t *handle, struct inode *inode, + struct ext4_map_blocks *map, int flags) { unsigned int status; int retval; @@ -573,8 +577,8 @@ static int ext4_map_query_blocks(handle_t *handle, struct inode *inode, map->m_len == orig_mlen) { status = map->m_flags & EXT4_MAP_UNWRITTEN ? EXTENT_STATUS_UNWRITTEN : EXTENT_STATUS_WRITTEN; - ext4_es_insert_extent(inode, map->m_lblk, map->m_len, - map->m_pblk, status, false); + ext4_es_cache_extent(inode, map->m_lblk, map->m_len, + map->m_pblk, status); } else { retval = ext4_map_query_blocks_next_in_leaf(handle, inode, map, orig_mlen); @@ -584,10 +588,9 @@ out: return retval; } -static int ext4_map_create_blocks(handle_t *handle, struct inode *inode, - struct ext4_map_blocks *map, int flags) +int ext4_map_create_blocks(handle_t *handle, struct inode *inode, + struct ext4_map_blocks *map, int flags) { - struct extent_status es; unsigned int status; int err, retval = 0; @@ -648,16 +651,6 @@ static int ext4_map_create_blocks(handle_t *handle, struct inode *inode, return err; } - /* - * If the extent has been zeroed out, we don't need to update - * extent status tree. - */ - if (flags & EXT4_GET_BLOCKS_SPLIT_NOMERGE && - ext4_es_lookup_extent(inode, map->m_lblk, NULL, &es, &map->m_seq)) { - if (ext4_es_is_written(&es)) - return retval; - } - status = map->m_flags & EXT4_MAP_UNWRITTEN ? EXTENT_STATUS_UNWRITTEN : EXTENT_STATUS_WRITTEN; ext4_es_insert_extent(inode, map->m_lblk, map->m_len, map->m_pblk, @@ -2375,7 +2368,7 @@ static int mpage_map_one_extent(handle_t *handle, struct mpage_da_data *mpd) dioread_nolock = ext4_should_dioread_nolock(inode); if (dioread_nolock) - get_blocks_flags |= EXT4_GET_BLOCKS_IO_CREATE_EXT; + get_blocks_flags |= EXT4_GET_BLOCKS_UNWRIT_EXT; err = ext4_map_blocks(handle, inode, map, get_blocks_flags); if (err < 0) @@ -3305,8 +3298,7 @@ int ext4_alloc_da_blocks(struct inode *inode) /* * We do something simple for now. The filemap_flush() will * also start triggering a write of the data blocks, which is - * not strictly speaking necessary (and for users of - * laptop_mode, not even desirable). However, to do otherwise + * not strictly speaking necessary. However, to do otherwise * would require replicating code paths in: * * ext4_writepages() -> @@ -3380,33 +3372,6 @@ out: return ret; } -static int ext4_read_folio(struct file *file, struct folio *folio) -{ - int ret = -EAGAIN; - struct inode *inode = folio->mapping->host; - - trace_ext4_read_folio(inode, folio); - - if (ext4_has_inline_data(inode)) - ret = ext4_readpage_inline(inode, folio); - - if (ret == -EAGAIN) - return ext4_mpage_readpages(inode, NULL, folio); - - return ret; -} - -static void ext4_readahead(struct readahead_control *rac) -{ - struct inode *inode = rac->mapping->host; - - /* If the file has inline data, no need to do readahead. */ - if (ext4_has_inline_data(inode)) - return; - - ext4_mpage_readpages(inode, rac, NULL); -} - static void ext4_invalidate_folio(struct folio *folio, size_t offset, size_t length) { @@ -3740,7 +3705,7 @@ retry: else if (EXT4_LBLK_TO_B(inode, map->m_lblk) >= i_size_read(inode)) m_flags = EXT4_GET_BLOCKS_CREATE; else if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)) - m_flags = EXT4_GET_BLOCKS_IO_CREATE_EXT; + m_flags = EXT4_GET_BLOCKS_CREATE_UNWRIT_EXT; if (flags & IOMAP_ATOMIC) ret = ext4_map_blocks_atomic_write(handle, inode, map, m_flags, @@ -3812,22 +3777,25 @@ static int ext4_iomap_begin(struct inode *inode, loff_t offset, loff_t length, if (offset + length <= i_size_read(inode)) { ret = ext4_map_blocks(NULL, inode, &map, 0); /* - * For atomic writes the entire requested length should - * be mapped. + * For DAX we convert extents to initialized ones before + * copying the data, otherwise we do it after I/O so + * there's no need to call into ext4_iomap_alloc(). */ - if (map.m_flags & EXT4_MAP_MAPPED) { - if ((!(flags & IOMAP_ATOMIC) && ret > 0) || - (flags & IOMAP_ATOMIC && ret >= orig_mlen)) + if ((map.m_flags & EXT4_MAP_MAPPED) || + (!(flags & IOMAP_DAX) && + (map.m_flags & EXT4_MAP_UNWRITTEN))) { + /* + * For atomic writes the entire requested + * length should be mapped. + */ + if (ret == orig_mlen || + (!(flags & IOMAP_ATOMIC) && ret > 0)) goto out; } map.m_len = orig_mlen; } ret = ext4_iomap_alloc(inode, &map, flags); } else { - /* - * This can be called for overwrites path from - * ext4_iomap_overwrite_begin(). - */ ret = ext4_map_blocks(NULL, inode, &map, 0); } @@ -3856,30 +3824,10 @@ out: return 0; } -static int ext4_iomap_overwrite_begin(struct inode *inode, loff_t offset, - loff_t length, unsigned flags, struct iomap *iomap, - struct iomap *srcmap) -{ - int ret; - - /* - * Even for writes we don't need to allocate blocks, so just pretend - * we are reading to save overhead of starting a transaction. - */ - flags &= ~IOMAP_WRITE; - ret = ext4_iomap_begin(inode, offset, length, flags, iomap, srcmap); - WARN_ON_ONCE(!ret && iomap->type != IOMAP_MAPPED); - return ret; -} - const struct iomap_ops ext4_iomap_ops = { .iomap_begin = ext4_iomap_begin, }; -const struct iomap_ops ext4_iomap_overwrite_ops = { - .iomap_begin = ext4_iomap_overwrite_begin, -}; - static int ext4_iomap_begin_report(struct inode *inode, loff_t offset, loff_t length, unsigned int flags, struct iomap *iomap, struct iomap *srcmap) @@ -4133,9 +4081,13 @@ static int __ext4_block_zero_page_range(handle_t *handle, if (ext4_should_journal_data(inode)) { err = ext4_dirty_journalled_data(handle, bh); } else { - err = 0; mark_buffer_dirty(bh); - if (ext4_should_order_data(inode)) + /* + * Only the written block requires ordered data to prevent + * exposing stale data. + */ + if (!buffer_unwritten(bh) && !buffer_delay(bh) && + ext4_should_order_data(inode)) err = ext4_jbd2_inode_add_write(handle, inode, from, length); } @@ -5835,10 +5787,6 @@ int ext4_setattr(struct mnt_idmap *idmap, struct dentry *dentry, if (error) return error; - error = fsverity_prepare_setattr(dentry, attr); - if (error) - return error; - if (is_quota_modification(idmap, inode, attr)) { error = dquot_initialize(inode); if (error) diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c index 7ce0fc40aec2..3ae9cb50a0c0 100644 --- a/fs/ext4/ioctl.c +++ b/fs/ext4/ioctl.c @@ -26,6 +26,7 @@ #include <linux/fsmap.h> #include "fsmap.h" #include <trace/events/ext4.h> +#include <linux/fserror.h> typedef void ext4_update_sb_callback(struct ext4_sb_info *sbi, struct ext4_super_block *es, @@ -844,6 +845,7 @@ int ext4_force_shutdown(struct super_block *sb, u32 flags) return -EINVAL; } clear_opt(sb, DISCARD); + fserror_report_shutdown(sb, GFP_KERNEL); return 0; } @@ -966,6 +968,7 @@ static long ext4_ioctl_group_add(struct file *file, err = ext4_group_add(sb, input); if (EXT4_SB(sb)->s_journal) { + ext4_fc_mark_ineligible(sb, EXT4_FC_REASON_RESIZE, NULL); jbd2_journal_lock_updates(EXT4_SB(sb)->s_journal); err2 = jbd2_journal_flush(EXT4_SB(sb)->s_journal, 0); jbd2_journal_unlock_updates(EXT4_SB(sb)->s_journal); @@ -1611,6 +1614,8 @@ setversion_out: err = ext4_group_extend(sb, EXT4_SB(sb)->s_es, n_blocks_count); if (EXT4_SB(sb)->s_journal) { + ext4_fc_mark_ineligible(sb, EXT4_FC_REASON_RESIZE, + NULL); jbd2_journal_lock_updates(EXT4_SB(sb)->s_journal); err2 = jbd2_journal_flush(EXT4_SB(sb)->s_journal, 0); jbd2_journal_unlock_updates(EXT4_SB(sb)->s_journal); diff --git a/fs/ext4/mballoc-test.c b/fs/ext4/mballoc-test.c index a9416b20ff64..4abb40d4561c 100644 --- a/fs/ext4/mballoc-test.c +++ b/fs/ext4/mballoc-test.c @@ -567,7 +567,7 @@ test_mark_diskspace_used_range(struct kunit *test, bitmap = mbt_ctx_bitmap(sb, TEST_GOAL_GROUP); memset(bitmap, 0, sb->s_blocksize); - ret = ext4_mb_mark_diskspace_used(ac, NULL, 0); + ret = ext4_mb_mark_diskspace_used(ac, NULL); KUNIT_ASSERT_EQ(test, ret, 0); max = EXT4_CLUSTERS_PER_GROUP(sb); diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c index 56d50fd3310b..b99d1a7e580e 100644 --- a/fs/ext4/mballoc.c +++ b/fs/ext4/mballoc.c @@ -892,6 +892,21 @@ mb_update_avg_fragment_size(struct super_block *sb, struct ext4_group_info *grp) } } +static ext4_group_t ext4_get_allocation_groups_count( + struct ext4_allocation_context *ac) +{ + ext4_group_t ngroups = ext4_get_groups_count(ac->ac_sb); + + /* non-extent files are limited to low blocks/groups */ + if (!(ext4_test_inode_flag(ac->ac_inode, EXT4_INODE_EXTENTS))) + ngroups = EXT4_SB(ac->ac_sb)->s_blockfile_groups; + + /* Pairs with smp_wmb() in ext4_update_super() */ + smp_rmb(); + + return ngroups; +} + static int ext4_mb_scan_groups_xa_range(struct ext4_allocation_context *ac, struct xarray *xa, ext4_group_t start, ext4_group_t end) @@ -899,7 +914,7 @@ static int ext4_mb_scan_groups_xa_range(struct ext4_allocation_context *ac, struct super_block *sb = ac->ac_sb; struct ext4_sb_info *sbi = EXT4_SB(sb); enum criteria cr = ac->ac_criteria; - ext4_group_t ngroups = ext4_get_groups_count(sb); + ext4_group_t ngroups = ext4_get_allocation_groups_count(ac); unsigned long group = start; struct ext4_group_info *grp; @@ -951,7 +966,7 @@ static int ext4_mb_scan_groups_p2_aligned(struct ext4_allocation_context *ac, ext4_group_t start, end; start = group; - end = ext4_get_groups_count(ac->ac_sb); + end = ext4_get_allocation_groups_count(ac); wrap_around: for (i = ac->ac_2order; i < MB_NUM_ORDERS(ac->ac_sb); i++) { ret = ext4_mb_scan_groups_largest_free_order_range(ac, i, @@ -1001,7 +1016,7 @@ static int ext4_mb_scan_groups_goal_fast(struct ext4_allocation_context *ac, ext4_group_t start, end; start = group; - end = ext4_get_groups_count(ac->ac_sb); + end = ext4_get_allocation_groups_count(ac); wrap_around: i = mb_avg_fragment_size_order(ac->ac_sb, ac->ac_g_ex.fe_len); for (; i < MB_NUM_ORDERS(ac->ac_sb); i++) { @@ -1083,7 +1098,7 @@ static int ext4_mb_scan_groups_best_avail(struct ext4_allocation_context *ac, min_order = fls(ac->ac_o_ex.fe_len); start = group; - end = ext4_get_groups_count(ac->ac_sb); + end = ext4_get_allocation_groups_count(ac); wrap_around: for (i = order; i >= min_order; i--) { int frag_order; @@ -1133,8 +1148,6 @@ static inline int should_optimize_scan(struct ext4_allocation_context *ac) return 0; if (ac->ac_criteria >= CR_GOAL_LEN_SLOW) return 0; - if (!ext4_test_inode_flag(ac->ac_inode, EXT4_INODE_EXTENTS)) - return 0; return 1; } @@ -1182,11 +1195,7 @@ static int ext4_mb_scan_groups(struct ext4_allocation_context *ac) int ret = 0; ext4_group_t start; struct ext4_sb_info *sbi = EXT4_SB(ac->ac_sb); - ext4_group_t ngroups = ext4_get_groups_count(ac->ac_sb); - - /* non-extent files are limited to low blocks/groups */ - if (!(ext4_test_inode_flag(ac->ac_inode, EXT4_INODE_EXTENTS))) - ngroups = sbi->s_blockfile_groups; + ext4_group_t ngroups = ext4_get_allocation_groups_count(ac); /* searching for the right group start from the goal value specified */ start = ac->ac_g_ex.fe_group; @@ -1706,16 +1715,17 @@ ext4_mb_load_buddy_gfp(struct super_block *sb, ext4_group_t group, /* Avoid locking the folio in the fast path ... */ folio = __filemap_get_folio(inode->i_mapping, pnum, FGP_ACCESSED, 0); - if (IS_ERR(folio) || !folio_test_uptodate(folio)) { + if (IS_ERR(folio) || !folio_test_uptodate(folio) || folio_test_locked(folio)) { + /* + * folio_test_locked is employed to detect ongoing folio + * migrations, since concurrent migrations can lead to + * bitmap inconsistency. And if we are not uptodate that + * implies somebody just created the folio but is yet to + * initialize it. We can drop the folio reference and + * try to get the folio with lock in both cases to avoid + * concurrency. + */ if (!IS_ERR(folio)) - /* - * drop the folio reference and try - * to get the folio with lock. If we - * are not uptodate that implies - * somebody just created the folio but - * is yet to initialize it. So - * wait for it to initialize. - */ folio_put(folio); folio = __filemap_get_folio(inode->i_mapping, pnum, FGP_LOCK | FGP_ACCESSED | FGP_CREAT, gfp); @@ -1764,7 +1774,7 @@ ext4_mb_load_buddy_gfp(struct super_block *sb, ext4_group_t group, /* we need another folio for the buddy */ folio = __filemap_get_folio(inode->i_mapping, pnum, FGP_ACCESSED, 0); - if (IS_ERR(folio) || !folio_test_uptodate(folio)) { + if (IS_ERR(folio) || !folio_test_uptodate(folio) || folio_test_locked(folio)) { if (!IS_ERR(folio)) folio_put(folio); folio = __filemap_get_folio(inode->i_mapping, pnum, @@ -4185,8 +4195,7 @@ out_err: * Returns 0 if success or error code */ static noinline_for_stack int -ext4_mb_mark_diskspace_used(struct ext4_allocation_context *ac, - handle_t *handle, unsigned int reserv_clstrs) +ext4_mb_mark_diskspace_used(struct ext4_allocation_context *ac, handle_t *handle) { struct ext4_group_desc *gdp; struct ext4_sb_info *sbi; @@ -4241,13 +4250,6 @@ ext4_mb_mark_diskspace_used(struct ext4_allocation_context *ac, BUG_ON(changed != ac->ac_b_ex.fe_len); #endif percpu_counter_sub(&sbi->s_freeclusters_counter, ac->ac_b_ex.fe_len); - /* - * Now reduce the dirty block count also. Should not go negative - */ - if (!(ac->ac_flags & EXT4_MB_DELALLOC_RESERVED)) - /* release all the reserved blocks if non delalloc */ - percpu_counter_sub(&sbi->s_dirtyclusters_counter, - reserv_clstrs); return err; } @@ -4276,8 +4278,7 @@ void ext4_mb_mark_bb(struct super_block *sb, ext4_fsblk_t block, * get the corresponding group metadata to work with. * For this we have goto again loop. */ - thisgrp_len = min_t(unsigned int, (unsigned int)len, - EXT4_BLOCKS_PER_GROUP(sb) - EXT4_C2B(sbi, blkoff)); + thisgrp_len = min(len, EXT4_BLOCKS_PER_GROUP(sb) - EXT4_C2B(sbi, blkoff)); clen = EXT4_NUM_B2C(sbi, thisgrp_len); if (!ext4_sb_block_valid(sb, NULL, block, thisgrp_len)) { @@ -6332,7 +6333,7 @@ repeat: ext4_mb_pa_put_free(ac); } if (likely(ac->ac_status == AC_STATUS_FOUND)) { - *errp = ext4_mb_mark_diskspace_used(ac, handle, reserv_clstrs); + *errp = ext4_mb_mark_diskspace_used(ac, handle); if (*errp) { ext4_discard_allocated_blocks(ac); goto errout; @@ -6363,12 +6364,9 @@ errout: out: if (inquota && ar->len < inquota) dquot_free_block(ar->inode, EXT4_C2B(sbi, inquota - ar->len)); - if (!ar->len) { - if ((ar->flags & EXT4_MB_DELALLOC_RESERVED) == 0) - /* release all the reserved blocks if non delalloc */ - percpu_counter_sub(&sbi->s_dirtyclusters_counter, - reserv_clstrs); - } + /* release any reserved blocks */ + if (reserv_clstrs) + percpu_counter_sub(&sbi->s_dirtyclusters_counter, reserv_clstrs); trace_ext4_allocate_blocks(ar, (unsigned long long)block); diff --git a/fs/ext4/migrate.c b/fs/ext4/migrate.c index 1b0dfd963d3f..96ab95167bd6 100644 --- a/fs/ext4/migrate.c +++ b/fs/ext4/migrate.c @@ -449,6 +449,12 @@ int ext4_ext_migrate(struct inode *inode) retval = PTR_ERR(handle); goto out_unlock; } + /* + * This operation rewrites the inode's block mapping layout + * (indirect to extents) and is not tracked in the fast commit + * log, so disable fast commits for this transaction. + */ + ext4_fc_mark_ineligible(inode->i_sb, EXT4_FC_REASON_MIGRATE, handle); goal = (((inode->i_ino - 1) / EXT4_INODES_PER_GROUP(inode->i_sb)) * EXT4_INODES_PER_GROUP(inode->i_sb)) + 1; owner[0] = i_uid_read(inode); @@ -630,6 +636,12 @@ int ext4_ind_migrate(struct inode *inode) ret = PTR_ERR(handle); goto out_unlock; } + /* + * This operation rewrites the inode's block mapping layout + * (extents to indirect blocks) and is not tracked in the fast + * commit log, so disable fast commits for this transaction. + */ + ext4_fc_mark_ineligible(inode->i_sb, EXT4_FC_REASON_MIGRATE, handle); down_write(&EXT4_I(inode)->i_data_sem); ret = ext4_ext_check_inode(inode); diff --git a/fs/ext4/move_extent.c b/fs/ext4/move_extent.c index 635fb8a52e0c..ce1f738dff93 100644 --- a/fs/ext4/move_extent.c +++ b/fs/ext4/move_extent.c @@ -321,6 +321,8 @@ static int mext_move_extent(struct mext_data *mext, u64 *m_len) ret = PTR_ERR(handle); goto out; } + ext4_fc_mark_ineligible(orig_inode->i_sb, EXT4_FC_REASON_MOVE_EXT, + handle); ret = mext_move_begin(mext, folio, &move_type); if (ret) diff --git a/fs/ext4/page-io.c b/fs/ext4/page-io.c index 39abfeec5f36..a8c95eee91b7 100644 --- a/fs/ext4/page-io.c +++ b/fs/ext4/page-io.c @@ -7,6 +7,7 @@ * Written by Theodore Ts'o, 2010. */ +#include <linux/blk-crypto.h> #include <linux/fs.h> #include <linux/time.h> #include <linux/highuid.h> @@ -401,7 +402,7 @@ void ext4_io_submit(struct ext4_io_submit *io) if (bio) { if (io->io_wbc->sync_mode == WB_SYNC_ALL) io->io_bio->bi_opf |= REQ_SYNC; - submit_bio(io->io_bio); + blk_crypto_submit_bio(io->io_bio); } io->io_bio = NULL; } diff --git a/fs/ext4/readpage.c b/fs/ext4/readpage.c index e7f2350c725b..830f3b8a321f 100644 --- a/fs/ext4/readpage.c +++ b/fs/ext4/readpage.c @@ -36,6 +36,7 @@ #include <linux/bio.h> #include <linux/fs.h> #include <linux/buffer_head.h> +#include <linux/blk-crypto.h> #include <linux/blkdev.h> #include <linux/highmem.h> #include <linux/prefetch.h> @@ -45,6 +46,7 @@ #include <linux/pagevec.h> #include "ext4.h" +#include <trace/events/ext4.h> #define NUM_PREALLOC_POST_READ_CTXS 128 @@ -61,6 +63,7 @@ enum bio_post_read_step { struct bio_post_read_ctx { struct bio *bio; + struct fsverity_info *vi; struct work_struct work; unsigned int cur_step; unsigned int enabled_steps; @@ -96,6 +99,7 @@ static void verity_work(struct work_struct *work) struct bio_post_read_ctx *ctx = container_of(work, struct bio_post_read_ctx, work); struct bio *bio = ctx->bio; + struct fsverity_info *vi = ctx->vi; /* * fsverity_verify_bio() may call readahead() again, and although verity @@ -108,7 +112,7 @@ static void verity_work(struct work_struct *work) mempool_free(ctx, bio_post_read_ctx_pool); bio->bi_private = NULL; - fsverity_verify_bio(bio); + fsverity_verify_bio(vi, bio); __read_end_io(bio); } @@ -130,7 +134,8 @@ static void bio_post_read_processing(struct bio_post_read_ctx *ctx) ctx->cur_step++; fallthrough; case STEP_VERITY: - if (ctx->enabled_steps & (1 << STEP_VERITY)) { + if (IS_ENABLED(CONFIG_FS_VERITY) && + ctx->enabled_steps & (1 << STEP_VERITY)) { INIT_WORK(&ctx->work, verity_work); fsverity_enqueue_verify_work(&ctx->work); return; @@ -171,22 +176,16 @@ static void mpage_end_io(struct bio *bio) __read_end_io(bio); } -static inline bool ext4_need_verity(const struct inode *inode, pgoff_t idx) -{ - return fsverity_active(inode) && - idx < DIV_ROUND_UP(inode->i_size, PAGE_SIZE); -} - static void ext4_set_bio_post_read_ctx(struct bio *bio, const struct inode *inode, - pgoff_t first_idx) + struct fsverity_info *vi) { unsigned int post_read_steps = 0; if (fscrypt_inode_uses_fs_layer_crypto(inode)) post_read_steps |= 1 << STEP_DECRYPT; - if (ext4_need_verity(inode, first_idx)) + if (vi) post_read_steps |= 1 << STEP_VERITY; if (post_read_steps) { @@ -195,6 +194,7 @@ static void ext4_set_bio_post_read_ctx(struct bio *bio, mempool_alloc(bio_post_read_ctx_pool, GFP_NOFS); ctx->bio = bio; + ctx->vi = vi; ctx->enabled_steps = post_read_steps; bio->bi_private = ctx; } @@ -208,7 +208,7 @@ static inline loff_t ext4_readpage_limit(struct inode *inode) return i_size_read(inode); } -int ext4_mpage_readpages(struct inode *inode, +static int ext4_mpage_readpages(struct inode *inode, struct fsverity_info *vi, struct readahead_control *rac, struct folio *folio) { struct bio *bio = NULL; @@ -328,8 +328,7 @@ int ext4_mpage_readpages(struct inode *inode, folio_zero_segment(folio, first_hole << blkbits, folio_size(folio)); if (first_hole == 0) { - if (ext4_need_verity(inode, folio->index) && - !fsverity_verify_folio(folio)) + if (vi && !fsverity_verify_folio(vi, folio)) goto set_error_page; folio_end_read(folio, true); continue; @@ -345,7 +344,7 @@ int ext4_mpage_readpages(struct inode *inode, if (bio && (last_block_in_bio != first_block - 1 || !fscrypt_mergeable_bio(bio, inode, next_block))) { submit_and_realloc: - submit_bio(bio); + blk_crypto_submit_bio(bio); bio = NULL; } if (bio == NULL) { @@ -357,7 +356,7 @@ int ext4_mpage_readpages(struct inode *inode, REQ_OP_READ, GFP_KERNEL); fscrypt_set_bio_crypt_ctx(bio, inode, next_block, GFP_KERNEL); - ext4_set_bio_post_read_ctx(bio, inode, folio->index); + ext4_set_bio_post_read_ctx(bio, inode, vi); bio->bi_iter.bi_sector = first_block << (blkbits - 9); bio->bi_end_io = mpage_end_io; if (rac) @@ -371,14 +370,14 @@ int ext4_mpage_readpages(struct inode *inode, if (((map.m_flags & EXT4_MAP_BOUNDARY) && (relative_block == map.m_len)) || (first_hole != blocks_per_folio)) { - submit_bio(bio); + blk_crypto_submit_bio(bio); bio = NULL; } else last_block_in_bio = first_block + blocks_per_folio - 1; continue; confused: if (bio) { - submit_bio(bio); + blk_crypto_submit_bio(bio); bio = NULL; } if (!folio_test_uptodate(folio)) @@ -389,10 +388,48 @@ next_page: ; /* A label shall be followed by a statement until C23 */ } if (bio) - submit_bio(bio); + blk_crypto_submit_bio(bio); return 0; } +int ext4_read_folio(struct file *file, struct folio *folio) +{ + struct inode *inode = folio->mapping->host; + struct fsverity_info *vi = NULL; + int ret; + + trace_ext4_read_folio(inode, folio); + + if (ext4_has_inline_data(inode)) { + ret = ext4_readpage_inline(inode, folio); + if (ret != -EAGAIN) + return ret; + } + + if (folio->index < DIV_ROUND_UP(inode->i_size, PAGE_SIZE)) + vi = fsverity_get_info(inode); + if (vi) + fsverity_readahead(vi, folio->index, folio_nr_pages(folio)); + return ext4_mpage_readpages(inode, vi, NULL, folio); +} + +void ext4_readahead(struct readahead_control *rac) +{ + struct inode *inode = rac->mapping->host; + struct fsverity_info *vi = NULL; + + /* If the file has inline data, no need to do readahead. */ + if (ext4_has_inline_data(inode)) + return; + + if (readahead_index(rac) < DIV_ROUND_UP(inode->i_size, PAGE_SIZE)) + vi = fsverity_get_info(inode); + if (vi) + fsverity_readahead(vi, readahead_index(rac), + readahead_count(rac)); + ext4_mpage_readpages(inode, vi, rac, NULL); +} + int __init ext4_init_post_read_processing(void) { bio_post_read_ctx_cache = KMEM_CACHE(bio_post_read_ctx, SLAB_RECLAIM_ACCOUNT); diff --git a/fs/ext4/resize.c b/fs/ext4/resize.c index 050f26168d97..76842f0957b5 100644 --- a/fs/ext4/resize.c +++ b/fs/ext4/resize.c @@ -1479,7 +1479,7 @@ static void ext4_update_super(struct super_block *sb, /* Update the global fs size fields */ sbi->s_groups_count += flex_gd->count; - sbi->s_blockfile_groups = min_t(ext4_group_t, sbi->s_groups_count, + sbi->s_blockfile_groups = min(sbi->s_groups_count, (EXT4_MAX_BLOCK_FILE_PHYS / EXT4_BLOCKS_PER_GROUP(sb))); /* Update the reserved block counts only once the new group is diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 87205660c5d0..504148b2142b 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -48,6 +48,7 @@ #include <linux/fsnotify.h> #include <linux/fs_context.h> #include <linux/fs_parser.h> +#include <linux/fserror.h> #include "ext4.h" #include "ext4_extents.h" /* Needed for trace points definition */ @@ -824,7 +825,8 @@ void __ext4_error(struct super_block *sb, const char *function, sb->s_id, function, line, current->comm, &vaf); va_end(args); } - fsnotify_sb_error(sb, NULL, error ? error : EFSCORRUPTED); + fserror_report_metadata(sb, error ? -abs(error) : -EFSCORRUPTED, + GFP_ATOMIC); ext4_handle_error(sb, force_ro, error, 0, block, function, line); } @@ -856,7 +858,9 @@ void __ext4_error_inode(struct inode *inode, const char *function, current->comm, &vaf); va_end(args); } - fsnotify_sb_error(inode->i_sb, inode, error ? error : EFSCORRUPTED); + fserror_report_file_metadata(inode, + error ? -abs(error) : -EFSCORRUPTED, + GFP_ATOMIC); ext4_handle_error(inode->i_sb, false, error, inode->i_ino, block, function, line); @@ -896,7 +900,7 @@ void __ext4_error_file(struct file *file, const char *function, current->comm, path, &vaf); va_end(args); } - fsnotify_sb_error(inode->i_sb, inode, EFSCORRUPTED); + fserror_report_file_metadata(inode, -EFSCORRUPTED, GFP_ATOMIC); ext4_handle_error(inode->i_sb, false, EFSCORRUPTED, inode->i_ino, block, function, line); @@ -965,7 +969,8 @@ void __ext4_std_error(struct super_block *sb, const char *function, printk(KERN_CRIT "EXT4-fs error (device %s) in %s:%d: %s\n", sb->s_id, function, line, errstr); } - fsnotify_sb_error(sb, NULL, errno ? errno : EFSCORRUPTED); + fserror_report_metadata(sb, errno ? -abs(errno) : -EFSCORRUPTED, + GFP_ATOMIC); ext4_handle_error(sb, false, -errno, 0, 0, function, line); } @@ -1484,19 +1489,23 @@ static void init_once(void *foo) #ifdef CONFIG_FS_ENCRYPTION ei->i_crypt_info = NULL; #endif -#ifdef CONFIG_FS_VERITY - ei->i_verity_info = NULL; -#endif } static int __init init_inodecache(void) { - ext4_inode_cachep = kmem_cache_create_usercopy("ext4_inode_cache", - sizeof(struct ext4_inode_info), 0, - SLAB_RECLAIM_ACCOUNT | SLAB_ACCOUNT, - offsetof(struct ext4_inode_info, i_data), - sizeof_field(struct ext4_inode_info, i_data), - init_once); + struct kmem_cache_args args = { + .useroffset = offsetof(struct ext4_inode_info, i_data), + .usersize = sizeof_field(struct ext4_inode_info, i_data), + .use_freeptr_offset = true, + .freeptr_offset = offsetof(struct ext4_inode_info, i_flags), + .ctor = init_once, + }; + + ext4_inode_cachep = kmem_cache_create("ext4_inode_cache", + sizeof(struct ext4_inode_info), + &args, + SLAB_RECLAIM_ACCOUNT | SLAB_ACCOUNT); + if (ext4_inode_cachep == NULL) return -ENOMEM; return 0; @@ -1527,7 +1536,6 @@ void ext4_clear_inode(struct inode *inode) EXT4_I(inode)->jinode = NULL; } fscrypt_put_encryption_info(inode); - fsverity_cleanup_inode(inode); } static struct inode *ext4_nfs_get_inode(struct super_block *sb, @@ -3638,10 +3646,12 @@ int ext4_feature_set_ok(struct super_block *sb, int readonly) } /* - * This function is called once a day if we have errors logged - * on the file system + * This function is called once a day by default if we have errors logged + * on the file system. + * Use the err_report_sec sysfs attribute to disable or adjust its call + * freequency. */ -static void print_daily_error_info(struct timer_list *t) +void print_daily_error_info(struct timer_list *t) { struct ext4_sb_info *sbi = timer_container_of(sbi, t, s_err_report); struct super_block *sb = sbi->s_sb; @@ -3681,7 +3691,9 @@ static void print_daily_error_info(struct timer_list *t) le64_to_cpu(es->s_last_error_block)); printk(KERN_CONT "\n"); } - mod_timer(&sbi->s_err_report, jiffies + 24*60*60*HZ); /* Once a day */ + + if (sbi->s_err_report_sec) + mod_timer(&sbi->s_err_report, jiffies + secs_to_jiffies(sbi->s_err_report_sec)); } /* Find next suitable group and run ext4_init_inode_table */ @@ -4832,7 +4844,7 @@ static int ext4_check_geometry(struct super_block *sb, return -EINVAL; } sbi->s_groups_count = blocks_count; - sbi->s_blockfile_groups = min_t(ext4_group_t, sbi->s_groups_count, + sbi->s_blockfile_groups = min(sbi->s_groups_count, (EXT4_MAX_BLOCK_FILE_PHYS / EXT4_BLOCKS_PER_GROUP(sb))); if (((u64)sbi->s_groups_count * sbi->s_inodes_per_group) != le32_to_cpu(es->s_inodes_count)) { @@ -5604,6 +5616,10 @@ static int __ext4_fill_super(struct fs_context *fc, struct super_block *sb) clear_opt2(sb, MB_OPTIMIZE_SCAN); } + err = ext4_percpu_param_init(sbi); + if (err) + goto failed_mount5; + err = ext4_mb_init(sb); if (err) { ext4_msg(sb, KERN_ERR, "failed to initialize mballoc (%d)", @@ -5619,10 +5635,6 @@ static int __ext4_fill_super(struct fs_context *fc, struct super_block *sb) sbi->s_journal->j_commit_callback = ext4_journal_commit_callback; - err = ext4_percpu_param_init(sbi); - if (err) - goto failed_mount6; - if (ext4_has_feature_flex_bg(sb)) if (!ext4_fill_flex_info(sb)) { ext4_msg(sb, KERN_ERR, @@ -5678,8 +5690,12 @@ static int __ext4_fill_super(struct fs_context *fc, struct super_block *sb) clear_opt(sb, DISCARD); } - if (es->s_error_count) - mod_timer(&sbi->s_err_report, jiffies + 300*HZ); /* 5 minutes */ + if (es->s_error_count) { + sbi->s_err_report_sec = 5*60; /* first time 5 minutes */ + mod_timer(&sbi->s_err_report, + jiffies + secs_to_jiffies(sbi->s_err_report_sec)); + } + sbi->s_err_report_sec = 24*60*60; /* Once a day */ /* Enable message ratelimiting. Default is 10 messages per 5 secs. */ ratelimit_state_init(&sbi->s_err_ratelimit_state, 5 * HZ, 10); @@ -5704,8 +5720,8 @@ failed_mount7: failed_mount6: ext4_mb_release(sb); ext4_flex_groups_free(sbi); - ext4_percpu_param_destroy(sbi); failed_mount5: + ext4_percpu_param_destroy(sbi); ext4_ext_release(sb); ext4_release_system_zone(sb); failed_mount4a: @@ -6225,10 +6241,11 @@ static void ext4_update_super(struct super_block *sb) ext4_errno_to_code(sbi->s_last_error_code); /* * Start the daily error reporting function if it hasn't been - * started already + * started already and sbi->s_err_report_sec is not zero */ - if (!es->s_error_count) - mod_timer(&sbi->s_err_report, jiffies + 24*60*60*HZ); + if (!es->s_error_count && !sbi->s_err_report_sec) + mod_timer(&sbi->s_err_report, + jiffies + secs_to_jiffies(sbi->s_err_report_sec)); le32_add_cpu(&es->s_error_count, sbi->s_add_error_count); sbi->s_add_error_count = 0; } diff --git a/fs/ext4/sysfs.c b/fs/ext4/sysfs.c index 0018e09b867e..d2ecc1026c0c 100644 --- a/fs/ext4/sysfs.c +++ b/fs/ext4/sysfs.c @@ -40,6 +40,7 @@ typedef enum { attr_pointer_string, attr_pointer_atomic, attr_journal_task, + attr_err_report_sec, } attr_id_t; typedef enum { @@ -130,6 +131,36 @@ static ssize_t trigger_test_error(struct ext4_sb_info *sbi, return count; } +static ssize_t err_report_sec_store(struct ext4_sb_info *sbi, + const char *buf, size_t count) +{ + unsigned long t; + int ret; + + ret = kstrtoul(skip_spaces(buf), 0, &t); + if (ret) + return ret; + + /*the maximum time interval must not exceed one year.*/ + if (t > (365*24*60*60)) + return -EINVAL; + + if (sbi->s_err_report_sec == t) /*nothing to do*/ + goto out; + else if (!sbi->s_err_report_sec && t) { + timer_setup(&sbi->s_err_report, print_daily_error_info, 0); + } else if (sbi->s_err_report_sec && !t) { + timer_delete_sync(&sbi->s_err_report); + goto out; + } + + sbi->s_err_report_sec = t; + mod_timer(&sbi->s_err_report, jiffies + secs_to_jiffies(sbi->s_err_report_sec)); + +out: + return count; +} + static ssize_t journal_task_show(struct ext4_sb_info *sbi, char *buf) { if (!sbi->s_journal) @@ -217,6 +248,7 @@ EXT4_ATTR_OFFSET(mb_group_prealloc, 0644, clusters_in_group, ext4_sb_info, s_mb_group_prealloc); EXT4_ATTR_OFFSET(mb_best_avail_max_trim_order, 0644, mb_order, ext4_sb_info, s_mb_best_avail_max_trim_order); +EXT4_ATTR_OFFSET(err_report_sec, 0644, err_report_sec, ext4_sb_info, s_err_report_sec); EXT4_RW_ATTR_SBI_UI(inode_goal, s_inode_goal); EXT4_RW_ATTR_SBI_UI(mb_stats, s_mb_stats); EXT4_RW_ATTR_SBI_UI(mb_max_to_scan, s_mb_max_to_scan); @@ -309,6 +341,7 @@ static struct attribute *ext4_attrs[] = { ATTR_LIST(last_trim_minblks), ATTR_LIST(sb_update_sec), ATTR_LIST(sb_update_kb), + ATTR_LIST(err_report_sec), NULL, }; ATTRIBUTE_GROUPS(ext4); @@ -402,6 +435,7 @@ static ssize_t ext4_generic_attr_show(struct ext4_attr *a, return sysfs_emit(buf, "%u\n", le32_to_cpup(ptr)); return sysfs_emit(buf, "%u\n", *((unsigned int *) ptr)); case attr_pointer_ul: + case attr_err_report_sec: return sysfs_emit(buf, "%lu\n", *((unsigned long *) ptr)); case attr_pointer_u8: return sysfs_emit(buf, "%u\n", *((unsigned char *) ptr)); @@ -525,6 +559,8 @@ static ssize_t ext4_attr_store(struct kobject *kobj, return inode_readahead_blks_store(sbi, buf, len); case attr_trigger_test_error: return trigger_test_error(sbi, buf, len); + case attr_err_report_sec: + return err_report_sec_store(sbi, buf, len); default: return ext4_generic_attr_store(a, sbi, buf, len); } diff --git a/fs/ext4/verity.c b/fs/ext4/verity.c index 415d9c4d8a32..ca61da53f313 100644 --- a/fs/ext4/verity.c +++ b/fs/ext4/verity.c @@ -231,6 +231,8 @@ static int ext4_end_enable_verity(struct file *filp, const void *desc, goto cleanup; } + ext4_fc_mark_ineligible(inode->i_sb, EXT4_FC_REASON_VERITY, handle); + err = ext4_orphan_del(handle, inode); if (err) goto stop_and_cleanup; @@ -358,42 +360,32 @@ static int ext4_get_verity_descriptor(struct inode *inode, void *buf, } static struct page *ext4_read_merkle_tree_page(struct inode *inode, - pgoff_t index, - unsigned long num_ra_pages) + pgoff_t index) { - struct folio *folio; - index += ext4_verity_metadata_pos(inode) >> PAGE_SHIFT; + return generic_read_merkle_tree_page(inode, index); +} - folio = __filemap_get_folio(inode->i_mapping, index, FGP_ACCESSED, 0); - if (IS_ERR(folio) || !folio_test_uptodate(folio)) { - DEFINE_READAHEAD(ractl, NULL, NULL, inode->i_mapping, index); - - if (!IS_ERR(folio)) - folio_put(folio); - else if (num_ra_pages > 1) - page_cache_ra_unbounded(&ractl, num_ra_pages, 0); - folio = read_mapping_folio(inode->i_mapping, index, NULL); - if (IS_ERR(folio)) - return ERR_CAST(folio); - } - return folio_file_page(folio, index); +static void ext4_readahead_merkle_tree(struct inode *inode, pgoff_t index, + unsigned long nr_pages) +{ + index += ext4_verity_metadata_pos(inode) >> PAGE_SHIFT; + generic_readahead_merkle_tree(inode, index, nr_pages); } -static int ext4_write_merkle_tree_block(struct inode *inode, const void *buf, +static int ext4_write_merkle_tree_block(struct file *file, const void *buf, u64 pos, unsigned int size) { - pos += ext4_verity_metadata_pos(inode); + pos += ext4_verity_metadata_pos(file_inode(file)); - return pagecache_write(inode, buf, size, pos); + return pagecache_write(file_inode(file), buf, size, pos); } const struct fsverity_operations ext4_verityops = { - .inode_info_offs = (int)offsetof(struct ext4_inode_info, i_verity_info) - - (int)offsetof(struct ext4_inode_info, vfs_inode), .begin_enable_verity = ext4_begin_enable_verity, .end_enable_verity = ext4_end_enable_verity, .get_verity_descriptor = ext4_get_verity_descriptor, .read_merkle_tree_page = ext4_read_merkle_tree_page, + .readahead_merkle_tree = ext4_readahead_merkle_tree, .write_merkle_tree_block = ext4_write_merkle_tree_block, }; diff --git a/fs/f2fs/compress.c b/fs/f2fs/compress.c index 7b68bf22989d..ef1225af2acf 100644 --- a/fs/f2fs/compress.c +++ b/fs/f2fs/compress.c @@ -1181,6 +1181,7 @@ int f2fs_prepare_compress_overwrite(struct inode *inode, .cluster_idx = index >> F2FS_I(inode)->i_log_cluster_size, .rpages = NULL, .nr_rpages = 0, + .vi = NULL, /* can't write to fsverity files */ }; return prepare_compress_overwrite(&cc, pagep, index, fsdata); @@ -1716,7 +1717,7 @@ struct decompress_io_ctx *f2fs_alloc_dic(struct compress_ctx *cc) dic->nr_cpages = cc->nr_cpages; refcount_set(&dic->refcnt, 1); dic->failed = false; - dic->need_verity = f2fs_need_verity(cc->inode, start_idx); + dic->vi = cc->vi; for (i = 0; i < dic->cluster_size; i++) dic->rpages[i] = cc->rpages[i]; @@ -1814,7 +1815,7 @@ static void f2fs_verify_cluster(struct work_struct *work) if (!rpage) continue; - if (fsverity_verify_page(rpage)) + if (fsverity_verify_page(dic->vi, rpage)) SetPageUptodate(rpage); else ClearPageUptodate(rpage); @@ -1833,7 +1834,7 @@ void f2fs_decompress_end_io(struct decompress_io_ctx *dic, bool failed, { int i; - if (!failed && dic->need_verity) { + if (IS_ENABLED(CONFIG_FS_VERITY) && !failed && dic->vi) { /* * Note that to avoid deadlocks, the verity work can't be done * on the decompression workqueue. This is because verifying diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index c30e69392a62..491f66511201 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -109,6 +109,7 @@ enum bio_post_read_step { struct bio_post_read_ctx { struct bio *bio; struct f2fs_sb_info *sbi; + struct fsverity_info *vi; struct work_struct work; unsigned int enabled_steps; /* @@ -165,6 +166,7 @@ static void f2fs_verify_bio(struct work_struct *work) container_of(work, struct bio_post_read_ctx, work); struct bio *bio = ctx->bio; bool may_have_compressed_pages = (ctx->enabled_steps & STEP_DECOMPRESS); + struct fsverity_info *vi = ctx->vi; /* * fsverity_verify_bio() may call readahead() again, and while verity @@ -187,13 +189,13 @@ static void f2fs_verify_bio(struct work_struct *work) struct folio *folio = fi.folio; if (!f2fs_is_compressed_page(folio) && - !fsverity_verify_page(&folio->page)) { + !fsverity_verify_page(vi, &folio->page)) { bio->bi_status = BLK_STS_IOERR; break; } } } else { - fsverity_verify_bio(bio); + fsverity_verify_bio(vi, bio); } f2fs_finish_read_bio(bio, true); @@ -513,7 +515,7 @@ void f2fs_submit_read_bio(struct f2fs_sb_info *sbi, struct bio *bio, trace_f2fs_submit_read_bio(sbi->sb, type, bio); iostat_update_submit_ctx(bio, type); - submit_bio(bio); + blk_crypto_submit_bio(bio); } static void f2fs_submit_write_bio(struct f2fs_sb_info *sbi, struct bio *bio, @@ -522,7 +524,7 @@ static void f2fs_submit_write_bio(struct f2fs_sb_info *sbi, struct bio *bio, WARN_ON_ONCE(is_read_io(bio_op(bio))); trace_f2fs_submit_write_bio(sbi->sb, type, bio); iostat_update_submit_ctx(bio, type); - submit_bio(bio); + blk_crypto_submit_bio(bio); } static void __submit_merged_bio(struct f2fs_bio_info *io) @@ -1036,7 +1038,8 @@ out: f2fs_up_write(&io->io_rwsem); } -static struct bio *f2fs_grab_read_bio(struct inode *inode, block_t blkaddr, +static struct bio *f2fs_grab_read_bio(struct inode *inode, + struct fsverity_info *vi, block_t blkaddr, unsigned nr_pages, blk_opf_t op_flag, pgoff_t first_idx, bool for_write) { @@ -1057,7 +1060,7 @@ static struct bio *f2fs_grab_read_bio(struct inode *inode, block_t blkaddr, if (fscrypt_inode_uses_fs_layer_crypto(inode)) post_read_steps |= STEP_DECRYPT; - if (f2fs_need_verity(inode, first_idx)) + if (vi) post_read_steps |= STEP_VERITY; /* @@ -1072,6 +1075,7 @@ static struct bio *f2fs_grab_read_bio(struct inode *inode, block_t blkaddr, ctx = mempool_alloc(bio_post_read_ctx_pool, GFP_NOFS); ctx->bio = bio; ctx->sbi = sbi; + ctx->vi = vi; ctx->enabled_steps = post_read_steps; ctx->fs_blkaddr = blkaddr; ctx->decompression_attempted = false; @@ -1083,15 +1087,15 @@ static struct bio *f2fs_grab_read_bio(struct inode *inode, block_t blkaddr, } /* This can handle encryption stuffs */ -static void f2fs_submit_page_read(struct inode *inode, struct folio *folio, - block_t blkaddr, blk_opf_t op_flags, - bool for_write) +static void f2fs_submit_page_read(struct inode *inode, struct fsverity_info *vi, + struct folio *folio, block_t blkaddr, + blk_opf_t op_flags, bool for_write) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); struct bio *bio; - bio = f2fs_grab_read_bio(inode, blkaddr, 1, op_flags, - folio->index, for_write); + bio = f2fs_grab_read_bio(inode, vi, blkaddr, 1, op_flags, folio->index, + for_write); /* wait for GCed page writeback via META_MAPPING */ f2fs_wait_on_block_writeback(inode, blkaddr); @@ -1193,6 +1197,14 @@ int f2fs_reserve_block(struct dnode_of_data *dn, pgoff_t index) return err; } +static inline struct fsverity_info *f2fs_need_verity(const struct inode *inode, + pgoff_t idx) +{ + if (idx < DIV_ROUND_UP(inode->i_size, PAGE_SIZE)) + return fsverity_get_info(inode); + return NULL; +} + struct folio *f2fs_get_read_data_folio(struct inode *inode, pgoff_t index, blk_opf_t op_flags, bool for_write, pgoff_t *next_pgofs) { @@ -1258,8 +1270,8 @@ got_it: return folio; } - f2fs_submit_page_read(inode, folio, dn.data_blkaddr, - op_flags, for_write); + f2fs_submit_page_read(inode, f2fs_need_verity(inode, folio->index), + folio, dn.data_blkaddr, op_flags, for_write); return folio; put_err: @@ -2063,12 +2075,12 @@ static inline blk_opf_t f2fs_ra_op_flags(struct readahead_control *rac) return rac ? REQ_RAHEAD : 0; } -static int f2fs_read_single_page(struct inode *inode, struct folio *folio, - unsigned nr_pages, - struct f2fs_map_blocks *map, - struct bio **bio_ret, - sector_t *last_block_in_bio, - struct readahead_control *rac) +static int f2fs_read_single_page(struct inode *inode, struct fsverity_info *vi, + struct folio *folio, unsigned int nr_pages, + struct f2fs_map_blocks *map, + struct bio **bio_ret, + sector_t *last_block_in_bio, + struct readahead_control *rac) { struct bio *bio = *bio_ret; const unsigned int blocksize = F2FS_BLKSIZE; @@ -2120,8 +2132,7 @@ got_it: } else { zero_out: folio_zero_segment(folio, 0, folio_size(folio)); - if (f2fs_need_verity(inode, index) && - !fsverity_verify_folio(folio)) { + if (vi && !fsverity_verify_folio(vi, folio)) { ret = -EIO; goto out; } @@ -2143,9 +2154,8 @@ submit_and_realloc: bio = NULL; } if (bio == NULL) - bio = f2fs_grab_read_bio(inode, block_nr, nr_pages, - f2fs_ra_op_flags(rac), index, - false); + bio = f2fs_grab_read_bio(inode, vi, block_nr, nr_pages, + f2fs_ra_op_flags(rac), index, false); /* * If the page is under writeback, we need to wait for @@ -2295,9 +2305,10 @@ submit_and_realloc: } if (!bio) - bio = f2fs_grab_read_bio(inode, blkaddr, nr_pages - i, - f2fs_ra_op_flags(rac), - folio->index, for_write); + bio = f2fs_grab_read_bio(inode, cc->vi, blkaddr, + nr_pages - i, + f2fs_ra_op_flags(rac), + folio->index, for_write); if (!bio_add_folio(bio, folio, blocksize, 0)) goto submit_and_realloc; @@ -2336,7 +2347,7 @@ out: * This function was originally taken from fs/mpage.c, and customized for f2fs. * Major change was from block_size == page_size in f2fs by default. */ -static int f2fs_mpage_readpages(struct inode *inode, +static int f2fs_mpage_readpages(struct inode *inode, struct fsverity_info *vi, struct readahead_control *rac, struct folio *folio) { struct bio *bio = NULL; @@ -2391,6 +2402,7 @@ static int f2fs_mpage_readpages(struct inode *inode, /* there are remained compressed pages, submit them */ if (!f2fs_cluster_can_merge_page(&cc, index)) { + cc.vi = vi; ret = f2fs_read_multi_pages(&cc, &bio, max_nr_pages, &last_block_in_bio, @@ -2424,8 +2436,9 @@ static int f2fs_mpage_readpages(struct inode *inode, read_single_page: #endif - ret = f2fs_read_single_page(inode, folio, max_nr_pages, &map, - &bio, &last_block_in_bio, rac); + ret = f2fs_read_single_page(inode, vi, folio, max_nr_pages, + &map, &bio, &last_block_in_bio, + rac); if (ret) { #ifdef CONFIG_F2FS_FS_COMPRESSION set_error_page: @@ -2441,6 +2454,7 @@ next_page: if (f2fs_compressed_file(inode)) { /* last page */ if (nr_pages == 1 && !f2fs_cluster_is_empty(&cc)) { + cc.vi = vi; ret = f2fs_read_multi_pages(&cc, &bio, max_nr_pages, &last_block_in_bio, @@ -2458,7 +2472,8 @@ next_page: static int f2fs_read_data_folio(struct file *file, struct folio *folio) { struct inode *inode = folio->mapping->host; - int ret = -EAGAIN; + struct fsverity_info *vi = NULL; + int ret; trace_f2fs_readpage(folio, DATA); @@ -2468,16 +2483,22 @@ static int f2fs_read_data_folio(struct file *file, struct folio *folio) } /* If the file has inline data, try to read it directly */ - if (f2fs_has_inline_data(inode)) + if (f2fs_has_inline_data(inode)) { ret = f2fs_read_inline_data(inode, folio); - if (ret == -EAGAIN) - ret = f2fs_mpage_readpages(inode, NULL, folio); - return ret; + if (ret != -EAGAIN) + return ret; + } + + vi = f2fs_need_verity(inode, folio->index); + if (vi) + fsverity_readahead(vi, folio->index, folio_nr_pages(folio)); + return f2fs_mpage_readpages(inode, vi, NULL, folio); } static void f2fs_readahead(struct readahead_control *rac) { struct inode *inode = rac->mapping->host; + struct fsverity_info *vi = NULL; trace_f2fs_readpages(inode, readahead_index(rac), readahead_count(rac)); @@ -2488,7 +2509,11 @@ static void f2fs_readahead(struct readahead_control *rac) if (f2fs_has_inline_data(inode)) return; - f2fs_mpage_readpages(inode, rac, NULL); + vi = f2fs_need_verity(inode, readahead_index(rac)); + if (vi) + fsverity_readahead(vi, readahead_index(rac), + readahead_count(rac)); + f2fs_mpage_readpages(inode, vi, rac, NULL); } int f2fs_encrypt_one_page(struct f2fs_io_info *fio) @@ -3637,9 +3662,10 @@ repeat: err = -EFSCORRUPTED; goto put_folio; } - f2fs_submit_page_read(use_cow ? - F2FS_I(inode)->cow_inode : inode, - folio, blkaddr, 0, true); + f2fs_submit_page_read(use_cow ? F2FS_I(inode)->cow_inode : + inode, + NULL, /* can't write to fsverity files */ + folio, blkaddr, 0, true); folio_lock(folio); if (unlikely(folio->mapping != mapping)) { diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index 48f4f98afb01..be70dfb3b152 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -8,6 +8,7 @@ #include <linux/unaligned.h> #include <linux/fs.h> #include <linux/f2fs_fs.h> +#include <linux/filelock.h> #include <linux/sched/signal.h> #include <linux/unicode.h> #include "f2fs.h" @@ -1136,4 +1137,5 @@ const struct file_operations f2fs_dir_operations = { #ifdef CONFIG_COMPAT .compat_ioctl = f2fs_compat_ioctl, #endif + .setlease = generic_setlease, }; diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 20edbb99b814..a90a62cfe617 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -974,9 +974,6 @@ struct f2fs_inode_info { #ifdef CONFIG_FS_ENCRYPTION struct fscrypt_inode_info *i_crypt_info; /* filesystem encryption info */ #endif -#ifdef CONFIG_FS_VERITY - struct fsverity_info *i_verity_info; /* filesystem verity info */ -#endif }; static inline void get_read_extent_info(struct extent_info *ext, @@ -1603,6 +1600,7 @@ struct compress_ctx { size_t clen; /* valid data length in cbuf */ void *private; /* payload buffer for specified compression algorithm */ void *private2; /* extra payload buffer */ + struct fsverity_info *vi; /* verity info if needed */ }; /* compress context for write IO path */ @@ -1658,7 +1656,7 @@ struct decompress_io_ctx { refcount_t refcnt; bool failed; /* IO error occurred before decompression? */ - bool need_verity; /* need fs-verity verification after decompression? */ + struct fsverity_info *vi; /* fs-verity context if needed */ unsigned char compress_algorithm; /* backup algorithm type */ void *private; /* payload buffer for specified decompression algorithm */ void *private2; /* extra payload buffer */ @@ -4886,12 +4884,6 @@ static inline bool f2fs_allow_multi_device_dio(struct f2fs_sb_info *sbi, return sbi->aligned_blksize; } -static inline bool f2fs_need_verity(const struct inode *inode, pgoff_t idx) -{ - return fsverity_active(inode) && - idx < DIV_ROUND_UP(inode->i_size, PAGE_SIZE); -} - #ifdef CONFIG_F2FS_FAULT_INJECTION extern int f2fs_build_fault_attr(struct f2fs_sb_info *sbi, unsigned long rate, unsigned long type, enum fault_option fo); @@ -5004,7 +4996,4 @@ static inline void f2fs_invalidate_internal_cache(struct f2fs_sb_info *sbi, f2fs_invalidate_compress_pages_range(sbi, blkaddr, len); } -#define EFSBADCRC EBADMSG /* Bad CRC detected */ -#define EFSCORRUPTED EUCLEAN /* Filesystem is corrupted */ - #endif /* _LINUX_F2FS_H */ diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index d7047ca6b98d..1fdbe18692be 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -5,12 +5,14 @@ * Copyright (c) 2012 Samsung Electronics Co., Ltd. * http://www.samsung.com/ */ +#include <linux/blk-crypto.h> #include <linux/fs.h> #include <linux/f2fs_fs.h> #include <linux/stat.h> #include <linux/writeback.h> #include <linux/blkdev.h> #include <linux/falloc.h> +#include <linux/filelock.h> #include <linux/types.h> #include <linux/compat.h> #include <linux/uaccess.h> @@ -1074,10 +1076,6 @@ int f2fs_setattr(struct mnt_idmap *idmap, struct dentry *dentry, if (err) return err; - err = fsverity_prepare_setattr(dentry, attr); - if (err) - return err; - if (unlikely(IS_IMMUTABLE(inode))) return -EPERM; @@ -4422,7 +4420,9 @@ static int redirty_blocks(struct inode *inode, pgoff_t page_idx, int len) pgoff_t redirty_idx = page_idx; int page_len = 0, ret = 0; + filemap_invalidate_lock_shared(mapping); page_cache_ra_unbounded(&ractl, len, 0); + filemap_invalidate_unlock_shared(mapping); do { folio = read_cache_folio(mapping, page_idx, NULL, NULL); @@ -5046,7 +5046,7 @@ static void f2fs_dio_write_submit_io(const struct iomap_iter *iter, enum temp_type temp = f2fs_get_segment_temp(sbi, type); bio->bi_write_hint = f2fs_io_type_to_rw_hint(sbi, DATA, temp); - submit_bio(bio); + blk_crypto_submit_bio(bio); } static const struct iomap_dio_ops f2fs_iomap_dio_write_ops = { @@ -5457,4 +5457,5 @@ const struct file_operations f2fs_file_operations = { .splice_write = iter_file_splice_write, .fadvise = f2fs_file_fadvise, .fop_flags = FOP_BUFFER_RASYNC, + .setlease = generic_setlease, }; diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index 38b8994bc1b2..ee332b994348 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -1000,7 +1000,6 @@ no_delete: } out_clear: fscrypt_put_encryption_info(inode); - fsverity_cleanup_inode(inode); clear_inode(inode); } diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index c4c225e09dc4..cd00d030edda 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -504,9 +504,6 @@ static void init_once(void *foo) #ifdef CONFIG_FS_ENCRYPTION fi->i_crypt_info = NULL; #endif -#ifdef CONFIG_FS_VERITY - fi->i_verity_info = NULL; -#endif } #ifdef CONFIG_QUOTA diff --git a/fs/f2fs/verity.c b/fs/f2fs/verity.c index 05b935b55216..92ebcc19cab0 100644 --- a/fs/f2fs/verity.c +++ b/fs/f2fs/verity.c @@ -256,42 +256,32 @@ static int f2fs_get_verity_descriptor(struct inode *inode, void *buf, } static struct page *f2fs_read_merkle_tree_page(struct inode *inode, - pgoff_t index, - unsigned long num_ra_pages) + pgoff_t index) { - struct folio *folio; - index += f2fs_verity_metadata_pos(inode) >> PAGE_SHIFT; + return generic_read_merkle_tree_page(inode, index); +} - folio = f2fs_filemap_get_folio(inode->i_mapping, index, FGP_ACCESSED, 0); - if (IS_ERR(folio) || !folio_test_uptodate(folio)) { - DEFINE_READAHEAD(ractl, NULL, NULL, inode->i_mapping, index); - - if (!IS_ERR(folio)) - folio_put(folio); - else if (num_ra_pages > 1) - page_cache_ra_unbounded(&ractl, num_ra_pages, 0); - folio = read_mapping_folio(inode->i_mapping, index, NULL); - if (IS_ERR(folio)) - return ERR_CAST(folio); - } - return folio_file_page(folio, index); +static void f2fs_readahead_merkle_tree(struct inode *inode, pgoff_t index, + unsigned long nr_pages) +{ + index += f2fs_verity_metadata_pos(inode) >> PAGE_SHIFT; + generic_readahead_merkle_tree(inode, index, nr_pages); } -static int f2fs_write_merkle_tree_block(struct inode *inode, const void *buf, +static int f2fs_write_merkle_tree_block(struct file *file, const void *buf, u64 pos, unsigned int size) { - pos += f2fs_verity_metadata_pos(inode); + pos += f2fs_verity_metadata_pos(file_inode(file)); - return pagecache_write(inode, buf, size, pos); + return pagecache_write(file_inode(file), buf, size, pos); } const struct fsverity_operations f2fs_verityops = { - .inode_info_offs = (int)offsetof(struct f2fs_inode_info, i_verity_info) - - (int)offsetof(struct f2fs_inode_info, vfs_inode), .begin_enable_verity = f2fs_begin_enable_verity, .end_enable_verity = f2fs_end_enable_verity, .get_verity_descriptor = f2fs_get_verity_descriptor, .read_merkle_tree_page = f2fs_read_merkle_tree_page, + .readahead_merkle_tree = f2fs_readahead_merkle_tree, .write_merkle_tree_block = f2fs_write_merkle_tree_block, }; diff --git a/fs/fat/cache.c b/fs/fat/cache.c index 630f3056658e..1b87354e24ba 100644 --- a/fs/fat/cache.c +++ b/fs/fat/cache.c @@ -54,7 +54,7 @@ void fat_cache_destroy(void) kmem_cache_destroy(fat_cache_cachep); } -static inline struct fat_cache *fat_cache_alloc(struct inode *inode) +static inline struct fat_cache *fat_cache_alloc(void) { return kmem_cache_alloc(fat_cache_cachep, GFP_NOFS); } @@ -144,7 +144,7 @@ static void fat_cache_add(struct inode *inode, struct fat_cache_id *new) MSDOS_I(inode)->nr_caches++; spin_unlock(&MSDOS_I(inode)->cache_lru_lock); - tmp = fat_cache_alloc(inode); + tmp = fat_cache_alloc(); if (!tmp) { spin_lock(&MSDOS_I(inode)->cache_lru_lock); MSDOS_I(inode)->nr_caches--; diff --git a/fs/fat/dir.c b/fs/fat/dir.c index 92b091783966..4b8b25f688e4 100644 --- a/fs/fat/dir.c +++ b/fs/fat/dir.c @@ -16,6 +16,8 @@ #include <linux/slab.h> #include <linux/compat.h> +#include <linux/filelock.h> +#include <linux/hex.h> #include <linux/uaccess.h> #include <linux/iversion.h> #include "fat.h" @@ -876,6 +878,7 @@ const struct file_operations fat_dir_operations = { .compat_ioctl = fat_compat_dir_ioctl, #endif .fsync = fat_file_fsync, + .setlease = generic_setlease, }; static int fat_get_short_entry(struct inode *dir, loff_t *pos, @@ -1080,7 +1083,7 @@ int fat_remove_entries(struct inode *dir, struct fat_slot_info *sinfo) } } - fat_truncate_time(dir, NULL, S_ATIME|S_MTIME); + fat_truncate_time(dir, NULL, FAT_UPDATE_ATIME | FAT_UPDATE_CMTIME); if (IS_DIRSYNC(dir)) (void)fat_sync_inode(dir); else @@ -1353,7 +1356,7 @@ found: /* Fill the long name slots. */ for (i = 0; i < long_bhs; i++) { - int copy = min_t(int, sb->s_blocksize - offset, size); + int copy = umin(sb->s_blocksize - offset, size); memcpy(bhs[i]->b_data + offset, slots, copy); mark_buffer_dirty_inode(bhs[i], dir); offset = 0; @@ -1364,7 +1367,7 @@ found: err = fat_sync_bhs(bhs, long_bhs); if (!err && i < nr_bhs) { /* Fill the short name slot. */ - int copy = min_t(int, sb->s_blocksize - offset, size); + int copy = umin(sb->s_blocksize - offset, size); memcpy(bhs[i]->b_data + offset, slots, copy); mark_buffer_dirty_inode(bhs[i], dir); if (IS_DIRSYNC(dir)) diff --git a/fs/fat/fat.h b/fs/fat/fat.h index d3e426de5f01..0d269dba897b 100644 --- a/fs/fat/fat.h +++ b/fs/fat/fat.h @@ -468,11 +468,12 @@ extern void fat_time_unix2fat(struct msdos_sb_info *sbi, struct timespec64 *ts, __le16 *time, __le16 *date, u8 *time_cs); extern struct timespec64 fat_truncate_atime(const struct msdos_sb_info *sbi, const struct timespec64 *ts); -extern struct timespec64 fat_truncate_mtime(const struct msdos_sb_info *sbi, - const struct timespec64 *ts); -extern int fat_truncate_time(struct inode *inode, struct timespec64 *now, - int flags); -extern int fat_update_time(struct inode *inode, int flags); +#define FAT_UPDATE_ATIME (1u << 0) +#define FAT_UPDATE_CMTIME (1u << 1) +void fat_truncate_time(struct inode *inode, struct timespec64 *now, + unsigned int flags); +int fat_update_time(struct inode *inode, enum fs_update_time type, + unsigned int flags); extern int fat_sync_bhs(struct buffer_head **bhs, int nr_bhs); int fat_cache_init(void); diff --git a/fs/fat/file.c b/fs/fat/file.c index 4fc49a614fb8..124d9c5431c8 100644 --- a/fs/fat/file.c +++ b/fs/fat/file.c @@ -13,6 +13,7 @@ #include <linux/mount.h> #include <linux/blkdev.h> #include <linux/backing-dev.h> +#include <linux/filelock.h> #include <linux/fsnotify.h> #include <linux/security.h> #include <linux/falloc.h> @@ -140,8 +141,7 @@ static int fat_ioctl_fitrim(struct inode *inode, unsigned long arg) if (copy_from_user(&range, user_range, sizeof(range))) return -EFAULT; - range.minlen = max_t(unsigned int, range.minlen, - bdev_discard_granularity(sb->s_bdev)); + range.minlen = max(range.minlen, bdev_discard_granularity(sb->s_bdev)); err = fat_trim_fs(inode, &range); if (err < 0) @@ -212,6 +212,7 @@ const struct file_operations fat_file_operations = { .splice_read = filemap_splice_read, .splice_write = iter_file_splice_write, .fallocate = fat_fallocate, + .setlease = generic_setlease, }; static int fat_cont_expand(struct inode *inode, loff_t size) @@ -224,7 +225,7 @@ static int fat_cont_expand(struct inode *inode, loff_t size) if (err) goto out; - fat_truncate_time(inode, NULL, S_CTIME|S_MTIME); + fat_truncate_time(inode, NULL, FAT_UPDATE_CMTIME); mark_inode_dirty(inode); if (IS_SYNC(inode)) { int err2; @@ -327,7 +328,7 @@ static int fat_free(struct inode *inode, int skip) MSDOS_I(inode)->i_logstart = 0; } MSDOS_I(inode)->i_attrs |= ATTR_ARCH; - fat_truncate_time(inode, NULL, S_CTIME|S_MTIME); + fat_truncate_time(inode, NULL, FAT_UPDATE_CMTIME); if (wait) { err = fat_sync_inode(inode); if (err) { @@ -553,15 +554,13 @@ int fat_setattr(struct mnt_idmap *idmap, struct dentry *dentry, } /* - * setattr_copy can't truncate these appropriately, so we'll - * copy them ourselves + * setattr_copy can't truncate these appropriately, so we'll copy them + * ourselves. See fat_truncate_time for the c/mtime logic on fat. */ if (attr->ia_valid & ATTR_ATIME) - fat_truncate_time(inode, &attr->ia_atime, S_ATIME); - if (attr->ia_valid & ATTR_CTIME) - fat_truncate_time(inode, &attr->ia_ctime, S_CTIME); + fat_truncate_time(inode, &attr->ia_atime, FAT_UPDATE_ATIME); if (attr->ia_valid & ATTR_MTIME) - fat_truncate_time(inode, &attr->ia_mtime, S_MTIME); + fat_truncate_time(inode, &attr->ia_mtime, FAT_UPDATE_CMTIME); attr->ia_valid &= ~(ATTR_ATIME|ATTR_CTIME|ATTR_MTIME); setattr_copy(idmap, inode, attr); diff --git a/fs/fat/inode.c b/fs/fat/inode.c index 0b6009cd1844..59fa90617b5b 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c @@ -246,7 +246,7 @@ static int fat_write_end(const struct kiocb *iocb, if (err < len) fat_write_failed(mapping, pos + len); if (!(err < 0) && !(MSDOS_I(inode)->i_attrs & ATTR_ARCH)) { - fat_truncate_time(inode, NULL, S_CTIME|S_MTIME); + fat_truncate_time(inode, NULL, FAT_UPDATE_CMTIME); MSDOS_I(inode)->i_attrs |= ATTR_ARCH; mark_inode_dirty(inode); } diff --git a/fs/fat/misc.c b/fs/fat/misc.c index 950da09f0961..b154a5162764 100644 --- a/fs/fat/misc.c +++ b/fs/fat/misc.c @@ -299,62 +299,47 @@ struct timespec64 fat_truncate_atime(const struct msdos_sb_info *sbi, } /* - * truncate mtime to 2 second granularity - */ -struct timespec64 fat_truncate_mtime(const struct msdos_sb_info *sbi, - const struct timespec64 *ts) -{ - return fat_timespec64_trunc_2secs(*ts); -} - -/* - * truncate the various times with appropriate granularity: - * all times in root node are always 0 + * Update the in-inode atime and/or mtime after truncating the timestamp to the + * granularity. All timestamps in root inode are always 0. + * + * ctime and mtime share the same on-disk field, and should be identical in + * memory. All mtime updates will be applied to ctime, but ctime updates are + * ignored. */ -int fat_truncate_time(struct inode *inode, struct timespec64 *now, int flags) +void fat_truncate_time(struct inode *inode, struct timespec64 *now, + unsigned int flags) { struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb); struct timespec64 ts; if (inode->i_ino == MSDOS_ROOT_INO) - return 0; + return; if (now == NULL) { now = &ts; ts = current_time(inode); } - if (flags & S_ATIME) + if (flags & FAT_UPDATE_ATIME) inode_set_atime_to_ts(inode, fat_truncate_atime(sbi, now)); - /* - * ctime and mtime share the same on-disk field, and should be - * identical in memory. all mtime updates will be applied to ctime, - * but ctime updates are ignored. - */ - if (flags & S_MTIME) - inode_set_mtime_to_ts(inode, - inode_set_ctime_to_ts(inode, fat_truncate_mtime(sbi, now))); + if (flags & FAT_UPDATE_CMTIME) { + /* truncate mtime to 2 second granularity */ + struct timespec64 mtime = fat_timespec64_trunc_2secs(*now); - return 0; + inode_set_mtime_to_ts(inode, mtime); + inode_set_ctime_to_ts(inode, mtime); + } } EXPORT_SYMBOL_GPL(fat_truncate_time); -int fat_update_time(struct inode *inode, int flags) +int fat_update_time(struct inode *inode, enum fs_update_time type, + unsigned int flags) { - int dirty_flags = 0; - - if (inode->i_ino == MSDOS_ROOT_INO) - return 0; - - if (flags & (S_ATIME | S_CTIME | S_MTIME)) { - fat_truncate_time(inode, NULL, flags); - if (inode->i_sb->s_flags & SB_LAZYTIME) - dirty_flags |= I_DIRTY_TIME; - else - dirty_flags |= I_DIRTY_SYNC; + if (inode->i_ino != MSDOS_ROOT_INO) { + fat_truncate_time(inode, NULL, type == FS_UPD_ATIME ? + FAT_UPDATE_ATIME : FAT_UPDATE_CMTIME); + __mark_inode_dirty(inode, inode_time_dirty_flag(inode)); } - - __mark_inode_dirty(inode, dirty_flags); return 0; } EXPORT_SYMBOL_GPL(fat_update_time); diff --git a/fs/fat/namei_msdos.c b/fs/fat/namei_msdos.c index 0b920ee40a7f..048c103b506a 100644 --- a/fs/fat/namei_msdos.c +++ b/fs/fat/namei_msdos.c @@ -251,7 +251,7 @@ static int msdos_add_entry(struct inode *dir, const unsigned char *name, if (err) return err; - fat_truncate_time(dir, ts, S_CTIME|S_MTIME); + fat_truncate_time(dir, ts, FAT_UPDATE_CMTIME); if (IS_DIRSYNC(dir)) (void)fat_sync_inode(dir); else @@ -295,7 +295,7 @@ static int msdos_create(struct mnt_idmap *idmap, struct inode *dir, err = PTR_ERR(inode); goto out; } - fat_truncate_time(inode, &ts, S_ATIME|S_CTIME|S_MTIME); + fat_truncate_time(inode, &ts, FAT_UPDATE_ATIME | FAT_UPDATE_CMTIME); /* timestamp is already written, so mark_inode_dirty() is unneeded. */ d_instantiate(dentry, inode); @@ -325,10 +325,14 @@ static int msdos_rmdir(struct inode *dir, struct dentry *dentry) err = fat_remove_entries(dir, &sinfo); /* and releases bh */ if (err) goto out; - drop_nlink(dir); + if (dir->i_nlink >= 3) + drop_nlink(dir); + else { + fat_fs_error(sb, "parent dir link count too low (%u)", + dir->i_nlink); + } clear_nlink(inode); - fat_truncate_time(inode, NULL, S_CTIME); fat_detach(inode); out: mutex_unlock(&MSDOS_SB(sb)->s_lock); @@ -382,7 +386,7 @@ static struct dentry *msdos_mkdir(struct mnt_idmap *idmap, struct inode *dir, goto out; } set_nlink(inode, 2); - fat_truncate_time(inode, &ts, S_ATIME|S_CTIME|S_MTIME); + fat_truncate_time(inode, &ts, FAT_UPDATE_ATIME | FAT_UPDATE_CMTIME); /* timestamp is already written, so mark_inode_dirty() is unneeded. */ d_instantiate(dentry, inode); @@ -415,7 +419,6 @@ static int msdos_unlink(struct inode *dir, struct dentry *dentry) if (err) goto out; clear_nlink(inode); - fat_truncate_time(inode, NULL, S_CTIME); fat_detach(inode); out: mutex_unlock(&MSDOS_SB(sb)->s_lock); @@ -480,7 +483,7 @@ static int do_msdos_rename(struct inode *old_dir, unsigned char *old_name, mark_inode_dirty(old_inode); inode_inc_iversion(old_dir); - fat_truncate_time(old_dir, NULL, S_CTIME|S_MTIME); + fat_truncate_time(old_dir, NULL, FAT_UPDATE_CMTIME); if (IS_DIRSYNC(old_dir)) (void)fat_sync_inode(old_dir); else @@ -540,7 +543,7 @@ static int do_msdos_rename(struct inode *old_dir, unsigned char *old_name, if (err) goto error_dotdot; inode_inc_iversion(old_dir); - fat_truncate_time(old_dir, &ts, S_CTIME|S_MTIME); + fat_truncate_time(old_dir, &ts, FAT_UPDATE_CMTIME); if (IS_DIRSYNC(old_dir)) (void)fat_sync_inode(old_dir); else @@ -550,7 +553,6 @@ static int do_msdos_rename(struct inode *old_dir, unsigned char *old_name, drop_nlink(new_inode); if (is_dir) drop_nlink(new_inode); - fat_truncate_time(new_inode, &ts, S_CTIME); } out: brelse(sinfo.bh); diff --git a/fs/fat/namei_vfat.c b/fs/fat/namei_vfat.c index 5dbc4cbb8fce..2acfe3123a72 100644 --- a/fs/fat/namei_vfat.c +++ b/fs/fat/namei_vfat.c @@ -20,6 +20,7 @@ #include <linux/ctype.h> #include <linux/slab.h> #include <linux/namei.h> +#include <linux/hex.h> #include <linux/kernel.h> #include <linux/iversion.h> #include "fat.h" @@ -676,7 +677,7 @@ static int vfat_add_entry(struct inode *dir, const struct qstr *qname, goto cleanup; /* update timestamp */ - fat_truncate_time(dir, ts, S_CTIME|S_MTIME); + fat_truncate_time(dir, ts, FAT_UPDATE_CMTIME); if (IS_DIRSYNC(dir)) (void)fat_sync_inode(dir); else @@ -803,10 +804,15 @@ static int vfat_rmdir(struct inode *dir, struct dentry *dentry) err = fat_remove_entries(dir, &sinfo); /* and releases bh */ if (err) goto out; - drop_nlink(dir); + if (dir->i_nlink >= 3) + drop_nlink(dir); + else { + fat_fs_error(sb, "parent dir link count too low (%u)", + dir->i_nlink); + } clear_nlink(inode); - fat_truncate_time(inode, NULL, S_ATIME|S_MTIME); + fat_truncate_time(inode, NULL, FAT_UPDATE_ATIME | FAT_UPDATE_CMTIME); fat_detach(inode); vfat_d_version_set(dentry, inode_query_iversion(dir)); out: @@ -832,7 +838,7 @@ static int vfat_unlink(struct inode *dir, struct dentry *dentry) if (err) goto out; clear_nlink(inode); - fat_truncate_time(inode, NULL, S_ATIME|S_MTIME); + fat_truncate_time(inode, NULL, FAT_UPDATE_ATIME | FAT_UPDATE_CMTIME); fat_detach(inode); vfat_d_version_set(dentry, inode_query_iversion(dir)); out: @@ -918,7 +924,7 @@ static int vfat_update_dotdot_de(struct inode *dir, struct inode *inode, static void vfat_update_dir_metadata(struct inode *dir, struct timespec64 *ts) { inode_inc_iversion(dir); - fat_truncate_time(dir, ts, S_CTIME | S_MTIME); + fat_truncate_time(dir, ts, FAT_UPDATE_CMTIME); if (IS_DIRSYNC(dir)) (void)fat_sync_inode(dir); else @@ -996,7 +1002,6 @@ static int vfat_rename(struct inode *old_dir, struct dentry *old_dentry, drop_nlink(new_inode); if (is_dir) drop_nlink(new_inode); - fat_truncate_time(new_inode, &ts, S_CTIME); } out: brelse(sinfo.bh); diff --git a/fs/fhandle.c b/fs/fhandle.c index 3de1547ec9d4..e15bcf4b0b23 100644 --- a/fs/fhandle.c +++ b/fs/fhandle.c @@ -157,9 +157,8 @@ SYSCALL_DEFINE5(name_to_handle_at, int, dfd, const char __user *, name, fh_flags |= EXPORT_FH_CONNECTABLE; lookup_flags = (flag & AT_SYMLINK_FOLLOW) ? LOOKUP_FOLLOW : 0; - if (flag & AT_EMPTY_PATH) - lookup_flags |= LOOKUP_EMPTY; - err = user_path_at(dfd, name, lookup_flags, &path); + CLASS(filename_uflags, filename)(name, flag); + err = filename_lookup(dfd, filename, lookup_flags, &path, NULL); if (!err) { err = do_sys_name_to_handle(&path, handle, mnt_id, flag & AT_HANDLE_MNT_ID_UNIQUE, diff --git a/fs/file_attr.c b/fs/file_attr.c index 13cdb31a3e94..42721427245a 100644 --- a/fs/file_attr.c +++ b/fs/file_attr.c @@ -374,7 +374,6 @@ SYSCALL_DEFINE5(file_getattr, int, dfd, const char __user *, filename, unsigned int, at_flags) { struct path filepath __free(path_put) = {}; - struct filename *name __free(putname) = NULL; unsigned int lookup_flags = 0; struct file_attr fattr; struct file_kattr fa; @@ -395,10 +394,7 @@ SYSCALL_DEFINE5(file_getattr, int, dfd, const char __user *, filename, if (usize < FILE_ATTR_SIZE_VER0) return -EINVAL; - name = getname_maybe_null(filename, at_flags); - if (IS_ERR(name)) - return PTR_ERR(name); - + CLASS(filename_maybe_null, name)(filename, at_flags); if (!name && dfd >= 0) { CLASS(fd, f)(dfd); if (fd_empty(f)) @@ -431,7 +427,6 @@ SYSCALL_DEFINE5(file_setattr, int, dfd, const char __user *, filename, unsigned int, at_flags) { struct path filepath __free(path_put) = {}; - struct filename *name __free(putname) = NULL; unsigned int lookup_flags = 0; struct file_attr fattr; struct file_kattr fa; @@ -461,10 +456,7 @@ SYSCALL_DEFINE5(file_setattr, int, dfd, const char __user *, filename, if (error) return error; - name = getname_maybe_null(filename, at_flags); - if (IS_ERR(name)) - return PTR_ERR(name); - + CLASS(filename_maybe_null, name)(filename, at_flags); if (!name && dfd >= 0) { CLASS(fd, f)(dfd); if (fd_empty(f)) diff --git a/fs/file_table.c b/fs/file_table.c index cd4a3db4659a..aaa5faaace1e 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -176,6 +176,11 @@ static int init_file(struct file *f, int flags, const struct cred *cred) f->f_flags = flags; f->f_mode = OPEN_FMODE(flags); + /* + * Disable permission and pre-content events for all files by default. + * They may be enabled later by fsnotify_open_perm_and_set_mode(). + */ + file_set_fsnotify_mode(f, FMODE_NONOTIFY_PERM); f->f_op = NULL; f->f_mapping = NULL; @@ -197,11 +202,6 @@ static int init_file(struct file *f, int flags, const struct cred *cred) * refcount bumps we should reinitialize the reused file first. */ file_ref_init(&f->f_ref, 1); - /* - * Disable permission and pre-content events for all files by default. - * They may be enabled later by fsnotify_open_perm_and_set_mode(). - */ - file_set_fsnotify_mode(f, FMODE_NONOTIFY_PERM); return 0; } @@ -308,6 +308,7 @@ struct file *alloc_empty_backing_file(int flags, const struct cred *cred) ff->file.f_mode |= FMODE_BACKING | FMODE_NOACCOUNT; return &ff->file; } +EXPORT_SYMBOL_GPL(alloc_empty_backing_file); /** * file_init_path - initialize a 'struct file' based on path diff --git a/fs/filesystems.c b/fs/filesystems.c index 95e5256821a5..0c7d2b7ac26c 100644 --- a/fs/filesystems.c +++ b/fs/filesystems.c @@ -132,24 +132,21 @@ EXPORT_SYMBOL(unregister_filesystem); static int fs_index(const char __user * __name) { struct file_system_type * tmp; - struct filename *name; + char *name __free(kfree) = strndup_user(__name, PATH_MAX); int err, index; - name = getname(__name); - err = PTR_ERR(name); if (IS_ERR(name)) - return err; + return PTR_ERR(name); err = -EINVAL; read_lock(&file_systems_lock); for (tmp=file_systems, index=0 ; tmp ; tmp=tmp->next, index++) { - if (strcmp(tmp->name, name->name) == 0) { + if (strcmp(tmp->name, name) == 0) { err = index; break; } } read_unlock(&file_systems_lock); - putname(name); return err; } diff --git a/fs/freevxfs/vxfs_lookup.c b/fs/freevxfs/vxfs_lookup.c index 1b0bca8b4cc6..138e08de976e 100644 --- a/fs/freevxfs/vxfs_lookup.c +++ b/fs/freevxfs/vxfs_lookup.c @@ -8,6 +8,7 @@ * Veritas filesystem driver - lookup and other directory related code. */ #include <linux/fs.h> +#include <linux/filelock.h> #include <linux/time.h> #include <linux/mm.h> #include <linux/highmem.h> @@ -36,6 +37,7 @@ const struct file_operations vxfs_dir_operations = { .llseek = generic_file_llseek, .read = generic_read_dir, .iterate_shared = vxfs_readdir, + .setlease = generic_setlease, }; diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 6800886c4d10..68228bf89b82 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -981,7 +981,7 @@ void wbc_account_cgroup_owner(struct writeback_control *wbc, struct folio *folio css = mem_cgroup_css_from_folio(folio); /* dead cgroups shouldn't contribute to inode ownership arbitration */ - if (!(css->flags & CSS_ONLINE)) + if (!css_is_online(css)) return; id = css->id; @@ -1711,6 +1711,19 @@ static void requeue_inode(struct inode *inode, struct bdi_writeback *wb, } } +bool sync_lazytime(struct inode *inode) +{ + if (!(inode_state_read_once(inode) & I_DIRTY_TIME)) + return false; + + trace_writeback_lazytime(inode); + if (inode->i_op->sync_lazytime) + inode->i_op->sync_lazytime(inode); + else + mark_inode_dirty_sync(inode); + return true; +} + /* * Write out an inode and its dirty pages (or some of its dirty pages, depending * on @wbc->nr_to_write), and clear the relevant dirty flags from i_state. @@ -1750,17 +1763,15 @@ __writeback_single_inode(struct inode *inode, struct writeback_control *wbc) } /* - * If the inode has dirty timestamps and we need to write them, call - * mark_inode_dirty_sync() to notify the filesystem about it and to - * change I_DIRTY_TIME into I_DIRTY_SYNC. + * For data integrity writeback, or when the dirty interval expired, + * ask the file system to propagata lazy timestamp updates into real + * dirty state. */ if ((inode_state_read_once(inode) & I_DIRTY_TIME) && (wbc->sync_mode == WB_SYNC_ALL || time_after(jiffies, inode->dirtied_time_when + - dirtytime_expire_interval * HZ))) { - trace_writeback_lazytime(inode); - mark_inode_dirty_sync(inode); - } + dirtytime_expire_interval * HZ))) + sync_lazytime(inode); /* * Get and clear the dirty flags from i_state. This needs to be done @@ -2492,7 +2503,9 @@ static void wakeup_dirtytime_writeback(struct work_struct *w) wb_wakeup(wb); } rcu_read_unlock(); - schedule_delayed_work(&dirtytime_work, dirtytime_expire_interval * HZ); + if (dirtytime_expire_interval) + schedule_delayed_work(&dirtytime_work, + round_jiffies_relative(dirtytime_expire_interval * HZ)); } static int dirtytime_interval_handler(const struct ctl_table *table, int write, @@ -2501,8 +2514,12 @@ static int dirtytime_interval_handler(const struct ctl_table *table, int write, int ret; ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos); - if (ret == 0 && write) - mod_delayed_work(system_percpu_wq, &dirtytime_work, 0); + if (ret == 0 && write) { + if (dirtytime_expire_interval) + mod_delayed_work(system_percpu_wq, &dirtytime_work, 0); + else + cancel_delayed_work_sync(&dirtytime_work); + } return ret; } @@ -2519,7 +2536,9 @@ static const struct ctl_table vm_fs_writeback_table[] = { static int __init start_dirtytime_writeback(void) { - schedule_delayed_work(&dirtytime_work, dirtytime_expire_interval * HZ); + if (dirtytime_expire_interval) + schedule_delayed_work(&dirtytime_work, + round_jiffies_relative(dirtytime_expire_interval * HZ)); register_sysctl_init("vm", vm_fs_writeback_table); return 0; } @@ -2561,6 +2580,8 @@ void __mark_inode_dirty(struct inode *inode, int flags) trace_writeback_mark_inode_dirty(inode, flags); if (flags & I_DIRTY_INODE) { + bool was_dirty_time = false; + /* * Inode timestamp update will piggback on this dirtying. * We tell ->dirty_inode callback that timestamps need to @@ -2571,6 +2592,7 @@ void __mark_inode_dirty(struct inode *inode, int flags) if (inode_state_read(inode) & I_DIRTY_TIME) { inode_state_clear(inode, I_DIRTY_TIME); flags |= I_DIRTY_TIME; + was_dirty_time = true; } spin_unlock(&inode->i_lock); } @@ -2583,9 +2605,12 @@ void __mark_inode_dirty(struct inode *inode, int flags) * for just I_DIRTY_PAGES or I_DIRTY_TIME. */ trace_writeback_dirty_inode_start(inode, flags); - if (sb->s_op->dirty_inode) + if (sb->s_op->dirty_inode) { sb->s_op->dirty_inode(inode, flags & (I_DIRTY_INODE | I_DIRTY_TIME)); + } else if (was_dirty_time && inode->i_op->sync_lazytime) { + inode->i_op->sync_lazytime(inode); + } trace_writeback_dirty_inode(inode, flags); /* I_DIRTY_INODE supersedes I_DIRTY_TIME. */ @@ -2750,8 +2775,13 @@ static void wait_sb_inodes(struct super_block *sb) * The mapping can appear untagged while still on-list since we * do not have the mapping lock. Skip it here, wb completion * will remove it. + * + * If the mapping does not have data integrity semantics, + * there's no need to wait for the writeout to complete, as the + * mapping cannot guarantee that data is persistently stored. */ - if (!mapping_tagged(mapping, PAGECACHE_TAG_WRITEBACK)) + if (!mapping_tagged(mapping, PAGECACHE_TAG_WRITEBACK) || + mapping_no_data_integrity(mapping)) continue; spin_unlock_irq(&sb->s_inode_wblist_lock); diff --git a/fs/fs_context.c b/fs/fs_context.c index 93b7ebf8d927..81ed94f46cac 100644 --- a/fs/fs_context.c +++ b/fs/fs_context.c @@ -24,20 +24,6 @@ #include "mount.h" #include "internal.h" -enum legacy_fs_param { - LEGACY_FS_UNSET_PARAMS, - LEGACY_FS_MONOLITHIC_PARAMS, - LEGACY_FS_INDIVIDUAL_PARAMS, -}; - -struct legacy_fs_context { - char *legacy_data; /* Data page for legacy filesystems */ - size_t data_size; - enum legacy_fs_param param_type; -}; - -static int legacy_init_fs_context(struct fs_context *fc); - static const struct constant_table common_set_sb_flag[] = { { "dirsync", SB_DIRSYNC }, { "lazytime", SB_LAZYTIME }, @@ -275,7 +261,6 @@ static struct fs_context *alloc_fs_context(struct file_system_type *fs_type, unsigned int sb_flags_mask, enum fs_context_purpose purpose) { - int (*init_fs_context)(struct fs_context *); struct fs_context *fc; int ret = -ENOMEM; @@ -307,12 +292,7 @@ static struct fs_context *alloc_fs_context(struct file_system_type *fs_type, break; } - /* TODO: Make all filesystems support this unconditionally */ - init_fs_context = fc->fs_type->init_fs_context; - if (!init_fs_context) - init_fs_context = legacy_init_fs_context; - - ret = init_fs_context(fc); + ret = fc->fs_type->init_fs_context(fc); if (ret < 0) goto err_fc; fc->need_free = true; @@ -376,8 +356,6 @@ void fc_drop_locked(struct fs_context *fc) deactivate_locked_super(sb); } -static void legacy_fs_context_free(struct fs_context *fc); - /** * vfs_dup_fs_context - Duplicate a filesystem context. * @src_fc: The context to copy. @@ -531,184 +509,6 @@ void put_fs_context(struct fs_context *fc) } EXPORT_SYMBOL(put_fs_context); -/* - * Free the config for a filesystem that doesn't support fs_context. - */ -static void legacy_fs_context_free(struct fs_context *fc) -{ - struct legacy_fs_context *ctx = fc->fs_private; - - if (ctx) { - if (ctx->param_type == LEGACY_FS_INDIVIDUAL_PARAMS) - kfree(ctx->legacy_data); - kfree(ctx); - } -} - -/* - * Duplicate a legacy config. - */ -static int legacy_fs_context_dup(struct fs_context *fc, struct fs_context *src_fc) -{ - struct legacy_fs_context *ctx; - struct legacy_fs_context *src_ctx = src_fc->fs_private; - - ctx = kmemdup(src_ctx, sizeof(*src_ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; - - if (ctx->param_type == LEGACY_FS_INDIVIDUAL_PARAMS) { - ctx->legacy_data = kmemdup(src_ctx->legacy_data, - src_ctx->data_size, GFP_KERNEL); - if (!ctx->legacy_data) { - kfree(ctx); - return -ENOMEM; - } - } - - fc->fs_private = ctx; - return 0; -} - -/* - * Add a parameter to a legacy config. We build up a comma-separated list of - * options. - */ -static int legacy_parse_param(struct fs_context *fc, struct fs_parameter *param) -{ - struct legacy_fs_context *ctx = fc->fs_private; - unsigned int size = ctx->data_size; - size_t len = 0; - int ret; - - ret = vfs_parse_fs_param_source(fc, param); - if (ret != -ENOPARAM) - return ret; - - if (ctx->param_type == LEGACY_FS_MONOLITHIC_PARAMS) - return invalf(fc, "VFS: Legacy: Can't mix monolithic and individual options"); - - switch (param->type) { - case fs_value_is_string: - len = 1 + param->size; - fallthrough; - case fs_value_is_flag: - len += strlen(param->key); - break; - default: - return invalf(fc, "VFS: Legacy: Parameter type for '%s' not supported", - param->key); - } - - if (size + len + 2 > PAGE_SIZE) - return invalf(fc, "VFS: Legacy: Cumulative options too large"); - if (strchr(param->key, ',') || - (param->type == fs_value_is_string && - memchr(param->string, ',', param->size))) - return invalf(fc, "VFS: Legacy: Option '%s' contained comma", - param->key); - if (!ctx->legacy_data) { - ctx->legacy_data = kmalloc(PAGE_SIZE, GFP_KERNEL); - if (!ctx->legacy_data) - return -ENOMEM; - } - - if (size) - ctx->legacy_data[size++] = ','; - len = strlen(param->key); - memcpy(ctx->legacy_data + size, param->key, len); - size += len; - if (param->type == fs_value_is_string) { - ctx->legacy_data[size++] = '='; - memcpy(ctx->legacy_data + size, param->string, param->size); - size += param->size; - } - ctx->legacy_data[size] = '\0'; - ctx->data_size = size; - ctx->param_type = LEGACY_FS_INDIVIDUAL_PARAMS; - return 0; -} - -/* - * Add monolithic mount data. - */ -static int legacy_parse_monolithic(struct fs_context *fc, void *data) -{ - struct legacy_fs_context *ctx = fc->fs_private; - - if (ctx->param_type != LEGACY_FS_UNSET_PARAMS) { - pr_warn("VFS: Can't mix monolithic and individual options\n"); - return -EINVAL; - } - - ctx->legacy_data = data; - ctx->param_type = LEGACY_FS_MONOLITHIC_PARAMS; - if (!ctx->legacy_data) - return 0; - - if (fc->fs_type->fs_flags & FS_BINARY_MOUNTDATA) - return 0; - return security_sb_eat_lsm_opts(ctx->legacy_data, &fc->security); -} - -/* - * Get a mountable root with the legacy mount command. - */ -static int legacy_get_tree(struct fs_context *fc) -{ - struct legacy_fs_context *ctx = fc->fs_private; - struct super_block *sb; - struct dentry *root; - - root = fc->fs_type->mount(fc->fs_type, fc->sb_flags, - fc->source, ctx->legacy_data); - if (IS_ERR(root)) - return PTR_ERR(root); - - sb = root->d_sb; - BUG_ON(!sb); - - fc->root = root; - return 0; -} - -/* - * Handle remount. - */ -static int legacy_reconfigure(struct fs_context *fc) -{ - struct legacy_fs_context *ctx = fc->fs_private; - struct super_block *sb = fc->root->d_sb; - - if (!sb->s_op->remount_fs) - return 0; - - return sb->s_op->remount_fs(sb, &fc->sb_flags, - ctx ? ctx->legacy_data : NULL); -} - -const struct fs_context_operations legacy_fs_context_ops = { - .free = legacy_fs_context_free, - .dup = legacy_fs_context_dup, - .parse_param = legacy_parse_param, - .parse_monolithic = legacy_parse_monolithic, - .get_tree = legacy_get_tree, - .reconfigure = legacy_reconfigure, -}; - -/* - * Initialise a legacy context for a filesystem that doesn't support - * fs_context. - */ -static int legacy_init_fs_context(struct fs_context *fc) -{ - fc->fs_private = kzalloc(sizeof(struct legacy_fs_context), GFP_KERNEL_ACCOUNT); - if (!fc->fs_private) - return -ENOMEM; - fc->ops = &legacy_fs_context_ops; - return 0; -} - int parse_monolithic_mount_data(struct fs_context *fc, void *data) { int (*monolithic_mount_data)(struct fs_context *, void *); @@ -757,10 +557,8 @@ int finish_clean_context(struct fs_context *fc) if (fc->phase != FS_CONTEXT_AWAITING_RECONF) return 0; - if (fc->fs_type->init_fs_context) - error = fc->fs_type->init_fs_context(fc); - else - error = legacy_init_fs_context(fc); + error = fc->fs_type->init_fs_context(fc); + if (unlikely(error)) { fc->phase = FS_CONTEXT_FAILED; return error; diff --git a/fs/fs_struct.c b/fs/fs_struct.c index b8c46c5a38a0..394875d06fd6 100644 --- a/fs/fs_struct.c +++ b/fs/fs_struct.c @@ -6,6 +6,7 @@ #include <linux/path.h> #include <linux/slab.h> #include <linux/fs_struct.h> +#include <linux/init_task.h> #include "internal.h" /* diff --git a/fs/fserror.c b/fs/fserror.c new file mode 100644 index 000000000000..06ca86adab9b --- /dev/null +++ b/fs/fserror.c @@ -0,0 +1,194 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2025 Oracle. All Rights Reserved. + * Author: Darrick J. Wong <djwong@kernel.org> + */ +#include <linux/fs.h> +#include <linux/fsnotify.h> +#include <linux/mempool.h> +#include <linux/fserror.h> + +#define FSERROR_DEFAULT_EVENT_POOL_SIZE (32) + +static struct mempool fserror_events_pool; + +void fserror_mount(struct super_block *sb) +{ + /* + * The pending error counter is biased by 1 so that we don't wake_var + * until we're actually trying to unmount. + */ + refcount_set(&sb->s_pending_errors, 1); +} + +void fserror_unmount(struct super_block *sb) +{ + /* + * If we don't drop the pending error count to zero, then wait for it + * to drop below 1, which means that the pending errors cleared and + * hopefully we didn't saturate with 1 billion+ concurrent events. + */ + if (!refcount_dec_and_test(&sb->s_pending_errors)) + wait_var_event(&sb->s_pending_errors, + refcount_read(&sb->s_pending_errors) < 1); +} + +static inline void fserror_pending_dec(struct super_block *sb) +{ + if (refcount_dec_and_test(&sb->s_pending_errors)) + wake_up_var(&sb->s_pending_errors); +} + +static inline void fserror_free_event(struct fserror_event *event) +{ + fserror_pending_dec(event->sb); + mempool_free(event, &fserror_events_pool); +} + +static void fserror_worker(struct work_struct *work) +{ + struct fserror_event *event = + container_of(work, struct fserror_event, work); + struct super_block *sb = event->sb; + + if (sb->s_flags & SB_ACTIVE) { + struct fs_error_report report = { + /* send positive error number to userspace */ + .error = -event->error, + .inode = event->inode, + .sb = event->sb, + }; + + if (sb->s_op->report_error) + sb->s_op->report_error(event); + + fsnotify(FS_ERROR, &report, FSNOTIFY_EVENT_ERROR, NULL, NULL, + NULL, 0); + } + + iput(event->inode); + fserror_free_event(event); +} + +static inline struct fserror_event *fserror_alloc_event(struct super_block *sb, + gfp_t gfp_flags) +{ + struct fserror_event *event = NULL; + + /* + * If pending_errors already reached zero or is no longer active, + * the superblock is being deactivated so there's no point in + * continuing. + * + * The order of the check of s_pending_errors and SB_ACTIVE are + * mandated by order of accesses in generic_shutdown_super and + * fserror_unmount. Barriers are implicitly provided by the refcount + * manipulations in this function and fserror_unmount. + */ + if (!refcount_inc_not_zero(&sb->s_pending_errors)) + return NULL; + if (!(sb->s_flags & SB_ACTIVE)) + goto out_pending; + + event = mempool_alloc(&fserror_events_pool, gfp_flags); + if (!event) + goto out_pending; + + /* mempool_alloc doesn't support GFP_ZERO */ + memset(event, 0, sizeof(*event)); + event->sb = sb; + INIT_WORK(&event->work, fserror_worker); + + return event; + +out_pending: + fserror_pending_dec(sb); + return NULL; +} + +/** + * fserror_report - report a filesystem error of some kind + * + * @sb: superblock of the filesystem + * @inode: inode within that filesystem, if applicable + * @type: type of error encountered + * @pos: start of inode range affected, if applicable + * @len: length of inode range affected, if applicable + * @error: error number encountered, must be negative + * @gfp: memory allocation flags for conveying the event to a worker, + * since this function can be called from atomic contexts + * + * Report details of a filesystem error to the super_operations::report_error + * callback if present; and to fsnotify for distribution to userspace. @sb, + * @gfp, @type, and @error must all be specified. For file I/O errors, the + * @inode, @pos, and @len fields must also be specified. For file metadata + * errors, @inode must be specified. If @inode is not NULL, then @inode->i_sb + * must point to @sb. + * + * Reporting work is deferred to a workqueue to ensure that ->report_error is + * called from process context without any locks held. An active reference to + * the inode is maintained until event handling is complete, and unmount will + * wait for queued events to drain. + */ +void fserror_report(struct super_block *sb, struct inode *inode, + enum fserror_type type, loff_t pos, u64 len, int error, + gfp_t gfp) +{ + struct fserror_event *event; + + /* sb and inode must be from the same filesystem */ + WARN_ON_ONCE(inode && inode->i_sb != sb); + + /* error number must be negative */ + WARN_ON_ONCE(error >= 0); + + event = fserror_alloc_event(sb, gfp); + if (!event) + goto lost; + + event->type = type; + event->pos = pos; + event->len = len; + event->error = error; + + /* + * Can't iput from non-sleeping context, so grabbing another reference + * to the inode must be the last thing before submitting the event. + */ + if (inode) { + event->inode = igrab(inode); + if (!event->inode) + goto lost_event; + } + + /* + * Use schedule_work here even if we're already in process context so + * that fsnotify and super_operations::report_error implementations are + * guaranteed to run in process context without any locks held. Since + * errors are supposed to be rare, the overhead shouldn't kill us any + * more than the failing device will. + */ + schedule_work(&event->work); + return; + +lost_event: + fserror_free_event(event); +lost: + if (inode) + pr_err_ratelimited( + "%s: lost file I/O error report for ino %lu type %u pos 0x%llx len 0x%llx error %d", + sb->s_id, inode->i_ino, type, pos, len, error); + else + pr_err_ratelimited( + "%s: lost filesystem error report for type %u error %d", + sb->s_id, type, error); +} +EXPORT_SYMBOL_GPL(fserror_report); + +static int __init fserror_init(void) +{ + return mempool_init_kmalloc_pool(&fserror_events_pool, + FSERROR_DEFAULT_EVENT_POOL_SIZE, + sizeof(struct fserror_event)); +} +fs_initcall(fserror_init); diff --git a/fs/fsopen.c b/fs/fsopen.c index f645c99204eb..614922623675 100644 --- a/fs/fsopen.c +++ b/fs/fsopen.c @@ -181,9 +181,9 @@ SYSCALL_DEFINE3(fspick, int, dfd, const char __user *, path, unsigned int, flags lookup_flags &= ~LOOKUP_FOLLOW; if (flags & FSPICK_NO_AUTOMOUNT) lookup_flags &= ~LOOKUP_AUTOMOUNT; - if (flags & FSPICK_EMPTY_PATH) - lookup_flags |= LOOKUP_EMPTY; - ret = user_path_at(dfd, path, lookup_flags, &target); + CLASS(filename_flags, filename)(path, + (flags & FSPICK_EMPTY_PATH) ? LOOKUP_EMPTY : 0); + ret = filename_lookup(dfd, filename, lookup_flags, &target, NULL); if (ret < 0) goto err; @@ -404,16 +404,6 @@ SYSCALL_DEFINE5(fsconfig, return -EINVAL; fc = fd_file(f)->private_data; - if (fc->ops == &legacy_fs_context_ops) { - switch (cmd) { - case FSCONFIG_SET_BINARY: - case FSCONFIG_SET_PATH: - case FSCONFIG_SET_PATH_EMPTY: - case FSCONFIG_SET_FD: - case FSCONFIG_CMD_CREATE_EXCL: - return -EOPNOTSUPP; - } - } if (_key) { param.key = strndup_user(_key, 256); diff --git a/fs/fuse/acl.c b/fs/fuse/acl.c index 8f484b105f13..cbde6ac1add3 100644 --- a/fs/fuse/acl.c +++ b/fs/fuse/acl.c @@ -122,20 +122,16 @@ int fuse_set_acl(struct mnt_idmap *idmap, struct dentry *dentry, * them to be refreshed the next time they are used, * and it also updates i_ctime. */ - size_t size = posix_acl_xattr_size(acl->a_count); + size_t size; void *value; - if (size > PAGE_SIZE) - return -E2BIG; - - value = kmalloc(size, GFP_KERNEL); + value = posix_acl_to_xattr(fc->user_ns, acl, &size, GFP_KERNEL); if (!value) return -ENOMEM; - ret = posix_acl_to_xattr(fc->user_ns, acl, value, size); - if (ret < 0) { + if (size > PAGE_SIZE) { kfree(value); - return ret; + return -E2BIG; } /* diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 6d59cbc877c6..a30c8b57d478 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -1813,7 +1813,7 @@ static int fuse_notify_store(struct fuse_conn *fc, unsigned int size, goto out_iput; folio_offset = ((index - folio->index) << PAGE_SHIFT) + offset; - nr_bytes = min_t(unsigned, num, folio_size(folio) - folio_offset); + nr_bytes = min(num, folio_size(folio) - folio_offset); nr_pages = (offset + nr_bytes + PAGE_SIZE - 1) >> PAGE_SHIFT; err = fuse_copy_folio(cs, &folio, folio_offset, nr_bytes, 0); diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 4b6b3d2758ff..f25ee47822ad 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -32,9 +32,9 @@ struct dentry_bucket { spinlock_t lock; }; -#define HASH_BITS 5 -#define HASH_SIZE (1 << HASH_BITS) -static struct dentry_bucket dentry_hash[HASH_SIZE]; +#define FUSE_HASH_BITS 5 +#define FUSE_HASH_SIZE (1 << FUSE_HASH_BITS) +static struct dentry_bucket dentry_hash[FUSE_HASH_SIZE]; struct delayed_work dentry_tree_work; /* Minimum invalidation work queue frequency */ @@ -83,7 +83,7 @@ MODULE_PARM_DESC(inval_wq, static inline struct dentry_bucket *get_dentry_bucket(struct dentry *dentry) { - int i = hash_ptr(dentry, HASH_BITS); + int i = hash_ptr(dentry, FUSE_HASH_BITS); return &dentry_hash[i]; } @@ -164,25 +164,31 @@ static void fuse_dentry_tree_work(struct work_struct *work) struct rb_node *node; int i; - for (i = 0; i < HASH_SIZE; i++) { + for (i = 0; i < FUSE_HASH_SIZE; i++) { spin_lock(&dentry_hash[i].lock); node = rb_first(&dentry_hash[i].tree); while (node) { fd = rb_entry(node, struct fuse_dentry, node); - if (time_after64(get_jiffies_64(), fd->time)) { - rb_erase(&fd->node, &dentry_hash[i].tree); - RB_CLEAR_NODE(&fd->node); + if (!time_before64(fd->time, get_jiffies_64())) + break; + + rb_erase(&fd->node, &dentry_hash[i].tree); + RB_CLEAR_NODE(&fd->node); + spin_lock(&fd->dentry->d_lock); + /* If dentry is still referenced, let next dput release it */ + fd->dentry->d_flags |= DCACHE_OP_DELETE; + spin_unlock(&fd->dentry->d_lock); + d_dispose_if_unused(fd->dentry, &dispose); + if (need_resched()) { spin_unlock(&dentry_hash[i].lock); - d_dispose_if_unused(fd->dentry, &dispose); cond_resched(); spin_lock(&dentry_hash[i].lock); - } else - break; + } node = rb_first(&dentry_hash[i].tree); } spin_unlock(&dentry_hash[i].lock); - shrink_dentry_list(&dispose); } + shrink_dentry_list(&dispose); if (inval_wq) schedule_delayed_work(&dentry_tree_work, @@ -213,7 +219,7 @@ void fuse_dentry_tree_init(void) { int i; - for (i = 0; i < HASH_SIZE; i++) { + for (i = 0; i < FUSE_HASH_SIZE; i++) { spin_lock_init(&dentry_hash[i].lock); dentry_hash[i].tree = RB_ROOT; } @@ -227,7 +233,7 @@ void fuse_dentry_tree_cleanup(void) inval_wq = 0; cancel_delayed_work_sync(&dentry_tree_work); - for (i = 0; i < HASH_SIZE; i++) + for (i = 0; i < FUSE_HASH_SIZE; i++) WARN_ON_ONCE(!RB_EMPTY_ROOT(&dentry_hash[i].tree)); } @@ -479,18 +485,12 @@ static int fuse_dentry_init(struct dentry *dentry) return 0; } -static void fuse_dentry_prune(struct dentry *dentry) +static void fuse_dentry_release(struct dentry *dentry) { struct fuse_dentry *fd = dentry->d_fsdata; if (!RB_EMPTY_NODE(&fd->node)) fuse_dentry_tree_del_node(dentry); -} - -static void fuse_dentry_release(struct dentry *dentry) -{ - struct fuse_dentry *fd = dentry->d_fsdata; - kfree_rcu(fd, rcu); } @@ -527,7 +527,6 @@ const struct dentry_operations fuse_dentry_operations = { .d_revalidate = fuse_dentry_revalidate, .d_delete = fuse_dentry_delete, .d_init = fuse_dentry_init, - .d_prune = fuse_dentry_prune, .d_release = fuse_dentry_release, .d_automount = fuse_dentry_automount, }; @@ -1584,8 +1583,8 @@ int fuse_reverse_inval_entry(struct fuse_conn *fc, u64 parent_nodeid, { int err = -ENOTDIR; struct inode *parent; - struct dentry *dir; - struct dentry *entry; + struct dentry *dir = NULL; + struct dentry *entry = NULL; parent = fuse_ilookup(fc, parent_nodeid, NULL); if (!parent) @@ -1598,11 +1597,19 @@ int fuse_reverse_inval_entry(struct fuse_conn *fc, u64 parent_nodeid, dir = d_find_alias(parent); if (!dir) goto put_parent; - - entry = start_removing_noperm(dir, name); - dput(dir); - if (IS_ERR(entry)) - goto put_parent; + while (!entry) { + struct dentry *child = try_lookup_noperm(name, dir); + if (!child || IS_ERR(child)) + goto put_parent; + entry = start_removing_dentry(dir, child); + dput(child); + if (IS_ERR(entry)) + goto put_parent; + if (!d_same_name(entry, dir, name)) { + end_removing(entry); + entry = NULL; + } + } fuse_dir_changed(parent); if (!(flags & FUSE_EXPIRE_ONLY)) @@ -1640,6 +1647,7 @@ int fuse_reverse_inval_entry(struct fuse_conn *fc, u64 parent_nodeid, end_removing(entry); put_parent: + dput(dir); iput(parent); return err; } @@ -2421,7 +2429,6 @@ static const struct file_operations fuse_dir_operations = { .fsync = fuse_dir_fsync, .unlocked_ioctl = fuse_dir_ioctl, .compat_ioctl = fuse_dir_compat_ioctl, - .setlease = simple_nosetlease, }; static const struct inode_operations fuse_common_inode_operations = { diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 01bc894e9c2b..80765ab6d04a 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -979,7 +979,7 @@ static int fuse_read_folio(struct file *file, struct folio *folio) return -EIO; } - iomap_read_folio(&fuse_iomap_ops, &ctx); + iomap_read_folio(&fuse_iomap_ops, &ctx, NULL); fuse_invalidate_atime(inode); return 0; } @@ -1081,7 +1081,7 @@ static void fuse_readahead(struct readahead_control *rac) if (fuse_is_bad(inode)) return; - iomap_readahead(&fuse_iomap_ops, &ctx); + iomap_readahead(&fuse_iomap_ops, &ctx, NULL); } static ssize_t fuse_cache_read_iter(struct kiocb *iocb, struct iov_iter *to) @@ -1323,10 +1323,10 @@ static ssize_t fuse_fill_write_pages(struct fuse_io_args *ia, static inline unsigned int fuse_wr_pages(loff_t pos, size_t len, unsigned int max_pages) { - return min_t(unsigned int, - ((pos + len - 1) >> PAGE_SHIFT) - - (pos >> PAGE_SHIFT) + 1, - max_pages); + unsigned int pages = ((pos + len - 1) >> PAGE_SHIFT) - + (pos >> PAGE_SHIFT) + 1; + + return min(pages, max_pages); } static ssize_t fuse_perform_write(struct kiocb *iocb, struct iov_iter *ii) @@ -1607,7 +1607,7 @@ static int fuse_get_user_pages(struct fuse_args_pages *ap, struct iov_iter *ii, struct folio *folio = page_folio(pages[i]); unsigned int offset = start + (folio_page_idx(folio, pages[i]) << PAGE_SHIFT); - unsigned int len = min_t(unsigned int, ret, PAGE_SIZE - start); + unsigned int len = umin(ret, PAGE_SIZE - start); ap->descs[ap->num_folios].offset = offset; ap->descs[ap->num_folios].length = len; @@ -3177,6 +3177,7 @@ static const struct file_operations fuse_file_operations = { .poll = fuse_file_poll, .fallocate = fuse_file_fallocate, .copy_file_range = fuse_copy_file_range, + .setlease = generic_setlease, }; static const struct address_space_operations fuse_file_aops = { @@ -3200,8 +3201,10 @@ void fuse_init_file_inode(struct inode *inode, unsigned int flags) inode->i_fop = &fuse_file_operations; inode->i_data.a_ops = &fuse_file_aops; - if (fc->writeback_cache) + if (fc->writeback_cache) { mapping_set_writeback_may_deadlock_on_reclaim(&inode->i_data); + mapping_set_no_data_integrity(&inode->i_data); + } INIT_LIST_HEAD(&fi->write_files); INIT_LIST_HEAD(&fi->queued_writes); diff --git a/fs/gfs2/acl.c b/fs/gfs2/acl.c index 443640e6fb9c..a5b60778b91c 100644 --- a/fs/gfs2/acl.c +++ b/fs/gfs2/acl.c @@ -83,21 +83,14 @@ struct posix_acl *gfs2_get_acl(struct inode *inode, int type, bool rcu) int __gfs2_set_acl(struct inode *inode, struct posix_acl *acl, int type) { int error; - size_t len; - char *data; + size_t len = 0; + char *data = NULL; const char *name = gfs2_acl_name(type); if (acl) { - len = posix_acl_xattr_size(acl->a_count); - data = kmalloc(len, GFP_NOFS); + data = posix_acl_to_xattr(&init_user_ns, acl, &len, GFP_NOFS); if (data == NULL) return -ENOMEM; - error = posix_acl_to_xattr(&init_user_ns, acl, data, len); - if (error < 0) - goto out; - } else { - data = NULL; - len = 0; } error = __gfs2_xattr_set(inode, name, data, len, 0, GFS2_EATYPE_SYS); diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c index 131091520de6..fdcac8e3f2ba 100644 --- a/fs/gfs2/bmap.c +++ b/fs/gfs2/bmap.c @@ -1127,10 +1127,18 @@ static int gfs2_iomap_begin(struct inode *inode, loff_t pos, loff_t length, goto out_unlock; break; default: - goto out_unlock; + goto out; } ret = gfs2_iomap_begin_write(inode, pos, length, flags, iomap, &mp); + if (ret) + goto out_unlock; + +out: + if (iomap->type == IOMAP_INLINE) { + iomap->private = metapath_dibh(&mp); + get_bh(iomap->private); + } out_unlock: release_metapath(&mp); @@ -1144,6 +1152,9 @@ static int gfs2_iomap_end(struct inode *inode, loff_t pos, loff_t length, struct gfs2_inode *ip = GFS2_I(inode); struct gfs2_sbd *sdp = GFS2_SB(inode); + if (iomap->private) + brelse(iomap->private); + switch (flags & (IOMAP_WRITE | IOMAP_ZERO)) { case IOMAP_WRITE: if (flags & IOMAP_DIRECT) diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c index b2d23c98c996..3e061e8115ec 100644 --- a/fs/gfs2/file.c +++ b/fs/gfs2/file.c @@ -1593,7 +1593,6 @@ const struct file_operations gfs2_file_fops = { .flock = gfs2_flock, .splice_read = copy_splice_read, .splice_write = gfs2_file_splice_write, - .setlease = simple_nosetlease, .fallocate = gfs2_fallocate, .fop_flags = FOP_ASYNC_LOCK, }; @@ -1638,5 +1637,6 @@ const struct file_operations gfs2_dir_fops_nolock = { .release = gfs2_release, .fsync = gfs2_fsync, .llseek = default_llseek, + .setlease = generic_setlease, }; diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c index 92e029104d8a..6fb2731e8be1 100644 --- a/fs/gfs2/glock.c +++ b/fs/gfs2/glock.c @@ -60,7 +60,8 @@ struct gfs2_glock_iter { typedef void (*glock_examiner) (struct gfs2_glock * gl); -static void do_xmote(struct gfs2_glock *gl, struct gfs2_holder *gh, unsigned int target); +static void do_xmote(struct gfs2_glock *gl, struct gfs2_holder *gh, + unsigned int target, bool may_cancel); static void request_demote(struct gfs2_glock *gl, unsigned int state, unsigned long delay, bool remote); @@ -146,7 +147,7 @@ static void __gfs2_glock_free(struct gfs2_glock *gl) } void gfs2_glock_free(struct gfs2_glock *gl) { - struct gfs2_sbd *sdp = gl->gl_name.ln_sbd; + struct gfs2_sbd *sdp = glock_sbd(gl); __gfs2_glock_free(gl); if (atomic_dec_and_test(&sdp->sd_glock_disposal)) @@ -154,7 +155,7 @@ void gfs2_glock_free(struct gfs2_glock *gl) { } void gfs2_glock_free_later(struct gfs2_glock *gl) { - struct gfs2_sbd *sdp = gl->gl_name.ln_sbd; + struct gfs2_sbd *sdp = glock_sbd(gl); spin_lock(&lru_lock); list_add(&gl->gl_lru, &sdp->sd_dead_glocks); @@ -184,8 +185,8 @@ static void gfs2_free_dead_glocks(struct gfs2_sbd *sdp) struct gfs2_glock *gfs2_glock_hold(struct gfs2_glock *gl) { - GLOCK_BUG_ON(gl, __lockref_is_dead(&gl->gl_lockref)); - lockref_get(&gl->gl_lockref); + if (!lockref_get_not_dead(&gl->gl_lockref)) + GLOCK_BUG_ON(gl, 1); return gl; } @@ -218,7 +219,7 @@ static void gfs2_glock_remove_from_lru(struct gfs2_glock *gl) * work queue. */ static void gfs2_glock_queue_work(struct gfs2_glock *gl, unsigned long delay) { - struct gfs2_sbd *sdp = gl->gl_name.ln_sbd; + struct gfs2_sbd *sdp = glock_sbd(gl); if (!queue_delayed_work(sdp->sd_glock_wq, &gl->gl_work, delay)) { /* @@ -234,7 +235,7 @@ static void gfs2_glock_queue_work(struct gfs2_glock *gl, unsigned long delay) { static void __gfs2_glock_put(struct gfs2_glock *gl) { - struct gfs2_sbd *sdp = gl->gl_name.ln_sbd; + struct gfs2_sbd *sdp = glock_sbd(gl); struct address_space *mapping = gfs2_glock2aspace(gl); lockref_mark_dead(&gl->gl_lockref); @@ -356,7 +357,7 @@ static void gfs2_holder_wake(struct gfs2_holder *gh) smp_mb__after_atomic(); wake_up_bit(&gh->gh_iflags, HIF_WAIT); if (gh->gh_flags & GL_ASYNC) { - struct gfs2_sbd *sdp = gh->gh_gl->gl_name.ln_sbd; + struct gfs2_sbd *sdp = glock_sbd(gh->gh_gl); wake_up(&sdp->sd_async_glock_wait); } @@ -458,7 +459,7 @@ done: static void do_promote(struct gfs2_glock *gl) { - struct gfs2_sbd *sdp = gl->gl_name.ln_sbd; + struct gfs2_sbd *sdp = glock_sbd(gl); struct gfs2_holder *gh, *current_gh; if (gfs2_withdrawn(sdp)) { @@ -538,7 +539,7 @@ static void state_change(struct gfs2_glock *gl, unsigned int new_state) static void gfs2_set_demote(int nr, struct gfs2_glock *gl) { - struct gfs2_sbd *sdp = gl->gl_name.ln_sbd; + struct gfs2_sbd *sdp = glock_sbd(gl); set_bit(nr, &gl->gl_flags); smp_mb(); @@ -600,17 +601,19 @@ static void finish_xmote(struct gfs2_glock *gl, unsigned int ret) switch(gl->gl_state) { /* Unlocked due to conversion deadlock, try again */ case LM_ST_UNLOCKED: - do_xmote(gl, gh, gl->gl_target); + do_xmote(gl, gh, gl->gl_target, + !test_bit(GLF_DEMOTE_IN_PROGRESS, + &gl->gl_flags)); break; /* Conversion fails, unlock and try again */ case LM_ST_SHARED: case LM_ST_DEFERRED: - do_xmote(gl, gh, LM_ST_UNLOCKED); + do_xmote(gl, gh, LM_ST_UNLOCKED, false); break; default: /* Everything else */ - fs_err(gl->gl_name.ln_sbd, + fs_err(glock_sbd(gl), "glock %u:%llu requested=%u ret=%u\n", - gl->gl_name.ln_type, gl->gl_name.ln_number, + glock_type(gl), glock_number(gl), gl->gl_req, ret); GLOCK_BUG_ON(gl, 1); } @@ -638,7 +641,7 @@ static void finish_xmote(struct gfs2_glock *gl, unsigned int ret) } out: if (!test_bit(GLF_CANCELING, &gl->gl_flags)) - clear_bit(GLF_LOCK, &gl->gl_flags); + clear_and_wake_up_bit(GLF_LOCK, &gl->gl_flags); } /** @@ -646,16 +649,17 @@ out: * @gl: The lock state * @gh: The holder (only for promotes) * @target: The target lock state + * @may_cancel: Operation may be canceled * */ static void do_xmote(struct gfs2_glock *gl, struct gfs2_holder *gh, - unsigned int target) + unsigned int target, bool may_cancel) __releases(&gl->gl_lockref.lock) __acquires(&gl->gl_lockref.lock) { const struct gfs2_glock_operations *glops = gl->gl_ops; - struct gfs2_sbd *sdp = gl->gl_name.ln_sbd; + struct gfs2_sbd *sdp = glock_sbd(gl); struct lm_lockstruct *ls = &sdp->sd_lockstruct; int ret; @@ -703,17 +707,20 @@ skip_inval: } if (ls->ls_ops->lm_lock) { - set_bit(GLF_PENDING_REPLY, &gl->gl_flags); spin_unlock(&gl->gl_lockref.lock); ret = ls->ls_ops->lm_lock(gl, target, gh ? gh->gh_flags : 0); spin_lock(&gl->gl_lockref.lock); if (!ret) { + if (may_cancel) { + set_bit(GLF_MAY_CANCEL, &gl->gl_flags); + smp_mb__after_atomic(); + wake_up_bit(&gl->gl_flags, GLF_LOCK); + } /* The operation will be completed asynchronously. */ gl->gl_lockref.count++; return; } - clear_bit(GLF_PENDING_REPLY, &gl->gl_flags); if (ret == -ENODEV) { /* @@ -753,13 +760,10 @@ __acquires(&gl->gl_lockref.lock) if (test_bit(GLF_LOCK, &gl->gl_flags)) return; - set_bit(GLF_LOCK, &gl->gl_flags); /* - * The GLF_DEMOTE_IN_PROGRESS flag is only set intermittently during - * locking operations. We have just started a locking operation by - * setting the GLF_LOCK flag, so the GLF_DEMOTE_IN_PROGRESS flag must - * be cleared. + * The GLF_DEMOTE_IN_PROGRESS flag must only be set when the GLF_LOCK + * flag is set as well. */ GLOCK_BUG_ON(gl, test_bit(GLF_DEMOTE_IN_PROGRESS, &gl->gl_flags)); @@ -770,39 +774,36 @@ __acquires(&gl->gl_lockref.lock) } if (find_first_holder(gl)) - goto out_unlock; + return; if (nonblock) goto out_sched; set_bit(GLF_DEMOTE_IN_PROGRESS, &gl->gl_flags); GLOCK_BUG_ON(gl, gl->gl_demote_state == LM_ST_EXCLUSIVE); gl->gl_target = gl->gl_demote_state; - do_xmote(gl, NULL, gl->gl_target); + set_bit(GLF_LOCK, &gl->gl_flags); + do_xmote(gl, NULL, gl->gl_target, false); return; } promote: do_promote(gl); if (find_first_holder(gl)) - goto out_unlock; + return; gh = find_first_waiter(gl); if (!gh) - goto out_unlock; + return; if (nonblock) goto out_sched; gl->gl_target = gh->gh_state; if (!(gh->gh_flags & (LM_FLAG_TRY | LM_FLAG_TRY_1CB))) do_error(gl, 0); /* Fail queued try locks */ - do_xmote(gl, gh, gl->gl_target); + set_bit(GLF_LOCK, &gl->gl_flags); + do_xmote(gl, gh, gl->gl_target, true); return; out_sched: - clear_bit(GLF_LOCK, &gl->gl_flags); gl->gl_lockref.count++; gfs2_glock_queue_work(gl, 0); - return; - -out_unlock: - clear_bit(GLF_LOCK, &gl->gl_flags); } /** @@ -818,7 +819,7 @@ void glock_set_object(struct gfs2_glock *gl, void *object) prev_object = gl->gl_object; gl->gl_object = object; spin_unlock(&gl->gl_lockref.lock); - if (gfs2_assert_warn(gl->gl_name.ln_sbd, prev_object == NULL)) + if (gfs2_assert_warn(glock_sbd(gl), prev_object == NULL)) gfs2_dump_glock(NULL, gl, true); } @@ -835,7 +836,7 @@ void glock_clear_object(struct gfs2_glock *gl, void *object) prev_object = gl->gl_object; gl->gl_object = NULL; spin_unlock(&gl->gl_lockref.lock); - if (gfs2_assert_warn(gl->gl_name.ln_sbd, prev_object == object)) + if (gfs2_assert_warn(glock_sbd(gl), prev_object == object)) gfs2_dump_glock(NULL, gl, true); } @@ -925,7 +926,7 @@ static void gfs2_try_to_evict(struct gfs2_glock *gl) bool gfs2_queue_try_to_evict(struct gfs2_glock *gl) { - struct gfs2_sbd *sdp = gl->gl_name.ln_sbd; + struct gfs2_sbd *sdp = glock_sbd(gl); if (test_and_set_bit(GLF_TRY_TO_EVICT, &gl->gl_flags)) return false; @@ -934,7 +935,7 @@ bool gfs2_queue_try_to_evict(struct gfs2_glock *gl) bool gfs2_queue_verify_delete(struct gfs2_glock *gl, bool later) { - struct gfs2_sbd *sdp = gl->gl_name.ln_sbd; + struct gfs2_sbd *sdp = glock_sbd(gl); unsigned long delay; if (test_and_set_bit(GLF_VERIFY_DELETE, &gl->gl_flags)) @@ -947,7 +948,7 @@ static void delete_work_func(struct work_struct *work) { struct delayed_work *dwork = to_delayed_work(work); struct gfs2_glock *gl = container_of(dwork, struct gfs2_glock, gl_delete); - struct gfs2_sbd *sdp = gl->gl_name.ln_sbd; + struct gfs2_sbd *sdp = glock_sbd(gl); bool verify_delete = test_and_clear_bit(GLF_VERIFY_DELETE, &gl->gl_flags); /* @@ -960,7 +961,7 @@ static void delete_work_func(struct work_struct *work) gfs2_try_to_evict(gl); if (verify_delete) { - u64 no_addr = gl->gl_name.ln_number; + u64 no_addr = glock_number(gl); struct inode *inode; inode = gfs2_lookup_by_inum(sdp, no_addr, gl->gl_no_formal_ino, @@ -994,7 +995,7 @@ static void glock_work_func(struct work_struct *work) if (test_bit(GLF_PENDING_DEMOTE, &gl->gl_flags) && gl->gl_state != LM_ST_UNLOCKED && gl->gl_demote_state != LM_ST_EXCLUSIVE) { - if (gl->gl_name.ln_type == LM_TYPE_INODE) { + if (glock_type(gl) == LM_TYPE_INODE) { unsigned long holdtime, now = jiffies; holdtime = gl->gl_tchange + gl->gl_hold_time; @@ -1136,7 +1137,7 @@ int gfs2_glock_get(struct gfs2_sbd *sdp, u64 number, gl->gl_object = NULL; gl->gl_hold_time = GL_GLOCK_DFT_HOLD; INIT_DELAYED_WORK(&gl->gl_work, glock_work_func); - if (gl->gl_name.ln_type == LM_TYPE_IOPEN) + if (glock_type(gl) == LM_TYPE_IOPEN) INIT_DELAYED_WORK(&gl->gl_delete, delete_work_func); mapping = gfs2_glock2aspace(gl); @@ -1284,31 +1285,45 @@ static int glocks_pending(unsigned int num_gh, struct gfs2_holder *ghs) * gfs2_glock_async_wait - wait on multiple asynchronous glock acquisitions * @num_gh: the number of holders in the array * @ghs: the glock holder array + * @retries: number of retries attempted so far * * Returns: 0 on success, meaning all glocks have been granted and are held. * -ESTALE if the request timed out, meaning all glocks were released, * and the caller should retry the operation. */ -int gfs2_glock_async_wait(unsigned int num_gh, struct gfs2_holder *ghs) +int gfs2_glock_async_wait(unsigned int num_gh, struct gfs2_holder *ghs, + unsigned int retries) { - struct gfs2_sbd *sdp = ghs[0].gh_gl->gl_name.ln_sbd; - int i, ret = 0, timeout = 0; + struct gfs2_sbd *sdp = glock_sbd(ghs[0].gh_gl); unsigned long start_time = jiffies; + int i, ret = 0; + long timeout; might_sleep(); - /* - * Total up the (minimum hold time * 2) of all glocks and use that to - * determine the max amount of time we should wait. - */ - for (i = 0; i < num_gh; i++) - timeout += ghs[i].gh_gl->gl_hold_time << 1; - if (!wait_event_timeout(sdp->sd_async_glock_wait, + timeout = GL_GLOCK_MIN_HOLD; + if (retries) { + unsigned int max_shift; + long incr; + + /* Add a random delay and increase the timeout exponentially. */ + max_shift = BITS_PER_LONG - 2 - __fls(GL_GLOCK_HOLD_INCR); + incr = min(GL_GLOCK_HOLD_INCR << min(retries - 1, max_shift), + 10 * HZ - GL_GLOCK_MIN_HOLD); + schedule_timeout_interruptible(get_random_long() % (incr / 3)); + if (signal_pending(current)) + goto interrupted; + timeout += (incr / 3) + get_random_long() % (incr / 3); + } + + if (!wait_event_interruptible_timeout(sdp->sd_async_glock_wait, !glocks_pending(num_gh, ghs), timeout)) { ret = -ESTALE; /* request timed out. */ goto out; } + if (signal_pending(current)) + goto interrupted; for (i = 0; i < num_gh; i++) { struct gfs2_holder *gh = &ghs[i]; @@ -1332,6 +1347,10 @@ out: } } return ret; + +interrupted: + ret = -EINTR; + goto out; } /** @@ -1418,7 +1437,7 @@ static inline bool pid_is_meaningful(const struct gfs2_holder *gh) static inline void add_to_queue(struct gfs2_holder *gh) { struct gfs2_glock *gl = gh->gh_gl; - struct gfs2_sbd *sdp = gl->gl_name.ln_sbd; + struct gfs2_sbd *sdp = glock_sbd(gl); struct gfs2_holder *gh2; GLOCK_BUG_ON(gl, gh->gh_owner_pid == NULL); @@ -1451,11 +1470,11 @@ trap_recursive: fs_err(sdp, "original: %pSR\n", (void *)gh2->gh_ip); fs_err(sdp, "pid: %d\n", pid_nr(gh2->gh_owner_pid)); fs_err(sdp, "lock type: %d req lock state : %d\n", - gh2->gh_gl->gl_name.ln_type, gh2->gh_state); + glock_type(gh2->gh_gl), gh2->gh_state); fs_err(sdp, "new: %pSR\n", (void *)gh->gh_ip); fs_err(sdp, "pid: %d\n", pid_nr(gh->gh_owner_pid)); fs_err(sdp, "lock type: %d req lock state : %d\n", - gh->gh_gl->gl_name.ln_type, gh->gh_state); + glock_type(gh->gh_gl), gh->gh_state); gfs2_dump_glock(NULL, gl, true); BUG(); } @@ -1472,7 +1491,7 @@ trap_recursive: int gfs2_glock_nq(struct gfs2_holder *gh) { struct gfs2_glock *gl = gh->gh_gl; - struct gfs2_sbd *sdp = gl->gl_name.ln_sbd; + struct gfs2_sbd *sdp = glock_sbd(gl); int error; if (gfs2_withdrawn(sdp)) @@ -1545,6 +1564,8 @@ static void __gfs2_glock_dq(struct gfs2_holder *gh) list_del_init(&gh->gh_list); clear_bit(HIF_HOLDER, &gh->gh_iflags); trace_gfs2_glock_queue(gh, 0); + if (test_bit(HIF_WAIT, &gh->gh_iflags)) + gfs2_holder_wake(gh); /* * If there hasn't been a demote request we are done. @@ -1559,7 +1580,7 @@ static void __gfs2_glock_dq(struct gfs2_holder *gh) gl->gl_lockref.count++; if (test_bit(GLF_PENDING_DEMOTE, &gl->gl_flags) && !test_bit(GLF_DEMOTE, &gl->gl_flags) && - gl->gl_name.ln_type == LM_TYPE_INODE) + glock_type(gl) == LM_TYPE_INODE) delay = gl->gl_hold_time; gfs2_glock_queue_work(gl, delay); } @@ -1574,6 +1595,7 @@ void gfs2_glock_dq(struct gfs2_holder *gh) { struct gfs2_glock *gl = gh->gh_gl; +again: spin_lock(&gl->gl_lockref.lock); if (!gfs2_holder_queued(gh)) { /* @@ -1588,13 +1610,25 @@ void gfs2_glock_dq(struct gfs2_holder *gh) test_bit(GLF_LOCK, &gl->gl_flags) && !test_bit(GLF_DEMOTE_IN_PROGRESS, &gl->gl_flags) && !test_bit(GLF_CANCELING, &gl->gl_flags)) { + if (!test_bit(GLF_MAY_CANCEL, &gl->gl_flags)) { + struct wait_queue_head *wq; + DEFINE_WAIT(wait); + + wq = bit_waitqueue(&gl->gl_flags, GLF_LOCK); + prepare_to_wait(wq, &wait, TASK_UNINTERRUPTIBLE); + spin_unlock(&gl->gl_lockref.lock); + schedule(); + finish_wait(wq, &wait); + goto again; + } + set_bit(GLF_CANCELING, &gl->gl_flags); spin_unlock(&gl->gl_lockref.lock); - gl->gl_name.ln_sbd->sd_lockstruct.ls_ops->lm_cancel(gl); + glock_sbd(gl)->sd_lockstruct.ls_ops->lm_cancel(gl); wait_on_bit(&gh->gh_iflags, HIF_WAIT, TASK_UNINTERRUPTIBLE); spin_lock(&gl->gl_lockref.lock); clear_bit(GLF_CANCELING, &gl->gl_flags); - clear_bit(GLF_LOCK, &gl->gl_flags); + clear_and_wake_up_bit(GLF_LOCK, &gl->gl_flags); if (!gfs2_holder_queued(gh)) goto out; } @@ -1764,7 +1798,7 @@ void gfs2_glock_cb(struct gfs2_glock *gl, unsigned int state) gfs2_glock_hold(gl); spin_lock(&gl->gl_lockref.lock); if (!list_empty(&gl->gl_holders) && - gl->gl_name.ln_type == LM_TYPE_INODE) { + glock_type(gl) == LM_TYPE_INODE) { unsigned long now = jiffies; unsigned long holdtime; @@ -1821,10 +1855,10 @@ static int gfs2_should_freeze(const struct gfs2_glock *gl) void gfs2_glock_complete(struct gfs2_glock *gl, int ret) { - struct lm_lockstruct *ls = &gl->gl_name.ln_sbd->sd_lockstruct; + struct lm_lockstruct *ls = &glock_sbd(gl)->sd_lockstruct; spin_lock(&gl->gl_lockref.lock); - clear_bit(GLF_PENDING_REPLY, &gl->gl_flags); + clear_bit(GLF_MAY_CANCEL, &gl->gl_flags); gl->gl_reply = ret; if (unlikely(test_bit(DFL_BLOCK_LOCKS, &ls->ls_recover_flags))) { @@ -1849,9 +1883,9 @@ static int glock_cmp(void *priv, const struct list_head *a, gla = list_entry(a, struct gfs2_glock, gl_lru); glb = list_entry(b, struct gfs2_glock, gl_lru); - if (gla->gl_name.ln_number > glb->gl_name.ln_number) + if (glock_number(gla) > glock_number(glb)) return 1; - if (gla->gl_name.ln_number < glb->gl_name.ln_number) + if (glock_number(gla) < glock_number(glb)) return -1; return 0; @@ -1859,7 +1893,7 @@ static int glock_cmp(void *priv, const struct list_head *a, static bool can_free_glock(struct gfs2_glock *gl) { - struct gfs2_sbd *sdp = gl->gl_name.ln_sbd; + struct gfs2_sbd *sdp = glock_sbd(gl); return !test_bit(GLF_LOCK, &gl->gl_flags) && !gl->gl_lockref.count && @@ -1981,7 +2015,7 @@ static void glock_hash_walk(glock_examiner examiner, const struct gfs2_sbd *sdp) rhashtable_walk_start(&iter); while ((gl = rhashtable_walk_next(&iter)) && !IS_ERR(gl)) { - if (gl->gl_name.ln_sbd == sdp) + if (glock_sbd(gl) == sdp) examiner(gl); } @@ -2001,8 +2035,8 @@ void gfs2_cancel_delete_work(struct gfs2_glock *gl) static void flush_delete_work(struct gfs2_glock *gl) { - if (gl->gl_name.ln_type == LM_TYPE_IOPEN) { - struct gfs2_sbd *sdp = gl->gl_name.ln_sbd; + if (glock_type(gl) == LM_TYPE_IOPEN) { + struct gfs2_sbd *sdp = glock_sbd(gl); if (cancel_delayed_work(&gl->gl_delete)) { queue_delayed_work(sdp->sd_delete_wq, @@ -2231,8 +2265,8 @@ static const char *gflags2str(char *buf, const struct gfs2_glock *gl) *p++ = 'y'; if (test_bit(GLF_LFLUSH, gflags)) *p++ = 'f'; - if (test_bit(GLF_PENDING_REPLY, gflags)) - *p++ = 'R'; + if (test_bit(GLF_MAY_CANCEL, gflags)) + *p++ = 'c'; if (test_bit(GLF_HAVE_REPLY, gflags)) *p++ = 'r'; if (test_bit(GLF_INITIAL, gflags)) @@ -2287,7 +2321,7 @@ void gfs2_dump_glock(struct seq_file *seq, struct gfs2_glock *gl, bool fsid) unsigned long long dtime; const struct gfs2_holder *gh; char gflags_buf[32]; - struct gfs2_sbd *sdp = gl->gl_name.ln_sbd; + struct gfs2_sbd *sdp = glock_sbd(gl); char fs_id_buf[sizeof(sdp->sd_fsname) + 7]; unsigned long nrpages = 0; @@ -2306,8 +2340,8 @@ void gfs2_dump_glock(struct seq_file *seq, struct gfs2_glock *gl, bool fsid) gfs2_print_dbg(seq, "%sG: s:%s n:%u/%llx f:%s t:%s d:%s/%llu a:%d " "v:%d r:%d m:%ld p:%lu\n", fs_id_buf, state2str(gl->gl_state), - gl->gl_name.ln_type, - (unsigned long long)gl->gl_name.ln_number, + glock_type(gl), + (unsigned long long) glock_number(gl), gflags2str(gflags_buf, gl), state2str(gl->gl_target), state2str(gl->gl_demote_state), dtime, @@ -2327,8 +2361,8 @@ static int gfs2_glstats_seq_show(struct seq_file *seq, void *iter_ptr) struct gfs2_glock *gl = iter_ptr; seq_printf(seq, "G: n:%u/%llx rtt:%llu/%llu rttb:%llu/%llu irt:%llu/%llu dcnt: %llu qcnt: %llu\n", - gl->gl_name.ln_type, - (unsigned long long)gl->gl_name.ln_number, + glock_type(gl), + (unsigned long long) glock_number(gl), (unsigned long long)gl->gl_stats.stats[GFS2_LKS_SRTT], (unsigned long long)gl->gl_stats.stats[GFS2_LKS_SRTTVAR], (unsigned long long)gl->gl_stats.stats[GFS2_LKS_SRTTB], @@ -2444,7 +2478,7 @@ static void gfs2_glock_iter_next(struct gfs2_glock_iter *gi, loff_t n) gl = NULL; break; } - if (gl->gl_name.ln_sbd != gi->sdp) + if (glock_sbd(gl) != gi->sdp) continue; if (n <= 1) { if (!lockref_get_not_dead(&gl->gl_lockref)) @@ -2740,8 +2774,8 @@ static int gfs2_glockfd_seq_show(struct seq_file *seq, void *iter_ptr) gl = GFS2_I(inode)->i_iopen_gh.gh_gl; if (gl) { seq_printf(seq, "%d %u %u/%llx\n", - i->tgid, i->fd, gl->gl_name.ln_type, - (unsigned long long)gl->gl_name.ln_number); + i->tgid, i->fd, glock_type(gl), + (unsigned long long) glock_number(gl)); } gfs2_glockfd_seq_show_flock(seq, i); inode_unlock_shared(inode); diff --git a/fs/gfs2/glock.h b/fs/gfs2/glock.h index 55d5985f32a0..6341ac9b863f 100644 --- a/fs/gfs2/glock.h +++ b/fs/gfs2/glock.h @@ -126,7 +126,7 @@ enum { #define GL_GLOCK_MAX_HOLD (long)(HZ / 5) #define GL_GLOCK_DFT_HOLD (long)(HZ / 5) -#define GL_GLOCK_MIN_HOLD (long)(10) +#define GL_GLOCK_MIN_HOLD (long)(HZ / 100) #define GL_GLOCK_HOLD_INCR (long)(HZ / 20) #define GL_GLOCK_HOLD_DECR (long)(HZ / 40) @@ -204,7 +204,8 @@ int gfs2_glock_poll(struct gfs2_holder *gh); int gfs2_instantiate(struct gfs2_holder *gh); int gfs2_glock_holder_ready(struct gfs2_holder *gh); int gfs2_glock_wait(struct gfs2_holder *gh); -int gfs2_glock_async_wait(unsigned int num_gh, struct gfs2_holder *ghs); +int gfs2_glock_async_wait(unsigned int num_gh, struct gfs2_holder *ghs, + unsigned int retries); void gfs2_glock_dq(struct gfs2_holder *gh); void gfs2_glock_dq_wait(struct gfs2_holder *gh); void gfs2_glock_dq_uninit(struct gfs2_holder *gh); @@ -221,11 +222,11 @@ void gfs2_dump_glock(struct seq_file *seq, struct gfs2_glock *gl, BUG(); } } while(0) #define gfs2_glock_assert_warn(gl, x) do { if (unlikely(!(x))) { \ gfs2_dump_glock(NULL, gl, true); \ - gfs2_assert_warn((gl)->gl_name.ln_sbd, (x)); } } \ + gfs2_assert_warn(glock_sbd(gl), (x)); } } \ while (0) #define gfs2_glock_assert_withdraw(gl, x) do { if (unlikely(!(x))) { \ gfs2_dump_glock(NULL, gl, true); \ - gfs2_assert_withdraw((gl)->gl_name.ln_sbd, (x)); } } \ + gfs2_assert_withdraw(glock_sbd(gl), (x)); } } \ while (0) __printf(2, 3) diff --git a/fs/gfs2/glops.c b/fs/gfs2/glops.c index 2173ccf5034b..ba61649368bf 100644 --- a/fs/gfs2/glops.c +++ b/fs/gfs2/glops.c @@ -32,7 +32,7 @@ struct workqueue_struct *gfs2_freeze_wq; static void gfs2_ail_error(struct gfs2_glock *gl, const struct buffer_head *bh) { - struct gfs2_sbd *sdp = gl->gl_name.ln_sbd; + struct gfs2_sbd *sdp = glock_sbd(gl); fs_err(sdp, "AIL buffer %p: blocknr %llu state 0x%08lx mapping %p page " @@ -40,7 +40,7 @@ static void gfs2_ail_error(struct gfs2_glock *gl, const struct buffer_head *bh) bh, (unsigned long long)bh->b_blocknr, bh->b_state, bh->b_folio->mapping, bh->b_folio->flags.f); fs_err(sdp, "AIL glock %u:%llu mapping %p\n", - gl->gl_name.ln_type, gl->gl_name.ln_number, + glock_type(gl), glock_number(gl), gfs2_glock2aspace(gl)); gfs2_lm(sdp, "AIL error\n"); gfs2_withdraw(sdp); @@ -58,7 +58,7 @@ static void gfs2_ail_error(struct gfs2_glock *gl, const struct buffer_head *bh) static void __gfs2_ail_flush(struct gfs2_glock *gl, bool fsync, unsigned int nr_revokes) { - struct gfs2_sbd *sdp = gl->gl_name.ln_sbd; + struct gfs2_sbd *sdp = glock_sbd(gl); struct list_head *head = &gl->gl_ail_list; struct gfs2_bufdata *bd, *tmp; struct buffer_head *bh; @@ -86,7 +86,7 @@ static void __gfs2_ail_flush(struct gfs2_glock *gl, bool fsync, static int gfs2_ail_empty_gl(struct gfs2_glock *gl) { - struct gfs2_sbd *sdp = gl->gl_name.ln_sbd; + struct gfs2_sbd *sdp = glock_sbd(gl); struct gfs2_trans tr; unsigned int revokes; int ret = 0; @@ -139,7 +139,7 @@ flush: void gfs2_ail_flush(struct gfs2_glock *gl, bool fsync) { - struct gfs2_sbd *sdp = gl->gl_name.ln_sbd; + struct gfs2_sbd *sdp = glock_sbd(gl); unsigned int revokes = atomic_read(&gl->gl_ail_count); int ret; @@ -163,7 +163,7 @@ void gfs2_ail_flush(struct gfs2_glock *gl, bool fsync) static int gfs2_rgrp_metasync(struct gfs2_glock *gl) { - struct gfs2_sbd *sdp = gl->gl_name.ln_sbd; + struct gfs2_sbd *sdp = glock_sbd(gl); struct address_space *metamapping = gfs2_aspace(sdp); struct gfs2_rgrpd *rgd = gfs2_glock2rgrp(gl); const unsigned bsize = sdp->sd_sb.sb_bsize; @@ -191,7 +191,7 @@ static int gfs2_rgrp_metasync(struct gfs2_glock *gl) static int rgrp_go_sync(struct gfs2_glock *gl) { - struct gfs2_sbd *sdp = gl->gl_name.ln_sbd; + struct gfs2_sbd *sdp = glock_sbd(gl); struct gfs2_rgrpd *rgd = gfs2_glock2rgrp(gl); int error; @@ -220,7 +220,7 @@ static int rgrp_go_sync(struct gfs2_glock *gl) static void rgrp_go_inval(struct gfs2_glock *gl, int flags) { - struct gfs2_sbd *sdp = gl->gl_name.ln_sbd; + struct gfs2_sbd *sdp = glock_sbd(gl); struct address_space *mapping = gfs2_aspace(sdp); struct gfs2_rgrpd *rgd = gfs2_glock2rgrp(gl); const unsigned bsize = sdp->sd_sb.sb_bsize; @@ -290,7 +290,7 @@ int gfs2_inode_metasync(struct gfs2_glock *gl) filemap_fdatawrite(metamapping); error = filemap_fdatawait(metamapping); if (error) - gfs2_io_error(gl->gl_name.ln_sbd); + gfs2_io_error(glock_sbd(gl)); return error; } @@ -317,7 +317,7 @@ static int inode_go_sync(struct gfs2_glock *gl) GLOCK_BUG_ON(gl, gl->gl_state != LM_ST_EXCLUSIVE); - gfs2_log_flush(gl->gl_name.ln_sbd, gl, GFS2_LOG_HEAD_FLUSH_NORMAL | + gfs2_log_flush(glock_sbd(gl), gl, GFS2_LOG_HEAD_FLUSH_NORMAL | GFS2_LFC_INODE_GO_SYNC); filemap_fdatawrite(metamapping); if (isreg) { @@ -359,7 +359,7 @@ static void inode_go_inval(struct gfs2_glock *gl, int flags) { struct gfs2_inode *ip = gfs2_glock2inode(gl); - gfs2_assert_withdraw(gl->gl_name.ln_sbd, !atomic_read(&gl->gl_ail_count)); + gfs2_assert_withdraw(glock_sbd(gl), !atomic_read(&gl->gl_ail_count)); if (flags & DIO_METADATA) { struct address_space *mapping = gfs2_glock2aspace(gl); @@ -372,11 +372,11 @@ static void inode_go_inval(struct gfs2_glock *gl, int flags) } } - if (ip == GFS2_I(gl->gl_name.ln_sbd->sd_rindex)) { - gfs2_log_flush(gl->gl_name.ln_sbd, NULL, + if (ip == GFS2_I(glock_sbd(gl)->sd_rindex)) { + gfs2_log_flush(glock_sbd(gl), NULL, GFS2_LOG_HEAD_FLUSH_NORMAL | GFS2_LFC_INODE_GO_INVAL); - gl->gl_name.ln_sbd->sd_rindex_uptodate = 0; + glock_sbd(gl)->sd_rindex_uptodate = 0; } if (ip && S_ISREG(ip->i_inode.i_mode)) truncate_inode_pages(ip->i_inode.i_mapping, 0); @@ -567,7 +567,7 @@ static void inode_go_dump(struct seq_file *seq, const struct gfs2_glock *gl, static void freeze_go_callback(struct gfs2_glock *gl, bool remote) { - struct gfs2_sbd *sdp = gl->gl_name.ln_sbd; + struct gfs2_sbd *sdp = glock_sbd(gl); struct super_block *sb = sdp->sd_vfs; if (!remote || @@ -596,7 +596,7 @@ static void freeze_go_callback(struct gfs2_glock *gl, bool remote) */ static int freeze_go_xmote_bh(struct gfs2_glock *gl) { - struct gfs2_sbd *sdp = gl->gl_name.ln_sbd; + struct gfs2_sbd *sdp = glock_sbd(gl); struct gfs2_inode *ip = GFS2_I(sdp->sd_jdesc->jd_inode); struct gfs2_glock *j_gl = ip->i_gl; struct gfs2_log_header_host head; @@ -626,7 +626,7 @@ static int freeze_go_xmote_bh(struct gfs2_glock *gl) static void iopen_go_callback(struct gfs2_glock *gl, bool remote) { struct gfs2_inode *ip = gl->gl_object; - struct gfs2_sbd *sdp = gl->gl_name.ln_sbd; + struct gfs2_sbd *sdp = glock_sbd(gl); if (!remote || test_bit(SDF_KILL, &sdp->sd_flags)) return; diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h index d05d8fe4e456..61465777826a 100644 --- a/fs/gfs2/incore.h +++ b/fs/gfs2/incore.h @@ -326,7 +326,7 @@ enum { GLF_BLOCKING = 15, GLF_TRY_TO_EVICT = 17, /* iopen glocks only */ GLF_VERIFY_DELETE = 18, /* iopen glocks only */ - GLF_PENDING_REPLY = 19, + GLF_MAY_CANCEL = 19, GLF_DEFER_DELETE = 20, /* iopen glocks only */ GLF_CANCELING = 21, }; @@ -369,6 +369,16 @@ struct gfs2_glock { struct rhash_head gl_node; }; +static inline unsigned int glock_type(const struct gfs2_glock *gl) +{ + return gl->gl_name.ln_type; +} + +static inline u64 glock_number(const struct gfs2_glock *gl) +{ + return gl->gl_name.ln_number; +} + enum { GIF_QD_LOCKED = 1, GIF_SW_PAGED = 3, @@ -839,6 +849,8 @@ struct gfs2_sbd { struct dentry *debugfs_dir; /* debugfs directory */ }; +#define glock_sbd(gl) ((gl)->gl_name.ln_sbd) + #define GFS2_BAD_INO 1 static inline struct address_space *gfs2_aspace(struct gfs2_sbd *sdp) @@ -853,9 +865,9 @@ static inline void gfs2_glstats_inc(struct gfs2_glock *gl, int which) static inline void gfs2_sbstats_inc(const struct gfs2_glock *gl, int which) { - const struct gfs2_sbd *sdp = gl->gl_name.ln_sbd; + const struct gfs2_sbd *sdp = glock_sbd(gl); preempt_disable(); - this_cpu_ptr(sdp->sd_lkstats)->lkstats[gl->gl_name.ln_type].stats[which]++; + this_cpu_ptr(sdp->sd_lkstats)->lkstats[glock_type(gl)].stats[which]++; preempt_enable(); } diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c index 36618e353199..8344040ecaf7 100644 --- a/fs/gfs2/inode.c +++ b/fs/gfs2/inode.c @@ -1495,7 +1495,7 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry, unsigned int num_gh; int dir_rename = 0; struct gfs2_diradd da = { .nr_blocks = 0, .save_loc = 0, }; - unsigned int x; + unsigned int retries = 0, x; int error; gfs2_holder_mark_uninitialized(&r_gh); @@ -1545,12 +1545,17 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry, num_gh++; } +again: for (x = 0; x < num_gh; x++) { error = gfs2_glock_nq(ghs + x); if (error) goto out_gunlock; } - error = gfs2_glock_async_wait(num_gh, ghs); + error = gfs2_glock_async_wait(num_gh, ghs, retries); + if (error == -ESTALE) { + retries++; + goto again; + } if (error) goto out_gunlock; @@ -1739,7 +1744,7 @@ static int gfs2_exchange(struct inode *odir, struct dentry *odentry, struct gfs2_sbd *sdp = GFS2_SB(odir); struct gfs2_holder ghs[4], r_gh; unsigned int num_gh; - unsigned int x; + unsigned int retries = 0, x; umode_t old_mode = oip->i_inode.i_mode; umode_t new_mode = nip->i_inode.i_mode; int error; @@ -1783,13 +1788,18 @@ static int gfs2_exchange(struct inode *odir, struct dentry *odentry, gfs2_holder_init(nip->i_gl, LM_ST_EXCLUSIVE, GL_ASYNC, ghs + num_gh); num_gh++; +again: for (x = 0; x < num_gh; x++) { error = gfs2_glock_nq(ghs + x); if (error) goto out_gunlock; } - error = gfs2_glock_async_wait(num_gh, ghs); + error = gfs2_glock_async_wait(num_gh, ghs, retries); + if (error == -ESTALE) { + retries++; + goto again; + } if (error) goto out_gunlock; @@ -2182,6 +2192,14 @@ static int gfs2_getattr(struct mnt_idmap *idmap, return 0; } +static bool fault_in_fiemap(struct fiemap_extent_info *fi) +{ + struct fiemap_extent __user *dest = fi->fi_extents_start; + size_t size = sizeof(*dest) * fi->fi_extents_max; + + return fault_in_safe_writeable((char __user *)dest, size) == 0; +} + static int gfs2_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, u64 start, u64 len) { @@ -2191,14 +2209,22 @@ static int gfs2_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, inode_lock_shared(inode); +retry: ret = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, 0, &gh); if (ret) goto out; + pagefault_disable(); ret = iomap_fiemap(inode, fieinfo, start, len, &gfs2_iomap_ops); + pagefault_enable(); gfs2_glock_dq_uninit(&gh); + if (ret == -EFAULT && fault_in_fiemap(fieinfo)) { + fieinfo->fi_extents_mapped = 0; + goto retry; + } + out: inode_unlock_shared(inode); return ret; @@ -2242,13 +2268,17 @@ loff_t gfs2_seek_hole(struct file *file, loff_t offset) return vfs_setpos(file, ret, inode->i_sb->s_maxbytes); } -static int gfs2_update_time(struct inode *inode, int flags) +static int gfs2_update_time(struct inode *inode, enum fs_update_time type, + unsigned int flags) { struct gfs2_inode *ip = GFS2_I(inode); struct gfs2_glock *gl = ip->i_gl; struct gfs2_holder *gh; int error; + if (flags & IOCB_NOWAIT) + return -EAGAIN; + gh = gfs2_glock_is_locked_by_me(gl); if (gh && gl->gl_state != LM_ST_EXCLUSIVE) { gfs2_glock_dq(gh); @@ -2257,8 +2287,7 @@ static int gfs2_update_time(struct inode *inode, int flags) if (error) return error; } - generic_update_time(inode, flags); - return 0; + return generic_update_time(inode, type, flags); } static const struct inode_operations gfs2_file_iops = { diff --git a/fs/gfs2/lock_dlm.c b/fs/gfs2/lock_dlm.c index b8d249925395..7828ad0b6f5a 100644 --- a/fs/gfs2/lock_dlm.c +++ b/fs/gfs2/lock_dlm.c @@ -8,6 +8,7 @@ #include <linux/fs.h> #include <linux/dlm.h> +#include <linux/hex.h> #include <linux/slab.h> #include <linux/types.h> #include <linux/delay.h> @@ -74,13 +75,13 @@ static inline void gfs2_update_reply_times(struct gfs2_glock *gl, bool blocking) { struct gfs2_pcpu_lkstats *lks; - const unsigned gltype = gl->gl_name.ln_type; + const unsigned gltype = glock_type(gl); unsigned index = blocking ? GFS2_LKS_SRTTB : GFS2_LKS_SRTT; s64 rtt; preempt_disable(); rtt = ktime_to_ns(ktime_sub(ktime_get_real(), gl->gl_dstamp)); - lks = this_cpu_ptr(gl->gl_name.ln_sbd->sd_lkstats); + lks = this_cpu_ptr(glock_sbd(gl)->sd_lkstats); gfs2_update_stats(&gl->gl_stats, index, rtt); /* Local */ gfs2_update_stats(&lks->lkstats[gltype], index, rtt); /* Global */ preempt_enable(); @@ -100,7 +101,7 @@ static inline void gfs2_update_reply_times(struct gfs2_glock *gl, static inline void gfs2_update_request_times(struct gfs2_glock *gl) { struct gfs2_pcpu_lkstats *lks; - const unsigned gltype = gl->gl_name.ln_type; + const unsigned gltype = glock_type(gl); ktime_t dstamp; s64 irt; @@ -108,7 +109,7 @@ static inline void gfs2_update_request_times(struct gfs2_glock *gl) dstamp = gl->gl_dstamp; gl->gl_dstamp = ktime_get_real(); irt = ktime_to_ns(ktime_sub(gl->gl_dstamp, dstamp)); - lks = this_cpu_ptr(gl->gl_name.ln_sbd->sd_lkstats); + lks = this_cpu_ptr(glock_sbd(gl)->sd_lkstats); gfs2_update_stats(&gl->gl_stats, GFS2_LKS_SIRT, irt); /* Local */ gfs2_update_stats(&lks->lkstats[gltype], GFS2_LKS_SIRT, irt); /* Global */ preempt_enable(); @@ -195,7 +196,7 @@ static void gdlm_bast(void *arg, int mode) gfs2_glock_cb(gl, LM_ST_SHARED); break; default: - fs_err(gl->gl_name.ln_sbd, "unknown bast mode %d\n", mode); + fs_err(glock_sbd(gl), "unknown bast mode %d\n", mode); BUG(); } } @@ -276,7 +277,7 @@ static void gfs2_reverse_hex(char *c, u64 value) static int gdlm_lock(struct gfs2_glock *gl, unsigned int req_state, unsigned int flags) { - struct lm_lockstruct *ls = &gl->gl_name.ln_sbd->sd_lockstruct; + struct lm_lockstruct *ls = &glock_sbd(gl)->sd_lockstruct; bool blocking; int cur, req; u32 lkf; @@ -284,8 +285,8 @@ static int gdlm_lock(struct gfs2_glock *gl, unsigned int req_state, int error; gl->gl_req = req_state; - cur = make_mode(gl->gl_name.ln_sbd, gl->gl_state); - req = make_mode(gl->gl_name.ln_sbd, req_state); + cur = make_mode(glock_sbd(gl), gl->gl_state); + req = make_mode(glock_sbd(gl), req_state); blocking = !down_conversion(cur, req) && !(flags & (LM_FLAG_TRY|LM_FLAG_TRY_1CB)); lkf = make_flags(gl, flags, req, blocking); @@ -296,8 +297,8 @@ static int gdlm_lock(struct gfs2_glock *gl, unsigned int req_state, if (test_bit(GLF_INITIAL, &gl->gl_flags)) { memset(strname, ' ', GDLM_STRNAME_BYTES - 1); strname[GDLM_STRNAME_BYTES - 1] = '\0'; - gfs2_reverse_hex(strname + 7, gl->gl_name.ln_type); - gfs2_reverse_hex(strname + 23, gl->gl_name.ln_number); + gfs2_reverse_hex(strname + 7, glock_type(gl)); + gfs2_reverse_hex(strname + 23, glock_number(gl)); gl->gl_dstamp = ktime_get_real(); } else { gfs2_update_request_times(gl); @@ -323,7 +324,7 @@ again: static void gdlm_put_lock(struct gfs2_glock *gl) { - struct gfs2_sbd *sdp = gl->gl_name.ln_sbd; + struct gfs2_sbd *sdp = glock_sbd(gl); struct lm_lockstruct *ls = &sdp->sd_lockstruct; uint32_t flags = 0; int error; @@ -375,14 +376,14 @@ again: if (error) { fs_err(sdp, "gdlm_unlock %x,%llx err=%d\n", - gl->gl_name.ln_type, - (unsigned long long)gl->gl_name.ln_number, error); + glock_type(gl), + (unsigned long long) glock_number(gl), error); } } static void gdlm_cancel(struct gfs2_glock *gl) { - struct lm_lockstruct *ls = &gl->gl_name.ln_sbd->sd_lockstruct; + struct lm_lockstruct *ls = &glock_sbd(gl)->sd_lockstruct; down_read(&ls->ls_sem); if (likely(ls->ls_dlm != NULL)) { diff --git a/fs/gfs2/log.c b/fs/gfs2/log.c index 8312cd2cdae4..347df29d610e 100644 --- a/fs/gfs2/log.c +++ b/fs/gfs2/log.c @@ -888,8 +888,9 @@ void gfs2_write_log_header(struct gfs2_sbd *sdp, struct gfs2_jdesc *jd, sb->s_blocksize - LH_V1_SIZE - 4); lh->lh_crc = cpu_to_be32(crc); - gfs2_log_write(sdp, jd, page, sb->s_blocksize, 0, dblock); - gfs2_log_submit_bio(&jd->jd_log_bio, REQ_OP_WRITE | op_flags); + gfs2_log_write(sdp, jd, page, sb->s_blocksize, 0, dblock, + REQ_OP_WRITE | op_flags); + gfs2_log_submit_write(&jd->jd_log_bio); } /** @@ -1096,7 +1097,7 @@ repeat: if (gfs2_withdrawn(sdp)) goto out_withdraw; if (sdp->sd_jdesc) - gfs2_log_submit_bio(&sdp->sd_jdesc->jd_log_bio, REQ_OP_WRITE); + gfs2_log_submit_write(&sdp->sd_jdesc->jd_log_bio); if (gfs2_withdrawn(sdp)) goto out_withdraw; diff --git a/fs/gfs2/lops.c b/fs/gfs2/lops.c index d27a0b1080a9..e03928def87e 100644 --- a/fs/gfs2/lops.c +++ b/fs/gfs2/lops.c @@ -65,15 +65,15 @@ void gfs2_pin(struct gfs2_sbd *sdp, struct buffer_head *bh) static bool buffer_is_rgrp(const struct gfs2_bufdata *bd) { - return bd->bd_gl->gl_name.ln_type == LM_TYPE_RGRP; + return glock_type(bd->bd_gl) == LM_TYPE_RGRP; } static void maybe_release_space(struct gfs2_bufdata *bd) { struct gfs2_glock *gl = bd->bd_gl; - struct gfs2_sbd *sdp = gl->gl_name.ln_sbd; + struct gfs2_sbd *sdp = glock_sbd(gl); struct gfs2_rgrpd *rgd = gfs2_glock2rgrp(gl); - unsigned int index = bd->bd_bh->b_blocknr - gl->gl_name.ln_number; + unsigned int index = bd->bd_bh->b_blocknr - glock_number(gl); struct gfs2_bitmap *bi = rgd->rd_bits + index; rgrp_lock_local(rgd); @@ -229,21 +229,19 @@ static void gfs2_end_log_write(struct bio *bio) } /** - * gfs2_log_submit_bio - Submit any pending log bio + * gfs2_log_submit_write - Submit a pending log write bio * @biop: Address of the bio pointer - * @opf: REQ_OP | op_flags * * Submit any pending part-built or full bio to the block device. If * there is no pending bio, then this is a no-op. */ -void gfs2_log_submit_bio(struct bio **biop, blk_opf_t opf) +void gfs2_log_submit_write(struct bio **biop) { struct bio *bio = *biop; if (bio) { struct gfs2_sbd *sdp = bio->bi_private; atomic_inc(&sdp->sd_log_in_flight); - bio->bi_opf = opf; submit_bio(bio); *biop = NULL; } @@ -254,6 +252,7 @@ void gfs2_log_submit_bio(struct bio **biop, blk_opf_t opf) * @sdp: The super block * @blkno: The device block number we want to write to * @end_io: The bi_end_io callback + * @opf: REQ_OP | op_flags * * Allocate a new bio, initialize it with the given parameters and return it. * @@ -261,10 +260,10 @@ void gfs2_log_submit_bio(struct bio **biop, blk_opf_t opf) */ static struct bio *gfs2_log_alloc_bio(struct gfs2_sbd *sdp, u64 blkno, - bio_end_io_t *end_io) + bio_end_io_t *end_io, blk_opf_t opf) { struct super_block *sb = sdp->sd_vfs; - struct bio *bio = bio_alloc(sb->s_bdev, BIO_MAX_VECS, 0, GFP_NOIO); + struct bio *bio = bio_alloc(sb->s_bdev, BIO_MAX_VECS, opf, GFP_NOIO); bio->bi_iter.bi_sector = blkno << sdp->sd_fsb2bb_shift; bio->bi_end_io = end_io; @@ -278,7 +277,7 @@ static struct bio *gfs2_log_alloc_bio(struct gfs2_sbd *sdp, u64 blkno, * @sdp: The super block * @blkno: The device block number we want to write to * @biop: The bio to get or allocate - * @op: REQ_OP + * @opf: REQ_OP | op_flags * @end_io: The bi_end_io callback * @flush: Always flush the current bio and allocate a new one? * @@ -291,7 +290,7 @@ static struct bio *gfs2_log_alloc_bio(struct gfs2_sbd *sdp, u64 blkno, */ static struct bio *gfs2_log_get_bio(struct gfs2_sbd *sdp, u64 blkno, - struct bio **biop, enum req_op op, + struct bio **biop, blk_opf_t opf, bio_end_io_t *end_io, bool flush) { struct bio *bio = *biop; @@ -303,10 +302,10 @@ static struct bio *gfs2_log_get_bio(struct gfs2_sbd *sdp, u64 blkno, nblk >>= sdp->sd_fsb2bb_shift; if (blkno == nblk && !flush) return bio; - gfs2_log_submit_bio(biop, op); + gfs2_log_submit_write(biop); } - *biop = gfs2_log_alloc_bio(sdp, blkno, end_io); + *biop = gfs2_log_alloc_bio(sdp, blkno, end_io, opf); return *biop; } @@ -318,6 +317,7 @@ static struct bio *gfs2_log_get_bio(struct gfs2_sbd *sdp, u64 blkno, * @size: the size of the data to write * @offset: the offset within the page * @blkno: block number of the log entry + * @opf: REQ_OP | op_flags * * Try and add the page segment to the current bio. If that fails, * submit the current bio to the device and create a new one, and @@ -326,17 +326,17 @@ static struct bio *gfs2_log_get_bio(struct gfs2_sbd *sdp, u64 blkno, void gfs2_log_write(struct gfs2_sbd *sdp, struct gfs2_jdesc *jd, struct page *page, unsigned size, unsigned offset, - u64 blkno) + u64 blkno, blk_opf_t opf) { struct bio *bio; int ret; - bio = gfs2_log_get_bio(sdp, blkno, &jd->jd_log_bio, REQ_OP_WRITE, + bio = gfs2_log_get_bio(sdp, blkno, &jd->jd_log_bio, opf, gfs2_end_log_write, false); ret = bio_add_page(bio, page, size, offset); if (ret == 0) { bio = gfs2_log_get_bio(sdp, blkno, &jd->jd_log_bio, - REQ_OP_WRITE, gfs2_end_log_write, true); + opf, gfs2_end_log_write, true); ret = bio_add_page(bio, page, size, offset); WARN_ON(ret == 0); } @@ -359,7 +359,7 @@ static void gfs2_log_write_bh(struct gfs2_sbd *sdp, struct buffer_head *bh) dblock = gfs2_log_bmap(sdp->sd_jdesc, sdp->sd_log_flush_head); gfs2_log_incr_head(sdp); gfs2_log_write(sdp, sdp->sd_jdesc, folio_page(bh->b_folio, 0), - bh->b_size, bh_offset(bh), dblock); + bh->b_size, bh_offset(bh), dblock, REQ_OP_WRITE); } /** @@ -380,7 +380,8 @@ static void gfs2_log_write_page(struct gfs2_sbd *sdp, struct page *page) dblock = gfs2_log_bmap(sdp->sd_jdesc, sdp->sd_log_flush_head); gfs2_log_incr_head(sdp); - gfs2_log_write(sdp, sdp->sd_jdesc, page, sb->s_blocksize, 0, dblock); + gfs2_log_write(sdp, sdp->sd_jdesc, page, sb->s_blocksize, 0, dblock, + REQ_OP_WRITE); } /** @@ -477,13 +478,14 @@ static void gfs2_jhead_process_page(struct gfs2_jdesc *jd, unsigned long index, folio_put_refs(folio, 2); } -static struct bio *gfs2_chain_bio(struct bio *prev, unsigned int nr_iovecs) +static struct bio *gfs2_chain_bio(struct bio *prev, unsigned int nr_iovecs, + sector_t sector, blk_opf_t opf) { struct bio *new; - new = bio_alloc(prev->bi_bdev, nr_iovecs, prev->bi_opf, GFP_NOIO); + new = bio_alloc(prev->bi_bdev, nr_iovecs, opf, GFP_NOIO); bio_clone_blkg_association(new, prev); - new->bi_iter.bi_sector = bio_end_sector(prev); + new->bi_iter.bi_sector = sector; bio_chain(new, prev); submit_bio(prev); return new; @@ -546,7 +548,8 @@ int gfs2_find_jhead(struct gfs2_jdesc *jd, struct gfs2_log_header_host *head) unsigned int blocks = (PAGE_SIZE - off) >> bsize_shift; - bio = gfs2_chain_bio(bio, blocks); + bio = gfs2_chain_bio(bio, blocks, sector, + REQ_OP_READ); goto add_block_to_new_bio; } } @@ -556,8 +559,8 @@ int gfs2_find_jhead(struct gfs2_jdesc *jd, struct gfs2_log_header_host *head) submit_bio(bio); } - bio = gfs2_log_alloc_bio(sdp, dblock, gfs2_end_log_read); - bio->bi_opf = REQ_OP_READ; + bio = gfs2_log_alloc_bio(sdp, dblock, gfs2_end_log_read, + REQ_OP_READ); add_block_to_new_bio: bio_add_folio_nofail(bio, folio, bsize, off); block_added: diff --git a/fs/gfs2/lops.h b/fs/gfs2/lops.h index be740bf33666..772557b63b48 100644 --- a/fs/gfs2/lops.h +++ b/fs/gfs2/lops.h @@ -16,8 +16,8 @@ void gfs2_log_incr_head(struct gfs2_sbd *sdp); u64 gfs2_log_bmap(struct gfs2_jdesc *jd, unsigned int lbn); void gfs2_log_write(struct gfs2_sbd *sdp, struct gfs2_jdesc *jd, struct page *page, unsigned size, unsigned offset, - u64 blkno); -void gfs2_log_submit_bio(struct bio **biop, blk_opf_t opf); + u64 blkno, blk_opf_t opf); +void gfs2_log_submit_write(struct bio **biop); void gfs2_pin(struct gfs2_sbd *sdp, struct buffer_head *bh); int gfs2_find_jhead(struct gfs2_jdesc *jd, struct gfs2_log_header_host *head); diff --git a/fs/gfs2/meta_io.c b/fs/gfs2/meta_io.c index e4356198d8d8..3c8e4553102d 100644 --- a/fs/gfs2/meta_io.c +++ b/fs/gfs2/meta_io.c @@ -126,7 +126,7 @@ const struct address_space_operations gfs2_rgrp_aops = { struct buffer_head *gfs2_getbuf(struct gfs2_glock *gl, u64 blkno, int create) { struct address_space *mapping = gfs2_glock2aspace(gl); - struct gfs2_sbd *sdp = gl->gl_name.ln_sbd; + struct gfs2_sbd *sdp = glock_sbd(gl); struct folio *folio; struct buffer_head *bh; unsigned int shift; @@ -259,7 +259,7 @@ static void gfs2_submit_bhs(blk_opf_t opf, struct buffer_head *bhs[], int num) int gfs2_meta_read(struct gfs2_glock *gl, u64 blkno, int flags, int rahead, struct buffer_head **bhp) { - struct gfs2_sbd *sdp = gl->gl_name.ln_sbd; + struct gfs2_sbd *sdp = glock_sbd(gl); struct buffer_head *bh, *bhs[2]; int num = 0; @@ -513,7 +513,7 @@ int gfs2_meta_buffer(struct gfs2_inode *ip, u32 mtype, u64 num, struct buffer_head *gfs2_meta_ra(struct gfs2_glock *gl, u64 dblock, u32 extlen) { - struct gfs2_sbd *sdp = gl->gl_name.ln_sbd; + struct gfs2_sbd *sdp = glock_sbd(gl); struct buffer_head *first_bh, *bh; u32 max_ra = gfs2_tune_get(sdp, gt_max_readahead) >> sdp->sd_sb.sb_bsize_shift; diff --git a/fs/gfs2/meta_io.h b/fs/gfs2/meta_io.h index b7c8a6684d02..2fe5dec193ed 100644 --- a/fs/gfs2/meta_io.h +++ b/fs/gfs2/meta_io.h @@ -43,7 +43,7 @@ static inline struct gfs2_sbd *gfs2_mapping2sbd(struct address_space *mapping) if (mapping->a_ops == &gfs2_meta_aops) { struct gfs2_glock_aspace *gla = container_of(mapping, struct gfs2_glock_aspace, mapping); - return gla->glock.gl_name.ln_sbd; + return glock_sbd(&gla->glock); } else return inode->i_sb->s_fs_info; } diff --git a/fs/gfs2/ops_fstype.c b/fs/gfs2/ops_fstype.c index e7a88b717991..c7d57de7c8f0 100644 --- a/fs/gfs2/ops_fstype.c +++ b/fs/gfs2/ops_fstype.c @@ -1276,7 +1276,6 @@ static int gfs2_fill_super(struct super_block *sb, struct fs_context *fc) if (error) { gfs2_freeze_unlock(sdp); - gfs2_destroy_threads(sdp); fs_err(sdp, "can't make FS RW: %d\n", error); goto fail_per_node; } @@ -1286,6 +1285,7 @@ static int gfs2_fill_super(struct super_block *sb, struct fs_context *fc) fail_per_node: init_per_node(sdp, UNDO); + gfs2_destroy_threads(sdp); fail_inodes: init_inodes(sdp, UNDO); fail_sb: diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index b1692f12a602..1c3455093ae8 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -334,6 +334,7 @@ static void qd_put(struct gfs2_quota_data *qd) lockref_mark_dead(&qd->qd_lockref); spin_unlock(&qd->qd_lockref.lock); + list_lru_del_obj(&gfs2_qd_lru, &qd->qd_lru); gfs2_qd_dispose(qd); return; } @@ -978,7 +979,7 @@ out_dq: gfs2_glock_dq_uninit(&ghs[qx]); inode_unlock(&ip->i_inode); kfree(ghs); - gfs2_log_flush(ip->i_gl->gl_name.ln_sbd, ip->i_gl, + gfs2_log_flush(glock_sbd(ip->i_gl), ip->i_gl, GFS2_LOG_HEAD_FLUSH_NORMAL | GFS2_LFC_DO_SYNC); if (!error) { for (x = 0; x < num_qd; x++) { @@ -1027,7 +1028,7 @@ static int do_glock(struct gfs2_quota_data *qd, int force_refresh, struct gfs2_holder i_gh; int error; - gfs2_assert_warn(sdp, sdp == qd->qd_gl->gl_name.ln_sbd); + gfs2_assert_warn(sdp, sdp == glock_sbd(qd->qd_gl)); restart: error = gfs2_glock_nq_init(qd->qd_gl, LM_ST_SHARED, 0, q_gh); if (error) diff --git a/fs/gfs2/rgrp.c b/fs/gfs2/rgrp.c index b14e54b38ee8..8a97ca734afc 100644 --- a/fs/gfs2/rgrp.c +++ b/fs/gfs2/rgrp.c @@ -1923,7 +1923,7 @@ static void try_rgrp_unlink(struct gfs2_rgrpd *rgd, u64 *last_unlinked, u64 skip static bool gfs2_rgrp_congested(const struct gfs2_rgrpd *rgd, int loops) { const struct gfs2_glock *gl = rgd->rd_gl; - const struct gfs2_sbd *sdp = gl->gl_name.ln_sbd; + const struct gfs2_sbd *sdp = glock_sbd(gl); struct gfs2_lkstats *st; u64 r_dcount, l_dcount; u64 l_srttb, a_srttb = 0; diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c index f6cd907b3ec6..d96160636161 100644 --- a/fs/gfs2/super.c +++ b/fs/gfs2/super.c @@ -147,8 +147,10 @@ int gfs2_make_fs_rw(struct gfs2_sbd *sdp) } error = gfs2_quota_init(sdp); - if (!error && gfs2_withdrawn(sdp)) + if (!error && gfs2_withdrawn(sdp)) { + gfs2_quota_cleanup(sdp); error = -EIO; + } if (!error) set_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags); return error; diff --git a/fs/gfs2/trace_gfs2.h b/fs/gfs2/trace_gfs2.h index fcfbf68ec725..6fd39fcdd00e 100644 --- a/fs/gfs2/trace_gfs2.h +++ b/fs/gfs2/trace_gfs2.h @@ -52,7 +52,7 @@ {(1UL << GLF_DEMOTE_IN_PROGRESS), "p" }, \ {(1UL << GLF_DIRTY), "y" }, \ {(1UL << GLF_LFLUSH), "f" }, \ - {(1UL << GLF_PENDING_REPLY), "R" }, \ + {(1UL << GLF_MAY_CANCEL), "c" }, \ {(1UL << GLF_HAVE_REPLY), "r" }, \ {(1UL << GLF_INITIAL), "a" }, \ {(1UL << GLF_HAVE_FROZEN_REPLY), "F" }, \ @@ -111,9 +111,9 @@ TRACE_EVENT(gfs2_glock_state_change, ), TP_fast_assign( - __entry->dev = gl->gl_name.ln_sbd->sd_vfs->s_dev; - __entry->glnum = gl->gl_name.ln_number; - __entry->gltype = gl->gl_name.ln_type; + __entry->dev = glock_sbd(gl)->sd_vfs->s_dev; + __entry->glnum = glock_number(gl); + __entry->gltype = glock_type(gl); __entry->cur_state = glock_trace_state(gl->gl_state); __entry->new_state = glock_trace_state(new_state); __entry->tgt_state = glock_trace_state(gl->gl_target); @@ -147,9 +147,9 @@ TRACE_EVENT(gfs2_glock_put, ), TP_fast_assign( - __entry->dev = gl->gl_name.ln_sbd->sd_vfs->s_dev; - __entry->gltype = gl->gl_name.ln_type; - __entry->glnum = gl->gl_name.ln_number; + __entry->dev = glock_sbd(gl)->sd_vfs->s_dev; + __entry->gltype = glock_type(gl); + __entry->glnum = glock_number(gl); __entry->cur_state = glock_trace_state(gl->gl_state); __entry->flags = gl->gl_flags | (gl->gl_object ? (1UL<<GLF_OBJECT) : 0); ), @@ -181,9 +181,9 @@ TRACE_EVENT(gfs2_demote_rq, ), TP_fast_assign( - __entry->dev = gl->gl_name.ln_sbd->sd_vfs->s_dev; - __entry->gltype = gl->gl_name.ln_type; - __entry->glnum = gl->gl_name.ln_number; + __entry->dev = glock_sbd(gl)->sd_vfs->s_dev; + __entry->gltype = glock_type(gl); + __entry->glnum = glock_number(gl); __entry->cur_state = glock_trace_state(gl->gl_state); __entry->dmt_state = glock_trace_state(gl->gl_demote_state); __entry->flags = gl->gl_flags | (gl->gl_object ? (1UL<<GLF_OBJECT) : 0); @@ -215,9 +215,9 @@ TRACE_EVENT(gfs2_promote, ), TP_fast_assign( - __entry->dev = gh->gh_gl->gl_name.ln_sbd->sd_vfs->s_dev; - __entry->glnum = gh->gh_gl->gl_name.ln_number; - __entry->gltype = gh->gh_gl->gl_name.ln_type; + __entry->dev = glock_sbd(gh->gh_gl)->sd_vfs->s_dev; + __entry->glnum = glock_number(gh->gh_gl); + __entry->gltype = glock_type(gh->gh_gl); __entry->state = glock_trace_state(gh->gh_state); ), @@ -243,9 +243,9 @@ TRACE_EVENT(gfs2_glock_queue, ), TP_fast_assign( - __entry->dev = gh->gh_gl->gl_name.ln_sbd->sd_vfs->s_dev; - __entry->glnum = gh->gh_gl->gl_name.ln_number; - __entry->gltype = gh->gh_gl->gl_name.ln_type; + __entry->dev = glock_sbd(gh->gh_gl)->sd_vfs->s_dev; + __entry->glnum = glock_number(gh->gh_gl); + __entry->gltype = glock_type(gh->gh_gl); __entry->queue = queue; __entry->state = glock_trace_state(gh->gh_state); ), @@ -282,9 +282,9 @@ TRACE_EVENT(gfs2_glock_lock_time, ), TP_fast_assign( - __entry->dev = gl->gl_name.ln_sbd->sd_vfs->s_dev; - __entry->glnum = gl->gl_name.ln_number; - __entry->gltype = gl->gl_name.ln_type; + __entry->dev = glock_sbd(gl)->sd_vfs->s_dev; + __entry->glnum = glock_number(gl); + __entry->gltype = glock_type(gl); __entry->status = gl->gl_lksb.sb_status; __entry->flags = gl->gl_lksb.sb_flags; __entry->tdiff = tdiff; @@ -337,11 +337,11 @@ TRACE_EVENT(gfs2_pin, ), TP_fast_assign( - __entry->dev = bd->bd_gl->gl_name.ln_sbd->sd_vfs->s_dev; + __entry->dev = glock_sbd(bd->bd_gl)->sd_vfs->s_dev; __entry->pin = pin; __entry->len = bd->bd_bh->b_size; __entry->block = bd->bd_bh->b_blocknr; - __entry->ino = bd->bd_gl->gl_name.ln_number; + __entry->ino = glock_number(bd->bd_gl); ), TP_printk("%u,%u log %s %llu/%lu inode %llu", @@ -458,7 +458,7 @@ TRACE_EVENT(gfs2_bmap, ), TP_fast_assign( - __entry->dev = ip->i_gl->gl_name.ln_sbd->sd_vfs->s_dev; + __entry->dev = glock_sbd(ip->i_gl)->sd_vfs->s_dev; __entry->lblock = lblock; __entry->pblock = buffer_mapped(bh) ? bh->b_blocknr : 0; __entry->inum = ip->i_no_addr; @@ -494,7 +494,7 @@ TRACE_EVENT(gfs2_iomap_start, ), TP_fast_assign( - __entry->dev = ip->i_gl->gl_name.ln_sbd->sd_vfs->s_dev; + __entry->dev = glock_sbd(ip->i_gl)->sd_vfs->s_dev; __entry->inum = ip->i_no_addr; __entry->pos = pos; __entry->length = length; @@ -526,7 +526,7 @@ TRACE_EVENT(gfs2_iomap_end, ), TP_fast_assign( - __entry->dev = ip->i_gl->gl_name.ln_sbd->sd_vfs->s_dev; + __entry->dev = glock_sbd(ip->i_gl)->sd_vfs->s_dev; __entry->inum = ip->i_no_addr; __entry->offset = iomap->offset; __entry->length = iomap->length; @@ -568,7 +568,7 @@ TRACE_EVENT(gfs2_block_alloc, ), TP_fast_assign( - __entry->dev = rgd->rd_gl->gl_name.ln_sbd->sd_vfs->s_dev; + __entry->dev = glock_sbd(rgd->rd_gl)->sd_vfs->s_dev; __entry->start = block; __entry->inum = ip->i_no_addr; __entry->len = len; diff --git a/fs/gfs2/trans.c b/fs/gfs2/trans.c index 6df65540e13d..95f2632cdb01 100644 --- a/fs/gfs2/trans.c +++ b/fs/gfs2/trans.c @@ -197,7 +197,7 @@ static struct gfs2_bufdata *gfs2_alloc_bufdata(struct gfs2_glock *gl, void gfs2_trans_add_data(struct gfs2_glock *gl, struct buffer_head *bh) { struct gfs2_trans *tr = current->journal_info; - struct gfs2_sbd *sdp = gl->gl_name.ln_sbd; + struct gfs2_sbd *sdp = glock_sbd(gl); struct gfs2_bufdata *bd; lock_buffer(bh); @@ -255,7 +255,7 @@ void gfs2_trans_add_databufs(struct gfs2_glock *gl, struct folio *folio, void gfs2_trans_add_meta(struct gfs2_glock *gl, struct buffer_head *bh) { - struct gfs2_sbd *sdp = gl->gl_name.ln_sbd; + struct gfs2_sbd *sdp = glock_sbd(gl); struct super_block *sb = sdp->sd_vfs; struct gfs2_bufdata *bd; struct gfs2_meta_header *mh; diff --git a/fs/hfs/dir.c b/fs/hfs/dir.c index 86a6b317b474..0c615c078650 100644 --- a/fs/hfs/dir.c +++ b/fs/hfs/dir.c @@ -196,8 +196,8 @@ static int hfs_create(struct mnt_idmap *idmap, struct inode *dir, int res; inode = hfs_new_inode(dir, &dentry->d_name, mode); - if (!inode) - return -ENOMEM; + if (IS_ERR(inode)) + return PTR_ERR(inode); res = hfs_cat_create(inode->i_ino, dir, &dentry->d_name, inode); if (res) { @@ -226,8 +226,8 @@ static struct dentry *hfs_mkdir(struct mnt_idmap *idmap, struct inode *dir, int res; inode = hfs_new_inode(dir, &dentry->d_name, S_IFDIR | mode); - if (!inode) - return ERR_PTR(-ENOMEM); + if (IS_ERR(inode)) + return ERR_CAST(inode); res = hfs_cat_create(inode->i_ino, dir, &dentry->d_name, inode); if (res) { @@ -254,11 +254,18 @@ static struct dentry *hfs_mkdir(struct mnt_idmap *idmap, struct inode *dir, */ static int hfs_remove(struct inode *dir, struct dentry *dentry) { + struct super_block *sb = dir->i_sb; struct inode *inode = d_inode(dentry); int res; if (S_ISDIR(inode->i_mode) && inode->i_size != 2) return -ENOTEMPTY; + + if (unlikely(!is_hfs_cnid_counts_valid(sb))) { + pr_err("cannot remove file/folder\n"); + return -ERANGE; + } + res = hfs_cat_delete(inode->i_ino, dir, &dentry->d_name); if (res) return res; diff --git a/fs/hfs/hfs_fs.h b/fs/hfs/hfs_fs.h index e94dbc04a1e4..ac0e83f77a0f 100644 --- a/fs/hfs/hfs_fs.h +++ b/fs/hfs/hfs_fs.h @@ -199,6 +199,7 @@ extern void hfs_delete_inode(struct inode *inode); extern const struct xattr_handler * const hfs_xattr_handlers[]; /* mdb.c */ +extern bool is_hfs_cnid_counts_valid(struct super_block *sb); extern int hfs_mdb_get(struct super_block *sb); extern void hfs_mdb_commit(struct super_block *sb); extern void hfs_mdb_close(struct super_block *sb); diff --git a/fs/hfs/inode.c b/fs/hfs/inode.c index 524db1389737..878535db64d6 100644 --- a/fs/hfs/inode.c +++ b/fs/hfs/inode.c @@ -187,16 +187,23 @@ struct inode *hfs_new_inode(struct inode *dir, const struct qstr *name, umode_t s64 next_id; s64 file_count; s64 folder_count; + int err = -ENOMEM; if (!inode) - return NULL; + goto out_err; + + err = -ERANGE; mutex_init(&HFS_I(inode)->extents_lock); INIT_LIST_HEAD(&HFS_I(inode)->open_dir_list); spin_lock_init(&HFS_I(inode)->open_dir_lock); hfs_cat_build_key(sb, (btree_key *)&HFS_I(inode)->cat_key, dir->i_ino, name); next_id = atomic64_inc_return(&HFS_SB(sb)->next_id); - BUG_ON(next_id > U32_MAX); + if (next_id > U32_MAX) { + atomic64_dec(&HFS_SB(sb)->next_id); + pr_err("cannot create new inode: next CNID exceeds limit\n"); + goto out_discard; + } inode->i_ino = (u32)next_id; inode->i_mode = mode; inode->i_uid = current_fsuid(); @@ -210,7 +217,11 @@ struct inode *hfs_new_inode(struct inode *dir, const struct qstr *name, umode_t if (S_ISDIR(mode)) { inode->i_size = 2; folder_count = atomic64_inc_return(&HFS_SB(sb)->folder_count); - BUG_ON(folder_count > U32_MAX); + if (folder_count> U32_MAX) { + atomic64_dec(&HFS_SB(sb)->folder_count); + pr_err("cannot create new inode: folder count exceeds limit\n"); + goto out_discard; + } if (dir->i_ino == HFS_ROOT_CNID) HFS_SB(sb)->root_dirs++; inode->i_op = &hfs_dir_inode_operations; @@ -220,7 +231,11 @@ struct inode *hfs_new_inode(struct inode *dir, const struct qstr *name, umode_t } else if (S_ISREG(mode)) { HFS_I(inode)->clump_blocks = HFS_SB(sb)->clumpablks; file_count = atomic64_inc_return(&HFS_SB(sb)->file_count); - BUG_ON(file_count > U32_MAX); + if (file_count > U32_MAX) { + atomic64_dec(&HFS_SB(sb)->file_count); + pr_err("cannot create new inode: file count exceeds limit\n"); + goto out_discard; + } if (dir->i_ino == HFS_ROOT_CNID) HFS_SB(sb)->root_files++; inode->i_op = &hfs_file_inode_operations; @@ -244,6 +259,11 @@ struct inode *hfs_new_inode(struct inode *dir, const struct qstr *name, umode_t hfs_mark_mdb_dirty(sb); return inode; + + out_discard: + iput(inode); + out_err: + return ERR_PTR(err); } void hfs_delete_inode(struct inode *inode) @@ -252,7 +272,6 @@ void hfs_delete_inode(struct inode *inode) hfs_dbg("ino %lu\n", inode->i_ino); if (S_ISDIR(inode->i_mode)) { - BUG_ON(atomic64_read(&HFS_SB(sb)->folder_count) > U32_MAX); atomic64_dec(&HFS_SB(sb)->folder_count); if (HFS_I(inode)->cat_key.ParID == cpu_to_be32(HFS_ROOT_CNID)) HFS_SB(sb)->root_dirs--; @@ -261,7 +280,6 @@ void hfs_delete_inode(struct inode *inode) return; } - BUG_ON(atomic64_read(&HFS_SB(sb)->file_count) > U32_MAX); atomic64_dec(&HFS_SB(sb)->file_count); if (HFS_I(inode)->cat_key.ParID == cpu_to_be32(HFS_ROOT_CNID)) HFS_SB(sb)->root_files--; diff --git a/fs/hfs/mdb.c b/fs/hfs/mdb.c index 53f3fae60217..a97cea35ca2e 100644 --- a/fs/hfs/mdb.c +++ b/fs/hfs/mdb.c @@ -64,6 +64,27 @@ static int hfs_get_last_session(struct super_block *sb, return 0; } +bool is_hfs_cnid_counts_valid(struct super_block *sb) +{ + struct hfs_sb_info *sbi = HFS_SB(sb); + bool corrupted = false; + + if (unlikely(atomic64_read(&sbi->next_id) > U32_MAX)) { + pr_warn("next CNID exceeds limit\n"); + corrupted = true; + } + if (unlikely(atomic64_read(&sbi->file_count) > U32_MAX)) { + pr_warn("file count exceeds limit\n"); + corrupted = true; + } + if (unlikely(atomic64_read(&sbi->folder_count) > U32_MAX)) { + pr_warn("folder count exceeds limit\n"); + corrupted = true; + } + + return !corrupted; +} + /* * hfs_mdb_get() * @@ -92,7 +113,7 @@ int hfs_mdb_get(struct super_block *sb) /* See if this is an HFS filesystem */ bh = sb_bread512(sb, part_start + HFS_MDB_BLK, mdb); if (!bh) - goto out; + return -EIO; if (mdb->drSigWord == cpu_to_be16(HFS_SUPER_MAGIC)) break; @@ -102,13 +123,14 @@ int hfs_mdb_get(struct super_block *sb) * (should do this only for cdrom/loop though) */ if (hfs_part_find(sb, &part_start, &part_size)) - goto out; + return -EIO; } HFS_SB(sb)->alloc_blksz = size = be32_to_cpu(mdb->drAlBlkSiz); if (!size || (size & (HFS_SECTOR_SIZE - 1))) { pr_err("bad allocation block size %d\n", size); - goto out_bh; + brelse(bh); + return -EIO; } size = min(HFS_SB(sb)->alloc_blksz, (u32)PAGE_SIZE); @@ -125,14 +147,16 @@ int hfs_mdb_get(struct super_block *sb) brelse(bh); if (!sb_set_blocksize(sb, size)) { pr_err("unable to set blocksize to %u\n", size); - goto out; + return -EIO; } bh = sb_bread512(sb, part_start + HFS_MDB_BLK, mdb); if (!bh) - goto out; - if (mdb->drSigWord != cpu_to_be16(HFS_SUPER_MAGIC)) - goto out_bh; + return -EIO; + if (mdb->drSigWord != cpu_to_be16(HFS_SUPER_MAGIC)) { + brelse(bh); + return -EIO; + } HFS_SB(sb)->mdb_bh = bh; HFS_SB(sb)->mdb = mdb; @@ -156,6 +180,11 @@ int hfs_mdb_get(struct super_block *sb) atomic64_set(&HFS_SB(sb)->file_count, be32_to_cpu(mdb->drFilCnt)); atomic64_set(&HFS_SB(sb)->folder_count, be32_to_cpu(mdb->drDirCnt)); + if (!is_hfs_cnid_counts_valid(sb)) { + pr_warn("filesystem possibly corrupted, running fsck.hfs is recommended. Mounting read-only.\n"); + sb->s_flags |= SB_RDONLY; + } + /* TRY to get the alternate (backup) MDB. */ sect = part_start + part_size - 2; bh = sb_bread512(sb, sect, mdb2); @@ -174,7 +203,7 @@ int hfs_mdb_get(struct super_block *sb) HFS_SB(sb)->bitmap = kzalloc(8192, GFP_KERNEL); if (!HFS_SB(sb)->bitmap) - goto out; + return -EIO; /* read in the bitmap */ block = be16_to_cpu(mdb->drVBMSt) + part_start; @@ -185,7 +214,7 @@ int hfs_mdb_get(struct super_block *sb) bh = sb_bread(sb, off >> sb->s_blocksize_bits); if (!bh) { pr_err("unable to read volume bitmap\n"); - goto out; + return -EIO; } off2 = off & (sb->s_blocksize - 1); len = min((int)sb->s_blocksize - off2, size); @@ -199,17 +228,17 @@ int hfs_mdb_get(struct super_block *sb) HFS_SB(sb)->ext_tree = hfs_btree_open(sb, HFS_EXT_CNID, hfs_ext_keycmp); if (!HFS_SB(sb)->ext_tree) { pr_err("unable to open extent tree\n"); - goto out; + return -EIO; } HFS_SB(sb)->cat_tree = hfs_btree_open(sb, HFS_CAT_CNID, hfs_cat_keycmp); if (!HFS_SB(sb)->cat_tree) { pr_err("unable to open catalog tree\n"); - goto out; + return -EIO; } attrib = mdb->drAtrb; if (!(attrib & cpu_to_be16(HFS_SB_ATTRIB_UNMNT))) { - pr_warn("filesystem was not cleanly unmounted, running fsck.hfs is recommended. mounting read-only.\n"); + pr_warn("filesystem was not cleanly unmounted, running fsck.hfs is recommended. Mounting read-only.\n"); sb->s_flags |= SB_RDONLY; } if ((attrib & cpu_to_be16(HFS_SB_ATTRIB_SLOCK))) { @@ -229,12 +258,6 @@ int hfs_mdb_get(struct super_block *sb) } return 0; - -out_bh: - brelse(bh); -out: - hfs_mdb_put(sb); - return -EIO; } /* @@ -273,15 +296,12 @@ void hfs_mdb_commit(struct super_block *sb) /* These parameters may have been modified, so write them back */ mdb->drLsMod = hfs_mtime(); mdb->drFreeBks = cpu_to_be16(HFS_SB(sb)->free_ablocks); - BUG_ON(atomic64_read(&HFS_SB(sb)->next_id) > U32_MAX); mdb->drNxtCNID = cpu_to_be32((u32)atomic64_read(&HFS_SB(sb)->next_id)); mdb->drNmFls = cpu_to_be16(HFS_SB(sb)->root_files); mdb->drNmRtDirs = cpu_to_be16(HFS_SB(sb)->root_dirs); - BUG_ON(atomic64_read(&HFS_SB(sb)->file_count) > U32_MAX); mdb->drFilCnt = cpu_to_be32((u32)atomic64_read(&HFS_SB(sb)->file_count)); - BUG_ON(atomic64_read(&HFS_SB(sb)->folder_count) > U32_MAX); mdb->drDirCnt = cpu_to_be32((u32)atomic64_read(&HFS_SB(sb)->folder_count)); @@ -359,8 +379,6 @@ void hfs_mdb_close(struct super_block *sb) * Release the resources associated with the in-core MDB. */ void hfs_mdb_put(struct super_block *sb) { - if (!HFS_SB(sb)) - return; /* free the B-trees */ hfs_btree_close(HFS_SB(sb)->ext_tree); hfs_btree_close(HFS_SB(sb)->cat_tree); @@ -373,6 +391,4 @@ void hfs_mdb_put(struct super_block *sb) unload_nls(HFS_SB(sb)->nls_disk); kfree(HFS_SB(sb)->bitmap); - kfree(HFS_SB(sb)); - sb->s_fs_info = NULL; } diff --git a/fs/hfs/super.c b/fs/hfs/super.c index 47f50fa555a4..97546d6b41f4 100644 --- a/fs/hfs/super.c +++ b/fs/hfs/super.c @@ -34,6 +34,7 @@ MODULE_LICENSE("GPL"); static int hfs_sync_fs(struct super_block *sb, int wait) { + is_hfs_cnid_counts_valid(sb); hfs_mdb_commit(sb); return 0; } @@ -65,6 +66,8 @@ static void flush_mdb(struct work_struct *work) sbi->work_queued = 0; spin_unlock(&sbi->work_lock); + is_hfs_cnid_counts_valid(sb); + hfs_mdb_commit(sb); } @@ -431,10 +434,18 @@ static int hfs_init_fs_context(struct fs_context *fc) return 0; } +static void hfs_kill_super(struct super_block *sb) +{ + struct hfs_sb_info *hsb = HFS_SB(sb); + + kill_block_super(sb); + kfree(hsb); +} + static struct file_system_type hfs_fs_type = { .owner = THIS_MODULE, .name = "hfs", - .kill_sb = kill_block_super, + .kill_sb = hfs_kill_super, .fs_flags = FS_REQUIRES_DEV, .init_fs_context = hfs_init_fs_context, }; diff --git a/fs/hfsplus/attributes.c b/fs/hfsplus/attributes.c index ba26980cc503..4b79cd606276 100644 --- a/fs/hfsplus/attributes.c +++ b/fs/hfsplus/attributes.c @@ -117,8 +117,10 @@ static int hfsplus_attr_build_record(hfsplus_attr_entry *entry, int record_type, entry->inline_data.record_type = cpu_to_be32(record_type); if (size <= HFSPLUS_MAX_INLINE_DATA_SIZE) len = size; - else + else { + hfs_dbg("value size %zu is too big\n", size); return HFSPLUS_INVALID_ATTR_RECORD; + } entry->inline_data.length = cpu_to_be16(len); memcpy(entry->inline_data.raw_bytes, value, len); /* @@ -191,14 +193,66 @@ attr_not_found: return 0; } +static +int hfsplus_create_attr_nolock(struct inode *inode, const char *name, + const void *value, size_t size, + struct hfs_find_data *fd, + hfsplus_attr_entry *entry_ptr) +{ + struct super_block *sb = inode->i_sb; + int entry_size; + int err; + + hfs_dbg("name %s, ino %ld\n", + name ? name : NULL, inode->i_ino); + + if (name) { + err = hfsplus_attr_build_key(sb, fd->search_key, + inode->i_ino, name); + if (err) + return err; + } else + return -EINVAL; + + /* Mac OS X supports only inline data attributes. */ + entry_size = hfsplus_attr_build_record(entry_ptr, + HFSPLUS_ATTR_INLINE_DATA, + inode->i_ino, + value, size); + if (entry_size == HFSPLUS_INVALID_ATTR_RECORD) { + if (size > HFSPLUS_MAX_INLINE_DATA_SIZE) + err = -E2BIG; + else + err = -EINVAL; + hfs_dbg("unable to store value: err %d\n", err); + return err; + } + + err = hfs_brec_find(fd, hfs_find_rec_by_key); + if (err != -ENOENT) { + if (!err) + err = -EEXIST; + return err; + } + + err = hfs_brec_insert(fd, entry_ptr, entry_size); + if (err) { + hfs_dbg("unable to store value: err %d\n", err); + return err; + } + + hfsplus_mark_inode_dirty(inode, HFSPLUS_I_ATTR_DIRTY); + + return 0; +} + int hfsplus_create_attr(struct inode *inode, - const char *name, - const void *value, size_t size) + const char *name, + const void *value, size_t size) { struct super_block *sb = inode->i_sb; struct hfs_find_data fd; hfsplus_attr_entry *entry_ptr; - int entry_size; int err; hfs_dbg("name %s, ino %ld\n", @@ -222,39 +276,11 @@ int hfsplus_create_attr(struct inode *inode, if (err) goto failed_create_attr; - if (name) { - err = hfsplus_attr_build_key(sb, fd.search_key, - inode->i_ino, name); - if (err) - goto failed_create_attr; - } else { - err = -EINVAL; - goto failed_create_attr; - } - - /* Mac OS X supports only inline data attributes. */ - entry_size = hfsplus_attr_build_record(entry_ptr, - HFSPLUS_ATTR_INLINE_DATA, - inode->i_ino, - value, size); - if (entry_size == HFSPLUS_INVALID_ATTR_RECORD) { - err = -EINVAL; - goto failed_create_attr; - } - - err = hfs_brec_find(&fd, hfs_find_rec_by_key); - if (err != -ENOENT) { - if (!err) - err = -EEXIST; - goto failed_create_attr; - } - - err = hfs_brec_insert(&fd, entry_ptr, entry_size); + err = hfsplus_create_attr_nolock(inode, name, value, size, + &fd, entry_ptr); if (err) goto failed_create_attr; - hfsplus_mark_inode_dirty(inode, HFSPLUS_I_ATTR_DIRTY); - failed_create_attr: hfs_find_exit(&fd); @@ -304,6 +330,37 @@ static int __hfsplus_delete_attr(struct inode *inode, u32 cnid, return err; } +static +int hfsplus_delete_attr_nolock(struct inode *inode, const char *name, + struct hfs_find_data *fd) +{ + struct super_block *sb = inode->i_sb; + int err; + + hfs_dbg("name %s, ino %ld\n", + name ? name : NULL, inode->i_ino); + + if (name) { + err = hfsplus_attr_build_key(sb, fd->search_key, + inode->i_ino, name); + if (err) + return err; + } else { + pr_err("invalid extended attribute name\n"); + return -EINVAL; + } + + err = hfs_brec_find(fd, hfs_find_rec_by_key); + if (err) + return err; + + err = __hfsplus_delete_attr(inode, inode->i_ino, fd); + if (err) + return err; + + return 0; +} + int hfsplus_delete_attr(struct inode *inode, const char *name) { int err = 0; @@ -327,22 +384,7 @@ int hfsplus_delete_attr(struct inode *inode, const char *name) if (err) goto out; - if (name) { - err = hfsplus_attr_build_key(sb, fd.search_key, - inode->i_ino, name); - if (err) - goto out; - } else { - pr_err("invalid extended attribute name\n"); - err = -EINVAL; - goto out; - } - - err = hfs_brec_find(&fd, hfs_find_rec_by_key); - if (err) - goto out; - - err = __hfsplus_delete_attr(inode, inode->i_ino, &fd); + err = hfsplus_delete_attr_nolock(inode, name, &fd); if (err) goto out; @@ -384,3 +426,50 @@ end_delete_all: hfs_find_exit(&fd); return err; } + +int hfsplus_replace_attr(struct inode *inode, + const char *name, + const void *value, size_t size) +{ + struct super_block *sb = inode->i_sb; + struct hfs_find_data fd; + hfsplus_attr_entry *entry_ptr; + int err = 0; + + hfs_dbg("name %s, ino %ld\n", + name ? name : NULL, inode->i_ino); + + if (!HFSPLUS_SB(sb)->attr_tree) { + pr_err("attributes file doesn't exist\n"); + return -EINVAL; + } + + entry_ptr = hfsplus_alloc_attr_entry(); + if (!entry_ptr) + return -ENOMEM; + + err = hfs_find_init(HFSPLUS_SB(sb)->attr_tree, &fd); + if (err) + goto failed_init_replace_attr; + + /* Fail early and avoid ENOSPC during the btree operation */ + err = hfs_bmap_reserve(fd.tree, fd.tree->depth + 1); + if (err) + goto failed_replace_attr; + + err = hfsplus_delete_attr_nolock(inode, name, &fd); + if (err) + goto failed_replace_attr; + + err = hfsplus_create_attr_nolock(inode, name, value, size, + &fd, entry_ptr); + if (err) + goto failed_replace_attr; + +failed_replace_attr: + hfs_find_exit(&fd); + +failed_init_replace_attr: + hfsplus_destroy_attr_entry(entry_ptr); + return err; +} diff --git a/fs/hfsplus/bnode.c b/fs/hfsplus/bnode.c index 191661af9677..250a226336ea 100644 --- a/fs/hfsplus/bnode.c +++ b/fs/hfsplus/bnode.c @@ -629,7 +629,7 @@ struct hfs_bnode *hfs_bnode_create(struct hfs_btree *tree, u32 num) if (node) { pr_crit("new node %u already hashed?\n", num); WARN_ON(1); - return node; + return ERR_PTR(-EEXIST); } node = __hfs_bnode_create(tree, num); if (!node) diff --git a/fs/hfsplus/dir.c b/fs/hfsplus/dir.c index cadf0b5f9342..ca5f74a140ec 100644 --- a/fs/hfsplus/dir.c +++ b/fs/hfsplus/dir.c @@ -313,6 +313,9 @@ static int hfsplus_link(struct dentry *src_dentry, struct inode *dst_dir, if (!S_ISREG(inode->i_mode)) return -EPERM; + hfs_dbg("src_dir->i_ino %lu, dst_dir->i_ino %lu, inode->i_ino %lu\n", + src_dir->i_ino, dst_dir->i_ino, inode->i_ino); + mutex_lock(&sbi->vh_mutex); if (inode->i_ino == (u32)(unsigned long)src_dentry->d_fsdata) { for (;;) { @@ -332,7 +335,7 @@ static int hfsplus_link(struct dentry *src_dentry, struct inode *dst_dir, cnid = sbi->next_cnid++; src_dentry->d_fsdata = (void *)(unsigned long)cnid; res = hfsplus_create_cat(cnid, src_dir, - &src_dentry->d_name, inode); + &src_dentry->d_name, inode); if (res) /* panic? */ goto out; @@ -350,6 +353,21 @@ static int hfsplus_link(struct dentry *src_dentry, struct inode *dst_dir, mark_inode_dirty(inode); sbi->file_count++; hfsplus_mark_mdb_dirty(dst_dir->i_sb); + + res = hfsplus_cat_write_inode(src_dir); + if (res) + goto out; + + res = hfsplus_cat_write_inode(dst_dir); + if (res) + goto out; + + res = hfsplus_cat_write_inode(sbi->hidden_dir); + if (res) + goto out; + + res = hfsplus_cat_write_inode(inode); + out: mutex_unlock(&sbi->vh_mutex); return res; @@ -367,6 +385,9 @@ static int hfsplus_unlink(struct inode *dir, struct dentry *dentry) if (HFSPLUS_IS_RSRC(inode)) return -EPERM; + hfs_dbg("dir->i_ino %lu, inode->i_ino %lu\n", + dir->i_ino, inode->i_ino); + mutex_lock(&sbi->vh_mutex); cnid = (u32)(unsigned long)dentry->d_fsdata; if (inode->i_ino == cnid && @@ -408,6 +429,15 @@ static int hfsplus_unlink(struct inode *dir, struct dentry *dentry) inode_set_ctime_current(inode); mark_inode_dirty(inode); out: + if (!res) { + res = hfsplus_cat_write_inode(dir); + if (!res) { + res = hfsplus_cat_write_inode(sbi->hidden_dir); + if (!res) + res = hfsplus_cat_write_inode(inode); + } + } + mutex_unlock(&sbi->vh_mutex); return res; } @@ -429,6 +459,8 @@ static int hfsplus_rmdir(struct inode *dir, struct dentry *dentry) inode_set_ctime_current(inode); hfsplus_delete_inode(inode); mark_inode_dirty(inode); + + res = hfsplus_cat_write_inode(dir); out: mutex_unlock(&sbi->vh_mutex); return res; @@ -465,6 +497,12 @@ static int hfsplus_symlink(struct mnt_idmap *idmap, struct inode *dir, hfsplus_instantiate(dentry, inode, inode->i_ino); mark_inode_dirty(inode); + + res = hfsplus_cat_write_inode(dir); + if (res) + goto out; + + res = hfsplus_cat_write_inode(inode); goto out; out_err: @@ -506,6 +544,12 @@ static int hfsplus_mknod(struct mnt_idmap *idmap, struct inode *dir, hfsplus_instantiate(dentry, inode, inode->i_ino); mark_inode_dirty(inode); + + res = hfsplus_cat_write_inode(dir); + if (res) + goto out; + + res = hfsplus_cat_write_inode(inode); goto out; failed_mknod: diff --git a/fs/hfsplus/hfsplus_fs.h b/fs/hfsplus/hfsplus_fs.h index 45fe3a12ecba..5f891b73a646 100644 --- a/fs/hfsplus/hfsplus_fs.h +++ b/fs/hfsplus/hfsplus_fs.h @@ -344,6 +344,9 @@ int hfsplus_create_attr(struct inode *inode, const char *name, const void *value, size_t size); int hfsplus_delete_attr(struct inode *inode, const char *name); int hfsplus_delete_all_attrs(struct inode *dir, u32 cnid); +int hfsplus_replace_attr(struct inode *inode, + const char *name, + const void *value, size_t size); /* bitmap.c */ int hfsplus_block_allocate(struct super_block *sb, u32 size, u32 offset, diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c index 7ae6745ca7ae..922ff41df042 100644 --- a/fs/hfsplus/inode.c +++ b/fs/hfsplus/inode.c @@ -328,6 +328,9 @@ int hfsplus_file_fsync(struct file *file, loff_t start, loff_t end, struct hfsplus_vh *vhdr = sbi->s_vhdr; int error = 0, error2; + hfs_dbg("inode->i_ino %lu, start %llu, end %llu\n", + inode->i_ino, start, end); + error = file_write_and_wait_range(file, start, end); if (error) return error; @@ -393,6 +396,19 @@ static const struct inode_operations hfsplus_file_inode_operations = { .fileattr_set = hfsplus_fileattr_set, }; +static const struct inode_operations hfsplus_symlink_inode_operations = { + .get_link = page_get_link, + .setattr = hfsplus_setattr, + .getattr = hfsplus_getattr, + .listxattr = hfsplus_listxattr, +}; + +static const struct inode_operations hfsplus_special_inode_operations = { + .setattr = hfsplus_setattr, + .getattr = hfsplus_getattr, + .listxattr = hfsplus_listxattr, +}; + static const struct file_operations hfsplus_file_operations = { .llseek = generic_file_llseek, .read_iter = generic_file_read_iter, @@ -452,12 +468,17 @@ struct inode *hfsplus_new_inode(struct super_block *sb, struct inode *dir, hip->clump_blocks = sbi->data_clump_blocks; } else if (S_ISLNK(inode->i_mode)) { sbi->file_count++; - inode->i_op = &page_symlink_inode_operations; + inode->i_op = &hfsplus_symlink_inode_operations; inode_nohighmem(inode); inode->i_mapping->a_ops = &hfsplus_aops; hip->clump_blocks = 1; + } else if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode) || + S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) { + sbi->file_count++; + inode->i_op = &hfsplus_special_inode_operations; } else sbi->file_count++; + insert_inode_hash(inode); mark_inode_dirty(inode); hfsplus_mark_mdb_dirty(sb); @@ -588,10 +609,11 @@ int hfsplus_cat_read_inode(struct inode *inode, struct hfs_find_data *fd) inode->i_fop = &hfsplus_file_operations; inode->i_mapping->a_ops = &hfsplus_aops; } else if (S_ISLNK(inode->i_mode)) { - inode->i_op = &page_symlink_inode_operations; + inode->i_op = &hfsplus_symlink_inode_operations; inode_nohighmem(inode); inode->i_mapping->a_ops = &hfsplus_aops; } else { + inode->i_op = &hfsplus_special_inode_operations; init_special_inode(inode, inode->i_mode, be32_to_cpu(file->permissions.dev)); } @@ -612,17 +634,20 @@ out: int hfsplus_cat_write_inode(struct inode *inode) { struct inode *main_inode = inode; + struct hfs_btree *tree = HFSPLUS_SB(inode->i_sb)->cat_tree; struct hfs_find_data fd; hfsplus_cat_entry entry; int res = 0; + hfs_dbg("inode->i_ino %lu\n", inode->i_ino); + if (HFSPLUS_IS_RSRC(inode)) main_inode = HFSPLUS_I(inode)->rsrc_inode; if (!main_inode->i_nlink) return 0; - if (hfs_find_init(HFSPLUS_SB(main_inode->i_sb)->cat_tree, &fd)) + if (hfs_find_init(tree, &fd)) /* panic? */ return -EIO; @@ -687,6 +712,15 @@ int hfsplus_cat_write_inode(struct inode *inode) set_bit(HFSPLUS_I_CAT_DIRTY, &HFSPLUS_I(inode)->flags); out: hfs_find_exit(&fd); + + if (!res) { + res = hfs_btree_write(tree); + if (res) { + pr_err("b-tree write err: %d, ino %lu\n", + res, inode->i_ino); + } + } + return res; } diff --git a/fs/hfsplus/super.c b/fs/hfsplus/super.c index aaffa9e060a0..592d8fbb748c 100644 --- a/fs/hfsplus/super.c +++ b/fs/hfsplus/super.c @@ -53,6 +53,12 @@ static int hfsplus_system_read_inode(struct inode *inode) return -EIO; } + /* + * Assign a dummy file type, for may_open() requires that + * an inode has a valid file type. + */ + inode->i_mode = S_IFREG; + return 0; } @@ -344,8 +350,6 @@ static void hfsplus_put_super(struct super_block *sb) hfs_btree_close(sbi->ext_tree); kfree(sbi->s_vhdr_buf); kfree(sbi->s_backup_vhdr_buf); - call_rcu(&sbi->rcu, delayed_free); - hfs_dbg("finished\n"); } @@ -648,9 +652,7 @@ out_free_vhdr: kfree(sbi->s_vhdr_buf); kfree(sbi->s_backup_vhdr_buf); out_unload_nls: - unload_nls(sbi->nls); unload_nls(nls); - kfree(sbi); return err; } @@ -709,10 +711,18 @@ static int hfsplus_init_fs_context(struct fs_context *fc) return 0; } +static void hfsplus_kill_super(struct super_block *sb) +{ + struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb); + + kill_block_super(sb); + call_rcu(&sbi->rcu, delayed_free); +} + static struct file_system_type hfsplus_fs_type = { .owner = THIS_MODULE, .name = "hfsplus", - .kill_sb = kill_block_super, + .kill_sb = hfsplus_kill_super, .fs_flags = FS_REQUIRES_DEV, .init_fs_context = hfsplus_init_fs_context, }; diff --git a/fs/hfsplus/xattr.c b/fs/hfsplus/xattr.c index da95a9de9a65..9904944cbd54 100644 --- a/fs/hfsplus/xattr.c +++ b/fs/hfsplus/xattr.c @@ -258,6 +258,15 @@ end_attr_file_creation: return err; } +static inline +bool is_xattr_operation_supported(struct inode *inode) +{ + if (HFSPLUS_IS_RSRC(inode)) + return false; + + return true; +} + int __hfsplus_setxattr(struct inode *inode, const char *name, const void *value, size_t size, int flags) { @@ -268,9 +277,11 @@ int __hfsplus_setxattr(struct inode *inode, const char *name, u16 folder_finderinfo_len = sizeof(DInfo) + sizeof(DXInfo); u16 file_finderinfo_len = sizeof(FInfo) + sizeof(FXInfo); - if ((!S_ISREG(inode->i_mode) && - !S_ISDIR(inode->i_mode)) || - HFSPLUS_IS_RSRC(inode)) + hfs_dbg("ino %lu, name %s, value %p, size %zu\n", + inode->i_ino, name ? name : NULL, + value, size); + + if (!is_xattr_operation_supported(inode)) return -EOPNOTSUPP; if (value == NULL) @@ -341,12 +352,11 @@ int __hfsplus_setxattr(struct inode *inode, const char *name, err = -EOPNOTSUPP; goto end_setxattr; } - err = hfsplus_delete_attr(inode, name); - if (err) - goto end_setxattr; - err = hfsplus_create_attr(inode, name, value, size); - if (err) + err = hfsplus_replace_attr(inode, name, value, size); + if (err) { + hfs_dbg("unable to replace xattr: err %d\n", err); goto end_setxattr; + } } else { if (flags & XATTR_REPLACE) { pr_err("cannot replace xattr\n"); @@ -354,8 +364,10 @@ int __hfsplus_setxattr(struct inode *inode, const char *name, goto end_setxattr; } err = hfsplus_create_attr(inode, name, value, size); - if (err) + if (err) { + hfs_dbg("unable to store value: err %d\n", err); goto end_setxattr; + } } cat_entry_type = hfs_bnode_read_u16(cat_fd.bnode, cat_fd.entryoffset); @@ -389,12 +401,13 @@ int __hfsplus_setxattr(struct inode *inode, const char *name, end_setxattr: hfs_find_exit(&cat_fd); + hfs_dbg("finished: res %d\n", err); return err; } -static int name_len(const char *xattr_name, int xattr_name_len) +static size_t name_len(const char *xattr_name, size_t xattr_name_len) { - int len = xattr_name_len + 1; + size_t len = xattr_name_len + 1; if (!is_known_namespace(xattr_name)) len += XATTR_MAC_OSX_PREFIX_LEN; @@ -402,15 +415,22 @@ static int name_len(const char *xattr_name, int xattr_name_len) return len; } -static ssize_t copy_name(char *buffer, const char *xattr_name, int name_len) +static ssize_t copy_name(char *buffer, const char *xattr_name, size_t name_len) { ssize_t len; - if (!is_known_namespace(xattr_name)) + memset(buffer, 0, name_len); + + if (!is_known_namespace(xattr_name)) { len = scnprintf(buffer, name_len + XATTR_MAC_OSX_PREFIX_LEN, "%s%s", XATTR_MAC_OSX_PREFIX, xattr_name); - else + } else { len = strscpy(buffer, xattr_name, name_len + 1); + if (len < 0) { + pr_err("fail to copy name: err %zd\n", len); + len = 0; + } + } /* include NUL-byte in length for non-empty name */ if (len >= 0) @@ -423,16 +443,26 @@ int hfsplus_setxattr(struct inode *inode, const char *name, const char *prefix, size_t prefixlen) { char *xattr_name; + size_t xattr_name_len = + NLS_MAX_CHARSET_SIZE * HFSPLUS_ATTR_MAX_STRLEN + 1; int res; - xattr_name = kmalloc(NLS_MAX_CHARSET_SIZE * HFSPLUS_ATTR_MAX_STRLEN + 1, - GFP_KERNEL); + hfs_dbg("ino %lu, name %s, prefix %s, prefixlen %zu, " + "value %p, size %zu\n", + inode->i_ino, name ? name : NULL, + prefix ? prefix : NULL, prefixlen, + value, size); + + xattr_name = kmalloc(xattr_name_len, GFP_KERNEL); if (!xattr_name) return -ENOMEM; strcpy(xattr_name, prefix); strcpy(xattr_name + prefixlen, name); res = __hfsplus_setxattr(inode, xattr_name, value, size, flags); kfree(xattr_name); + + hfs_dbg("finished: res %d\n", res); + return res; } @@ -496,9 +526,7 @@ ssize_t __hfsplus_getxattr(struct inode *inode, const char *name, u16 record_length = 0; ssize_t res; - if ((!S_ISREG(inode->i_mode) && - !S_ISDIR(inode->i_mode)) || - HFSPLUS_IS_RSRC(inode)) + if (!is_xattr_operation_supported(inode)) return -EOPNOTSUPP; if (!strcmp_xattr_finder_info(name)) @@ -579,6 +607,10 @@ ssize_t hfsplus_getxattr(struct inode *inode, const char *name, int res; char *xattr_name; + hfs_dbg("ino %lu, name %s, prefix %s\n", + inode->i_ino, name ? name : NULL, + prefix ? prefix : NULL); + xattr_name = kmalloc(NLS_MAX_CHARSET_SIZE * HFSPLUS_ATTR_MAX_STRLEN + 1, GFP_KERNEL); if (!xattr_name) @@ -589,6 +621,9 @@ ssize_t hfsplus_getxattr(struct inode *inode, const char *name, res = __hfsplus_getxattr(inode, xattr_name, value, size); kfree(xattr_name); + + hfs_dbg("finished: res %d\n", res); + return res; } @@ -679,11 +714,12 @@ ssize_t hfsplus_listxattr(struct dentry *dentry, char *buffer, size_t size) struct hfs_find_data fd; struct hfsplus_attr_key attr_key; char *strbuf; + size_t strbuf_size; int xattr_name_len; - if ((!S_ISREG(inode->i_mode) && - !S_ISDIR(inode->i_mode)) || - HFSPLUS_IS_RSRC(inode)) + hfs_dbg("ino %lu\n", inode->i_ino); + + if (!is_xattr_operation_supported(inode)) return -EOPNOTSUPP; res = hfsplus_listxattr_finder_info(dentry, buffer, size); @@ -698,8 +734,9 @@ ssize_t hfsplus_listxattr(struct dentry *dentry, char *buffer, size_t size) return err; } - strbuf = kzalloc(NLS_MAX_CHARSET_SIZE * HFSPLUS_ATTR_MAX_STRLEN + - XATTR_MAC_OSX_PREFIX_LEN + 1, GFP_KERNEL); + strbuf_size = NLS_MAX_CHARSET_SIZE * HFSPLUS_ATTR_MAX_STRLEN + + XATTR_MAC_OSX_PREFIX_LEN + 1; + strbuf = kzalloc(strbuf_size, GFP_KERNEL); if (!strbuf) { res = -ENOMEM; goto out; @@ -708,8 +745,7 @@ ssize_t hfsplus_listxattr(struct dentry *dentry, char *buffer, size_t size) err = hfsplus_find_attr(inode->i_sb, inode->i_ino, NULL, &fd); if (err) { if (err == -ENOENT) { - if (res == 0) - res = -ENODATA; + res = 0; goto end_listxattr; } else { res = err; @@ -746,6 +782,9 @@ ssize_t hfsplus_listxattr(struct dentry *dentry, char *buffer, size_t size) res += name_len(strbuf, xattr_name_len); } else if (can_list(strbuf)) { if (size < (res + name_len(strbuf, xattr_name_len))) { + pr_err("size %zu, res %zd, name_len %zu\n", + size, res, + name_len(strbuf, xattr_name_len)); res = -ERANGE; goto end_listxattr; } else @@ -753,6 +792,10 @@ ssize_t hfsplus_listxattr(struct dentry *dentry, char *buffer, size_t size) strbuf, xattr_name_len); } + memset(fd.key->attr.key_name.unicode, 0, + sizeof(fd.key->attr.key_name.unicode)); + memset(strbuf, 0, strbuf_size); + if (hfs_brec_goto(&fd, 1)) goto end_listxattr; } @@ -761,6 +804,9 @@ end_listxattr: kfree(strbuf); out: hfs_find_exit(&fd); + + hfs_dbg("finished: res %zd\n", res); + return res; } @@ -773,6 +819,9 @@ static int hfsplus_removexattr(struct inode *inode, const char *name) int is_xattr_acl_deleted; int is_all_xattrs_deleted; + hfs_dbg("ino %lu, name %s\n", + inode->i_ino, name ? name : NULL); + if (!HFSPLUS_SB(inode->i_sb)->attr_tree) return -EOPNOTSUPP; @@ -833,6 +882,9 @@ static int hfsplus_removexattr(struct inode *inode, const char *name) end_removexattr: hfs_find_exit(&cat_fd); + + hfs_dbg("finished: err %d\n", err); + return err; } diff --git a/fs/init.c b/fs/init.c index e0f5429c0a49..33e312d74f58 100644 --- a/fs/init.c +++ b/fs/init.c @@ -13,6 +13,23 @@ #include <linux/security.h> #include "internal.h" +int __init init_pivot_root(const char *new_root, const char *put_old) +{ + struct path new_path __free(path_put) = {}; + struct path old_path __free(path_put) = {}; + int ret; + + ret = kern_path(new_root, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &new_path); + if (ret) + return ret; + + ret = kern_path(put_old, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &old_path); + if (ret) + return ret; + + return path_pivot_root(&new_path, &old_path); +} + int __init init_mount(const char *dev_name, const char *dir_name, const char *type_page, unsigned long flags, void *data_page) { @@ -140,110 +157,40 @@ int __init init_stat(const char *filename, struct kstat *stat, int flags) int __init init_mknod(const char *filename, umode_t mode, unsigned int dev) { - struct dentry *dentry; - struct path path; - int error; - - if (S_ISFIFO(mode) || S_ISSOCK(mode)) - dev = 0; - else if (!(S_ISBLK(mode) || S_ISCHR(mode))) - return -EINVAL; - - dentry = start_creating_path(AT_FDCWD, filename, &path, 0); - if (IS_ERR(dentry)) - return PTR_ERR(dentry); - - mode = mode_strip_umask(d_inode(path.dentry), mode); - error = security_path_mknod(&path, dentry, mode, dev); - if (!error) - error = vfs_mknod(mnt_idmap(path.mnt), path.dentry->d_inode, - dentry, mode, new_decode_dev(dev), NULL); - end_creating_path(&path, dentry); - return error; + CLASS(filename_kernel, name)(filename); + return filename_mknodat(AT_FDCWD, name, mode, dev); } int __init init_link(const char *oldname, const char *newname) { - struct dentry *new_dentry; - struct path old_path, new_path; - struct mnt_idmap *idmap; - int error; - - error = kern_path(oldname, 0, &old_path); - if (error) - return error; - - new_dentry = start_creating_path(AT_FDCWD, newname, &new_path, 0); - error = PTR_ERR(new_dentry); - if (IS_ERR(new_dentry)) - goto out; - - error = -EXDEV; - if (old_path.mnt != new_path.mnt) - goto out_dput; - idmap = mnt_idmap(new_path.mnt); - error = may_linkat(idmap, &old_path); - if (unlikely(error)) - goto out_dput; - error = security_path_link(old_path.dentry, &new_path, new_dentry); - if (error) - goto out_dput; - error = vfs_link(old_path.dentry, idmap, new_path.dentry->d_inode, - new_dentry, NULL); -out_dput: - end_creating_path(&new_path, new_dentry); -out: - path_put(&old_path); - return error; + CLASS(filename_kernel, old)(oldname); + CLASS(filename_kernel, new)(newname); + return filename_linkat(AT_FDCWD, old, AT_FDCWD, new, 0); } int __init init_symlink(const char *oldname, const char *newname) { - struct dentry *dentry; - struct path path; - int error; - - dentry = start_creating_path(AT_FDCWD, newname, &path, 0); - if (IS_ERR(dentry)) - return PTR_ERR(dentry); - error = security_path_symlink(&path, dentry, oldname); - if (!error) - error = vfs_symlink(mnt_idmap(path.mnt), path.dentry->d_inode, - dentry, oldname, NULL); - end_creating_path(&path, dentry); - return error; + CLASS(filename_kernel, old)(oldname); + CLASS(filename_kernel, new)(newname); + return filename_symlinkat(old, AT_FDCWD, new); } int __init init_unlink(const char *pathname) { - return do_unlinkat(AT_FDCWD, getname_kernel(pathname)); + CLASS(filename_kernel, name)(pathname); + return filename_unlinkat(AT_FDCWD, name); } int __init init_mkdir(const char *pathname, umode_t mode) { - struct dentry *dentry; - struct path path; - int error; - - dentry = start_creating_path(AT_FDCWD, pathname, &path, - LOOKUP_DIRECTORY); - if (IS_ERR(dentry)) - return PTR_ERR(dentry); - mode = mode_strip_umask(d_inode(path.dentry), mode); - error = security_path_mkdir(&path, dentry, mode); - if (!error) { - dentry = vfs_mkdir(mnt_idmap(path.mnt), path.dentry->d_inode, - dentry, mode, NULL); - if (IS_ERR(dentry)) - error = PTR_ERR(dentry); - } - end_creating_path(&path, dentry); - return error; + CLASS(filename_kernel, name)(pathname); + return filename_mkdirat(AT_FDCWD, name, mode); } int __init init_rmdir(const char *pathname) { - return do_rmdir(AT_FDCWD, getname_kernel(pathname)); + CLASS(filename_kernel, name)(pathname); + return filename_rmdir(AT_FDCWD, name); } int __init init_utimes(char *filename, struct timespec64 *ts) diff --git a/fs/inode.c b/fs/inode.c index 379f4c19845c..cc12b68e021b 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -14,6 +14,7 @@ #include <linux/cdev.h> #include <linux/memblock.h> #include <linux/fsnotify.h> +#include <linux/fsverity.h> #include <linux/mount.h> #include <linux/posix_acl.h> #include <linux/buffer_head.h> /* for inode_has_buffers */ @@ -774,6 +775,14 @@ void dump_mapping(const struct address_space *mapping) void clear_inode(struct inode *inode) { /* + * Only IS_VERITY() inodes can have verity info, so start by checking + * for IS_VERITY() (which is faster than retrieving the pointer to the + * verity info). This minimizes overhead for non-verity inodes. + */ + if (IS_ENABLED(CONFIG_FS_VERITY) && IS_VERITY(inode)) + fsverity_cleanup_inode(inode); + + /* * We have to cycle the i_pages lock here because reclaim can be in the * process of removing the last page (in __filemap_remove_folio()) * and we must not free the mapping under it. @@ -1028,19 +1037,20 @@ long prune_icache_sb(struct super_block *sb, struct shrink_control *sc) return freed; } -static void __wait_on_freeing_inode(struct inode *inode, bool is_inode_hash_locked); +static void __wait_on_freeing_inode(struct inode *inode, bool hash_locked, bool rcu_locked); + /* * Called with the inode lock held. */ static struct inode *find_inode(struct super_block *sb, struct hlist_head *head, int (*test)(struct inode *, void *), - void *data, bool is_inode_hash_locked, + void *data, bool hash_locked, bool *isnew) { struct inode *inode = NULL; - if (is_inode_hash_locked) + if (hash_locked) lockdep_assert_held(&inode_hash_lock); else lockdep_assert_not_held(&inode_hash_lock); @@ -1054,7 +1064,7 @@ repeat: continue; spin_lock(&inode->i_lock); if (inode_state_read(inode) & (I_FREEING | I_WILL_FREE)) { - __wait_on_freeing_inode(inode, is_inode_hash_locked); + __wait_on_freeing_inode(inode, hash_locked, true); goto repeat; } if (unlikely(inode_state_read(inode) & I_CREATING)) { @@ -1078,11 +1088,11 @@ repeat: */ static struct inode *find_inode_fast(struct super_block *sb, struct hlist_head *head, unsigned long ino, - bool is_inode_hash_locked, bool *isnew) + bool hash_locked, bool *isnew) { struct inode *inode = NULL; - if (is_inode_hash_locked) + if (hash_locked) lockdep_assert_held(&inode_hash_lock); else lockdep_assert_not_held(&inode_hash_lock); @@ -1096,7 +1106,7 @@ repeat: continue; spin_lock(&inode->i_lock); if (inode_state_read(inode) & (I_FREEING | I_WILL_FREE)) { - __wait_on_freeing_inode(inode, is_inode_hash_locked); + __wait_on_freeing_inode(inode, hash_locked, true); goto repeat; } if (unlikely(inode_state_read(inode) & I_CREATING)) { @@ -1832,16 +1842,13 @@ int insert_inode_locked(struct inode *inode) while (1) { struct inode *old = NULL; spin_lock(&inode_hash_lock); +repeat: hlist_for_each_entry(old, head, i_hash) { if (old->i_ino != ino) continue; if (old->i_sb != sb) continue; spin_lock(&old->i_lock); - if (inode_state_read(old) & (I_FREEING | I_WILL_FREE)) { - spin_unlock(&old->i_lock); - continue; - } break; } if (likely(!old)) { @@ -1852,6 +1859,11 @@ int insert_inode_locked(struct inode *inode) spin_unlock(&inode_hash_lock); return 0; } + if (inode_state_read(old) & (I_FREEING | I_WILL_FREE)) { + __wait_on_freeing_inode(old, true, false); + old = NULL; + goto repeat; + } if (unlikely(inode_state_read(old) & I_CREATING)) { spin_unlock(&old->i_lock); spin_unlock(&inode_hash_lock); @@ -1982,11 +1994,8 @@ retry: if (atomic_add_unless(&inode->i_count, -1, 1)) return; - if ((inode_state_read_once(inode) & I_DIRTY_TIME) && inode->i_nlink) { - trace_writeback_lazytime_iput(inode); - mark_inode_dirty_sync(inode); + if (inode->i_nlink && sync_lazytime(inode)) goto retry; - } spin_lock(&inode->i_lock); if (unlikely((inode_state_read(inode) & I_DIRTY_TIME) && inode->i_nlink)) { @@ -2084,94 +2093,112 @@ static bool relatime_need_update(struct vfsmount *mnt, struct inode *inode, return false; } -/** - * inode_update_timestamps - update the timestamps on the inode - * @inode: inode to be updated - * @flags: S_* flags that needed to be updated - * - * The update_time function is called when an inode's timestamps need to be - * updated for a read or write operation. This function handles updating the - * actual timestamps. It's up to the caller to ensure that the inode is marked - * dirty appropriately. - * - * In the case where any of S_MTIME, S_CTIME, or S_VERSION need to be updated, - * attempt to update all three of them. S_ATIME updates can be handled - * independently of the rest. - * - * Returns a set of S_* flags indicating which values changed. - */ -int inode_update_timestamps(struct inode *inode, int flags) +static int inode_update_atime(struct inode *inode) { - int updated = 0; - struct timespec64 now; + struct timespec64 atime = inode_get_atime(inode); + struct timespec64 now = current_time(inode); - if (flags & (S_MTIME|S_CTIME|S_VERSION)) { - struct timespec64 ctime = inode_get_ctime(inode); - struct timespec64 mtime = inode_get_mtime(inode); + if (timespec64_equal(&now, &atime)) + return 0; - now = inode_set_ctime_current(inode); - if (!timespec64_equal(&now, &ctime)) - updated |= S_CTIME; - if (!timespec64_equal(&now, &mtime)) { - inode_set_mtime_to_ts(inode, now); - updated |= S_MTIME; - } - if (IS_I_VERSION(inode) && inode_maybe_inc_iversion(inode, updated)) - updated |= S_VERSION; - } else { - now = current_time(inode); - } + inode_set_atime_to_ts(inode, now); + return inode_time_dirty_flag(inode); +} - if (flags & S_ATIME) { - struct timespec64 atime = inode_get_atime(inode); +static int inode_update_cmtime(struct inode *inode, unsigned int flags) +{ + struct timespec64 ctime = inode_get_ctime(inode); + struct timespec64 mtime = inode_get_mtime(inode); + struct timespec64 now = inode_set_ctime_current(inode); + unsigned int dirty = 0; + bool mtime_changed; - if (!timespec64_equal(&now, &atime)) { - inode_set_atime_to_ts(inode, now); - updated |= S_ATIME; + mtime_changed = !timespec64_equal(&now, &mtime); + if (mtime_changed || !timespec64_equal(&now, &ctime)) + dirty = inode_time_dirty_flag(inode); + + /* + * Pure timestamp updates can be recorded in the inode without blocking + * by not dirtying the inode. But when the file system requires + * i_version updates, the update of i_version can still block. + * Error out if we'd actually have to update i_version or don't support + * lazytime. + */ + if (IS_I_VERSION(inode)) { + if (flags & IOCB_NOWAIT) { + if (!(inode->i_sb->s_flags & SB_LAZYTIME) || + inode_iversion_need_inc(inode)) + return -EAGAIN; + } else { + if (inode_maybe_inc_iversion(inode, !!dirty)) + dirty |= I_DIRTY_SYNC; } } - return updated; + + if (mtime_changed) + inode_set_mtime_to_ts(inode, now); + return dirty; } -EXPORT_SYMBOL(inode_update_timestamps); /** - * generic_update_time - update the timestamps on the inode + * inode_update_time - update either atime or c/mtime and i_version on the inode * @inode: inode to be updated - * @flags: S_* flags that needed to be updated + * @type: timestamp to be updated + * @flags: flags for the update * - * The update_time function is called when an inode's timestamps need to be - * updated for a read or write operation. In the case where any of S_MTIME, S_CTIME, - * or S_VERSION need to be updated we attempt to update all three of them. S_ATIME - * updates can be handled done independently of the rest. + * Update either atime or c/mtime and version in a inode if needed for a file + * access or modification. It is up to the caller to mark the inode dirty + * appropriately. * - * Returns a S_* mask indicating which fields were updated. + * Returns the positive I_DIRTY_* flags for __mark_inode_dirty() if the inode + * needs to be marked dirty, 0 if it did not, or a negative errno if an error + * happened. */ -int generic_update_time(struct inode *inode, int flags) +int inode_update_time(struct inode *inode, enum fs_update_time type, + unsigned int flags) { - int updated = inode_update_timestamps(inode, flags); - int dirty_flags = 0; - - if (updated & (S_ATIME|S_MTIME|S_CTIME)) - dirty_flags = inode->i_sb->s_flags & SB_LAZYTIME ? I_DIRTY_TIME : I_DIRTY_SYNC; - if (updated & S_VERSION) - dirty_flags |= I_DIRTY_SYNC; - __mark_inode_dirty(inode, dirty_flags); - return updated; + switch (type) { + case FS_UPD_ATIME: + return inode_update_atime(inode); + case FS_UPD_CMTIME: + return inode_update_cmtime(inode, flags); + default: + WARN_ON_ONCE(1); + return -EIO; + } } -EXPORT_SYMBOL(generic_update_time); +EXPORT_SYMBOL(inode_update_time); -/* - * This does the actual work of updating an inodes time or version. Must have - * had called mnt_want_write() before calling this. +/** + * generic_update_time - update the timestamps on the inode + * @inode: inode to be updated + * @type: timestamp to be updated + * @flags: flags for the update + * + * Returns a negative error value on error, else 0. */ -int inode_update_time(struct inode *inode, int flags) +int generic_update_time(struct inode *inode, enum fs_update_time type, + unsigned int flags) { - if (inode->i_op->update_time) - return inode->i_op->update_time(inode, flags); - generic_update_time(inode, flags); + int dirty; + + /* + * ->dirty_inode is what could make generic timestamp updates block. + * Don't support non-blocking timestamp updates here if it is set. + * File systems that implement ->dirty_inode but want to support + * non-blocking timestamp updates should call inode_update_time + * directly. + */ + if ((flags & IOCB_NOWAIT) && inode->i_sb->s_op->dirty_inode) + return -EAGAIN; + + dirty = inode_update_time(inode, type, flags); + if (dirty <= 0) + return dirty; + __mark_inode_dirty(inode, dirty); return 0; } -EXPORT_SYMBOL(inode_update_time); +EXPORT_SYMBOL(generic_update_time); /** * atime_needs_update - update the access time @@ -2240,7 +2267,10 @@ void touch_atime(const struct path *path) * We may also fail on filesystems that have the ability to make parts * of the fs read only, e.g. subvolumes in Btrfs. */ - inode_update_time(inode, S_ATIME); + if (inode->i_op->update_time) + inode->i_op->update_time(inode, FS_UPD_ATIME, 0); + else + generic_update_time(inode, FS_UPD_ATIME, 0); mnt_put_write_access(mnt); skip_update: sb_end_write(inode->i_sb); @@ -2363,39 +2393,39 @@ out: } EXPORT_SYMBOL(current_time); +static inline bool need_cmtime_update(struct inode *inode) +{ + struct timespec64 now = current_time(inode), ts; + + ts = inode_get_mtime(inode); + if (!timespec64_equal(&ts, &now)) + return true; + ts = inode_get_ctime(inode); + if (!timespec64_equal(&ts, &now)) + return true; + return IS_I_VERSION(inode) && inode_iversion_need_inc(inode); +} + static int file_update_time_flags(struct file *file, unsigned int flags) { struct inode *inode = file_inode(file); - struct timespec64 now, ts; - int sync_mode = 0; - int ret = 0; + int ret; /* First try to exhaust all avenues to not sync */ if (IS_NOCMTIME(inode)) return 0; if (unlikely(file->f_mode & FMODE_NOCMTIME)) return 0; - - now = current_time(inode); - - ts = inode_get_mtime(inode); - if (!timespec64_equal(&ts, &now)) - sync_mode |= S_MTIME; - ts = inode_get_ctime(inode); - if (!timespec64_equal(&ts, &now)) - sync_mode |= S_CTIME; - if (IS_I_VERSION(inode) && inode_iversion_need_inc(inode)) - sync_mode |= S_VERSION; - - if (!sync_mode) + if (!need_cmtime_update(inode)) return 0; - if (flags & IOCB_NOWAIT) - return -EAGAIN; - + flags &= IOCB_NOWAIT; if (mnt_get_write_access_file(file)) return 0; - ret = inode_update_time(inode, sync_mode); + if (inode->i_op->update_time) + ret = inode->i_op->update_time(inode, FS_UPD_CMTIME, flags); + else + ret = generic_update_time(inode, FS_UPD_CMTIME, flags); mnt_put_write_access_file(file); return ret; } @@ -2504,16 +2534,18 @@ EXPORT_SYMBOL(inode_needs_sync); * wake_up_bit(&inode->i_state, __I_NEW) after removing from the hash list * will DTRT. */ -static void __wait_on_freeing_inode(struct inode *inode, bool is_inode_hash_locked) +static void __wait_on_freeing_inode(struct inode *inode, bool hash_locked, bool rcu_locked) { struct wait_bit_queue_entry wqe; struct wait_queue_head *wq_head; + VFS_BUG_ON(!hash_locked && !rcu_locked); + /* * Handle racing against evict(), see that routine for more details. */ if (unlikely(inode_unhashed(inode))) { - WARN_ON(is_inode_hash_locked); + WARN_ON(hash_locked); spin_unlock(&inode->i_lock); return; } @@ -2521,23 +2553,22 @@ static void __wait_on_freeing_inode(struct inode *inode, bool is_inode_hash_lock wq_head = inode_bit_waitqueue(&wqe, inode, __I_NEW); prepare_to_wait_event(wq_head, &wqe.wq_entry, TASK_UNINTERRUPTIBLE); spin_unlock(&inode->i_lock); - rcu_read_unlock(); - if (is_inode_hash_locked) + if (rcu_locked) + rcu_read_unlock(); + if (hash_locked) spin_unlock(&inode_hash_lock); schedule(); finish_wait(wq_head, &wqe.wq_entry); - if (is_inode_hash_locked) + if (hash_locked) spin_lock(&inode_hash_lock); - rcu_read_lock(); + if (rcu_locked) + rcu_read_lock(); } static __initdata unsigned long ihash_entries; static int __init set_ihash_entries(char *str) { - if (!str) - return 0; - ihash_entries = simple_strtoul(str, &str, 0); - return 1; + return kstrtoul(str, 0, &ihash_entries) == 0; } __setup("ihash_entries=", set_ihash_entries); @@ -2987,24 +3018,45 @@ umode_t mode_strip_sgid(struct mnt_idmap *idmap, EXPORT_SYMBOL(mode_strip_sgid); #ifdef CONFIG_DEBUG_VFS -/* - * Dump an inode. - * - * TODO: add a proper inode dumping routine, this is a stub to get debug off the - * ground. +/** + * dump_inode - dump an inode. + * @inode: inode to dump + * @reason: reason for dumping * - * TODO: handle getting to fs type with get_kernel_nofault()? - * See dump_mapping() above. + * If inode is an invalid pointer, we don't want to crash accessing it, + * so probe everything depending on it carefully with get_kernel_nofault(). */ void dump_inode(struct inode *inode, const char *reason) { - struct super_block *sb = inode->i_sb; + struct super_block *sb; + struct file_system_type *s_type; + const char *fs_name_ptr; + char fs_name[32] = {}; + umode_t mode; + unsigned short opflags; + unsigned int flags; + unsigned int state; + int count; + + if (get_kernel_nofault(sb, &inode->i_sb) || + get_kernel_nofault(mode, &inode->i_mode) || + get_kernel_nofault(opflags, &inode->i_opflags) || + get_kernel_nofault(flags, &inode->i_flags)) { + pr_warn("%s: unreadable inode:%px\n", reason, inode); + return; + } - pr_warn("%s encountered for inode %px\n" - "fs %s mode %ho opflags 0x%hx flags 0x%x state 0x%x count %d\n", - reason, inode, sb->s_type->name, inode->i_mode, inode->i_opflags, - inode->i_flags, inode_state_read_once(inode), atomic_read(&inode->i_count)); -} + state = inode_state_read_once(inode); + count = atomic_read(&inode->i_count); + if (!sb || + get_kernel_nofault(s_type, &sb->s_type) || !s_type || + get_kernel_nofault(fs_name_ptr, &s_type->name) || !fs_name_ptr || + strncpy_from_kernel_nofault(fs_name, fs_name_ptr, sizeof(fs_name) - 1) < 0) + strscpy(fs_name, "<unknown, sb unreadable>"); + + pr_warn("%s: inode:%px fs:%s mode:%ho opflags:%#x flags:%#x state:%#x count:%d\n", + reason, inode, fs_name, mode, opflags, flags, state, count); +} EXPORT_SYMBOL(dump_inode); #endif diff --git a/fs/internal.h b/fs/internal.h index ab638d41ab81..cbc384a1aa09 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -44,7 +44,6 @@ extern void __init chrdev_init(void); /* * fs_context.c */ -extern const struct fs_context_operations legacy_fs_context_ops; extern int parse_monolithic_mount_data(struct fs_context *, void *); extern void vfs_clean_context(struct fs_context *fc); extern int finish_clean_context(struct fs_context *fc); @@ -54,14 +53,15 @@ extern int finish_clean_context(struct fs_context *fc); */ extern int filename_lookup(int dfd, struct filename *name, unsigned flags, struct path *path, const struct path *root); -int do_rmdir(int dfd, struct filename *name); -int do_unlinkat(int dfd, struct filename *name); +int filename_rmdir(int dfd, struct filename *name); +int filename_unlinkat(int dfd, struct filename *name); int may_linkat(struct mnt_idmap *idmap, const struct path *link); -int do_renameat2(int olddfd, struct filename *oldname, int newdfd, +int filename_renameat2(int olddfd, struct filename *oldname, int newdfd, struct filename *newname, unsigned int flags); -int do_mkdirat(int dfd, struct filename *name, umode_t mode); -int do_symlinkat(struct filename *from, int newdfd, struct filename *to); -int do_linkat(int olddfd, struct filename *old, int newdfd, +int filename_mkdirat(int dfd, struct filename *name, umode_t mode); +int filename_mknodat(int dfd, struct filename *name, umode_t mode, unsigned int dev); +int filename_symlinkat(struct filename *from, int newdfd, struct filename *to); +int filename_linkat(int olddfd, struct filename *old, int newdfd, struct filename *new, int flags); int vfs_tmpfile(struct mnt_idmap *idmap, const struct path *parentpath, @@ -71,6 +71,8 @@ struct dentry *start_dirop(struct dentry *parent, struct qstr *name, unsigned int lookup_flags); int lookup_noperm_common(struct qstr *qname, struct dentry *base); +void __init filename_init(void); + /* * namespace.c */ @@ -90,6 +92,7 @@ extern bool may_mount(void); int path_mount(const char *dev_name, const struct path *path, const char *type_page, unsigned long flags, void *data_page); int path_umount(const struct path *path, int flags); +int path_pivot_root(struct path *new, struct path *old); int show_path(struct seq_file *m, struct dentry *root); @@ -187,7 +190,7 @@ struct open_flags { int intent; int lookup_flags; }; -extern struct file *do_filp_open(int dfd, struct filename *pathname, +extern struct file *do_file_open(int dfd, struct filename *pathname, const struct open_flags *op); extern struct file *do_file_open_root(const struct path *, const char *, const struct open_flags *); @@ -214,7 +217,8 @@ bool in_group_or_capable(struct mnt_idmap *idmap, /* * fs-writeback.c */ -extern long get_nr_dirty_inodes(void); +long get_nr_dirty_inodes(void); +bool sync_lazytime(struct inode *inode); /* * dcache.c @@ -247,6 +251,7 @@ extern void mnt_pin_kill(struct mount *m); */ extern const struct dentry_operations ns_dentry_operations; int open_namespace(struct ns_common *ns); +struct file *open_namespace_file(struct ns_common *ns); /* * fs/stat.c: diff --git a/fs/iomap/buffered-io.c b/fs/iomap/buffered-io.c index fd9a2cf95620..1fe19b4ee2f4 100644 --- a/fs/iomap/buffered-io.c +++ b/fs/iomap/buffered-io.c @@ -8,6 +8,7 @@ #include <linux/writeback.h> #include <linux/swap.h> #include <linux/migrate.h> +#include <linux/fserror.h> #include "internal.h" #include "trace.h" @@ -371,8 +372,11 @@ static int iomap_read_inline_data(const struct iomap_iter *iter, if (folio_test_uptodate(folio)) return 0; - if (WARN_ON_ONCE(size > iomap->length)) + if (WARN_ON_ONCE(size > iomap->length)) { + fserror_report_io(iter->inode, FSERR_BUFFERED_READ, + iomap->offset, size, -EIO, GFP_NOFS); return -EIO; + } if (offset > 0) ifs_alloc(iter->inode, folio, iter->flags); @@ -399,6 +403,11 @@ void iomap_finish_folio_read(struct folio *folio, size_t off, size_t len, spin_unlock_irqrestore(&ifs->state_lock, flags); } + if (error) + fserror_report_io(folio->mapping->host, FSERR_BUFFERED_READ, + folio_pos(folio) + off, len, error, + GFP_ATOMIC); + if (finished) folio_end_read(folio, uptodate); } @@ -409,8 +418,6 @@ static void iomap_read_init(struct folio *folio) struct iomap_folio_state *ifs = folio->private; if (ifs) { - size_t len = folio_size(folio); - /* * ifs->read_bytes_pending is used to track how many bytes are * read in asynchronously by the IO helper. We need to track @@ -418,23 +425,19 @@ static void iomap_read_init(struct folio *folio) * reading in all the necessary ranges of the folio and can end * the read. * - * Increase ->read_bytes_pending by the folio size to start, and - * add a +1 bias. We'll subtract the bias and any uptodate / - * zeroed ranges that did not require IO in iomap_read_end() - * after we're done processing the folio. + * Increase ->read_bytes_pending by the folio size to start. + * We'll subtract any uptodate / zeroed ranges that did not + * require IO in iomap_read_end() after we're done processing + * the folio. * * We do this because otherwise, we would have to increment * ifs->read_bytes_pending every time a range in the folio needs * to be read in, which can get expensive since the spinlock * needs to be held whenever modifying ifs->read_bytes_pending. - * - * We add the bias to ensure the read has not been ended on the - * folio when iomap_read_end() is called, even if the IO helper - * has already finished reading in the entire folio. */ spin_lock_irq(&ifs->state_lock); WARN_ON_ONCE(ifs->read_bytes_pending != 0); - ifs->read_bytes_pending = len + 1; + ifs->read_bytes_pending = folio_size(folio); spin_unlock_irq(&ifs->state_lock); } } @@ -465,11 +468,9 @@ static void iomap_read_end(struct folio *folio, size_t bytes_submitted) /* * Subtract any bytes that were initially accounted to - * read_bytes_pending but skipped for IO. The +1 accounts for - * the bias we added in iomap_read_init(). + * read_bytes_pending but skipped for IO. */ - ifs->read_bytes_pending -= - (folio_size(folio) + 1 - bytes_submitted); + ifs->read_bytes_pending -= folio_size(folio) - bytes_submitted; /* * If !ifs->read_bytes_pending, this means all pending reads by @@ -483,14 +484,16 @@ static void iomap_read_end(struct folio *folio, size_t bytes_submitted) spin_unlock_irq(&ifs->state_lock); if (end_read) folio_end_read(folio, uptodate); - } else if (!bytes_submitted) { + } else { /* - * If there were no bytes submitted, this means we are - * responsible for unlocking the folio here, since no IO helper - * has taken ownership of it. If there were bytes submitted, - * then the IO helper will end the read via - * iomap_finish_folio_read(). + * If a folio without an ifs is submitted to the IO helper, the + * read must be on the entire folio and the IO helper takes + * ownership of the folio. This means we should only enter + * iomap_read_end() for the !ifs case if no bytes were submitted + * to the IO helper, in which case we are responsible for + * unlocking the folio here. */ + WARN_ON_ONCE(bytes_submitted); folio_unlock(folio); } } @@ -502,6 +505,7 @@ static int iomap_read_folio_iter(struct iomap_iter *iter, loff_t pos = iter->pos; loff_t length = iomap_length(iter); struct folio *folio = ctx->cur_folio; + size_t folio_len = folio_size(folio); size_t poff, plen; loff_t pos_diff; int ret; @@ -515,8 +519,7 @@ static int iomap_read_folio_iter(struct iomap_iter *iter, ifs_alloc(iter->inode, folio, iter->flags); - length = min_t(loff_t, length, - folio_size(folio) - offset_in_folio(folio, pos)); + length = min_t(loff_t, length, folio_len - offset_in_folio(folio, pos)); while (length) { iomap_adjust_read_range(iter->inode, folio, &pos, length, &poff, &plen); @@ -540,9 +543,21 @@ static int iomap_read_folio_iter(struct iomap_iter *iter, if (!*bytes_submitted) iomap_read_init(folio); ret = ctx->ops->read_folio_range(iter, ctx, plen); + if (ret < 0) + fserror_report_io(iter->inode, + FSERR_BUFFERED_READ, pos, + plen, ret, GFP_NOFS); if (ret) return ret; + *bytes_submitted += plen; + /* + * If the entire folio has been read in by the IO + * helper, then the helper owns the folio and will end + * the read on it. + */ + if (*bytes_submitted == folio_len) + ctx->cur_folio = NULL; } ret = iomap_iter_advance(iter, plen); @@ -555,13 +570,14 @@ static int iomap_read_folio_iter(struct iomap_iter *iter, } void iomap_read_folio(const struct iomap_ops *ops, - struct iomap_read_folio_ctx *ctx) + struct iomap_read_folio_ctx *ctx, void *private) { struct folio *folio = ctx->cur_folio; struct iomap_iter iter = { .inode = folio->mapping->host, .pos = folio_pos(folio), .len = folio_size(folio), + .private = private, }; size_t bytes_submitted = 0; int ret; @@ -575,7 +591,8 @@ void iomap_read_folio(const struct iomap_ops *ops, if (ctx->ops->submit_read) ctx->ops->submit_read(ctx); - iomap_read_end(folio, bytes_submitted); + if (ctx->cur_folio) + iomap_read_end(ctx->cur_folio, bytes_submitted); } EXPORT_SYMBOL_GPL(iomap_read_folio); @@ -620,13 +637,14 @@ static int iomap_readahead_iter(struct iomap_iter *iter, * the filesystem to be reentered. */ void iomap_readahead(const struct iomap_ops *ops, - struct iomap_read_folio_ctx *ctx) + struct iomap_read_folio_ctx *ctx, void *private) { struct readahead_control *rac = ctx->rac; struct iomap_iter iter = { .inode = rac->mapping->host, .pos = readahead_pos(rac), .len = readahead_length(rac), + .private = private, }; size_t cur_bytes_submitted; @@ -815,6 +833,10 @@ static int __iomap_write_begin(const struct iomap_iter *iter, else status = iomap_bio_read_folio_range_sync(iter, folio, block_start, plen); + if (status < 0) + fserror_report_io(iter->inode, + FSERR_BUFFERED_READ, pos, + len, status, GFP_NOFS); if (status) return status; } @@ -851,6 +873,7 @@ static struct folio *__iomap_get_folio(struct iomap_iter *iter, } folio_get(folio); + folio_wait_stable(folio); return folio; } @@ -1825,6 +1848,7 @@ int iomap_writeback_folio(struct iomap_writepage_ctx *wpc, struct folio *folio) u64 pos = folio_pos(folio); u64 end_pos = pos + folio_size(folio); u64 end_aligned = 0; + loff_t orig_pos = pos; size_t bytes_submitted = 0; int error = 0; u32 rlen; @@ -1868,6 +1892,9 @@ int iomap_writeback_folio(struct iomap_writepage_ctx *wpc, struct folio *folio) if (bytes_submitted) wpc->nr_folios++; + if (error && pos > orig_pos) + fserror_report_io(inode, FSERR_BUFFERED_WRITE, orig_pos, 0, + error, GFP_NOFS); /* * We can have dirty bits set past end of file in page_mkwrite path diff --git a/fs/iomap/direct-io.c b/fs/iomap/direct-io.c index 8e273408453a..8c1fd7573aee 100644 --- a/fs/iomap/direct-io.c +++ b/fs/iomap/direct-io.c @@ -3,10 +3,12 @@ * Copyright (C) 2010 Red Hat, Inc. * Copyright (c) 2016-2025 Christoph Hellwig. */ +#include <linux/blk-crypto.h> #include <linux/fscrypt.h> #include <linux/pagemap.h> #include <linux/iomap.h> #include <linux/task_io_accounting_ops.h> +#include <linux/fserror.h> #include "internal.h" #include "trace.h" @@ -21,7 +23,7 @@ #define IOMAP_DIO_WRITE_THROUGH (1U << 28) #define IOMAP_DIO_NEED_SYNC (1U << 29) #define IOMAP_DIO_WRITE (1U << 30) -#define IOMAP_DIO_DIRTY (1U << 31) +#define IOMAP_DIO_USER_BACKED (1U << 31) struct iomap_dio { struct kiocb *iocb; @@ -74,10 +76,17 @@ static void iomap_dio_submit_bio(const struct iomap_iter *iter, dio->dops->submit_io(iter, bio, pos); } else { WARN_ON_ONCE(iter->iomap.flags & IOMAP_F_ANON_WRITE); - submit_bio(bio); + blk_crypto_submit_bio(bio); } } +static inline enum fserror_type iomap_dio_err_type(const struct iomap_dio *dio) +{ + if (dio->flags & IOMAP_DIO_WRITE) + return FSERR_DIRECTIO_WRITE; + return FSERR_DIRECTIO_READ; +} + ssize_t iomap_dio_complete(struct iomap_dio *dio) { const struct iomap_dio_ops *dops = dio->dops; @@ -87,6 +96,10 @@ ssize_t iomap_dio_complete(struct iomap_dio *dio) if (dops && dops->end_io) ret = dops->end_io(iocb, dio->size, ret, dio->flags); + if (dio->error) + fserror_report_io(file_inode(iocb->ki_filp), + iomap_dio_err_type(dio), offset, dio->size, + dio->error, GFP_NOFS); if (likely(!ret)) { ret = dio->size; @@ -210,51 +223,52 @@ static void iomap_dio_done(struct iomap_dio *dio) iomap_dio_complete_work(&dio->aio.work); } -void iomap_dio_bio_end_io(struct bio *bio) +static void __iomap_dio_bio_end_io(struct bio *bio, bool inline_completion) { struct iomap_dio *dio = bio->bi_private; - bool should_dirty = (dio->flags & IOMAP_DIO_DIRTY); - if (bio->bi_status) - iomap_dio_set_error(dio, blk_status_to_errno(bio->bi_status)); - - if (atomic_dec_and_test(&dio->ref)) - iomap_dio_done(dio); - - if (should_dirty) { + if (dio->flags & IOMAP_DIO_BOUNCE) { + bio_iov_iter_unbounce(bio, !!dio->error, + dio->flags & IOMAP_DIO_USER_BACKED); + bio_put(bio); + } else if (dio->flags & IOMAP_DIO_USER_BACKED) { bio_check_pages_dirty(bio); } else { bio_release_pages(bio, false); bio_put(bio); } + + /* Do not touch bio below, we just gave up our reference. */ + + if (atomic_dec_and_test(&dio->ref)) { + /* + * Avoid another context switch for the completion when already + * called from the ioend completion workqueue. + */ + if (inline_completion) + dio->flags &= ~IOMAP_DIO_COMP_WORK; + iomap_dio_done(dio); + } +} + +void iomap_dio_bio_end_io(struct bio *bio) +{ + struct iomap_dio *dio = bio->bi_private; + + if (bio->bi_status) + iomap_dio_set_error(dio, blk_status_to_errno(bio->bi_status)); + __iomap_dio_bio_end_io(bio, false); } EXPORT_SYMBOL_GPL(iomap_dio_bio_end_io); u32 iomap_finish_ioend_direct(struct iomap_ioend *ioend) { struct iomap_dio *dio = ioend->io_bio.bi_private; - bool should_dirty = (dio->flags & IOMAP_DIO_DIRTY); u32 vec_count = ioend->io_bio.bi_vcnt; if (ioend->io_error) iomap_dio_set_error(dio, ioend->io_error); - - if (atomic_dec_and_test(&dio->ref)) { - /* - * Try to avoid another context switch for the completion given - * that we are already called from the ioend completion - * workqueue. - */ - dio->flags &= ~IOMAP_DIO_COMP_WORK; - iomap_dio_done(dio); - } - - if (should_dirty) { - bio_check_pages_dirty(&ioend->io_bio); - } else { - bio_release_pages(&ioend->io_bio, false); - bio_put(&ioend->io_bio); - } + __iomap_dio_bio_end_io(&ioend->io_bio, true); /* * Return the number of bvecs completed as even direct I/O completions @@ -301,6 +315,65 @@ static int iomap_dio_zero(const struct iomap_iter *iter, struct iomap_dio *dio, return 0; } +static ssize_t iomap_dio_bio_iter_one(struct iomap_iter *iter, + struct iomap_dio *dio, loff_t pos, unsigned int alignment, + blk_opf_t op) +{ + unsigned int nr_vecs; + struct bio *bio; + ssize_t ret; + + if (dio->flags & IOMAP_DIO_BOUNCE) + nr_vecs = bio_iov_bounce_nr_vecs(dio->submit.iter, op); + else + nr_vecs = bio_iov_vecs_to_alloc(dio->submit.iter, BIO_MAX_VECS); + + bio = iomap_dio_alloc_bio(iter, dio, nr_vecs, op); + fscrypt_set_bio_crypt_ctx(bio, iter->inode, + pos >> iter->inode->i_blkbits, GFP_KERNEL); + bio->bi_iter.bi_sector = iomap_sector(&iter->iomap, pos); + bio->bi_write_hint = iter->inode->i_write_hint; + bio->bi_ioprio = dio->iocb->ki_ioprio; + bio->bi_private = dio; + bio->bi_end_io = iomap_dio_bio_end_io; + + if (dio->flags & IOMAP_DIO_BOUNCE) + ret = bio_iov_iter_bounce(bio, dio->submit.iter); + else + ret = bio_iov_iter_get_pages(bio, dio->submit.iter, + alignment - 1); + if (unlikely(ret)) + goto out_put_bio; + ret = bio->bi_iter.bi_size; + + /* + * An atomic write bio must cover the complete length. If it doesn't, + * error out. + */ + if ((op & REQ_ATOMIC) && WARN_ON_ONCE(ret != iomap_length(iter))) { + ret = -EINVAL; + goto out_put_bio; + } + + if (dio->flags & IOMAP_DIO_WRITE) + task_io_account_write(ret); + else if ((dio->flags & IOMAP_DIO_USER_BACKED) && + !(dio->flags & IOMAP_DIO_BOUNCE)) + bio_set_pages_dirty(bio); + + /* + * We can only poll for single bio I/Os. + */ + if (iov_iter_count(dio->submit.iter)) + dio->iocb->ki_flags &= ~IOCB_HIPRI; + iomap_dio_submit_bio(iter, dio, bio, pos); + return ret; + +out_put_bio: + bio_put(bio); + return ret; +} + static int iomap_dio_bio_iter(struct iomap_iter *iter, struct iomap_dio *dio) { const struct iomap *iomap = &iter->iomap; @@ -309,12 +382,11 @@ static int iomap_dio_bio_iter(struct iomap_iter *iter, struct iomap_dio *dio) const loff_t length = iomap_length(iter); loff_t pos = iter->pos; blk_opf_t bio_opf = REQ_SYNC | REQ_IDLE; - struct bio *bio; bool need_zeroout = false; - int nr_pages, ret = 0; u64 copied = 0; size_t orig_count; unsigned int alignment; + ssize_t ret = 0; /* * File systems that write out of place and always allocate new blocks @@ -439,67 +511,29 @@ static int iomap_dio_bio_iter(struct iomap_iter *iter, struct iomap_dio *dio) goto out; } - nr_pages = bio_iov_vecs_to_alloc(dio->submit.iter, BIO_MAX_VECS); do { - size_t n; - if (dio->error) { - iov_iter_revert(dio->submit.iter, copied); - copied = ret = 0; + /* + * If completions already occurred and reported errors, give up now and + * don't bother submitting more bios. + */ + if (unlikely(data_race(dio->error))) goto out; - } - bio = iomap_dio_alloc_bio(iter, dio, nr_pages, bio_opf); - fscrypt_set_bio_crypt_ctx(bio, inode, pos >> inode->i_blkbits, - GFP_KERNEL); - bio->bi_iter.bi_sector = iomap_sector(iomap, pos); - bio->bi_write_hint = inode->i_write_hint; - bio->bi_ioprio = dio->iocb->ki_ioprio; - bio->bi_private = dio; - bio->bi_end_io = iomap_dio_bio_end_io; - - ret = bio_iov_iter_get_pages(bio, dio->submit.iter, - alignment - 1); - if (unlikely(ret)) { + ret = iomap_dio_bio_iter_one(iter, dio, pos, alignment, bio_opf); + if (unlikely(ret < 0)) { /* * We have to stop part way through an IO. We must fall * through to the sub-block tail zeroing here, otherwise * this short IO may expose stale data in the tail of * the block we haven't written data to. */ - bio_put(bio); - goto zero_tail; - } - - n = bio->bi_iter.bi_size; - if (WARN_ON_ONCE((bio_opf & REQ_ATOMIC) && n != length)) { - /* - * An atomic write bio must cover the complete length, - * which it doesn't, so error. We may need to zero out - * the tail (complete FS block), similar to when - * bio_iov_iter_get_pages() returns an error, above. - */ - ret = -EINVAL; - bio_put(bio); - goto zero_tail; + break; } - if (dio->flags & IOMAP_DIO_WRITE) - task_io_account_write(n); - else if (dio->flags & IOMAP_DIO_DIRTY) - bio_set_pages_dirty(bio); - - dio->size += n; - copied += n; - - nr_pages = bio_iov_vecs_to_alloc(dio->submit.iter, - BIO_MAX_VECS); - /* - * We can only poll for single bio I/Os. - */ - if (nr_pages) - dio->iocb->ki_flags &= ~IOCB_HIPRI; - iomap_dio_submit_bio(iter, dio, bio, pos); - pos += n; - } while (nr_pages); + dio->size += ret; + copied += ret; + pos += ret; + ret = 0; + } while (iov_iter_count(dio->submit.iter)); /* * We need to zeroout the tail of a sub-block write if the extent type @@ -507,7 +541,6 @@ static int iomap_dio_bio_iter(struct iomap_iter *iter, struct iomap_dio *dio) * the block tail in the latter case, we can expose stale data via mmap * reads of the EOF block. */ -zero_tail: if (need_zeroout || ((dio->flags & IOMAP_DIO_WRITE) && pos >= i_size_read(inode))) { /* zero out from the end of the write to the end of the block */ @@ -654,7 +687,7 @@ __iomap_dio_rw(struct kiocb *iocb, struct iov_iter *iter, dio->i_size = i_size_read(inode); dio->dops = dops; dio->error = 0; - dio->flags = 0; + dio->flags = dio_flags & (IOMAP_DIO_FSBLOCK_ALIGNED | IOMAP_DIO_BOUNCE); dio->done_before = done_before; dio->submit.iter = iter; @@ -663,15 +696,12 @@ __iomap_dio_rw(struct kiocb *iocb, struct iov_iter *iter, if (iocb->ki_flags & IOCB_NOWAIT) iomi.flags |= IOMAP_NOWAIT; - if (dio_flags & IOMAP_DIO_FSBLOCK_ALIGNED) - dio->flags |= IOMAP_DIO_FSBLOCK_ALIGNED; - if (iov_iter_rw(iter) == READ) { if (iomi.pos >= dio->i_size) goto out_free_dio; if (user_backed_iter(iter)) - dio->flags |= IOMAP_DIO_DIRTY; + dio->flags |= IOMAP_DIO_USER_BACKED; ret = kiocb_write_and_wait(iocb, iomi.len); if (ret) diff --git a/fs/iomap/ioend.c b/fs/iomap/ioend.c index 86f44922ed3b..e4d57cb969f1 100644 --- a/fs/iomap/ioend.c +++ b/fs/iomap/ioend.c @@ -6,6 +6,7 @@ #include <linux/list_sort.h> #include <linux/pagemap.h> #include <linux/writeback.h> +#include <linux/fserror.h> #include "internal.h" #include "trace.h" @@ -55,6 +56,11 @@ static u32 iomap_finish_ioend_buffered(struct iomap_ioend *ioend) /* walk all folios in bio, ending page IO on them */ bio_for_each_folio_all(fi, bio) { + if (ioend->io_error) + fserror_report_io(inode, FSERR_BUFFERED_WRITE, + folio_pos(fi.folio) + fi.offset, + fi.length, ioend->io_error, + GFP_ATOMIC); iomap_finish_folio_write(inode, fi.folio, fi.length); folio_count++; } @@ -299,6 +305,14 @@ EXPORT_SYMBOL_GPL(iomap_finish_ioends); static bool iomap_ioend_can_merge(struct iomap_ioend *ioend, struct iomap_ioend *next) { + /* + * There is no point in merging reads as there is no completion + * processing that can be easily batched up for them. + */ + if (bio_op(&ioend->io_bio) == REQ_OP_READ || + bio_op(&next->io_bio) == REQ_OP_READ) + return false; + if (ioend->io_bio.bi_status != next->io_bio.bi_status) return false; if (next->io_flags & IOMAP_IOEND_BOUNDARY) diff --git a/fs/isofs/dir.c b/fs/isofs/dir.c index 09df40b612fb..2ca16c3fe5ef 100644 --- a/fs/isofs/dir.c +++ b/fs/isofs/dir.c @@ -12,6 +12,7 @@ * isofs directory handling functions */ #include <linux/gfp.h> +#include <linux/filelock.h> #include "isofs.h" int isofs_name_translate(struct iso_directory_record *de, char *new, struct inode *inode) @@ -271,6 +272,7 @@ const struct file_operations isofs_dir_operations = .llseek = generic_file_llseek, .read = generic_read_dir, .iterate_shared = isofs_readdir, + .setlease = generic_setlease, }; /* diff --git a/fs/isofs/rock.c b/fs/isofs/rock.c index 576498245b9d..6fe6dbd0c740 100644 --- a/fs/isofs/rock.c +++ b/fs/isofs/rock.c @@ -271,7 +271,7 @@ repeat: break; } len = rr->len - 5; - if (retnamlen + len >= 254) { + if (retnamlen + len > NAME_MAX) { truncate = 1; break; } diff --git a/fs/jffs2/dir.c b/fs/jffs2/dir.c index dd91f725ded6..2b38ce1fd8e8 100644 --- a/fs/jffs2/dir.c +++ b/fs/jffs2/dir.c @@ -15,6 +15,7 @@ #include <linux/kernel.h> #include <linux/slab.h> #include <linux/fs.h> +#include <linux/filelock.h> #include <linux/crc32.h> #include <linux/jffs2.h> #include "jffs2_fs_i.h" @@ -48,6 +49,7 @@ const struct file_operations jffs2_dir_operations = .unlocked_ioctl=jffs2_ioctl, .fsync = jffs2_fsync, .llseek = generic_file_llseek, + .setlease = generic_setlease, }; diff --git a/fs/jffs2/file.c b/fs/jffs2/file.c index b697f3c259ef..5e1ef4bc009b 100644 --- a/fs/jffs2/file.c +++ b/fs/jffs2/file.c @@ -14,6 +14,7 @@ #include <linux/kernel.h> #include <linux/fs.h> +#include <linux/filelock.h> #include <linux/time.h> #include <linux/pagemap.h> #include <linux/highmem.h> @@ -60,6 +61,7 @@ const struct file_operations jffs2_file_operations = .fsync = jffs2_fsync, .splice_read = filemap_splice_read, .splice_write = iter_file_splice_write, + .setlease = generic_setlease, }; /* jffs2_file_inode_operations */ diff --git a/fs/jfs/acl.c b/fs/jfs/acl.c index 1de3602c98de..16b71a23ff1e 100644 --- a/fs/jfs/acl.c +++ b/fs/jfs/acl.c @@ -61,7 +61,7 @@ static int __jfs_set_acl(tid_t tid, struct inode *inode, int type, { char *ea_name; int rc; - int size = 0; + size_t size = 0; char *value = NULL; switch (type) { @@ -76,16 +76,11 @@ static int __jfs_set_acl(tid_t tid, struct inode *inode, int type, } if (acl) { - size = posix_acl_xattr_size(acl->a_count); - value = kmalloc(size, GFP_KERNEL); + value = posix_acl_to_xattr(&init_user_ns, acl, &size, GFP_KERNEL); if (!value) return -ENOMEM; - rc = posix_acl_to_xattr(&init_user_ns, acl, value, size); - if (rc < 0) - goto out; } rc = __jfs_setxattr(tid, inode, ea_name, value, size, 0); -out: kfree(value); if (!rc) diff --git a/fs/jfs/file.c b/fs/jfs/file.c index 87ad042221e7..246568cb9a6e 100644 --- a/fs/jfs/file.c +++ b/fs/jfs/file.c @@ -6,6 +6,7 @@ #include <linux/mm.h> #include <linux/fs.h> +#include <linux/filelock.h> #include <linux/posix_acl.h> #include <linux/quotaops.h> #include "jfs_incore.h" @@ -153,4 +154,5 @@ const struct file_operations jfs_file_operations = { .release = jfs_release, .unlocked_ioctl = jfs_ioctl, .compat_ioctl = compat_ptr_ioctl, + .setlease = generic_setlease, }; diff --git a/fs/jfs/jfs_dtree.c b/fs/jfs/jfs_dtree.c index 0ab83bb7bbdf..9ab3f2fc61d1 100644 --- a/fs/jfs/jfs_dtree.c +++ b/fs/jfs/jfs_dtree.c @@ -2903,7 +2903,7 @@ int jfs_readdir(struct file *file, struct dir_context *ctx) stbl = DT_GETSTBL(p); for (i = index; i < p->header.nextindex; i++) { - if (stbl[i] < 0 || stbl[i] >= DTPAGEMAXSLOT) { + if (stbl[i] < 0) { jfs_err("JFS: Invalid stbl[%d] = %d for inode %ld, block = %lld", i, stbl[i], (long)ip->i_ino, (long long)bn); free_page(dirent_buf); @@ -3108,7 +3108,7 @@ static int dtReadFirst(struct inode *ip, struct btstack * btstack) /* get the leftmost entry */ stbl = DT_GETSTBL(p); - if (stbl[0] < 0 || stbl[0] >= DTPAGEMAXSLOT) { + if (stbl[0] < 0) { DT_PUTPAGE(mp); jfs_error(ip->i_sb, "stbl[0] out of bound\n"); return -EIO; diff --git a/fs/jfs/jfs_logmgr.c b/fs/jfs/jfs_logmgr.c index b343c5ea1159..5b1c5da04163 100644 --- a/fs/jfs/jfs_logmgr.c +++ b/fs/jfs/jfs_logmgr.c @@ -2311,6 +2311,7 @@ int jfsIOWait(void *arg) { struct lbuf *bp; + set_freezable(); do { spin_lock_irq(&log_redrive_lock); while ((bp = log_redrive_list)) { diff --git a/fs/jfs/namei.c b/fs/jfs/namei.c index 65a218eba8fa..60c4a0e0fca5 100644 --- a/fs/jfs/namei.c +++ b/fs/jfs/namei.c @@ -5,6 +5,7 @@ */ #include <linux/fs.h> +#include <linux/filelock.h> #include <linux/namei.h> #include <linux/ctype.h> #include <linux/quotaops.h> @@ -1228,7 +1229,7 @@ static int jfs_rename(struct mnt_idmap *idmap, struct inode *old_dir, jfs_err("jfs_rename: dtInsert returned -EIO"); goto out_tx; } - if (S_ISDIR(old_ip->i_mode)) + if (S_ISDIR(old_ip->i_mode) && old_dir != new_dir) inc_nlink(new_dir); } /* @@ -1244,7 +1245,9 @@ static int jfs_rename(struct mnt_idmap *idmap, struct inode *old_dir, goto out_tx; } if (S_ISDIR(old_ip->i_mode)) { - drop_nlink(old_dir); + if (new_ip || old_dir != new_dir) + drop_nlink(old_dir); + if (old_dir != new_dir) { /* * Change inode number of parent for moved directory @@ -1545,6 +1548,7 @@ const struct file_operations jfs_dir_operations = { .unlocked_ioctl = jfs_ioctl, .compat_ioctl = compat_ptr_ioctl, .llseek = generic_file_llseek, + .setlease = generic_setlease, }; static int jfs_ci_hash(const struct dentry *dir, struct qstr *this) diff --git a/fs/libfs.c b/fs/libfs.c index 591eb649ebba..f1860dff86f2 100644 --- a/fs/libfs.c +++ b/fs/libfs.c @@ -6,6 +6,7 @@ #include <linux/blkdev.h> #include <linux/export.h> +#include <linux/filelock.h> #include <linux/pagemap.h> #include <linux/slab.h> #include <linux/cred.h> @@ -570,6 +571,7 @@ const struct file_operations simple_offset_dir_operations = { .iterate_shared = offset_readdir, .read = generic_read_dir, .fsync = noop_fsync, + .setlease = generic_setlease, }; struct dentry *find_next_child(struct dentry *parent, struct dentry *prev) @@ -1698,24 +1700,6 @@ struct inode *alloc_anon_inode(struct super_block *s) EXPORT_SYMBOL(alloc_anon_inode); /** - * simple_nosetlease - generic helper for prohibiting leases - * @filp: file pointer - * @arg: type of lease to obtain - * @flp: new lease supplied for insertion - * @priv: private data for lm_setup operation - * - * Generic helper for filesystems that do not wish to allow leases to be set. - * All arguments are ignored and it just returns -EINVAL. - */ -int -simple_nosetlease(struct file *filp, int arg, struct file_lease **flp, - void **priv) -{ - return -EINVAL; -} -EXPORT_SYMBOL(simple_nosetlease); - -/** * simple_get_link - generic helper to get the target of "fast" symlinks * @dentry: not used here * @inode: the symlink inode diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c index d68afa196535..dcd80c4e74c9 100644 --- a/fs/lockd/svc.c +++ b/fs/lockd/svc.c @@ -141,7 +141,7 @@ lockd(void *vrqstp) */ while (!svc_thread_should_stop(rqstp)) { nlmsvc_retry_blocked(rqstp); - svc_recv(rqstp); + svc_recv(rqstp, 0); } if (nlmsvc_ops) nlmsvc_invalidate_all(); @@ -340,7 +340,7 @@ static int lockd_get(void) return -ENOMEM; } - error = svc_set_num_threads(serv, NULL, 1); + error = svc_set_num_threads(serv, 0, 1); if (error < 0) { svc_destroy(&serv); return error; @@ -368,7 +368,7 @@ static void lockd_put(void) unregister_inet6addr_notifier(&lockd_inet6addr_notifier); #endif - svc_set_num_threads(nlmsvc_serv, NULL, 0); + svc_set_num_threads(nlmsvc_serv, 0, 0); timer_delete_sync(&nlmsvc_retry); svc_destroy(&nlmsvc_serv); dprintk("lockd_down: service destroyed\n"); diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c index 6bce19fd024c..712df1e025d8 100644 --- a/fs/lockd/svclock.c +++ b/fs/lockd/svclock.c @@ -641,10 +641,6 @@ nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file, conflock->fl.c.flc_owner = lock->fl.c.flc_owner; error = vfs_test_lock(file->f_file[mode], &conflock->fl); if (error) { - /* We can't currently deal with deferred test requests */ - if (error == FILE_LOCK_DEFERRED) - WARN_ON_ONCE(1); - ret = nlm_lck_denied_nolocks; goto out; } diff --git a/fs/locks.c b/fs/locks.c index 7ea949d7ff45..d13ec930b7bb 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -178,7 +178,6 @@ locks_get_lock_context(struct inode *inode, int type) { struct file_lock_context *ctx; - /* paired with cmpxchg() below */ ctx = locks_inode_context(inode); if (likely(ctx) || type == F_UNLCK) goto out; @@ -196,7 +195,18 @@ locks_get_lock_context(struct inode *inode, int type) * Assign the pointer if it's not already assigned. If it is, then * free the context we just allocated. */ - if (cmpxchg(&inode->i_flctx, NULL, ctx)) { + spin_lock(&inode->i_lock); + if (!(inode->i_opflags & IOP_FLCTX)) { + VFS_BUG_ON_INODE(inode->i_flctx, inode); + WRITE_ONCE(inode->i_flctx, ctx); + /* + * Paired with locks_inode_context(). + */ + smp_store_release(&inode->i_opflags, inode->i_opflags | IOP_FLCTX); + spin_unlock(&inode->i_lock); + } else { + VFS_BUG_ON_INODE(!inode->i_flctx, inode); + spin_unlock(&inode->i_lock); kmem_cache_free(flctx_cache, ctx); ctx = locks_inode_context(inode); } @@ -2019,8 +2029,7 @@ kernel_setlease(struct file *filp, int arg, struct file_lease **lease, void **pr setlease_notifier(arg, *lease); if (filp->f_op->setlease) return filp->f_op->setlease(filp, arg, lease, priv); - else - return generic_setlease(filp, arg, lease, priv); + return -EINVAL; } EXPORT_SYMBOL_GPL(kernel_setlease); @@ -2253,12 +2262,23 @@ SYSCALL_DEFINE2(flock, unsigned int, fd, unsigned int, cmd) */ int vfs_test_lock(struct file *filp, struct file_lock *fl) { + int error = 0; + WARN_ON_ONCE(fl->fl_ops || fl->fl_lmops); WARN_ON_ONCE(filp != fl->c.flc_file); if (filp->f_op->lock) - return filp->f_op->lock(filp, F_GETLK, fl); - posix_test_lock(filp, fl); - return 0; + error = filp->f_op->lock(filp, F_GETLK, fl); + else + posix_test_lock(filp, fl); + + /* + * We don't expect FILE_LOCK_DEFERRED and callers cannot + * handle it. + */ + if (WARN_ON_ONCE(error == FILE_LOCK_DEFERRED)) + error = -EIO; + + return error; } EXPORT_SYMBOL_GPL(vfs_test_lock); diff --git a/fs/minix/inode.c b/fs/minix/inode.c index 51ea9bdc813f..c8c6b2135abe 100644 --- a/fs/minix/inode.c +++ b/fs/minix/inode.c @@ -170,10 +170,38 @@ static int minix_reconfigure(struct fs_context *fc) static bool minix_check_superblock(struct super_block *sb) { struct minix_sb_info *sbi = minix_sb(sb); + unsigned long block; - if (sbi->s_imap_blocks == 0 || sbi->s_zmap_blocks == 0) + if (sbi->s_log_zone_size != 0) { + printk("minix-fs error: zone size must equal block size. " + "s_log_zone_size > 0 is not supported.\n"); + return false; + } + + if (sbi->s_ninodes < 1 || sbi->s_firstdatazone <= 4 || + sbi->s_firstdatazone >= sbi->s_nzones) return false; + /* Apparently minix can create filesystems that allocate more blocks for + * the bitmaps than needed. We simply ignore that, but verify it didn't + * create one with not enough blocks and bail out if so. + */ + block = minix_blocks_needed(sbi->s_ninodes, sb->s_blocksize); + if (sbi->s_imap_blocks < block) { + printk("MINIX-fs: file system does not have enough " + "imap blocks allocated. Refusing to mount.\n"); + return false; + } + + block = minix_blocks_needed( + (sbi->s_nzones - sbi->s_firstdatazone + 1), + sb->s_blocksize); + if (sbi->s_zmap_blocks < block) { + printk("MINIX-fs: file system does not have enough " + "zmap blocks allocated. Refusing to mount.\n"); + return false; + } + /* * s_max_size must not exceed the block mapping limitation. This check * is only needed for V1 filesystems, since V2/V3 support an extra level @@ -293,26 +321,6 @@ static int minix_fill_super(struct super_block *s, struct fs_context *fc) minix_set_bit(0,sbi->s_imap[0]->b_data); minix_set_bit(0,sbi->s_zmap[0]->b_data); - /* Apparently minix can create filesystems that allocate more blocks for - * the bitmaps than needed. We simply ignore that, but verify it didn't - * create one with not enough blocks and bail out if so. - */ - block = minix_blocks_needed(sbi->s_ninodes, s->s_blocksize); - if (sbi->s_imap_blocks < block) { - printk("MINIX-fs: file system does not have enough " - "imap blocks allocated. Refusing to mount.\n"); - goto out_no_bitmap; - } - - block = minix_blocks_needed( - (sbi->s_nzones - sbi->s_firstdatazone + 1), - s->s_blocksize); - if (sbi->s_zmap_blocks < block) { - printk("MINIX-fs: file system does not have enough " - "zmap blocks allocated. Refusing to mount.\n"); - goto out_no_bitmap; - } - /* set up enough so that it can read an inode */ s->s_op = &minix_sops; s->s_time_min = 0; diff --git a/fs/minix/minix.h b/fs/minix/minix.h index 2bfaf377f208..7e1f652f16d3 100644 --- a/fs/minix/minix.h +++ b/fs/minix/minix.h @@ -175,6 +175,4 @@ static inline int minix_test_bit(int nr, const void *vaddr) __minix_error_inode((inode), __func__, __LINE__, \ (fmt), ##__VA_ARGS__) -#define EFSCORRUPTED EUCLEAN /* Filesystem is corrupted */ - #endif /* FS_MINIX_H */ diff --git a/fs/mount.h b/fs/mount.h index 2d28ef2a3aed..e0816c11a198 100644 --- a/fs/mount.h +++ b/fs/mount.h @@ -5,6 +5,7 @@ #include <linux/ns_common.h> #include <linux/fs_pin.h> +extern struct file_system_type nullfs_fs_type; extern struct list_head notify_list; struct mnt_namespace { diff --git a/fs/namei.c b/fs/namei.c index cf16b6822dd3..8e7792de0000 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -41,6 +41,8 @@ #include <linux/init_task.h> #include <linux/uaccess.h> +#include <asm/runtime-const.h> + #include "internal.h" #include "mount.h" @@ -123,27 +125,61 @@ * PATH_MAX includes the nul terminator --RR. */ -#define EMBEDDED_NAME_MAX (PATH_MAX - offsetof(struct filename, iname)) +/* SLAB cache for struct filename instances */ +static struct kmem_cache *__names_cache __ro_after_init; +#define names_cache runtime_const_ptr(__names_cache) + +void __init filename_init(void) +{ + __names_cache = kmem_cache_create_usercopy("names_cache", sizeof(struct filename), 0, + SLAB_HWCACHE_ALIGN|SLAB_PANIC, offsetof(struct filename, iname), + EMBEDDED_NAME_MAX, NULL); + runtime_const_init(ptr, __names_cache); +} + +static inline struct filename *alloc_filename(void) +{ + return kmem_cache_alloc(names_cache, GFP_KERNEL); +} + +static inline void free_filename(struct filename *p) +{ + kmem_cache_free(names_cache, p); +} -static inline void initname(struct filename *name, const char __user *uptr) +static inline void initname(struct filename *name) { - name->uptr = uptr; name->aname = NULL; - atomic_set(&name->refcnt, 1); + name->refcnt = 1; } -struct filename * -getname_flags(const char __user *filename, int flags) +static int getname_long(struct filename *name, const char __user *filename) +{ + int len; + char *p __free(kfree) = kmalloc(PATH_MAX, GFP_KERNEL); + if (unlikely(!p)) + return -ENOMEM; + + memcpy(p, &name->iname, EMBEDDED_NAME_MAX); + len = strncpy_from_user(p + EMBEDDED_NAME_MAX, + filename + EMBEDDED_NAME_MAX, + PATH_MAX - EMBEDDED_NAME_MAX); + if (unlikely(len < 0)) + return len; + if (unlikely(len == PATH_MAX - EMBEDDED_NAME_MAX)) + return -ENAMETOOLONG; + name->name = no_free_ptr(p); + return 0; +} + +static struct filename * +do_getname(const char __user *filename, int flags, bool incomplete) { struct filename *result; char *kname; int len; - result = audit_reusename(filename); - if (result) - return result; - - result = __getname(); + result = alloc_filename(); if (unlikely(!result)) return ERR_PTR(-ENOMEM); @@ -159,16 +195,9 @@ getname_flags(const char __user *filename, int flags) * Handle both empty path and copy failure in one go. */ if (unlikely(len <= 0)) { - if (unlikely(len < 0)) { - __putname(result); - return ERR_PTR(len); - } - /* The empty path is special. */ - if (!(flags & LOOKUP_EMPTY)) { - __putname(result); - return ERR_PTR(-ENOENT); - } + if (!len && !(flags & LOOKUP_EMPTY)) + len = -ENOENT; } /* @@ -177,44 +206,25 @@ getname_flags(const char __user *filename, int flags) * names_cache allocation for the pathname, and re-do the copy from * userland. */ - if (unlikely(len == EMBEDDED_NAME_MAX)) { - const size_t size = offsetof(struct filename, iname[1]); - kname = (char *)result; - - /* - * size is chosen that way we to guarantee that - * result->iname[0] is within the same object and that - * kname can't be equal to result->iname, no matter what. - */ - result = kzalloc(size, GFP_KERNEL); - if (unlikely(!result)) { - __putname(kname); - return ERR_PTR(-ENOMEM); - } - result->name = kname; - len = strncpy_from_user(kname, filename, PATH_MAX); - if (unlikely(len < 0)) { - __putname(kname); - kfree(result); - return ERR_PTR(len); - } - /* The empty path is special. */ - if (unlikely(!len) && !(flags & LOOKUP_EMPTY)) { - __putname(kname); - kfree(result); - return ERR_PTR(-ENOENT); - } - if (unlikely(len == PATH_MAX)) { - __putname(kname); - kfree(result); - return ERR_PTR(-ENAMETOOLONG); - } + if (unlikely(len == EMBEDDED_NAME_MAX)) + len = getname_long(result, filename); + if (unlikely(len < 0)) { + free_filename(result); + return ERR_PTR(len); } - initname(result, filename); - audit_getname(result); + + initname(result); + if (likely(!incomplete)) + audit_getname(result); return result; } +struct filename * +getname_flags(const char __user *filename, int flags) +{ + return do_getname(filename, flags, false); +} + struct filename *getname_uflags(const char __user *filename, int uflags) { int flags = (uflags & AT_EMPTY_PATH) ? LOOKUP_EMPTY : 0; @@ -224,7 +234,6 @@ struct filename *getname_uflags(const char __user *filename, int uflags) struct filename *__getname_maybe_null(const char __user *pathname) { - struct filename *name; char c; /* try to save on allocations; loss on um, though */ @@ -233,45 +242,47 @@ struct filename *__getname_maybe_null(const char __user *pathname) if (!c) return NULL; - name = getname_flags(pathname, LOOKUP_EMPTY); - if (!IS_ERR(name) && !(name->name[0])) { - putname(name); - name = NULL; - } - return name; + CLASS(filename_flags, name)(pathname, LOOKUP_EMPTY); + /* empty pathname translates to NULL */ + if (!IS_ERR(name) && !(name->name[0])) + return NULL; + return no_free_ptr(name); } -struct filename *getname_kernel(const char * filename) +static struct filename *do_getname_kernel(const char *filename, bool incomplete) { struct filename *result; int len = strlen(filename) + 1; + char *p; + + if (unlikely(len > PATH_MAX)) + return ERR_PTR(-ENAMETOOLONG); - result = __getname(); + result = alloc_filename(); if (unlikely(!result)) return ERR_PTR(-ENOMEM); if (len <= EMBEDDED_NAME_MAX) { - result->name = (char *)result->iname; - } else if (len <= PATH_MAX) { - const size_t size = offsetof(struct filename, iname[1]); - struct filename *tmp; - - tmp = kmalloc(size, GFP_KERNEL); - if (unlikely(!tmp)) { - __putname(result); + p = (char *)result->iname; + memcpy(p, filename, len); + } else { + p = kmemdup(filename, len, GFP_KERNEL); + if (unlikely(!p)) { + free_filename(result); return ERR_PTR(-ENOMEM); } - tmp->name = (char *)result; - result = tmp; - } else { - __putname(result); - return ERR_PTR(-ENAMETOOLONG); } - memcpy((char *)result->name, filename, len); - initname(result, NULL); - audit_getname(result); + result->name = p; + initname(result); + if (likely(!incomplete)) + audit_getname(result); return result; } + +struct filename *getname_kernel(const char *filename) +{ + return do_getname_kernel(filename, false); +} EXPORT_SYMBOL(getname_kernel); void putname(struct filename *name) @@ -281,23 +292,64 @@ void putname(struct filename *name) if (IS_ERR_OR_NULL(name)) return; - refcnt = atomic_read(&name->refcnt); + refcnt = name->refcnt; if (unlikely(refcnt != 1)) { if (WARN_ON_ONCE(!refcnt)) return; - if (!atomic_dec_and_test(&name->refcnt)) - return; + name->refcnt--; + return; } - if (unlikely(name->name != name->iname)) { - __putname(name->name); - kfree(name); - } else - __putname(name); + if (unlikely(name->name != name->iname)) + kfree(name->name); + free_filename(name); } EXPORT_SYMBOL(putname); +static inline int __delayed_getname(struct delayed_filename *v, + const char __user *string, int flags) +{ + v->__incomplete_filename = do_getname(string, flags, true); + return PTR_ERR_OR_ZERO(v->__incomplete_filename); +} + +int delayed_getname(struct delayed_filename *v, const char __user *string) +{ + return __delayed_getname(v, string, 0); +} + +int delayed_getname_uflags(struct delayed_filename *v, const char __user *string, + int uflags) +{ + int flags = (uflags & AT_EMPTY_PATH) ? LOOKUP_EMPTY : 0; + return __delayed_getname(v, string, flags); +} + +int putname_to_delayed(struct delayed_filename *v, struct filename *name) +{ + if (likely(name->refcnt == 1)) { + v->__incomplete_filename = name; + return 0; + } + name->refcnt--; + v->__incomplete_filename = do_getname_kernel(name->name, true); + return PTR_ERR_OR_ZERO(v->__incomplete_filename); +} + +void dismiss_delayed_filename(struct delayed_filename *v) +{ + putname(no_free_ptr(v->__incomplete_filename)); +} + +struct filename *complete_getname(struct delayed_filename *v) +{ + struct filename *res = no_free_ptr(v->__incomplete_filename); + if (!IS_ERR(res)) + audit_getname(res); + return res; +} + /** * check_acl - perform ACL permission checking * @idmap: idmap of the mount the inode was found from @@ -879,7 +931,7 @@ static bool try_to_unlazy(struct nameidata *nd) { struct dentry *parent = nd->path.dentry; - BUG_ON(!(nd->flags & LOOKUP_RCU)); + VFS_BUG_ON(!(nd->flags & LOOKUP_RCU)); if (unlikely(nd->flags & LOOKUP_CACHED)) { drop_links(nd); @@ -919,7 +971,8 @@ out: static bool try_to_unlazy_next(struct nameidata *nd, struct dentry *dentry) { int res; - BUG_ON(!(nd->flags & LOOKUP_RCU)); + + VFS_BUG_ON(!(nd->flags & LOOKUP_RCU)); if (unlikely(nd->flags & LOOKUP_CACHED)) { drop_links(nd); @@ -1631,9 +1684,6 @@ static bool __follow_mount_rcu(struct nameidata *nd, struct path *path) struct dentry *dentry = path->dentry; unsigned int flags = dentry->d_flags; - if (likely(!(flags & DCACHE_MANAGED_DENTRY))) - return true; - if (unlikely(nd->flags & LOOKUP_NO_XDEV)) return false; @@ -2950,7 +3000,7 @@ drop: struct dentry *kern_path_parent(const char *name, struct path *path) { struct path parent_path __free(path_put) = {}; - struct filename *filename __free(putname) = getname_kernel(name); + CLASS(filename_kernel, filename)(name); struct dentry *d; struct qstr last; int type, error; @@ -2971,33 +3021,23 @@ struct dentry *kern_path_parent(const char *name, struct path *path) struct dentry *start_removing_path(const char *name, struct path *path) { - struct filename *filename = getname_kernel(name); - struct dentry *res = __start_removing_path(AT_FDCWD, filename, path); - - putname(filename); - return res; + CLASS(filename_kernel, filename)(name); + return __start_removing_path(AT_FDCWD, filename, path); } struct dentry *start_removing_user_path_at(int dfd, const char __user *name, struct path *path) { - struct filename *filename = getname(name); - struct dentry *res = __start_removing_path(dfd, filename, path); - - putname(filename); - return res; + CLASS(filename, filename)(name); + return __start_removing_path(dfd, filename, path); } EXPORT_SYMBOL(start_removing_user_path_at); int kern_path(const char *name, unsigned int flags, struct path *path) { - struct filename *filename = getname_kernel(name); - int ret = filename_lookup(AT_FDCWD, filename, flags, path, NULL); - - putname(filename); - return ret; - + CLASS(filename_kernel, filename)(name); + return filename_lookup(AT_FDCWD, filename, flags, path, NULL); } EXPORT_SYMBOL(kern_path); @@ -3031,15 +3071,11 @@ int vfs_path_lookup(struct dentry *dentry, struct vfsmount *mnt, const char *name, unsigned int flags, struct path *path) { - struct filename *filename; + CLASS(filename_kernel, filename)(name); struct path root = {.mnt = mnt, .dentry = dentry}; - int ret; - filename = getname_kernel(name); /* the first argument of filename_lookup() is ignored with root */ - ret = filename_lookup(AT_FDCWD, filename, flags, path, &root); - putname(filename); - return ret; + return filename_lookup(AT_FDCWD, filename, flags, path, &root); } EXPORT_SYMBOL(vfs_path_lookup); @@ -3572,11 +3608,8 @@ int path_pts(struct path *path) int user_path_at(int dfd, const char __user *name, unsigned flags, struct path *path) { - struct filename *filename = getname_flags(name, flags); - int ret = filename_lookup(dfd, filename, flags, path, NULL); - - putname(filename); - return ret; + CLASS(filename_flags, filename)(name, flags); + return filename_lookup(dfd, filename, flags, path, NULL); } EXPORT_SYMBOL(user_path_at); @@ -3613,7 +3646,7 @@ EXPORT_SYMBOL(__check_sticky); * 11. We don't allow removal of NFS sillyrenamed files; it's handled by * nfs_async_unlink(). */ -static int may_delete(struct mnt_idmap *idmap, struct inode *dir, +int may_delete_dentry(struct mnt_idmap *idmap, struct inode *dir, struct dentry *victim, bool isdir) { struct inode *inode = d_backing_inode(victim); @@ -3655,6 +3688,7 @@ static int may_delete(struct mnt_idmap *idmap, struct inode *dir, return -EBUSY; return 0; } +EXPORT_SYMBOL(may_delete_dentry); /* Check whether we can create an object with dentry child in directory * dir. @@ -3665,8 +3699,8 @@ static int may_delete(struct mnt_idmap *idmap, struct inode *dir, * 4. We should have write and exec permissions on dir * 5. We can't do it if dir is immutable (done in permission()) */ -static inline int may_create(struct mnt_idmap *idmap, - struct inode *dir, struct dentry *child) +int may_create_dentry(struct mnt_idmap *idmap, + struct inode *dir, struct dentry *child) { audit_inode_child(dir, child, AUDIT_TYPE_CHILD_CREATE); if (child->d_inode) @@ -3678,6 +3712,7 @@ static inline int may_create(struct mnt_idmap *idmap, return inode_permission(idmap, dir, MAY_WRITE | MAY_EXEC); } +EXPORT_SYMBOL(may_create_dentry); // p1 != p2, both are on the same filesystem, ->s_vfs_rename_mutex is held static struct dentry *lock_two_directories(struct dentry *p1, struct dentry *p2) @@ -4124,7 +4159,7 @@ int vfs_create(struct mnt_idmap *idmap, struct dentry *dentry, umode_t mode, struct inode *dir = d_inode(dentry->d_parent); int error; - error = may_create(idmap, dir, dentry); + error = may_create_dentry(idmap, dir, dentry); if (error) return error; @@ -4150,7 +4185,7 @@ int vfs_mkobj(struct dentry *dentry, umode_t mode, void *arg) { struct inode *dir = dentry->d_parent->d_inode; - int error = may_create(&nop_mnt_idmap, dir, dentry); + int error = may_create_dentry(&nop_mnt_idmap, dir, dentry); if (error) return error; @@ -4288,19 +4323,16 @@ static int may_o_create(struct mnt_idmap *idmap, * * Returns an error code otherwise. */ -static struct dentry *atomic_open(struct nameidata *nd, struct dentry *dentry, +static struct dentry *atomic_open(const struct path *path, struct dentry *dentry, struct file *file, int open_flag, umode_t mode) { struct dentry *const DENTRY_NOT_SET = (void *) -1UL; - struct inode *dir = nd->path.dentry->d_inode; + struct inode *dir = path->dentry->d_inode; int error; - if (nd->flags & LOOKUP_DIRECTORY) - open_flag |= O_DIRECTORY; - file->__f_path.dentry = DENTRY_NOT_SET; - file->__f_path.mnt = nd->path.mnt; + file->__f_path.mnt = path->mnt; error = dir->i_op->atomic_open(dir, dentry, file, open_to_namei_flags(open_flag), mode); d_lookup_done(dentry); @@ -4412,7 +4444,9 @@ static struct dentry *lookup_open(struct nameidata *nd, struct file *file, if (create_error) open_flag &= ~O_CREAT; if (dir_inode->i_op->atomic_open) { - dentry = atomic_open(nd, dentry, file, open_flag, mode); + if (nd->flags & LOOKUP_DIRECTORY) + open_flag |= O_DIRECTORY; + dentry = atomic_open(&nd->path, dentry, file, open_flag, mode); if (unlikely(create_error) && dentry == ERR_PTR(-ENOENT)) dentry = ERR_PTR(create_error); return dentry; @@ -4812,13 +4846,15 @@ static struct file *path_openat(struct nameidata *nd, return ERR_PTR(error); } -struct file *do_filp_open(int dfd, struct filename *pathname, +struct file *do_file_open(int dfd, struct filename *pathname, const struct open_flags *op) { struct nameidata nd; int flags = op->lookup_flags; struct file *filp; + if (IS_ERR(pathname)) + return ERR_CAST(pathname); set_nameidata(&nd, dfd, pathname, NULL); filp = path_openat(&nd, op, flags | LOOKUP_RCU); if (unlikely(filp == ERR_PTR(-ECHILD))) @@ -4834,13 +4870,12 @@ struct file *do_file_open_root(const struct path *root, { struct nameidata nd; struct file *file; - struct filename *filename; int flags = op->lookup_flags; if (d_is_symlink(root->dentry) && op->intent & LOOKUP_OPEN) return ERR_PTR(-ELOOP); - filename = getname_kernel(name); + CLASS(filename_kernel, filename)(name); if (IS_ERR(filename)) return ERR_CAST(filename); @@ -4851,7 +4886,6 @@ struct file *do_file_open_root(const struct path *root, if (unlikely(file == ERR_PTR(-ESTALE))) file = path_openat(&nd, op, flags | LOOKUP_REVAL); restore_nameidata(); - putname(filename); return file; } @@ -4907,11 +4941,8 @@ out: struct dentry *start_creating_path(int dfd, const char *pathname, struct path *path, unsigned int lookup_flags) { - struct filename *filename = getname_kernel(pathname); - struct dentry *res = filename_create(dfd, filename, path, lookup_flags); - - putname(filename); - return res; + CLASS(filename_kernel, filename)(pathname); + return filename_create(dfd, filename, path, lookup_flags); } EXPORT_SYMBOL(start_creating_path); @@ -4938,14 +4969,78 @@ inline struct dentry *start_creating_user_path( int dfd, const char __user *pathname, struct path *path, unsigned int lookup_flags) { - struct filename *filename = getname(pathname); - struct dentry *res = filename_create(dfd, filename, path, lookup_flags); - - putname(filename); - return res; + CLASS(filename, filename)(pathname); + return filename_create(dfd, filename, path, lookup_flags); } EXPORT_SYMBOL(start_creating_user_path); +/** + * dentry_create - Create and open a file + * @path: path to create + * @flags: O\_ flags + * @mode: mode bits for new file + * @cred: credentials to use + * + * Caller must hold the parent directory's lock, and have prepared + * a negative dentry, placed in @path->dentry, for the new file. + * + * Caller sets @path->mnt to the vfsmount of the filesystem where + * the new file is to be created. The parent directory and the + * negative dentry must reside on the same filesystem instance. + * + * On success, returns a ``struct file *``. Otherwise an ERR_PTR + * is returned. + */ +struct file *dentry_create(struct path *path, int flags, umode_t mode, + const struct cred *cred) +{ + struct file *file __free(fput) = NULL; + struct dentry *dentry = path->dentry; + struct dentry *dir = dentry->d_parent; + struct inode *dir_inode = d_inode(dir); + struct mnt_idmap *idmap; + int error, create_error; + + file = alloc_empty_file(flags, cred); + if (IS_ERR(file)) + return file; + + idmap = mnt_idmap(path->mnt); + + if (dir_inode->i_op->atomic_open) { + path->dentry = dir; + mode = vfs_prepare_mode(idmap, dir_inode, mode, S_IALLUGO, S_IFREG); + + create_error = may_o_create(idmap, path, dentry, mode); + if (create_error) + flags &= ~O_CREAT; + + dentry = atomic_open(path, dentry, file, flags, mode); + error = PTR_ERR_OR_ZERO(dentry); + + if (unlikely(create_error) && error == -ENOENT) + error = create_error; + + if (!error) { + if (file->f_mode & FMODE_CREATED) + fsnotify_create(dir->d_inode, dentry); + if (file->f_mode & FMODE_OPENED) + fsnotify_open(file); + } + + path->dentry = dentry; + + } else { + error = vfs_create(mnt_idmap(path->mnt), path->dentry, mode, NULL); + if (!error) + error = vfs_open(path, file); + } + if (unlikely(error)) + return ERR_PTR(error); + + return no_free_ptr(file); +} +EXPORT_SYMBOL(dentry_create); /** * vfs_mknod - create device node or file @@ -4969,7 +5064,7 @@ int vfs_mknod(struct mnt_idmap *idmap, struct inode *dir, struct delegated_inode *delegated_inode) { bool is_whiteout = S_ISCHR(mode) && dev == WHITEOUT_DEV; - int error = may_create(idmap, dir, dentry); + int error = may_create_dentry(idmap, dir, dentry); if (error) return error; @@ -5018,8 +5113,8 @@ static int may_mknod(umode_t mode) } } -static int do_mknodat(int dfd, struct filename *name, umode_t mode, - unsigned int dev) +int filename_mknodat(int dfd, struct filename *name, umode_t mode, + unsigned int dev) { struct delegated_inode di = { }; struct mnt_idmap *idmap; @@ -5030,12 +5125,11 @@ static int do_mknodat(int dfd, struct filename *name, umode_t mode, error = may_mknod(mode); if (error) - goto out1; + return error; retry: dentry = filename_create(dfd, name, &path, lookup_flags); - error = PTR_ERR(dentry); if (IS_ERR(dentry)) - goto out1; + return PTR_ERR(dentry); error = security_path_mknod(&path, dentry, mode_strip_umask(path.dentry->d_inode, mode), dev); @@ -5069,20 +5163,20 @@ out2: lookup_flags |= LOOKUP_REVAL; goto retry; } -out1: - putname(name); return error; } SYSCALL_DEFINE4(mknodat, int, dfd, const char __user *, filename, umode_t, mode, unsigned int, dev) { - return do_mknodat(dfd, getname(filename), mode, dev); + CLASS(filename, name)(filename); + return filename_mknodat(dfd, name, mode, dev); } SYSCALL_DEFINE3(mknod, const char __user *, filename, umode_t, mode, unsigned, dev) { - return do_mknodat(AT_FDCWD, getname(filename), mode, dev); + CLASS(filename, name)(filename); + return filename_mknodat(AT_FDCWD, name, mode, dev); } /** @@ -5115,7 +5209,7 @@ struct dentry *vfs_mkdir(struct mnt_idmap *idmap, struct inode *dir, unsigned max_links = dir->i_sb->s_max_links; struct dentry *de; - error = may_create(idmap, dir, dentry); + error = may_create_dentry(idmap, dir, dentry); if (error) goto err; @@ -5153,7 +5247,7 @@ err: } EXPORT_SYMBOL(vfs_mkdir); -int do_mkdirat(int dfd, struct filename *name, umode_t mode) +int filename_mkdirat(int dfd, struct filename *name, umode_t mode) { struct dentry *dentry; struct path path; @@ -5163,9 +5257,8 @@ int do_mkdirat(int dfd, struct filename *name, umode_t mode) retry: dentry = filename_create(dfd, name, &path, lookup_flags); - error = PTR_ERR(dentry); if (IS_ERR(dentry)) - goto out_putname; + return PTR_ERR(dentry); error = security_path_mkdir(&path, dentry, mode_strip_umask(path.dentry->d_inode, mode)); @@ -5185,19 +5278,19 @@ retry: lookup_flags |= LOOKUP_REVAL; goto retry; } -out_putname: - putname(name); return error; } SYSCALL_DEFINE3(mkdirat, int, dfd, const char __user *, pathname, umode_t, mode) { - return do_mkdirat(dfd, getname(pathname), mode); + CLASS(filename, name)(pathname); + return filename_mkdirat(dfd, name, mode); } SYSCALL_DEFINE2(mkdir, const char __user *, pathname, umode_t, mode) { - return do_mkdirat(AT_FDCWD, getname(pathname), mode); + CLASS(filename, name)(pathname); + return filename_mkdirat(AT_FDCWD, name, mode); } /** @@ -5218,7 +5311,7 @@ SYSCALL_DEFINE2(mkdir, const char __user *, pathname, umode_t, mode) int vfs_rmdir(struct mnt_idmap *idmap, struct inode *dir, struct dentry *dentry, struct delegated_inode *delegated_inode) { - int error = may_delete(idmap, dir, dentry, 1); + int error = may_delete_dentry(idmap, dir, dentry, true); if (error) return error; @@ -5260,7 +5353,7 @@ out: } EXPORT_SYMBOL(vfs_rmdir); -int do_rmdir(int dfd, struct filename *name) +int filename_rmdir(int dfd, struct filename *name) { int error; struct dentry *dentry; @@ -5272,7 +5365,7 @@ int do_rmdir(int dfd, struct filename *name) retry: error = filename_parentat(dfd, name, lookup_flags, &path, &last, &type); if (error) - goto exit1; + return error; switch (type) { case LAST_DOTDOT: @@ -5314,14 +5407,13 @@ exit2: lookup_flags |= LOOKUP_REVAL; goto retry; } -exit1: - putname(name); return error; } SYSCALL_DEFINE1(rmdir, const char __user *, pathname) { - return do_rmdir(AT_FDCWD, getname(pathname)); + CLASS(filename, name)(pathname); + return filename_rmdir(AT_FDCWD, name); } /** @@ -5353,7 +5445,7 @@ int vfs_unlink(struct mnt_idmap *idmap, struct inode *dir, struct dentry *dentry, struct delegated_inode *delegated_inode) { struct inode *target = dentry->d_inode; - int error = may_delete(idmap, dir, dentry, 0); + int error = may_delete_dentry(idmap, dir, dentry, false); if (error) return error; @@ -5403,7 +5495,7 @@ EXPORT_SYMBOL(vfs_unlink); * writeout happening, and we don't want to prevent access to the directory * while waiting on the I/O. */ -int do_unlinkat(int dfd, struct filename *name) +int filename_unlinkat(int dfd, struct filename *name) { int error; struct dentry *dentry; @@ -5416,7 +5508,7 @@ int do_unlinkat(int dfd, struct filename *name) retry: error = filename_parentat(dfd, name, lookup_flags, &path, &last, &type); if (error) - goto exit_putname; + return error; error = -EISDIR; if (type != LAST_NORM) @@ -5463,8 +5555,6 @@ exit_path_put: lookup_flags |= LOOKUP_REVAL; goto retry; } -exit_putname: - putname(name); return error; } @@ -5473,14 +5563,16 @@ SYSCALL_DEFINE3(unlinkat, int, dfd, const char __user *, pathname, int, flag) if ((flag & ~AT_REMOVEDIR) != 0) return -EINVAL; + CLASS(filename, name)(pathname); if (flag & AT_REMOVEDIR) - return do_rmdir(dfd, getname(pathname)); - return do_unlinkat(dfd, getname(pathname)); + return filename_rmdir(dfd, name); + return filename_unlinkat(dfd, name); } SYSCALL_DEFINE1(unlink, const char __user *, pathname) { - return do_unlinkat(AT_FDCWD, getname(pathname)); + CLASS(filename, name)(pathname); + return filename_unlinkat(AT_FDCWD, name); } /** @@ -5505,7 +5597,7 @@ int vfs_symlink(struct mnt_idmap *idmap, struct inode *dir, { int error; - error = may_create(idmap, dir, dentry); + error = may_create_dentry(idmap, dir, dentry); if (error) return error; @@ -5527,7 +5619,7 @@ int vfs_symlink(struct mnt_idmap *idmap, struct inode *dir, } EXPORT_SYMBOL(vfs_symlink); -int do_symlinkat(struct filename *from, int newdfd, struct filename *to) +int filename_symlinkat(struct filename *from, int newdfd, struct filename *to) { int error; struct dentry *dentry; @@ -5535,15 +5627,13 @@ int do_symlinkat(struct filename *from, int newdfd, struct filename *to) unsigned int lookup_flags = 0; struct delegated_inode delegated_inode = { }; - if (IS_ERR(from)) { - error = PTR_ERR(from); - goto out_putnames; - } + if (IS_ERR(from)) + return PTR_ERR(from); + retry: dentry = filename_create(newdfd, to, &path, lookup_flags); - error = PTR_ERR(dentry); if (IS_ERR(dentry)) - goto out_putnames; + return PTR_ERR(dentry); error = security_path_symlink(&path, dentry, from->name); if (!error) @@ -5559,21 +5649,22 @@ retry: lookup_flags |= LOOKUP_REVAL; goto retry; } -out_putnames: - putname(to); - putname(from); return error; } SYSCALL_DEFINE3(symlinkat, const char __user *, oldname, int, newdfd, const char __user *, newname) { - return do_symlinkat(getname(oldname), newdfd, getname(newname)); + CLASS(filename, old)(oldname); + CLASS(filename, new)(newname); + return filename_symlinkat(old, newdfd, new); } SYSCALL_DEFINE2(symlink, const char __user *, oldname, const char __user *, newname) { - return do_symlinkat(getname(oldname), AT_FDCWD, getname(newname)); + CLASS(filename, old)(oldname); + CLASS(filename, new)(newname); + return filename_symlinkat(old, AT_FDCWD, new); } /** @@ -5613,7 +5704,7 @@ int vfs_link(struct dentry *old_dentry, struct mnt_idmap *idmap, if (!inode) return -ENOENT; - error = may_create(idmap, dir, new_dentry); + error = may_create_dentry(idmap, dir, new_dentry); if (error) return error; @@ -5675,9 +5766,9 @@ EXPORT_SYMBOL(vfs_link); * We don't follow them on the oldname either to be compatible * with linux 2.0, and to avoid hard-linking to directories * and other special files. --ADM - */ -int do_linkat(int olddfd, struct filename *old, int newdfd, - struct filename *new, int flags) +*/ +int filename_linkat(int olddfd, struct filename *old, + int newdfd, struct filename *new, int flags) { struct mnt_idmap *idmap; struct dentry *new_dentry; @@ -5686,10 +5777,8 @@ int do_linkat(int olddfd, struct filename *old, int newdfd, int how = 0; int error; - if ((flags & ~(AT_SYMLINK_FOLLOW | AT_EMPTY_PATH)) != 0) { - error = -EINVAL; - goto out_putnames; - } + if ((flags & ~(AT_SYMLINK_FOLLOW | AT_EMPTY_PATH)) != 0) + return -EINVAL; /* * To use null names we require CAP_DAC_READ_SEARCH or * that the open-time creds of the dfd matches current. @@ -5704,7 +5793,7 @@ int do_linkat(int olddfd, struct filename *old, int newdfd, retry: error = filename_lookup(olddfd, old, how, &old_path, NULL); if (error) - goto out_putnames; + return error; new_dentry = filename_create(newdfd, new, &new_path, (how & LOOKUP_REVAL)); @@ -5740,23 +5829,22 @@ out_dput: } out_putpath: path_put(&old_path); -out_putnames: - putname(old); - putname(new); - return error; } SYSCALL_DEFINE5(linkat, int, olddfd, const char __user *, oldname, int, newdfd, const char __user *, newname, int, flags) { - return do_linkat(olddfd, getname_uflags(oldname, flags), - newdfd, getname(newname), flags); + CLASS(filename_uflags, old)(oldname, flags); + CLASS(filename, new)(newname); + return filename_linkat(olddfd, old, newdfd, new, flags); } SYSCALL_DEFINE2(link, const char __user *, oldname, const char __user *, newname) { - return do_linkat(AT_FDCWD, getname(oldname), AT_FDCWD, getname(newname), 0); + CLASS(filename, old)(oldname); + CLASS(filename, new)(newname); + return filename_linkat(AT_FDCWD, old, AT_FDCWD, new, 0); } /** @@ -5825,21 +5913,21 @@ int vfs_rename(struct renamedata *rd) if (source == target) return 0; - error = may_delete(rd->mnt_idmap, old_dir, old_dentry, is_dir); + error = may_delete_dentry(rd->mnt_idmap, old_dir, old_dentry, is_dir); if (error) return error; if (!target) { - error = may_create(rd->mnt_idmap, new_dir, new_dentry); + error = may_create_dentry(rd->mnt_idmap, new_dir, new_dentry); } else { new_is_dir = d_is_dir(new_dentry); if (!(flags & RENAME_EXCHANGE)) - error = may_delete(rd->mnt_idmap, new_dir, - new_dentry, is_dir); + error = may_delete_dentry(rd->mnt_idmap, new_dir, + new_dentry, is_dir); else - error = may_delete(rd->mnt_idmap, new_dir, - new_dentry, new_is_dir); + error = may_delete_dentry(rd->mnt_idmap, new_dir, + new_dentry, new_is_dir); } if (error) return error; @@ -5974,8 +6062,8 @@ out: } EXPORT_SYMBOL(vfs_rename); -int do_renameat2(int olddfd, struct filename *from, int newdfd, - struct filename *to, unsigned int flags) +int filename_renameat2(int olddfd, struct filename *from, + int newdfd, struct filename *to, unsigned int flags) { struct renamedata rd; struct path old_path, new_path; @@ -5984,20 +6072,20 @@ int do_renameat2(int olddfd, struct filename *from, int newdfd, struct delegated_inode delegated_inode = { }; unsigned int lookup_flags = 0; bool should_retry = false; - int error = -EINVAL; + int error; if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE | RENAME_WHITEOUT)) - goto put_names; + return -EINVAL; if ((flags & (RENAME_NOREPLACE | RENAME_WHITEOUT)) && (flags & RENAME_EXCHANGE)) - goto put_names; + return -EINVAL; retry: error = filename_parentat(olddfd, from, lookup_flags, &old_path, &old_last, &old_type); if (error) - goto put_names; + return error; error = filename_parentat(newdfd, to, lookup_flags, &new_path, &new_last, &new_type); @@ -6074,30 +6162,30 @@ exit1: lookup_flags |= LOOKUP_REVAL; goto retry; } -put_names: - putname(from); - putname(to); return error; } SYSCALL_DEFINE5(renameat2, int, olddfd, const char __user *, oldname, int, newdfd, const char __user *, newname, unsigned int, flags) { - return do_renameat2(olddfd, getname(oldname), newdfd, getname(newname), - flags); + CLASS(filename, old)(oldname); + CLASS(filename, new)(newname); + return filename_renameat2(olddfd, old, newdfd, new, flags); } SYSCALL_DEFINE4(renameat, int, olddfd, const char __user *, oldname, int, newdfd, const char __user *, newname) { - return do_renameat2(olddfd, getname(oldname), newdfd, getname(newname), - 0); + CLASS(filename, old)(oldname); + CLASS(filename, new)(newname); + return filename_renameat2(olddfd, old, newdfd, new, 0); } SYSCALL_DEFINE2(rename, const char __user *, oldname, const char __user *, newname) { - return do_renameat2(AT_FDCWD, getname(oldname), AT_FDCWD, - getname(newname), 0); + CLASS(filename, old)(oldname); + CLASS(filename, new)(newname); + return filename_renameat2(AT_FDCWD, old, AT_FDCWD, new, 0); } int readlink_copy(char __user *buffer, int buflen, const char *link, int linklen) diff --git a/fs/namespace.c b/fs/namespace.c index c58674a20cad..a67cbe42746d 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -49,20 +49,14 @@ static unsigned int mp_hash_shift __ro_after_init; static __initdata unsigned long mhash_entries; static int __init set_mhash_entries(char *str) { - if (!str) - return 0; - mhash_entries = simple_strtoul(str, &str, 0); - return 1; + return kstrtoul(str, 0, &mhash_entries) == 0; } __setup("mhash_entries=", set_mhash_entries); static __initdata unsigned long mphash_entries; static int __init set_mphash_entries(char *str) { - if (!str) - return 0; - mphash_entries = simple_strtoul(str, &str, 0); - return 1; + return kstrtoul(str, 0, &mphash_entries) == 0; } __setup("mphash_entries=", set_mphash_entries); @@ -221,7 +215,7 @@ static int mnt_alloc_id(struct mount *mnt) int res; xa_lock(&mnt_id_xa); - res = __xa_alloc(&mnt_id_xa, &mnt->mnt_id, mnt, XA_LIMIT(1, INT_MAX), GFP_KERNEL); + res = __xa_alloc(&mnt_id_xa, &mnt->mnt_id, mnt, xa_limit_31b, GFP_KERNEL); if (!res) mnt->mnt_id_unique = ++mnt_id_ctr; xa_unlock(&mnt_id_xa); @@ -2796,6 +2790,9 @@ static inline void unlock_mount(struct pinned_mountpoint *m) __unlock_mount(m); } +static void lock_mount_exact(const struct path *path, + struct pinned_mountpoint *mp); + #define LOCK_MOUNT_MAYBE_BENEATH(mp, path, beneath) \ struct pinned_mountpoint mp __cleanup(unlock_mount) = {}; \ do_lock_mount((path), &mp, (beneath)) @@ -2946,10 +2943,11 @@ static inline bool may_copy_tree(const struct path *path) return check_anonymous_mnt(mnt); } - -static struct mount *__do_loopback(const struct path *old_path, int recurse) +static struct mount *__do_loopback(const struct path *old_path, + unsigned int flags, unsigned int copy_flags) { struct mount *old = real_mount(old_path->mnt); + bool recurse = flags & AT_RECURSIVE; if (IS_MNT_UNBINDABLE(old)) return ERR_PTR(-EINVAL); @@ -2960,10 +2958,22 @@ static struct mount *__do_loopback(const struct path *old_path, int recurse) if (!recurse && __has_locked_children(old, old_path->dentry)) return ERR_PTR(-EINVAL); + /* + * When creating a new mount namespace we don't want to copy over + * mounts of mount namespaces to avoid the risk of cycles and also to + * minimize the default complex interdependencies between mount + * namespaces. + * + * We could ofc just check whether all mount namespace files aren't + * creating cycles but really let's keep this simple. + */ + if (!(flags & OPEN_TREE_NAMESPACE)) + copy_flags |= CL_COPY_MNT_NS_FILE; + if (recurse) - return copy_tree(old, old_path->dentry, CL_COPY_MNT_NS_FILE); - else - return clone_mnt(old, old_path->dentry, 0); + return copy_tree(old, old_path->dentry, copy_flags); + + return clone_mnt(old, old_path->dentry, copy_flags); } /* @@ -2974,7 +2984,9 @@ static int do_loopback(const struct path *path, const char *old_name, { struct path old_path __free(path_put) = {}; struct mount *mnt = NULL; + unsigned int flags = recurse ? AT_RECURSIVE : 0; int err; + if (!old_name || !*old_name) return -EINVAL; err = kern_path(old_name, LOOKUP_FOLLOW|LOOKUP_AUTOMOUNT, &old_path); @@ -2991,7 +3003,7 @@ static int do_loopback(const struct path *path, const char *old_name, if (!check_mnt(mp.parent)) return -EINVAL; - mnt = __do_loopback(&old_path, recurse); + mnt = __do_loopback(&old_path, flags, 0); if (IS_ERR(mnt)) return PTR_ERR(mnt); @@ -3004,7 +3016,7 @@ static int do_loopback(const struct path *path, const char *old_name, return err; } -static struct mnt_namespace *get_detached_copy(const struct path *path, bool recursive) +static struct mnt_namespace *get_detached_copy(const struct path *path, unsigned int flags) { struct mnt_namespace *ns, *mnt_ns = current->nsproxy->mnt_ns, *src_mnt_ns; struct user_namespace *user_ns = mnt_ns->user_ns; @@ -3029,7 +3041,7 @@ static struct mnt_namespace *get_detached_copy(const struct path *path, bool rec ns->seq_origin = src_mnt_ns->ns.ns_id; } - mnt = __do_loopback(path, recursive); + mnt = __do_loopback(path, flags, 0); if (IS_ERR(mnt)) { emptied_ns = ns; return ERR_CAST(mnt); @@ -3043,9 +3055,9 @@ static struct mnt_namespace *get_detached_copy(const struct path *path, bool rec return ns; } -static struct file *open_detached_copy(struct path *path, bool recursive) +static struct file *open_detached_copy(struct path *path, unsigned int flags) { - struct mnt_namespace *ns = get_detached_copy(path, recursive); + struct mnt_namespace *ns = get_detached_copy(path, flags); struct file *file; if (IS_ERR(ns)) @@ -3061,39 +3073,151 @@ static struct file *open_detached_copy(struct path *path, bool recursive) return file; } +DEFINE_FREE(put_empty_mnt_ns, struct mnt_namespace *, + if (!IS_ERR_OR_NULL(_T)) free_mnt_ns(_T)) + +static struct mnt_namespace *create_new_namespace(struct path *path, unsigned int flags) +{ + struct mnt_namespace *new_ns __free(put_empty_mnt_ns) = NULL; + struct path to_path __free(path_put) = {}; + struct mnt_namespace *ns = current->nsproxy->mnt_ns; + struct user_namespace *user_ns = current_user_ns(); + struct mount *new_ns_root; + struct mount *mnt; + unsigned int copy_flags = 0; + bool locked = false; + + if (user_ns != ns->user_ns) + copy_flags |= CL_SLAVE; + + new_ns = alloc_mnt_ns(user_ns, false); + if (IS_ERR(new_ns)) + return ERR_CAST(new_ns); + + scoped_guard(namespace_excl) { + new_ns_root = clone_mnt(ns->root, ns->root->mnt.mnt_root, copy_flags); + if (IS_ERR(new_ns_root)) + return ERR_CAST(new_ns_root); + + /* + * If the real rootfs had a locked mount on top of it somewhere + * in the stack, lock the new mount tree as well so it can't be + * exposed. + */ + mnt = ns->root; + while (mnt->overmount) { + mnt = mnt->overmount; + if (mnt->mnt.mnt_flags & MNT_LOCKED) + locked = true; + } + } + + /* + * We dropped the namespace semaphore so we can actually lock + * the copy for mounting. The copied mount isn't attached to any + * mount namespace and it is thus excluded from any propagation. + * So realistically we're isolated and the mount can't be + * overmounted. + */ + + /* Borrow the reference from clone_mnt(). */ + to_path.mnt = &new_ns_root->mnt; + to_path.dentry = dget(new_ns_root->mnt.mnt_root); + + /* Now lock for actual mounting. */ + LOCK_MOUNT_EXACT(mp, &to_path); + if (unlikely(IS_ERR(mp.parent))) + return ERR_CAST(mp.parent); + + /* + * We don't emulate unshare()ing a mount namespace. We stick to the + * restrictions of creating detached bind-mounts. It has a lot + * saner and simpler semantics. + */ + mnt = __do_loopback(path, flags, copy_flags); + if (IS_ERR(mnt)) + return ERR_CAST(mnt); + + scoped_guard(mount_writer) { + if (locked) + mnt->mnt.mnt_flags |= MNT_LOCKED; + /* + * Now mount the detached tree on top of the copy of the + * real rootfs we created. + */ + attach_mnt(mnt, new_ns_root, mp.mp); + if (user_ns != ns->user_ns) + lock_mnt_tree(new_ns_root); + } + + /* Add all mounts to the new namespace. */ + for (struct mount *p = new_ns_root; p; p = next_mnt(p, new_ns_root)) { + mnt_add_to_ns(new_ns, p); + new_ns->nr_mounts++; + } + + new_ns->root = real_mount(no_free_ptr(to_path.mnt)); + ns_tree_add_raw(new_ns); + return no_free_ptr(new_ns); +} + +static struct file *open_new_namespace(struct path *path, unsigned int flags) +{ + struct mnt_namespace *new_ns; + + new_ns = create_new_namespace(path, flags); + if (IS_ERR(new_ns)) + return ERR_CAST(new_ns); + return open_namespace_file(to_ns_common(new_ns)); +} + static struct file *vfs_open_tree(int dfd, const char __user *filename, unsigned int flags) { int ret; struct path path __free(path_put) = {}; int lookup_flags = LOOKUP_AUTOMOUNT | LOOKUP_FOLLOW; - bool detached = flags & OPEN_TREE_CLONE; BUILD_BUG_ON(OPEN_TREE_CLOEXEC != O_CLOEXEC); if (flags & ~(AT_EMPTY_PATH | AT_NO_AUTOMOUNT | AT_RECURSIVE | AT_SYMLINK_NOFOLLOW | OPEN_TREE_CLONE | - OPEN_TREE_CLOEXEC)) + OPEN_TREE_CLOEXEC | OPEN_TREE_NAMESPACE)) return ERR_PTR(-EINVAL); - if ((flags & (AT_RECURSIVE | OPEN_TREE_CLONE)) == AT_RECURSIVE) + if ((flags & (AT_RECURSIVE | OPEN_TREE_CLONE | OPEN_TREE_NAMESPACE)) == + AT_RECURSIVE) + return ERR_PTR(-EINVAL); + + if (hweight32(flags & (OPEN_TREE_CLONE | OPEN_TREE_NAMESPACE)) > 1) return ERR_PTR(-EINVAL); if (flags & AT_NO_AUTOMOUNT) lookup_flags &= ~LOOKUP_AUTOMOUNT; if (flags & AT_SYMLINK_NOFOLLOW) lookup_flags &= ~LOOKUP_FOLLOW; - if (flags & AT_EMPTY_PATH) - lookup_flags |= LOOKUP_EMPTY; - if (detached && !may_mount()) + /* + * If we create a new mount namespace with the cloned mount tree we + * just care about being privileged over our current user namespace. + * The new mount namespace will be owned by it. + */ + if ((flags & OPEN_TREE_NAMESPACE) && + !ns_capable(current_user_ns(), CAP_SYS_ADMIN)) + return ERR_PTR(-EPERM); + + if ((flags & OPEN_TREE_CLONE) && !may_mount()) return ERR_PTR(-EPERM); - ret = user_path_at(dfd, filename, lookup_flags, &path); + CLASS(filename_uflags, name)(filename, flags); + ret = filename_lookup(dfd, name, lookup_flags, &path, NULL); if (unlikely(ret)) return ERR_PTR(ret); - if (detached) - return open_detached_copy(&path, flags & AT_RECURSIVE); + if (flags & OPEN_TREE_NAMESPACE) + return open_new_namespace(&path, flags); + + if (flags & OPEN_TREE_CLONE) + return open_detached_copy(&path, flags); return dentry_open(&path, O_PATH, current_cred()); } @@ -4403,8 +4527,6 @@ SYSCALL_DEFINE5(move_mount, { struct path to_path __free(path_put) = {}; struct path from_path __free(path_put) = {}; - struct filename *to_name __free(putname) = NULL; - struct filename *from_name __free(putname) = NULL; unsigned int lflags, uflags; enum mnt_tree_flags_t mflags = 0; int ret = 0; @@ -4426,10 +4548,7 @@ SYSCALL_DEFINE5(move_mount, if (flags & MOVE_MOUNT_T_EMPTY_PATH) uflags = AT_EMPTY_PATH; - to_name = getname_maybe_null(to_pathname, uflags); - if (IS_ERR(to_name)) - return PTR_ERR(to_name); - + CLASS(filename_maybe_null,to_name)(to_pathname, uflags); if (!to_name && to_dfd >= 0) { CLASS(fd_raw, f_to)(to_dfd); if (fd_empty(f_to)) @@ -4452,10 +4571,7 @@ SYSCALL_DEFINE5(move_mount, if (flags & MOVE_MOUNT_F_EMPTY_PATH) uflags = AT_EMPTY_PATH; - from_name = getname_maybe_null(from_pathname, uflags); - if (IS_ERR(from_name)) - return PTR_ERR(from_name); - + CLASS(filename_maybe_null,from_name)(from_pathname, uflags); if (!from_name && from_dfd >= 0) { CLASS(fd_raw, f_from)(from_dfd); if (fd_empty(f_from)) @@ -4498,36 +4614,8 @@ bool path_is_under(const struct path *path1, const struct path *path2) } EXPORT_SYMBOL(path_is_under); -/* - * pivot_root Semantics: - * Moves the root file system of the current process to the directory put_old, - * makes new_root as the new root file system of the current process, and sets - * root/cwd of all processes which had them on the current root to new_root. - * - * Restrictions: - * The new_root and put_old must be directories, and must not be on the - * same file system as the current process root. The put_old must be - * underneath new_root, i.e. adding a non-zero number of /.. to the string - * pointed to by put_old must yield the same directory as new_root. No other - * file system may be mounted on put_old. After all, new_root is a mountpoint. - * - * Also, the current root cannot be on the 'rootfs' (initial ramfs) filesystem. - * See Documentation/filesystems/ramfs-rootfs-initramfs.rst for alternatives - * in this situation. - * - * Notes: - * - we don't move root/cwd if they are not at the root (reason: if something - * cared enough to change them, it's probably wrong to force them elsewhere) - * - it's okay to pick a root that isn't the root of a file system, e.g. - * /nfs/my_root where /nfs is the mount point. It must be a mountpoint, - * though, so you may need to say mount --bind /nfs/my_root /nfs/my_root - * first. - */ -SYSCALL_DEFINE2(pivot_root, const char __user *, new_root, - const char __user *, put_old) +int path_pivot_root(struct path *new, struct path *old) { - struct path new __free(path_put) = {}; - struct path old __free(path_put) = {}; struct path root __free(path_put) = {}; struct mount *new_mnt, *root_mnt, *old_mnt, *root_parent, *ex_parent; int error; @@ -4535,28 +4623,18 @@ SYSCALL_DEFINE2(pivot_root, const char __user *, new_root, if (!may_mount()) return -EPERM; - error = user_path_at(AT_FDCWD, new_root, - LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &new); - if (error) - return error; - - error = user_path_at(AT_FDCWD, put_old, - LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &old); - if (error) - return error; - - error = security_sb_pivotroot(&old, &new); + error = security_sb_pivotroot(old, new); if (error) return error; get_fs_root(current->fs, &root); - LOCK_MOUNT(old_mp, &old); + LOCK_MOUNT(old_mp, old); old_mnt = old_mp.parent; if (IS_ERR(old_mnt)) return PTR_ERR(old_mnt); - new_mnt = real_mount(new.mnt); + new_mnt = real_mount(new->mnt); root_mnt = real_mount(root.mnt); ex_parent = new_mnt->mnt_parent; root_parent = root_mnt->mnt_parent; @@ -4568,7 +4646,7 @@ SYSCALL_DEFINE2(pivot_root, const char __user *, new_root, return -EINVAL; if (new_mnt->mnt.mnt_flags & MNT_LOCKED) return -EINVAL; - if (d_unlinked(new.dentry)) + if (d_unlinked(new->dentry)) return -ENOENT; if (new_mnt == root_mnt || old_mnt == root_mnt) return -EBUSY; /* loop, on the same file system */ @@ -4576,15 +4654,15 @@ SYSCALL_DEFINE2(pivot_root, const char __user *, new_root, return -EINVAL; /* not a mountpoint */ if (!mnt_has_parent(root_mnt)) return -EINVAL; /* absolute root */ - if (!path_mounted(&new)) + if (!path_mounted(new)) return -EINVAL; /* not a mountpoint */ if (!mnt_has_parent(new_mnt)) return -EINVAL; /* absolute root */ /* make sure we can reach put_old from new_root */ - if (!is_path_reachable(old_mnt, old_mp.mp->m_dentry, &new)) + if (!is_path_reachable(old_mnt, old_mp.mp->m_dentry, new)) return -EINVAL; /* make certain new is below the root */ - if (!is_path_reachable(new_mnt, new.dentry, &root)) + if (!is_path_reachable(new_mnt, new->dentry, &root)) return -EINVAL; lock_mount_hash(); umount_mnt(new_mnt); @@ -4603,10 +4681,55 @@ SYSCALL_DEFINE2(pivot_root, const char __user *, new_root, unlock_mount_hash(); mnt_notify_add(root_mnt); mnt_notify_add(new_mnt); - chroot_fs_refs(&root, &new); + chroot_fs_refs(&root, new); return 0; } +/* + * pivot_root Semantics: + * Moves the root file system of the current process to the directory put_old, + * makes new_root as the new root file system of the current process, and sets + * root/cwd of all processes which had them on the current root to new_root. + * + * Restrictions: + * The new_root and put_old must be directories, and must not be on the + * same file system as the current process root. The put_old must be + * underneath new_root, i.e. adding a non-zero number of /.. to the string + * pointed to by put_old must yield the same directory as new_root. No other + * file system may be mounted on put_old. After all, new_root is a mountpoint. + * + * The immutable nullfs filesystem is mounted as the true root of the VFS + * hierarchy. The mutable rootfs (tmpfs/ramfs) is layered on top of this, + * allowing pivot_root() to work normally from initramfs. + * + * Notes: + * - we don't move root/cwd if they are not at the root (reason: if something + * cared enough to change them, it's probably wrong to force them elsewhere) + * - it's okay to pick a root that isn't the root of a file system, e.g. + * /nfs/my_root where /nfs is the mount point. It must be a mountpoint, + * though, so you may need to say mount --bind /nfs/my_root /nfs/my_root + * first. + */ +SYSCALL_DEFINE2(pivot_root, const char __user *, new_root, + const char __user *, put_old) +{ + struct path new __free(path_put) = {}; + struct path old __free(path_put) = {}; + int error; + + error = user_path_at(AT_FDCWD, new_root, + LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &new); + if (error) + return error; + + error = user_path_at(AT_FDCWD, put_old, + LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &old); + if (error) + return error; + + return path_pivot_root(&new, &old); +} + static unsigned int recalc_flags(struct mount_kattr *kattr, struct mount *mnt) { unsigned int flags = mnt->mnt.mnt_flags; @@ -4984,8 +5107,6 @@ SYSCALL_DEFINE5(mount_setattr, int, dfd, const char __user *, path, lookup_flags &= ~LOOKUP_AUTOMOUNT; if (flags & AT_SYMLINK_NOFOLLOW) lookup_flags &= ~LOOKUP_FOLLOW; - if (flags & AT_EMPTY_PATH) - lookup_flags |= LOOKUP_EMPTY; kattr = (struct mount_kattr) { .lookup_flags = lookup_flags, @@ -4998,7 +5119,8 @@ SYSCALL_DEFINE5(mount_setattr, int, dfd, const char __user *, path, if (err <= 0) return err; - err = user_path_at(dfd, path, kattr.lookup_flags, &target); + CLASS(filename_uflags, name)(path, flags); + err = filename_lookup(dfd, name, kattr.lookup_flags, &target, NULL); if (!err) { err = do_mount_setattr(&target, &kattr); path_put(&target); @@ -5547,31 +5669,49 @@ static int grab_requested_root(struct mnt_namespace *ns, struct path *root) /* locks: namespace_shared */ static int do_statmount(struct kstatmount *s, u64 mnt_id, u64 mnt_ns_id, - struct mnt_namespace *ns) + struct file *mnt_file, struct mnt_namespace *ns) { - struct mount *m; int err; - /* Has the namespace already been emptied? */ - if (mnt_ns_id && mnt_ns_empty(ns)) - return -ENOENT; + if (mnt_file) { + WARN_ON_ONCE(ns != NULL); - s->mnt = lookup_mnt_in_ns(mnt_id, ns); - if (!s->mnt) - return -ENOENT; + s->mnt = mnt_file->f_path.mnt; + ns = real_mount(s->mnt)->mnt_ns; + if (!ns) + /* + * We can't set mount point and mnt_ns_id since we don't have a + * ns for the mount. This can happen if the mount is unmounted + * with MNT_DETACH. + */ + s->mask &= ~(STATMOUNT_MNT_POINT | STATMOUNT_MNT_NS_ID); + } else { + /* Has the namespace already been emptied? */ + if (mnt_ns_id && mnt_ns_empty(ns)) + return -ENOENT; - err = grab_requested_root(ns, &s->root); - if (err) - return err; + s->mnt = lookup_mnt_in_ns(mnt_id, ns); + if (!s->mnt) + return -ENOENT; + } - /* - * Don't trigger audit denials. We just want to determine what - * mounts to show users. - */ - m = real_mount(s->mnt); - if (!is_path_reachable(m, m->mnt.mnt_root, &s->root) && - !ns_capable_noaudit(ns->user_ns, CAP_SYS_ADMIN)) - return -EPERM; + if (ns) { + err = grab_requested_root(ns, &s->root); + if (err) + return err; + + if (!mnt_file) { + struct mount *m; + /* + * Don't trigger audit denials. We just want to determine what + * mounts to show users. + */ + m = real_mount(s->mnt); + if (!is_path_reachable(m, m->mnt.mnt_root, &s->root) && + !ns_capable_noaudit(ns->user_ns, CAP_SYS_ADMIN)) + return -EPERM; + } + } err = security_sb_statfs(s->mnt->mnt_root); if (err) @@ -5693,7 +5833,7 @@ static int prepare_kstatmount(struct kstatmount *ks, struct mnt_id_req *kreq, } static int copy_mnt_id_req(const struct mnt_id_req __user *req, - struct mnt_id_req *kreq) + struct mnt_id_req *kreq, unsigned int flags) { int ret; size_t usize; @@ -5711,11 +5851,17 @@ static int copy_mnt_id_req(const struct mnt_id_req __user *req, ret = copy_struct_from_user(kreq, sizeof(*kreq), req, usize); if (ret) return ret; - if (kreq->mnt_ns_fd != 0 && kreq->mnt_ns_id) - return -EINVAL; - /* The first valid unique mount id is MNT_UNIQUE_ID_OFFSET + 1. */ - if (kreq->mnt_id <= MNT_UNIQUE_ID_OFFSET) - return -EINVAL; + + if (flags & STATMOUNT_BY_FD) { + if (kreq->mnt_id || kreq->mnt_ns_id) + return -EINVAL; + } else { + if (kreq->mnt_ns_fd != 0 && kreq->mnt_ns_id) + return -EINVAL; + /* The first valid unique mount id is MNT_UNIQUE_ID_OFFSET + 1. */ + if (kreq->mnt_id <= MNT_UNIQUE_ID_OFFSET) + return -EINVAL; + } return 0; } @@ -5762,25 +5908,33 @@ SYSCALL_DEFINE4(statmount, const struct mnt_id_req __user *, req, { struct mnt_namespace *ns __free(mnt_ns_release) = NULL; struct kstatmount *ks __free(kfree) = NULL; + struct file *mnt_file __free(fput) = NULL; struct mnt_id_req kreq; /* We currently support retrieval of 3 strings. */ size_t seq_size = 3 * PATH_MAX; int ret; - if (flags) + if (flags & ~STATMOUNT_BY_FD) return -EINVAL; - ret = copy_mnt_id_req(req, &kreq); + ret = copy_mnt_id_req(req, &kreq, flags); if (ret) return ret; - ns = grab_requested_mnt_ns(&kreq); - if (IS_ERR(ns)) - return PTR_ERR(ns); + if (flags & STATMOUNT_BY_FD) { + mnt_file = fget_raw(kreq.mnt_fd); + if (!mnt_file) + return -EBADF; + /* do_statmount sets ns in case of STATMOUNT_BY_FD */ + } else { + ns = grab_requested_mnt_ns(&kreq); + if (IS_ERR(ns)) + return PTR_ERR(ns); - if (kreq.mnt_ns_id && (ns != current->nsproxy->mnt_ns) && - !ns_capable_noaudit(ns->user_ns, CAP_SYS_ADMIN)) - return -ENOENT; + if (kreq.mnt_ns_id && (ns != current->nsproxy->mnt_ns) && + !ns_capable_noaudit(ns->user_ns, CAP_SYS_ADMIN)) + return -EPERM; + } ks = kmalloc(sizeof(*ks), GFP_KERNEL_ACCOUNT); if (!ks) @@ -5792,7 +5946,7 @@ retry: return ret; scoped_guard(namespace_shared) - ret = do_statmount(ks, kreq.mnt_id, kreq.mnt_ns_id, ns); + ret = do_statmount(ks, kreq.mnt_id, kreq.mnt_ns_id, mnt_file, ns); if (!ret) ret = copy_statmount_to_user(ks); @@ -5932,7 +6086,7 @@ SYSCALL_DEFINE4(listmount, const struct mnt_id_req __user *, req, if (!access_ok(mnt_ids, nr_mnt_ids * sizeof(*mnt_ids))) return -EFAULT; - ret = copy_mnt_id_req(req, &kreq); + ret = copy_mnt_id_req(req, &kreq, 0); if (ret) return ret; @@ -5969,24 +6123,62 @@ struct mnt_namespace init_mnt_ns = { static void __init init_mount_tree(void) { - struct vfsmount *mnt; - struct mount *m; + struct vfsmount *mnt, *nullfs_mnt; + struct mount *mnt_root; struct path root; + /* + * We create two mounts: + * + * (1) nullfs with mount id 1 + * (2) mutable rootfs with mount id 2 + * + * with (2) mounted on top of (1). + */ + nullfs_mnt = vfs_kern_mount(&nullfs_fs_type, 0, "nullfs", NULL); + if (IS_ERR(nullfs_mnt)) + panic("VFS: Failed to create nullfs"); + mnt = vfs_kern_mount(&rootfs_fs_type, 0, "rootfs", initramfs_options); if (IS_ERR(mnt)) panic("Can't create rootfs"); - m = real_mount(mnt); - init_mnt_ns.root = m; - init_mnt_ns.nr_mounts = 1; - mnt_add_to_ns(&init_mnt_ns, m); + VFS_WARN_ON_ONCE(real_mount(nullfs_mnt)->mnt_id != 1); + VFS_WARN_ON_ONCE(real_mount(mnt)->mnt_id != 2); + + /* The namespace root is the nullfs mnt. */ + mnt_root = real_mount(nullfs_mnt); + init_mnt_ns.root = mnt_root; + + /* Mount mutable rootfs on top of nullfs. */ + root.mnt = nullfs_mnt; + root.dentry = nullfs_mnt->mnt_root; + + LOCK_MOUNT_EXACT(mp, &root); + if (unlikely(IS_ERR(mp.parent))) + panic("VFS: Failed to mount rootfs on nullfs"); + scoped_guard(mount_writer) + attach_mnt(real_mount(mnt), mp.parent, mp.mp); + + pr_info("VFS: Finished mounting rootfs on nullfs\n"); + + /* + * We've dropped all locks here but that's fine. Not just are we + * the only task that's running, there's no other mount + * namespace in existence and the initial mount namespace is + * completely empty until we add the mounts we just created. + */ + for (struct mount *p = mnt_root; p; p = next_mnt(p, mnt_root)) { + mnt_add_to_ns(&init_mnt_ns, p); + init_mnt_ns.nr_mounts++; + } + init_task.nsproxy->mnt_ns = &init_mnt_ns; get_mnt_ns(&init_mnt_ns); - root.mnt = mnt; - root.dentry = mnt->mnt_root; - + /* The root and pwd always point to the mutable rootfs. */ + root.mnt = mnt; + root.dentry = mnt->mnt_root; set_fs_pwd(current->fs, &root); set_fs_root(current->fs, &root); diff --git a/fs/netfs/read_collect.c b/fs/netfs/read_collect.c index 7a0ffa675fb1..137f0e28a44c 100644 --- a/fs/netfs/read_collect.c +++ b/fs/netfs/read_collect.c @@ -546,6 +546,15 @@ void netfs_read_subreq_terminated(struct netfs_io_subrequest *subreq) } } + /* If need retry is set, error should not matter unless we hit too many + * retries. Pause the generation of new subreqs + */ + if (test_bit(NETFS_SREQ_NEED_RETRY, &subreq->flags)) { + trace_netfs_rreq(rreq, netfs_rreq_trace_set_pause); + set_bit(NETFS_RREQ_PAUSE, &rreq->flags); + goto skip_error_checks; + } + if (unlikely(subreq->error < 0)) { trace_netfs_failure(rreq, subreq, subreq->error, netfs_fail_read); if (subreq->source == NETFS_READ_FROM_CACHE) { @@ -559,6 +568,7 @@ void netfs_read_subreq_terminated(struct netfs_io_subrequest *subreq) set_bit(NETFS_RREQ_PAUSE, &rreq->flags); } +skip_error_checks: trace_netfs_sreq(subreq, netfs_sreq_trace_terminated); netfs_subreq_clear_in_progress(subreq); netfs_put_subrequest(subreq, netfs_sreq_trace_put_terminated); diff --git a/fs/netfs/read_retry.c b/fs/netfs/read_retry.c index b99e84a8170a..7793ba5e3e8f 100644 --- a/fs/netfs/read_retry.c +++ b/fs/netfs/read_retry.c @@ -12,6 +12,7 @@ static void netfs_reissue_read(struct netfs_io_request *rreq, struct netfs_io_subrequest *subreq) { + subreq->error = 0; __clear_bit(NETFS_SREQ_MADE_PROGRESS, &subreq->flags); __set_bit(NETFS_SREQ_IN_PROGRESS, &subreq->flags); netfs_stat(&netfs_n_rh_retry_read_subreq); @@ -242,8 +243,7 @@ abandon_after: subreq = list_next_entry(subreq, rreq_link); abandon: list_for_each_entry_from(subreq, &stream->subrequests, rreq_link) { - if (!subreq->error && - !test_bit(NETFS_SREQ_FAILED, &subreq->flags) && + if (!test_bit(NETFS_SREQ_FAILED, &subreq->flags) && !test_bit(NETFS_SREQ_NEED_RETRY, &subreq->flags)) continue; subreq->error = -ENOMEM; diff --git a/fs/netfs/write_collect.c b/fs/netfs/write_collect.c index cbf3d9194c7b..61eab34ea67e 100644 --- a/fs/netfs/write_collect.c +++ b/fs/netfs/write_collect.c @@ -492,11 +492,11 @@ void netfs_write_subrequest_terminated(void *_op, ssize_t transferred_or_error) if (IS_ERR_VALUE(transferred_or_error)) { subreq->error = transferred_or_error; - if (subreq->error == -EAGAIN) - set_bit(NETFS_SREQ_NEED_RETRY, &subreq->flags); - else + /* if need retry is set, error should not matter */ + if (!test_bit(NETFS_SREQ_NEED_RETRY, &subreq->flags)) { set_bit(NETFS_SREQ_FAILED, &subreq->flags); - trace_netfs_failure(wreq, subreq, transferred_or_error, netfs_fail_write); + trace_netfs_failure(wreq, subreq, transferred_or_error, netfs_fail_write); + } switch (subreq->source) { case NETFS_WRITE_TO_CACHE: diff --git a/fs/netfs/write_issue.c b/fs/netfs/write_issue.c index dd8743bc8d7f..34894da5a23e 100644 --- a/fs/netfs/write_issue.c +++ b/fs/netfs/write_issue.c @@ -250,6 +250,7 @@ void netfs_reissue_write(struct netfs_io_stream *stream, iov_iter_truncate(&subreq->io_iter, size); subreq->retry_count++; + subreq->error = 0; __clear_bit(NETFS_SREQ_MADE_PROGRESS, &subreq->flags); __set_bit(NETFS_SREQ_IN_PROGRESS, &subreq->flags); netfs_stat(&netfs_n_wh_retry_write_subreq); diff --git a/fs/netfs/write_retry.c b/fs/netfs/write_retry.c index fc9c3e0d34d8..29489a23a220 100644 --- a/fs/netfs/write_retry.c +++ b/fs/netfs/write_retry.c @@ -98,7 +98,6 @@ static void netfs_retry_write_stream(struct netfs_io_request *wreq, subreq->start = start; subreq->len = len; __clear_bit(NETFS_SREQ_NEED_RETRY, &subreq->flags); - subreq->retry_count++; trace_netfs_sreq(subreq, netfs_sreq_trace_retry); /* Renegotiate max_len (wsize) */ diff --git a/fs/nfs/callback.c b/fs/nfs/callback.c index 6af67bdf0e40..ff4e9fd38e83 100644 --- a/fs/nfs/callback.c +++ b/fs/nfs/callback.c @@ -81,7 +81,7 @@ nfs4_callback_svc(void *vrqstp) set_freezable(); while (!svc_thread_should_stop(rqstp)) - svc_recv(rqstp); + svc_recv(rqstp, 0); svc_exit_thread(rqstp); return 0; @@ -112,9 +112,9 @@ static int nfs_callback_start_svc(int minorversion, struct rpc_xprt *xprt, if (serv->sv_nrthreads == nrservs) return 0; - ret = svc_set_num_threads(serv, NULL, nrservs); + ret = svc_set_num_threads(serv, 0, nrservs); if (ret) { - svc_set_num_threads(serv, NULL, 0); + svc_set_num_threads(serv, 0, 0); return ret; } dprintk("nfs_callback_up: service started\n"); @@ -231,7 +231,7 @@ int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt) cb_info->users++; err_net: if (!cb_info->users) { - svc_set_num_threads(cb_info->serv, NULL, 0); + svc_set_num_threads(cb_info->serv, 0, 0); svc_destroy(&cb_info->serv); } err_create: @@ -257,7 +257,7 @@ void nfs_callback_down(int minorversion, struct net *net, struct rpc_xprt *xprt) nfs_callback_down_net(minorversion, serv, net); cb_info->users--; if (cb_info->users == 0) { - svc_set_num_threads(serv, NULL, 0); + svc_set_num_threads(serv, 0, 0); dprintk("nfs_callback_down: service destroyed\n"); xprt_svc_destroy_nullify_bc(xprt, &cb_info->serv); } diff --git a/fs/nfs/file.c b/fs/nfs/file.c index d1c138a416cf..5d08b6409c28 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -963,7 +963,6 @@ const struct file_operations nfs_file_operations = { .splice_read = nfs_file_splice_read, .splice_write = iter_file_splice_write, .check_flags = nfs_check_flags, - .setlease = simple_nosetlease, .fop_flags = FOP_DONTCACHE, }; EXPORT_SYMBOL_GPL(nfs_file_operations); diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index de2cce1d08f4..331cdecdd966 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -649,15 +649,15 @@ static void nfs_set_timestamps_to_ts(struct inode *inode, struct iattr *attr) struct timespec64 ctime = inode_get_ctime(inode); struct timespec64 mtime = inode_get_mtime(inode); struct timespec64 now; - int updated = 0; + bool updated = false; now = inode_set_ctime_current(inode); if (!timespec64_equal(&now, &ctime)) - updated |= S_CTIME; + updated = true; inode_set_mtime_to_ts(inode, attr->ia_mtime); if (!timespec64_equal(&now, &mtime)) - updated |= S_MTIME; + updated = true; inode_maybe_inc_iversion(inode, updated); cache_flags |= NFS_INO_INVALID_CTIME | NFS_INO_INVALID_MTIME; @@ -669,35 +669,31 @@ static void nfs_set_timestamps_to_ts(struct inode *inode, struct iattr *attr) NFS_I(inode)->cache_validity &= ~cache_flags; } -static void nfs_update_timestamps(struct inode *inode, unsigned int ia_valid) +static void nfs_update_atime(struct inode *inode) { - enum file_time_flags time_flags = 0; - unsigned int cache_flags = 0; + inode_update_time(inode, FS_UPD_ATIME, 0); + NFS_I(inode)->cache_validity &= ~NFS_INO_INVALID_ATIME; +} - if (ia_valid & ATTR_MTIME) { - time_flags |= S_MTIME | S_CTIME; - cache_flags |= NFS_INO_INVALID_CTIME | NFS_INO_INVALID_MTIME; - } - if (ia_valid & ATTR_ATIME) { - time_flags |= S_ATIME; - cache_flags |= NFS_INO_INVALID_ATIME; - } - inode_update_timestamps(inode, time_flags); - NFS_I(inode)->cache_validity &= ~cache_flags; +static void nfs_update_mtime(struct inode *inode) +{ + inode_update_time(inode, FS_UPD_CMTIME, 0); + NFS_I(inode)->cache_validity &= + ~(NFS_INO_INVALID_CTIME | NFS_INO_INVALID_MTIME); } void nfs_update_delegated_atime(struct inode *inode) { spin_lock(&inode->i_lock); if (nfs_have_delegated_atime(inode)) - nfs_update_timestamps(inode, ATTR_ATIME); + nfs_update_atime(inode); spin_unlock(&inode->i_lock); } void nfs_update_delegated_mtime_locked(struct inode *inode) { if (nfs_have_delegated_mtime(inode)) - nfs_update_timestamps(inode, ATTR_MTIME); + nfs_update_mtime(inode); } void nfs_update_delegated_mtime(struct inode *inode) @@ -751,7 +747,10 @@ nfs_setattr(struct mnt_idmap *idmap, struct dentry *dentry, ATTR_ATIME|ATTR_ATIME_SET); } } else { - nfs_update_timestamps(inode, attr->ia_valid); + if (attr->ia_valid & ATTR_MTIME) + nfs_update_mtime(inode); + if (attr->ia_valid & ATTR_ATIME) + nfs_update_atime(inode); attr->ia_valid &= ~(ATTR_MTIME|ATTR_ATIME); } spin_unlock(&inode->i_lock); diff --git a/fs/nfs/nfs4file.c b/fs/nfs/nfs4file.c index 7317f26892c5..7f43e890d356 100644 --- a/fs/nfs/nfs4file.c +++ b/fs/nfs/nfs4file.c @@ -431,8 +431,6 @@ void nfs42_ssc_unregister_ops(void) static int nfs4_setlease(struct file *file, int arg, struct file_lease **lease, void **priv) { - if (!S_ISREG(file_inode(file)->i_mode)) - return -EINVAL; return nfs4_proc_setlease(file, arg, lease, priv); } diff --git a/fs/nfsd/Kconfig b/fs/nfsd/Kconfig index 0b5c1a0bf1cf..4fd6e818565e 100644 --- a/fs/nfsd/Kconfig +++ b/fs/nfsd/Kconfig @@ -186,3 +186,22 @@ config NFSD_V4_DELEG_TIMESTAMPS draft-ietf-nfsv4-delstid-08 "Extending the Opening of Files". This is currently an experimental feature and is therefore left disabled by default. + +config NFSD_V4_POSIX_ACLS + bool "Support NFSv4 POSIX draft ACLs" + depends on NFSD_V4 + default n + help + Include experimental support for POSIX Access Control Lists + (ACLs) in NFSv4 as specified in the IETF draft + draft-ietf-nfsv4-posix-acls. This protocol extension enables + NFSv4 clients to retrieve and modify POSIX ACLs on exported + filesystems that support them. + + This feature is based on an unratified IETF draft + specification that may change in ways that impact + interoperability with existing clients. Enable only for + testing environments or when interoperability with specific + clients that implement this draft is required. + + If unsure, say N. diff --git a/fs/nfsd/Makefile b/fs/nfsd/Makefile index 55744bb786c9..f0da4d69dc74 100644 --- a/fs/nfsd/Makefile +++ b/fs/nfsd/Makefile @@ -26,7 +26,15 @@ nfsd-$(CONFIG_NFSD_FLEXFILELAYOUT) += flexfilelayout.o flexfilelayoutxdr.o nfsd-$(CONFIG_NFS_LOCALIO) += localio.o nfsd-$(CONFIG_DEBUG_FS) += debugfs.o - +# +# XDR code generation (requires Python and additional packages) +# +# The generated *xdr_gen.{h,c} files are checked into git. Normal kernel +# builds do not require the xdrgen tool or its Python dependencies. +# +# Developers modifying .x files in Documentation/sunrpc/xdr/ should run +# "make xdrgen" to regenerate the affected files. +# .PHONY: xdrgen xdrgen: ../../include/linux/sunrpc/xdrgen/nfs4_1.h nfs4xdr_gen.h nfs4xdr_gen.c diff --git a/fs/nfsd/acl.h b/fs/nfsd/acl.h index 4b7324458a94..2003523d0e65 100644 --- a/fs/nfsd/acl.h +++ b/fs/nfsd/acl.h @@ -49,5 +49,6 @@ int nfsd4_get_nfs4_acl(struct svc_rqst *rqstp, struct dentry *dentry, struct nfs4_acl **acl); __be32 nfsd4_acl_to_attr(enum nfs_ftype4 type, struct nfs4_acl *acl, struct nfsd_attrs *attr); +void sort_pacl_range(struct posix_acl *pacl, int start, int end); #endif /* LINUX_NFS4_ACL_H */ diff --git a/fs/nfsd/netlink.c b/fs/nfsd/netlink.c index ac51a44e1065..887525964451 100644 --- a/fs/nfsd/netlink.c +++ b/fs/nfsd/netlink.c @@ -24,11 +24,12 @@ const struct nla_policy nfsd_version_nl_policy[NFSD_A_VERSION_ENABLED + 1] = { }; /* NFSD_CMD_THREADS_SET - do */ -static const struct nla_policy nfsd_threads_set_nl_policy[NFSD_A_SERVER_SCOPE + 1] = { +static const struct nla_policy nfsd_threads_set_nl_policy[NFSD_A_SERVER_MIN_THREADS + 1] = { [NFSD_A_SERVER_THREADS] = { .type = NLA_U32, }, [NFSD_A_SERVER_GRACETIME] = { .type = NLA_U32, }, [NFSD_A_SERVER_LEASETIME] = { .type = NLA_U32, }, [NFSD_A_SERVER_SCOPE] = { .type = NLA_NUL_STRING, }, + [NFSD_A_SERVER_MIN_THREADS] = { .type = NLA_U32, }, }; /* NFSD_CMD_VERSION_SET - do */ @@ -57,7 +58,7 @@ static const struct genl_split_ops nfsd_nl_ops[] = { .cmd = NFSD_CMD_THREADS_SET, .doit = nfsd_nl_threads_set_doit, .policy = nfsd_threads_set_nl_policy, - .maxattr = NFSD_A_SERVER_SCOPE, + .maxattr = NFSD_A_SERVER_MIN_THREADS, .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO, }, { diff --git a/fs/nfsd/netns.h b/fs/nfsd/netns.h index fe8338735e7c..9fa600602658 100644 --- a/fs/nfsd/netns.h +++ b/fs/nfsd/netns.h @@ -67,7 +67,6 @@ struct nfsd_net { struct lock_manager nfsd4_manager; bool grace_ended; bool grace_end_forced; - bool client_tracking_active; time64_t boot_time; struct dentry *nfsd_client_dir; @@ -130,6 +129,12 @@ struct nfsd_net { seqlock_t writeverf_lock; unsigned char writeverf[8]; + /* + * Minimum number of threads to run per pool. If 0 then the + * min == max requested number of threads. + */ + unsigned int min_threads; + u32 clientid_base; u32 clientid_counter; u32 clverifier_counter; diff --git a/fs/nfsd/nfs2acl.c b/fs/nfsd/nfs2acl.c index 5fb202acb0fd..0ac538c76180 100644 --- a/fs/nfsd/nfs2acl.c +++ b/fs/nfsd/nfs2acl.c @@ -45,7 +45,7 @@ static __be32 nfsacld_proc_getacl(struct svc_rqst *rqstp) inode = d_inode(fh->fh_dentry); if (argp->mask & ~NFS_ACL_MASK) { - resp->status = nfserr_inval; + resp->status = nfserr_io; goto out; } resp->mask = argp->mask; diff --git a/fs/nfsd/nfs4acl.c b/fs/nfsd/nfs4acl.c index 936ea1ad9586..2c2f2fd89e87 100644 --- a/fs/nfsd/nfs4acl.c +++ b/fs/nfsd/nfs4acl.c @@ -369,12 +369,21 @@ pace_gt(struct posix_acl_entry *pace1, struct posix_acl_entry *pace2) return false; } -static void -sort_pacl_range(struct posix_acl *pacl, int start, int end) { +/** + * sort_pacl_range - sort a range of POSIX ACL entries by tag and id + * @pacl: POSIX ACL containing entries to sort + * @start: starting index of range to sort + * @end: ending index of range to sort (inclusive) + * + * Sorts ACL entries in place so that USER entries are ordered by UID + * and GROUP entries are ordered by GID. Required before calling + * posix_acl_valid(). + */ +void sort_pacl_range(struct posix_acl *pacl, int start, int end) +{ int sorted = 0, i; - /* We just do a bubble sort; easy to do in place, and we're not - * expecting acl's to be long enough to justify anything more. */ + /* Bubble sort: acceptable here because ACLs are typically short. */ while (!sorted) { sorted = 1; for (i = start; i < end; i++) { diff --git a/fs/nfsd/nfs4idmap.c b/fs/nfsd/nfs4idmap.c index 8cca1329f348..c319c31b0f64 100644 --- a/fs/nfsd/nfs4idmap.c +++ b/fs/nfsd/nfs4idmap.c @@ -643,34 +643,74 @@ static __be32 encode_name_from_id(struct xdr_stream *xdr, return idmap_id_to_name(xdr, rqstp, type, id); } -__be32 -nfsd_map_name_to_uid(struct svc_rqst *rqstp, const char *name, size_t namelen, - kuid_t *uid) +/** + * nfsd_map_name_to_uid - Map user@domain to local UID + * @rqstp: RPC execution context + * @name: user@domain name to be mapped + * @namelen: length of name, in bytes + * @uid: OUT: mapped local UID value + * + * Returns nfs_ok on success or an NFSv4 status code on failure. + */ +__be32 nfsd_map_name_to_uid(struct svc_rqst *rqstp, const char *name, + size_t namelen, kuid_t *uid) { __be32 status; u32 id = -1; + /* + * The idmap lookup below triggers an upcall that invokes + * cache_check(). RQ_USEDEFERRAL must be clear to prevent + * cache_check() from setting RQ_DROPME via svc_defer(). + * NFSv4 servers are not permitted to drop requests. Also + * RQ_DROPME will force NFSv4.1 session slot processing to + * be skipped. + */ + WARN_ON_ONCE(test_bit(RQ_USEDEFERRAL, &rqstp->rq_flags)); + if (name == NULL || namelen == 0) return nfserr_inval; status = do_name_to_id(rqstp, IDMAP_TYPE_USER, name, namelen, &id); + if (status) + return status; *uid = make_kuid(nfsd_user_namespace(rqstp), id); if (!uid_valid(*uid)) status = nfserr_badowner; return status; } -__be32 -nfsd_map_name_to_gid(struct svc_rqst *rqstp, const char *name, size_t namelen, - kgid_t *gid) +/** + * nfsd_map_name_to_gid - Map user@domain to local GID + * @rqstp: RPC execution context + * @name: user@domain name to be mapped + * @namelen: length of name, in bytes + * @gid: OUT: mapped local GID value + * + * Returns nfs_ok on success or an NFSv4 status code on failure. + */ +__be32 nfsd_map_name_to_gid(struct svc_rqst *rqstp, const char *name, + size_t namelen, kgid_t *gid) { __be32 status; u32 id = -1; + /* + * The idmap lookup below triggers an upcall that invokes + * cache_check(). RQ_USEDEFERRAL must be clear to prevent + * cache_check() from setting RQ_DROPME via svc_defer(). + * NFSv4 servers are not permitted to drop requests. Also + * RQ_DROPME will force NFSv4.1 session slot processing to + * be skipped. + */ + WARN_ON_ONCE(test_bit(RQ_USEDEFERRAL, &rqstp->rq_flags)); + if (name == NULL || namelen == 0) return nfserr_inval; status = do_name_to_id(rqstp, IDMAP_TYPE_GROUP, name, namelen, &id); + if (status) + return status; *gid = make_kgid(nfsd_user_namespace(rqstp), id); if (!gid_valid(*gid)) status = nfserr_badowner; diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index 9ec08dd4fe82..37ab3a69c4b6 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -81,8 +81,8 @@ static u32 nfsd41_ex_attrmask[] = { }; static __be32 -check_attr_support(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, - u32 *bmval, u32 *writable) +check_attr_support(struct nfsd4_compound_state *cstate, u32 *bmval, + u32 *writable) { struct dentry *dentry = cstate->current_fh.fh_dentry; struct svc_export *exp = cstate->current_fh.fh_export; @@ -91,6 +91,10 @@ check_attr_support(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, return nfserr_attrnotsupp; if ((bmval[0] & FATTR4_WORD0_ACL) && !IS_POSIXACL(d_inode(dentry))) return nfserr_attrnotsupp; + if ((bmval[2] & (FATTR4_WORD2_POSIX_DEFAULT_ACL | + FATTR4_WORD2_POSIX_ACCESS_ACL)) && + !IS_POSIXACL(d_inode(dentry))) + return nfserr_attrnotsupp; if ((bmval[2] & FATTR4_WORD2_SECURITY_LABEL) && !(exp->ex_flags & NFSEXP_SECURITY_LABEL)) return nfserr_attrnotsupp; @@ -103,21 +107,25 @@ check_attr_support(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, } static __be32 -nfsd4_check_open_attributes(struct svc_rqst *rqstp, - struct nfsd4_compound_state *cstate, struct nfsd4_open *open) +nfsd4_check_open_attributes(struct nfsd4_compound_state *cstate, + struct nfsd4_open *open) { __be32 status = nfs_ok; - if (open->op_create == NFS4_OPEN_CREATE) { - if (open->op_createmode == NFS4_CREATE_UNCHECKED - || open->op_createmode == NFS4_CREATE_GUARDED) - status = check_attr_support(rqstp, cstate, - open->op_bmval, nfsd_attrmask); - else if (open->op_createmode == NFS4_CREATE_EXCLUSIVE4_1) - status = check_attr_support(rqstp, cstate, - open->op_bmval, nfsd41_ex_attrmask); - } + if (open->op_create != NFS4_OPEN_CREATE) + return status; + switch (open->op_createmode) { + case NFS4_CREATE_UNCHECKED: + case NFS4_CREATE_GUARDED: + status = check_attr_support(cstate, open->op_bmval, + nfsd_attrmask); + break; + case NFS4_CREATE_EXCLUSIVE4_1: + status = check_attr_support(cstate, open->op_bmval, + nfsd41_ex_attrmask); + break; + } return status; } @@ -194,7 +202,7 @@ static inline bool nfsd4_create_is_exclusive(int createmode) } static __be32 -nfsd4_vfs_create(struct svc_fh *fhp, struct dentry *child, +nfsd4_vfs_create(struct svc_fh *fhp, struct dentry **child, struct nfsd4_open *open) { struct file *filp; @@ -202,6 +210,9 @@ nfsd4_vfs_create(struct svc_fh *fhp, struct dentry *child, int oflags; oflags = O_CREAT | O_LARGEFILE; + if (nfsd4_create_is_exclusive(open->op_createmode)) + oflags |= O_EXCL; + switch (open->op_share_access & NFS4_SHARE_ACCESS_BOTH) { case NFS4_SHARE_ACCESS_WRITE: oflags |= O_WRONLY; @@ -214,9 +225,11 @@ nfsd4_vfs_create(struct svc_fh *fhp, struct dentry *child, } path.mnt = fhp->fh_export->ex_path.mnt; - path.dentry = child; + path.dentry = *child; filp = dentry_create(&path, oflags, open->op_iattr.ia_mode, current_cred()); + *child = path.dentry; + if (IS_ERR(filp)) return nfserrno(PTR_ERR(filp)); @@ -261,8 +274,20 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp, if (host_err) return nfserrno(host_err); - if (is_create_with_attrs(open)) - nfsd4_acl_to_attr(NF4REG, open->op_acl, &attrs); + if (open->op_acl) { + if (open->op_dpacl || open->op_pacl) { + status = nfserr_inval; + goto out_write; + } + if (is_create_with_attrs(open)) + nfsd4_acl_to_attr(NF4REG, open->op_acl, &attrs); + } else if (is_create_with_attrs(open)) { + /* The dpacl and pacl will get released by nfsd_attrs_free(). */ + attrs.na_dpacl = open->op_dpacl; + attrs.na_pacl = open->op_pacl; + open->op_dpacl = NULL; + open->op_pacl = NULL; + } child = start_creating(&nop_mnt_idmap, parent, &QSTR_LEN(open->op_fname, open->op_fnamelen)); @@ -350,7 +375,7 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp, status = fh_fill_pre_attrs(fhp); if (status != nfs_ok) goto out; - status = nfsd4_vfs_create(fhp, child, open); + status = nfsd4_vfs_create(fhp, &child, open); if (status != nfs_ok) goto out; open->op_created = true; @@ -373,8 +398,12 @@ set_attr: if (attrs.na_labelerr) open->op_bmval[2] &= ~FATTR4_WORD2_SECURITY_LABEL; - if (attrs.na_aclerr) + if (attrs.na_paclerr || attrs.na_dpaclerr) open->op_bmval[0] &= ~FATTR4_WORD0_ACL; + if (attrs.na_dpaclerr) + open->op_bmval[2] &= ~FATTR4_WORD2_POSIX_DEFAULT_ACL; + if (attrs.na_paclerr) + open->op_bmval[2] &= ~FATTR4_WORD2_POSIX_ACCESS_ACL; out: end_creating(child); nfsd_attrs_free(&attrs); @@ -542,8 +571,10 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, open->op_rqstp = rqstp; /* This check required by spec. */ - if (open->op_create && open->op_claim_type != NFS4_OPEN_CLAIM_NULL) - return nfserr_inval; + if (open->op_create && open->op_claim_type != NFS4_OPEN_CLAIM_NULL) { + status = nfserr_inval; + goto out_err; + } open->op_created = false; /* @@ -552,8 +583,10 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, */ if (nfsd4_has_session(cstate) && !test_bit(NFSD4_CLIENT_RECLAIM_COMPLETE, &cstate->clp->cl_flags) && - open->op_claim_type != NFS4_OPEN_CLAIM_PREVIOUS) - return nfserr_grace; + open->op_claim_type != NFS4_OPEN_CLAIM_PREVIOUS) { + status = nfserr_grace; + goto out_err; + } if (nfsd4_has_session(cstate)) copy_clientid(&open->op_clientid, cstate->session); @@ -579,7 +612,7 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, goto out; } - status = nfsd4_check_open_attributes(rqstp, cstate, open); + status = nfsd4_check_open_attributes(cstate, open); if (status) goto out; @@ -640,6 +673,9 @@ out: } nfsd4_cleanup_open_state(cstate, open); nfsd4_bump_seqid(cstate, status); +out_err: + posix_acl_release(open->op_dpacl); + posix_acl_release(open->op_pacl); return status; } @@ -780,23 +816,34 @@ nfsd4_create(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfsd_attrs attrs = { .na_iattr = &create->cr_iattr, .na_seclabel = &create->cr_label, + .na_dpacl = create->cr_dpacl, + .na_pacl = create->cr_pacl, }; struct svc_fh resfh; __be32 status; dev_t rdev; + create->cr_dpacl = NULL; + create->cr_pacl = NULL; + fh_init(&resfh, NFS4_FHSIZE); status = fh_verify(rqstp, &cstate->current_fh, S_IFDIR, NFSD_MAY_NOP); if (status) - return status; + goto out_aftermask; - status = check_attr_support(rqstp, cstate, create->cr_bmval, - nfsd_attrmask); + status = check_attr_support(cstate, create->cr_bmval, nfsd_attrmask); if (status) - return status; + goto out_aftermask; - status = nfsd4_acl_to_attr(create->cr_type, create->cr_acl, &attrs); + if (create->cr_acl) { + if (create->cr_dpacl || create->cr_pacl) { + status = nfserr_inval; + goto out_aftermask; + } + status = nfsd4_acl_to_attr(create->cr_type, create->cr_acl, + &attrs); + } current->fs->umask = create->cr_umask; switch (create->cr_type) { case NF4LNK: @@ -855,14 +902,19 @@ nfsd4_create(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, if (attrs.na_labelerr) create->cr_bmval[2] &= ~FATTR4_WORD2_SECURITY_LABEL; - if (attrs.na_aclerr) + if (attrs.na_paclerr || attrs.na_dpaclerr) create->cr_bmval[0] &= ~FATTR4_WORD0_ACL; + if (attrs.na_dpaclerr) + create->cr_bmval[2] &= ~FATTR4_WORD2_POSIX_DEFAULT_ACL; + if (attrs.na_paclerr) + create->cr_bmval[2] &= ~FATTR4_WORD2_POSIX_ACCESS_ACL; set_change_info(&create->cr_cinfo, &cstate->current_fh); fh_dup2(&cstate->current_fh, &resfh); out: fh_put(&resfh); out_umask: current->fs->umask = 0; +out_aftermask: nfsd_attrs_free(&attrs); return status; } @@ -1167,6 +1219,8 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfsd_attrs attrs = { .na_iattr = &setattr->sa_iattr, .na_seclabel = &setattr->sa_label, + .na_pacl = setattr->sa_pacl, + .na_dpacl = setattr->sa_dpacl, }; bool save_no_wcc, deleg_attrs; struct nfs4_stid *st = NULL; @@ -1174,6 +1228,10 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, __be32 status = nfs_ok; int err; + /* Transfer ownership to attrs for cleanup via nfsd_attrs_free() */ + setattr->sa_pacl = NULL; + setattr->sa_dpacl = NULL; + deleg_attrs = setattr->sa_bmval[2] & (FATTR4_WORD2_TIME_DELEG_ACCESS | FATTR4_WORD2_TIME_DELEG_MODIFY); @@ -1187,7 +1245,7 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, &cstate->current_fh, &setattr->sa_stateid, flags, NULL, &st); if (status) - return status; + goto out_err; } if (deleg_attrs) { @@ -1205,18 +1263,24 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, if (st) nfs4_put_stid(st); if (status) - return status; + goto out_err; err = fh_want_write(&cstate->current_fh); - if (err) - return nfserrno(err); + if (err) { + status = nfserrno(err); + goto out_err; + } status = nfs_ok; - status = check_attr_support(rqstp, cstate, setattr->sa_bmval, - nfsd_attrmask); + status = check_attr_support(cstate, setattr->sa_bmval, nfsd_attrmask); if (status) goto out; + if (setattr->sa_acl && (attrs.na_dpacl || attrs.na_pacl)) { + status = nfserr_inval; + goto out; + } + inode = cstate->current_fh.fh_dentry->d_inode; status = nfsd4_acl_to_attr(S_ISDIR(inode->i_mode) ? NF4DIR : NF4REG, setattr->sa_acl, &attrs); @@ -1230,10 +1294,13 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, if (!status) status = nfserrno(attrs.na_labelerr); if (!status) - status = nfserrno(attrs.na_aclerr); + status = nfserrno(attrs.na_dpaclerr); + if (!status) + status = nfserrno(attrs.na_paclerr); out: - nfsd_attrs_free(&attrs); fh_drop_write(&cstate->current_fh); +out_err: + nfsd_attrs_free(&attrs); return status; } @@ -1425,14 +1492,26 @@ static void nfs4_put_copy(struct nfsd4_copy *copy) kfree(copy); } +static void release_copy_files(struct nfsd4_copy *copy); + static void nfsd4_stop_copy(struct nfsd4_copy *copy) { trace_nfsd_copy_async_cancel(copy); if (!test_and_set_bit(NFSD4_COPY_F_STOPPED, ©->cp_flags)) { kthread_stop(copy->copy_task); - copy->nfserr = nfs_ok; + if (!test_bit(NFSD4_COPY_F_CB_ERROR, ©->cp_flags)) + copy->nfserr = nfs_ok; set_bit(NFSD4_COPY_F_COMPLETED, ©->cp_flags); } + + /* + * The copy was removed from async_copies before this function + * was called, so the reaper cannot clean it up. Release files + * here regardless of who won the STOPPED race. If the thread + * set STOPPED, it has finished using the files. If STOPPED + * was set here, kthread_stop() waited for the thread to exit. + */ + release_copy_files(copy); nfs4_put_copy(copy); } @@ -1460,6 +1539,72 @@ void nfsd4_shutdown_copy(struct nfs4_client *clp) while ((copy = nfsd4_unhash_copy(clp)) != NULL) nfsd4_stop_copy(copy); } + +static bool nfsd4_copy_on_sb(const struct nfsd4_copy *copy, + const struct super_block *sb) +{ + if (copy->nf_src && + file_inode(copy->nf_src->nf_file)->i_sb == sb) + return true; + if (copy->nf_dst && + file_inode(copy->nf_dst->nf_file)->i_sb == sb) + return true; + return false; +} + +/** + * nfsd4_cancel_copy_by_sb - cancel async copy operations on @sb + * @net: net namespace containing the copy operations + * @sb: targeted superblock + */ +void nfsd4_cancel_copy_by_sb(struct net *net, struct super_block *sb) +{ + struct nfsd_net *nn = net_generic(net, nfsd_net_id); + struct nfsd4_copy *copy, *tmp; + struct nfs4_client *clp; + unsigned int idhashval; + LIST_HEAD(to_cancel); + + spin_lock(&nn->client_lock); + for (idhashval = 0; idhashval < CLIENT_HASH_SIZE; idhashval++) { + struct list_head *head = &nn->conf_id_hashtbl[idhashval]; + + list_for_each_entry(clp, head, cl_idhash) { + spin_lock(&clp->async_lock); + list_for_each_entry_safe(copy, tmp, + &clp->async_copies, copies) { + if (nfsd4_copy_on_sb(copy, sb)) { + refcount_inc(©->refcount); + /* + * Hold a reference on the client while + * nfsd4_stop_copy() runs. Unlike + * nfsd4_unhash_copy(), cp_clp is not + * NULLed here because nfsd4_send_cb_offload() + * needs a valid client to send CB_OFFLOAD. + * That function takes its own reference to + * survive callback flight. + */ + kref_get(&clp->cl_nfsdfs.cl_ref); + copy->nfserr = nfserr_admin_revoked; + set_bit(NFSD4_COPY_F_CB_ERROR, + ©->cp_flags); + list_move(©->copies, &to_cancel); + } + } + spin_unlock(&clp->async_lock); + } + } + spin_unlock(&nn->client_lock); + + list_for_each_entry_safe(copy, tmp, &to_cancel, copies) { + struct nfs4_client *clp = copy->cp_clp; + + list_del_init(©->copies); + nfsd4_stop_copy(copy); + nfsd4_put_client(clp); + } +} + #ifdef CONFIG_NFSD_V4_2_INTER_SSC extern struct file *nfs42_ssc_open(struct vfsmount *ss_mnt, @@ -1749,6 +1894,7 @@ static void nfsd4_cb_offload_release(struct nfsd4_callback *cb) container_of(cbo, struct nfsd4_copy, cp_cb_offload); set_bit(NFSD4_COPY_F_OFFLOAD_DONE, ©->cp_flags); + nfsd4_put_client(cb->cb_clp); } static int nfsd4_cb_offload_done(struct nfsd4_callback *cb, @@ -1868,10 +2014,14 @@ static void dup_copy_fields(struct nfsd4_copy *src, struct nfsd4_copy *dst) static void release_copy_files(struct nfsd4_copy *copy) { - if (copy->nf_src) + if (copy->nf_src) { nfsd_file_put(copy->nf_src); - if (copy->nf_dst) + copy->nf_src = NULL; + } + if (copy->nf_dst) { nfsd_file_put(copy->nf_dst); + copy->nf_dst = NULL; + } } static void cleanup_async_copy(struct nfsd4_copy *copy) @@ -1890,18 +2040,34 @@ static void cleanup_async_copy(struct nfsd4_copy *copy) static void nfsd4_send_cb_offload(struct nfsd4_copy *copy) { struct nfsd4_cb_offload *cbo = ©->cp_cb_offload; + struct nfs4_client *clp = copy->cp_clp; + + /* + * cp_clp is NULL when called via nfsd4_shutdown_copy() during + * client destruction. Skip the callback; the client is gone. + */ + if (!clp) { + set_bit(NFSD4_COPY_F_OFFLOAD_DONE, ©->cp_flags); + return; + } memcpy(&cbo->co_res, ©->cp_res, sizeof(copy->cp_res)); memcpy(&cbo->co_fh, ©->fh, sizeof(copy->fh)); cbo->co_nfserr = copy->nfserr; cbo->co_retries = 5; - nfsd4_init_cb(&cbo->co_cb, copy->cp_clp, &nfsd4_cb_offload_ops, + /* + * Hold a reference on the client while the callback is in flight. + * Released in nfsd4_cb_offload_release(). + */ + kref_get(&clp->cl_nfsdfs.cl_ref); + + nfsd4_init_cb(&cbo->co_cb, clp, &nfsd4_cb_offload_ops, NFSPROC4_CLNT_CB_OFFLOAD); nfsd41_cb_referring_call(&cbo->co_cb, &cbo->co_referring_sessionid, cbo->co_referring_slotid, cbo->co_referring_seqno); - trace_nfsd_cb_offload(copy->cp_clp, &cbo->co_res.cb_stateid, + trace_nfsd_cb_offload(clp, &cbo->co_res.cb_stateid, &cbo->co_fh, copy->cp_count, copy->nfserr); nfsd4_try_run_cb(&cbo->co_cb); } @@ -1916,6 +2082,7 @@ static void nfsd4_send_cb_offload(struct nfsd4_copy *copy) static int nfsd4_do_async_copy(void *data) { struct nfsd4_copy *copy = (struct nfsd4_copy *)data; + __be32 nfserr = nfs_ok; trace_nfsd_copy_async(copy); if (nfsd4_ssc_is_inter(copy)) { @@ -1926,23 +2093,25 @@ static int nfsd4_do_async_copy(void *data) if (IS_ERR(filp)) { switch (PTR_ERR(filp)) { case -EBADF: - copy->nfserr = nfserr_wrong_type; + nfserr = nfserr_wrong_type; break; default: - copy->nfserr = nfserr_offload_denied; + nfserr = nfserr_offload_denied; } /* ss_mnt will be unmounted by the laundromat */ goto do_callback; } - copy->nfserr = nfsd4_do_copy(copy, filp, copy->nf_dst->nf_file, - false); + nfserr = nfsd4_do_copy(copy, filp, copy->nf_dst->nf_file, + false); nfsd4_cleanup_inter_ssc(copy->ss_nsui, filp, copy->nf_dst); } else { - copy->nfserr = nfsd4_do_copy(copy, copy->nf_src->nf_file, - copy->nf_dst->nf_file, false); + nfserr = nfsd4_do_copy(copy, copy->nf_src->nf_file, + copy->nf_dst->nf_file, false); } do_callback: + if (!test_bit(NFSD4_COPY_F_CB_ERROR, ©->cp_flags)) + copy->nfserr = nfserr; /* The kthread exits forthwith. Ensure that a subsequent * OFFLOAD_CANCEL won't try to kill it again. */ set_bit(NFSD4_COPY_F_STOPPED, ©->cp_flags); @@ -2266,7 +2435,7 @@ _nfsd4_verify(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, if (status) return status; - status = check_attr_support(rqstp, cstate, verify->ve_bmval, NULL); + status = check_attr_support(cstate, verify->ve_bmval, NULL); if (status) return status; @@ -2276,6 +2445,11 @@ _nfsd4_verify(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, if (verify->ve_attrlen & 3) return nfserr_inval; + /* The POSIX draft ACLs cannot be tested via (N)VERIFY. */ + if (verify->ve_bmval[2] & (FATTR4_WORD2_POSIX_DEFAULT_ACL | + FATTR4_WORD2_POSIX_ACCESS_ACL)) + return nfserr_inval; + /* count in words: * bitmap_len(1) + bitmap(2) + attr_len(1) = 4 */ @@ -3011,8 +3185,6 @@ encode_op: BUG_ON(cstate->replay_owner); out: cstate->status = status; - /* Reset deferral mechanism for RPC deferrals */ - set_bit(RQ_USEDEFERRAL, &rqstp->rq_flags); return rpc_success; } diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c index 441dfbfe2d2b..1e6b2dd47ba7 100644 --- a/fs/nfsd/nfs4recover.c +++ b/fs/nfsd/nfs4recover.c @@ -39,6 +39,7 @@ #include <linux/namei.h> #include <linux/sched.h> #include <linux/fs.h> +#include <linux/hex.h> #include <linux/module.h> #include <net/net_namespace.h> #include <linux/sunrpc/rpc_pipe_fs.h> diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index d5e0f3a52d4f..f5cb067a1e50 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -1253,7 +1253,7 @@ static void nfsd4_finalize_deleg_timestamps(struct nfs4_delegation *dp, struct f if (ret) { struct inode *inode = file_inode(f); - pr_notice_ratelimited("Unable to update timestamps on inode %02x:%02x:%lu: %d\n", + pr_notice_ratelimited("nfsd: Unable to update timestamps on inode %02x:%02x:%lu: %d\n", MAJOR(inode->i_sb->s_dev), MINOR(inode->i_sb->s_dev), inode->i_ino, ret); @@ -2413,7 +2413,13 @@ static void __free_client(struct kref *k) kmem_cache_free(client_slab, clp); } -static void drop_client(struct nfs4_client *clp) +/** + * nfsd4_put_client - release a reference on an nfs4_client + * @clp: the client to be released + * + * When the last reference is released, the client is freed. + */ +void nfsd4_put_client(struct nfs4_client *clp) { kref_put(&clp->cl_nfsdfs.cl_ref, __free_client); } @@ -2435,7 +2441,7 @@ free_client(struct nfs4_client *clp) clp->cl_nfsd_dentry = NULL; wake_up_all(&expiry_wq); } - drop_client(clp); + nfsd4_put_client(clp); } /* must be called under the client_lock */ @@ -2833,7 +2839,7 @@ static int client_info_show(struct seq_file *m, void *v) spin_unlock(&clp->cl_lock); seq_puts(m, "\n"); - drop_client(clp); + nfsd4_put_client(clp); return 0; } @@ -3099,7 +3105,7 @@ static int client_states_open(struct inode *inode, struct file *file) ret = seq_open(file, &states_seq_ops); if (ret) { - drop_client(clp); + nfsd4_put_client(clp); return ret; } s = file->private_data; @@ -3113,7 +3119,7 @@ static int client_opens_release(struct inode *inode, struct file *file) struct nfs4_client *clp = m->private; /* XXX: alternatively, we could get/drop in seq start/stop */ - drop_client(clp); + nfsd4_put_client(clp); return seq_release(inode, file); } @@ -3169,7 +3175,7 @@ static ssize_t client_ctl_write(struct file *file, const char __user *buf, if (!clp) return -ENXIO; force_expire_client(clp); - drop_client(clp); + nfsd4_put_client(clp); return 7; } @@ -3204,7 +3210,7 @@ nfsd4_cb_recall_any_release(struct nfsd4_callback *cb) { struct nfs4_client *clp = cb->cb_clp; - drop_client(clp); + nfsd4_put_client(clp); } static int @@ -6353,7 +6359,8 @@ nfs4_open_delegation(struct svc_rqst *rqstp, struct nfsd4_open *open, dp->dl_ctime = stat.ctime; dp->dl_mtime = stat.mtime; spin_lock(&f->f_lock); - f->f_mode |= FMODE_NOCMTIME; + if (deleg_ts) + f->f_mode |= FMODE_NOCMTIME; spin_unlock(&f->f_lock); trace_nfsd_deleg_write(&dp->dl_stid.sc_stateid); } else { @@ -6637,14 +6644,14 @@ bool nfsd4_force_end_grace(struct nfsd_net *nn) { if (!nn->client_tracking_ops) return false; - spin_lock(&nn->client_lock); - if (nn->grace_ended || !nn->client_tracking_active) { - spin_unlock(&nn->client_lock); + if (READ_ONCE(nn->grace_ended)) return false; - } + /* laundromat_work must be initialised now, though it might be disabled */ WRITE_ONCE(nn->grace_end_forced, true); + /* mod_delayed_work() doesn't queue work after + * nfs4_state_shutdown_net() has called disable_delayed_work_sync() + */ mod_delayed_work(laundry_wq, &nn->laundromat_work, 0); - spin_unlock(&nn->client_lock); return true; } @@ -8980,7 +8987,6 @@ static int nfs4_state_create_net(struct net *net) nn->boot_time = ktime_get_real_seconds(); nn->grace_ended = false; nn->grace_end_forced = false; - nn->client_tracking_active = false; nn->nfsd4_manager.block_opens = true; INIT_LIST_HEAD(&nn->nfsd4_manager.list); INIT_LIST_HEAD(&nn->client_lru); @@ -8995,6 +9001,8 @@ static int nfs4_state_create_net(struct net *net) INIT_LIST_HEAD(&nn->blocked_locks_lru); INIT_DELAYED_WORK(&nn->laundromat_work, laundromat_main); + /* Make sure this cannot run until client tracking is initialised */ + disable_delayed_work(&nn->laundromat_work); INIT_WORK(&nn->nfsd_shrinker_work, nfsd4_state_shrinker_worker); get_net(net); @@ -9062,9 +9070,7 @@ nfs4_state_start_net(struct net *net) locks_start_grace(net, &nn->nfsd4_manager); nfsd4_client_tracking_init(net); /* safe for laundromat to run now */ - spin_lock(&nn->client_lock); - nn->client_tracking_active = true; - spin_unlock(&nn->client_lock); + enable_delayed_work(&nn->laundromat_work); if (nn->track_reclaim_completes && nn->reclaim_str_hashtbl_size == 0) goto skip_grace; printk(KERN_INFO "NFSD: starting %lld-second grace period (net %x)\n", @@ -9113,10 +9119,7 @@ nfs4_state_shutdown_net(struct net *net) shrinker_free(nn->nfsd_client_shrinker); cancel_work_sync(&nn->nfsd_shrinker_work); - spin_lock(&nn->client_lock); - nn->client_tracking_active = false; - spin_unlock(&nn->client_lock); - cancel_delayed_work_sync(&nn->laundromat_work); + disable_delayed_work_sync(&nn->laundromat_work); locks_end_grace(&nn->nfsd4_manager); INIT_LIST_HEAD(&reaplist); @@ -9520,8 +9523,10 @@ nfsd_get_dir_deleg(struct nfsd4_compound_state *cstate, spin_unlock(&clp->cl_lock); spin_unlock(&state_lock); - if (!status) + if (!status) { + put_nfs4_file(fp); return dp; + } /* Something failed. Drop the lease and clean up the stid */ kernel_setlease(fp->fi_deleg_file->nf_file, F_UNLCK, NULL, (void **)&dp); @@ -9529,5 +9534,6 @@ out_put_stid: nfs4_put_stid(&dp->dl_stid); out_delegees: put_deleg_file(fp); + put_nfs4_file(fp); return ERR_PTR(status); } diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index 51ef97c25456..5172dbd0cb05 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -43,6 +43,7 @@ #include <linux/sunrpc/addr.h> #include <linux/xattr.h> #include <linux/vmalloc.h> +#include <linux/nfsacl.h> #include <uapi/linux/xattr.h> @@ -377,10 +378,111 @@ nfsd4_decode_security_label(struct nfsd4_compoundargs *argp, return nfs_ok; } +#ifdef CONFIG_NFSD_V4_POSIX_ACLS + +static short nfsd4_posixacetag4_to_tag(posixacetag4 tag) +{ + switch (tag) { + case POSIXACE4_TAG_USER_OBJ: return ACL_USER_OBJ; + case POSIXACE4_TAG_GROUP_OBJ: return ACL_GROUP_OBJ; + case POSIXACE4_TAG_USER: return ACL_USER; + case POSIXACE4_TAG_GROUP: return ACL_GROUP; + case POSIXACE4_TAG_MASK: return ACL_MASK; + case POSIXACE4_TAG_OTHER: return ACL_OTHER; + } + return ACL_OTHER; +} + +static __be32 +nfsd4_decode_posixace4(struct nfsd4_compoundargs *argp, + struct posix_acl_entry *ace) +{ + posixaceperm4 perm; + __be32 *p, status; + posixacetag4 tag; + u32 len; + + if (!xdrgen_decode_posixacetag4(argp->xdr, &tag)) + return nfserr_bad_xdr; + ace->e_tag = nfsd4_posixacetag4_to_tag(tag); + + if (!xdrgen_decode_posixaceperm4(argp->xdr, &perm)) + return nfserr_bad_xdr; + if (perm & ~S_IRWXO) + return nfserr_bad_xdr; + ace->e_perm = perm; + + if (xdr_stream_decode_u32(argp->xdr, &len) < 0) + return nfserr_bad_xdr; + p = xdr_inline_decode(argp->xdr, len); + if (!p) + return nfserr_bad_xdr; + switch (tag) { + case POSIXACE4_TAG_USER: + if (len > 0) + status = nfsd_map_name_to_uid(argp->rqstp, + (char *)p, len, &ace->e_uid); + else + status = nfserr_bad_xdr; + break; + case POSIXACE4_TAG_GROUP: + if (len > 0) + status = nfsd_map_name_to_gid(argp->rqstp, + (char *)p, len, &ace->e_gid); + else + status = nfserr_bad_xdr; + break; + default: + status = nfs_ok; + } + + return status; +} + +static noinline __be32 +nfsd4_decode_posixacl(struct nfsd4_compoundargs *argp, struct posix_acl **acl) +{ + struct posix_acl_entry *ace; + __be32 status; + u32 count; + + if (xdr_stream_decode_u32(argp->xdr, &count) < 0) + return nfserr_bad_xdr; + + *acl = posix_acl_alloc(count, GFP_KERNEL); + if (*acl == NULL) + return nfserr_resource; + + (*acl)->a_count = count; + for (ace = (*acl)->a_entries; ace < (*acl)->a_entries + count; ace++) { + status = nfsd4_decode_posixace4(argp, ace); + if (status) { + posix_acl_release(*acl); + *acl = NULL; + return status; + } + } + + /* + * posix_acl_valid() requires the ACEs to be sorted. + * If they are already sorted, sort_pacl_range() will return + * after one pass through the ACEs, since it implements bubble sort. + * Note that a count == 0 is used to delete a POSIX ACL and a count + * of 1 or 2 will always be found invalid by posix_acl_valid(). + */ + if (count >= 3) + sort_pacl_range(*acl, 0, count - 1); + + return nfs_ok; +} + +#endif /* CONFIG_NFSD_V4_POSIX_ACLS */ + static __be32 nfsd4_decode_fattr4(struct nfsd4_compoundargs *argp, u32 *bmval, u32 bmlen, struct iattr *iattr, struct nfs4_acl **acl, - struct xdr_netobj *label, int *umask) + struct xdr_netobj *label, int *umask, + struct posix_acl **dpaclp, struct posix_acl **paclp) { unsigned int starting_pos; u32 attrlist4_count; @@ -543,9 +645,40 @@ nfsd4_decode_fattr4(struct nfsd4_compoundargs *argp, u32 *bmval, u32 bmlen, ATTR_MTIME | ATTR_MTIME_SET | ATTR_DELEG; } + *dpaclp = NULL; + *paclp = NULL; +#ifdef CONFIG_NFSD_V4_POSIX_ACLS + if (bmval[2] & FATTR4_WORD2_POSIX_DEFAULT_ACL) { + struct posix_acl *dpacl; + + status = nfsd4_decode_posixacl(argp, &dpacl); + if (status) + return status; + *dpaclp = dpacl; + } + if (bmval[2] & FATTR4_WORD2_POSIX_ACCESS_ACL) { + struct posix_acl *pacl; + + status = nfsd4_decode_posixacl(argp, &pacl); + if (status) { + posix_acl_release(*dpaclp); + *dpaclp = NULL; + return status; + } + *paclp = pacl; + } +#endif /* CONFIG_NFSD_V4_POSIX_ACLS */ + /* request sanity: did attrlist4 contain the expected number of words? */ - if (attrlist4_count != xdr_stream_pos(argp->xdr) - starting_pos) + if (attrlist4_count != xdr_stream_pos(argp->xdr) - starting_pos) { +#ifdef CONFIG_NFSD_V4_POSIX_ACLS + posix_acl_release(*dpaclp); + posix_acl_release(*paclp); + *dpaclp = NULL; + *paclp = NULL; +#endif return nfserr_bad_xdr; + } return nfs_ok; } @@ -849,7 +982,8 @@ nfsd4_decode_create(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u) status = nfsd4_decode_fattr4(argp, create->cr_bmval, ARRAY_SIZE(create->cr_bmval), &create->cr_iattr, &create->cr_acl, - &create->cr_label, &create->cr_umask); + &create->cr_label, &create->cr_umask, + &create->cr_dpacl, &create->cr_pacl); if (status) return status; @@ -1000,7 +1134,8 @@ nfsd4_decode_createhow4(struct nfsd4_compoundargs *argp, struct nfsd4_open *open status = nfsd4_decode_fattr4(argp, open->op_bmval, ARRAY_SIZE(open->op_bmval), &open->op_iattr, &open->op_acl, - &open->op_label, &open->op_umask); + &open->op_label, &open->op_umask, + &open->op_dpacl, &open->op_pacl); if (status) return status; break; @@ -1018,7 +1153,8 @@ nfsd4_decode_createhow4(struct nfsd4_compoundargs *argp, struct nfsd4_open *open status = nfsd4_decode_fattr4(argp, open->op_bmval, ARRAY_SIZE(open->op_bmval), &open->op_iattr, &open->op_acl, - &open->op_label, &open->op_umask); + &open->op_label, &open->op_umask, + &open->op_dpacl, &open->op_pacl); if (status) return status; break; @@ -1345,7 +1481,8 @@ nfsd4_decode_setattr(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u) return nfsd4_decode_fattr4(argp, setattr->sa_bmval, ARRAY_SIZE(setattr->sa_bmval), &setattr->sa_iattr, &setattr->sa_acl, - &setattr->sa_label, NULL); + &setattr->sa_label, NULL, &setattr->sa_dpacl, + &setattr->sa_pacl); } static __be32 @@ -2849,6 +2986,89 @@ nfsd4_encode_security_label(struct xdr_stream *xdr, struct svc_rqst *rqstp, { return 0; } #endif +#ifdef CONFIG_NFSD_V4_POSIX_ACLS + +static int nfsd4_posix_tagtotype(short tag) +{ + switch (tag) { + case ACL_USER_OBJ: return POSIXACE4_TAG_USER_OBJ; + case ACL_GROUP_OBJ: return POSIXACE4_TAG_GROUP_OBJ; + case ACL_USER: return POSIXACE4_TAG_USER; + case ACL_GROUP: return POSIXACE4_TAG_GROUP; + case ACL_MASK: return POSIXACE4_TAG_MASK; + case ACL_OTHER: return POSIXACE4_TAG_OTHER; + default: return -EINVAL; + } +} + +static __be32 +nfsd4_encode_posixace4(struct xdr_stream *xdr, struct svc_rqst *rqstp, + struct posix_acl_entry *acep) +{ + __be32 status; + int type; + + type = nfsd4_posix_tagtotype(acep->e_tag); + if (type < 0) + return nfserr_resource; + if (!xdrgen_encode_posixacetag4(xdr, type)) + return nfserr_resource; + if (!xdrgen_encode_posixaceperm4(xdr, acep->e_perm)) + return nfserr_resource; + + /* who */ + switch (acep->e_tag) { + case ACL_USER_OBJ: + case ACL_GROUP_OBJ: + case ACL_MASK: + case ACL_OTHER: + if (xdr_stream_encode_u32(xdr, 0) != XDR_UNIT) + return nfserr_resource; + break; + case ACL_USER: + status = nfsd4_encode_user(xdr, rqstp, acep->e_uid); + if (status != nfs_ok) + return status; + break; + case ACL_GROUP: + status = nfsd4_encode_group(xdr, rqstp, acep->e_gid); + if (status != nfs_ok) + return status; + break; + default: + return nfserr_resource; + } + return nfs_ok; +} + +static __be32 +nfsd4_encode_posixacl(struct xdr_stream *xdr, struct svc_rqst *rqstp, + struct posix_acl *acl) +{ + __be32 status; + int i; + + if (!acl) { + if (xdr_stream_encode_u32(xdr, 0) != XDR_UNIT) + return nfserr_resource; + return nfs_ok; + } + + if (acl->a_count > NFS_ACL_MAX_ENTRIES) + return nfserr_resource; + if (xdr_stream_encode_u32(xdr, acl->a_count) != XDR_UNIT) + return nfserr_resource; + for (i = 0; i < acl->a_count; i++) { + status = nfsd4_encode_posixace4(xdr, rqstp, &acl->a_entries[i]); + if (status != nfs_ok) + return status; + } + + return nfs_ok; +} + +#endif /* CONFIG_NFSD_V4_POSIX_ACL */ + static __be32 fattr_handle_absent_fs(u32 *bmval0, u32 *bmval1, u32 *bmval2, u32 *rdattr_err) { /* As per referral draft: */ @@ -2930,6 +3150,10 @@ struct nfsd4_fattr_args { #ifdef CONFIG_NFSD_V4_SECURITY_LABEL struct lsm_context context; #endif +#ifdef CONFIG_NFSD_V4_POSIX_ACLS + struct posix_acl *dpacl; + struct posix_acl *pacl; +#endif u32 rdattr_err; bool contextsupport; bool ignore_crossmnt; @@ -3470,6 +3694,42 @@ static __be32 nfsd4_encode_fattr4_open_arguments(struct xdr_stream *xdr, return nfs_ok; } +#ifdef CONFIG_NFSD_V4_POSIX_ACLS + +static __be32 nfsd4_encode_fattr4_acl_trueform(struct xdr_stream *xdr, + const struct nfsd4_fattr_args *args) +{ + aclmodel4 trueform = ACL_MODEL_NONE; + + if (IS_POSIXACL(d_inode(args->dentry))) + trueform = ACL_MODEL_POSIX_DRAFT; + if (!xdrgen_encode_aclmodel4(xdr, trueform)) + return nfserr_resource; + return nfs_ok; +} + +static __be32 nfsd4_encode_fattr4_acl_trueform_scope(struct xdr_stream *xdr, + const struct nfsd4_fattr_args *args) +{ + if (!xdrgen_encode_aclscope4(xdr, ACL_SCOPE_FILE_SYSTEM)) + return nfserr_resource; + return nfs_ok; +} + +static __be32 nfsd4_encode_fattr4_posix_default_acl(struct xdr_stream *xdr, + const struct nfsd4_fattr_args *args) +{ + return nfsd4_encode_posixacl(xdr, args->rqstp, args->dpacl); +} + +static __be32 nfsd4_encode_fattr4_posix_access_acl(struct xdr_stream *xdr, + const struct nfsd4_fattr_args *args) +{ + return nfsd4_encode_posixacl(xdr, args->rqstp, args->pacl); +} + +#endif /* CONFIG_NFSD_V4_POSIX_ACLS */ + static const nfsd4_enc_attr nfsd4_enc_fattr4_encode_ops[] = { [FATTR4_SUPPORTED_ATTRS] = nfsd4_encode_fattr4_supported_attrs, [FATTR4_TYPE] = nfsd4_encode_fattr4_type, @@ -3573,6 +3833,22 @@ static const nfsd4_enc_attr nfsd4_enc_fattr4_encode_ops[] = { [FATTR4_TIME_DELEG_ACCESS] = nfsd4_encode_fattr4__inval, [FATTR4_TIME_DELEG_MODIFY] = nfsd4_encode_fattr4__inval, [FATTR4_OPEN_ARGUMENTS] = nfsd4_encode_fattr4_open_arguments, + + /* Reserved */ + [87] = nfsd4_encode_fattr4__inval, + [88] = nfsd4_encode_fattr4__inval, + +#ifdef CONFIG_NFSD_V4_POSIX_ACLS + [FATTR4_ACL_TRUEFORM] = nfsd4_encode_fattr4_acl_trueform, + [FATTR4_ACL_TRUEFORM_SCOPE] = nfsd4_encode_fattr4_acl_trueform_scope, + [FATTR4_POSIX_DEFAULT_ACL] = nfsd4_encode_fattr4_posix_default_acl, + [FATTR4_POSIX_ACCESS_ACL] = nfsd4_encode_fattr4_posix_access_acl, +#else + [FATTR4_ACL_TRUEFORM] = nfsd4_encode_fattr4__noop, + [FATTR4_ACL_TRUEFORM_SCOPE] = nfsd4_encode_fattr4__noop, + [FATTR4_POSIX_DEFAULT_ACL] = nfsd4_encode_fattr4__noop, + [FATTR4_POSIX_ACCESS_ACL] = nfsd4_encode_fattr4__noop, +#endif }; /* @@ -3613,6 +3889,10 @@ nfsd4_encode_fattr4(struct svc_rqst *rqstp, struct xdr_stream *xdr, #ifdef CONFIG_NFSD_V4_SECURITY_LABEL args.context.context = NULL; #endif +#ifdef CONFIG_NFSD_V4_POSIX_ACLS + args.dpacl = NULL; + args.pacl = NULL; +#endif /* * Make a local copy of the attribute bitmap that can be modified. @@ -3719,6 +3999,55 @@ nfsd4_encode_fattr4(struct svc_rqst *rqstp, struct xdr_stream *xdr, } #endif /* CONFIG_NFSD_V4_SECURITY_LABEL */ +#ifdef CONFIG_NFSD_V4_POSIX_ACLS + if (attrmask[2] & FATTR4_WORD2_POSIX_DEFAULT_ACL) { + struct inode *inode = d_inode(dentry); + struct posix_acl *dpacl; + + if (S_ISDIR(inode->i_mode)) { + dpacl = get_inode_acl(inode, ACL_TYPE_DEFAULT); + if (IS_ERR(dpacl)) { + switch (PTR_ERR(dpacl)) { + case -EOPNOTSUPP: + attrmask[2] &= ~FATTR4_WORD2_POSIX_DEFAULT_ACL; + break; + case -EINVAL: + status = nfserr_attrnotsupp; + goto out; + default: + err = PTR_ERR(dpacl); + goto out_nfserr; + } + } else { + args.dpacl = dpacl; + } + } + } + if (attrmask[2] & FATTR4_WORD2_POSIX_ACCESS_ACL) { + struct inode *inode = d_inode(dentry); + struct posix_acl *pacl; + + pacl = get_inode_acl(inode, ACL_TYPE_ACCESS); + if (!pacl) + pacl = posix_acl_from_mode(inode->i_mode, GFP_KERNEL); + if (IS_ERR(pacl)) { + switch (PTR_ERR(pacl)) { + case -EOPNOTSUPP: + attrmask[2] &= ~FATTR4_WORD2_POSIX_ACCESS_ACL; + break; + case -EINVAL: + status = nfserr_attrnotsupp; + goto out; + default: + err = PTR_ERR(pacl); + goto out_nfserr; + } + } else { + args.pacl = pacl; + } + } +#endif /* CONFIG_NFSD_V4_POSIX_ACLS */ + /* attrmask */ status = nfsd4_encode_bitmap4(xdr, attrmask[0], attrmask[1], attrmask[2]); @@ -3742,6 +4071,12 @@ nfsd4_encode_fattr4(struct svc_rqst *rqstp, struct xdr_stream *xdr, status = nfs_ok; out: +#ifdef CONFIG_NFSD_V4_POSIX_ACLS + if (args.dpacl) + posix_acl_release(args.dpacl); + if (args.pacl) + posix_acl_release(args.pacl); +#endif /* CONFIG_NFSD_V4_POSIX_ACLS */ #ifdef CONFIG_NFSD_V4_SECURITY_LABEL if (args.context.context) security_release_secctx(&args.context); @@ -6013,6 +6348,22 @@ nfs4svc_decode_compoundargs(struct svc_rqst *rqstp, struct xdr_stream *xdr) args->ops = args->iops; args->rqstp = rqstp; + /* + * NFSv4 operation decoders can invoke svc cache lookups + * that trigger svc_defer() when RQ_USEDEFERRAL is set, + * setting RQ_DROPME. This creates two problems: + * + * 1. Non-idempotency: Compounds make it too hard to avoid + * problems if a request is deferred and replayed. + * + * 2. Session slot leakage (NFSv4.1+): If RQ_DROPME is set + * during decode but SEQUENCE executes successfully, the + * session slot will be marked INUSE. The request is then + * dropped before encoding, so the slot is never released, + * rendering it permanently unusable by the client. + */ + clear_bit(RQ_USEDEFERRAL, &rqstp->rq_flags); + return nfsd4_decode_compound(args); } diff --git a/fs/nfsd/nfs4xdr_gen.c b/fs/nfsd/nfs4xdr_gen.c index a17b5d8e60b3..824497051b87 100644 --- a/fs/nfsd/nfs4xdr_gen.c +++ b/fs/nfsd/nfs4xdr_gen.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 // Generated by xdrgen. Manual edits will be lost. // XDR specification file: ../../Documentation/sunrpc/xdr/nfs4_1.x -// XDR specification modification time: Mon Oct 14 09:10:13 2024 +// XDR specification modification time: Thu Jan 8 23:12:07 2026 #include <linux/sunrpc/svc.h> @@ -11,13 +11,13 @@ static bool __maybe_unused xdrgen_decode_int64_t(struct xdr_stream *xdr, int64_t *ptr) { return xdrgen_decode_hyper(xdr, ptr); -}; +} static bool __maybe_unused xdrgen_decode_uint32_t(struct xdr_stream *xdr, uint32_t *ptr) { return xdrgen_decode_unsigned_int(xdr, ptr); -}; +} static bool __maybe_unused xdrgen_decode_bitmap4(struct xdr_stream *xdr, bitmap4 *ptr) @@ -28,7 +28,31 @@ xdrgen_decode_bitmap4(struct xdr_stream *xdr, bitmap4 *ptr) if (!xdrgen_decode_uint32_t(xdr, &ptr->element[i])) return false; return true; -}; +} + +static bool __maybe_unused +xdrgen_decode_utf8string(struct xdr_stream *xdr, utf8string *ptr) +{ + return xdrgen_decode_opaque(xdr, ptr, 0); +} + +static bool __maybe_unused +xdrgen_decode_utf8str_cis(struct xdr_stream *xdr, utf8str_cis *ptr) +{ + return xdrgen_decode_utf8string(xdr, ptr); +} + +static bool __maybe_unused +xdrgen_decode_utf8str_cs(struct xdr_stream *xdr, utf8str_cs *ptr) +{ + return xdrgen_decode_utf8string(xdr, ptr); +} + +static bool __maybe_unused +xdrgen_decode_utf8str_mixed(struct xdr_stream *xdr, utf8str_mixed *ptr) +{ + return xdrgen_decode_utf8string(xdr, ptr); +} static bool __maybe_unused xdrgen_decode_nfstime4(struct xdr_stream *xdr, struct nfstime4 *ptr) @@ -38,13 +62,13 @@ xdrgen_decode_nfstime4(struct xdr_stream *xdr, struct nfstime4 *ptr) if (!xdrgen_decode_uint32_t(xdr, &ptr->nseconds)) return false; return true; -}; +} static bool __maybe_unused xdrgen_decode_fattr4_offline(struct xdr_stream *xdr, fattr4_offline *ptr) { return xdrgen_decode_bool(xdr, ptr); -}; +} static bool __maybe_unused xdrgen_decode_open_arguments4(struct xdr_stream *xdr, struct open_arguments4 *ptr) @@ -60,7 +84,7 @@ xdrgen_decode_open_arguments4(struct xdr_stream *xdr, struct open_arguments4 *pt if (!xdrgen_decode_bitmap4(xdr, &ptr->oa_create_mode)) return false; return true; -}; +} static bool __maybe_unused xdrgen_decode_open_args_share_access4(struct xdr_stream *xdr, open_args_share_access4 *ptr) @@ -69,6 +93,15 @@ xdrgen_decode_open_args_share_access4(struct xdr_stream *xdr, open_args_share_ac if (xdr_stream_decode_u32(xdr, &val) < 0) return false; + /* Compiler may optimize to a range check for dense enums */ + switch (val) { + case OPEN_ARGS_SHARE_ACCESS_READ: + case OPEN_ARGS_SHARE_ACCESS_WRITE: + case OPEN_ARGS_SHARE_ACCESS_BOTH: + break; + default: + return false; + } *ptr = val; return true; } @@ -80,6 +113,16 @@ xdrgen_decode_open_args_share_deny4(struct xdr_stream *xdr, open_args_share_deny if (xdr_stream_decode_u32(xdr, &val) < 0) return false; + /* Compiler may optimize to a range check for dense enums */ + switch (val) { + case OPEN_ARGS_SHARE_DENY_NONE: + case OPEN_ARGS_SHARE_DENY_READ: + case OPEN_ARGS_SHARE_DENY_WRITE: + case OPEN_ARGS_SHARE_DENY_BOTH: + break; + default: + return false; + } *ptr = val; return true; } @@ -91,6 +134,19 @@ xdrgen_decode_open_args_share_access_want4(struct xdr_stream *xdr, open_args_sha if (xdr_stream_decode_u32(xdr, &val) < 0) return false; + /* Compiler may optimize to a range check for dense enums */ + switch (val) { + case OPEN_ARGS_SHARE_ACCESS_WANT_ANY_DELEG: + case OPEN_ARGS_SHARE_ACCESS_WANT_NO_DELEG: + case OPEN_ARGS_SHARE_ACCESS_WANT_CANCEL: + case OPEN_ARGS_SHARE_ACCESS_WANT_SIGNAL_DELEG_WHEN_RESRC_AVAIL: + case OPEN_ARGS_SHARE_ACCESS_WANT_PUSH_DELEG_WHEN_UNCONTENDED: + case OPEN_ARGS_SHARE_ACCESS_WANT_DELEG_TIMESTAMPS: + case OPEN_ARGS_SHARE_ACCESS_WANT_OPEN_XOR_DELEGATION: + break; + default: + return false; + } *ptr = val; return true; } @@ -102,6 +158,19 @@ xdrgen_decode_open_args_open_claim4(struct xdr_stream *xdr, open_args_open_claim if (xdr_stream_decode_u32(xdr, &val) < 0) return false; + /* Compiler may optimize to a range check for dense enums */ + switch (val) { + case OPEN_ARGS_OPEN_CLAIM_NULL: + case OPEN_ARGS_OPEN_CLAIM_PREVIOUS: + case OPEN_ARGS_OPEN_CLAIM_DELEGATE_CUR: + case OPEN_ARGS_OPEN_CLAIM_DELEGATE_PREV: + case OPEN_ARGS_OPEN_CLAIM_FH: + case OPEN_ARGS_OPEN_CLAIM_DELEG_CUR_FH: + case OPEN_ARGS_OPEN_CLAIM_DELEG_PREV_FH: + break; + default: + return false; + } *ptr = val; return true; } @@ -113,6 +182,16 @@ xdrgen_decode_open_args_createmode4(struct xdr_stream *xdr, open_args_createmode if (xdr_stream_decode_u32(xdr, &val) < 0) return false; + /* Compiler may optimize to a range check for dense enums */ + switch (val) { + case OPEN_ARGS_CREATEMODE_UNCHECKED4: + case OPEN_ARGS_CREATE_MODE_GUARDED: + case OPEN_ARGS_CREATEMODE_EXCLUSIVE4: + case OPEN_ARGS_CREATE_MODE_EXCLUSIVE4_1: + break; + default: + return false; + } *ptr = val; return true; } @@ -121,19 +200,28 @@ bool xdrgen_decode_fattr4_open_arguments(struct xdr_stream *xdr, fattr4_open_arguments *ptr) { return xdrgen_decode_open_arguments4(xdr, ptr); -}; +} + +/* + * Determine what OPEN supports. + */ bool xdrgen_decode_fattr4_time_deleg_access(struct xdr_stream *xdr, fattr4_time_deleg_access *ptr) { return xdrgen_decode_nfstime4(xdr, ptr); -}; +} bool xdrgen_decode_fattr4_time_deleg_modify(struct xdr_stream *xdr, fattr4_time_deleg_modify *ptr) { return xdrgen_decode_nfstime4(xdr, ptr); -}; +} + +/* + * New RECOMMENDED Attribute for + * delegation caching of times + */ static bool __maybe_unused xdrgen_decode_open_delegation_type4(struct xdr_stream *xdr, open_delegation_type4 *ptr) @@ -142,21 +230,152 @@ xdrgen_decode_open_delegation_type4(struct xdr_stream *xdr, open_delegation_type if (xdr_stream_decode_u32(xdr, &val) < 0) return false; + /* Compiler may optimize to a range check for dense enums */ + switch (val) { + case OPEN_DELEGATE_NONE: + case OPEN_DELEGATE_READ: + case OPEN_DELEGATE_WRITE: + case OPEN_DELEGATE_NONE_EXT: + case OPEN_DELEGATE_READ_ATTRS_DELEG: + case OPEN_DELEGATE_WRITE_ATTRS_DELEG: + break; + default: + return false; + } + *ptr = val; + return true; +} + +bool +xdrgen_decode_aclmodel4(struct xdr_stream *xdr, aclmodel4 *ptr) +{ + u32 val; + + if (xdr_stream_decode_u32(xdr, &val) < 0) + return false; + /* Compiler may optimize to a range check for dense enums */ + switch (val) { + case ACL_MODEL_NFS4: + case ACL_MODEL_POSIX_DRAFT: + case ACL_MODEL_NONE: + break; + default: + return false; + } + *ptr = val; + return true; +} + +bool +xdrgen_decode_aclscope4(struct xdr_stream *xdr, aclscope4 *ptr) +{ + u32 val; + + if (xdr_stream_decode_u32(xdr, &val) < 0) + return false; + /* Compiler may optimize to a range check for dense enums */ + switch (val) { + case ACL_SCOPE_FILE_OBJECT: + case ACL_SCOPE_FILE_SYSTEM: + case ACL_SCOPE_SERVER: + break; + default: + return false; + } *ptr = val; return true; } +bool +xdrgen_decode_posixacetag4(struct xdr_stream *xdr, posixacetag4 *ptr) +{ + u32 val; + + if (xdr_stream_decode_u32(xdr, &val) < 0) + return false; + /* Compiler may optimize to a range check for dense enums */ + switch (val) { + case POSIXACE4_TAG_USER_OBJ: + case POSIXACE4_TAG_USER: + case POSIXACE4_TAG_GROUP_OBJ: + case POSIXACE4_TAG_GROUP: + case POSIXACE4_TAG_MASK: + case POSIXACE4_TAG_OTHER: + break; + default: + return false; + } + *ptr = val; + return true; +} + +bool +xdrgen_decode_posixaceperm4(struct xdr_stream *xdr, posixaceperm4 *ptr) +{ + return xdrgen_decode_uint32_t(xdr, ptr); +} + +static bool __maybe_unused +xdrgen_decode_posixace4(struct xdr_stream *xdr, struct posixace4 *ptr) +{ + if (!xdrgen_decode_posixacetag4(xdr, &ptr->tag)) + return false; + if (!xdrgen_decode_posixaceperm4(xdr, &ptr->perm)) + return false; + if (!xdrgen_decode_utf8str_mixed(xdr, &ptr->who)) + return false; + return true; +} + +static bool __maybe_unused +xdrgen_decode_fattr4_acl_trueform(struct xdr_stream *xdr, fattr4_acl_trueform *ptr) +{ + return xdrgen_decode_aclmodel4(xdr, ptr); +} + +static bool __maybe_unused +xdrgen_decode_fattr4_acl_trueform_scope(struct xdr_stream *xdr, fattr4_acl_trueform_scope *ptr) +{ + return xdrgen_decode_aclscope4(xdr, ptr); +} + +static bool __maybe_unused +xdrgen_decode_fattr4_posix_default_acl(struct xdr_stream *xdr, fattr4_posix_default_acl *ptr) +{ + if (xdr_stream_decode_u32(xdr, &ptr->count) < 0) + return false; + for (u32 i = 0; i < ptr->count; i++) + if (!xdrgen_decode_posixace4(xdr, &ptr->element[i])) + return false; + return true; +} + +static bool __maybe_unused +xdrgen_decode_fattr4_posix_access_acl(struct xdr_stream *xdr, fattr4_posix_access_acl *ptr) +{ + if (xdr_stream_decode_u32(xdr, &ptr->count) < 0) + return false; + for (u32 i = 0; i < ptr->count; i++) + if (!xdrgen_decode_posixace4(xdr, &ptr->element[i])) + return false; + return true; +} + +/* + * New for POSIX ACL extension + */ + static bool __maybe_unused xdrgen_encode_int64_t(struct xdr_stream *xdr, const int64_t value) { return xdrgen_encode_hyper(xdr, value); -}; +} static bool __maybe_unused xdrgen_encode_uint32_t(struct xdr_stream *xdr, const uint32_t value) { return xdrgen_encode_unsigned_int(xdr, value); -}; +} static bool __maybe_unused xdrgen_encode_bitmap4(struct xdr_stream *xdr, const bitmap4 value) @@ -167,7 +386,31 @@ xdrgen_encode_bitmap4(struct xdr_stream *xdr, const bitmap4 value) if (!xdrgen_encode_uint32_t(xdr, value.element[i])) return false; return true; -}; +} + +static bool __maybe_unused +xdrgen_encode_utf8string(struct xdr_stream *xdr, const utf8string value) +{ + return xdr_stream_encode_opaque(xdr, value.data, value.len) >= 0; +} + +static bool __maybe_unused +xdrgen_encode_utf8str_cis(struct xdr_stream *xdr, const utf8str_cis value) +{ + return xdrgen_encode_utf8string(xdr, value); +} + +static bool __maybe_unused +xdrgen_encode_utf8str_cs(struct xdr_stream *xdr, const utf8str_cs value) +{ + return xdrgen_encode_utf8string(xdr, value); +} + +static bool __maybe_unused +xdrgen_encode_utf8str_mixed(struct xdr_stream *xdr, const utf8str_mixed value) +{ + return xdrgen_encode_utf8string(xdr, value); +} static bool __maybe_unused xdrgen_encode_nfstime4(struct xdr_stream *xdr, const struct nfstime4 *value) @@ -177,13 +420,13 @@ xdrgen_encode_nfstime4(struct xdr_stream *xdr, const struct nfstime4 *value) if (!xdrgen_encode_uint32_t(xdr, value->nseconds)) return false; return true; -}; +} static bool __maybe_unused xdrgen_encode_fattr4_offline(struct xdr_stream *xdr, const fattr4_offline value) { return xdrgen_encode_bool(xdr, value); -}; +} static bool __maybe_unused xdrgen_encode_open_arguments4(struct xdr_stream *xdr, const struct open_arguments4 *value) @@ -199,7 +442,7 @@ xdrgen_encode_open_arguments4(struct xdr_stream *xdr, const struct open_argument if (!xdrgen_encode_bitmap4(xdr, value->oa_create_mode)) return false; return true; -}; +} static bool __maybe_unused xdrgen_encode_open_args_share_access4(struct xdr_stream *xdr, open_args_share_access4 value) @@ -235,22 +478,92 @@ bool xdrgen_encode_fattr4_open_arguments(struct xdr_stream *xdr, const fattr4_open_arguments *value) { return xdrgen_encode_open_arguments4(xdr, value); -}; +} bool xdrgen_encode_fattr4_time_deleg_access(struct xdr_stream *xdr, const fattr4_time_deleg_access *value) { return xdrgen_encode_nfstime4(xdr, value); -}; +} bool xdrgen_encode_fattr4_time_deleg_modify(struct xdr_stream *xdr, const fattr4_time_deleg_modify *value) { return xdrgen_encode_nfstime4(xdr, value); -}; +} static bool __maybe_unused xdrgen_encode_open_delegation_type4(struct xdr_stream *xdr, open_delegation_type4 value) { return xdr_stream_encode_u32(xdr, value) == XDR_UNIT; } + +bool +xdrgen_encode_aclmodel4(struct xdr_stream *xdr, aclmodel4 value) +{ + return xdr_stream_encode_u32(xdr, value) == XDR_UNIT; +} + +bool +xdrgen_encode_aclscope4(struct xdr_stream *xdr, aclscope4 value) +{ + return xdr_stream_encode_u32(xdr, value) == XDR_UNIT; +} + +bool +xdrgen_encode_posixacetag4(struct xdr_stream *xdr, posixacetag4 value) +{ + return xdr_stream_encode_u32(xdr, value) == XDR_UNIT; +} + +bool +xdrgen_encode_posixaceperm4(struct xdr_stream *xdr, const posixaceperm4 value) +{ + return xdrgen_encode_uint32_t(xdr, value); +} + +static bool __maybe_unused +xdrgen_encode_posixace4(struct xdr_stream *xdr, const struct posixace4 *value) +{ + if (!xdrgen_encode_posixacetag4(xdr, value->tag)) + return false; + if (!xdrgen_encode_posixaceperm4(xdr, value->perm)) + return false; + if (!xdrgen_encode_utf8str_mixed(xdr, value->who)) + return false; + return true; +} + +static bool __maybe_unused +xdrgen_encode_fattr4_acl_trueform(struct xdr_stream *xdr, const fattr4_acl_trueform value) +{ + return xdrgen_encode_aclmodel4(xdr, value); +} + +static bool __maybe_unused +xdrgen_encode_fattr4_acl_trueform_scope(struct xdr_stream *xdr, const fattr4_acl_trueform_scope value) +{ + return xdrgen_encode_aclscope4(xdr, value); +} + +static bool __maybe_unused +xdrgen_encode_fattr4_posix_default_acl(struct xdr_stream *xdr, const fattr4_posix_default_acl value) +{ + if (xdr_stream_encode_u32(xdr, value.count) != XDR_UNIT) + return false; + for (u32 i = 0; i < value.count; i++) + if (!xdrgen_encode_posixace4(xdr, &value.element[i])) + return false; + return true; +} + +static bool __maybe_unused +xdrgen_encode_fattr4_posix_access_acl(struct xdr_stream *xdr, const fattr4_posix_access_acl value) +{ + if (xdr_stream_encode_u32(xdr, value.count) != XDR_UNIT) + return false; + for (u32 i = 0; i < value.count; i++) + if (!xdrgen_encode_posixace4(xdr, &value.element[i])) + return false; + return true; +} diff --git a/fs/nfsd/nfs4xdr_gen.h b/fs/nfsd/nfs4xdr_gen.h index 41a0033b7256..1c487f1a11ab 100644 --- a/fs/nfsd/nfs4xdr_gen.h +++ b/fs/nfsd/nfs4xdr_gen.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* Generated by xdrgen. Manual edits will be lost. */ /* XDR specification file: ../../Documentation/sunrpc/xdr/nfs4_1.x */ -/* XDR specification modification time: Mon Oct 14 09:10:13 2024 */ +/* XDR specification modification time: Thu Jan 8 23:12:07 2026 */ #ifndef _LINUX_XDRGEN_NFS4_1_DECL_H #define _LINUX_XDRGEN_NFS4_1_DECL_H @@ -21,5 +21,15 @@ bool xdrgen_encode_fattr4_time_deleg_access(struct xdr_stream *xdr, const fattr4 bool xdrgen_decode_fattr4_time_deleg_modify(struct xdr_stream *xdr, fattr4_time_deleg_modify *ptr); bool xdrgen_encode_fattr4_time_deleg_modify(struct xdr_stream *xdr, const fattr4_time_deleg_modify *value); +bool xdrgen_decode_aclmodel4(struct xdr_stream *xdr, aclmodel4 *ptr); +bool xdrgen_encode_aclmodel4(struct xdr_stream *xdr, aclmodel4 value); +bool xdrgen_decode_aclscope4(struct xdr_stream *xdr, aclscope4 *ptr); +bool xdrgen_encode_aclscope4(struct xdr_stream *xdr, aclscope4 value); +bool xdrgen_decode_posixacetag4(struct xdr_stream *xdr, posixacetag4 *ptr); +bool xdrgen_encode_posixacetag4(struct xdr_stream *xdr, posixacetag4 value); + +bool xdrgen_decode_posixaceperm4(struct xdr_stream *xdr, posixaceperm4 *ptr); +bool xdrgen_encode_posixaceperm4(struct xdr_stream *xdr, const posixaceperm4 value); + #endif /* _LINUX_XDRGEN_NFS4_1_DECL_H */ diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index 084fc517e9e1..89fe2c0e8d44 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -285,6 +285,7 @@ static ssize_t write_unlock_fs(struct file *file, char *buf, size_t size) * 2. Is that directory a mount point, or * 3. Is that directory the root of an exported file system? */ + nfsd4_cancel_copy_by_sb(netns(file), path.dentry->d_sb); error = nlmsvc_unlock_all_by_sb(path.dentry->d_sb); mutex_lock(&nfsd_mutex); nn = net_generic(netns(file), nfsd_net_id); @@ -1642,6 +1643,10 @@ int nfsd_nl_threads_set_doit(struct sk_buff *skb, struct genl_info *info) scope = nla_data(attr); } + attr = info->attrs[NFSD_A_SERVER_MIN_THREADS]; + if (attr) + nn->min_threads = nla_get_u32(attr); + ret = nfsd_svc(nrpools, nthreads, net, get_current_cred(), scope); if (ret > 0) ret = 0; @@ -1681,6 +1686,8 @@ int nfsd_nl_threads_get_doit(struct sk_buff *skb, struct genl_info *info) nn->nfsd4_grace) || nla_put_u32(skb, NFSD_A_SERVER_LEASETIME, nn->nfsd4_lease) || + nla_put_u32(skb, NFSD_A_SERVER_MIN_THREADS, + nn->min_threads) || nla_put_string(skb, NFSD_A_SERVER_SCOPE, nn->nfsd_name); if (err) diff --git a/fs/nfsd/nfsd.h b/fs/nfsd/nfsd.h index b0283213a8f5..a01d70953358 100644 --- a/fs/nfsd/nfsd.h +++ b/fs/nfsd/nfsd.h @@ -454,6 +454,16 @@ enum { #define NFSD4_2_SECURITY_ATTRS 0 #endif +#ifdef CONFIG_NFSD_V4_POSIX_ACLS +#define NFSD4_2_POSIX_ACL_ATTRS \ + (FATTR4_WORD2_ACL_TRUEFORM | \ + FATTR4_WORD2_ACL_TRUEFORM_SCOPE | \ + FATTR4_WORD2_POSIX_DEFAULT_ACL | \ + FATTR4_WORD2_POSIX_ACCESS_ACL) +#else +#define NFSD4_2_POSIX_ACL_ATTRS 0 +#endif + #define NFSD4_2_SUPPORTED_ATTRS_WORD2 \ (NFSD4_1_SUPPORTED_ATTRS_WORD2 | \ FATTR4_WORD2_MODE_UMASK | \ @@ -462,7 +472,8 @@ enum { FATTR4_WORD2_XATTR_SUPPORT | \ FATTR4_WORD2_TIME_DELEG_ACCESS | \ FATTR4_WORD2_TIME_DELEG_MODIFY | \ - FATTR4_WORD2_OPEN_ARGUMENTS) + FATTR4_WORD2_OPEN_ARGUMENTS | \ + NFSD4_2_POSIX_ACL_ATTRS) extern const u32 nfsd_suppattrs[3][3]; @@ -530,11 +541,18 @@ static inline bool nfsd_attrs_supported(u32 minorversion, const u32 *bmval) #else #define MAYBE_FATTR4_WORD2_SECURITY_LABEL 0 #endif +#ifdef CONFIG_NFSD_V4_POSIX_ACLS +#define MAYBE_FATTR4_WORD2_POSIX_ACL_ATTRS \ + FATTR4_WORD2_POSIX_DEFAULT_ACL | FATTR4_WORD2_POSIX_ACCESS_ACL +#else +#define MAYBE_FATTR4_WORD2_POSIX_ACL_ATTRS 0 +#endif #define NFSD_WRITEABLE_ATTRS_WORD2 \ (FATTR4_WORD2_MODE_UMASK \ | MAYBE_FATTR4_WORD2_SECURITY_LABEL \ | FATTR4_WORD2_TIME_DELEG_ACCESS \ | FATTR4_WORD2_TIME_DELEG_MODIFY \ + | MAYBE_FATTR4_WORD2_POSIX_ACL_ATTRS \ ) #define NFSD_SUPPATTR_EXCLCREAT_WORD0 \ @@ -550,6 +568,10 @@ static inline bool nfsd_attrs_supported(u32 minorversion, const u32 *bmval) * The FATTR4_WORD2_TIME_DELEG attributes are not to be allowed for * OPEN(create) with EXCLUSIVE4_1. It doesn't make sense to set a * delegated timestamp on a new file. + * + * This mask includes NFSv4.2-only attributes (e.g., POSIX ACLs). + * Version filtering occurs via nfsd_suppattrs[] before this mask + * is applied, so pre-4.2 clients never see unsupported attributes. */ #define NFSD_SUPPATTR_EXCLCREAT_WORD2 \ (NFSD_WRITEABLE_ATTRS_WORD2 & \ diff --git a/fs/nfsd/nfsproc.c b/fs/nfsd/nfsproc.c index 481e789a7697..8873033d1e82 100644 --- a/fs/nfsd/nfsproc.c +++ b/fs/nfsd/nfsproc.c @@ -33,7 +33,7 @@ static __be32 nfsd_map_status(__be32 status) break; case nfserr_symlink: case nfserr_wrong_type: - status = nfserr_inval; + status = nfserr_io; break; } return status; diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c index f1cc223ecee2..0887ee601d3c 100644 --- a/fs/nfsd/nfssvc.c +++ b/fs/nfsd/nfssvc.c @@ -580,7 +580,7 @@ void nfsd_shutdown_threads(struct net *net) } /* Kill outstanding nfsd threads */ - svc_set_num_threads(serv, NULL, 0); + svc_set_num_threads(serv, 0, 0); nfsd_destroy_serv(net); mutex_unlock(&nfsd_mutex); } @@ -688,12 +688,9 @@ int nfsd_set_nrthreads(int n, int *nthreads, struct net *net) if (nn->nfsd_serv == NULL || n <= 0) return 0; - /* - * Special case: When n == 1, pass in NULL for the pool, so that the - * change is distributed equally among them. - */ + /* Special case: When n == 1, distribute threads equally among pools. */ if (n == 1) - return svc_set_num_threads(nn->nfsd_serv, NULL, nthreads[0]); + return svc_set_num_threads(nn->nfsd_serv, nn->min_threads, nthreads[0]); if (n > nn->nfsd_serv->sv_nrpools) n = nn->nfsd_serv->sv_nrpools; @@ -719,18 +716,18 @@ int nfsd_set_nrthreads(int n, int *nthreads, struct net *net) /* apply the new numbers */ for (i = 0; i < n; i++) { - err = svc_set_num_threads(nn->nfsd_serv, - &nn->nfsd_serv->sv_pools[i], - nthreads[i]); + err = svc_set_pool_threads(nn->nfsd_serv, + &nn->nfsd_serv->sv_pools[i], + nn->min_threads, nthreads[i]); if (err) goto out; } /* Anything undefined in array is considered to be 0 */ for (i = n; i < nn->nfsd_serv->sv_nrpools; ++i) { - err = svc_set_num_threads(nn->nfsd_serv, - &nn->nfsd_serv->sv_pools[i], - 0); + err = svc_set_pool_threads(nn->nfsd_serv, + &nn->nfsd_serv->sv_pools[i], + 0, 0); if (err) goto out; } @@ -885,9 +882,11 @@ static int nfsd(void *vrqstp) { struct svc_rqst *rqstp = (struct svc_rqst *) vrqstp; + struct svc_pool *pool = rqstp->rq_pool; struct svc_xprt *perm_sock = list_entry(rqstp->rq_server->sv_permsocks.next, typeof(struct svc_xprt), xpt_list); struct net *net = perm_sock->xpt_net; struct nfsd_net *nn = net_generic(net, nfsd_net_id); + bool have_mutex = false; /* At this point, the thread shares current->fs * with the init process. We need to create files with the @@ -905,7 +904,44 @@ nfsd(void *vrqstp) * The main request loop */ while (!svc_thread_should_stop(rqstp)) { - svc_recv(rqstp); + switch (svc_recv(rqstp, 5 * HZ)) { + case -ETIMEDOUT: + /* No work arrived within the timeout window */ + if (mutex_trylock(&nfsd_mutex)) { + if (pool->sp_nrthreads > pool->sp_nrthrmin) { + trace_nfsd_dynthread_kill(net, pool); + set_bit(RQ_VICTIM, &rqstp->rq_flags); + have_mutex = true; + } else { + mutex_unlock(&nfsd_mutex); + } + } else { + trace_nfsd_dynthread_trylock_fail(net, pool); + } + break; + case -EBUSY: + /* No idle threads; consider spawning another */ + if (pool->sp_nrthreads < pool->sp_nrthrmax) { + if (mutex_trylock(&nfsd_mutex)) { + if (pool->sp_nrthreads < pool->sp_nrthrmax) { + int ret; + + trace_nfsd_dynthread_start(net, pool); + ret = svc_new_thread(rqstp->rq_server, pool); + if (ret) + pr_notice_ratelimited("%s: unable to spawn new thread: %d\n", + __func__, ret); + } + mutex_unlock(&nfsd_mutex); + } else { + trace_nfsd_dynthread_trylock_fail(net, pool); + } + } + clear_bit(SP_TASK_STARTING, &pool->sp_flags); + break; + default: + break; + } nfsd_file_net_dispose(nn); } @@ -913,6 +949,8 @@ nfsd(void *vrqstp) /* Release the thread */ svc_exit_thread(rqstp); + if (have_mutex) + mutex_unlock(&nfsd_mutex); return 0; } diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index 508b7e36d846..6fcbf1e427d4 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h @@ -822,6 +822,7 @@ static inline void nfsd4_try_run_cb(struct nfsd4_callback *cb) extern void nfsd4_shutdown_callback(struct nfs4_client *); extern void nfsd4_shutdown_copy(struct nfs4_client *clp); +void nfsd4_put_client(struct nfs4_client *clp); void nfsd4_async_copy_reaper(struct nfsd_net *nn); bool nfsd4_has_active_async_copies(struct nfs4_client *clp); extern struct nfs4_client_reclaim *nfs4_client_to_reclaim(struct xdr_netobj name, @@ -842,10 +843,14 @@ struct nfsd_file *find_any_file(struct nfs4_file *f); #ifdef CONFIG_NFSD_V4 void nfsd4_revoke_states(struct nfsd_net *nn, struct super_block *sb); +void nfsd4_cancel_copy_by_sb(struct net *net, struct super_block *sb); #else static inline void nfsd4_revoke_states(struct nfsd_net *nn, struct super_block *sb) { } +static inline void nfsd4_cancel_copy_by_sb(struct net *net, struct super_block *sb) +{ +} #endif /* grace period management */ diff --git a/fs/nfsd/trace.h b/fs/nfsd/trace.h index 5ae2a611e57f..d1d0b0dd0545 100644 --- a/fs/nfsd/trace.h +++ b/fs/nfsd/trace.h @@ -91,6 +91,41 @@ DEFINE_EVENT(nfsd_xdr_err_class, nfsd_##name##_err, \ DEFINE_NFSD_XDR_ERR_EVENT(garbage_args); DEFINE_NFSD_XDR_ERR_EVENT(cant_encode); +DECLARE_EVENT_CLASS(nfsd_dynthread_class, + TP_PROTO( + const struct net *net, + const struct svc_pool *pool + ), + TP_ARGS(net, pool), + TP_STRUCT__entry( + __field(unsigned int, netns_ino) + __field(unsigned int, pool_id) + __field(unsigned int, nrthreads) + __field(unsigned int, nrthrmin) + __field(unsigned int, nrthrmax) + ), + TP_fast_assign( + __entry->netns_ino = net->ns.inum; + __entry->pool_id = pool->sp_id; + __entry->nrthreads = pool->sp_nrthreads; + __entry->nrthrmin = pool->sp_nrthrmin; + __entry->nrthrmax = pool->sp_nrthrmax; + ), + TP_printk("pool=%u nrthreads=%u nrthrmin=%u nrthrmax=%u", + __entry->pool_id, __entry->nrthreads, + __entry->nrthrmin, __entry->nrthrmax + ) +); + +#define DEFINE_NFSD_DYNTHREAD_EVENT(name) \ +DEFINE_EVENT(nfsd_dynthread_class, nfsd_dynthread_##name, \ + TP_PROTO(const struct net *net, const struct svc_pool *pool), \ + TP_ARGS(net, pool)) + +DEFINE_NFSD_DYNTHREAD_EVENT(start); +DEFINE_NFSD_DYNTHREAD_EVENT(kill); +DEFINE_NFSD_DYNTHREAD_EVENT(trylock_fail); + #define show_nfsd_may_flags(x) \ __print_flags(x, "|", \ { NFSD_MAY_EXEC, "EXEC" }, \ @@ -2129,6 +2164,25 @@ TRACE_EVENT(nfsd_ctl_maxblksize, ) ); +TRACE_EVENT(nfsd_ctl_minthreads, + TP_PROTO( + const struct net *net, + int minthreads + ), + TP_ARGS(net, minthreads), + TP_STRUCT__entry( + __field(unsigned int, netns_ino) + __field(int, minthreads) + ), + TP_fast_assign( + __entry->netns_ino = net->ns.inum; + __entry->minthreads = minthreads + ), + TP_printk("minthreads=%d", + __entry->minthreads + ) +); + TRACE_EVENT(nfsd_ctl_time, TP_PROTO( const struct net *net, diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 168d3ccc8155..c884c3f34afb 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -596,15 +596,35 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, if (attr->na_seclabel && attr->na_seclabel->len) attr->na_labelerr = security_inode_setsecctx(dentry, attr->na_seclabel->data, attr->na_seclabel->len); - if (IS_ENABLED(CONFIG_FS_POSIX_ACL) && attr->na_pacl) - attr->na_aclerr = set_posix_acl(&nop_mnt_idmap, - dentry, ACL_TYPE_ACCESS, - attr->na_pacl); - if (IS_ENABLED(CONFIG_FS_POSIX_ACL) && - !attr->na_aclerr && attr->na_dpacl && S_ISDIR(inode->i_mode)) - attr->na_aclerr = set_posix_acl(&nop_mnt_idmap, + if (IS_ENABLED(CONFIG_FS_POSIX_ACL) && attr->na_dpacl) { + if (!S_ISDIR(inode->i_mode)) + attr->na_dpaclerr = -EINVAL; + else if (attr->na_dpacl->a_count > 0) + /* a_count == 0 means delete the ACL. */ + attr->na_dpaclerr = set_posix_acl(&nop_mnt_idmap, dentry, ACL_TYPE_DEFAULT, attr->na_dpacl); + else + attr->na_dpaclerr = set_posix_acl(&nop_mnt_idmap, + dentry, ACL_TYPE_DEFAULT, + NULL); + } + if (IS_ENABLED(CONFIG_FS_POSIX_ACL) && attr->na_pacl) { + /* + * For any file system that is not ACL_SCOPE_FILE_OBJECT, + * a_count == 0 MUST reply nfserr_inval. + * For a file system that is ACL_SCOPE_FILE_OBJECT, + * a_count == 0 deletes the ACL. + * XXX File systems that are ACL_SCOPE_FILE_OBJECT + * are not yet supported. + */ + if (attr->na_pacl->a_count > 0) + attr->na_paclerr = set_posix_acl(&nop_mnt_idmap, + dentry, ACL_TYPE_ACCESS, + attr->na_pacl); + else + attr->na_paclerr = -EINVAL; + } out_fill_attrs: /* * RFC 1813 Section 3.3.2 does not mandate that an NFS server diff --git a/fs/nfsd/vfs.h b/fs/nfsd/vfs.h index e192dca4a679..702a844f2106 100644 --- a/fs/nfsd/vfs.h +++ b/fs/nfsd/vfs.h @@ -53,7 +53,8 @@ struct nfsd_attrs { struct posix_acl *na_dpacl; /* input */ int na_labelerr; /* output */ - int na_aclerr; /* output */ + int na_dpaclerr; /* output */ + int na_paclerr; /* output */ }; static inline void nfsd_attrs_free(struct nfsd_attrs *attrs) diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h index ae75846b3cd7..417e9ad9fbb3 100644 --- a/fs/nfsd/xdr4.h +++ b/fs/nfsd/xdr4.h @@ -245,6 +245,8 @@ struct nfsd4_create { int cr_umask; /* request */ struct nfsd4_change_info cr_cinfo; /* response */ struct nfs4_acl *cr_acl; + struct posix_acl *cr_dpacl; + struct posix_acl *cr_pacl; struct xdr_netobj cr_label; }; #define cr_datalen u.link.datalen @@ -397,6 +399,8 @@ struct nfsd4_open { struct nfs4_ol_stateid *op_stp; /* used during processing */ struct nfs4_clnt_odstate *op_odstate; /* used during processing */ struct nfs4_acl *op_acl; + struct posix_acl *op_dpacl; + struct posix_acl *op_pacl; struct xdr_netobj op_label; struct svc_rqst *op_rqstp; }; @@ -483,6 +487,8 @@ struct nfsd4_setattr { struct iattr sa_iattr; /* request */ struct nfs4_acl *sa_acl; struct xdr_netobj sa_label; + struct posix_acl *sa_dpacl; + struct posix_acl *sa_pacl; }; struct nfsd4_setclientid { @@ -732,6 +738,7 @@ struct nfsd4_copy { #define NFSD4_COPY_F_COMMITTED (3) #define NFSD4_COPY_F_COMPLETED (4) #define NFSD4_COPY_F_OFFLOAD_DONE (5) +#define NFSD4_COPY_F_CB_ERROR (6) /* response */ __be32 nfserr; diff --git a/fs/nilfs2/dir.c b/fs/nilfs2/dir.c index 6ca3d74be1e1..b243199036df 100644 --- a/fs/nilfs2/dir.c +++ b/fs/nilfs2/dir.c @@ -30,6 +30,7 @@ */ #include <linux/pagemap.h> +#include <linux/filelock.h> #include "nilfs.h" #include "page.h" @@ -661,5 +662,5 @@ const struct file_operations nilfs_dir_operations = { .compat_ioctl = nilfs_compat_ioctl, #endif /* CONFIG_COMPAT */ .fsync = nilfs_sync_file, - + .setlease = generic_setlease, }; diff --git a/fs/nilfs2/file.c b/fs/nilfs2/file.c index 1b8d754db44d..f93b68c4877c 100644 --- a/fs/nilfs2/file.c +++ b/fs/nilfs2/file.c @@ -8,6 +8,7 @@ */ #include <linux/fs.h> +#include <linux/filelock.h> #include <linux/mm.h> #include <linux/writeback.h> #include "nilfs.h" @@ -150,6 +151,7 @@ const struct file_operations nilfs_file_operations = { .fsync = nilfs_sync_file, .splice_read = filemap_splice_read, .splice_write = iter_file_splice_write, + .setlease = generic_setlease, }; const struct inode_operations nilfs_file_inode_operations = { diff --git a/fs/nilfs2/sufile.c b/fs/nilfs2/sufile.c index 83f93337c01b..eceedca02697 100644 --- a/fs/nilfs2/sufile.c +++ b/fs/nilfs2/sufile.c @@ -1093,6 +1093,9 @@ int nilfs_sufile_trim_fs(struct inode *sufile, struct fstrim_range *range) else end_block = start_block + len - 1; + if (end_block < nilfs->ns_first_data_block) + goto out; + segnum = nilfs_get_segnum_of_block(nilfs, start_block); segnum_end = nilfs_get_segnum_of_block(nilfs, end_block); @@ -1191,6 +1194,7 @@ int nilfs_sufile_trim_fs(struct inode *sufile, struct fstrim_range *range) out_sem: up_read(&NILFS_MDT(sufile)->mi_sem); +out: range->len = ndiscarded << nilfs->ns_blocksize_bits; return ret; } diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index 71bd44e5ab6d..9995de1710e5 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -33,65 +33,6 @@ void __fsnotify_mntns_delete(struct mnt_namespace *mntns) fsnotify_clear_marks_by_mntns(mntns); } -/** - * fsnotify_unmount_inodes - an sb is unmounting. handle any watched inodes. - * @sb: superblock being unmounted. - * - * Called during unmount with no locks held, so needs to be safe against - * concurrent modifiers. We temporarily drop sb->s_inode_list_lock and CAN block. - */ -static void fsnotify_unmount_inodes(struct super_block *sb) -{ - struct inode *inode, *iput_inode = NULL; - - spin_lock(&sb->s_inode_list_lock); - list_for_each_entry(inode, &sb->s_inodes, i_sb_list) { - /* - * We cannot __iget() an inode in state I_FREEING, - * I_WILL_FREE, or I_NEW which is fine because by that point - * the inode cannot have any associated watches. - */ - spin_lock(&inode->i_lock); - if (inode_state_read(inode) & (I_FREEING | I_WILL_FREE | I_NEW)) { - spin_unlock(&inode->i_lock); - continue; - } - - /* - * If i_count is zero, the inode cannot have any watches and - * doing an __iget/iput with SB_ACTIVE clear would actually - * evict all inodes with zero i_count from icache which is - * unnecessarily violent and may in fact be illegal to do. - * However, we should have been called /after/ evict_inodes - * removed all zero refcount inodes, in any case. Test to - * be sure. - */ - if (!icount_read(inode)) { - spin_unlock(&inode->i_lock); - continue; - } - - __iget(inode); - spin_unlock(&inode->i_lock); - spin_unlock(&sb->s_inode_list_lock); - - iput(iput_inode); - - /* for each watch, send FS_UNMOUNT and then remove it */ - fsnotify_inode(inode, FS_UNMOUNT); - - fsnotify_inode_delete(inode); - - iput_inode = inode; - - cond_resched(); - spin_lock(&sb->s_inode_list_lock); - } - spin_unlock(&sb->s_inode_list_lock); - - iput(iput_inode); -} - void fsnotify_sb_delete(struct super_block *sb) { struct fsnotify_sb_info *sbinfo = fsnotify_sb_info(sb); @@ -100,7 +41,7 @@ void fsnotify_sb_delete(struct super_block *sb) if (!sbinfo) return; - fsnotify_unmount_inodes(sb); + fsnotify_unmount_inodes(sbinfo); fsnotify_clear_marks_by_sb(sb); /* Wait for outstanding object references from connectors */ wait_var_event(fsnotify_sb_watched_objects(sb), @@ -112,7 +53,10 @@ void fsnotify_sb_delete(struct super_block *sb) void fsnotify_sb_free(struct super_block *sb) { - kfree(sb->s_fsnotify_info); + if (sb->s_fsnotify_info) { + WARN_ON_ONCE(!list_empty(&sb->s_fsnotify_info->inode_conn_list)); + kfree(sb->s_fsnotify_info); + } } /* @@ -777,8 +721,7 @@ static __init int fsnotify_init(void) if (ret) panic("initializing fsnotify_mark_srcu"); - fsnotify_mark_connector_cachep = KMEM_CACHE(fsnotify_mark_connector, - SLAB_PANIC); + fsnotify_init_connector_caches(); return 0; } diff --git a/fs/notify/fsnotify.h b/fs/notify/fsnotify.h index 5950c7a67f41..58c7bb25e571 100644 --- a/fs/notify/fsnotify.h +++ b/fs/notify/fsnotify.h @@ -77,6 +77,9 @@ extern struct srcu_struct fsnotify_mark_srcu; extern int fsnotify_compare_groups(struct fsnotify_group *a, struct fsnotify_group *b); +/* Destroy all inode marks for given superblock */ +void fsnotify_unmount_inodes(struct fsnotify_sb_info *sbinfo); + /* Destroy all marks attached to an object via connector */ extern void fsnotify_destroy_marks(fsnotify_connp_t *connp); /* run the list of all marks associated with inode and destroy them */ @@ -106,6 +109,6 @@ static inline void fsnotify_clear_marks_by_mntns(struct mnt_namespace *mntns) */ extern void fsnotify_set_children_dentry_flags(struct inode *inode); -extern struct kmem_cache *fsnotify_mark_connector_cachep; +void fsnotify_init_connector_caches(void); #endif /* __FS_NOTIFY_FSNOTIFY_H_ */ diff --git a/fs/notify/mark.c b/fs/notify/mark.c index 55a03bb05aa1..8e6997e9aebb 100644 --- a/fs/notify/mark.c +++ b/fs/notify/mark.c @@ -79,7 +79,8 @@ #define FSNOTIFY_REAPER_DELAY (1) /* 1 jiffy */ struct srcu_struct fsnotify_mark_srcu; -struct kmem_cache *fsnotify_mark_connector_cachep; +static struct kmem_cache *fsnotify_mark_connector_cachep; +static struct kmem_cache *fsnotify_inode_mark_connector_cachep; static DEFINE_SPINLOCK(destroy_lock); static LIST_HEAD(destroy_list); @@ -323,10 +324,12 @@ static void fsnotify_connector_destroy_workfn(struct work_struct *work) while (conn) { free = conn; conn = conn->destroy_next; - kmem_cache_free(fsnotify_mark_connector_cachep, free); + kfree(free); } } +static void fsnotify_untrack_connector(struct fsnotify_mark_connector *conn); + static void *fsnotify_detach_connector_from_object( struct fsnotify_mark_connector *conn, unsigned int *type) @@ -342,6 +345,7 @@ static void *fsnotify_detach_connector_from_object( if (conn->type == FSNOTIFY_OBJ_TYPE_INODE) { inode = fsnotify_conn_inode(conn); inode->i_fsnotify_mask = 0; + fsnotify_untrack_connector(conn); /* Unpin inode when detaching from connector */ if (!(conn->flags & FSNOTIFY_CONN_FLAG_HAS_IREF)) @@ -644,6 +648,8 @@ static int fsnotify_attach_info_to_sb(struct super_block *sb) if (!sbinfo) return -ENOMEM; + INIT_LIST_HEAD(&sbinfo->inode_conn_list); + spin_lock_init(&sbinfo->list_lock); /* * cmpxchg() provides the barrier so that callers of fsnotify_sb_info() * will observe an initialized structure @@ -655,20 +661,123 @@ static int fsnotify_attach_info_to_sb(struct super_block *sb) return 0; } -static int fsnotify_attach_connector_to_object(fsnotify_connp_t *connp, - void *obj, unsigned int obj_type) +struct fsnotify_inode_mark_connector { + struct fsnotify_mark_connector common; + struct list_head conns_list; +}; + +static struct inode *fsnotify_get_living_inode(struct fsnotify_sb_info *sbinfo) { - struct fsnotify_mark_connector *conn; + struct fsnotify_inode_mark_connector *iconn; + struct inode *inode; + + spin_lock(&sbinfo->list_lock); + /* Find the first non-evicting inode */ + list_for_each_entry(iconn, &sbinfo->inode_conn_list, conns_list) { + /* All connectors on the list are still attached to an inode */ + inode = iconn->common.obj; + /* + * For connectors without FSNOTIFY_CONN_FLAG_HAS_IREF + * (evictable marks) corresponding inode may well have 0 + * refcount and can be undergoing eviction. OTOH list_lock + * protects us from the connector getting detached and inode + * freed. So we can poke around the inode safely. + */ + spin_lock(&inode->i_lock); + if (likely( + !(inode_state_read(inode) & (I_FREEING | I_WILL_FREE)))) { + __iget(inode); + spin_unlock(&inode->i_lock); + spin_unlock(&sbinfo->list_lock); + return inode; + } + spin_unlock(&inode->i_lock); + } + spin_unlock(&sbinfo->list_lock); - conn = kmem_cache_alloc(fsnotify_mark_connector_cachep, GFP_KERNEL); - if (!conn) - return -ENOMEM; + return NULL; +} + +/** + * fsnotify_unmount_inodes - an sb is unmounting. Handle any watched inodes. + * @sbinfo: fsnotify info for superblock being unmounted. + * + * Walk all inode connectors for the superblock and free all associated marks. + */ +void fsnotify_unmount_inodes(struct fsnotify_sb_info *sbinfo) +{ + struct inode *inode; + + while ((inode = fsnotify_get_living_inode(sbinfo))) { + fsnotify_inode(inode, FS_UNMOUNT); + fsnotify_clear_marks_by_inode(inode); + iput(inode); + cond_resched(); + } +} + +static void fsnotify_init_connector(struct fsnotify_mark_connector *conn, + void *obj, unsigned int obj_type) +{ spin_lock_init(&conn->lock); INIT_HLIST_HEAD(&conn->list); conn->flags = 0; conn->prio = 0; conn->type = obj_type; conn->obj = obj; +} + +static struct fsnotify_mark_connector * +fsnotify_alloc_inode_connector(struct inode *inode) +{ + struct fsnotify_inode_mark_connector *iconn; + struct fsnotify_sb_info *sbinfo = fsnotify_sb_info(inode->i_sb); + + iconn = kmem_cache_alloc(fsnotify_inode_mark_connector_cachep, + GFP_KERNEL); + if (!iconn) + return NULL; + + fsnotify_init_connector(&iconn->common, inode, FSNOTIFY_OBJ_TYPE_INODE); + spin_lock(&sbinfo->list_lock); + list_add(&iconn->conns_list, &sbinfo->inode_conn_list); + spin_unlock(&sbinfo->list_lock); + + return &iconn->common; +} + +static void fsnotify_untrack_connector(struct fsnotify_mark_connector *conn) +{ + struct fsnotify_inode_mark_connector *iconn; + struct fsnotify_sb_info *sbinfo; + + if (conn->type != FSNOTIFY_OBJ_TYPE_INODE) + return; + + iconn = container_of(conn, struct fsnotify_inode_mark_connector, common); + sbinfo = fsnotify_sb_info(fsnotify_conn_inode(conn)->i_sb); + spin_lock(&sbinfo->list_lock); + list_del(&iconn->conns_list); + spin_unlock(&sbinfo->list_lock); +} + +static int fsnotify_attach_connector_to_object(fsnotify_connp_t *connp, + void *obj, unsigned int obj_type) +{ + struct fsnotify_mark_connector *conn; + + if (obj_type == FSNOTIFY_OBJ_TYPE_INODE) { + struct inode *inode = obj; + + conn = fsnotify_alloc_inode_connector(inode); + } else { + conn = kmem_cache_alloc(fsnotify_mark_connector_cachep, + GFP_KERNEL); + if (conn) + fsnotify_init_connector(conn, obj, obj_type); + } + if (!conn) + return -ENOMEM; /* * cmpxchg() provides the barrier so that readers of *connp can see @@ -676,7 +785,8 @@ static int fsnotify_attach_connector_to_object(fsnotify_connp_t *connp, */ if (cmpxchg(connp, NULL, conn)) { /* Someone else created list structure for us */ - kmem_cache_free(fsnotify_mark_connector_cachep, conn); + fsnotify_untrack_connector(conn); + kfree(conn); } return 0; } @@ -1007,3 +1117,12 @@ void fsnotify_wait_marks_destroyed(void) flush_delayed_work(&reaper_work); } EXPORT_SYMBOL_GPL(fsnotify_wait_marks_destroyed); + +__init void fsnotify_init_connector_caches(void) +{ + fsnotify_mark_connector_cachep = KMEM_CACHE(fsnotify_mark_connector, + SLAB_PANIC); + fsnotify_inode_mark_connector_cachep = KMEM_CACHE( + fsnotify_inode_mark_connector, + SLAB_PANIC); +} diff --git a/fs/nsfs.c b/fs/nsfs.c index bf27d5da91f1..db91de208645 100644 --- a/fs/nsfs.c +++ b/fs/nsfs.c @@ -99,6 +99,19 @@ int ns_get_path(struct path *path, struct task_struct *task, return ns_get_path_cb(path, ns_get_path_task, &args); } +struct file *open_namespace_file(struct ns_common *ns) +{ + struct path path __free(path_put) = {}; + int err; + + /* call first to consume reference */ + err = path_from_stashed(&ns->stashed, nsfs_mnt, ns, &path); + if (err < 0) + return ERR_PTR(err); + + return dentry_open(&path, O_RDONLY, current_cred()); +} + /** * open_namespace - open a namespace * @ns: the namespace to open diff --git a/fs/ntfs3/dir.c b/fs/ntfs3/dir.c index b98e95d6b4d9..596f8c62f033 100644 --- a/fs/ntfs3/dir.c +++ b/fs/ntfs3/dir.c @@ -8,6 +8,7 @@ */ #include <linux/fs.h> +#include <linux/filelock.h> #include <linux/nls.h> #include "debug.h" @@ -423,8 +424,7 @@ static int ntfs_readdir(struct file *file, struct dir_context *ctx) if (!dir_emit_dots(file, ctx)) return 0; - /* Allocate PATH_MAX bytes. */ - name = __getname(); + name = kmalloc(PATH_MAX, GFP_KERNEL); if (!name) return -ENOMEM; @@ -502,7 +502,7 @@ static int ntfs_readdir(struct file *file, struct dir_context *ctx) out: - __putname(name); + kfree(name); put_indx_node(node); if (err == 1) { @@ -630,6 +630,7 @@ const struct file_operations ntfs_dir_operations = { #ifdef CONFIG_COMPAT .compat_ioctl = ntfs_compat_ioctl, #endif + .setlease = generic_setlease, }; #if IS_ENABLED(CONFIG_NTFS_FS) @@ -638,6 +639,7 @@ const struct file_operations ntfs_legacy_dir_operations = { .read = generic_read_dir, .iterate_shared = ntfs_readdir, .open = ntfs_file_open, + .setlease = generic_setlease, }; #endif // clang-format on diff --git a/fs/ntfs3/file.c b/fs/ntfs3/file.c index 2e7b2e566ebe..6cb4479072a6 100644 --- a/fs/ntfs3/file.c +++ b/fs/ntfs3/file.c @@ -14,6 +14,7 @@ #include <linux/falloc.h> #include <linux/fiemap.h> #include <linux/fileattr.h> +#include <linux/filelock.h> #include "debug.h" #include "ntfs.h" @@ -1477,6 +1478,7 @@ const struct file_operations ntfs_file_operations = { .fsync = ntfs_file_fsync, .fallocate = ntfs_fallocate, .release = ntfs_file_release, + .setlease = generic_setlease, }; #if IS_ENABLED(CONFIG_NTFS_FS) @@ -1486,6 +1488,7 @@ const struct file_operations ntfs_legacy_file_operations = { .splice_read = ntfs_file_splice_read, .open = ntfs_file_open, .release = ntfs_file_release, + .setlease = generic_setlease, }; #endif // clang-format on diff --git a/fs/ntfs3/fsntfs.c b/fs/ntfs3/fsntfs.c index 5f138f715835..bd67ba7b5015 100644 --- a/fs/ntfs3/fsntfs.c +++ b/fs/ntfs3/fsntfs.c @@ -2627,7 +2627,7 @@ int ntfs_set_label(struct ntfs_sb_info *sbi, u8 *label, int len) u32 uni_bytes; struct ntfs_inode *ni = sbi->volume.ni; /* Allocate PATH_MAX bytes. */ - struct cpu_str *uni = __getname(); + struct cpu_str *uni = kmalloc(PATH_MAX, GFP_KERNEL); if (!uni) return -ENOMEM; @@ -2671,6 +2671,6 @@ unlock_out: err = _ni_write_inode(&ni->vfs_inode, 0); out: - __putname(uni); + kfree(uni); return err; } diff --git a/fs/ntfs3/inode.c b/fs/ntfs3/inode.c index 0a9ac5efeb67..edfb973e4e82 100644 --- a/fs/ntfs3/inode.c +++ b/fs/ntfs3/inode.c @@ -1281,7 +1281,7 @@ int ntfs_create_inode(struct mnt_idmap *idmap, struct inode *dir, fa |= FILE_ATTRIBUTE_READONLY; /* Allocate PATH_MAX bytes. */ - new_de = kmem_cache_zalloc(names_cachep, GFP_KERNEL); + new_de = kzalloc(PATH_MAX, GFP_KERNEL); if (!new_de) { err = -ENOMEM; goto out1; @@ -1702,7 +1702,7 @@ out3: ntfs_mark_rec_free(sbi, ino, false); out2: - __putname(new_de); + kfree(new_de); kfree(rp); out1: @@ -1723,7 +1723,7 @@ int ntfs_link_inode(struct inode *inode, struct dentry *dentry) struct NTFS_DE *de; /* Allocate PATH_MAX bytes. */ - de = kmem_cache_zalloc(names_cachep, GFP_KERNEL); + de = kzalloc(PATH_MAX, GFP_KERNEL); if (!de) return -ENOMEM; @@ -1737,7 +1737,7 @@ int ntfs_link_inode(struct inode *inode, struct dentry *dentry) err = ni_add_name(ntfs_i(d_inode(dentry->d_parent)), ni, de); out: - __putname(de); + kfree(de); return err; } @@ -1760,8 +1760,7 @@ int ntfs_unlink_inode(struct inode *dir, const struct dentry *dentry) if (ntfs_is_meta_file(sbi, ni->mi.rno)) return -EINVAL; - /* Allocate PATH_MAX bytes. */ - de = kmem_cache_zalloc(names_cachep, GFP_KERNEL); + de = kzalloc(PATH_MAX, GFP_KERNEL); if (!de) return -ENOMEM; @@ -1797,7 +1796,7 @@ int ntfs_unlink_inode(struct inode *dir, const struct dentry *dentry) out: ni_unlock(ni); - __putname(de); + kfree(de); return err; } diff --git a/fs/ntfs3/namei.c b/fs/ntfs3/namei.c index 3b24ca02de61..b2af8f695e60 100644 --- a/fs/ntfs3/namei.c +++ b/fs/ntfs3/namei.c @@ -68,7 +68,7 @@ static struct dentry *ntfs_lookup(struct inode *dir, struct dentry *dentry, u32 flags) { struct ntfs_inode *ni = ntfs_i(dir); - struct cpu_str *uni = __getname(); + struct cpu_str *uni = kmalloc(PATH_MAX, GFP_KERNEL); struct inode *inode; int err; @@ -85,7 +85,7 @@ static struct dentry *ntfs_lookup(struct inode *dir, struct dentry *dentry, inode = dir_search_u(dir, uni, NULL); ni_unlock(ni); } - __putname(uni); + kfree(uni); } /* @@ -303,8 +303,7 @@ static int ntfs_rename(struct mnt_idmap *idmap, struct inode *dir, return err; } - /* Allocate PATH_MAX bytes. */ - de = __getname(); + de = kmalloc(PATH_MAX, GFP_KERNEL); if (!de) return -ENOMEM; @@ -349,7 +348,7 @@ static int ntfs_rename(struct mnt_idmap *idmap, struct inode *dir, ni_unlock(ni); ni_unlock(dir_ni); out: - __putname(de); + kfree(de); return err; } @@ -407,7 +406,7 @@ static int ntfs_d_hash(const struct dentry *dentry, struct qstr *name) /* * Try slow way with current upcase table */ - uni = kmem_cache_alloc(names_cachep, GFP_NOWAIT); + uni = kmalloc(PATH_MAX, GFP_NOWAIT); if (!uni) return -ENOMEM; @@ -429,7 +428,7 @@ static int ntfs_d_hash(const struct dentry *dentry, struct qstr *name) err = 0; out: - kmem_cache_free(names_cachep, uni); + kfree(uni); return err; } @@ -468,7 +467,7 @@ static int ntfs_d_compare(const struct dentry *dentry, unsigned int len1, * Try slow way with current upcase table */ sbi = dentry->d_sb->s_fs_info; - uni1 = __getname(); + uni1 = kmalloc(PATH_MAX, GFP_NOWAIT); if (!uni1) return -ENOMEM; @@ -498,7 +497,7 @@ static int ntfs_d_compare(const struct dentry *dentry, unsigned int len1, ret = !ntfs_cmp_names_cpu(uni1, uni2, sbi->upcase, false) ? 0 : 1; out: - __putname(uni1); + kfree(uni1); return ret; } diff --git a/fs/ntfs3/ntfs_fs.h b/fs/ntfs3/ntfs_fs.h index a4559c9f64e6..f18349689458 100644 --- a/fs/ntfs3/ntfs_fs.h +++ b/fs/ntfs3/ntfs_fs.h @@ -14,6 +14,7 @@ #include <linux/fs.h> #include <linux/highmem.h> #include <linux/kernel.h> +#include <linux/hex.h> #include <linux/mm.h> #include <linux/mutex.h> #include <linux/page-flags.h> diff --git a/fs/ntfs3/xattr.c b/fs/ntfs3/xattr.c index c93df55e98d0..b96e6ac90e9f 100644 --- a/fs/ntfs3/xattr.c +++ b/fs/ntfs3/xattr.c @@ -556,8 +556,7 @@ struct posix_acl *ntfs_get_acl(struct mnt_idmap *idmap, struct dentry *dentry, if (unlikely(is_bad_ni(ni))) return ERR_PTR(-EINVAL); - /* Allocate PATH_MAX bytes. */ - buf = __getname(); + buf = kmalloc(PATH_MAX, GFP_KERNEL); if (!buf) return ERR_PTR(-ENOMEM); @@ -588,7 +587,7 @@ struct posix_acl *ntfs_get_acl(struct mnt_idmap *idmap, struct dentry *dentry, if (!IS_ERR(acl)) set_cached_acl(inode, type, acl); - __putname(buf); + kfree(buf); return acl; } @@ -641,13 +640,9 @@ static noinline int ntfs_set_acl_ex(struct mnt_idmap *idmap, value = NULL; flags = XATTR_REPLACE; } else { - size = posix_acl_xattr_size(acl->a_count); - value = kmalloc(size, GFP_NOFS); + value = posix_acl_to_xattr(&init_user_ns, acl, &size, GFP_NOFS); if (!value) return -ENOMEM; - err = posix_acl_to_xattr(&init_user_ns, acl, value, size); - if (err < 0) - goto out; flags = 0; } diff --git a/fs/nullfs.c b/fs/nullfs.c new file mode 100644 index 000000000000..fdbd3e5d3d71 --- /dev/null +++ b/fs/nullfs.c @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2026 Christian Brauner <brauner@kernel.org> */ +#include <linux/fs/super_types.h> +#include <linux/fs_context.h> +#include <linux/magic.h> + +static const struct super_operations nullfs_super_operations = { + .statfs = simple_statfs, +}; + +static int nullfs_fs_fill_super(struct super_block *s, struct fs_context *fc) +{ + struct inode *inode; + + s->s_maxbytes = MAX_LFS_FILESIZE; + s->s_blocksize = PAGE_SIZE; + s->s_blocksize_bits = PAGE_SHIFT; + s->s_magic = NULL_FS_MAGIC; + s->s_op = &nullfs_super_operations; + s->s_export_op = NULL; + s->s_xattr = NULL; + s->s_time_gran = 1; + s->s_d_flags = 0; + + inode = new_inode(s); + if (!inode) + return -ENOMEM; + + /* nullfs is permanently empty... */ + make_empty_dir_inode(inode); + simple_inode_init_ts(inode); + inode->i_ino = 1; + /* ... and immutable. */ + inode->i_flags |= S_IMMUTABLE; + + s->s_root = d_make_root(inode); + if (!s->s_root) + return -ENOMEM; + + return 0; +} + +/* + * For now this is a single global instance. If needed we can make it + * mountable by userspace at which point we will need to make it + * multi-instance. + */ +static int nullfs_fs_get_tree(struct fs_context *fc) +{ + return get_tree_single(fc, nullfs_fs_fill_super); +} + +static const struct fs_context_operations nullfs_fs_context_ops = { + .get_tree = nullfs_fs_get_tree, +}; + +static int nullfs_init_fs_context(struct fs_context *fc) +{ + fc->ops = &nullfs_fs_context_ops; + fc->global = true; + fc->sb_flags = SB_NOUSER; + fc->s_iflags = SB_I_NOEXEC | SB_I_NODEV; + return 0; +} + +struct file_system_type nullfs_fs_type = { + .name = "nullfs", + .init_fs_context = nullfs_init_fs_context, + .kill_sb = kill_anon_super, +}; diff --git a/fs/ocfs2/alloc.c b/fs/ocfs2/alloc.c index 58bf58b68955..b7db177d17d6 100644 --- a/fs/ocfs2/alloc.c +++ b/fs/ocfs2/alloc.c @@ -1812,14 +1812,15 @@ static int __ocfs2_find_path(struct ocfs2_caching_info *ci, ret = -EROFS; goto out; } - if (le16_to_cpu(el->l_next_free_rec) == 0) { + if (!el->l_next_free_rec || !el->l_count) { ocfs2_error(ocfs2_metadata_cache_get_super(ci), - "Owner %llu has empty extent list at depth %u\n", + "Owner %llu has empty extent list at depth %u\n" + "(next free=%u count=%u)\n", (unsigned long long)ocfs2_metadata_cache_owner(ci), - le16_to_cpu(el->l_tree_depth)); + le16_to_cpu(el->l_tree_depth), + le16_to_cpu(el->l_next_free_rec), le16_to_cpu(el->l_count)); ret = -EROFS; goto out; - } for(i = 0; i < le16_to_cpu(el->l_next_free_rec) - 1; i++) { diff --git a/fs/ocfs2/cluster/heartbeat.c b/fs/ocfs2/cluster/heartbeat.c index 724350925aff..8e9cbc334cf4 100644 --- a/fs/ocfs2/cluster/heartbeat.c +++ b/fs/ocfs2/cluster/heartbeat.c @@ -1942,7 +1942,7 @@ static struct configfs_attribute *o2hb_region_attrs[] = { NULL, }; -static struct configfs_item_operations o2hb_region_item_ops = { +static const struct configfs_item_operations o2hb_region_item_ops = { .release = o2hb_region_release, }; @@ -2193,7 +2193,7 @@ static struct configfs_attribute *o2hb_heartbeat_group_attrs[] = { NULL, }; -static struct configfs_group_operations o2hb_heartbeat_group_group_ops = { +static const struct configfs_group_operations o2hb_heartbeat_group_group_ops = { .make_item = o2hb_heartbeat_group_make_item, .drop_item = o2hb_heartbeat_group_drop_item, }; diff --git a/fs/ocfs2/cluster/nodemanager.c b/fs/ocfs2/cluster/nodemanager.c index 6bc4e064ace4..c5e83c774d73 100644 --- a/fs/ocfs2/cluster/nodemanager.c +++ b/fs/ocfs2/cluster/nodemanager.c @@ -396,7 +396,7 @@ static struct configfs_attribute *o2nm_node_attrs[] = { NULL, }; -static struct configfs_item_operations o2nm_node_item_ops = { +static const struct configfs_item_operations o2nm_node_item_ops = { .release = o2nm_node_release, }; @@ -638,7 +638,7 @@ static void o2nm_node_group_drop_item(struct config_group *group, config_item_put(item); } -static struct configfs_group_operations o2nm_node_group_group_ops = { +static const struct configfs_group_operations o2nm_node_group_group_ops = { .make_item = o2nm_node_group_make_item, .drop_item = o2nm_node_group_drop_item, }; @@ -657,7 +657,7 @@ static void o2nm_cluster_release(struct config_item *item) kfree(cluster); } -static struct configfs_item_operations o2nm_cluster_item_ops = { +static const struct configfs_item_operations o2nm_cluster_item_ops = { .release = o2nm_cluster_release, }; @@ -741,7 +741,7 @@ static void o2nm_cluster_group_drop_item(struct config_group *group, struct conf config_item_put(item); } -static struct configfs_group_operations o2nm_cluster_group_group_ops = { +static const struct configfs_group_operations o2nm_cluster_group_group_ops = { .make_group = o2nm_cluster_group_make_group, .drop_item = o2nm_cluster_group_drop_item, }; diff --git a/fs/ocfs2/dlm/dlmdomain.c b/fs/ocfs2/dlm/dlmdomain.c index 2347a50f079b..cf3ca2f597c2 100644 --- a/fs/ocfs2/dlm/dlmdomain.c +++ b/fs/ocfs2/dlm/dlmdomain.c @@ -1105,7 +1105,7 @@ static int dlm_query_region_handler(struct o2net_msg *msg, u32 len, mlog(0, "Node %u queries hb regions on domain %s\n", qr->qr_node, qr->qr_domain); - /* buffer used in dlm_mast_regions() */ + /* buffer used in dlm_match_regions() */ local = kmalloc(sizeof(qr->qr_regions), GFP_KERNEL); if (!local) return -ENOMEM; diff --git a/fs/ocfs2/export.c b/fs/ocfs2/export.c index b95724b767e1..9c2665dd24e2 100644 --- a/fs/ocfs2/export.c +++ b/fs/ocfs2/export.c @@ -74,8 +74,9 @@ static struct dentry *ocfs2_get_dentry(struct super_block *sb, * nice */ status = -ESTALE; - } else + } else if (status != -ESTALE) { mlog(ML_ERROR, "test inode bit failed %d\n", status); + } goto unlock_nfs_sync; } @@ -162,8 +163,9 @@ static struct dentry *ocfs2_get_parent(struct dentry *child) if (status < 0) { if (status == -EINVAL) { status = -ESTALE; - } else + } else if (status != -ESTALE) { mlog(ML_ERROR, "test inode bit failed %d\n", status); + } parent = ERR_PTR(status); goto bail_unlock; } diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c index 732c61599159..ed961a854983 100644 --- a/fs/ocfs2/file.c +++ b/fs/ocfs2/file.c @@ -19,6 +19,7 @@ #include <linux/mount.h> #include <linux/writeback.h> #include <linux/falloc.h> +#include <linux/filelock.h> #include <linux/quotaops.h> #include <linux/blkdev.h> #include <linux/backing-dev.h> @@ -2823,6 +2824,7 @@ const struct file_operations ocfs2_fops = { .fallocate = ocfs2_fallocate, .remap_file_range = ocfs2_remap_file_range, .fop_flags = FOP_ASYNC_LOCK, + .setlease = generic_setlease, }; WRAP_DIR_ITER(ocfs2_readdir) // FIXME! @@ -2840,6 +2842,7 @@ const struct file_operations ocfs2_dops = { .lock = ocfs2_lock, .flock = ocfs2_flock, .fop_flags = FOP_ASYNC_LOCK, + .setlease = generic_setlease, }; /* @@ -2871,6 +2874,7 @@ const struct file_operations ocfs2_fops_no_plocks = { .splice_write = iter_file_splice_write, .fallocate = ocfs2_fallocate, .remap_file_range = ocfs2_remap_file_range, + .setlease = generic_setlease, }; const struct file_operations ocfs2_dops_no_plocks = { @@ -2885,4 +2889,5 @@ const struct file_operations ocfs2_dops_no_plocks = { .compat_ioctl = ocfs2_compat_ioctl, #endif .flock = ocfs2_flock, + .setlease = generic_setlease, }; diff --git a/fs/ocfs2/inode.c b/fs/ocfs2/inode.c index b5fcc2725a29..03a51662ea8e 100644 --- a/fs/ocfs2/inode.c +++ b/fs/ocfs2/inode.c @@ -1494,12 +1494,25 @@ int ocfs2_validate_inode_block(struct super_block *sb, goto bail; } - if ((le16_to_cpu(di->i_dyn_features) & OCFS2_INLINE_DATA_FL) && - le32_to_cpu(di->i_clusters)) { - rc = ocfs2_error(sb, "Invalid dinode %llu: %u clusters\n", - (unsigned long long)bh->b_blocknr, - le32_to_cpu(di->i_clusters)); - goto bail; + if (le16_to_cpu(di->i_dyn_features) & OCFS2_INLINE_DATA_FL) { + struct ocfs2_inline_data *data = &di->id2.i_data; + + if (le32_to_cpu(di->i_clusters)) { + rc = ocfs2_error(sb, + "Invalid dinode %llu: %u clusters\n", + (unsigned long long)bh->b_blocknr, + le32_to_cpu(di->i_clusters)); + goto bail; + } + + if (le64_to_cpu(di->i_size) > le16_to_cpu(data->id_count)) { + rc = ocfs2_error(sb, + "Invalid dinode #%llu: inline data i_size %llu exceeds id_count %u\n", + (unsigned long long)bh->b_blocknr, + (unsigned long long)le64_to_cpu(di->i_size), + le16_to_cpu(data->id_count)); + goto bail; + } } if (le32_to_cpu(di->i_flags) & OCFS2_CHAIN_FL) { @@ -1529,6 +1542,13 @@ int ocfs2_validate_inode_block(struct super_block *sb, } } + if ((le16_to_cpu(di->i_dyn_features) & OCFS2_HAS_REFCOUNT_FL) && + !di->i_refcount_loc) { + rc = ocfs2_error(sb, "Inode #%llu has refcount flag but no i_refcount_loc\n", + (unsigned long long)bh->b_blocknr); + goto bail; + } + rc = 0; bail: diff --git a/fs/ocfs2/localalloc.c b/fs/ocfs2/localalloc.c index d1aa04a5af1b..56be21c695d6 100644 --- a/fs/ocfs2/localalloc.c +++ b/fs/ocfs2/localalloc.c @@ -905,13 +905,11 @@ bail: static void ocfs2_clear_local_alloc(struct ocfs2_dinode *alloc) { struct ocfs2_local_alloc *la = OCFS2_LOCAL_ALLOC(alloc); - int i; alloc->id1.bitmap1.i_total = 0; alloc->id1.bitmap1.i_used = 0; la->la_bm_off = 0; - for(i = 0; i < le16_to_cpu(la->la_size); i++) - la->la_bitmap[i] = 0; + memset(la->la_bitmap, 0, le16_to_cpu(la->la_size)); } #if 0 diff --git a/fs/ocfs2/move_extents.c b/fs/ocfs2/move_extents.c index 99637e34d9da..e3cdf8788484 100644 --- a/fs/ocfs2/move_extents.c +++ b/fs/ocfs2/move_extents.c @@ -662,6 +662,12 @@ static int ocfs2_move_extent(struct ocfs2_move_extents_context *context, goto out_commit; } + gd = (struct ocfs2_group_desc *)gd_bh->b_data; + if (le16_to_cpu(gd->bg_free_bits_count) < len) { + ret = -ENOSPC; + goto out_commit; + } + /* * probe the victim cluster group to find a proper * region to fit wanted movement, it even will perform @@ -682,7 +688,6 @@ static int ocfs2_move_extent(struct ocfs2_move_extents_context *context, goto out_commit; } - gd = (struct ocfs2_group_desc *)gd_bh->b_data; ret = ocfs2_alloc_dinode_update_counts(gb_inode, handle, gb_bh, len, le16_to_cpu(gd->bg_chain)); if (ret) { diff --git a/fs/ocfs2/ocfs2_fs.h b/fs/ocfs2/ocfs2_fs.h index f7763da5c4a2..c501eb3cdcda 100644 --- a/fs/ocfs2/ocfs2_fs.h +++ b/fs/ocfs2/ocfs2_fs.h @@ -641,7 +641,7 @@ struct ocfs2_local_alloc __le16 la_size; /* Size of included bitmap, in bytes */ __le16 la_reserved1; __le64 la_reserved2; -/*10*/ __u8 la_bitmap[]; +/*10*/ __u8 la_bitmap[] __counted_by_le(la_size); }; /* @@ -654,7 +654,7 @@ struct ocfs2_inline_data * for data, starting at id_data */ __le16 id_reserved0; __le32 id_reserved1; - __u8 id_data[]; /* Start of user data */ + __u8 id_data[] __counted_by_le(id_count); /* Start of user data */ }; /* diff --git a/fs/ocfs2/slot_map.c b/fs/ocfs2/slot_map.c index e544c704b583..ea4a68abc25b 100644 --- a/fs/ocfs2/slot_map.c +++ b/fs/ocfs2/slot_map.c @@ -44,6 +44,9 @@ struct ocfs2_slot_info { static int __ocfs2_node_num_to_slot(struct ocfs2_slot_info *si, unsigned int node_num); +static int ocfs2_validate_slot_map_block(struct super_block *sb, + struct buffer_head *bh); + static void ocfs2_invalidate_slot(struct ocfs2_slot_info *si, int slot_num) { @@ -132,7 +135,8 @@ int ocfs2_refresh_slot_info(struct ocfs2_super *osb) * this is not true, the read of -1 (UINT64_MAX) will fail. */ ret = ocfs2_read_blocks(INODE_CACHE(si->si_inode), -1, si->si_blocks, - si->si_bh, OCFS2_BH_IGNORE_CACHE, NULL); + si->si_bh, OCFS2_BH_IGNORE_CACHE, + ocfs2_validate_slot_map_block); if (ret == 0) { spin_lock(&osb->osb_lock); ocfs2_update_slot_info(si); @@ -332,6 +336,24 @@ int ocfs2_clear_slot(struct ocfs2_super *osb, int slot_num) return ocfs2_update_disk_slot(osb, osb->slot_info, slot_num); } +static int ocfs2_validate_slot_map_block(struct super_block *sb, + struct buffer_head *bh) +{ + int rc; + + BUG_ON(!buffer_uptodate(bh)); + + if (bh->b_blocknr < OCFS2_SUPER_BLOCK_BLKNO) { + rc = ocfs2_error(sb, + "Invalid Slot Map Buffer Head " + "Block Number : %llu, Should be >= %d", + (unsigned long long)bh->b_blocknr, + OCFS2_SUPER_BLOCK_BLKNO); + return rc; + } + return 0; +} + static int ocfs2_map_slot_buffers(struct ocfs2_super *osb, struct ocfs2_slot_info *si) { @@ -383,7 +405,8 @@ static int ocfs2_map_slot_buffers(struct ocfs2_super *osb, bh = NULL; /* Acquire a fresh bh */ status = ocfs2_read_blocks(INODE_CACHE(si->si_inode), blkno, - 1, &bh, OCFS2_BH_IGNORE_CACHE, NULL); + 1, &bh, OCFS2_BH_IGNORE_CACHE, + ocfs2_validate_slot_map_block); if (status < 0) { mlog_errno(status); goto bail; diff --git a/fs/ocfs2/suballoc.c b/fs/ocfs2/suballoc.c index 8e6e5235b30c..79d1325b2111 100644 --- a/fs/ocfs2/suballoc.c +++ b/fs/ocfs2/suballoc.c @@ -295,6 +295,74 @@ static int ocfs2_validate_group_descriptor(struct super_block *sb, return ocfs2_validate_gd_self(sb, bh, 0); } +/* + * The hint group descriptor (gd) may already have been released + * in _ocfs2_free_suballoc_bits(). We first check the gd signature, + * then perform the standard ocfs2_read_group_descriptor() jobs. + * + * If the gd signature is invalid, we return 'rc=0' and set + * '*released=1'. The caller is expected to handle this specific case. + * Otherwise, we return the actual error code. + * + * We treat gd signature corruption case as a release case. The + * caller ocfs2_claim_suballoc_bits() will use ocfs2_search_chain() + * to search each gd block. The code will eventually find this + * corrupted gd block - Late, but not missed. + * + * Note: + * The caller is responsible for initializing the '*released' status. + */ +static int ocfs2_read_hint_group_descriptor(struct inode *inode, + struct ocfs2_dinode *di, u64 gd_blkno, + struct buffer_head **bh, int *released) +{ + int rc; + struct buffer_head *tmp = *bh; + struct ocfs2_group_desc *gd; + + rc = ocfs2_read_block(INODE_CACHE(inode), gd_blkno, &tmp, NULL); + if (rc) + goto out; + + gd = (struct ocfs2_group_desc *) tmp->b_data; + if (!OCFS2_IS_VALID_GROUP_DESC(gd)) { + /* + * Invalid gd cache was set in ocfs2_read_block(), + * which will affect block_group allocation. + * Path: + * ocfs2_reserve_suballoc_bits + * ocfs2_block_group_alloc + * ocfs2_block_group_alloc_contig + * ocfs2_set_new_buffer_uptodate + */ + ocfs2_remove_from_cache(INODE_CACHE(inode), tmp); + *released = 1; /* we return 'rc=0' for this case */ + goto free_bh; + } + + /* below jobs same with ocfs2_read_group_descriptor() */ + if (!buffer_jbd(tmp)) { + rc = ocfs2_validate_group_descriptor(inode->i_sb, tmp); + if (rc) + goto free_bh; + } + + rc = ocfs2_validate_gd_parent(inode->i_sb, di, tmp, 0); + if (rc) + goto free_bh; + + /* If ocfs2_read_block() got us a new bh, pass it up. */ + if (!*bh) + *bh = tmp; + + return rc; + +free_bh: + brelse(tmp); +out: + return rc; +} + int ocfs2_read_group_descriptor(struct inode *inode, struct ocfs2_dinode *di, u64 gd_blkno, struct buffer_head **bh) { @@ -1725,7 +1793,7 @@ static int ocfs2_search_one_group(struct ocfs2_alloc_context *ac, u32 bits_wanted, u32 min_bits, struct ocfs2_suballoc_result *res, - u16 *bits_left) + u16 *bits_left, int *released) { int ret; struct buffer_head *group_bh = NULL; @@ -1733,9 +1801,11 @@ static int ocfs2_search_one_group(struct ocfs2_alloc_context *ac, struct ocfs2_dinode *di = (struct ocfs2_dinode *)ac->ac_bh->b_data; struct inode *alloc_inode = ac->ac_inode; - ret = ocfs2_read_group_descriptor(alloc_inode, di, - res->sr_bg_blkno, &group_bh); - if (ret < 0) { + ret = ocfs2_read_hint_group_descriptor(alloc_inode, di, + res->sr_bg_blkno, &group_bh, released); + if (*released) { + return 0; + } else if (ret < 0) { mlog_errno(ret); return ret; } @@ -1950,6 +2020,7 @@ static int ocfs2_claim_suballoc_bits(struct ocfs2_alloc_context *ac, struct ocfs2_suballoc_result *res) { int status; + int released = 0; u16 victim, i; u16 bits_left = 0; u64 hint = ac->ac_last_group; @@ -1976,6 +2047,7 @@ static int ocfs2_claim_suballoc_bits(struct ocfs2_alloc_context *ac, goto bail; } + /* the hint bg may already be released, we quiet search this group. */ res->sr_bg_blkno = hint; if (res->sr_bg_blkno) { /* Attempt to short-circuit the usual search mechanism @@ -1983,7 +2055,12 @@ static int ocfs2_claim_suballoc_bits(struct ocfs2_alloc_context *ac, * allocation group. This helps us maintain some * contiguousness across allocations. */ status = ocfs2_search_one_group(ac, handle, bits_wanted, - min_bits, res, &bits_left); + min_bits, res, &bits_left, + &released); + if (released) { + res->sr_bg_blkno = 0; + goto chain_search; + } if (!status) goto set_hint; if (status < 0 && status != -ENOSPC) { @@ -1991,7 +2068,7 @@ static int ocfs2_claim_suballoc_bits(struct ocfs2_alloc_context *ac, goto bail; } } - +chain_search: cl = (struct ocfs2_chain_list *) &fe->id2.i_chain; if (!le16_to_cpu(cl->cl_next_free_rec) || le16_to_cpu(cl->cl_next_free_rec) > le16_to_cpu(cl->cl_count)) { @@ -2113,6 +2190,12 @@ bail: return status; } +/* + * after ocfs2 has the ability to release block group unused space, + * the ->ip_last_used_group may be invalid. so this function returns + * ac->ac_last_group need to verify. + * refer the 'hint' in ocfs2_claim_suballoc_bits() for more details. + */ static void ocfs2_init_inode_ac_group(struct inode *dir, struct buffer_head *parent_di_bh, struct ocfs2_alloc_context *ac) @@ -2552,6 +2635,198 @@ bail: } /* + * Reclaim the suballocator managed space to main bitmap. + * This function first works on the suballocator to perform the + * cleanup rec/alloc_inode job, then switches to the main bitmap + * to reclaim released space. + * + * handle: The transaction handle + * alloc_inode: The suballoc inode + * alloc_bh: The buffer_head of suballoc inode + * group_bh: The group descriptor buffer_head of suballocator managed. + * Caller should release the input group_bh. + */ +static int _ocfs2_reclaim_suballoc_to_main(handle_t *handle, + struct inode *alloc_inode, + struct buffer_head *alloc_bh, + struct buffer_head *group_bh) +{ + int idx, status = 0; + int i, next_free_rec, len = 0; + __le16 old_bg_contig_free_bits = 0; + u16 start_bit; + u32 tmp_used; + u64 bg_blkno, start_blk; + unsigned int count; + struct ocfs2_chain_rec *rec; + struct buffer_head *main_bm_bh = NULL; + struct inode *main_bm_inode = NULL; + struct ocfs2_super *osb = OCFS2_SB(alloc_inode->i_sb); + struct ocfs2_dinode *fe = (struct ocfs2_dinode *) alloc_bh->b_data; + struct ocfs2_chain_list *cl = &fe->id2.i_chain; + struct ocfs2_group_desc *group = (struct ocfs2_group_desc *) group_bh->b_data; + + idx = le16_to_cpu(group->bg_chain); + rec = &(cl->cl_recs[idx]); + + status = ocfs2_extend_trans(handle, + ocfs2_calc_group_alloc_credits(osb->sb, + le16_to_cpu(cl->cl_cpg))); + if (status) { + mlog_errno(status); + goto bail; + } + status = ocfs2_journal_access_di(handle, INODE_CACHE(alloc_inode), + alloc_bh, OCFS2_JOURNAL_ACCESS_WRITE); + if (status < 0) { + mlog_errno(status); + goto bail; + } + + /* + * Only clear the suballocator rec item in-place. + * + * If idx is not the last, we don't compress (remove the empty item) + * the cl_recs[]. If not, we need to do lots jobs. + * + * Compress cl_recs[] code example: + * if (idx != cl->cl_next_free_rec - 1) + * memmove(&cl->cl_recs[idx], &cl->cl_recs[idx + 1], + * sizeof(struct ocfs2_chain_rec) * + * (cl->cl_next_free_rec - idx - 1)); + * for(i = idx; i < cl->cl_next_free_rec-1; i++) { + * group->bg_chain = "later group->bg_chain"; + * group->bg_blkno = xxx; + * ... ... + * } + */ + + tmp_used = le32_to_cpu(fe->id1.bitmap1.i_total); + fe->id1.bitmap1.i_total = cpu_to_le32(tmp_used - le32_to_cpu(rec->c_total)); + + /* Substraction 1 for the block group itself */ + tmp_used = le32_to_cpu(fe->id1.bitmap1.i_used); + fe->id1.bitmap1.i_used = cpu_to_le32(tmp_used - 1); + + tmp_used = le32_to_cpu(fe->i_clusters); + fe->i_clusters = cpu_to_le32(tmp_used - le16_to_cpu(cl->cl_cpg)); + + spin_lock(&OCFS2_I(alloc_inode)->ip_lock); + OCFS2_I(alloc_inode)->ip_clusters -= le32_to_cpu(fe->i_clusters); + fe->i_size = cpu_to_le64(ocfs2_clusters_to_bytes(alloc_inode->i_sb, + le32_to_cpu(fe->i_clusters))); + spin_unlock(&OCFS2_I(alloc_inode)->ip_lock); + i_size_write(alloc_inode, le64_to_cpu(fe->i_size)); + alloc_inode->i_blocks = ocfs2_inode_sector_count(alloc_inode); + + ocfs2_journal_dirty(handle, alloc_bh); + ocfs2_update_inode_fsync_trans(handle, alloc_inode, 0); + + start_blk = le64_to_cpu(rec->c_blkno); + count = le32_to_cpu(rec->c_total) / le16_to_cpu(cl->cl_bpc); + + /* + * If the rec is the last one, let's compress the chain list by + * removing the empty cl_recs[] at the end. + */ + next_free_rec = le16_to_cpu(cl->cl_next_free_rec); + if (idx == (next_free_rec - 1)) { + len++; /* the last item should be counted first */ + for (i = (next_free_rec - 2); i > 0; i--) { + if (cl->cl_recs[i].c_free == cl->cl_recs[i].c_total) + len++; + else + break; + } + } + le16_add_cpu(&cl->cl_next_free_rec, -len); + + rec->c_free = 0; + rec->c_total = 0; + rec->c_blkno = 0; + ocfs2_remove_from_cache(INODE_CACHE(alloc_inode), group_bh); + memset(group, 0, sizeof(struct ocfs2_group_desc)); + + /* prepare job for reclaim clusters */ + main_bm_inode = ocfs2_get_system_file_inode(osb, + GLOBAL_BITMAP_SYSTEM_INODE, + OCFS2_INVALID_SLOT); + if (!main_bm_inode) + goto bail; /* ignore the error in reclaim path */ + + inode_lock(main_bm_inode); + + status = ocfs2_inode_lock(main_bm_inode, &main_bm_bh, 1); + if (status < 0) + goto free_bm_inode; /* ignore the error in reclaim path */ + + ocfs2_block_to_cluster_group(main_bm_inode, start_blk, &bg_blkno, + &start_bit); + fe = (struct ocfs2_dinode *) main_bm_bh->b_data; + cl = &fe->id2.i_chain; + /* reuse group_bh, caller will release the input group_bh */ + group_bh = NULL; + + /* reclaim clusters to global_bitmap */ + status = ocfs2_read_group_descriptor(main_bm_inode, fe, bg_blkno, + &group_bh); + if (status < 0) { + mlog_errno(status); + goto free_bm_bh; + } + group = (struct ocfs2_group_desc *) group_bh->b_data; + + if ((count + start_bit) > le16_to_cpu(group->bg_bits)) { + ocfs2_error(alloc_inode->i_sb, + "reclaim length (%d) beyands block group length (%d)", + count + start_bit, le16_to_cpu(group->bg_bits)); + goto free_group_bh; + } + + old_bg_contig_free_bits = group->bg_contig_free_bits; + status = ocfs2_block_group_clear_bits(handle, main_bm_inode, + group, group_bh, + start_bit, count, 0, + _ocfs2_clear_bit); + if (status < 0) { + mlog_errno(status); + goto free_group_bh; + } + + status = ocfs2_journal_access_di(handle, INODE_CACHE(main_bm_inode), + main_bm_bh, OCFS2_JOURNAL_ACCESS_WRITE); + if (status < 0) { + mlog_errno(status); + ocfs2_block_group_set_bits(handle, main_bm_inode, group, group_bh, + start_bit, count, + le16_to_cpu(old_bg_contig_free_bits), 1); + goto free_group_bh; + } + + idx = le16_to_cpu(group->bg_chain); + rec = &(cl->cl_recs[idx]); + + le32_add_cpu(&rec->c_free, count); + tmp_used = le32_to_cpu(fe->id1.bitmap1.i_used); + fe->id1.bitmap1.i_used = cpu_to_le32(tmp_used - count); + ocfs2_journal_dirty(handle, main_bm_bh); + +free_group_bh: + brelse(group_bh); + +free_bm_bh: + ocfs2_inode_unlock(main_bm_inode, 1); + brelse(main_bm_bh); + +free_bm_inode: + inode_unlock(main_bm_inode); + iput(main_bm_inode); + +bail: + return status; +} + +/* * expects the suballoc inode to already be locked. */ static int _ocfs2_free_suballoc_bits(handle_t *handle, @@ -2563,12 +2838,13 @@ static int _ocfs2_free_suballoc_bits(handle_t *handle, void (*undo_fn)(unsigned int bit, unsigned long *bitmap)) { - int status = 0; + int idx, status = 0; u32 tmp_used; struct ocfs2_dinode *fe = (struct ocfs2_dinode *) alloc_bh->b_data; struct ocfs2_chain_list *cl = &fe->id2.i_chain; struct buffer_head *group_bh = NULL; struct ocfs2_group_desc *group; + struct ocfs2_chain_rec *rec; __le16 old_bg_contig_free_bits = 0; /* The alloc_bh comes from ocfs2_free_dinode() or @@ -2614,12 +2890,26 @@ static int _ocfs2_free_suballoc_bits(handle_t *handle, goto bail; } - le32_add_cpu(&cl->cl_recs[le16_to_cpu(group->bg_chain)].c_free, - count); + idx = le16_to_cpu(group->bg_chain); + rec = &(cl->cl_recs[idx]); + + le32_add_cpu(&rec->c_free, count); tmp_used = le32_to_cpu(fe->id1.bitmap1.i_used); fe->id1.bitmap1.i_used = cpu_to_le32(tmp_used - count); ocfs2_journal_dirty(handle, alloc_bh); + /* + * Reclaim suballocator free space. + * Bypass: global_bitmap, non empty rec, first rec in cl_recs[] + */ + if (ocfs2_is_cluster_bitmap(alloc_inode) || + (le32_to_cpu(rec->c_free) != (le32_to_cpu(rec->c_total) - 1)) || + (le16_to_cpu(cl->cl_next_free_rec) == 1)) { + goto bail; + } + + _ocfs2_reclaim_suballoc_to_main(handle, alloc_inode, alloc_bh, group_bh); + bail: brelse(group_bh); return status; @@ -2873,7 +3163,7 @@ static int ocfs2_test_suballoc_bit(struct ocfs2_super *osb, struct ocfs2_group_desc *group; struct buffer_head *group_bh = NULL; u64 bg_blkno; - int status; + int status, quiet = 0, released = 0; trace_ocfs2_test_suballoc_bit((unsigned long long)blkno, (unsigned int)bit); @@ -2889,9 +3179,13 @@ static int ocfs2_test_suballoc_bit(struct ocfs2_super *osb, bg_blkno = group_blkno ? group_blkno : ocfs2_which_suballoc_group(blkno, bit); - status = ocfs2_read_group_descriptor(suballoc, alloc_di, bg_blkno, - &group_bh); - if (status < 0) { + status = ocfs2_read_hint_group_descriptor(suballoc, alloc_di, bg_blkno, + &group_bh, &released); + if (released) { + quiet = 1; + status = -ESTALE; + goto bail; + } else if (status < 0) { mlog(ML_ERROR, "read group %llu failed %d\n", (unsigned long long)bg_blkno, status); goto bail; @@ -2903,7 +3197,7 @@ static int ocfs2_test_suballoc_bit(struct ocfs2_super *osb, bail: brelse(group_bh); - if (status) + if (status && !quiet) mlog_errno(status); return status; } @@ -2923,7 +3217,7 @@ bail: */ int ocfs2_test_inode_bit(struct ocfs2_super *osb, u64 blkno, int *res) { - int status; + int status, quiet = 0; u64 group_blkno = 0; u16 suballoc_bit = 0, suballoc_slot = 0; struct inode *inode_alloc_inode; @@ -2965,8 +3259,12 @@ int ocfs2_test_inode_bit(struct ocfs2_super *osb, u64 blkno, int *res) status = ocfs2_test_suballoc_bit(osb, inode_alloc_inode, alloc_bh, group_blkno, blkno, suballoc_bit, res); - if (status < 0) - mlog(ML_ERROR, "test suballoc bit failed %d\n", status); + if (status < 0) { + if (status == -ESTALE) + quiet = 1; + else + mlog(ML_ERROR, "test suballoc bit failed %d\n", status); + } ocfs2_inode_unlock(inode_alloc_inode, 0); inode_unlock(inode_alloc_inode); @@ -2974,7 +3272,7 @@ int ocfs2_test_inode_bit(struct ocfs2_super *osb, u64 blkno, int *res) iput(inode_alloc_inode); brelse(alloc_bh); bail: - if (status) + if (status && !quiet) mlog_errno(status); return status; } diff --git a/fs/ocfs2/xattr.c b/fs/ocfs2/xattr.c index 1b21fbc16d73..e434a62dd69f 100644 --- a/fs/ocfs2/xattr.c +++ b/fs/ocfs2/xattr.c @@ -1971,8 +1971,7 @@ static void ocfs2_xa_remove_entry(struct ocfs2_xa_loc *loc) ocfs2_xa_wipe_namevalue(loc); loc->xl_entry = NULL; - le16_add_cpu(&xh->xh_count, -1); - count = le16_to_cpu(xh->xh_count); + count = le16_to_cpu(xh->xh_count) - 1; /* * Only zero out the entry if there are more remaining. This is @@ -1987,6 +1986,8 @@ static void ocfs2_xa_remove_entry(struct ocfs2_xa_loc *loc) memset(&xh->xh_entries[count], 0, sizeof(struct ocfs2_xattr_entry)); } + + xh->xh_count = cpu_to_le16(count); } /* @@ -6394,6 +6395,10 @@ static int ocfs2_reflink_xattr_header(handle_t *handle, (void *)last - (void *)xe); memset(last, 0, sizeof(struct ocfs2_xattr_entry)); + last = &new_xh->xh_entries[le16_to_cpu(new_xh->xh_count)] - 1; + } else { + memset(xe, 0, sizeof(struct ocfs2_xattr_entry)); + last = NULL; } /* diff --git a/fs/open.c b/fs/open.c index f328622061c5..91f1139591ab 100644 --- a/fs/open.c +++ b/fs/open.c @@ -135,15 +135,16 @@ int do_sys_truncate(const char __user *pathname, loff_t length) if (length < 0) /* sorry, but loff_t says... */ return -EINVAL; + CLASS(filename, name)(pathname); retry: - error = user_path_at(AT_FDCWD, pathname, lookup_flags, &path); + error = filename_lookup(AT_FDCWD, name, lookup_flags, &path, NULL); if (!error) { error = vfs_truncate(&path, length); path_put(&path); - } - if (retry_estale(error, lookup_flags)) { - lookup_flags |= LOOKUP_REVAL; - goto retry; + if (retry_estale(error, lookup_flags)) { + lookup_flags |= LOOKUP_REVAL; + goto retry; + } } return error; } @@ -477,8 +478,6 @@ static int do_faccessat(int dfd, const char __user *filename, int mode, int flag if (flags & AT_SYMLINK_NOFOLLOW) lookup_flags &= ~LOOKUP_FOLLOW; - if (flags & AT_EMPTY_PATH) - lookup_flags |= LOOKUP_EMPTY; if (access_need_override_creds(flags)) { old_cred = access_override_creds(); @@ -486,8 +485,9 @@ static int do_faccessat(int dfd, const char __user *filename, int mode, int flag return -ENOMEM; } + CLASS(filename_uflags, name)(filename, flags); retry: - res = user_path_at(dfd, filename, lookup_flags, &path); + res = filename_lookup(dfd, name, lookup_flags, &path, NULL); if (res) goto out; @@ -554,24 +554,19 @@ SYSCALL_DEFINE1(chdir, const char __user *, filename) struct path path; int error; unsigned int lookup_flags = LOOKUP_FOLLOW | LOOKUP_DIRECTORY; + CLASS(filename, name)(filename); retry: - error = user_path_at(AT_FDCWD, filename, lookup_flags, &path); - if (error) - goto out; - - error = path_permission(&path, MAY_EXEC | MAY_CHDIR); - if (error) - goto dput_and_out; - - set_fs_pwd(current->fs, &path); - -dput_and_out: - path_put(&path); - if (retry_estale(error, lookup_flags)) { - lookup_flags |= LOOKUP_REVAL; - goto retry; + error = filename_lookup(AT_FDCWD, name, lookup_flags, &path, NULL); + if (!error) { + error = path_permission(&path, MAY_EXEC | MAY_CHDIR); + if (!error) + set_fs_pwd(current->fs, &path); + path_put(&path); + if (retry_estale(error, lookup_flags)) { + lookup_flags |= LOOKUP_REVAL; + goto retry; + } } -out: return error; } @@ -597,10 +592,11 @@ SYSCALL_DEFINE1(chroot, const char __user *, filename) struct path path; int error; unsigned int lookup_flags = LOOKUP_FOLLOW | LOOKUP_DIRECTORY; + CLASS(filename, name)(filename); retry: - error = user_path_at(AT_FDCWD, filename, lookup_flags, &path); + error = filename_lookup(AT_FDCWD, name, lookup_flags, &path, NULL); if (error) - goto out; + return error; error = path_permission(&path, MAY_EXEC | MAY_CHDIR); if (error) @@ -610,18 +606,14 @@ retry: if (!ns_capable(current_user_ns(), CAP_SYS_CHROOT)) goto dput_and_out; error = security_path_chroot(&path); - if (error) - goto dput_and_out; - - set_fs_root(current->fs, &path); - error = 0; + if (!error) + set_fs_root(current->fs, &path); dput_and_out: path_put(&path); if (retry_estale(error, lookup_flags)) { lookup_flags |= LOOKUP_REVAL; goto retry; } -out: return error; } @@ -685,11 +677,9 @@ static int do_fchmodat(int dfd, const char __user *filename, umode_t mode, return -EINVAL; lookup_flags = (flags & AT_SYMLINK_NOFOLLOW) ? 0 : LOOKUP_FOLLOW; - if (flags & AT_EMPTY_PATH) - lookup_flags |= LOOKUP_EMPTY; - + CLASS(filename_uflags, name)(filename, flags); retry: - error = user_path_at(dfd, filename, lookup_flags, &path); + error = filename_lookup(dfd, name, lookup_flags, &path, NULL); if (!error) { error = chmod_common(&path, mode); path_put(&path); @@ -800,31 +790,28 @@ int do_fchownat(int dfd, const char __user *filename, uid_t user, gid_t group, int flag) { struct path path; - int error = -EINVAL; + int error; int lookup_flags; if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH)) != 0) - goto out; + return -EINVAL; lookup_flags = (flag & AT_SYMLINK_NOFOLLOW) ? 0 : LOOKUP_FOLLOW; - if (flag & AT_EMPTY_PATH) - lookup_flags |= LOOKUP_EMPTY; + CLASS(filename_uflags, name)(filename, flag); retry: - error = user_path_at(dfd, filename, lookup_flags, &path); - if (error) - goto out; - error = mnt_want_write(path.mnt); - if (error) - goto out_release; - error = chown_common(&path, user, group); - mnt_drop_write(path.mnt); -out_release: - path_put(&path); - if (retry_estale(error, lookup_flags)) { - lookup_flags |= LOOKUP_REVAL; - goto retry; + error = filename_lookup(dfd, name, lookup_flags, &path, NULL); + if (!error) { + error = mnt_want_write(path.mnt); + if (!error) { + error = chown_common(&path, user, group); + mnt_drop_write(path.mnt); + } + path_put(&path); + if (retry_estale(error, lookup_flags)) { + lookup_flags |= LOOKUP_REVAL; + goto retry; + } } -out: return error; } @@ -1142,45 +1129,6 @@ struct file *dentry_open_nonotify(const struct path *path, int flags, } /** - * dentry_create - Create and open a file - * @path: path to create - * @flags: O_ flags - * @mode: mode bits for new file - * @cred: credentials to use - * - * Caller must hold the parent directory's lock, and have prepared - * a negative dentry, placed in @path->dentry, for the new file. - * - * Caller sets @path->mnt to the vfsmount of the filesystem where - * the new file is to be created. The parent directory and the - * negative dentry must reside on the same filesystem instance. - * - * On success, returns a "struct file *". Otherwise a ERR_PTR - * is returned. - */ -struct file *dentry_create(const struct path *path, int flags, umode_t mode, - const struct cred *cred) -{ - struct file *f; - int error; - - f = alloc_empty_file(flags, cred); - if (IS_ERR(f)) - return f; - - error = vfs_create(mnt_idmap(path->mnt), path->dentry, mode, NULL); - if (!error) - error = vfs_open(path, f); - - if (unlikely(error)) { - fput(f); - return ERR_PTR(error); - } - return f; -} -EXPORT_SYMBOL(dentry_create); - -/** * kernel_file_open - open a file for kernel internal use * @path: path of the file to open * @flags: open flags @@ -1373,7 +1321,7 @@ struct file *file_open_name(struct filename *name, int flags, umode_t mode) int err = build_open_flags(&how, &op); if (err) return ERR_PTR(err); - return do_filp_open(AT_FDCWD, name, &op); + return do_file_open(AT_FDCWD, name, &op); } /** @@ -1389,14 +1337,8 @@ struct file *file_open_name(struct filename *name, int flags, umode_t mode) */ struct file *filp_open(const char *filename, int flags, umode_t mode) { - struct filename *name = getname_kernel(filename); - struct file *file = ERR_CAST(name); - - if (!IS_ERR(name)) { - file = file_open_name(name, flags, mode); - putname(name); - } - return file; + CLASS(filename_kernel, name)(filename); + return file_open_name(name, flags, mode); } EXPORT_SYMBOL(filp_open); @@ -1416,18 +1358,12 @@ static int do_sys_openat2(int dfd, const char __user *filename, struct open_how *how) { struct open_flags op; - struct filename *tmp __free(putname) = NULL; - int err; - - err = build_open_flags(how, &op); + int err = build_open_flags(how, &op); if (unlikely(err)) return err; - tmp = getname(filename); - if (IS_ERR(tmp)) - return PTR_ERR(tmp); - - return FD_ADD(how->flags, do_filp_open(dfd, tmp, &op)); + CLASS(filename, name)(filename); + return FD_ADD(how->flags, do_file_open(dfd, name, &op)); } int do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode) diff --git a/fs/orangefs/acl.c b/fs/orangefs/acl.c index 5aefb705bcc8..a01ef0c1b1bf 100644 --- a/fs/orangefs/acl.c +++ b/fs/orangefs/acl.c @@ -90,14 +90,9 @@ int __orangefs_set_acl(struct inode *inode, struct posix_acl *acl, int type) type); if (acl) { - size = posix_acl_xattr_size(acl->a_count); - value = kmalloc(size, GFP_KERNEL); + value = posix_acl_to_xattr(&init_user_ns, acl, &size, GFP_KERNEL); if (!value) return -ENOMEM; - - error = posix_acl_to_xattr(&init_user_ns, acl, value, size); - if (error < 0) - goto out; } gossip_debug(GOSSIP_ACL_DEBUG, @@ -111,7 +106,6 @@ int __orangefs_set_acl(struct inode *inode, struct posix_acl *acl, int type) */ error = orangefs_inode_setxattr(inode, name, value, size, 0); -out: kfree(value); if (!error) set_cached_acl(inode, type, acl); diff --git a/fs/orangefs/dir.c b/fs/orangefs/dir.c index 6d1fbeca9d81..3c32bf9f1296 100644 --- a/fs/orangefs/dir.c +++ b/fs/orangefs/dir.c @@ -3,6 +3,7 @@ * Copyright 2017 Omnibond Systems, L.L.C. */ +#include <linux/filelock.h> #include "protocol.h" #include "orangefs-kernel.h" #include "orangefs-bufmap.h" @@ -392,5 +393,6 @@ const struct file_operations orangefs_dir_operations = { .read = generic_read_dir, .iterate_shared = orangefs_dir_iterate, .open = orangefs_dir_open, - .release = orangefs_dir_release + .release = orangefs_dir_release, + .setlease = generic_setlease, }; diff --git a/fs/orangefs/file.c b/fs/orangefs/file.c index 919f99b16834..afd610a3fc68 100644 --- a/fs/orangefs/file.c +++ b/fs/orangefs/file.c @@ -583,4 +583,5 @@ const struct file_operations orangefs_file_operations = { .flush = orangefs_flush, .release = orangefs_file_release, .fsync = orangefs_fsync, + .setlease = generic_setlease, }; diff --git a/fs/orangefs/inode.c b/fs/orangefs/inode.c index d7275990ffa4..f420f48fc069 100644 --- a/fs/orangefs/inode.c +++ b/fs/orangefs/inode.c @@ -872,22 +872,27 @@ int orangefs_permission(struct mnt_idmap *idmap, return generic_permission(&nop_mnt_idmap, inode, mask); } -int orangefs_update_time(struct inode *inode, int flags) +int orangefs_update_time(struct inode *inode, enum fs_update_time type, + unsigned int flags) { - struct iattr iattr; + struct iattr iattr = { }; + int dirty; - gossip_debug(GOSSIP_INODE_DEBUG, "orangefs_update_time: %pU\n", - get_khandle_from_ino(inode)); + if (flags & IOCB_NOWAIT) + return -EAGAIN; - flags = inode_update_timestamps(inode, flags); + switch (type) { + case FS_UPD_ATIME: + iattr.ia_valid = ATTR_ATIME; + break; + case FS_UPD_CMTIME: + iattr.ia_valid = ATTR_CTIME | ATTR_MTIME; + break; + } - memset(&iattr, 0, sizeof iattr); - if (flags & S_ATIME) - iattr.ia_valid |= ATTR_ATIME; - if (flags & S_CTIME) - iattr.ia_valid |= ATTR_CTIME; - if (flags & S_MTIME) - iattr.ia_valid |= ATTR_MTIME; + dirty = inode_update_time(inode, type, flags); + if (dirty <= 0) + return dirty; return __orangefs_setattr(inode, &iattr); } diff --git a/fs/orangefs/orangefs-debugfs.c b/fs/orangefs/orangefs-debugfs.c index 79267b3419f2..002092950605 100644 --- a/fs/orangefs/orangefs-debugfs.c +++ b/fs/orangefs/orangefs-debugfs.c @@ -238,14 +238,16 @@ void orangefs_debugfs_init(int debug_mask) static void orangefs_kernel_debug_init(void) { static char k_buffer[ORANGEFS_MAX_DEBUG_STRING_LEN] = { }; + size_t len = strlen(kernel_debug_string); gossip_debug(GOSSIP_DEBUGFS_DEBUG, "%s: start\n", __func__); - if (strlen(kernel_debug_string) + 1 < ORANGEFS_MAX_DEBUG_STRING_LEN) { - strcpy(k_buffer, kernel_debug_string); - strcat(k_buffer, "\n"); + if (len + 1 < ORANGEFS_MAX_DEBUG_STRING_LEN) { + memcpy(k_buffer, kernel_debug_string, len); + k_buffer[len] = '\n'; + k_buffer[len + 1] = '\0'; } else { - strcpy(k_buffer, "none\n"); + strscpy(k_buffer, "none\n"); pr_info("%s: overflow 1!\n", __func__); } @@ -336,16 +338,17 @@ static int help_show(struct seq_file *m, void *v) */ static void orangefs_client_debug_init(void) { - static char c_buffer[ORANGEFS_MAX_DEBUG_STRING_LEN] = { }; + size_t len = strlen(client_debug_string); gossip_debug(GOSSIP_DEBUGFS_DEBUG, "%s: start\n", __func__); - if (strlen(client_debug_string) + 1 < ORANGEFS_MAX_DEBUG_STRING_LEN) { - strcpy(c_buffer, client_debug_string); - strcat(c_buffer, "\n"); + if (len + 1 < ORANGEFS_MAX_DEBUG_STRING_LEN) { + memcpy(c_buffer, client_debug_string, len); + c_buffer[len] = '\n'; + c_buffer[len + 1] = '\0'; } else { - strcpy(c_buffer, "none\n"); + strscpy(c_buffer, "none\n"); pr_info("%s: overflow! 2\n", __func__); } @@ -748,15 +751,14 @@ static void debug_mask_to_string(void *mask, int type) else if (len) kernel_debug_string[len - 1] = '\0'; else if (type) - strcpy(client_debug_string, "none"); + strscpy(client_debug_string, "none"); else - strcpy(kernel_debug_string, "none"); + strscpy(kernel_debug_string, "none"); out: gossip_debug(GOSSIP_UTILS_DEBUG, "%s: string:%s:\n", __func__, debug_string); return; - } static void do_k_string(void *k_mask, int index) @@ -775,7 +777,7 @@ static void do_k_string(void *k_mask, int index) strcat(kernel_debug_string, ","); } else { gossip_err("%s: overflow!\n", __func__); - strcpy(kernel_debug_string, ORANGEFS_ALL); + strscpy(kernel_debug_string, ORANGEFS_ALL); goto out; } } @@ -802,7 +804,7 @@ static void do_c_string(void *c_mask, int index) strcat(client_debug_string, ","); } else { gossip_err("%s: overflow!\n", __func__); - strcpy(client_debug_string, ORANGEFS_ALL); + strscpy(client_debug_string, ORANGEFS_ALL); goto out; } } @@ -838,14 +840,14 @@ static int check_amalgam_keyword(void *mask, int type) if ((c_mask->mask1 == cdm_array[client_all_index].mask1) && (c_mask->mask2 == cdm_array[client_all_index].mask2)) { - strcpy(client_debug_string, ORANGEFS_ALL); + strscpy(client_debug_string, ORANGEFS_ALL); rc = 1; goto out; } if ((c_mask->mask1 == cdm_array[client_verbose_index].mask1) && (c_mask->mask2 == cdm_array[client_verbose_index].mask2)) { - strcpy(client_debug_string, ORANGEFS_VERBOSE); + strscpy(client_debug_string, ORANGEFS_VERBOSE); rc = 1; goto out; } @@ -854,7 +856,7 @@ static int check_amalgam_keyword(void *mask, int type) k_mask = (__u64 *) mask; if (*k_mask >= s_kmod_keyword_mask_map[k_all_index].mask_val) { - strcpy(kernel_debug_string, ORANGEFS_ALL); + strscpy(kernel_debug_string, ORANGEFS_ALL); rc = 1; goto out; } diff --git a/fs/orangefs/orangefs-kernel.h b/fs/orangefs/orangefs-kernel.h index 29c6da43e396..1451fc2c1917 100644 --- a/fs/orangefs/orangefs-kernel.h +++ b/fs/orangefs/orangefs-kernel.h @@ -360,7 +360,8 @@ int orangefs_getattr(struct mnt_idmap *idmap, const struct path *path, int orangefs_permission(struct mnt_idmap *idmap, struct inode *inode, int mask); -int orangefs_update_time(struct inode *, int); +int orangefs_update_time(struct inode *inode, enum fs_update_time type, + unsigned int flags); /* * defined in xattr.c diff --git a/fs/orangefs/xattr.c b/fs/orangefs/xattr.c index eee3c5ed1bbb..a431aa07a229 100644 --- a/fs/orangefs/xattr.c +++ b/fs/orangefs/xattr.c @@ -152,7 +152,7 @@ ssize_t orangefs_inode_getxattr(struct inode *inode, const char *name, goto out_unlock; new_op->upcall.req.getxattr.refn = orangefs_inode->refn; - strcpy(new_op->upcall.req.getxattr.key, name); + strscpy(new_op->upcall.req.getxattr.key, name); /* * NOTE: Although keys are meant to be NULL terminated textual @@ -173,7 +173,7 @@ ssize_t orangefs_inode_getxattr(struct inode *inode, const char *name, (char *)new_op->upcall.req.getxattr.key); cx = kmalloc(sizeof *cx, GFP_KERNEL); if (cx) { - strcpy(cx->key, name); + strscpy(cx->key, name); cx->length = -1; cx->timeout = jiffies + orangefs_getattr_timeout_msecs*HZ/1000; @@ -220,14 +220,14 @@ ssize_t orangefs_inode_getxattr(struct inode *inode, const char *name, ret = length; if (cx) { - strcpy(cx->key, name); + strscpy(cx->key, name); memcpy(cx->val, buffer, length); cx->length = length; cx->timeout = jiffies + HZ; } else { cx = kmalloc(sizeof *cx, GFP_KERNEL); if (cx) { - strcpy(cx->key, name); + strscpy(cx->key, name); memcpy(cx->val, buffer, length); cx->length = length; cx->timeout = jiffies + HZ; @@ -267,7 +267,7 @@ static int orangefs_inode_removexattr(struct inode *inode, const char *name, * textual strings, I am going to explicitly pass the * length just in case we change this later on... */ - strcpy(new_op->upcall.req.removexattr.key, name); + strscpy(new_op->upcall.req.removexattr.key, name); new_op->upcall.req.removexattr.key_sz = strlen(name) + 1; gossip_debug(GOSSIP_XATTR_DEBUG, @@ -361,7 +361,7 @@ int orangefs_inode_setxattr(struct inode *inode, const char *name, * strings, I am going to explicitly pass the length just in * case we change this later on... */ - strcpy(new_op->upcall.req.setxattr.keyval.key, name); + strscpy(new_op->upcall.req.setxattr.keyval.key, name); new_op->upcall.req.setxattr.keyval.key_sz = strlen(name) + 1; memcpy(new_op->upcall.req.setxattr.keyval.val, value, size); new_op->upcall.req.setxattr.keyval.val_sz = size; diff --git a/fs/overlayfs/file.c b/fs/overlayfs/file.c index cbae89457234..8269431ba3c6 100644 --- a/fs/overlayfs/file.c +++ b/fs/overlayfs/file.c @@ -5,6 +5,7 @@ #include <linux/cred.h> #include <linux/file.h> +#include <linux/filelock.h> #include <linux/mount.h> #include <linux/xattr.h> #include <linux/uio.h> @@ -647,4 +648,5 @@ const struct file_operations ovl_file_operations = { .copy_file_range = ovl_copy_file_range, .remap_file_range = ovl_remap_file_range, + .setlease = generic_setlease, }; diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c index bdbf86b56a9b..00c69707bda9 100644 --- a/fs/overlayfs/inode.c +++ b/fs/overlayfs/inode.c @@ -555,9 +555,10 @@ int ovl_set_acl(struct mnt_idmap *idmap, struct dentry *dentry, } #endif -int ovl_update_time(struct inode *inode, int flags) +int ovl_update_time(struct inode *inode, enum fs_update_time type, + unsigned int flags) { - if (flags & S_ATIME) { + if (type == FS_UPD_ATIME) { struct ovl_fs *ofs = OVL_FS(inode->i_sb); struct path upperpath = { .mnt = ovl_upper_mnt(ofs), @@ -565,6 +566,8 @@ int ovl_update_time(struct inode *inode, int flags) }; if (upperpath.dentry) { + if (flags & IOCB_NOWAIT) + return -EAGAIN; touch_atime(&upperpath); inode_set_atime_to_ts(inode, inode_get_atime(d_inode(upperpath.dentry))); diff --git a/fs/overlayfs/namei.c b/fs/overlayfs/namei.c index e9a69c95be91..cda26bdef3b9 100644 --- a/fs/overlayfs/namei.c +++ b/fs/overlayfs/namei.c @@ -7,6 +7,7 @@ #include <linux/fs.h> #include <linux/cred.h> #include <linux/ctype.h> +#include <linux/hex.h> #include <linux/namei.h> #include <linux/xattr.h> #include <linux/ratelimit.h> diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h index f9ac9bdde830..315882a360ce 100644 --- a/fs/overlayfs/overlayfs.h +++ b/fs/overlayfs/overlayfs.h @@ -820,7 +820,8 @@ static inline struct posix_acl *ovl_get_acl_path(const struct path *path, } #endif -int ovl_update_time(struct inode *inode, int flags); +int ovl_update_time(struct inode *inode, enum fs_update_time type, + unsigned int flags); bool ovl_is_private_xattr(struct super_block *sb, const char *name); struct ovl_inode_params { diff --git a/fs/overlayfs/readdir.c b/fs/overlayfs/readdir.c index 160960bb0ad0..7fd415d7471e 100644 --- a/fs/overlayfs/readdir.c +++ b/fs/overlayfs/readdir.c @@ -8,6 +8,7 @@ #include <linux/slab.h> #include <linux/namei.h> #include <linux/file.h> +#include <linux/filelock.h> #include <linux/xattr.h> #include <linux/rbtree.h> #include <linux/security.h> @@ -1070,6 +1071,7 @@ const struct file_operations ovl_dir_operations = { .llseek = ovl_dir_llseek, .fsync = ovl_dir_fsync, .release = ovl_dir_release, + .setlease = generic_setlease, }; int ovl_check_empty_dir(struct dentry *dentry, struct list_head *list) diff --git a/fs/posix_acl.c b/fs/posix_acl.c index 768f027c1428..4ef6f9d2b8d6 100644 --- a/fs/posix_acl.c +++ b/fs/posix_acl.c @@ -829,19 +829,19 @@ EXPORT_SYMBOL (posix_acl_from_xattr); /* * Convert from in-memory to extended attribute representation. */ -int +void * posix_acl_to_xattr(struct user_namespace *user_ns, const struct posix_acl *acl, - void *buffer, size_t size) + size_t *sizep, gfp_t gfp) { - struct posix_acl_xattr_header *ext_acl = buffer; + struct posix_acl_xattr_header *ext_acl; struct posix_acl_xattr_entry *ext_entry; - int real_size, n; + size_t size; + int n; - real_size = posix_acl_xattr_size(acl->a_count); - if (!buffer) - return real_size; - if (real_size > size) - return -ERANGE; + size = posix_acl_xattr_size(acl->a_count); + ext_acl = kmalloc(size, gfp); + if (!ext_acl) + return NULL; ext_entry = (void *)(ext_acl + 1); ext_acl->a_version = cpu_to_le32(POSIX_ACL_XATTR_VERSION); @@ -864,7 +864,8 @@ posix_acl_to_xattr(struct user_namespace *user_ns, const struct posix_acl *acl, break; } } - return real_size; + *sizep = size; + return ext_acl; } EXPORT_SYMBOL (posix_acl_to_xattr); diff --git a/fs/proc/array.c b/fs/proc/array.c index 42932f88141a..f447e734612a 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -55,6 +55,7 @@ #include <linux/types.h> #include <linux/errno.h> +#include <linux/hex.h> #include <linux/time.h> #include <linux/time_namespace.h> #include <linux/kernel.h> @@ -528,7 +529,7 @@ static int do_task_stat(struct seq_file *m, struct pid_namespace *ns, } sid = task_session_nr_ns(task, ns); - ppid = task_tgid_nr_ns(task->real_parent, ns); + ppid = task_ppid_nr_ns(task, ns); pgid = task_pgrp_nr_ns(task, ns); unlock_task_sighand(task, &flags); diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index 81dfc26bfae8..dd3b5cf9f0b7 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -656,6 +656,7 @@ static int do_procmap_query(struct mm_struct *mm, void __user *uarg) struct proc_maps_locking_ctx lock_ctx = { .mm = mm }; struct procmap_query karg; struct vm_area_struct *vma; + struct file *vm_file = NULL; const char *name = NULL; char build_id_buf[BUILD_ID_SIZE_MAX], *name_buf = NULL; __u64 usize; @@ -727,21 +728,6 @@ static int do_procmap_query(struct mm_struct *mm, void __user *uarg) karg.inode = 0; } - if (karg.build_id_size) { - __u32 build_id_sz; - - err = build_id_parse(vma, build_id_buf, &build_id_sz); - if (err) { - karg.build_id_size = 0; - } else { - if (karg.build_id_size < build_id_sz) { - err = -ENAMETOOLONG; - goto out; - } - karg.build_id_size = build_id_sz; - } - } - if (karg.vma_name_size) { size_t name_buf_sz = min_t(size_t, PATH_MAX, karg.vma_name_size); const struct path *path; @@ -775,10 +761,34 @@ static int do_procmap_query(struct mm_struct *mm, void __user *uarg) karg.vma_name_size = name_sz; } + if (karg.build_id_size && vma->vm_file) + vm_file = get_file(vma->vm_file); + /* unlock vma or mmap_lock, and put mm_struct before copying data to user */ query_vma_teardown(&lock_ctx); mmput(mm); + if (karg.build_id_size) { + __u32 build_id_sz; + + if (vm_file) + err = build_id_parse_file(vm_file, build_id_buf, &build_id_sz); + else + err = -ENOENT; + if (err) { + karg.build_id_size = 0; + } else { + if (karg.build_id_size < build_id_sz) { + err = -ENAMETOOLONG; + goto out; + } + karg.build_id_size = build_id_sz; + } + } + + if (vm_file) + fput(vm_file); + if (karg.vma_name_size && copy_to_user(u64_to_user_ptr(karg.vma_name_addr), name, karg.vma_name_size)) { kfree(name_buf); @@ -798,6 +808,8 @@ static int do_procmap_query(struct mm_struct *mm, void __user *uarg) out: query_vma_teardown(&lock_ctx); mmput(mm); + if (vm_file) + fput(vm_file); kfree(name_buf); return err; } @@ -2739,7 +2751,7 @@ static int pagemap_scan_pmd_entry(pmd_t *pmd, unsigned long start, return 0; } - arch_enter_lazy_mmu_mode(); + lazy_mmu_mode_enable(); if ((p->arg.flags & PM_SCAN_WP_MATCHING) && !p->vec_out) { /* Fast path for performing exclusive WP */ @@ -2809,7 +2821,7 @@ flush_and_return: if (flush_end) flush_tlb_range(vma, start, addr); - arch_leave_lazy_mmu_mode(); + lazy_mmu_mode_disable(); pte_unmap_unlock(start_pte, ptl); cond_resched(); diff --git a/fs/pstore/ram_core.c b/fs/pstore/ram_core.c index f1848cdd6d34..7b6d6378a3b8 100644 --- a/fs/pstore/ram_core.c +++ b/fs/pstore/ram_core.c @@ -298,6 +298,17 @@ void persistent_ram_save_old(struct persistent_ram_zone *prz) if (!size) return; + /* + * If the existing buffer is differently sized, free it so a new + * one is allocated. This can happen when persistent_ram_save_old() + * is called early in boot and later for a timer-triggered + * survivable crash when the crash dumps don't match in size + * (which would be extremely unlikely given kmsg buffers usually + * exceed prz buffer sizes). + */ + if (prz->old_log && prz->old_log_size != size) + persistent_ram_free_old(prz); + if (!prz->old_log) { persistent_ram_ecc_old(prz); prz->old_log = kvzalloc(size, GFP_KERNEL); @@ -447,6 +458,13 @@ static void *persistent_ram_vmap(phys_addr_t start, size_t size, kfree(pages); /* + * vmap() may fail and return NULL. Do not add the offset in this + * case, otherwise a NULL mapping would appear successful. + */ + if (!vaddr) + return NULL; + + /* * Since vmap() uses page granularity, we must add the offset * into the page here, to get the byte granularity address * into the mapping to represent the actual "start" location. diff --git a/fs/qnx4/dir.c b/fs/qnx4/dir.c index 42a529e26bd6..6402715ab377 100644 --- a/fs/qnx4/dir.c +++ b/fs/qnx4/dir.c @@ -13,6 +13,7 @@ */ #include <linux/buffer_head.h> +#include <linux/filelock.h> #include "qnx4.h" static int qnx4_readdir(struct file *file, struct dir_context *ctx) @@ -71,6 +72,7 @@ const struct file_operations qnx4_dir_operations = .read = generic_read_dir, .iterate_shared = qnx4_readdir, .fsync = generic_file_fsync, + .setlease = generic_setlease, }; const struct inode_operations qnx4_dir_inode_operations = diff --git a/fs/qnx6/dir.c b/fs/qnx6/dir.c index b4d10e45f2e4..ae0c9846833d 100644 --- a/fs/qnx6/dir.c +++ b/fs/qnx6/dir.c @@ -11,6 +11,7 @@ * */ +#include <linux/filelock.h> #include "qnx6.h" static unsigned qnx6_lfile_checksum(char *name, unsigned size) @@ -275,6 +276,7 @@ const struct file_operations qnx6_dir_operations = { .read = generic_read_dir, .iterate_shared = qnx6_readdir, .fsync = generic_file_fsync, + .setlease = generic_setlease, }; const struct inode_operations qnx6_dir_inode_operations = { diff --git a/fs/quota/quota.c b/fs/quota/quota.c index 7c2b75a44485..33bacd707580 100644 --- a/fs/quota/quota.c +++ b/fs/quota/quota.c @@ -867,7 +867,7 @@ static struct super_block *quotactl_block(const char __user *special, int cmd) { #ifdef CONFIG_BLOCK struct super_block *sb; - struct filename *tmp = getname(special); + CLASS(filename, tmp)(special); bool excl = false, thawed = false; int error; dev_t dev; @@ -875,7 +875,6 @@ static struct super_block *quotactl_block(const char __user *special, int cmd) if (IS_ERR(tmp)) return ERR_CAST(tmp); error = lookup_bdev(tmp->name, &dev); - putname(tmp); if (error) return ERR_PTR(error); @@ -899,6 +898,7 @@ retry: sb_start_write(sb); sb_end_write(sb); put_super(sb); + cond_resched(); goto retry; } return sb; diff --git a/fs/read_write.c b/fs/read_write.c index 833bae068770..50bff7edc91f 100644 --- a/fs/read_write.c +++ b/fs/read_write.c @@ -20,6 +20,7 @@ #include <linux/compat.h> #include <linux/mount.h> #include <linux/fs.h> +#include <linux/filelock.h> #include "internal.h" #include <linux/uaccess.h> @@ -30,6 +31,7 @@ const struct file_operations generic_ro_fops = { .read_iter = generic_file_read_iter, .mmap_prepare = generic_file_readonly_mmap_prepare, .splice_read = filemap_splice_read, + .setlease = generic_setlease, }; EXPORT_SYMBOL(generic_ro_fops); diff --git a/fs/readdir.c b/fs/readdir.c index 7764b8638978..73707b6816e9 100644 --- a/fs/readdir.c +++ b/fs/readdir.c @@ -316,6 +316,7 @@ SYSCALL_DEFINE3(getdents, unsigned int, fd, struct getdents_callback buf = { .ctx.actor = filldir, .ctx.count = count, + .ctx.dt_flags_mask = FILLDIR_FLAG_NOINTR, .current_dir = dirent }; int error; @@ -400,6 +401,7 @@ SYSCALL_DEFINE3(getdents64, unsigned int, fd, struct getdents_callback64 buf = { .ctx.actor = filldir64, .ctx.count = count, + .ctx.dt_flags_mask = FILLDIR_FLAG_NOINTR, .current_dir = dirent }; int error; @@ -569,6 +571,7 @@ COMPAT_SYSCALL_DEFINE3(getdents, unsigned int, fd, struct compat_getdents_callback buf = { .ctx.actor = compat_filldir, .ctx.count = count, + .ctx.dt_flags_mask = FILLDIR_FLAG_NOINTR, .current_dir = dirent, }; int error; diff --git a/fs/resctrl/ctrlmondata.c b/fs/resctrl/ctrlmondata.c index b2d178d3556e..cc4237c57cbe 100644 --- a/fs/resctrl/ctrlmondata.c +++ b/fs/resctrl/ctrlmondata.c @@ -17,6 +17,7 @@ #include <linux/cpu.h> #include <linux/kernfs.h> +#include <linux/math.h> #include <linux/seq_file.h> #include <linux/slab.h> #include <linux/tick.h> @@ -551,8 +552,8 @@ struct rdt_domain_hdr *resctrl_find_domain(struct list_head *h, int id, } void mon_event_read(struct rmid_read *rr, struct rdt_resource *r, - struct rdt_mon_domain *d, struct rdtgroup *rdtgrp, - cpumask_t *cpumask, int evtid, int first) + struct rdt_domain_hdr *hdr, struct rdtgroup *rdtgrp, + cpumask_t *cpumask, struct mon_evt *evt, int first) { int cpu; @@ -563,21 +564,26 @@ void mon_event_read(struct rmid_read *rr, struct rdt_resource *r, * Setup the parameters to pass to mon_event_count() to read the data. */ rr->rgrp = rdtgrp; - rr->evtid = evtid; + rr->evt = evt; rr->r = r; - rr->d = d; + rr->hdr = hdr; rr->first = first; if (resctrl_arch_mbm_cntr_assign_enabled(r) && - resctrl_is_mbm_event(evtid)) { + resctrl_is_mbm_event(evt->evtid)) { rr->is_mbm_cntr = true; } else { - rr->arch_mon_ctx = resctrl_arch_mon_ctx_alloc(r, evtid); + rr->arch_mon_ctx = resctrl_arch_mon_ctx_alloc(r, evt->evtid); if (IS_ERR(rr->arch_mon_ctx)) { rr->err = -EINVAL; return; } } + if (evt->any_cpu) { + mon_event_count(rr); + goto out_ctx_free; + } + cpu = cpumask_any_housekeeping(cpumask, RESCTRL_PICK_ANY_CPU); /* @@ -591,22 +597,93 @@ void mon_event_read(struct rmid_read *rr, struct rdt_resource *r, else smp_call_on_cpu(cpu, smp_mon_event_count, rr, false); +out_ctx_free: if (rr->arch_mon_ctx) - resctrl_arch_mon_ctx_free(r, evtid, rr->arch_mon_ctx); + resctrl_arch_mon_ctx_free(r, evt->evtid, rr->arch_mon_ctx); +} + +/* + * Decimal place precision to use for each number of fixed-point + * binary bits computed from ceil(binary_bits * log10(2)) except + * binary_bits == 0 which will print "value.0" + */ +static const unsigned int decplaces[MAX_BINARY_BITS + 1] = { + [0] = 1, + [1] = 1, + [2] = 1, + [3] = 1, + [4] = 2, + [5] = 2, + [6] = 2, + [7] = 3, + [8] = 3, + [9] = 3, + [10] = 4, + [11] = 4, + [12] = 4, + [13] = 4, + [14] = 5, + [15] = 5, + [16] = 5, + [17] = 6, + [18] = 6, + [19] = 6, + [20] = 7, + [21] = 7, + [22] = 7, + [23] = 7, + [24] = 8, + [25] = 8, + [26] = 8, + [27] = 9 +}; + +static void print_event_value(struct seq_file *m, unsigned int binary_bits, u64 val) +{ + unsigned long long frac = 0; + + if (binary_bits) { + /* Mask off the integer part of the fixed-point value. */ + frac = val & GENMASK_ULL(binary_bits - 1, 0); + + /* + * Multiply by 10^{desired decimal places}. The integer part of + * the fixed point value is now almost what is needed. + */ + frac *= int_pow(10ull, decplaces[binary_bits]); + + /* + * Round to nearest by adding a value that would be a "1" in the + * binary_bits + 1 place. Integer part of fixed point value is + * now the needed value. + */ + frac += 1ull << (binary_bits - 1); + + /* + * Extract the integer part of the value. This is the decimal + * representation of the original fixed-point fractional value. + */ + frac >>= binary_bits; + } + + /* + * "frac" is now in the range [0 .. 10^decplaces). I.e. string + * representation will fit into chosen number of decimal places. + */ + seq_printf(m, "%llu.%0*llu\n", val >> binary_bits, decplaces[binary_bits], frac); } int rdtgroup_mondata_show(struct seq_file *m, void *arg) { struct kernfs_open_file *of = m->private; enum resctrl_res_level resid; - enum resctrl_event_id evtid; struct rdt_domain_hdr *hdr; struct rmid_read rr = {0}; - struct rdt_mon_domain *d; struct rdtgroup *rdtgrp; int domid, cpu, ret = 0; struct rdt_resource *r; struct cacheinfo *ci; + struct mon_evt *evt; struct mon_data *md; rdtgrp = rdtgroup_kn_lock_live(of->kn); @@ -623,10 +700,17 @@ int rdtgroup_mondata_show(struct seq_file *m, void *arg) resid = md->rid; domid = md->domid; - evtid = md->evtid; + evt = md->evt; r = resctrl_arch_get_resource(resid); if (md->sum) { + struct rdt_l3_mon_domain *d; + + if (WARN_ON_ONCE(resid != RDT_RESOURCE_L3)) { + ret = -EINVAL; + goto out; + } + /* * This file requires summing across all domains that share * the L3 cache id that was provided in the "domid" field of the @@ -641,7 +725,7 @@ int rdtgroup_mondata_show(struct seq_file *m, void *arg) continue; rr.ci = ci; mon_event_read(&rr, r, NULL, rdtgrp, - &ci->shared_cpu_map, evtid, false); + &ci->shared_cpu_map, evt, false); goto checkresult; } } @@ -653,12 +737,11 @@ int rdtgroup_mondata_show(struct seq_file *m, void *arg) * the resource to find the domain with "domid". */ hdr = resctrl_find_domain(&r->mon_domains, domid, NULL); - if (!hdr || WARN_ON_ONCE(hdr->type != RESCTRL_MON_DOMAIN)) { + if (!hdr) { ret = -ENOENT; goto out; } - d = container_of(hdr, struct rdt_mon_domain, hdr); - mon_event_read(&rr, r, d, rdtgrp, &d->hdr.cpu_mask, evtid, false); + mon_event_read(&rr, r, hdr, rdtgrp, &hdr->cpu_mask, evt, false); } checkresult: @@ -673,6 +756,8 @@ checkresult: seq_puts(m, "Unavailable\n"); else if (rr.err == -ENOENT) seq_puts(m, "Unassigned\n"); + else if (evt->is_floating_point) + print_event_value(m, evt->binary_bits, rr.val); else seq_printf(m, "%llu\n", rr.val); diff --git a/fs/resctrl/internal.h b/fs/resctrl/internal.h index bff4a54ae333..1a9b29119f88 100644 --- a/fs/resctrl/internal.h +++ b/fs/resctrl/internal.h @@ -61,7 +61,14 @@ static inline struct rdt_fs_context *rdt_fc2context(struct fs_context *fc) * READS_TO_REMOTE_MEM) being tracked by @evtid. * Only valid if @evtid is an MBM event. * @configurable: true if the event is configurable + * @any_cpu: true if the event can be read from any CPU + * @is_floating_point: event values are displayed in floating point format + * @binary_bits: number of fixed-point binary bits from architecture, + * only valid if @is_floating_point is true * @enabled: true if the event is enabled + * @arch_priv: Architecture private data for this event. + * The @arch_priv provided by the architecture via + * resctrl_enable_mon_event(). */ struct mon_evt { enum resctrl_event_id evtid; @@ -69,7 +76,11 @@ struct mon_evt { char *name; u32 evt_cfg; bool configurable; + bool any_cpu; + bool is_floating_point; + unsigned int binary_bits; bool enabled; + void *arch_priv; }; extern struct mon_evt mon_event_all[QOS_NUM_EVENTS]; @@ -77,13 +88,16 @@ extern struct mon_evt mon_event_all[QOS_NUM_EVENTS]; #define for_each_mon_event(mevt) for (mevt = &mon_event_all[QOS_FIRST_EVENT]; \ mevt < &mon_event_all[QOS_NUM_EVENTS]; mevt++) +/* Limit for mon_evt::binary_bits */ +#define MAX_BINARY_BITS 27 + /** * struct mon_data - Monitoring details for each event file. * @list: Member of the global @mon_data_kn_priv_list list. * @rid: Resource id associated with the event file. - * @evtid: Event id associated with the event file. - * @sum: Set when event must be summed across multiple - * domains. + * @evt: Event structure associated with the event file. + * @sum: Set for RDT_RESOURCE_L3 when event must be summed + * across multiple domains. * @domid: When @sum is zero this is the domain to which * the event file belongs. When @sum is one this * is the id of the L3 cache that all domains to be @@ -95,7 +109,7 @@ extern struct mon_evt mon_event_all[QOS_NUM_EVENTS]; struct mon_data { struct list_head list; enum resctrl_res_level rid; - enum resctrl_event_id evtid; + struct mon_evt *evt; int domid; bool sum; }; @@ -106,25 +120,27 @@ struct mon_data { * resource group then its event count is summed with the count from all * its child resource groups. * @r: Resource describing the properties of the event being read. - * @d: Domain that the counter should be read from. If NULL then sum all - * domains in @r sharing L3 @ci.id - * @evtid: Which monitor event to read. + * @hdr: Header of domain that the counter should be read from. If NULL then + * sum all domains in @r sharing L3 @ci.id + * @evt: Which monitor event to read. * @first: Initialize MBM counter when true. - * @ci: Cacheinfo for L3. Only set when @d is NULL. Used when summing domains. + * @ci: Cacheinfo for L3. Only set when @hdr is NULL. Used when summing + * domains. * @is_mbm_cntr: true if "mbm_event" counter assignment mode is enabled and it * is an MBM event. * @err: Error encountered when reading counter. - * @val: Returned value of event counter. If @rgrp is a parent resource group, - * @val includes the sum of event counts from its child resource groups. - * If @d is NULL, @val includes the sum of all domains in @r sharing @ci.id, - * (summed across child resource groups if @rgrp is a parent resource group). + * @val: Returned value of event counter. If @rgrp is a parent resource + * group, @val includes the sum of event counts from its child + * resource groups. If @hdr is NULL, @val includes the sum of all + * domains in @r sharing @ci.id, (summed across child resource groups + * if @rgrp is a parent resource group). * @arch_mon_ctx: Hardware monitor allocated for this read request (MPAM only). */ struct rmid_read { struct rdtgroup *rgrp; struct rdt_resource *r; - struct rdt_mon_domain *d; - enum resctrl_event_id evtid; + struct rdt_domain_hdr *hdr; + struct mon_evt *evt; bool first; struct cacheinfo *ci; bool is_mbm_cntr; @@ -243,6 +259,8 @@ struct rdtgroup { #define RFTYPE_ASSIGN_CONFIG BIT(11) +#define RFTYPE_RES_PERF_PKG BIT(12) + #define RFTYPE_CTRL_INFO (RFTYPE_INFO | RFTYPE_CTRL) #define RFTYPE_MON_INFO (RFTYPE_INFO | RFTYPE_MON) @@ -351,23 +369,27 @@ int closids_supported(void); void closid_free(int closid); +int setup_rmid_lru_list(void); + +void free_rmid_lru_list(void); + int alloc_rmid(u32 closid); void free_rmid(u32 closid, u32 rmid); -void resctrl_mon_resource_exit(void); +int resctrl_l3_mon_resource_init(void); + +void resctrl_l3_mon_resource_exit(void); void mon_event_count(void *info); int rdtgroup_mondata_show(struct seq_file *m, void *arg); void mon_event_read(struct rmid_read *rr, struct rdt_resource *r, - struct rdt_mon_domain *d, struct rdtgroup *rdtgrp, - cpumask_t *cpumask, int evtid, int first); - -int resctrl_mon_resource_init(void); + struct rdt_domain_hdr *hdr, struct rdtgroup *rdtgrp, + cpumask_t *cpumask, struct mon_evt *evt, int first); -void mbm_setup_overflow_handler(struct rdt_mon_domain *dom, +void mbm_setup_overflow_handler(struct rdt_l3_mon_domain *dom, unsigned long delay_ms, int exclude_cpu); @@ -375,14 +397,14 @@ void mbm_handle_overflow(struct work_struct *work); bool is_mba_sc(struct rdt_resource *r); -void cqm_setup_limbo_handler(struct rdt_mon_domain *dom, unsigned long delay_ms, +void cqm_setup_limbo_handler(struct rdt_l3_mon_domain *dom, unsigned long delay_ms, int exclude_cpu); void cqm_handle_limbo(struct work_struct *work); -bool has_busy_rmid(struct rdt_mon_domain *d); +bool has_busy_rmid(struct rdt_l3_mon_domain *d); -void __check_limbo(struct rdt_mon_domain *d, bool force_free); +void __check_limbo(struct rdt_l3_mon_domain *d, bool force_free); void resctrl_file_fflags_init(const char *config, unsigned long fflags); diff --git a/fs/resctrl/monitor.c b/fs/resctrl/monitor.c index 572a9925bd6c..0cd5476a483a 100644 --- a/fs/resctrl/monitor.c +++ b/fs/resctrl/monitor.c @@ -130,16 +130,18 @@ static void limbo_release_entry(struct rmid_entry *entry) * decrement the count. If the busy count gets to zero on an RMID, we * free the RMID */ -void __check_limbo(struct rdt_mon_domain *d, bool force_free) +void __check_limbo(struct rdt_l3_mon_domain *d, bool force_free) { struct rdt_resource *r = resctrl_arch_get_resource(RDT_RESOURCE_L3); u32 idx_limit = resctrl_arch_system_num_rmid_idx(); struct rmid_entry *entry; u32 idx, cur_idx = 1; void *arch_mon_ctx; + void *arch_priv; bool rmid_dirty; u64 val = 0; + arch_priv = mon_event_all[QOS_L3_OCCUP_EVENT_ID].arch_priv; arch_mon_ctx = resctrl_arch_mon_ctx_alloc(r, QOS_L3_OCCUP_EVENT_ID); if (IS_ERR(arch_mon_ctx)) { pr_warn_ratelimited("Failed to allocate monitor context: %ld", @@ -159,8 +161,8 @@ void __check_limbo(struct rdt_mon_domain *d, bool force_free) break; entry = __rmid_entry(idx); - if (resctrl_arch_rmid_read(r, d, entry->closid, entry->rmid, - QOS_L3_OCCUP_EVENT_ID, &val, + if (resctrl_arch_rmid_read(r, &d->hdr, entry->closid, entry->rmid, + QOS_L3_OCCUP_EVENT_ID, arch_priv, &val, arch_mon_ctx)) { rmid_dirty = true; } else { @@ -188,7 +190,7 @@ void __check_limbo(struct rdt_mon_domain *d, bool force_free) resctrl_arch_mon_ctx_free(r, QOS_L3_OCCUP_EVENT_ID, arch_mon_ctx); } -bool has_busy_rmid(struct rdt_mon_domain *d) +bool has_busy_rmid(struct rdt_l3_mon_domain *d) { u32 idx_limit = resctrl_arch_system_num_rmid_idx(); @@ -289,7 +291,7 @@ int alloc_rmid(u32 closid) static void add_rmid_to_limbo(struct rmid_entry *entry) { struct rdt_resource *r = resctrl_arch_get_resource(RDT_RESOURCE_L3); - struct rdt_mon_domain *d; + struct rdt_l3_mon_domain *d; u32 idx; lockdep_assert_held(&rdtgroup_mutex); @@ -342,7 +344,7 @@ void free_rmid(u32 closid, u32 rmid) list_add_tail(&entry->list, &rmid_free_lru); } -static struct mbm_state *get_mbm_state(struct rdt_mon_domain *d, u32 closid, +static struct mbm_state *get_mbm_state(struct rdt_l3_mon_domain *d, u32 closid, u32 rmid, enum resctrl_event_id evtid) { u32 idx = resctrl_arch_rmid_idx_encode(closid, rmid); @@ -362,7 +364,7 @@ static struct mbm_state *get_mbm_state(struct rdt_mon_domain *d, u32 closid, * Return: * Valid counter ID on success, or -ENOENT on failure. */ -static int mbm_cntr_get(struct rdt_resource *r, struct rdt_mon_domain *d, +static int mbm_cntr_get(struct rdt_resource *r, struct rdt_l3_mon_domain *d, struct rdtgroup *rdtgrp, enum resctrl_event_id evtid) { int cntr_id; @@ -389,7 +391,7 @@ static int mbm_cntr_get(struct rdt_resource *r, struct rdt_mon_domain *d, * Return: * Valid counter ID on success, or -ENOSPC on failure. */ -static int mbm_cntr_alloc(struct rdt_resource *r, struct rdt_mon_domain *d, +static int mbm_cntr_alloc(struct rdt_resource *r, struct rdt_l3_mon_domain *d, struct rdtgroup *rdtgrp, enum resctrl_event_id evtid) { int cntr_id; @@ -408,24 +410,29 @@ static int mbm_cntr_alloc(struct rdt_resource *r, struct rdt_mon_domain *d, /* * mbm_cntr_free() - Clear the counter ID configuration details in the domain @d. */ -static void mbm_cntr_free(struct rdt_mon_domain *d, int cntr_id) +static void mbm_cntr_free(struct rdt_l3_mon_domain *d, int cntr_id) { memset(&d->cntr_cfg[cntr_id], 0, sizeof(*d->cntr_cfg)); } -static int __mon_event_count(struct rdtgroup *rdtgrp, struct rmid_read *rr) +static int __l3_mon_event_count(struct rdtgroup *rdtgrp, struct rmid_read *rr) { int cpu = smp_processor_id(); u32 closid = rdtgrp->closid; u32 rmid = rdtgrp->mon.rmid; - struct rdt_mon_domain *d; + struct rdt_l3_mon_domain *d; int cntr_id = -ENOENT; struct mbm_state *m; - int err, ret; u64 tval = 0; + if (!domain_header_is_valid(rr->hdr, RESCTRL_MON_DOMAIN, RDT_RESOURCE_L3)) { + rr->err = -EIO; + return -EINVAL; + } + d = container_of(rr->hdr, struct rdt_l3_mon_domain, hdr); + if (rr->is_mbm_cntr) { - cntr_id = mbm_cntr_get(rr->r, rr->d, rdtgrp, rr->evtid); + cntr_id = mbm_cntr_get(rr->r, d, rdtgrp, rr->evt->evtid); if (cntr_id < 0) { rr->err = -ENOENT; return -EINVAL; @@ -434,31 +441,51 @@ static int __mon_event_count(struct rdtgroup *rdtgrp, struct rmid_read *rr) if (rr->first) { if (rr->is_mbm_cntr) - resctrl_arch_reset_cntr(rr->r, rr->d, closid, rmid, cntr_id, rr->evtid); + resctrl_arch_reset_cntr(rr->r, d, closid, rmid, cntr_id, rr->evt->evtid); else - resctrl_arch_reset_rmid(rr->r, rr->d, closid, rmid, rr->evtid); - m = get_mbm_state(rr->d, closid, rmid, rr->evtid); + resctrl_arch_reset_rmid(rr->r, d, closid, rmid, rr->evt->evtid); + m = get_mbm_state(d, closid, rmid, rr->evt->evtid); if (m) memset(m, 0, sizeof(struct mbm_state)); return 0; } - if (rr->d) { - /* Reading a single domain, must be on a CPU in that domain. */ - if (!cpumask_test_cpu(cpu, &rr->d->hdr.cpu_mask)) - return -EINVAL; - if (rr->is_mbm_cntr) - rr->err = resctrl_arch_cntr_read(rr->r, rr->d, closid, rmid, cntr_id, - rr->evtid, &tval); - else - rr->err = resctrl_arch_rmid_read(rr->r, rr->d, closid, rmid, - rr->evtid, &tval, rr->arch_mon_ctx); - if (rr->err) - return rr->err; + /* Reading a single domain, must be on a CPU in that domain. */ + if (!cpumask_test_cpu(cpu, &d->hdr.cpu_mask)) + return -EINVAL; + if (rr->is_mbm_cntr) + rr->err = resctrl_arch_cntr_read(rr->r, d, closid, rmid, cntr_id, + rr->evt->evtid, &tval); + else + rr->err = resctrl_arch_rmid_read(rr->r, rr->hdr, closid, rmid, + rr->evt->evtid, rr->evt->arch_priv, + &tval, rr->arch_mon_ctx); + if (rr->err) + return rr->err; - rr->val += tval; + rr->val += tval; - return 0; + return 0; +} + +static int __l3_mon_event_count_sum(struct rdtgroup *rdtgrp, struct rmid_read *rr) +{ + int cpu = smp_processor_id(); + u32 closid = rdtgrp->closid; + u32 rmid = rdtgrp->mon.rmid; + struct rdt_l3_mon_domain *d; + u64 tval = 0; + int err, ret; + + /* + * Summing across domains is only done for systems that implement + * Sub-NUMA Cluster. There is no overlap with systems that support + * assignable counters. + */ + if (rr->is_mbm_cntr) { + pr_warn_once("Summing domains using assignable counters is not supported\n"); + rr->err = -EINVAL; + return -EINVAL; } /* Summing domains that share a cache, must be on a CPU for that cache. */ @@ -476,12 +503,9 @@ static int __mon_event_count(struct rdtgroup *rdtgrp, struct rmid_read *rr) list_for_each_entry(d, &rr->r->mon_domains, hdr.list) { if (d->ci_id != rr->ci->id) continue; - if (rr->is_mbm_cntr) - err = resctrl_arch_cntr_read(rr->r, d, closid, rmid, cntr_id, - rr->evtid, &tval); - else - err = resctrl_arch_rmid_read(rr->r, d, closid, rmid, - rr->evtid, &tval, rr->arch_mon_ctx); + err = resctrl_arch_rmid_read(rr->r, &d->hdr, closid, rmid, + rr->evt->evtid, rr->evt->arch_priv, + &tval, rr->arch_mon_ctx); if (!err) { rr->val += tval; ret = 0; @@ -494,6 +518,35 @@ static int __mon_event_count(struct rdtgroup *rdtgrp, struct rmid_read *rr) return ret; } +static int __mon_event_count(struct rdtgroup *rdtgrp, struct rmid_read *rr) +{ + switch (rr->r->rid) { + case RDT_RESOURCE_L3: + WARN_ON_ONCE(rr->evt->any_cpu); + if (rr->hdr) + return __l3_mon_event_count(rdtgrp, rr); + else + return __l3_mon_event_count_sum(rdtgrp, rr); + case RDT_RESOURCE_PERF_PKG: { + u64 tval = 0; + + rr->err = resctrl_arch_rmid_read(rr->r, rr->hdr, rdtgrp->closid, + rdtgrp->mon.rmid, rr->evt->evtid, + rr->evt->arch_priv, + &tval, rr->arch_mon_ctx); + if (rr->err) + return rr->err; + + rr->val += tval; + + return 0; + } + default: + rr->err = -EINVAL; + return -EINVAL; + } +} + /* * mbm_bw_count() - Update bw count from values previously read by * __mon_event_count(). @@ -511,9 +564,13 @@ static void mbm_bw_count(struct rdtgroup *rdtgrp, struct rmid_read *rr) u64 cur_bw, bytes, cur_bytes; u32 closid = rdtgrp->closid; u32 rmid = rdtgrp->mon.rmid; + struct rdt_l3_mon_domain *d; struct mbm_state *m; - m = get_mbm_state(rr->d, closid, rmid, rr->evtid); + if (!domain_header_is_valid(rr->hdr, RESCTRL_MON_DOMAIN, RDT_RESOURCE_L3)) + return; + d = container_of(rr->hdr, struct rdt_l3_mon_domain, hdr); + m = get_mbm_state(d, closid, rmid, rr->evt->evtid); if (WARN_ON_ONCE(!m)) return; @@ -612,7 +669,7 @@ static struct rdt_ctrl_domain *get_ctrl_domain_from_cpu(int cpu, * throttle MSRs already have low percentage values. To avoid * unnecessarily restricting such rdtgroups, we also increase the bandwidth. */ -static void update_mba_bw(struct rdtgroup *rgrp, struct rdt_mon_domain *dom_mbm) +static void update_mba_bw(struct rdtgroup *rgrp, struct rdt_l3_mon_domain *dom_mbm) { u32 closid, rmid, cur_msr_val, new_msr_val; struct mbm_state *pmbm_data, *cmbm_data; @@ -680,18 +737,18 @@ static void update_mba_bw(struct rdtgroup *rgrp, struct rdt_mon_domain *dom_mbm) resctrl_arch_update_one(r_mba, dom_mba, closid, CDP_NONE, new_msr_val); } -static void mbm_update_one_event(struct rdt_resource *r, struct rdt_mon_domain *d, +static void mbm_update_one_event(struct rdt_resource *r, struct rdt_l3_mon_domain *d, struct rdtgroup *rdtgrp, enum resctrl_event_id evtid) { struct rmid_read rr = {0}; rr.r = r; - rr.d = d; - rr.evtid = evtid; + rr.hdr = &d->hdr; + rr.evt = &mon_event_all[evtid]; if (resctrl_arch_mbm_cntr_assign_enabled(r)) { rr.is_mbm_cntr = true; } else { - rr.arch_mon_ctx = resctrl_arch_mon_ctx_alloc(rr.r, rr.evtid); + rr.arch_mon_ctx = resctrl_arch_mon_ctx_alloc(rr.r, evtid); if (IS_ERR(rr.arch_mon_ctx)) { pr_warn_ratelimited("Failed to allocate monitor context: %ld", PTR_ERR(rr.arch_mon_ctx)); @@ -709,10 +766,10 @@ static void mbm_update_one_event(struct rdt_resource *r, struct rdt_mon_domain * mbm_bw_count(rdtgrp, &rr); if (rr.arch_mon_ctx) - resctrl_arch_mon_ctx_free(rr.r, rr.evtid, rr.arch_mon_ctx); + resctrl_arch_mon_ctx_free(rr.r, evtid, rr.arch_mon_ctx); } -static void mbm_update(struct rdt_resource *r, struct rdt_mon_domain *d, +static void mbm_update(struct rdt_resource *r, struct rdt_l3_mon_domain *d, struct rdtgroup *rdtgrp) { /* @@ -733,12 +790,12 @@ static void mbm_update(struct rdt_resource *r, struct rdt_mon_domain *d, void cqm_handle_limbo(struct work_struct *work) { unsigned long delay = msecs_to_jiffies(CQM_LIMBOCHECK_INTERVAL); - struct rdt_mon_domain *d; + struct rdt_l3_mon_domain *d; cpus_read_lock(); mutex_lock(&rdtgroup_mutex); - d = container_of(work, struct rdt_mon_domain, cqm_limbo.work); + d = container_of(work, struct rdt_l3_mon_domain, cqm_limbo.work); __check_limbo(d, false); @@ -761,7 +818,7 @@ void cqm_handle_limbo(struct work_struct *work) * @exclude_cpu: Which CPU the handler should not run on, * RESCTRL_PICK_ANY_CPU to pick any CPU. */ -void cqm_setup_limbo_handler(struct rdt_mon_domain *dom, unsigned long delay_ms, +void cqm_setup_limbo_handler(struct rdt_l3_mon_domain *dom, unsigned long delay_ms, int exclude_cpu) { unsigned long delay = msecs_to_jiffies(delay_ms); @@ -778,7 +835,7 @@ void mbm_handle_overflow(struct work_struct *work) { unsigned long delay = msecs_to_jiffies(MBM_OVERFLOW_INTERVAL); struct rdtgroup *prgrp, *crgrp; - struct rdt_mon_domain *d; + struct rdt_l3_mon_domain *d; struct list_head *head; struct rdt_resource *r; @@ -793,7 +850,7 @@ void mbm_handle_overflow(struct work_struct *work) goto out_unlock; r = resctrl_arch_get_resource(RDT_RESOURCE_L3); - d = container_of(work, struct rdt_mon_domain, mbm_over.work); + d = container_of(work, struct rdt_l3_mon_domain, mbm_over.work); list_for_each_entry(prgrp, &rdt_all_groups, rdtgroup_list) { mbm_update(r, d, prgrp); @@ -827,7 +884,7 @@ out_unlock: * @exclude_cpu: Which CPU the handler should not run on, * RESCTRL_PICK_ANY_CPU to pick any CPU. */ -void mbm_setup_overflow_handler(struct rdt_mon_domain *dom, unsigned long delay_ms, +void mbm_setup_overflow_handler(struct rdt_l3_mon_domain *dom, unsigned long delay_ms, int exclude_cpu) { unsigned long delay = msecs_to_jiffies(delay_ms); @@ -846,42 +903,29 @@ void mbm_setup_overflow_handler(struct rdt_mon_domain *dom, unsigned long delay_ schedule_delayed_work_on(cpu, &dom->mbm_over, delay); } -static int dom_data_init(struct rdt_resource *r) +int setup_rmid_lru_list(void) { - u32 idx_limit = resctrl_arch_system_num_rmid_idx(); - u32 num_closid = resctrl_arch_get_num_closid(r); struct rmid_entry *entry = NULL; - int err = 0, i; + u32 idx_limit; u32 idx; + int i; - mutex_lock(&rdtgroup_mutex); - if (IS_ENABLED(CONFIG_RESCTRL_RMID_DEPENDS_ON_CLOSID)) { - u32 *tmp; - - /* - * If the architecture hasn't provided a sanitised value here, - * this may result in larger arrays than necessary. Resctrl will - * use a smaller system wide value based on the resources in - * use. - */ - tmp = kcalloc(num_closid, sizeof(*tmp), GFP_KERNEL); - if (!tmp) { - err = -ENOMEM; - goto out_unlock; - } + if (!resctrl_arch_mon_capable()) + return 0; - closid_num_dirty_rmid = tmp; - } + /* + * Called on every mount, but the number of RMIDs cannot change + * after the first mount, so keep using the same set of rmid_ptrs[] + * until resctrl_exit(). Note that the limbo handler continues to + * access rmid_ptrs[] after resctrl is unmounted. + */ + if (rmid_ptrs) + return 0; + idx_limit = resctrl_arch_system_num_rmid_idx(); rmid_ptrs = kcalloc(idx_limit, sizeof(struct rmid_entry), GFP_KERNEL); - if (!rmid_ptrs) { - if (IS_ENABLED(CONFIG_RESCTRL_RMID_DEPENDS_ON_CLOSID)) { - kfree(closid_num_dirty_rmid); - closid_num_dirty_rmid = NULL; - } - err = -ENOMEM; - goto out_unlock; - } + if (!rmid_ptrs) + return -ENOMEM; for (i = 0; i < idx_limit; i++) { entry = &rmid_ptrs[i]; @@ -894,71 +938,76 @@ static int dom_data_init(struct rdt_resource *r) /* * RESCTRL_RESERVED_CLOSID and RESCTRL_RESERVED_RMID are special and * are always allocated. These are used for the rdtgroup_default - * control group, which will be setup later in resctrl_init(). + * control group, which was setup earlier in rdtgroup_setup_default(). */ idx = resctrl_arch_rmid_idx_encode(RESCTRL_RESERVED_CLOSID, RESCTRL_RESERVED_RMID); entry = __rmid_entry(idx); list_del(&entry->list); -out_unlock: - mutex_unlock(&rdtgroup_mutex); - - return err; + return 0; } -static void dom_data_exit(struct rdt_resource *r) +void free_rmid_lru_list(void) { - mutex_lock(&rdtgroup_mutex); - - if (!r->mon_capable) - goto out_unlock; - - if (IS_ENABLED(CONFIG_RESCTRL_RMID_DEPENDS_ON_CLOSID)) { - kfree(closid_num_dirty_rmid); - closid_num_dirty_rmid = NULL; - } + if (!resctrl_arch_mon_capable()) + return; + mutex_lock(&rdtgroup_mutex); kfree(rmid_ptrs); rmid_ptrs = NULL; - -out_unlock: mutex_unlock(&rdtgroup_mutex); } +#define MON_EVENT(_eventid, _name, _res, _fp) \ + [_eventid] = { \ + .name = _name, \ + .evtid = _eventid, \ + .rid = _res, \ + .is_floating_point = _fp, \ +} + /* * All available events. Architecture code marks the ones that * are supported by a system using resctrl_enable_mon_event() * to set .enabled. */ struct mon_evt mon_event_all[QOS_NUM_EVENTS] = { - [QOS_L3_OCCUP_EVENT_ID] = { - .name = "llc_occupancy", - .evtid = QOS_L3_OCCUP_EVENT_ID, - .rid = RDT_RESOURCE_L3, - }, - [QOS_L3_MBM_TOTAL_EVENT_ID] = { - .name = "mbm_total_bytes", - .evtid = QOS_L3_MBM_TOTAL_EVENT_ID, - .rid = RDT_RESOURCE_L3, - }, - [QOS_L3_MBM_LOCAL_EVENT_ID] = { - .name = "mbm_local_bytes", - .evtid = QOS_L3_MBM_LOCAL_EVENT_ID, - .rid = RDT_RESOURCE_L3, - }, + MON_EVENT(QOS_L3_OCCUP_EVENT_ID, "llc_occupancy", RDT_RESOURCE_L3, false), + MON_EVENT(QOS_L3_MBM_TOTAL_EVENT_ID, "mbm_total_bytes", RDT_RESOURCE_L3, false), + MON_EVENT(QOS_L3_MBM_LOCAL_EVENT_ID, "mbm_local_bytes", RDT_RESOURCE_L3, false), + MON_EVENT(PMT_EVENT_ENERGY, "core_energy", RDT_RESOURCE_PERF_PKG, true), + MON_EVENT(PMT_EVENT_ACTIVITY, "activity", RDT_RESOURCE_PERF_PKG, true), + MON_EVENT(PMT_EVENT_STALLS_LLC_HIT, "stalls_llc_hit", RDT_RESOURCE_PERF_PKG, false), + MON_EVENT(PMT_EVENT_C1_RES, "c1_res", RDT_RESOURCE_PERF_PKG, false), + MON_EVENT(PMT_EVENT_UNHALTED_CORE_CYCLES, "unhalted_core_cycles", RDT_RESOURCE_PERF_PKG, false), + MON_EVENT(PMT_EVENT_STALLS_LLC_MISS, "stalls_llc_miss", RDT_RESOURCE_PERF_PKG, false), + MON_EVENT(PMT_EVENT_AUTO_C6_RES, "c6_res", RDT_RESOURCE_PERF_PKG, false), + MON_EVENT(PMT_EVENT_UNHALTED_REF_CYCLES, "unhalted_ref_cycles", RDT_RESOURCE_PERF_PKG, false), + MON_EVENT(PMT_EVENT_UOPS_RETIRED, "uops_retired", RDT_RESOURCE_PERF_PKG, false), }; -void resctrl_enable_mon_event(enum resctrl_event_id eventid) +bool resctrl_enable_mon_event(enum resctrl_event_id eventid, bool any_cpu, + unsigned int binary_bits, void *arch_priv) { - if (WARN_ON_ONCE(eventid < QOS_FIRST_EVENT || eventid >= QOS_NUM_EVENTS)) - return; + if (WARN_ON_ONCE(eventid < QOS_FIRST_EVENT || eventid >= QOS_NUM_EVENTS || + binary_bits > MAX_BINARY_BITS)) + return false; if (mon_event_all[eventid].enabled) { pr_warn("Duplicate enable for event %d\n", eventid); - return; + return false; + } + if (binary_bits && !mon_event_all[eventid].is_floating_point) { + pr_warn("Event %d may not be floating point\n", eventid); + return false; } + mon_event_all[eventid].any_cpu = any_cpu; + mon_event_all[eventid].binary_bits = binary_bits; + mon_event_all[eventid].arch_priv = arch_priv; mon_event_all[eventid].enabled = true; + + return true; } bool resctrl_is_mon_event_enabled(enum resctrl_event_id eventid) @@ -1082,7 +1131,7 @@ out_unlock: * mbm_cntr_free_all() - Clear all the counter ID configuration details in the * domain @d. Called when mbm_assign_mode is changed. */ -static void mbm_cntr_free_all(struct rdt_resource *r, struct rdt_mon_domain *d) +static void mbm_cntr_free_all(struct rdt_resource *r, struct rdt_l3_mon_domain *d) { memset(d->cntr_cfg, 0, sizeof(*d->cntr_cfg) * r->mon.num_mbm_cntrs); } @@ -1091,7 +1140,7 @@ static void mbm_cntr_free_all(struct rdt_resource *r, struct rdt_mon_domain *d) * resctrl_reset_rmid_all() - Reset all non-architecture states for all the * supported RMIDs. */ -static void resctrl_reset_rmid_all(struct rdt_resource *r, struct rdt_mon_domain *d) +static void resctrl_reset_rmid_all(struct rdt_resource *r, struct rdt_l3_mon_domain *d) { u32 idx_limit = resctrl_arch_system_num_rmid_idx(); enum resctrl_event_id evt; @@ -1112,7 +1161,7 @@ static void resctrl_reset_rmid_all(struct rdt_resource *r, struct rdt_mon_domain * Assign the counter if @assign is true else unassign the counter. Reset the * associated non-architectural state. */ -static void rdtgroup_assign_cntr(struct rdt_resource *r, struct rdt_mon_domain *d, +static void rdtgroup_assign_cntr(struct rdt_resource *r, struct rdt_l3_mon_domain *d, enum resctrl_event_id evtid, u32 rmid, u32 closid, u32 cntr_id, bool assign) { @@ -1132,7 +1181,7 @@ static void rdtgroup_assign_cntr(struct rdt_resource *r, struct rdt_mon_domain * * Return: * 0 on success, < 0 on failure. */ -static int rdtgroup_alloc_assign_cntr(struct rdt_resource *r, struct rdt_mon_domain *d, +static int rdtgroup_alloc_assign_cntr(struct rdt_resource *r, struct rdt_l3_mon_domain *d, struct rdtgroup *rdtgrp, struct mon_evt *mevt) { int cntr_id; @@ -1167,7 +1216,7 @@ static int rdtgroup_alloc_assign_cntr(struct rdt_resource *r, struct rdt_mon_dom * Return: * 0 on success, < 0 on failure. */ -static int rdtgroup_assign_cntr_event(struct rdt_mon_domain *d, struct rdtgroup *rdtgrp, +static int rdtgroup_assign_cntr_event(struct rdt_l3_mon_domain *d, struct rdtgroup *rdtgrp, struct mon_evt *mevt) { struct rdt_resource *r = resctrl_arch_get_resource(mevt->rid); @@ -1217,7 +1266,7 @@ void rdtgroup_assign_cntrs(struct rdtgroup *rdtgrp) * rdtgroup_free_unassign_cntr() - Unassign and reset the counter ID configuration * for the event pointed to by @mevt within the domain @d and resctrl group @rdtgrp. */ -static void rdtgroup_free_unassign_cntr(struct rdt_resource *r, struct rdt_mon_domain *d, +static void rdtgroup_free_unassign_cntr(struct rdt_resource *r, struct rdt_l3_mon_domain *d, struct rdtgroup *rdtgrp, struct mon_evt *mevt) { int cntr_id; @@ -1238,7 +1287,7 @@ static void rdtgroup_free_unassign_cntr(struct rdt_resource *r, struct rdt_mon_d * the event structure @mevt from the domain @d and the group @rdtgrp. Unassign * the counters from all the domains if @d is NULL else unassign from @d. */ -static void rdtgroup_unassign_cntr_event(struct rdt_mon_domain *d, struct rdtgroup *rdtgrp, +static void rdtgroup_unassign_cntr_event(struct rdt_l3_mon_domain *d, struct rdtgroup *rdtgrp, struct mon_evt *mevt) { struct rdt_resource *r = resctrl_arch_get_resource(mevt->rid); @@ -1313,7 +1362,7 @@ next_config: static void rdtgroup_update_cntr_event(struct rdt_resource *r, struct rdtgroup *rdtgrp, enum resctrl_event_id evtid) { - struct rdt_mon_domain *d; + struct rdt_l3_mon_domain *d; int cntr_id; list_for_each_entry(d, &r->mon_domains, hdr.list) { @@ -1419,7 +1468,7 @@ ssize_t resctrl_mbm_assign_mode_write(struct kernfs_open_file *of, char *buf, size_t nbytes, loff_t off) { struct rdt_resource *r = rdt_kn_parent_priv(of->kn); - struct rdt_mon_domain *d; + struct rdt_l3_mon_domain *d; int ret = 0; bool enable; @@ -1492,7 +1541,7 @@ int resctrl_num_mbm_cntrs_show(struct kernfs_open_file *of, struct seq_file *s, void *v) { struct rdt_resource *r = rdt_kn_parent_priv(of->kn); - struct rdt_mon_domain *dom; + struct rdt_l3_mon_domain *dom; bool sep = false; cpus_read_lock(); @@ -1516,7 +1565,7 @@ int resctrl_available_mbm_cntrs_show(struct kernfs_open_file *of, struct seq_file *s, void *v) { struct rdt_resource *r = rdt_kn_parent_priv(of->kn); - struct rdt_mon_domain *dom; + struct rdt_l3_mon_domain *dom; bool sep = false; u32 cntrs, i; int ret = 0; @@ -1557,7 +1606,7 @@ out_unlock: int mbm_L3_assignments_show(struct kernfs_open_file *of, struct seq_file *s, void *v) { struct rdt_resource *r = resctrl_arch_get_resource(RDT_RESOURCE_L3); - struct rdt_mon_domain *d; + struct rdt_l3_mon_domain *d; struct rdtgroup *rdtgrp; struct mon_evt *mevt; int ret = 0; @@ -1620,7 +1669,7 @@ static struct mon_evt *mbm_get_mon_event_by_name(struct rdt_resource *r, char *n return NULL; } -static int rdtgroup_modify_assign_state(char *assign, struct rdt_mon_domain *d, +static int rdtgroup_modify_assign_state(char *assign, struct rdt_l3_mon_domain *d, struct rdtgroup *rdtgrp, struct mon_evt *mevt) { int ret = 0; @@ -1646,7 +1695,7 @@ static int rdtgroup_modify_assign_state(char *assign, struct rdt_mon_domain *d, static int resctrl_parse_mbm_assignment(struct rdt_resource *r, struct rdtgroup *rdtgrp, char *event, char *tok) { - struct rdt_mon_domain *d; + struct rdt_l3_mon_domain *d; unsigned long dom_id = 0; char *dom_str, *id_str; struct mon_evt *mevt; @@ -1741,19 +1790,59 @@ ssize_t mbm_L3_assignments_write(struct kernfs_open_file *of, char *buf, return ret ?: nbytes; } +static int closid_num_dirty_rmid_alloc(struct rdt_resource *r) +{ + if (IS_ENABLED(CONFIG_RESCTRL_RMID_DEPENDS_ON_CLOSID)) { + u32 num_closid = resctrl_arch_get_num_closid(r); + u32 *tmp; + + /* For ARM memory ordering access to closid_num_dirty_rmid */ + mutex_lock(&rdtgroup_mutex); + + /* + * If the architecture hasn't provided a sanitised value here, + * this may result in larger arrays than necessary. Resctrl will + * use a smaller system wide value based on the resources in + * use. + */ + tmp = kcalloc(num_closid, sizeof(*tmp), GFP_KERNEL); + if (!tmp) { + mutex_unlock(&rdtgroup_mutex); + return -ENOMEM; + } + + closid_num_dirty_rmid = tmp; + + mutex_unlock(&rdtgroup_mutex); + } + + return 0; +} + +static void closid_num_dirty_rmid_free(void) +{ + if (IS_ENABLED(CONFIG_RESCTRL_RMID_DEPENDS_ON_CLOSID)) { + mutex_lock(&rdtgroup_mutex); + kfree(closid_num_dirty_rmid); + closid_num_dirty_rmid = NULL; + mutex_unlock(&rdtgroup_mutex); + } +} + /** - * resctrl_mon_resource_init() - Initialise global monitoring structures. + * resctrl_l3_mon_resource_init() - Initialise global monitoring structures. * * Allocate and initialise global monitor resources that do not belong to a - * specific domain. i.e. the rmid_ptrs[] used for the limbo and free lists. + * specific domain. i.e. the closid_num_dirty_rmid[] used to find the CLOSID + * with the cleanest set of RMIDs. * Called once during boot after the struct rdt_resource's have been configured * but before the filesystem is mounted. * Resctrl's cpuhp callbacks may be called before this point to bring a domain * online. * - * Returns 0 for success, or -ENOMEM. + * Return: 0 for success, or -ENOMEM. */ -int resctrl_mon_resource_init(void) +int resctrl_l3_mon_resource_init(void) { struct rdt_resource *r = resctrl_arch_get_resource(RDT_RESOURCE_L3); int ret; @@ -1761,7 +1850,7 @@ int resctrl_mon_resource_init(void) if (!r->mon_capable) return 0; - ret = dom_data_init(r); + ret = closid_num_dirty_rmid_alloc(r); if (ret) return ret; @@ -1803,9 +1892,12 @@ int resctrl_mon_resource_init(void) return 0; } -void resctrl_mon_resource_exit(void) +void resctrl_l3_mon_resource_exit(void) { struct rdt_resource *r = resctrl_arch_get_resource(RDT_RESOURCE_L3); - dom_data_exit(r); + if (!r->mon_capable) + return; + + closid_num_dirty_rmid_free(); } diff --git a/fs/resctrl/rdtgroup.c b/fs/resctrl/rdtgroup.c index 8e39dfda56bc..ba8d503551cd 100644 --- a/fs/resctrl/rdtgroup.c +++ b/fs/resctrl/rdtgroup.c @@ -18,6 +18,7 @@ #include <linux/fs_parser.h> #include <linux/sysfs.h> #include <linux/kernfs.h> +#include <linux/once.h> #include <linux/resctrl.h> #include <linux/seq_buf.h> #include <linux/seq_file.h> @@ -1157,7 +1158,7 @@ static int rdt_num_rmids_show(struct kernfs_open_file *of, { struct rdt_resource *r = rdt_kn_parent_priv(of->kn); - seq_printf(seq, "%d\n", r->mon.num_rmid); + seq_printf(seq, "%u\n", r->mon.num_rmid); return 0; } @@ -1640,7 +1641,7 @@ static void mondata_config_read(struct resctrl_mon_config_info *mon_info) static int mbm_config_show(struct seq_file *s, struct rdt_resource *r, u32 evtid) { struct resctrl_mon_config_info mon_info; - struct rdt_mon_domain *dom; + struct rdt_l3_mon_domain *dom; bool sep = false; cpus_read_lock(); @@ -1688,7 +1689,7 @@ static int mbm_local_bytes_config_show(struct kernfs_open_file *of, } static void mbm_config_write_domain(struct rdt_resource *r, - struct rdt_mon_domain *d, u32 evtid, u32 val) + struct rdt_l3_mon_domain *d, u32 evtid, u32 val) { struct resctrl_mon_config_info mon_info = {0}; @@ -1729,8 +1730,8 @@ static void mbm_config_write_domain(struct rdt_resource *r, static int mon_config_write(struct rdt_resource *r, char *tok, u32 evtid) { char *dom_str = NULL, *id_str; + struct rdt_l3_mon_domain *d; unsigned long dom_id, val; - struct rdt_mon_domain *d; /* Walking r->domains, ensure it can't race with cpuhp */ lockdep_assert_cpus_held(); @@ -2395,6 +2396,8 @@ static unsigned long fflags_from_resource(struct rdt_resource *r) case RDT_RESOURCE_MBA: case RDT_RESOURCE_SMBA: return RFTYPE_RES_MB; + case RDT_RESOURCE_PERF_PKG: + return RFTYPE_RES_PERF_PKG; } return WARN_ON_ONCE(1); @@ -2781,10 +2784,12 @@ static int rdt_get_tree(struct fs_context *fc) { struct rdt_fs_context *ctx = rdt_fc2context(fc); unsigned long flags = RFTYPE_CTRL_BASE; - struct rdt_mon_domain *dom; + struct rdt_l3_mon_domain *dom; struct rdt_resource *r; int ret; + DO_ONCE_SLEEPABLE(resctrl_arch_pre_mount); + cpus_read_lock(); mutex_lock(&rdtgroup_mutex); /* @@ -2795,6 +2800,10 @@ static int rdt_get_tree(struct fs_context *fc) goto out; } + ret = setup_rmid_lru_list(); + if (ret) + goto out; + ret = rdtgroup_setup_root(ctx); if (ret) goto out; @@ -3091,7 +3100,8 @@ static void rmdir_all_sub(void) * @rid: The resource id for the event file being created. * @domid: The domain id for the event file being created. * @mevt: The type of event file being created. - * @do_sum: Whether SNC summing monitors are being created. + * @do_sum: Whether SNC summing monitors are being created. Only set + * when @rid == RDT_RESOURCE_L3. */ static struct mon_data *mon_get_kn_priv(enum resctrl_res_level rid, int domid, struct mon_evt *mevt, @@ -3103,7 +3113,7 @@ static struct mon_data *mon_get_kn_priv(enum resctrl_res_level rid, int domid, list_for_each_entry(priv, &mon_data_kn_priv_list, list) { if (priv->rid == rid && priv->domid == domid && - priv->sum == do_sum && priv->evtid == mevt->evtid) + priv->sum == do_sum && priv->evt == mevt) return priv; } @@ -3114,7 +3124,7 @@ static struct mon_data *mon_get_kn_priv(enum resctrl_res_level rid, int domid, priv->rid = rid; priv->domid = domid; priv->sum = do_sum; - priv->evtid = mevt->evtid; + priv->evt = mevt; list_add_tail(&priv->list, &mon_data_kn_priv_list); return priv; @@ -3223,23 +3233,24 @@ static void mon_rmdir_one_subdir(struct kernfs_node *pkn, char *name, char *subn } /* - * Remove all subdirectories of mon_data of ctrl_mon groups - * and monitor groups for the given domain. - * Remove files and directories containing "sum" of domain data - * when last domain being summed is removed. + * Remove files and directories for one SNC node. If it is the last node + * sharing an L3 cache, then remove the upper level directory containing + * the "sum" files too. */ -static void rmdir_mondata_subdir_allrdtgrp(struct rdt_resource *r, - struct rdt_mon_domain *d) +static void rmdir_mondata_subdir_allrdtgrp_snc(struct rdt_resource *r, + struct rdt_domain_hdr *hdr) { struct rdtgroup *prgrp, *crgrp; + struct rdt_l3_mon_domain *d; char subname[32]; - bool snc_mode; char name[32]; - snc_mode = r->mon_scope == RESCTRL_L3_NODE; - sprintf(name, "mon_%s_%02d", r->name, snc_mode ? d->ci_id : d->hdr.id); - if (snc_mode) - sprintf(subname, "mon_sub_%s_%02d", r->name, d->hdr.id); + if (!domain_header_is_valid(hdr, RESCTRL_MON_DOMAIN, RDT_RESOURCE_L3)) + return; + + d = container_of(hdr, struct rdt_l3_mon_domain, hdr); + sprintf(name, "mon_%s_%02d", r->name, d->ci_id); + sprintf(subname, "mon_sub_%s_%02d", r->name, hdr->id); list_for_each_entry(prgrp, &rdt_all_groups, rdtgroup_list) { mon_rmdir_one_subdir(prgrp->mon.mon_data_kn, name, subname); @@ -3249,47 +3260,89 @@ static void rmdir_mondata_subdir_allrdtgrp(struct rdt_resource *r, } } -static int mon_add_all_files(struct kernfs_node *kn, struct rdt_mon_domain *d, - struct rdt_resource *r, struct rdtgroup *prgrp, - bool do_sum) +/* + * Remove all subdirectories of mon_data of ctrl_mon groups + * and monitor groups for the given domain. + */ +static void rmdir_mondata_subdir_allrdtgrp(struct rdt_resource *r, + struct rdt_domain_hdr *hdr) +{ + struct rdtgroup *prgrp, *crgrp; + char name[32]; + + if (r->rid == RDT_RESOURCE_L3 && r->mon_scope == RESCTRL_L3_NODE) { + rmdir_mondata_subdir_allrdtgrp_snc(r, hdr); + return; + } + + sprintf(name, "mon_%s_%02d", r->name, hdr->id); + list_for_each_entry(prgrp, &rdt_all_groups, rdtgroup_list) { + kernfs_remove_by_name(prgrp->mon.mon_data_kn, name); + + list_for_each_entry(crgrp, &prgrp->mon.crdtgrp_list, mon.crdtgrp_list) + kernfs_remove_by_name(crgrp->mon.mon_data_kn, name); + } +} + +/* + * Create a directory for a domain and populate it with monitor files. Create + * summing monitors when @hdr is NULL. No need to initialize summing monitors. + */ +static struct kernfs_node *_mkdir_mondata_subdir(struct kernfs_node *parent_kn, char *name, + struct rdt_domain_hdr *hdr, + struct rdt_resource *r, + struct rdtgroup *prgrp, int domid) { struct rmid_read rr = {0}; + struct kernfs_node *kn; struct mon_data *priv; struct mon_evt *mevt; - int ret, domid; + int ret; + + kn = kernfs_create_dir(parent_kn, name, parent_kn->mode, prgrp); + if (IS_ERR(kn)) + return kn; + + ret = rdtgroup_kn_set_ugid(kn); + if (ret) + goto out_destroy; for_each_mon_event(mevt) { if (mevt->rid != r->rid || !mevt->enabled) continue; - domid = do_sum ? d->ci_id : d->hdr.id; - priv = mon_get_kn_priv(r->rid, domid, mevt, do_sum); - if (WARN_ON_ONCE(!priv)) - return -EINVAL; + priv = mon_get_kn_priv(r->rid, domid, mevt, !hdr); + if (WARN_ON_ONCE(!priv)) { + ret = -EINVAL; + goto out_destroy; + } ret = mon_addfile(kn, mevt->name, priv); if (ret) - return ret; + goto out_destroy; - if (!do_sum && resctrl_is_mbm_event(mevt->evtid)) - mon_event_read(&rr, r, d, prgrp, &d->hdr.cpu_mask, mevt->evtid, true); + if (hdr && resctrl_is_mbm_event(mevt->evtid)) + mon_event_read(&rr, r, hdr, prgrp, &hdr->cpu_mask, mevt, true); } - return 0; + return kn; +out_destroy: + kernfs_remove(kn); + return ERR_PTR(ret); } -static int mkdir_mondata_subdir(struct kernfs_node *parent_kn, - struct rdt_mon_domain *d, - struct rdt_resource *r, struct rdtgroup *prgrp) +static int mkdir_mondata_subdir_snc(struct kernfs_node *parent_kn, + struct rdt_domain_hdr *hdr, + struct rdt_resource *r, struct rdtgroup *prgrp) { - struct kernfs_node *kn, *ckn; + struct kernfs_node *ckn, *kn; + struct rdt_l3_mon_domain *d; char name[32]; - bool snc_mode; - int ret = 0; - lockdep_assert_held(&rdtgroup_mutex); + if (!domain_header_is_valid(hdr, RESCTRL_MON_DOMAIN, RDT_RESOURCE_L3)) + return -EINVAL; - snc_mode = r->mon_scope == RESCTRL_L3_NODE; - sprintf(name, "mon_%s_%02d", r->name, snc_mode ? d->ci_id : d->hdr.id); + d = container_of(hdr, struct rdt_l3_mon_domain, hdr); + sprintf(name, "mon_%s_%02d", r->name, d->ci_id); kn = kernfs_find_and_get(parent_kn, name); if (kn) { /* @@ -3298,41 +3351,41 @@ static int mkdir_mondata_subdir(struct kernfs_node *parent_kn, */ kernfs_put(kn); } else { - kn = kernfs_create_dir(parent_kn, name, parent_kn->mode, prgrp); + kn = _mkdir_mondata_subdir(parent_kn, name, NULL, r, prgrp, d->ci_id); if (IS_ERR(kn)) return PTR_ERR(kn); + } - ret = rdtgroup_kn_set_ugid(kn); - if (ret) - goto out_destroy; - ret = mon_add_all_files(kn, d, r, prgrp, snc_mode); - if (ret) - goto out_destroy; + sprintf(name, "mon_sub_%s_%02d", r->name, hdr->id); + ckn = _mkdir_mondata_subdir(kn, name, hdr, r, prgrp, hdr->id); + if (IS_ERR(ckn)) { + kernfs_remove(kn); + return PTR_ERR(ckn); } - if (snc_mode) { - sprintf(name, "mon_sub_%s_%02d", r->name, d->hdr.id); - ckn = kernfs_create_dir(kn, name, parent_kn->mode, prgrp); - if (IS_ERR(ckn)) { - ret = -EINVAL; - goto out_destroy; - } + kernfs_activate(kn); + return 0; +} - ret = rdtgroup_kn_set_ugid(ckn); - if (ret) - goto out_destroy; +static int mkdir_mondata_subdir(struct kernfs_node *parent_kn, + struct rdt_domain_hdr *hdr, + struct rdt_resource *r, struct rdtgroup *prgrp) +{ + struct kernfs_node *kn; + char name[32]; - ret = mon_add_all_files(ckn, d, r, prgrp, false); - if (ret) - goto out_destroy; - } + lockdep_assert_held(&rdtgroup_mutex); + + if (r->rid == RDT_RESOURCE_L3 && r->mon_scope == RESCTRL_L3_NODE) + return mkdir_mondata_subdir_snc(parent_kn, hdr, r, prgrp); + + sprintf(name, "mon_%s_%02d", r->name, hdr->id); + kn = _mkdir_mondata_subdir(parent_kn, name, hdr, r, prgrp, hdr->id); + if (IS_ERR(kn)) + return PTR_ERR(kn); kernfs_activate(kn); return 0; - -out_destroy: - kernfs_remove(kn); - return ret; } /* @@ -3340,7 +3393,7 @@ out_destroy: * and "monitor" groups with given domain id. */ static void mkdir_mondata_subdir_allrdtgrp(struct rdt_resource *r, - struct rdt_mon_domain *d) + struct rdt_domain_hdr *hdr) { struct kernfs_node *parent_kn; struct rdtgroup *prgrp, *crgrp; @@ -3348,12 +3401,12 @@ static void mkdir_mondata_subdir_allrdtgrp(struct rdt_resource *r, list_for_each_entry(prgrp, &rdt_all_groups, rdtgroup_list) { parent_kn = prgrp->mon.mon_data_kn; - mkdir_mondata_subdir(parent_kn, d, r, prgrp); + mkdir_mondata_subdir(parent_kn, hdr, r, prgrp); head = &prgrp->mon.crdtgrp_list; list_for_each_entry(crgrp, head, mon.crdtgrp_list) { parent_kn = crgrp->mon.mon_data_kn; - mkdir_mondata_subdir(parent_kn, d, r, crgrp); + mkdir_mondata_subdir(parent_kn, hdr, r, crgrp); } } } @@ -3362,14 +3415,14 @@ static int mkdir_mondata_subdir_alldom(struct kernfs_node *parent_kn, struct rdt_resource *r, struct rdtgroup *prgrp) { - struct rdt_mon_domain *dom; + struct rdt_domain_hdr *hdr; int ret; /* Walking r->domains, ensure it can't race with cpuhp */ lockdep_assert_cpus_held(); - list_for_each_entry(dom, &r->mon_domains, hdr.list) { - ret = mkdir_mondata_subdir(parent_kn, dom, r, prgrp); + list_for_each_entry(hdr, &r->mon_domains, list) { + ret = mkdir_mondata_subdir(parent_kn, hdr, r, prgrp); if (ret) return ret; } @@ -4231,7 +4284,7 @@ static void rdtgroup_setup_default(void) mutex_unlock(&rdtgroup_mutex); } -static void domain_destroy_mon_state(struct rdt_mon_domain *d) +static void domain_destroy_l3_mon_state(struct rdt_l3_mon_domain *d) { int idx; @@ -4253,8 +4306,10 @@ void resctrl_offline_ctrl_domain(struct rdt_resource *r, struct rdt_ctrl_domain mutex_unlock(&rdtgroup_mutex); } -void resctrl_offline_mon_domain(struct rdt_resource *r, struct rdt_mon_domain *d) +void resctrl_offline_mon_domain(struct rdt_resource *r, struct rdt_domain_hdr *hdr) { + struct rdt_l3_mon_domain *d; + mutex_lock(&rdtgroup_mutex); /* @@ -4262,8 +4317,15 @@ void resctrl_offline_mon_domain(struct rdt_resource *r, struct rdt_mon_domain *d * per domain monitor data directories. */ if (resctrl_mounted && resctrl_arch_mon_capable()) - rmdir_mondata_subdir_allrdtgrp(r, d); + rmdir_mondata_subdir_allrdtgrp(r, hdr); + + if (r->rid != RDT_RESOURCE_L3) + goto out_unlock; + if (!domain_header_is_valid(hdr, RESCTRL_MON_DOMAIN, RDT_RESOURCE_L3)) + goto out_unlock; + + d = container_of(hdr, struct rdt_l3_mon_domain, hdr); if (resctrl_is_mbm_enabled()) cancel_delayed_work(&d->mbm_over); if (resctrl_is_mon_event_enabled(QOS_L3_OCCUP_EVENT_ID) && has_busy_rmid(d)) { @@ -4279,13 +4341,13 @@ void resctrl_offline_mon_domain(struct rdt_resource *r, struct rdt_mon_domain *d cancel_delayed_work(&d->cqm_limbo); } - domain_destroy_mon_state(d); - + domain_destroy_l3_mon_state(d); +out_unlock: mutex_unlock(&rdtgroup_mutex); } /** - * domain_setup_mon_state() - Initialise domain monitoring structures. + * domain_setup_l3_mon_state() - Initialise domain monitoring structures. * @r: The resource for the newly online domain. * @d: The newly online domain. * @@ -4293,11 +4355,17 @@ void resctrl_offline_mon_domain(struct rdt_resource *r, struct rdt_mon_domain *d * Called when the first CPU of a domain comes online, regardless of whether * the filesystem is mounted. * During boot this may be called before global allocations have been made by - * resctrl_mon_resource_init(). + * resctrl_l3_mon_resource_init(). + * + * Called during CPU online that may run as soon as CPU online callbacks + * are set up during resctrl initialization. The number of supported RMIDs + * may be reduced if additional mon_capable resources are enumerated + * at mount time. This means the rdt_l3_mon_domain::mbm_states[] and + * rdt_l3_mon_domain::rmid_busy_llc allocations may be larger than needed. * - * Returns 0 for success, or -ENOMEM. + * Return: 0 for success, or -ENOMEM. */ -static int domain_setup_mon_state(struct rdt_resource *r, struct rdt_mon_domain *d) +static int domain_setup_l3_mon_state(struct rdt_resource *r, struct rdt_l3_mon_domain *d) { u32 idx_limit = resctrl_arch_system_num_rmid_idx(); size_t tsize = sizeof(*d->mbm_states[0]); @@ -4353,13 +4421,21 @@ int resctrl_online_ctrl_domain(struct rdt_resource *r, struct rdt_ctrl_domain *d return err; } -int resctrl_online_mon_domain(struct rdt_resource *r, struct rdt_mon_domain *d) +int resctrl_online_mon_domain(struct rdt_resource *r, struct rdt_domain_hdr *hdr) { - int err; + struct rdt_l3_mon_domain *d; + int err = -EINVAL; mutex_lock(&rdtgroup_mutex); - err = domain_setup_mon_state(r, d); + if (r->rid != RDT_RESOURCE_L3) + goto mkdir; + + if (!domain_header_is_valid(hdr, RESCTRL_MON_DOMAIN, RDT_RESOURCE_L3)) + goto out_unlock; + + d = container_of(hdr, struct rdt_l3_mon_domain, hdr); + err = domain_setup_l3_mon_state(r, d); if (err) goto out_unlock; @@ -4372,6 +4448,8 @@ int resctrl_online_mon_domain(struct rdt_resource *r, struct rdt_mon_domain *d) if (resctrl_is_mon_event_enabled(QOS_L3_OCCUP_EVENT_ID)) INIT_DELAYED_WORK(&d->cqm_limbo, cqm_handle_limbo); +mkdir: + err = 0; /* * If the filesystem is not mounted then only the default resource group * exists. Creation of its directories is deferred until mount time @@ -4379,7 +4457,7 @@ int resctrl_online_mon_domain(struct rdt_resource *r, struct rdt_mon_domain *d) * If resctrl is mounted, add per domain monitor data directories. */ if (resctrl_mounted && resctrl_arch_mon_capable()) - mkdir_mondata_subdir_allrdtgrp(r, d); + mkdir_mondata_subdir_allrdtgrp(r, hdr); out_unlock: mutex_unlock(&rdtgroup_mutex); @@ -4405,10 +4483,10 @@ static void clear_childcpus(struct rdtgroup *r, unsigned int cpu) } } -static struct rdt_mon_domain *get_mon_domain_from_cpu(int cpu, - struct rdt_resource *r) +static struct rdt_l3_mon_domain *get_mon_domain_from_cpu(int cpu, + struct rdt_resource *r) { - struct rdt_mon_domain *d; + struct rdt_l3_mon_domain *d; lockdep_assert_cpus_held(); @@ -4424,7 +4502,7 @@ static struct rdt_mon_domain *get_mon_domain_from_cpu(int cpu, void resctrl_offline_cpu(unsigned int cpu) { struct rdt_resource *l3 = resctrl_arch_get_resource(RDT_RESOURCE_L3); - struct rdt_mon_domain *d; + struct rdt_l3_mon_domain *d; struct rdtgroup *rdtgrp; mutex_lock(&rdtgroup_mutex); @@ -4476,13 +4554,13 @@ int resctrl_init(void) io_alloc_init(); - ret = resctrl_mon_resource_init(); + ret = resctrl_l3_mon_resource_init(); if (ret) return ret; ret = sysfs_create_mount_point(fs_kobj, "resctrl"); if (ret) { - resctrl_mon_resource_exit(); + resctrl_l3_mon_resource_exit(); return ret; } @@ -4517,7 +4595,7 @@ int resctrl_init(void) cleanup_mountpoint: sysfs_remove_mount_point(fs_kobj, "resctrl"); - resctrl_mon_resource_exit(); + resctrl_l3_mon_resource_exit(); return ret; } @@ -4553,7 +4631,7 @@ static bool resctrl_online_domains_exist(void) * When called by the architecture code, all CPUs and resctrl domains must be * offline. This ensures the limbo and overflow handlers are not scheduled to * run, meaning the data structures they access can be freed by - * resctrl_mon_resource_exit(). + * resctrl_l3_mon_resource_exit(). * * After resctrl_exit() returns, the architecture code should return an * error from all resctrl_arch_ functions that can do this. @@ -4580,5 +4658,6 @@ void resctrl_exit(void) * it can be used to umount resctrl. */ - resctrl_mon_resource_exit(); + resctrl_l3_mon_resource_exit(); + free_rmid_lru_list(); } diff --git a/fs/romfs/super.c b/fs/romfs/super.c index 360b00854115..ac55193bf398 100644 --- a/fs/romfs/super.c +++ b/fs/romfs/super.c @@ -458,7 +458,10 @@ static int romfs_fill_super(struct super_block *sb, struct fs_context *fc) #ifdef CONFIG_BLOCK if (!sb->s_mtd) { - sb_set_blocksize(sb, ROMBSIZE); + if (!sb_set_blocksize(sb, ROMBSIZE)) { + errorf(fc, "romfs: unable to set blocksize\n"); + return -EINVAL; + } } else { sb->s_blocksize = ROMBSIZE; sb->s_blocksize_bits = blksize_bits(ROMBSIZE); diff --git a/fs/select.c b/fs/select.c index 65019b8ba3f7..78a1508c84d3 100644 --- a/fs/select.c +++ b/fs/select.c @@ -1038,14 +1038,11 @@ static long do_restart_poll(struct restart_block *restart_block) { struct pollfd __user *ufds = restart_block->poll.ufds; int nfds = restart_block->poll.nfds; - struct timespec64 *to = NULL, end_time; + struct timespec64 *to = NULL; int ret; - if (restart_block->poll.has_timeout) { - end_time.tv_sec = restart_block->poll.tv_sec; - end_time.tv_nsec = restart_block->poll.tv_nsec; - to = &end_time; - } + if (restart_block->poll.has_timeout) + to = &restart_block->poll.end_time; ret = do_sys_poll(ufds, nfds, to); @@ -1077,8 +1074,7 @@ SYSCALL_DEFINE3(poll, struct pollfd __user *, ufds, unsigned int, nfds, restart_block->poll.nfds = nfds; if (timeout_msecs >= 0) { - restart_block->poll.tv_sec = end_time.tv_sec; - restart_block->poll.tv_nsec = end_time.tv_nsec; + restart_block->poll.end_time = end_time; restart_block->poll.has_timeout = 1; } else restart_block->poll.has_timeout = 0; diff --git a/fs/seq_file.c b/fs/seq_file.c index 8bbb1ad46335..8894cbde8d3a 100644 --- a/fs/seq_file.c +++ b/fs/seq_file.c @@ -11,6 +11,7 @@ #include <linux/cache.h> #include <linux/fs.h> #include <linux/export.h> +#include <linux/hex.h> #include <linux/seq_file.h> #include <linux/vmalloc.h> #include <linux/slab.h> diff --git a/fs/smb/Kconfig b/fs/smb/Kconfig index ef425789fa6a..85f7ad5fbc5e 100644 --- a/fs/smb/Kconfig +++ b/fs/smb/Kconfig @@ -9,3 +9,20 @@ config SMBFS tristate default y if CIFS=y || SMB_SERVER=y default m if CIFS=m || SMB_SERVER=m + +config SMB_KUNIT_TESTS + tristate "KUnit tests for SMB" if !KUNIT_ALL_TESTS + depends on SMBFS && KUNIT + default KUNIT_ALL_TESTS + help + This builds the SMB KUnit tests. + + KUnit tests run during boot and output the results to the debug log + in TAP format (https://testanything.org/). Only useful for kernel devs + running KUnit test harness and are not for inclusion into a production + build. + + For more information on KUnit and unit tests in general please refer + to the KUnit documentation in Documentation/dev-tools/kunit/. + + If unsure, say N. diff --git a/fs/smb/client/.gitignore b/fs/smb/client/.gitignore new file mode 100644 index 000000000000..8a6e04ab62b9 --- /dev/null +++ b/fs/smb/client/.gitignore @@ -0,0 +1 @@ +smb2_mapping_table.c diff --git a/fs/smb/client/Makefile b/fs/smb/client/Makefile index 4c97b31a25c2..3abd357d6df6 100644 --- a/fs/smb/client/Makefile +++ b/fs/smb/client/Makefile @@ -32,6 +32,28 @@ cifs-$(CONFIG_CIFS_SMB_DIRECT) += smbdirect.o cifs-$(CONFIG_CIFS_ROOT) += cifsroot.o -cifs-$(CONFIG_CIFS_ALLOW_INSECURE_LEGACY) += smb1ops.o cifssmb.o cifstransport.o +cifs-$(CONFIG_CIFS_ALLOW_INSECURE_LEGACY) += \ + cifssmb.o \ + smb1debug.o \ + smb1encrypt.o \ + smb1maperror.o \ + smb1misc.o \ + smb1ops.o \ + smb1session.o \ + smb1transport.o cifs-$(CONFIG_CIFS_COMPRESSION) += compress.o compress/lz77.o + +# +# Build the SMB2 error mapping table from smb2status.h +# +$(obj)/smb2_mapping_table.c: $(src)/../common/smb2status.h \ + $(src)/gen_smb2_mapping + $(call cmd,gen_smb2_mapping) + +$(obj)/smb2maperror.o: $(obj)/smb2_mapping_table.c + +quiet_cmd_gen_smb2_mapping = GEN $@ + cmd_gen_smb2_mapping = perl $(src)/gen_smb2_mapping $< $@ + +clean-files += smb2_mapping_table.c diff --git a/fs/smb/client/cached_dir.c b/fs/smb/client/cached_dir.c index 1db7ab6c2529..2a6b8ce80be2 100644 --- a/fs/smb/client/cached_dir.c +++ b/fs/smb/client/cached_dir.c @@ -154,7 +154,7 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon, struct cached_fid *cfid; struct cached_fids *cfids; const char *npath; - int retries = 0, cur_sleep = 1; + int retries = 0, cur_sleep = 0; __le32 lease_flags = 0; if (cifs_sb->root == NULL) @@ -304,6 +304,10 @@ replay_again: smb2_set_related(&rqst[1]); if (retries) { + /* Back-off before retry */ + if (cur_sleep) + msleep(cur_sleep); + smb2_set_replay(server, &rqst[0]); smb2_set_replay(server, &rqst[1]); } @@ -788,11 +792,11 @@ static void cfids_laundromat_worker(struct work_struct *work) cfid->dentry = NULL; if (cfid->is_open) { - spin_lock(&cifs_tcp_ses_lock); + spin_lock(&cfid->tcon->tc_lock); ++cfid->tcon->tc_count; trace_smb3_tcon_ref(cfid->tcon->debug_id, cfid->tcon->tc_count, netfs_trace_tcon_ref_get_cached_laundromat); - spin_unlock(&cifs_tcp_ses_lock); + spin_unlock(&cfid->tcon->tc_lock); queue_work(serverclose_wq, &cfid->close_work); } else /* diff --git a/fs/smb/client/cached_dir.h b/fs/smb/client/cached_dir.h index 1e383db7c337..19d5592512e4 100644 --- a/fs/smb/client/cached_dir.h +++ b/fs/smb/client/cached_dir.h @@ -36,10 +36,10 @@ struct cached_fid { struct list_head entry; struct cached_fids *cfids; const char *path; - bool has_lease:1; - bool is_open:1; - bool on_list:1; - bool file_all_info_is_valid:1; + bool has_lease; + bool is_open; + bool on_list; + bool file_all_info_is_valid; unsigned long time; /* jiffies of when lease was taken */ unsigned long last_access_time; /* jiffies of when last accessed */ struct kref refcount; @@ -48,8 +48,10 @@ struct cached_fid { struct dentry *dentry; struct work_struct put_work; struct work_struct close_work; - struct smb2_file_all_info file_all_info; struct cached_dirents dirents; + + /* Must be last as it ends in a flexible-array member. */ + struct smb2_file_all_info file_all_info; }; /* default MAX_CACHED_FIDS is 16 */ @@ -77,22 +79,18 @@ is_valid_cached_dir(struct cached_fid *cfid) return cfid->time && cfid->has_lease; } -extern struct cached_fids *init_cached_dirs(void); -extern void free_cached_dirs(struct cached_fids *cfids); -extern int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon, - const char *path, - struct cifs_sb_info *cifs_sb, - bool lookup_only, struct cached_fid **cfid); -extern int open_cached_dir_by_dentry(struct cifs_tcon *tcon, - struct dentry *dentry, - struct cached_fid **cfid); -extern void close_cached_dir(struct cached_fid *cfid); -extern void drop_cached_dir_by_name(const unsigned int xid, - struct cifs_tcon *tcon, - const char *name, - struct cifs_sb_info *cifs_sb); -extern void close_all_cached_dirs(struct cifs_sb_info *cifs_sb); -extern void invalidate_all_cached_dirs(struct cifs_tcon *tcon); -extern bool cached_dir_lease_break(struct cifs_tcon *tcon, __u8 lease_key[16]); +struct cached_fids *init_cached_dirs(void); +void free_cached_dirs(struct cached_fids *cfids); +int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon, const char *path, + struct cifs_sb_info *cifs_sb, bool lookup_only, + struct cached_fid **ret_cfid); +int open_cached_dir_by_dentry(struct cifs_tcon *tcon, struct dentry *dentry, + struct cached_fid **ret_cfid); +void close_cached_dir(struct cached_fid *cfid); +void drop_cached_dir_by_name(const unsigned int xid, struct cifs_tcon *tcon, + const char *name, struct cifs_sb_info *cifs_sb); +void close_all_cached_dirs(struct cifs_sb_info *cifs_sb); +void invalidate_all_cached_dirs(struct cifs_tcon *tcon); +bool cached_dir_lease_break(struct cifs_tcon *tcon, __u8 lease_key[16]); #endif /* _CACHED_DIR_H */ diff --git a/fs/smb/client/cifs_debug.c b/fs/smb/client/cifs_debug.c index 2cb234d4bd2f..217444e3e6d0 100644 --- a/fs/smb/client/cifs_debug.c +++ b/fs/smb/client/cifs_debug.c @@ -13,7 +13,6 @@ #include <linux/proc_fs.h> #include <linux/uaccess.h> #include <uapi/linux/ethtool.h> -#include "cifspdu.h" #include "cifsglob.h" #include "cifsproto.h" #include "cifs_debug.h" @@ -37,21 +36,6 @@ cifs_dump_mem(char *label, void *data, int length) data, length, true); } -void cifs_dump_detail(void *buf, size_t buf_len, struct TCP_Server_Info *server) -{ -#ifdef CONFIG_CIFS_DEBUG2 - struct smb_hdr *smb = buf; - - cifs_dbg(VFS, "Cmd: %d Err: 0x%x Flags: 0x%x Flgs2: 0x%x Mid: %d Pid: %d Wct: %d\n", - smb->Command, smb->Status.CifsError, smb->Flags, - smb->Flags2, smb->Mid, smb->Pid, smb->WordCount); - if (!server->ops->check_message(buf, buf_len, server->total_read, server)) { - cifs_dbg(VFS, "smb buf %p len %u\n", smb, - server->ops->calc_smb_size(smb)); - } -#endif /* CONFIG_CIFS_DEBUG2 */ -} - void cifs_dump_mids(struct TCP_Server_Info *server) { #ifdef CONFIG_CIFS_DEBUG2 @@ -79,7 +63,7 @@ void cifs_dump_mids(struct TCP_Server_Info *server) cifs_dbg(VFS, "IsMult: %d IsEnd: %d\n", mid_entry->multiRsp, mid_entry->multiEnd); if (mid_entry->resp_buf) { - cifs_dump_detail(mid_entry->resp_buf, + server->ops->dump_detail(mid_entry->resp_buf, mid_entry->response_pdu_len, server); cifs_dump_mem("existing buf: ", mid_entry->resp_buf, 62); } diff --git a/fs/smb/client/cifs_debug.h b/fs/smb/client/cifs_debug.h index e0035ff42dba..00650929a133 100644 --- a/fs/smb/client/cifs_debug.h +++ b/fs/smb/client/cifs_debug.h @@ -15,7 +15,6 @@ #define pr_fmt(fmt) "CIFS: " fmt void cifs_dump_mem(char *label, void *data, int length); -void cifs_dump_detail(void *buf, size_t buf_len, struct TCP_Server_Info *server); void cifs_dump_mids(struct TCP_Server_Info *server); extern bool traceSMB; /* flag which enables the function below */ void dump_smb(void *buf, int smb_buf_length); diff --git a/fs/smb/client/cifs_spnego.h b/fs/smb/client/cifs_spnego.h index e70929db3611..987768348624 100644 --- a/fs/smb/client/cifs_spnego.h +++ b/fs/smb/client/cifs_spnego.h @@ -28,7 +28,7 @@ struct cifs_spnego_msg { }; extern struct key_type cifs_spnego_key_type; -extern struct key *cifs_get_spnego_key(struct cifs_ses *sesInfo, - struct TCP_Server_Info *server); +struct key *cifs_get_spnego_key(struct cifs_ses *sesInfo, + struct TCP_Server_Info *server); #endif /* _CIFS_SPNEGO_H */ diff --git a/fs/smb/client/cifs_swn.h b/fs/smb/client/cifs_swn.h index 8a9d2a5c9077..955d07b69450 100644 --- a/fs/smb/client/cifs_swn.h +++ b/fs/smb/client/cifs_swn.h @@ -14,15 +14,15 @@ struct sk_buff; struct genl_info; #ifdef CONFIG_CIFS_SWN_UPCALL -extern int cifs_swn_register(struct cifs_tcon *tcon); +int cifs_swn_register(struct cifs_tcon *tcon); -extern int cifs_swn_unregister(struct cifs_tcon *tcon); +int cifs_swn_unregister(struct cifs_tcon *tcon); -extern int cifs_swn_notify(struct sk_buff *skb, struct genl_info *info); +int cifs_swn_notify(struct sk_buff *skb, struct genl_info *info); -extern void cifs_swn_dump(struct seq_file *m); +void cifs_swn_dump(struct seq_file *m); -extern void cifs_swn_check(void); +void cifs_swn_check(void); static inline bool cifs_swn_set_server_dstaddr(struct TCP_Server_Info *server) { diff --git a/fs/smb/client/cifs_unicode.c b/fs/smb/client/cifs_unicode.c index f8659d36793f..e7891b4406f2 100644 --- a/fs/smb/client/cifs_unicode.c +++ b/fs/smb/client/cifs_unicode.c @@ -8,7 +8,6 @@ #include <linux/slab.h> #include "cifs_fs_sb.h" #include "cifs_unicode.h" -#include "cifspdu.h" #include "cifsglob.h" #include "cifs_debug.h" diff --git a/fs/smb/client/cifs_unicode.h b/fs/smb/client/cifs_unicode.h index 6e4b99786498..9249db3b78c3 100644 --- a/fs/smb/client/cifs_unicode.h +++ b/fs/smb/client/cifs_unicode.h @@ -55,19 +55,20 @@ #define SFU_MAP_UNI_RSVD 2 int cifs_from_utf16(char *to, const __le16 *from, int tolen, int fromlen, - const struct nls_table *cp, int map_type); + const struct nls_table *codepage, int map_type); int cifs_utf16_bytes(const __le16 *from, int maxbytes, const struct nls_table *codepage); -int cifs_strtoUTF16(__le16 *, const char *, int, const struct nls_table *); +int cifs_strtoUTF16(__le16 *to, const char *from, int len, + const struct nls_table *codepage); char *cifs_strndup_from_utf16(const char *src, const int maxlen, const bool is_unicode, const struct nls_table *codepage); -extern int cifsConvertToUTF16(__le16 *target, const char *source, int maxlen, - const struct nls_table *cp, int mapChars); -extern int cifs_remap(struct cifs_sb_info *cifs_sb); -extern __le16 *cifs_strndup_to_utf16(const char *src, const int maxlen, - int *utf16_len, const struct nls_table *cp, - int remap); +int cifsConvertToUTF16(__le16 *target, const char *source, int srclen, + const struct nls_table *cp, int map_chars); +int cifs_remap(struct cifs_sb_info *cifs_sb); +__le16 *cifs_strndup_to_utf16(const char *src, const int maxlen, + int *utf16_len, const struct nls_table *cp, + int remap); wchar_t cifs_toupper(wchar_t in); #endif /* _CIFS_UNICODE_H */ diff --git a/fs/smb/client/cifsacl.c b/fs/smb/client/cifsacl.c index 7e6e473bd4a0..f03eda46f452 100644 --- a/fs/smb/client/cifsacl.c +++ b/fs/smb/client/cifsacl.c @@ -17,7 +17,6 @@ #include <linux/posix_acl.h> #include <linux/posix_acl_xattr.h> #include <keys/user-type.h> -#include "cifspdu.h" #include "cifsglob.h" #include "cifsacl.h" #include "cifsproto.h" diff --git a/fs/smb/client/cifsencrypt.c b/fs/smb/client/cifsencrypt.c index ca2a84e8673e..50b7ec39053c 100644 --- a/fs/smb/client/cifsencrypt.c +++ b/fs/smb/client/cifsencrypt.c @@ -11,7 +11,6 @@ #include <linux/fs.h> #include <linux/slab.h> -#include "cifspdu.h" #include "cifsglob.h" #include "cifs_debug.h" #include "cifs_unicode.h" @@ -116,129 +115,6 @@ int __cifs_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server, return rc; } -/* - * Calculate and return the CIFS signature based on the mac key and SMB PDU. - * The 16 byte signature must be allocated by the caller. Note we only use the - * 1st eight bytes and that the smb header signature field on input contains - * the sequence number before this function is called. Also, this function - * should be called with the server->srv_mutex held. - */ -static int cifs_calc_signature(struct smb_rqst *rqst, - struct TCP_Server_Info *server, char *signature) -{ - struct md5_ctx ctx; - - if (!rqst->rq_iov || !signature || !server) - return -EINVAL; - if (fips_enabled) { - cifs_dbg(VFS, - "MD5 signature support is disabled due to FIPS\n"); - return -EOPNOTSUPP; - } - - md5_init(&ctx); - md5_update(&ctx, server->session_key.response, server->session_key.len); - - return __cifs_calc_signature( - rqst, server, signature, - &(struct cifs_calc_sig_ctx){ .md5 = &ctx }); -} - -/* must be called with server->srv_mutex held */ -int cifs_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server, - __u32 *pexpected_response_sequence_number) -{ - int rc = 0; - char smb_signature[20]; - struct smb_hdr *cifs_pdu = (struct smb_hdr *)rqst->rq_iov[0].iov_base; - - if ((cifs_pdu == NULL) || (server == NULL)) - return -EINVAL; - - spin_lock(&server->srv_lock); - if (!(cifs_pdu->Flags2 & SMBFLG2_SECURITY_SIGNATURE) || - server->tcpStatus == CifsNeedNegotiate) { - spin_unlock(&server->srv_lock); - return rc; - } - spin_unlock(&server->srv_lock); - - if (!server->session_estab) { - memcpy(cifs_pdu->Signature.SecuritySignature, "BSRSPYL", 8); - return rc; - } - - cifs_pdu->Signature.Sequence.SequenceNumber = - cpu_to_le32(server->sequence_number); - cifs_pdu->Signature.Sequence.Reserved = 0; - - *pexpected_response_sequence_number = ++server->sequence_number; - ++server->sequence_number; - - rc = cifs_calc_signature(rqst, server, smb_signature); - if (rc) - memset(cifs_pdu->Signature.SecuritySignature, 0, 8); - else - memcpy(cifs_pdu->Signature.SecuritySignature, smb_signature, 8); - - return rc; -} - -int cifs_verify_signature(struct smb_rqst *rqst, - struct TCP_Server_Info *server, - __u32 expected_sequence_number) -{ - unsigned int rc; - char server_response_sig[8]; - char what_we_think_sig_should_be[20]; - struct smb_hdr *cifs_pdu = (struct smb_hdr *)rqst->rq_iov[0].iov_base; - - if (cifs_pdu == NULL || server == NULL) - return -EINVAL; - - if (!server->session_estab) - return 0; - - if (cifs_pdu->Command == SMB_COM_LOCKING_ANDX) { - struct smb_com_lock_req *pSMB = - (struct smb_com_lock_req *)cifs_pdu; - if (pSMB->LockType & LOCKING_ANDX_OPLOCK_RELEASE) - return 0; - } - - /* BB what if signatures are supposed to be on for session but - server does not send one? BB */ - - /* Do not need to verify session setups with signature "BSRSPYL " */ - if (memcmp(cifs_pdu->Signature.SecuritySignature, "BSRSPYL ", 8) == 0) - cifs_dbg(FYI, "dummy signature received for smb command 0x%x\n", - cifs_pdu->Command); - - /* save off the original signature so we can modify the smb and check - its signature against what the server sent */ - memcpy(server_response_sig, cifs_pdu->Signature.SecuritySignature, 8); - - cifs_pdu->Signature.Sequence.SequenceNumber = - cpu_to_le32(expected_sequence_number); - cifs_pdu->Signature.Sequence.Reserved = 0; - - cifs_server_lock(server); - rc = cifs_calc_signature(rqst, server, what_we_think_sig_should_be); - cifs_server_unlock(server); - - if (rc) - return rc; - -/* cifs_dump_mem("what we think it should be: ", - what_we_think_sig_should_be, 16); */ - - if (memcmp(server_response_sig, what_we_think_sig_should_be, 8)) - return -EACCES; - else - return 0; - -} - /* Build a proper attribute value/target info pairs blob. * Fill in netbios and dns domain name and workstation name * and client time (total five av pairs and + one end of fields indicator. diff --git a/fs/smb/client/cifsfs.c b/fs/smb/client/cifsfs.c index d9664634144d..afda1d7c1ee1 100644 --- a/fs/smb/client/cifsfs.c +++ b/fs/smb/client/cifsfs.c @@ -33,7 +33,6 @@ #include <uapi/linux/magic.h> #include <net/ipv6.h> #include "cifsfs.h" -#include "cifspdu.h" #define DECLARE_GLOBALS_HERE #include "cifsglob.h" #include "cifsproto.h" @@ -1149,9 +1148,6 @@ cifs_setlease(struct file *file, int arg, struct file_lease **lease, void **priv struct inode *inode = file_inode(file); struct cifsFileInfo *cfile = file->private_data; - if (!S_ISREG(inode->i_mode)) - return -EINVAL; - /* Check if file is oplocked if this is request for new lease */ if (arg == F_UNLCK || ((arg == F_RDLCK) && CIFS_CACHE_READ(CIFS_I(inode))) || @@ -1907,6 +1903,11 @@ static int __init init_cifs(void) { int rc = 0; + + rc = smb2_init_maperror(); + if (rc) + return rc; + cifs_proc_init(); INIT_LIST_HEAD(&cifs_tcp_ses_list); /* diff --git a/fs/smb/client/cifsfs.h b/fs/smb/client/cifsfs.h index 75d372ceb655..121821e6c872 100644 --- a/fs/smb/client/cifsfs.h +++ b/fs/smb/client/cifsfs.h @@ -43,40 +43,41 @@ extern const struct address_space_operations cifs_addr_ops; extern const struct address_space_operations cifs_addr_ops_smallbuf; /* Functions related to super block operations */ -extern void cifs_sb_active(struct super_block *sb); -extern void cifs_sb_deactive(struct super_block *sb); +void cifs_sb_active(struct super_block *sb); +void cifs_sb_deactive(struct super_block *sb); /* Functions related to inodes */ extern const struct inode_operations cifs_dir_inode_ops; -extern struct inode *cifs_root_iget(struct super_block *); -extern int cifs_create(struct mnt_idmap *, struct inode *, - struct dentry *, umode_t, bool excl); -extern int cifs_atomic_open(struct inode *, struct dentry *, - struct file *, unsigned, umode_t); -extern struct dentry *cifs_lookup(struct inode *, struct dentry *, - unsigned int); -extern int cifs_unlink(struct inode *dir, struct dentry *dentry); -extern int cifs_hardlink(struct dentry *, struct inode *, struct dentry *); -extern int cifs_mknod(struct mnt_idmap *, struct inode *, struct dentry *, - umode_t, dev_t); -extern struct dentry *cifs_mkdir(struct mnt_idmap *, struct inode *, struct dentry *, - umode_t); -extern int cifs_rmdir(struct inode *, struct dentry *); -extern int cifs_rename2(struct mnt_idmap *, struct inode *, - struct dentry *, struct inode *, struct dentry *, - unsigned int); -extern int cifs_revalidate_file_attr(struct file *filp); -extern int cifs_revalidate_dentry_attr(struct dentry *); -extern int cifs_revalidate_file(struct file *filp); -extern int cifs_revalidate_dentry(struct dentry *); -extern int cifs_revalidate_mapping(struct inode *inode); -extern int cifs_zap_mapping(struct inode *inode); -extern int cifs_getattr(struct mnt_idmap *, const struct path *, - struct kstat *, u32, unsigned int); -extern int cifs_setattr(struct mnt_idmap *, struct dentry *, - struct iattr *); -extern int cifs_fiemap(struct inode *, struct fiemap_extent_info *, u64 start, - u64 len); +struct inode *cifs_root_iget(struct super_block *sb); +int cifs_create(struct mnt_idmap *idmap, struct inode *inode, + struct dentry *direntry, umode_t mode, bool excl); +int cifs_atomic_open(struct inode *inode, struct dentry *direntry, + struct file *file, unsigned int oflags, umode_t mode); +struct dentry *cifs_lookup(struct inode *parent_dir_inode, + struct dentry *direntry, unsigned int flags); +int cifs_unlink(struct inode *dir, struct dentry *dentry); +int cifs_hardlink(struct dentry *old_file, struct inode *inode, + struct dentry *direntry); +int cifs_mknod(struct mnt_idmap *idmap, struct inode *inode, + struct dentry *direntry, umode_t mode, dev_t device_number); +struct dentry *cifs_mkdir(struct mnt_idmap *idmap, struct inode *inode, + struct dentry *direntry, umode_t mode); +int cifs_rmdir(struct inode *inode, struct dentry *direntry); +int cifs_rename2(struct mnt_idmap *idmap, struct inode *source_dir, + struct dentry *source_dentry, struct inode *target_dir, + struct dentry *target_dentry, unsigned int flags); +int cifs_revalidate_file_attr(struct file *filp); +int cifs_revalidate_dentry_attr(struct dentry *dentry); +int cifs_revalidate_file(struct file *filp); +int cifs_revalidate_dentry(struct dentry *dentry); +int cifs_revalidate_mapping(struct inode *inode); +int cifs_zap_mapping(struct inode *inode); +int cifs_getattr(struct mnt_idmap *idmap, const struct path *path, + struct kstat *stat, u32 request_mask, unsigned int flags); +int cifs_setattr(struct mnt_idmap *idmap, struct dentry *direntry, + struct iattr *attrs); +int cifs_fiemap(struct inode *inode, struct fiemap_extent_info *fei, u64 start, + u64 len); extern const struct inode_operations cifs_file_inode_ops; extern const struct inode_operations cifs_symlink_inode_ops; @@ -91,54 +92,55 @@ extern const struct file_operations cifs_file_strict_ops; /* if strictio mnt */ extern const struct file_operations cifs_file_nobrl_ops; /* no brlocks */ extern const struct file_operations cifs_file_direct_nobrl_ops; extern const struct file_operations cifs_file_strict_nobrl_ops; -extern int cifs_open(struct inode *inode, struct file *file); -extern int cifs_close(struct inode *inode, struct file *file); -extern int cifs_closedir(struct inode *inode, struct file *file); -extern ssize_t cifs_strict_readv(struct kiocb *iocb, struct iov_iter *to); -extern ssize_t cifs_strict_writev(struct kiocb *iocb, struct iov_iter *from); +int cifs_open(struct inode *inode, struct file *file); +int cifs_close(struct inode *inode, struct file *file); +int cifs_closedir(struct inode *inode, struct file *file); +ssize_t cifs_strict_readv(struct kiocb *iocb, struct iov_iter *to); +ssize_t cifs_strict_writev(struct kiocb *iocb, struct iov_iter *from); ssize_t cifs_file_write_iter(struct kiocb *iocb, struct iov_iter *from); ssize_t cifs_loose_read_iter(struct kiocb *iocb, struct iov_iter *iter); -extern int cifs_flock(struct file *pfile, int cmd, struct file_lock *plock); -extern int cifs_lock(struct file *, int, struct file_lock *); -extern int cifs_fsync(struct file *, loff_t, loff_t, int); -extern int cifs_strict_fsync(struct file *, loff_t, loff_t, int); -extern int cifs_flush(struct file *, fl_owner_t id); +int cifs_flock(struct file *file, int cmd, struct file_lock *fl); +int cifs_lock(struct file *file, int cmd, struct file_lock *flock); +int cifs_fsync(struct file *file, loff_t start, loff_t end, int datasync); +int cifs_strict_fsync(struct file *file, loff_t start, loff_t end, + int datasync); +int cifs_flush(struct file *file, fl_owner_t id); int cifs_file_mmap_prepare(struct vm_area_desc *desc); int cifs_file_strict_mmap_prepare(struct vm_area_desc *desc); extern const struct file_operations cifs_dir_ops; -extern int cifs_readdir(struct file *file, struct dir_context *ctx); +int cifs_readdir(struct file *file, struct dir_context *ctx); /* Functions related to dir entries */ extern const struct dentry_operations cifs_dentry_ops; extern const struct dentry_operations cifs_ci_dentry_ops; -extern struct vfsmount *cifs_d_automount(struct path *path); +struct vfsmount *cifs_d_automount(struct path *path); /* Functions related to symlinks */ -extern const char *cifs_get_link(struct dentry *, struct inode *, - struct delayed_call *); -extern int cifs_symlink(struct mnt_idmap *idmap, struct inode *inode, - struct dentry *direntry, const char *symname); +const char *cifs_get_link(struct dentry *dentry, struct inode *inode, + struct delayed_call *done); +int cifs_symlink(struct mnt_idmap *idmap, struct inode *inode, + struct dentry *direntry, const char *symname); #ifdef CONFIG_CIFS_XATTR extern const struct xattr_handler * const cifs_xattr_handlers[]; -extern ssize_t cifs_listxattr(struct dentry *, char *, size_t); +ssize_t cifs_listxattr(struct dentry *direntry, char *data, size_t buf_size); #else # define cifs_xattr_handlers NULL # define cifs_listxattr NULL #endif -extern ssize_t cifs_file_copychunk_range(unsigned int xid, - struct file *src_file, loff_t off, - struct file *dst_file, loff_t destoff, - size_t len, unsigned int flags); +ssize_t cifs_file_copychunk_range(unsigned int xid, struct file *src_file, + loff_t off, struct file *dst_file, + loff_t destoff, size_t len, + unsigned int flags); -extern long cifs_ioctl(struct file *filep, unsigned int cmd, unsigned long arg); -extern void cifs_setsize(struct inode *inode, loff_t offset); +long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg); +void cifs_setsize(struct inode *inode, loff_t offset); struct smb3_fs_context; -extern struct dentry *cifs_smb3_do_mount(struct file_system_type *fs_type, - int flags, struct smb3_fs_context *ctx); +struct dentry *cifs_smb3_do_mount(struct file_system_type *fs_type, int flags, + struct smb3_fs_context *old_ctx); #ifdef CONFIG_CIFS_NFSD_EXPORT extern const struct export_operations cifs_export_ops; diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h index 3eca5bfb7030..7eb0131963dd 100644 --- a/fs/smb/client/cifsglob.h +++ b/fs/smb/client/cifsglob.h @@ -28,6 +28,7 @@ #include "../common/smb2pdu.h" #include "../common/fscc.h" #include "smb2pdu.h" +#include "smb1pdu.h" #include <linux/filelock.h> #define SMB_PATH_MAX 260 @@ -103,8 +104,6 @@ */ #define SMB2_MAX_CREDITS_AVAILABLE 32000 -#include "cifspdu.h" - #ifndef XATTR_DOS_ATTRIB #define XATTR_DOS_ATTRIB "user.DOSATTRIB" #endif @@ -950,18 +949,6 @@ revert_current_mid_from_hdr(struct TCP_Server_Info *server, return revert_current_mid(server, num > 0 ? num : 1); } -static inline __u16 -get_mid(const struct smb_hdr *smb) -{ - return le16_to_cpu(smb->Mid); -} - -static inline bool -compare_mid(__u16 mid, const struct smb_hdr *smb) -{ - return mid == le16_to_cpu(smb->Mid); -} - /* * When the server supports very large reads and writes via POSIX extensions, * we can allow up to 2^24-1, minus the size of a READ/WRITE_AND_X header, not @@ -1327,8 +1314,8 @@ struct tcon_link { struct cifs_tcon *tl_tcon; }; -extern struct tcon_link *cifs_sb_tlink(struct cifs_sb_info *cifs_sb); -extern void smb3_free_compound_rqst(int num_rqst, struct smb_rqst *rqst); +struct tcon_link *cifs_sb_tlink(struct cifs_sb_info *cifs_sb); +void smb3_free_compound_rqst(int num_rqst, struct smb_rqst *rqst); static inline struct cifs_tcon * tlink_tcon(struct tcon_link *tlink) @@ -1342,7 +1329,7 @@ cifs_sb_master_tlink(struct cifs_sb_info *cifs_sb) return cifs_sb->master_tlink; } -extern void cifs_put_tlink(struct tcon_link *tlink); +void cifs_put_tlink(struct tcon_link *tlink); static inline struct tcon_link * cifs_get_tlink(struct tcon_link *tlink) @@ -1353,7 +1340,7 @@ cifs_get_tlink(struct tcon_link *tlink) } /* This function is always expected to succeed */ -extern struct cifs_tcon *cifs_sb_master_tcon(struct cifs_sb_info *cifs_sb); +struct cifs_tcon *cifs_sb_master_tcon(struct cifs_sb_info *cifs_sb); #define CIFS_OPLOCK_NO_CHANGE 0xfe @@ -1507,6 +1494,8 @@ struct cifs_io_subrequest { int result; bool have_xid; bool replay; + unsigned int retries; /* number of retries so far */ + unsigned int cur_sleep; /* time to sleep before replay */ struct kvec iov[2]; struct TCP_Server_Info *server; #ifdef CONFIG_CIFS_SMB_DIRECT @@ -1526,8 +1515,8 @@ cifsFileInfo_get_locked(struct cifsFileInfo *cifs_file) } struct cifsFileInfo *cifsFileInfo_get(struct cifsFileInfo *cifs_file); -void _cifsFileInfo_put(struct cifsFileInfo *cifs_file, bool wait_oplock_hdlr, - bool offload); +void _cifsFileInfo_put(struct cifsFileInfo *cifs_file, + bool wait_oplock_handler, bool offload); void cifsFileInfo_put(struct cifsFileInfo *cifs_file); int cifs_file_flush(const unsigned int xid, struct inode *inode, struct cifsFileInfo *cfile); @@ -1796,6 +1785,11 @@ struct cifs_mount_ctx { struct cifs_tcon *tcon; }; +struct mchan_mount { + struct work_struct work; + struct cifs_ses *ses; +}; + static inline void __free_dfs_info_param(struct dfs_info3_param *param) { kfree(param->path_name); @@ -1943,6 +1937,8 @@ require use of the stronger protocol */ */ /**************************************************************************** + * LOCK ORDERING NOTES: + **************************************************************************** * Here are all the locks (spinlock, mutex, semaphore) in cifs.ko, arranged according * to the locking order. i.e. if two locks are to be held together, the lock that * appears higher in this list needs to be taken before the other. @@ -1971,18 +1967,21 @@ require use of the stronger protocol */ * ===================================================================================== * Lock Protects Initialization fn * ===================================================================================== + * cifs_mount_mutex mount/unmount operations * vol_list_lock * vol_info->ctx_lock vol_info->ctx * cifs_sb_info->tlink_tree_lock cifs_sb_info->tlink_tree cifs_setup_cifs_sb * TCP_Server_Info-> TCP_Server_Info cifs_get_tcp_session * reconnect_mutex - * TCP_Server_Info->srv_mutex TCP_Server_Info cifs_get_tcp_session * cifs_ses->session_mutex cifs_ses sesInfoAlloc + * TCP_Server_Info->srv_mutex TCP_Server_Info cifs_get_tcp_session + * cifs_tcp_ses_lock cifs_tcp_ses_list sesInfoAlloc * cifs_tcon->open_file_lock cifs_tcon->openFileList tconInfoAlloc * cifs_tcon->pending_opens * cifs_tcon->stat_lock cifs_tcon->bytes_read tconInfoAlloc * cifs_tcon->bytes_written - * cifs_tcp_ses_lock cifs_tcp_ses_list sesInfoAlloc + * cifs_tcon->fscache_lock cifs_tcon->fscache tconInfoAlloc + * cifs_tcon->sb_list_lock cifs_tcon->cifs_sb_list tconInfoAlloc * GlobalMid_Lock GlobalMaxActiveXid init_cifs * GlobalCurrentXid * GlobalTotalActiveXid @@ -2005,6 +2004,8 @@ require use of the stronger protocol */ * ->chans_in_reconnect * cifs_tcon->tc_lock (anything that is not protected by another lock and can change) * tcon_info_alloc + * cifs_swnreg_idr_mutex cifs_swnreg_idr cifs_swn.c + * (witness service registration, accesses tcon fields under tc_lock) * inode->i_rwsem, taken by fs/netfs/locking.c e.g. should be taken before cifsInodeInfo locks * cifsInodeInfo->open_file_lock cifsInodeInfo->openFileList cifs_alloc_inode * cifsInodeInfo->writers_lock cifsInodeInfo->writers cifsInodeInfo_alloc @@ -2012,12 +2013,12 @@ require use of the stronger protocol */ * ->can_cache_brlcks * cifsInodeInfo->deferred_lock cifsInodeInfo->deferred_closes cifsInodeInfo_alloc * cached_fids->cfid_list_lock cifs_tcon->cfids->entries init_cached_dirs - * cached_fid->fid_lock (anything that is not protected by another lock and can change) - * init_cached_dir + * cached_fid->dirents.de_mutex cached_fid->dirents alloc_cached_dir * cifsFileInfo->fh_mutex cifsFileInfo cifs_new_fileinfo * cifsFileInfo->file_info_lock cifsFileInfo->count cifs_new_fileinfo * ->invalidHandle initiate_cifs_search * ->oplock_break_cancelled + * smbdirect_mr->mutex RDMA memory region management (SMBDirect only) * mid_q_entry->mid_lock mid_q_entry->callback alloc_mid * smb2_mid_entry_alloc * (Any fields of mid_q_entry that will need protection) @@ -2115,8 +2116,6 @@ extern mempool_t cifs_io_subrequest_pool; /* Operations for different SMB versions */ #ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY -extern struct smb_version_operations smb1_operations; -extern struct smb_version_values smb1_values; extern struct smb_version_operations smb20_operations; extern struct smb_version_values smb20_values; #endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ diff --git a/fs/smb/client/cifspdu.h b/fs/smb/client/cifspdu.h index fdd84369e7b8..78512af18d96 100644 --- a/fs/smb/client/cifspdu.h +++ b/fs/smb/client/cifspdu.h @@ -9,2379 +9,4 @@ #ifndef _CIFSPDU_H #define _CIFSPDU_H -#include <net/sock.h> -#include <linux/unaligned.h> -#include "../common/smbfsctl.h" -#include "../common/smb1pdu.h" - -#define CIFS_PROT 0 -#define POSIX_PROT (CIFS_PROT+1) -#define BAD_PROT 0xFFFF - -/* SMB command codes: - * See MS-CIFS 2.2.2.1 - * Note some commands have minimal (wct=0,bcc=0), or uninteresting, responses - * (ie which include no useful data other than the SMB error code itself). - * This can allow us to avoid response buffer allocations and copy in some cases - */ -#define SMB_COM_CREATE_DIRECTORY 0x00 /* trivial response */ -#define SMB_COM_DELETE_DIRECTORY 0x01 /* trivial response */ -#define SMB_COM_CLOSE 0x04 /* triv req/rsp, timestamp ignored */ -#define SMB_COM_FLUSH 0x05 /* triv req/rsp */ -#define SMB_COM_DELETE 0x06 /* trivial response */ -#define SMB_COM_RENAME 0x07 /* trivial response */ -#define SMB_COM_QUERY_INFORMATION 0x08 /* aka getattr */ -#define SMB_COM_SETATTR 0x09 /* trivial response */ -#define SMB_COM_LOCKING_ANDX 0x24 /* trivial response */ -#define SMB_COM_COPY 0x29 /* trivial rsp, fail filename ignrd*/ -#define SMB_COM_ECHO 0x2B /* echo request */ -#define SMB_COM_OPEN_ANDX 0x2D /* Legacy open for old servers */ -#define SMB_COM_READ_ANDX 0x2E -#define SMB_COM_WRITE_ANDX 0x2F -#define SMB_COM_TRANSACTION2 0x32 -#define SMB_COM_TRANSACTION2_SECONDARY 0x33 -#define SMB_COM_FIND_CLOSE2 0x34 /* trivial response */ -#define SMB_COM_TREE_DISCONNECT 0x71 /* trivial response */ -#define SMB_COM_NEGOTIATE 0x72 -#define SMB_COM_SESSION_SETUP_ANDX 0x73 -#define SMB_COM_LOGOFF_ANDX 0x74 /* trivial response */ -#define SMB_COM_TREE_CONNECT_ANDX 0x75 -#define SMB_COM_NT_TRANSACT 0xA0 -#define SMB_COM_NT_TRANSACT_SECONDARY 0xA1 -#define SMB_COM_NT_CREATE_ANDX 0xA2 -#define SMB_COM_NT_CANCEL 0xA4 /* no response */ -#define SMB_COM_NT_RENAME 0xA5 /* trivial response */ - -/* Transact2 subcommand codes */ -#define TRANS2_OPEN 0x00 -#define TRANS2_FIND_FIRST 0x01 -#define TRANS2_FIND_NEXT 0x02 -#define TRANS2_QUERY_FS_INFORMATION 0x03 -#define TRANS2_SET_FS_INFORMATION 0x04 -#define TRANS2_QUERY_PATH_INFORMATION 0x05 -#define TRANS2_SET_PATH_INFORMATION 0x06 -#define TRANS2_QUERY_FILE_INFORMATION 0x07 -#define TRANS2_SET_FILE_INFORMATION 0x08 -#define TRANS2_GET_DFS_REFERRAL 0x10 -#define TRANS2_REPORT_DFS_INCOSISTENCY 0x11 - -/* SMB Transact (Named Pipe) subcommand codes */ -#define TRANS_SET_NMPIPE_STATE 0x0001 -#define TRANS_RAW_READ_NMPIPE 0x0011 -#define TRANS_QUERY_NMPIPE_STATE 0x0021 -#define TRANS_QUERY_NMPIPE_INFO 0x0022 -#define TRANS_PEEK_NMPIPE 0x0023 -#define TRANS_TRANSACT_NMPIPE 0x0026 -#define TRANS_RAW_WRITE_NMPIPE 0x0031 -#define TRANS_READ_NMPIPE 0x0036 -#define TRANS_WRITE_NMPIPE 0x0037 -#define TRANS_WAIT_NMPIPE 0x0053 -#define TRANS_CALL_NMPIPE 0x0054 - -/* NT Transact subcommand codes */ -#define NT_TRANSACT_CREATE 0x01 -#define NT_TRANSACT_IOCTL 0x02 -#define NT_TRANSACT_SET_SECURITY_DESC 0x03 -#define NT_TRANSACT_NOTIFY_CHANGE 0x04 -#define NT_TRANSACT_RENAME 0x05 -#define NT_TRANSACT_QUERY_SECURITY_DESC 0x06 -#define NT_TRANSACT_GET_USER_QUOTA 0x07 -#define NT_TRANSACT_SET_USER_QUOTA 0x08 - -/* future chained NTCreateXReadX bigger, but for time being NTCreateX biggest */ -/* among the requests (NTCreateX response is bigger with wct of 34) */ -#define MAX_CIFS_HDR_SIZE 0x54 /* 32 hdr + (2*24 wct) + 2 bct + 2 pad */ -#define CIFS_SMALL_PATH 120 /* allows for (448-88)/3 */ - -/* internal cifs vfs structures */ -/***************************************************************** - * All constants go here - ***************************************************************** - */ - -/* - * Starting value for maximum SMB size negotiation - */ -#define CIFS_MAX_MSGSIZE (4*4096) - -/* - * Size of encrypted user password in bytes - */ -#define CIFS_ENCPWD_SIZE (16) - -/* - * Size of the crypto key returned on the negotiate SMB in bytes - */ -#define CIFS_CRYPTO_KEY_SIZE (8) - -/* - * Size of the ntlm client response - */ -#define CIFS_AUTH_RESP_SIZE (24) - -/* - * Size of the session key (crypto key encrypted with the password - */ -#define CIFS_SESS_KEY_SIZE (16) - -#define CIFS_SERVER_CHALLENGE_SIZE (8) -#define CIFS_HMAC_MD5_HASH_SIZE (16) -#define CIFS_CPHTXT_SIZE (16) -#define CIFS_NTHASH_SIZE (16) - -/* - * Maximum user name length - */ -#define CIFS_UNLEN (20) - -/* - * Flags on SMB open - */ -#define SMBOPEN_WRITE_THROUGH 0x4000 -#define SMBOPEN_DENY_ALL 0x0010 -#define SMBOPEN_DENY_WRITE 0x0020 -#define SMBOPEN_DENY_READ 0x0030 -#define SMBOPEN_DENY_NONE 0x0040 -#define SMBOPEN_READ 0x0000 -#define SMBOPEN_WRITE 0x0001 -#define SMBOPEN_READWRITE 0x0002 -#define SMBOPEN_EXECUTE 0x0003 - -#define SMBOPEN_OCREATE 0x0010 -#define SMBOPEN_OTRUNC 0x0002 -#define SMBOPEN_OAPPEND 0x0001 - -/* - * SMB flag definitions - * See MS-CIFS 2.2.3.1 - */ -#define SMBFLG_EXTD_LOCK 0x01 /* server supports lock-read write-unlock smb */ -#define SMBFLG_RCV_POSTED 0x02 /* obsolete */ -#define SMBFLG_RSVD 0x04 -#define SMBFLG_CASELESS 0x08 /* all pathnames treated as caseless (off - implies case sensitive file handling request) */ -#define SMBFLG_CANONICAL_PATH_FORMAT 0x10 /* obsolete */ -#define SMBFLG_OLD_OPLOCK 0x20 /* obsolete */ -#define SMBFLG_OLD_OPLOCK_NOTIFY 0x40 /* obsolete */ -#define SMBFLG_RESPONSE 0x80 /* this PDU is a response from server */ - -/* - * SMB flag2 definitions - * See MS-CIFS 2.2.3.1 - * MS-SMB 2.2.3.1 - */ -#define SMBFLG2_KNOWS_LONG_NAMES cpu_to_le16(1) /* can send long (non-8.3) - path names in response */ -#define SMBFLG2_KNOWS_EAS cpu_to_le16(2) -#define SMBFLG2_SECURITY_SIGNATURE cpu_to_le16(4) -#define SMBFLG2_COMPRESSED (8) -#define SMBFLG2_SECURITY_SIGNATURE_REQUIRED (0x10) -#define SMBFLG2_IS_LONG_NAME cpu_to_le16(0x40) -#define SMBFLG2_REPARSE_PATH (0x400) -#define SMBFLG2_EXT_SEC cpu_to_le16(0x800) -#define SMBFLG2_DFS cpu_to_le16(0x1000) -#define SMBFLG2_PAGING_IO cpu_to_le16(0x2000) -#define SMBFLG2_ERR_STATUS cpu_to_le16(0x4000) -#define SMBFLG2_UNICODE cpu_to_le16(0x8000) - -/* Combinations of file access permission bits */ -#define SET_FILE_READ_RIGHTS (FILE_READ_DATA | FILE_READ_EA | FILE_WRITE_EA \ - | FILE_READ_ATTRIBUTES \ - | FILE_WRITE_ATTRIBUTES \ - | DELETE | READ_CONTROL | WRITE_DAC \ - | WRITE_OWNER | SYNCHRONIZE) -#define SET_FILE_WRITE_RIGHTS (FILE_WRITE_DATA | FILE_APPEND_DATA \ - | FILE_READ_EA | FILE_WRITE_EA \ - | FILE_READ_ATTRIBUTES \ - | FILE_WRITE_ATTRIBUTES \ - | DELETE | READ_CONTROL | WRITE_DAC \ - | WRITE_OWNER | SYNCHRONIZE) - -/* - * Invalid readdir handle - */ -#define CIFS_NO_HANDLE 0xFFFF - -#define NO_CHANGE_64 0xFFFFFFFFFFFFFFFFULL - -/* IPC$ in ASCII */ -#define CIFS_IPC_RESOURCE "\x49\x50\x43\x24" - -/* IPC$ in Unicode */ -#define CIFS_IPC_UNICODE_RESOURCE "\x00\x49\x00\x50\x00\x43\x00\x24\x00\x00" - -/* Unicode Null terminate 2 bytes of 0 */ -#define UNICODE_NULL "\x00\x00" -#define ASCII_NULL 0x00 - -/* - * Server type values (returned on EnumServer API - */ -#define CIFS_SV_TYPE_DC 0x00000008 -#define CIFS_SV_TYPE_BACKDC 0x00000010 - -/* - * Alias type flags (From EnumAlias API call - */ -#define CIFS_ALIAS_TYPE_FILE 0x0001 -#define CIFS_SHARE_TYPE_FILE 0x0000 - -/* - * File Attribute flags - */ -#define ATTR_READONLY 0x0001 /* See MS-CIFS 2.2.1.2.3 */ -#define ATTR_HIDDEN 0x0002 /* See MS-CIFS 2.2.1.2.3 */ -#define ATTR_SYSTEM 0x0004 /* See MS-CIFS 2.2.1.2.3 */ -#define ATTR_VOLUME 0x0008 -#define ATTR_DIRECTORY 0x0010 /* See MS-CIFS 2.2.1.2.3 */ -#define ATTR_ARCHIVE 0x0020 /* See MS-CIFS 2.2.1.2.3 */ -#define ATTR_DEVICE 0x0040 -#define ATTR_NORMAL 0x0080 /* See MS-CIFS 2.2.1.2.3 */ -#define ATTR_TEMPORARY 0x0100 /* See MS-CIFS 2.2.1.2.3 */ -#define ATTR_SPARSE 0x0200 /* See MS-SMB 2.2.1.2.1 */ -#define ATTR_REPARSE_POINT 0x0400 /* See MS-SMB 2.2.1.2.1 */ -#define ATTR_COMPRESSED 0x0800 /* See MS-CIFS 2.2.1.2.3 */ -#define ATTR_OFFLINE 0x1000 /* See MS-SMB 2.2.1.2.1 - ie file not immediately available - - on offline storage */ -#define ATTR_NOT_CONTENT_INDEXED 0x2000 /* See MS-SMB 2.2.1.2.1 */ -#define ATTR_ENCRYPTED 0x4000 /* See MS-SMB 2.2.1.2.1 */ -#define ATTR_POSIX_SEMANTICS 0x0100000 /* See MS-CIFS 2.2.1.2.3 */ -#define ATTR_BACKUP_SEMANTICS 0x0200000 /* See MS-CIFS 2.2.1.2.3 */ -#define ATTR_DELETE_ON_CLOSE 0x0400000 /* See MS-CIFS 2.2.1.2.3 */ -#define ATTR_SEQUENTIAL_SCAN 0x0800000 /* See MS-CIFS 2.2.1.2.3 */ -#define ATTR_RANDOM_ACCESS 0x1000000 /* See MS-CIFS 2.2.1.2.3 */ -#define ATTR_NO_BUFFERING 0x2000000 /* See MS-CIFS 2.2.1.2.3 */ -#define ATTR_WRITE_THROUGH 0x8000000 /* See MS-CIFS 2.2.1.2.3 */ - -/* ShareAccess flags */ -#define FILE_NO_SHARE 0x00000000 -#define FILE_SHARE_READ 0x00000001 -#define FILE_SHARE_WRITE 0x00000002 -#define FILE_SHARE_DELETE 0x00000004 -#define FILE_SHARE_ALL 0x00000007 - -/* CreateDisposition flags, similar to CreateAction as well */ -#define FILE_SUPERSEDE 0x00000000 -#define FILE_OPEN 0x00000001 -#define FILE_CREATE 0x00000002 -#define FILE_OPEN_IF 0x00000003 -#define FILE_OVERWRITE 0x00000004 -#define FILE_OVERWRITE_IF 0x00000005 - -/* CreateOptions */ -#define CREATE_NOT_FILE 0x00000001 /* if set must not be file */ -#define CREATE_WRITE_THROUGH 0x00000002 -#define CREATE_SEQUENTIAL 0x00000004 -#define CREATE_NO_BUFFER 0x00000008 /* should not buffer on srv */ -#define CREATE_SYNC_ALERT 0x00000010 /* MBZ */ -#define CREATE_ASYNC_ALERT 0x00000020 /* MBZ */ -#define CREATE_NOT_DIR 0x00000040 /* if set must not be directory */ -#define CREATE_TREE_CONNECTION 0x00000080 /* should be zero */ -#define CREATE_COMPLETE_IF_OPLK 0x00000100 /* should be zero */ -#define CREATE_NO_EA_KNOWLEDGE 0x00000200 -#define CREATE_EIGHT_DOT_THREE 0x00000400 /* doc says this is obsolete - "open for recovery" flag should - be zero in any case */ -#define CREATE_OPEN_FOR_RECOVERY 0x00000400 -#define CREATE_RANDOM_ACCESS 0x00000800 -#define CREATE_DELETE_ON_CLOSE 0x00001000 -#define CREATE_OPEN_BY_ID 0x00002000 -#define CREATE_OPEN_BACKUP_INTENT 0x00004000 -#define CREATE_NO_COMPRESSION 0x00008000 -#define CREATE_RESERVE_OPFILTER 0x00100000 /* should be zero */ -#define OPEN_REPARSE_POINT 0x00200000 -#define OPEN_NO_RECALL 0x00400000 -#define OPEN_FREE_SPACE_QUERY 0x00800000 /* should be zero */ -#define CREATE_OPTIONS_MASK 0x007FFFFF -#define CREATE_OPTION_READONLY 0x10000000 -#define CREATE_OPTION_SPECIAL 0x20000000 /* system. NB not sent over wire */ - -/* ImpersonationLevel flags */ -#define SECURITY_ANONYMOUS 0 -#define SECURITY_IDENTIFICATION 1 -#define SECURITY_IMPERSONATION 2 -#define SECURITY_DELEGATION 3 - -/* SecurityFlags */ -#define SECURITY_CONTEXT_TRACKING 0x01 -#define SECURITY_EFFECTIVE_ONLY 0x02 - -/* - * Default PID value, used in all SMBs where the PID is not important - */ -#define CIFS_DFT_PID 0x1234 - -/* - * We use the same routine for Copy and Move SMBs. This flag is used to - * distinguish - */ -#define CIFS_COPY_OP 1 -#define CIFS_RENAME_OP 2 - -#define GETU16(var) (*((__u16 *)var)) /* BB check for endian issues */ -#define GETU32(var) (*((__u32 *)var)) /* BB check for endian issues */ - -/* given a pointer to an smb_hdr, retrieve a void pointer to the ByteCount */ -static inline void * -BCC(struct smb_hdr *smb) -{ - return (void *)smb + sizeof(*smb) + 2 * smb->WordCount; -} - -/* given a pointer to an smb_hdr retrieve the pointer to the byte area */ -#define pByteArea(smb_var) (BCC(smb_var) + 2) - -/* get the unconverted ByteCount for a SMB packet and return it */ -static inline __u16 -get_bcc(struct smb_hdr *hdr) -{ - __le16 *bc_ptr = (__le16 *)BCC(hdr); - - return get_unaligned_le16(bc_ptr); -} - -/* set the ByteCount for a SMB packet in little-endian */ -static inline void -put_bcc(__u16 count, struct smb_hdr *hdr) -{ - __le16 *bc_ptr = (__le16 *)BCC(hdr); - - put_unaligned_le16(count, bc_ptr); -} - -/* - * Computer Name Length (since Netbios name was length 16 with last byte 0x20) - * No longer as important, now that TCP names are more commonly used to - * resolve hosts. - */ -#define CNLEN 15 - -/* - * Share Name Length (SNLEN) - * Note: This length was limited by the SMB used to get - * the Share info. NetShareEnum only returned 13 - * chars, including the null termination. - * This was removed because it no longer is limiting. - */ - -/* - * Comment Length - */ -#define MAXCOMMENTLEN 40 - -/* - * The OS/2 maximum path name - */ -#define MAX_PATHCONF 256 - -/* - * SMB frame definitions (following must be packed structs) - * See the SNIA CIFS Specification for details. - * - * The Naming convention is the lower case version of the - * smb command code name for the struct and this is typedef to the - * uppercase version of the same name with the prefix SMB_ removed - * for brevity. Although typedefs are not commonly used for - * structure definitions in the Linux kernel, their use in the - * CIFS standards document, which this code is based on, may - * make this one of the cases where typedefs for structures make - * sense to improve readability for readers of the standards doc. - * Typedefs can always be removed later if they are too distracting - * and they are only used for the CIFSs PDUs themselves, not - * internal cifs vfs structures - * - */ - -#define MIN_TZ_ADJ (15 * 60) /* minimum grid for timezones in seconds */ - -#define READ_RAW_ENABLE 1 -#define WRITE_RAW_ENABLE 2 -#define RAW_ENABLE (READ_RAW_ENABLE | WRITE_RAW_ENABLE) -#define SMB1_CLIENT_GUID_SIZE (16) - -/* See MS-CIFS 2.2.4.52.2 */ -typedef struct smb_negotiate_rsp { - struct smb_hdr hdr; /* wct = 17 */ - __le16 DialectIndex; /* 0xFFFF = no dialect acceptable */ - __u8 SecurityMode; - __le16 MaxMpxCount; - __le16 MaxNumberVcs; - __le32 MaxBufferSize; - __le32 MaxRawSize; - __le32 SessionKey; - __le32 Capabilities; /* see below */ - __le32 SystemTimeLow; - __le32 SystemTimeHigh; - __le16 ServerTimeZone; - __u8 EncryptionKeyLength; - __u16 ByteCount; - union { - /* cap extended security off */ - DECLARE_FLEX_ARRAY(unsigned char, EncryptionKey); - /* followed by Domain name - if extended security is off */ - /* followed by 16 bytes of server GUID */ - /* then security blob if cap_extended_security negotiated */ - struct { - unsigned char GUID[SMB1_CLIENT_GUID_SIZE]; - unsigned char SecurityBlob[]; - } __packed extended_response; - } __packed u; -} __packed SMB_NEGOTIATE_RSP; - -/* SecurityMode bits */ -#define SECMODE_USER 0x01 /* off indicates share level security */ -#define SECMODE_PW_ENCRYPT 0x02 -#define SECMODE_SIGN_ENABLED 0x04 /* SMB security signatures enabled */ -#define SECMODE_SIGN_REQUIRED 0x08 /* SMB security signatures required */ - -/* Negotiate response Capabilities */ -#define CAP_RAW_MODE 0x00000001 -#define CAP_MPX_MODE 0x00000002 -#define CAP_UNICODE 0x00000004 -#define CAP_LARGE_FILES 0x00000008 -#define CAP_NT_SMBS 0x00000010 /* implies CAP_NT_FIND */ -#define CAP_RPC_REMOTE_APIS 0x00000020 -#define CAP_STATUS32 0x00000040 -#define CAP_LEVEL_II_OPLOCKS 0x00000080 -#define CAP_LOCK_AND_READ 0x00000100 -#define CAP_NT_FIND 0x00000200 -#define CAP_DFS 0x00001000 -#define CAP_INFOLEVEL_PASSTHRU 0x00002000 -#define CAP_LARGE_READ_X 0x00004000 -#define CAP_LARGE_WRITE_X 0x00008000 -#define CAP_LWIO 0x00010000 /* support fctl_srv_req_resume_key */ -#define CAP_UNIX 0x00800000 -#define CAP_COMPRESSED_DATA 0x02000000 -#define CAP_DYNAMIC_REAUTH 0x20000000 -#define CAP_PERSISTENT_HANDLES 0x40000000 -#define CAP_EXTENDED_SECURITY 0x80000000 - -typedef union smb_com_session_setup_andx { - struct { /* request format */ - struct smb_hdr hdr; /* wct = 12 */ - __u8 AndXCommand; - __u8 AndXReserved; - __le16 AndXOffset; - __le16 MaxBufferSize; - __le16 MaxMpxCount; - __le16 VcNumber; - __le32 SessionKey; - __le16 SecurityBlobLength; - __u32 Reserved; - __le32 Capabilities; /* see below */ - __le16 ByteCount; - unsigned char SecurityBlob[]; /* followed by */ - /* STRING NativeOS */ - /* STRING NativeLanMan */ - } __packed req; /* NTLM request format (with - extended security */ - - struct { /* request format */ - struct smb_hdr hdr; /* wct = 13 */ - __u8 AndXCommand; - __u8 AndXReserved; - __le16 AndXOffset; - __le16 MaxBufferSize; - __le16 MaxMpxCount; - __le16 VcNumber; - __le32 SessionKey; - __le16 CaseInsensitivePasswordLength; /* ASCII password len */ - __le16 CaseSensitivePasswordLength; /* Unicode password length*/ - __u32 Reserved; /* see below */ - __le32 Capabilities; - __le16 ByteCount; - unsigned char CaseInsensitivePassword[]; /* followed by: */ - /* unsigned char * CaseSensitivePassword; */ - /* STRING AccountName */ - /* STRING PrimaryDomain */ - /* STRING NativeOS */ - /* STRING NativeLanMan */ - } __packed req_no_secext; /* NTLM request format (without - extended security */ - - struct { /* default (NTLM) response format */ - struct smb_hdr hdr; /* wct = 4 */ - __u8 AndXCommand; - __u8 AndXReserved; - __le16 AndXOffset; - __le16 Action; /* see below */ - __le16 SecurityBlobLength; - __u16 ByteCount; - unsigned char SecurityBlob[]; /* followed by */ -/* unsigned char * NativeOS; */ -/* unsigned char * NativeLanMan; */ -/* unsigned char * PrimaryDomain; */ - } __packed resp; /* NTLM response - (with or without extended sec) */ - - struct { /* request format */ - struct smb_hdr hdr; /* wct = 10 */ - __u8 AndXCommand; - __u8 AndXReserved; - __le16 AndXOffset; - __le16 MaxBufferSize; - __le16 MaxMpxCount; - __le16 VcNumber; - __le32 SessionKey; - __le16 PasswordLength; - __u32 Reserved; /* encrypt key len and offset */ - __le16 ByteCount; - unsigned char AccountPassword[]; /* followed by */ - /* STRING AccountName */ - /* STRING PrimaryDomain */ - /* STRING NativeOS */ - /* STRING NativeLanMan */ - } __packed old_req; /* pre-NTLM (LANMAN2.1) req format */ - - struct { /* default (NTLM) response format */ - struct smb_hdr hdr; /* wct = 3 */ - __u8 AndXCommand; - __u8 AndXReserved; - __le16 AndXOffset; - __le16 Action; /* see below */ - __u16 ByteCount; - unsigned char NativeOS[]; /* followed by */ -/* unsigned char * NativeLanMan; */ -/* unsigned char * PrimaryDomain; */ - } __packed old_resp; /* pre-NTLM (LANMAN2.1) response */ -} __packed SESSION_SETUP_ANDX; - -/* format of NLTMv2 Response ie "case sensitive password" hash when NTLMv2 */ - -#define NTLMSSP_SERVER_TYPE 1 -#define NTLMSSP_DOMAIN_TYPE 2 -#define NTLMSSP_FQ_DOMAIN_TYPE 3 -#define NTLMSSP_DNS_DOMAIN_TYPE 4 -#define NTLMSSP_DNS_PARENT_TYPE 5 - -struct ntlmssp2_name { - __le16 type; - __le16 length; - __u8 data[]; -} __packed; - -struct ntlmv2_resp { - union { - char ntlmv2_hash[CIFS_ENCPWD_SIZE]; - struct { - __u8 reserved[8]; - __u8 key[CIFS_SERVER_CHALLENGE_SIZE]; - } __packed challenge; - } __packed; - __le32 blob_signature; - __u32 reserved; - __le64 time; - __u64 client_chal; /* random */ - __u32 reserved2; - /* array of name entries could follow ending in minimum 4 byte struct */ -} __packed; - - -#define CIFS_NETWORK_OPSYS "CIFS VFS Client for Linux" - - -/* - * Capabilities bits (for NTLM SessSetup request) - * See MS-CIFS 2.2.4.52.2 - * MS-SMB 2.2.4.5.2.1 - */ -#define CAP_UNICODE 0x00000004 -#define CAP_LARGE_FILES 0x00000008 -#define CAP_NT_SMBS 0x00000010 -#define CAP_STATUS32 0x00000040 -#define CAP_LEVEL_II_OPLOCKS 0x00000080 -#define CAP_NT_FIND 0x00000200 /* reserved should be zero - (because NT_SMBs implies the same thing?) */ -#define CAP_BULK_TRANSFER 0x00000400 -#define CAP_EXTENDED_SECURITY 0x80000000 - -/* Action bits */ -#define GUEST_LOGIN 1 - -typedef struct smb_com_tconx_req { - struct smb_hdr hdr; /* wct = 4 */ - __u8 AndXCommand; - __u8 AndXReserved; - __le16 AndXOffset; - __le16 Flags; /* see below */ - __le16 PasswordLength; - __le16 ByteCount; - unsigned char Password[]; /* followed by */ -/* STRING Path *//* \\server\share name */ - /* STRING Service */ -} __packed TCONX_REQ; - -typedef struct smb_com_tconx_rsp { - struct smb_hdr hdr; /* wct = 3 , not extended response */ - __u8 AndXCommand; - __u8 AndXReserved; - __le16 AndXOffset; - __le16 OptionalSupport; /* see below */ - __u16 ByteCount; - unsigned char Service[]; /* always ASCII, not Unicode */ - /* STRING NativeFileSystem */ -} __packed TCONX_RSP; - -typedef struct smb_com_tconx_rsp_ext { - struct smb_hdr hdr; /* wct = 7, extended response */ - __u8 AndXCommand; - __u8 AndXReserved; - __le16 AndXOffset; - __le16 OptionalSupport; /* see below */ - __le32 MaximalShareAccessRights; - __le32 GuestMaximalShareAccessRights; - __u16 ByteCount; - unsigned char Service[]; /* always ASCII, not Unicode */ - /* STRING NativeFileSystem */ -} __packed TCONX_RSP_EXT; - - -/* tree connect Flags */ -#define DISCONNECT_TID 0x0001 -#define TCON_EXTENDED_SIGNATURES 0x0004 -#define TCON_EXTENDED_SECINFO 0x0008 - -/* OptionalSupport bits */ -#define SMB_SUPPORT_SEARCH_BITS 0x0001 /* "must have" directory search bits - (exclusive searches supported) */ -#define SMB_SHARE_IS_IN_DFS 0x0002 -#define SMB_CSC_MASK 0x000C -/* CSC flags defined as follows */ -#define SMB_CSC_CACHE_MANUAL_REINT 0x0000 -#define SMB_CSC_CACHE_AUTO_REINT 0x0004 -#define SMB_CSC_CACHE_VDO 0x0008 -#define SMB_CSC_NO_CACHING 0x000C -#define SMB_UNIQUE_FILE_NAME 0x0010 -#define SMB_EXTENDED_SIGNATURES 0x0020 - -/* services - * - * A: ie disk - * LPT1: ie printer - * IPC ie named pipe - * COMM - * ????? ie any type - * - */ - -typedef struct smb_com_echo_req { - struct smb_hdr hdr; - __le16 EchoCount; - __le16 ByteCount; - char Data[]; -} __packed ECHO_REQ; - -typedef struct smb_com_echo_rsp { - struct smb_hdr hdr; - __le16 SequenceNumber; - __le16 ByteCount; - char Data[]; -} __packed ECHO_RSP; - -typedef struct smb_com_logoff_andx_req { - struct smb_hdr hdr; /* wct = 2 */ - __u8 AndXCommand; - __u8 AndXReserved; - __u16 AndXOffset; - __u16 ByteCount; -} __packed LOGOFF_ANDX_REQ; - -typedef struct smb_com_logoff_andx_rsp { - struct smb_hdr hdr; /* wct = 2 */ - __u8 AndXCommand; - __u8 AndXReserved; - __u16 AndXOffset; - __u16 ByteCount; -} __packed LOGOFF_ANDX_RSP; - -typedef union smb_com_tree_disconnect { /* as an alternative can use flag on - tree_connect PDU to effect disconnect */ - /* tdis is probably simplest SMB PDU */ - struct { - struct smb_hdr hdr; /* wct = 0 */ - __u16 ByteCount; /* bcc = 0 */ - } __packed req; - struct { - struct smb_hdr hdr; /* wct = 0 */ - __u16 ByteCount; /* bcc = 0 */ - } __packed resp; -} __packed TREE_DISCONNECT; - -typedef struct smb_com_close_req { - struct smb_hdr hdr; /* wct = 3 */ - __u16 FileID; - __u32 LastWriteTime; /* should be zero or -1 */ - __u16 ByteCount; /* 0 */ -} __packed CLOSE_REQ; - -typedef struct smb_com_close_rsp { - struct smb_hdr hdr; /* wct = 0 */ - __u16 ByteCount; /* bct = 0 */ -} __packed CLOSE_RSP; - -typedef struct smb_com_flush_req { - struct smb_hdr hdr; /* wct = 1 */ - __u16 FileID; - __u16 ByteCount; /* 0 */ -} __packed FLUSH_REQ; - -typedef struct smb_com_findclose_req { - struct smb_hdr hdr; /* wct = 1 */ - __u16 FileID; - __u16 ByteCount; /* 0 */ -} __packed FINDCLOSE_REQ; - -/* OpenFlags */ -#define REQ_MORE_INFO 0x00000001 /* legacy (OPEN_AND_X) only */ -#define REQ_OPLOCK 0x00000002 -#define REQ_BATCHOPLOCK 0x00000004 -#define REQ_OPENDIRONLY 0x00000008 -#define REQ_EXTENDED_INFO 0x00000010 - -/* File type */ -#define DISK_TYPE 0x0000 -#define BYTE_PIPE_TYPE 0x0001 -#define MESSAGE_PIPE_TYPE 0x0002 -#define PRINTER_TYPE 0x0003 -#define COMM_DEV_TYPE 0x0004 -#define UNKNOWN_TYPE 0xFFFF - -/* Device Type or File Status Flags */ -#define NO_EAS 0x0001 -#define NO_SUBSTREAMS 0x0002 -#define NO_REPARSETAG 0x0004 -/* following flags can apply if pipe */ -#define ICOUNT_MASK 0x00FF -#define PIPE_READ_MODE 0x0100 -#define NAMED_PIPE_TYPE 0x0400 -#define PIPE_END_POINT 0x4000 -#define BLOCKING_NAMED_PIPE 0x8000 - -typedef struct smb_com_open_req { /* also handles create */ - struct smb_hdr hdr; /* wct = 24 */ - __u8 AndXCommand; - __u8 AndXReserved; - __le16 AndXOffset; - __u8 Reserved; /* Must Be Zero */ - __le16 NameLength; - __le32 OpenFlags; - __u32 RootDirectoryFid; - __le32 DesiredAccess; - __le64 AllocationSize; - __le32 FileAttributes; - __le32 ShareAccess; - __le32 CreateDisposition; - __le32 CreateOptions; - __le32 ImpersonationLevel; - __u8 SecurityFlags; - __le16 ByteCount; - char fileName[]; -} __packed OPEN_REQ; - -/* open response: oplock levels */ -#define OPLOCK_NONE 0 -#define OPLOCK_EXCLUSIVE 1 -#define OPLOCK_BATCH 2 -#define OPLOCK_READ 3 /* level 2 oplock */ - -/* open response for CreateAction shifted left */ -#define CIFS_CREATE_ACTION 0x20000 /* file created */ - -typedef struct smb_com_open_rsp { - struct smb_hdr hdr; /* wct = 34 BB */ - __u8 AndXCommand; - __u8 AndXReserved; - __le16 AndXOffset; - __u8 OplockLevel; - __u16 Fid; - __le32 CreateAction; - struct_group_attr(common_attributes, __packed, - __le64 CreationTime; - __le64 LastAccessTime; - __le64 LastWriteTime; - __le64 ChangeTime; - __le32 FileAttributes; - ); - __le64 AllocationSize; - __le64 EndOfFile; - __le16 FileType; - __le16 DeviceState; - __u8 DirectoryFlag; - __u16 ByteCount; /* bct = 0 */ -} __packed OPEN_RSP; - -typedef struct smb_com_open_rsp_ext { - struct smb_hdr hdr; /* wct = 42 but meaningless due to MS bug? */ - __u8 AndXCommand; - __u8 AndXReserved; - __le16 AndXOffset; - __u8 OplockLevel; - __u16 Fid; - __le32 CreateAction; - __le64 CreationTime; - __le64 LastAccessTime; - __le64 LastWriteTime; - __le64 ChangeTime; - __le32 FileAttributes; - __le64 AllocationSize; - __le64 EndOfFile; - __le16 FileType; - __le16 DeviceState; - __u8 DirectoryFlag; - __u8 VolumeGUID[16]; - __u64 FileId; /* note no endian conversion - is opaque UniqueID */ - __le32 MaximalAccessRights; - __le32 GuestMaximalAccessRights; - __u16 ByteCount; /* bct = 0 */ -} __packed OPEN_RSP_EXT; - - -/* format of legacy open request */ -typedef struct smb_com_openx_req { - struct smb_hdr hdr; /* wct = 15 */ - __u8 AndXCommand; - __u8 AndXReserved; - __le16 AndXOffset; - __le16 OpenFlags; - __le16 Mode; - __le16 Sattr; /* search attributes */ - __le16 FileAttributes; /* dos attrs */ - __le32 CreateTime; /* os2 format */ - __le16 OpenFunction; - __le32 EndOfFile; - __le32 Timeout; - __le32 Reserved; - __le16 ByteCount; /* file name follows */ - char fileName[]; -} __packed OPENX_REQ; - -typedef struct smb_com_openx_rsp { - struct smb_hdr hdr; /* wct = 15 */ - __u8 AndXCommand; - __u8 AndXReserved; - __le16 AndXOffset; - __u16 Fid; - __le16 FileAttributes; - __le32 LastWriteTime; /* os2 format */ - __le32 EndOfFile; - __le16 Access; - __le16 FileType; - __le16 IPCState; - __le16 Action; - __u32 FileId; - __u16 Reserved; - __u16 ByteCount; -} __packed OPENX_RSP; - -/* For encoding of POSIX Open Request - see trans2 function 0x209 data struct */ - -/* Legacy write request for older servers */ -typedef struct smb_com_writex_req { - struct smb_hdr hdr; /* wct = 12 */ - __u8 AndXCommand; - __u8 AndXReserved; - __le16 AndXOffset; - __u16 Fid; - __le32 OffsetLow; - __u32 Reserved; /* Timeout */ - __le16 WriteMode; /* 1 = write through */ - __le16 Remaining; - __le16 Reserved2; - __le16 DataLengthLow; - __le16 DataOffset; - __le16 ByteCount; - __u8 Pad; /* BB check for whether padded to DWORD - boundary and optimum performance here */ - char Data[]; -} __packed WRITEX_REQ; - -typedef struct smb_com_write_req { - struct smb_hdr hdr; /* wct = 14 */ - __u8 AndXCommand; - __u8 AndXReserved; - __le16 AndXOffset; - __u16 Fid; - __le32 OffsetLow; - __u32 Reserved; - __le16 WriteMode; - __le16 Remaining; - __le16 DataLengthHigh; - __le16 DataLengthLow; - __le16 DataOffset; - __le32 OffsetHigh; - __le16 ByteCount; - __u8 Pad; /* BB check for whether padded to DWORD - boundary and optimum performance here */ - char Data[]; -} __packed WRITE_REQ; - -typedef struct smb_com_write_rsp { - struct smb_hdr hdr; /* wct = 6 */ - __u8 AndXCommand; - __u8 AndXReserved; - __le16 AndXOffset; - __le16 Count; - __le16 Remaining; - __le16 CountHigh; - __u16 Reserved; - __u16 ByteCount; -} __packed WRITE_RSP; - -/* legacy read request for older servers */ -typedef struct smb_com_readx_req { - struct smb_hdr hdr; /* wct = 10 */ - __u8 AndXCommand; - __u8 AndXReserved; - __le16 AndXOffset; - __u16 Fid; - __le32 OffsetLow; - __le16 MaxCount; - __le16 MinCount; /* obsolete */ - __le32 Reserved; - __le16 Remaining; - __le16 ByteCount; -} __packed READX_REQ; - -typedef struct smb_com_read_req { - struct smb_hdr hdr; /* wct = 12 */ - __u8 AndXCommand; - __u8 AndXReserved; - __le16 AndXOffset; - __u16 Fid; - __le32 OffsetLow; - __le16 MaxCount; - __le16 MinCount; /* obsolete */ - __le32 MaxCountHigh; - __le16 Remaining; - __le32 OffsetHigh; - __le16 ByteCount; -} __packed READ_REQ; - -typedef struct smb_com_read_rsp { - struct smb_hdr hdr; /* wct = 12 */ - __u8 AndXCommand; - __u8 AndXReserved; - __le16 AndXOffset; - __le16 Remaining; - __le16 DataCompactionMode; - __le16 Reserved; - __le16 DataLength; - __le16 DataOffset; - __le16 DataLengthHigh; - __u64 Reserved2; - __u16 ByteCount; - /* read response data immediately follows */ -} __packed READ_RSP; - -typedef struct locking_andx_range { - __le16 Pid; - __le16 Pad; - __le32 OffsetHigh; - __le32 OffsetLow; - __le32 LengthHigh; - __le32 LengthLow; -} __packed LOCKING_ANDX_RANGE; - -#define LOCKING_ANDX_SHARED_LOCK 0x01 -#define LOCKING_ANDX_OPLOCK_RELEASE 0x02 -#define LOCKING_ANDX_CHANGE_LOCKTYPE 0x04 -#define LOCKING_ANDX_CANCEL_LOCK 0x08 -#define LOCKING_ANDX_LARGE_FILES 0x10 /* always on for us */ - -typedef struct smb_com_lock_req { - struct smb_hdr hdr; /* wct = 8 */ - __u8 AndXCommand; - __u8 AndXReserved; - __le16 AndXOffset; - __u16 Fid; - __u8 LockType; - __u8 OplockLevel; - __le32 Timeout; - __le16 NumberOfUnlocks; - __le16 NumberOfLocks; - __le16 ByteCount; - LOCKING_ANDX_RANGE Locks[]; -} __packed LOCK_REQ; - -/* lock type */ -#define CIFS_RDLCK 0 -#define CIFS_WRLCK 1 -#define CIFS_UNLCK 2 -typedef struct cifs_posix_lock { - __le16 lock_type; /* 0 = Read, 1 = Write, 2 = Unlock */ - __le16 lock_flags; /* 1 = Wait (only valid for setlock) */ - __le32 pid; - __le64 start; - __le64 length; - /* BB what about additional owner info to identify network client */ -} __packed CIFS_POSIX_LOCK; - -typedef struct smb_com_lock_rsp { - struct smb_hdr hdr; /* wct = 2 */ - __u8 AndXCommand; - __u8 AndXReserved; - __le16 AndXOffset; - __u16 ByteCount; -} __packed LOCK_RSP; - -typedef struct smb_com_rename_req { - struct smb_hdr hdr; /* wct = 1 */ - __le16 SearchAttributes; /* target file attributes */ - __le16 ByteCount; - __u8 BufferFormat; /* 4 = ASCII or Unicode */ - unsigned char OldFileName[]; - /* followed by __u8 BufferFormat2 */ - /* followed by NewFileName */ -} __packed RENAME_REQ; - - /* copy request flags */ -#define COPY_MUST_BE_FILE 0x0001 -#define COPY_MUST_BE_DIR 0x0002 -#define COPY_TARGET_MODE_ASCII 0x0004 /* if not set, binary */ -#define COPY_SOURCE_MODE_ASCII 0x0008 /* if not set, binary */ -#define COPY_VERIFY_WRITES 0x0010 -#define COPY_TREE 0x0020 - -typedef struct smb_com_copy_req { - struct smb_hdr hdr; /* wct = 3 */ - __u16 Tid2; - __le16 OpenFunction; - __le16 Flags; - __le16 ByteCount; - __u8 BufferFormat; /* 4 = ASCII or Unicode */ - unsigned char OldFileName[]; - /* followed by __u8 BufferFormat2 */ - /* followed by NewFileName string */ -} __packed COPY_REQ; - -typedef struct smb_com_copy_rsp { - struct smb_hdr hdr; /* wct = 1 */ - __le16 CopyCount; /* number of files copied */ - __u16 ByteCount; /* may be zero */ - __u8 BufferFormat; /* 0x04 - only present if errored file follows */ - unsigned char ErrorFileName[]; /* only present if error in copy */ -} __packed COPY_RSP; - -#define CREATE_HARD_LINK 0x103 -#define MOVEFILE_COPY_ALLOWED 0x0002 -#define MOVEFILE_REPLACE_EXISTING 0x0001 - -typedef struct smb_com_nt_rename_req { /* A5 - also used for create hardlink */ - struct smb_hdr hdr; /* wct = 4 */ - __le16 SearchAttributes; /* target file attributes */ - __le16 Flags; /* spec says Information Level */ - __le32 ClusterCount; - __le16 ByteCount; - __u8 BufferFormat; /* 4 = ASCII or Unicode */ - unsigned char OldFileName[]; - /* followed by __u8 BufferFormat2 */ - /* followed by NewFileName */ -} __packed NT_RENAME_REQ; - -typedef struct smb_com_rename_rsp { - struct smb_hdr hdr; /* wct = 0 */ - __u16 ByteCount; /* bct = 0 */ -} __packed RENAME_RSP; - -typedef struct smb_com_delete_file_req { - struct smb_hdr hdr; /* wct = 1 */ - __le16 SearchAttributes; - __le16 ByteCount; - __u8 BufferFormat; /* 4 = ASCII */ - unsigned char fileName[]; -} __packed DELETE_FILE_REQ; - -typedef struct smb_com_delete_file_rsp { - struct smb_hdr hdr; /* wct = 0 */ - __u16 ByteCount; /* bct = 0 */ -} __packed DELETE_FILE_RSP; - -typedef struct smb_com_delete_directory_req { - struct smb_hdr hdr; /* wct = 0 */ - __le16 ByteCount; - __u8 BufferFormat; /* 4 = ASCII */ - unsigned char DirName[]; -} __packed DELETE_DIRECTORY_REQ; - -typedef struct smb_com_delete_directory_rsp { - struct smb_hdr hdr; /* wct = 0 */ - __u16 ByteCount; /* bct = 0 */ -} __packed DELETE_DIRECTORY_RSP; - -typedef struct smb_com_create_directory_req { - struct smb_hdr hdr; /* wct = 0 */ - __le16 ByteCount; - __u8 BufferFormat; /* 4 = ASCII */ - unsigned char DirName[]; -} __packed CREATE_DIRECTORY_REQ; - -typedef struct smb_com_create_directory_rsp { - struct smb_hdr hdr; /* wct = 0 */ - __u16 ByteCount; /* bct = 0 */ -} __packed CREATE_DIRECTORY_RSP; - -typedef struct smb_com_query_information_req { - struct smb_hdr hdr; /* wct = 0 */ - __le16 ByteCount; /* 1 + namelen + 1 */ - __u8 BufferFormat; /* 4 = ASCII */ - unsigned char FileName[]; -} __packed QUERY_INFORMATION_REQ; - -typedef struct smb_com_query_information_rsp { - struct smb_hdr hdr; /* wct = 10 */ - __le16 attr; - __le32 last_write_time; - __le32 size; - __u16 reserved[5]; - __le16 ByteCount; /* bcc = 0 */ -} __packed QUERY_INFORMATION_RSP; - -typedef struct smb_com_setattr_req { - struct smb_hdr hdr; /* wct = 8 */ - __le16 attr; - __le32 last_write_time; - __le16 reserved[5]; /* must be zero */ - __le16 ByteCount; - __u8 BufferFormat; /* 4 = ASCII */ - unsigned char fileName[]; -} __packed SETATTR_REQ; - -typedef struct smb_com_setattr_rsp { - struct smb_hdr hdr; /* wct = 0 */ - __u16 ByteCount; /* bct = 0 */ -} __packed SETATTR_RSP; - -/* empty wct response to setattr */ - -/*******************************************************/ -/* NT Transact structure definitions follow */ -/* Currently only ioctl, acl (get security descriptor) */ -/* and notify are implemented */ -/*******************************************************/ -typedef struct smb_com_ntransact_req { - struct smb_hdr hdr; /* wct >= 19 */ - __u8 MaxSetupCount; - __u16 Reserved; - __le32 TotalParameterCount; - __le32 TotalDataCount; - __le32 MaxParameterCount; - __le32 MaxDataCount; - __le32 ParameterCount; - __le32 ParameterOffset; - __le32 DataCount; - __le32 DataOffset; - __u8 SetupCount; /* four setup words follow subcommand */ - /* SNIA spec incorrectly included spurious pad here */ - __le16 SubCommand; /* 2 = IOCTL/FSCTL */ - /* SetupCount words follow then */ - __le16 ByteCount; - __u8 Pad[3]; - __u8 Parms[]; -} __packed NTRANSACT_REQ; - -typedef struct smb_com_ntransact_rsp { - struct smb_hdr hdr; /* wct = 18 */ - __u8 Reserved[3]; - __le32 TotalParameterCount; - __le32 TotalDataCount; - __le32 ParameterCount; - __le32 ParameterOffset; - __le32 ParameterDisplacement; - __le32 DataCount; - __le32 DataOffset; - __le32 DataDisplacement; - __u8 SetupCount; /* 0 */ - __u16 ByteCount; - /* __u8 Pad[3]; */ - /* parms and data follow */ -} __packed NTRANSACT_RSP; - -typedef struct smb_com_transaction_ioctl_req { - struct smb_hdr hdr; /* wct = 23 */ - __u8 MaxSetupCount; - __u16 Reserved; - __le32 TotalParameterCount; - __le32 TotalDataCount; - __le32 MaxParameterCount; - __le32 MaxDataCount; - __le32 ParameterCount; - __le32 ParameterOffset; - __le32 DataCount; - __le32 DataOffset; - __u8 SetupCount; /* four setup words follow subcommand */ - /* SNIA spec incorrectly included spurious pad here */ - __le16 SubCommand; /* 2 = IOCTL/FSCTL */ - __le32 FunctionCode; - __u16 Fid; - __u8 IsFsctl; /* 1 = File System Control 0 = device control (IOCTL) */ - __u8 IsRootFlag; /* 1 = apply command to root of share (must be DFS) */ - __le16 ByteCount; - __u8 Pad[3]; - __u8 Data[]; -} __packed TRANSACT_IOCTL_REQ; - -typedef struct smb_com_transaction_compr_ioctl_req { - struct smb_hdr hdr; /* wct = 23 */ - __u8 MaxSetupCount; - __u16 Reserved; - __le32 TotalParameterCount; - __le32 TotalDataCount; - __le32 MaxParameterCount; - __le32 MaxDataCount; - __le32 ParameterCount; - __le32 ParameterOffset; - __le32 DataCount; - __le32 DataOffset; - __u8 SetupCount; /* four setup words follow subcommand */ - /* SNIA spec incorrectly included spurious pad here */ - __le16 SubCommand; /* 2 = IOCTL/FSCTL */ - __le32 FunctionCode; - __u16 Fid; - __u8 IsFsctl; /* 1 = File System Control 0 = device control (IOCTL) */ - __u8 IsRootFlag; /* 1 = apply command to root of share (must be DFS) */ - __le16 ByteCount; - __u8 Pad[3]; - __le16 compression_state; /* See below for valid flags */ -} __packed TRANSACT_COMPR_IOCTL_REQ; - -/* compression state flags */ -#define COMPRESSION_FORMAT_NONE 0x0000 -#define COMPRESSION_FORMAT_DEFAULT 0x0001 -#define COMPRESSION_FORMAT_LZNT1 0x0002 - -typedef struct smb_com_transaction_ioctl_rsp { - struct smb_hdr hdr; /* wct = 19 */ - __u8 Reserved[3]; - __le32 TotalParameterCount; - __le32 TotalDataCount; - __le32 ParameterCount; - __le32 ParameterOffset; - __le32 ParameterDisplacement; - __le32 DataCount; - __le32 DataOffset; - __le32 DataDisplacement; - __u8 SetupCount; /* 1 */ - __le16 ReturnedDataLen; - __le16 ByteCount; -} __packed TRANSACT_IOCTL_RSP; - -#define CIFS_ACL_OWNER 1 -#define CIFS_ACL_GROUP 2 -#define CIFS_ACL_DACL 4 -#define CIFS_ACL_SACL 8 - -typedef struct smb_com_transaction_qsec_req { - struct smb_hdr hdr; /* wct = 19 */ - __u8 MaxSetupCount; - __u16 Reserved; - __le32 TotalParameterCount; - __le32 TotalDataCount; - __le32 MaxParameterCount; - __le32 MaxDataCount; - __le32 ParameterCount; - __le32 ParameterOffset; - __le32 DataCount; - __le32 DataOffset; - __u8 SetupCount; /* no setup words follow subcommand */ - /* SNIA spec incorrectly included spurious pad here */ - __le16 SubCommand; /* 6 = QUERY_SECURITY_DESC */ - __le16 ByteCount; /* bcc = 3 + 8 */ - __u8 Pad[3]; - __u16 Fid; - __u16 Reserved2; - __le32 AclFlags; -} __packed QUERY_SEC_DESC_REQ; - - -typedef struct smb_com_transaction_ssec_req { - struct smb_hdr hdr; /* wct = 19 */ - __u8 MaxSetupCount; - __u16 Reserved; - __le32 TotalParameterCount; - __le32 TotalDataCount; - __le32 MaxParameterCount; - __le32 MaxDataCount; - __le32 ParameterCount; - __le32 ParameterOffset; - __le32 DataCount; - __le32 DataOffset; - __u8 SetupCount; /* no setup words follow subcommand */ - /* SNIA spec incorrectly included spurious pad here */ - __le16 SubCommand; /* 3 = SET_SECURITY_DESC */ - __le16 ByteCount; /* bcc = 3 + 8 */ - __u8 Pad[3]; - __u16 Fid; - __u16 Reserved2; - __le32 AclFlags; -} __packed SET_SEC_DESC_REQ; - -typedef struct smb_com_transaction_change_notify_req { - struct smb_hdr hdr; /* wct = 23 */ - __u8 MaxSetupCount; - __u16 Reserved; - __le32 TotalParameterCount; - __le32 TotalDataCount; - __le32 MaxParameterCount; - __le32 MaxDataCount; - __le32 ParameterCount; - __le32 ParameterOffset; - __le32 DataCount; - __le32 DataOffset; - __u8 SetupCount; /* four setup words follow subcommand */ - /* SNIA spec incorrectly included spurious pad here */ - __le16 SubCommand;/* 4 = Change Notify */ - __le32 CompletionFilter; /* operation to monitor */ - __u16 Fid; - __u8 WatchTree; /* 1 = Monitor subdirectories */ - __u8 Reserved2; - __le16 ByteCount; -/* __u8 Pad[3];*/ -/* __u8 Data[];*/ -} __packed TRANSACT_CHANGE_NOTIFY_REQ; - -/* BB eventually change to use generic ntransact rsp struct - and validation routine */ -typedef struct smb_com_transaction_change_notify_rsp { - struct smb_hdr hdr; /* wct = 18 */ - __u8 Reserved[3]; - __le32 TotalParameterCount; - __le32 TotalDataCount; - __le32 ParameterCount; - __le32 ParameterOffset; - __le32 ParameterDisplacement; - __le32 DataCount; - __le32 DataOffset; - __le32 DataDisplacement; - __u8 SetupCount; /* 0 */ - __u16 ByteCount; - /* __u8 Pad[3]; */ -} __packed TRANSACT_CHANGE_NOTIFY_RSP; - -struct cifs_quota_data { - __u32 rsrvd1; /* 0 */ - __u32 sid_size; - __u64 rsrvd2; /* 0 */ - __u64 space_used; - __u64 soft_limit; - __u64 hard_limit; - char sid[]; /* variable size? */ -} __packed; - -/* quota sub commands */ -#define QUOTA_LIST_CONTINUE 0 -#define QUOTA_LIST_START 0x100 -#define QUOTA_FOR_SID 0x101 - -struct trans2_req { - /* struct smb_hdr hdr precedes. Set wct = 14+ */ - __le16 TotalParameterCount; - __le16 TotalDataCount; - __le16 MaxParameterCount; - __le16 MaxDataCount; - __u8 MaxSetupCount; - __u8 Reserved; - __le16 Flags; - __le32 Timeout; - __u16 Reserved2; - __le16 ParameterCount; - __le16 ParameterOffset; - __le16 DataCount; - __le16 DataOffset; - __u8 SetupCount; - __u8 Reserved3; - __le16 SubCommand; /* 1st setup word - SetupCount words follow */ - __le16 ByteCount; -} __packed; - -struct smb_t2_req { - struct smb_hdr hdr; - struct trans2_req t2_req; -} __packed; - -struct trans2_resp { - /* struct smb_hdr hdr precedes. Note wct = 10 + setup count */ - __le16 TotalParameterCount; - __le16 TotalDataCount; - __u16 Reserved; - __le16 ParameterCount; - __le16 ParameterOffset; - __le16 ParameterDisplacement; - __le16 DataCount; - __le16 DataOffset; - __le16 DataDisplacement; - __u8 SetupCount; - __u8 Reserved1; - /* SetupWords[SetupCount]; - __u16 ByteCount; - __u16 Reserved2;*/ - /* data area follows */ -} __packed; - -struct smb_t2_rsp { - struct smb_hdr hdr; - struct trans2_resp t2_rsp; -} __packed; - -/* PathInfo/FileInfo infolevels */ -#define SMB_INFO_STANDARD 1 -#define SMB_SET_FILE_EA 2 -#define SMB_QUERY_FILE_EA_SIZE 2 -#define SMB_INFO_QUERY_EAS_FROM_LIST 3 -#define SMB_INFO_QUERY_ALL_EAS 4 -#define SMB_INFO_IS_NAME_VALID 6 -#define SMB_QUERY_FILE_BASIC_INFO 0x101 -#define SMB_QUERY_FILE_STANDARD_INFO 0x102 -#define SMB_QUERY_FILE_EA_INFO 0x103 -#define SMB_QUERY_FILE_NAME_INFO 0x104 -#define SMB_QUERY_FILE_ALLOCATION_INFO 0x105 -#define SMB_QUERY_FILE_END_OF_FILEINFO 0x106 -#define SMB_QUERY_FILE_ALL_INFO 0x107 -#define SMB_QUERY_ALT_NAME_INFO 0x108 -#define SMB_QUERY_FILE_STREAM_INFO 0x109 -#define SMB_QUERY_FILE_COMPRESSION_INFO 0x10B -#define SMB_QUERY_FILE_UNIX_BASIC 0x200 -#define SMB_QUERY_FILE_UNIX_LINK 0x201 -#define SMB_QUERY_POSIX_ACL 0x204 -#define SMB_QUERY_XATTR 0x205 /* e.g. system EA name space */ -#define SMB_QUERY_ATTR_FLAGS 0x206 /* append,immutable etc. */ -#define SMB_QUERY_POSIX_PERMISSION 0x207 -#define SMB_QUERY_POSIX_LOCK 0x208 -/* #define SMB_POSIX_OPEN 0x209 */ -/* #define SMB_POSIX_UNLINK 0x20a */ -#define SMB_QUERY_FILE__UNIX_INFO2 0x20b -#define SMB_QUERY_FILE_INTERNAL_INFO 0x3ee -#define SMB_QUERY_FILE_ACCESS_INFO 0x3f0 -#define SMB_QUERY_FILE_NAME_INFO2 0x3f1 /* 0x30 bytes */ -#define SMB_QUERY_FILE_POSITION_INFO 0x3f6 -#define SMB_QUERY_FILE_MODE_INFO 0x3f8 -#define SMB_QUERY_FILE_ALGN_INFO 0x3f9 - - -#define SMB_SET_FILE_BASIC_INFO 0x101 -#define SMB_SET_FILE_DISPOSITION_INFO 0x102 -#define SMB_SET_FILE_ALLOCATION_INFO 0x103 -#define SMB_SET_FILE_END_OF_FILE_INFO 0x104 -#define SMB_SET_FILE_UNIX_BASIC 0x200 -#define SMB_SET_FILE_UNIX_LINK 0x201 -#define SMB_SET_FILE_UNIX_HLINK 0x203 -#define SMB_SET_POSIX_ACL 0x204 -#define SMB_SET_XATTR 0x205 -#define SMB_SET_ATTR_FLAGS 0x206 /* append, immutable etc. */ -#define SMB_SET_POSIX_LOCK 0x208 -#define SMB_POSIX_OPEN 0x209 -#define SMB_POSIX_UNLINK 0x20a -#define SMB_SET_FILE_UNIX_INFO2 0x20b -#define SMB_SET_FILE_BASIC_INFO2 0x3ec -#define SMB_SET_FILE_RENAME_INFORMATION 0x3f2 /* BB check if qpathinfo too */ -#define SMB_FILE_ALL_INFO2 0x3fa -#define SMB_SET_FILE_ALLOCATION_INFO2 0x3fb -#define SMB_SET_FILE_END_OF_FILE_INFO2 0x3fc -#define SMB_FILE_MOVE_CLUSTER_INFO 0x407 -#define SMB_FILE_QUOTA_INFO 0x408 -#define SMB_FILE_REPARSEPOINT_INFO 0x409 -#define SMB_FILE_MAXIMUM_INFO 0x40d - -/* Find File infolevels */ -#define SMB_FIND_FILE_INFO_STANDARD 0x001 -#define SMB_FIND_FILE_QUERY_EA_SIZE 0x002 -#define SMB_FIND_FILE_QUERY_EAS_FROM_LIST 0x003 -#define SMB_FIND_FILE_DIRECTORY_INFO 0x101 -#define SMB_FIND_FILE_FULL_DIRECTORY_INFO 0x102 -#define SMB_FIND_FILE_NAMES_INFO 0x103 -#define SMB_FIND_FILE_BOTH_DIRECTORY_INFO 0x104 -#define SMB_FIND_FILE_ID_FULL_DIR_INFO 0x105 -#define SMB_FIND_FILE_ID_BOTH_DIR_INFO 0x106 -#define SMB_FIND_FILE_UNIX 0x202 -/* #define SMB_FIND_FILE_POSIX_INFO 0x064 */ - -typedef struct smb_com_transaction2_qpi_req { - struct smb_hdr hdr; /* wct = 14+ */ - __le16 TotalParameterCount; - __le16 TotalDataCount; - __le16 MaxParameterCount; - __le16 MaxDataCount; - __u8 MaxSetupCount; - __u8 Reserved; - __le16 Flags; - __le32 Timeout; - __u16 Reserved2; - __le16 ParameterCount; - __le16 ParameterOffset; - __le16 DataCount; - __le16 DataOffset; - __u8 SetupCount; - __u8 Reserved3; - __le16 SubCommand; /* one setup word */ - __le16 ByteCount; - __u8 Pad; - __le16 InformationLevel; - __u32 Reserved4; - char FileName[]; -} __packed TRANSACTION2_QPI_REQ; - -typedef struct smb_com_transaction2_qpi_rsp { - struct smb_hdr hdr; /* wct = 10 + SetupCount */ - struct trans2_resp t2; - __u16 ByteCount; - __u16 Reserved2; /* parameter word is present for infolevels > 100 */ -} __packed TRANSACTION2_QPI_RSP; - -typedef struct smb_com_transaction2_spi_req { - struct smb_hdr hdr; /* wct = 15 */ - __le16 TotalParameterCount; - __le16 TotalDataCount; - __le16 MaxParameterCount; - __le16 MaxDataCount; - __u8 MaxSetupCount; - __u8 Reserved; - __le16 Flags; - __le32 Timeout; - __u16 Reserved2; - __le16 ParameterCount; - __le16 ParameterOffset; - __le16 DataCount; - __le16 DataOffset; - __u8 SetupCount; - __u8 Reserved3; - __le16 SubCommand; /* one setup word */ - __le16 ByteCount; - __u8 Pad; - __u16 Pad1; - __le16 InformationLevel; - __u32 Reserved4; - char FileName[]; -} __packed TRANSACTION2_SPI_REQ; - -typedef struct smb_com_transaction2_spi_rsp { - struct smb_hdr hdr; /* wct = 10 + SetupCount */ - struct trans2_resp t2; - __u16 ByteCount; - __u16 Reserved2; /* parameter word is present for infolevels > 100 */ -} __packed TRANSACTION2_SPI_RSP; - -struct set_file_rename { - __le32 overwrite; /* 1 = overwrite dest */ - __u32 root_fid; /* zero */ - __le32 target_name_len; - char target_name[]; /* Must be unicode */ -} __packed; - -struct smb_com_transaction2_sfi_req { - struct smb_hdr hdr; /* wct = 15 */ - __le16 TotalParameterCount; - __le16 TotalDataCount; - __le16 MaxParameterCount; - __le16 MaxDataCount; - __u8 MaxSetupCount; - __u8 Reserved; - __le16 Flags; - __le32 Timeout; - __u16 Reserved2; - __le16 ParameterCount; - __le16 ParameterOffset; - __le16 DataCount; - __le16 DataOffset; - __u8 SetupCount; - __u8 Reserved3; - __le16 SubCommand; /* one setup word */ - __le16 ByteCount; - __u8 Pad; - __u16 Pad1; - __u16 Fid; - __le16 InformationLevel; - __u16 Reserved4; - __u8 payload[]; -} __packed; - -struct smb_com_transaction2_sfi_rsp { - struct smb_hdr hdr; /* wct = 10 + SetupCount */ - struct trans2_resp t2; - __u16 ByteCount; - __u16 Reserved2; /* parameter word reserved - present for infolevels > 100 */ -} __packed; - -struct smb_t2_qfi_req { - struct smb_hdr hdr; - struct trans2_req t2; - __u8 Pad; - __u16 Fid; - __le16 InformationLevel; -} __packed; - -struct smb_t2_qfi_rsp { - struct smb_hdr hdr; /* wct = 10 + SetupCount */ - struct trans2_resp t2; - __u16 ByteCount; - __u16 Reserved2; /* parameter word reserved - present for infolevels > 100 */ -} __packed; - -/* - * Flags on T2 FINDFIRST and FINDNEXT - */ -#define CIFS_SEARCH_CLOSE_ALWAYS 0x0001 -#define CIFS_SEARCH_CLOSE_AT_END 0x0002 -#define CIFS_SEARCH_RETURN_RESUME 0x0004 -#define CIFS_SEARCH_CONTINUE_FROM_LAST 0x0008 -#define CIFS_SEARCH_BACKUP_SEARCH 0x0010 - -/* - * Size of the resume key on FINDFIRST and FINDNEXT calls - */ -#define CIFS_SMB_RESUME_KEY_SIZE 4 - -typedef struct smb_com_transaction2_ffirst_req { - struct smb_hdr hdr; /* wct = 15 */ - __le16 TotalParameterCount; - __le16 TotalDataCount; - __le16 MaxParameterCount; - __le16 MaxDataCount; - __u8 MaxSetupCount; - __u8 Reserved; - __le16 Flags; - __le32 Timeout; - __u16 Reserved2; - __le16 ParameterCount; - __le16 ParameterOffset; - __le16 DataCount; - __le16 DataOffset; - __u8 SetupCount; /* one */ - __u8 Reserved3; - __le16 SubCommand; /* TRANS2_FIND_FIRST */ - __le16 ByteCount; - __u8 Pad; - __le16 SearchAttributes; - __le16 SearchCount; - __le16 SearchFlags; - __le16 InformationLevel; - __le32 SearchStorageType; - char FileName[]; -} __packed TRANSACTION2_FFIRST_REQ; - -typedef struct smb_com_transaction2_ffirst_rsp { - struct smb_hdr hdr; /* wct = 10 */ - struct trans2_resp t2; - __u16 ByteCount; -} __packed TRANSACTION2_FFIRST_RSP; - -typedef struct smb_com_transaction2_ffirst_rsp_parms { - __u16 SearchHandle; - __le16 SearchCount; - __le16 EndofSearch; - __le16 EAErrorOffset; - __le16 LastNameOffset; -} __packed T2_FFIRST_RSP_PARMS; - -typedef struct smb_com_transaction2_fnext_req { - struct smb_hdr hdr; /* wct = 15 */ - __le16 TotalParameterCount; - __le16 TotalDataCount; - __le16 MaxParameterCount; - __le16 MaxDataCount; - __u8 MaxSetupCount; - __u8 Reserved; - __le16 Flags; - __le32 Timeout; - __u16 Reserved2; - __le16 ParameterCount; - __le16 ParameterOffset; - __le16 DataCount; - __le16 DataOffset; - __u8 SetupCount; /* one */ - __u8 Reserved3; - __le16 SubCommand; /* TRANS2_FIND_NEXT */ - __le16 ByteCount; - __u8 Pad; - __u16 SearchHandle; - __le16 SearchCount; - __le16 InformationLevel; - __u32 ResumeKey; - __le16 SearchFlags; - char ResumeFileName[]; -} __packed TRANSACTION2_FNEXT_REQ; - -typedef struct smb_com_transaction2_fnext_rsp { - struct smb_hdr hdr; /* wct = 10 */ - struct trans2_resp t2; - __u16 ByteCount; -} __packed TRANSACTION2_FNEXT_RSP; - -typedef struct smb_com_transaction2_fnext_rsp_parms { - __le16 SearchCount; - __le16 EndofSearch; - __le16 EAErrorOffset; - __le16 LastNameOffset; -} __packed T2_FNEXT_RSP_PARMS; - -/* QFSInfo Levels */ -#define SMB_INFO_ALLOCATION 1 -#define SMB_INFO_VOLUME 2 -#define SMB_QUERY_FS_VOLUME_INFO 0x102 -#define SMB_QUERY_FS_SIZE_INFO 0x103 -#define SMB_QUERY_FS_DEVICE_INFO 0x104 -#define SMB_QUERY_FS_ATTRIBUTE_INFO 0x105 -#define SMB_QUERY_CIFS_UNIX_INFO 0x200 -#define SMB_QUERY_POSIX_FS_INFO 0x201 -#define SMB_QUERY_POSIX_WHO_AM_I 0x202 -#define SMB_REQUEST_TRANSPORT_ENCRYPTION 0x203 -#define SMB_QUERY_FS_PROXY 0x204 /* WAFS enabled. Returns structure - FILE_SYSTEM__UNIX_INFO to tell - whether new NTIOCTL available - (0xACE) for WAN friendly SMB - operations to be carried */ -#define SMB_QUERY_LABEL_INFO 0x3ea -#define SMB_QUERY_FS_QUOTA_INFO 0x3ee -#define SMB_QUERY_FS_FULL_SIZE_INFO 0x3ef -#define SMB_QUERY_OBJECTID_INFO 0x3f0 - -typedef struct smb_com_transaction2_qfsi_req { - struct smb_hdr hdr; /* wct = 14+ */ - __le16 TotalParameterCount; - __le16 TotalDataCount; - __le16 MaxParameterCount; - __le16 MaxDataCount; - __u8 MaxSetupCount; - __u8 Reserved; - __le16 Flags; - __le32 Timeout; - __u16 Reserved2; - __le16 ParameterCount; - __le16 ParameterOffset; - __le16 DataCount; - __le16 DataOffset; - __u8 SetupCount; - __u8 Reserved3; - __le16 SubCommand; /* one setup word */ - __le16 ByteCount; - __u8 Pad; - __le16 InformationLevel; -} __packed TRANSACTION2_QFSI_REQ; - -typedef struct smb_com_transaction_qfsi_rsp { - struct smb_hdr hdr; /* wct = 10 + SetupCount */ - struct trans2_resp t2; - __u16 ByteCount; - __u8 Pad; /* may be three bytes? *//* followed by data area */ -} __packed TRANSACTION2_QFSI_RSP; - -typedef struct whoami_rsp_data { /* Query level 0x202 */ - __u32 flags; /* 0 = Authenticated user 1 = GUEST */ - __u32 mask; /* which flags bits server understands ie 0x0001 */ - __u64 unix_user_id; - __u64 unix_user_gid; - __u32 number_of_supplementary_gids; /* may be zero */ - __u32 number_of_sids; /* may be zero */ - __u32 length_of_sid_array; /* in bytes - may be zero */ - __u32 pad; /* reserved - MBZ */ - /* __u64 gid_array[0]; */ /* may be empty */ - /* __u8 * psid_list */ /* may be empty */ -} __packed WHOAMI_RSP_DATA; - -/* SETFSInfo Levels */ -#define SMB_SET_CIFS_UNIX_INFO 0x200 -/* level 0x203 is defined above in list of QFS info levels */ -/* #define SMB_REQUEST_TRANSPORT_ENCRYPTION 0x203 */ - -/* Level 0x200 request structure follows */ -typedef struct smb_com_transaction2_setfsi_req { - struct smb_hdr hdr; /* wct = 15 */ - __le16 TotalParameterCount; - __le16 TotalDataCount; - __le16 MaxParameterCount; - __le16 MaxDataCount; - __u8 MaxSetupCount; - __u8 Reserved; - __le16 Flags; - __le32 Timeout; - __u16 Reserved2; - __le16 ParameterCount; /* 4 */ - __le16 ParameterOffset; - __le16 DataCount; /* 12 */ - __le16 DataOffset; - __u8 SetupCount; /* one */ - __u8 Reserved3; - __le16 SubCommand; /* TRANS2_SET_FS_INFORMATION */ - __le16 ByteCount; - __u8 Pad; - __u16 FileNum; /* Parameters start. */ - __le16 InformationLevel;/* Parameters end. */ - __le16 ClientUnixMajor; /* Data start. */ - __le16 ClientUnixMinor; - __le64 ClientUnixCap; /* Data end */ -} __packed TRANSACTION2_SETFSI_REQ; - -/* level 0x203 request structure follows */ -typedef struct smb_com_transaction2_setfs_enc_req { - struct smb_hdr hdr; /* wct = 15 */ - __le16 TotalParameterCount; - __le16 TotalDataCount; - __le16 MaxParameterCount; - __le16 MaxDataCount; - __u8 MaxSetupCount; - __u8 Reserved; - __le16 Flags; - __le32 Timeout; - __u16 Reserved2; - __le16 ParameterCount; /* 4 */ - __le16 ParameterOffset; - __le16 DataCount; /* 12 */ - __le16 DataOffset; - __u8 SetupCount; /* one */ - __u8 Reserved3; - __le16 SubCommand; /* TRANS2_SET_FS_INFORMATION */ - __le16 ByteCount; - __u8 Pad; - __u16 Reserved4; /* Parameters start. */ - __le16 InformationLevel;/* Parameters end. */ - /* NTLMSSP Blob, Data start. */ -} __packed TRANSACTION2_SETFSI_ENC_REQ; - -/* response for setfsinfo levels 0x200 and 0x203 */ -typedef struct smb_com_transaction2_setfsi_rsp { - struct smb_hdr hdr; /* wct = 10 */ - struct trans2_resp t2; - __u16 ByteCount; -} __packed TRANSACTION2_SETFSI_RSP; - -typedef struct smb_com_transaction2_get_dfs_refer_req { - struct smb_hdr hdr; /* wct = 15 */ - __le16 TotalParameterCount; - __le16 TotalDataCount; - __le16 MaxParameterCount; - __le16 MaxDataCount; - __u8 MaxSetupCount; - __u8 Reserved; - __le16 Flags; - __le32 Timeout; - __u16 Reserved2; - __le16 ParameterCount; - __le16 ParameterOffset; - __le16 DataCount; - __le16 DataOffset; - __u8 SetupCount; - __u8 Reserved3; - __le16 SubCommand; /* one setup word */ - __le16 ByteCount; - __u8 Pad[3]; /* Win2K has sent 0x0F01 (max response length - perhaps?) followed by one byte pad - doesn't - seem to matter though */ - __le16 MaxReferralLevel; - char RequestFileName[]; -} __packed TRANSACTION2_GET_DFS_REFER_REQ; - -#define DFS_VERSION cpu_to_le16(0x0003) - -/* DFS server target type */ -#define DFS_TYPE_LINK 0x0000 /* also for sysvol targets */ -#define DFS_TYPE_ROOT 0x0001 - -/* Referral Entry Flags */ -#define DFS_NAME_LIST_REF 0x0200 /* set for domain or DC referral responses */ -#define DFS_TARGET_SET_BOUNDARY 0x0400 /* only valid with version 4 dfs req */ - -typedef struct dfs_referral_level_3 { /* version 4 is same, + one flag bit */ - __le16 VersionNumber; /* must be 3 or 4 */ - __le16 Size; - __le16 ServerType; /* 0x0001 = root targets; 0x0000 = link targets */ - __le16 ReferralEntryFlags; - __le32 TimeToLive; - __le16 DfsPathOffset; - __le16 DfsAlternatePathOffset; - __le16 NetworkAddressOffset; /* offset of the link target */ - __u8 ServiceSiteGuid[16]; /* MBZ, ignored */ -} __packed REFERRAL3; - -struct get_dfs_referral_rsp { - __le16 PathConsumed; - __le16 NumberOfReferrals; - __le32 DFSFlags; - REFERRAL3 referrals[]; /* array of level 3 dfs_referral structures */ - /* followed by the strings pointed to by the referral structures */ -} __packed; - -typedef struct smb_com_transaction_get_dfs_refer_rsp { - struct smb_hdr hdr; /* wct = 10 */ - struct trans2_resp t2; - __u16 ByteCount; - __u8 Pad; - struct get_dfs_referral_rsp dfs_data; -} __packed TRANSACTION2_GET_DFS_REFER_RSP; - -/* DFS Flags */ -#define DFSREF_REFERRAL_SERVER 0x00000001 /* all targets are DFS roots */ -#define DFSREF_STORAGE_SERVER 0x00000002 /* no further ref requests needed */ -#define DFSREF_TARGET_FAILBACK 0x00000004 /* only for DFS referral version 4 */ - -/* - ************************************************************************ - * All structs for everything above the SMB PDUs themselves - * (such as the T2 level specific data) go here - ************************************************************************ - */ - -/* - * Information on a server - */ - -struct serverInfo { - char name[16]; - unsigned char versionMajor; - unsigned char versionMinor; - unsigned long type; - unsigned int commentOffset; -} __packed; - -/* - * The following structure is the format of the data returned on a NetShareEnum - * with level "90" (x5A) - */ - -struct shareInfo { - char shareName[13]; - char pad; - unsigned short type; - unsigned int commentOffset; -} __packed; - -struct aliasInfo { - char aliasName[9]; - char pad; - unsigned int commentOffset; - unsigned char type[2]; -} __packed; - -struct aliasInfo92 { - int aliasNameOffset; - int serverNameOffset; - int shareNameOffset; -} __packed; - -typedef struct { - __le32 fsid; - __le32 SectorsPerAllocationUnit; - __le32 TotalAllocationUnits; - __le32 FreeAllocationUnits; - __le16 BytesPerSector; -} __packed FILE_SYSTEM_ALLOC_INFO; - -typedef struct { - __le16 MajorVersionNumber; - __le16 MinorVersionNumber; - __le64 Capability; -} __packed FILE_SYSTEM_UNIX_INFO; /* Unix extension level 0x200*/ - -/* Version numbers for CIFS UNIX major and minor. */ -#define CIFS_UNIX_MAJOR_VERSION 1 -#define CIFS_UNIX_MINOR_VERSION 0 - -/* Linux/Unix extensions capability flags */ -#define CIFS_UNIX_FCNTL_CAP 0x00000001 /* support for fcntl locks */ -#define CIFS_UNIX_POSIX_ACL_CAP 0x00000002 /* support getfacl/setfacl */ -#define CIFS_UNIX_XATTR_CAP 0x00000004 /* support new namespace */ -#define CIFS_UNIX_EXTATTR_CAP 0x00000008 /* support chattr/chflag */ -#define CIFS_UNIX_POSIX_PATHNAMES_CAP 0x00000010 /* Allow POSIX path chars */ -#define CIFS_UNIX_POSIX_PATH_OPS_CAP 0x00000020 /* Allow new POSIX path based - calls including posix open - and posix unlink */ -#define CIFS_UNIX_LARGE_READ_CAP 0x00000040 /* support reads >128K (up to 0xFFFF00 */ -#define CIFS_UNIX_LARGE_WRITE_CAP 0x00000080 -#define CIFS_UNIX_TRANSPORT_ENCRYPTION_CAP 0x00000100 /* can do SPNEGO crypt */ -#define CIFS_UNIX_TRANSPORT_ENCRYPTION_MANDATORY_CAP 0x00000200 /* must do */ -#define CIFS_UNIX_PROXY_CAP 0x00000400 /* Proxy cap: 0xACE ioctl and QFS PROXY call */ -#ifdef CONFIG_CIFS_POSIX -/* presumably don't need the 0x20 POSIX_PATH_OPS_CAP since we never send - LockingX instead of posix locking call on unix sess (and we do not expect - LockingX to use different (ie Windows) semantics than posix locking on - the same session (if WINE needs to do this later, we can add this cap - back in later */ -/* #define CIFS_UNIX_CAP_MASK 0x000000fb */ -#define CIFS_UNIX_CAP_MASK 0x000003db -#else -#define CIFS_UNIX_CAP_MASK 0x00000013 -#endif /* CONFIG_CIFS_POSIX */ - - -#define CIFS_POSIX_EXTENSIONS 0x00000010 /* support for new QFSInfo */ - -/******************************************************************************/ -/* QueryFileInfo/QueryPathinfo (also for SetPath/SetFile) data buffer formats */ -/******************************************************************************/ -typedef struct { /* data block encoding of response to level 263 QPathInfo */ - struct_group_attr(common_attributes, __packed, - __le64 CreationTime; - __le64 LastAccessTime; - __le64 LastWriteTime; - __le64 ChangeTime; - __le32 Attributes; - ); - __u32 Pad1; - __le64 AllocationSize; - __le64 EndOfFile; /* size ie offset to first free byte in file */ - __le32 NumberOfLinks; /* hard links */ - __u8 DeletePending; - __u8 Directory; - __u16 Pad2; - __le32 EASize; - __le32 FileNameLength; - union { - char __pad; - DECLARE_FLEX_ARRAY(char, FileName); - }; -} __packed FILE_ALL_INFO; /* level 0x107 QPathInfo */ - -typedef struct { - __le64 AllocationSize; - __le64 EndOfFile; /* size ie offset to first free byte in file */ - __le32 NumberOfLinks; /* hard links */ - __u8 DeletePending; - __u8 Directory; - __u16 Pad; -} __packed FILE_STANDARD_INFO; /* level 0x102 QPathInfo */ - - -/* defines for enumerating possible values of the Unix type field below */ -#define UNIX_FILE 0 -#define UNIX_DIR 1 -#define UNIX_SYMLINK 2 -#define UNIX_CHARDEV 3 -#define UNIX_BLOCKDEV 4 -#define UNIX_FIFO 5 -#define UNIX_SOCKET 6 -typedef struct { - __le64 EndOfFile; - __le64 NumOfBytes; - __le64 LastStatusChange; /*SNIA specs DCE time for the 3 time fields */ - __le64 LastAccessTime; - __le64 LastModificationTime; - __le64 Uid; - __le64 Gid; - __le32 Type; - __le64 DevMajor; - __le64 DevMinor; - __le64 UniqueId; - __le64 Permissions; - __le64 Nlinks; -} __packed FILE_UNIX_BASIC_INFO; /* level 0x200 QPathInfo */ - -typedef struct { - DECLARE_FLEX_ARRAY(char, LinkDest); -} __packed FILE_UNIX_LINK_INFO; /* level 0x201 QPathInfo */ - -/* The following three structures are needed only for - setting time to NT4 and some older servers via - the primitive DOS time format */ -typedef struct { - __u16 Day:5; - __u16 Month:4; - __u16 Year:7; -} __packed SMB_DATE; - -typedef struct { - __u16 TwoSeconds:5; - __u16 Minutes:6; - __u16 Hours:5; -} __packed SMB_TIME; - -typedef struct { - __le16 CreationDate; /* SMB Date see above */ - __le16 CreationTime; /* SMB Time */ - __le16 LastAccessDate; - __le16 LastAccessTime; - __le16 LastWriteDate; - __le16 LastWriteTime; - __le32 DataSize; /* File Size (EOF) */ - __le32 AllocationSize; - __le16 Attributes; /* verify not u32 */ - __le32 EASize; -} __packed FILE_INFO_STANDARD; /* level 1 SetPath/FileInfo */ - -typedef struct { - __le64 CreationTime; - __le64 LastAccessTime; - __le64 LastWriteTime; - __le64 ChangeTime; - __le32 Attributes; - __u32 Pad; -} __packed FILE_BASIC_INFO; /* size info, level 0x101 */ - -struct file_allocation_info { - __le64 AllocationSize; /* Note old Samba srvr rounds this up too much */ -} __packed; /* size used on disk, for level 0x103 for set, 0x105 for query */ - -struct file_end_of_file_info { - __le64 FileSize; /* offset to end of file */ -} __packed; /* size info, level 0x104 for set, 0x106 for query */ - -struct file_alt_name_info { - DECLARE_FLEX_ARRAY(__u8, alt_name); -} __packed; /* level 0x0108 */ - -struct file_stream_info { - __le32 number_of_streams; /* BB check sizes and verify location */ - /* followed by info on streams themselves - u64 size; - u64 allocation_size - stream info */ -}; /* level 0x109 */ - -struct file_compression_info { - __le64 compressed_size; - __le16 format; - __u8 unit_shift; - __u8 ch_shift; - __u8 cl_shift; - __u8 pad[3]; -} __packed; /* level 0x10b */ - -/* POSIX ACL set/query path info structures */ -#define CIFS_ACL_VERSION 1 -struct cifs_posix_ace { /* access control entry (ACE) */ - __u8 cifs_e_tag; - __u8 cifs_e_perm; - __le64 cifs_uid; /* or gid */ -} __packed; - -struct cifs_posix_acl { /* access control list (ACL) */ - __le16 version; - __le16 access_entry_count; /* access ACL - count of entries */ - __le16 default_entry_count; /* default ACL - count of entries */ - struct cifs_posix_ace ace_array[]; - /* followed by struct cifs_posix_ace default_ace_array[] */ -} __packed; /* level 0x204 */ - -/* types of access control entries already defined in posix_acl.h */ -/* #define CIFS_POSIX_ACL_USER_OBJ 0x01 -#define CIFS_POSIX_ACL_USER 0x02 -#define CIFS_POSIX_ACL_GROUP_OBJ 0x04 -#define CIFS_POSIX_ACL_GROUP 0x08 -#define CIFS_POSIX_ACL_MASK 0x10 -#define CIFS_POSIX_ACL_OTHER 0x20 */ - -/* types of perms */ -/* #define CIFS_POSIX_ACL_EXECUTE 0x01 -#define CIFS_POSIX_ACL_WRITE 0x02 -#define CIFS_POSIX_ACL_READ 0x04 */ - -/* end of POSIX ACL definitions */ - -/* POSIX Open Flags */ -#define SMB_O_RDONLY 0x1 -#define SMB_O_WRONLY 0x2 -#define SMB_O_RDWR 0x4 -#define SMB_O_CREAT 0x10 -#define SMB_O_EXCL 0x20 -#define SMB_O_TRUNC 0x40 -#define SMB_O_APPEND 0x80 -#define SMB_O_SYNC 0x100 -#define SMB_O_DIRECTORY 0x200 -#define SMB_O_NOFOLLOW 0x400 -#define SMB_O_DIRECT 0x800 - -typedef struct { - __le32 OpenFlags; /* same as NT CreateX */ - __le32 PosixOpenFlags; - __le64 Permissions; - __le16 Level; /* reply level requested (see QPathInfo levels) */ -} __packed OPEN_PSX_REQ; /* level 0x209 SetPathInfo data */ - -typedef struct { - __le16 OplockFlags; - __u16 Fid; - __le32 CreateAction; - __le16 ReturnedLevel; - __le16 Pad; - /* struct following varies based on requested level */ -} __packed OPEN_PSX_RSP; /* level 0x209 SetPathInfo data */ - -#define SMB_POSIX_UNLINK_FILE_TARGET 0 -#define SMB_POSIX_UNLINK_DIRECTORY_TARGET 1 - -struct unlink_psx_rq { /* level 0x20a SetPathInfo */ - __le16 type; -} __packed; - -struct file_internal_info { - __le64 UniqueId; /* inode number */ -} __packed; /* level 0x3ee */ - -struct file_mode_info { - __le32 Mode; -} __packed; /* level 0x3f8 */ - -struct file_attrib_tag { - __le32 Attribute; - __le32 ReparseTag; -} __packed; /* level 0x40b */ - - -/********************************************************/ -/* FindFirst/FindNext transact2 data buffer formats */ -/********************************************************/ - -typedef struct { - __le32 NextEntryOffset; - __u32 ResumeKey; /* as with FileIndex - no need to convert */ - FILE_UNIX_BASIC_INFO basic; - union { - char __pad; - DECLARE_FLEX_ARRAY(char, FileName); - }; -} __packed FILE_UNIX_INFO; /* level 0x202 */ - -typedef struct { - __u32 ResumeKey; - __le16 CreationDate; /* SMB Date */ - __le16 CreationTime; /* SMB Time */ - __le16 LastAccessDate; - __le16 LastAccessTime; - __le16 LastWriteDate; - __le16 LastWriteTime; - __le32 DataSize; /* File Size (EOF) */ - __le32 AllocationSize; - __le16 Attributes; /* verify not u32 */ - __u8 FileNameLength; - char FileName[]; -} __packed FIND_FILE_STANDARD_INFO; /* level 0x1 FF resp data */ - - -struct fea { - unsigned char EA_flags; - __u8 name_len; - __le16 value_len; - char name[]; - /* optionally followed by value */ -} __packed; -/* flags for _FEA.fEA */ -#define FEA_NEEDEA 0x80 /* need EA bit */ - -struct fealist { - __le32 list_len; - struct fea list; -} __packed; - -/* used to hold an arbitrary blob of data */ -struct data_blob { - __u8 *data; - size_t length; - void (*free) (struct data_blob *data_blob); -} __packed; - - -#ifdef CONFIG_CIFS_POSIX -/* - For better POSIX semantics from Linux client, (even better - than the existing CIFS Unix Extensions) we need updated PDUs for: - - 1) PosixCreateX - to set and return the mode, inode#, device info and - perhaps add a CreateDevice - to create Pipes and other special .inodes - Also note POSIX open flags - 2) Close - to return the last write time to do cache across close - more safely - 3) FindFirst return unique inode number - what about resume key, two - forms short (matches readdir) and full (enough info to cache inodes) - 4) Mkdir - set mode - - And under consideration: - 5) FindClose2 (return nanosecond timestamp ??) - 6) Use nanosecond timestamps throughout all time fields if - corresponding attribute flag is set - 7) sendfile - handle based copy - - what about fixing 64 bit alignment - - There are also various legacy SMB/CIFS requests used as is - - From existing Lanman and NTLM dialects: - -------------------------------------- - NEGOTIATE - SESSION_SETUP_ANDX (BB which?) - TREE_CONNECT_ANDX (BB which wct?) - TREE_DISCONNECT (BB add volume timestamp on response) - LOGOFF_ANDX - DELETE (note delete open file behavior) - DELETE_DIRECTORY - READ_AND_X - WRITE_AND_X - LOCKING_AND_X (note posix lock semantics) - RENAME (note rename across dirs and open file rename posix behaviors) - NT_RENAME (for hardlinks) Is this good enough for all features? - FIND_CLOSE2 - TRANSACTION2 (18 cases) - SMB_SET_FILE_END_OF_FILE_INFO2 SMB_SET_PATH_END_OF_FILE_INFO2 - (BB verify that never need to set allocation size) - SMB_SET_FILE_BASIC_INFO2 (setting times - BB can it be done via - Unix ext?) - - COPY (note support for copy across directories) - FUTURE, OPTIONAL - setting/getting OS/2 EAs - FUTURE (BB can this handle - setting Linux xattrs perfectly) - OPTIONAL - dnotify - FUTURE, OPTIONAL - quota - FUTURE, OPTIONAL - - Note that various requests implemented for NT interop such as - NT_TRANSACT (IOCTL) QueryReparseInfo - are unneeded to servers compliant with the CIFS POSIX extensions - - From CIFS Unix Extensions: - ------------------------- - T2 SET_PATH_INFO (SMB_SET_FILE_UNIX_LINK) for symlinks - T2 SET_PATH_INFO (SMB_SET_FILE_BASIC_INFO2) - T2 QUERY_PATH_INFO (SMB_QUERY_FILE_UNIX_LINK) - T2 QUERY_PATH_INFO (SMB_QUERY_FILE_UNIX_BASIC) BB check for missing - inode fields - Actually a need QUERY_FILE_UNIX_INFO - since has inode num - BB what about a) blksize/blkbits/blocks - b) i_version - c) i_rdev - d) notify mask? - e) generation - f) size_seqcount - T2 FIND_FIRST/FIND_NEXT FIND_FILE_UNIX - TRANS2_GET_DFS_REFERRAL - OPTIONAL but recommended - T2_QFS_INFO QueryDevice/AttributeInfo - OPTIONAL - */ - -/* xsymlink is a symlink format (used by MacOS) that can be used - to save symlink info in a regular file when - mounted to operating systems that do not - support the cifs Unix extensions or EAs (for xattr - based symlinks). For such a file to be recognized - as containing symlink data: - - 1) file size must be 1067, - 2) signature must begin file data, - 3) length field must be set to ASCII representation - of a number which is less than or equal to 1024, - 4) md5 must match that of the path data */ - -struct xsymlink { - /* 1067 bytes */ - char signature[4]; /* XSym */ /* not null terminated */ - char cr0; /* \n */ -/* ASCII representation of length (4 bytes decimal) terminated by \n not null */ - char length[4]; - char cr1; /* \n */ -/* md5 of valid subset of path ie path[0] through path[length-1] */ - __u8 md5[32]; - char cr2; /* \n */ -/* if room left, then end with \n then 0x20s by convention but not required */ - char path[1024]; -} __packed; - -typedef struct file_xattr_info { - /* BB do we need another field for flags? BB */ - __u32 xattr_name_len; - __u32 xattr_value_len; - char xattr_name[]; - /* followed by xattr_value[xattr_value_len], no pad */ -} __packed FILE_XATTR_INFO; /* extended attribute info level 0x205 */ - -/* flags for lsattr and chflags commands removed arein uapi/linux/fs.h */ - -typedef struct file_chattr_info { - __le64 mask; /* list of all possible attribute bits */ - __le64 mode; /* list of actual attribute bits on this inode */ -} __packed FILE_CHATTR_INFO; /* ext attributes (chattr, chflags) level 0x206 */ -#endif /* POSIX */ -#endif /* _CIFSPDU_H */ +#endif /* _CIFSPDU_H */ diff --git a/fs/smb/client/cifsproto.h b/fs/smb/client/cifsproto.h index f8c0615d4ee4..96d6b5325aa3 100644 --- a/fs/smb/client/cifsproto.h +++ b/fs/smb/client/cifsproto.h @@ -14,6 +14,7 @@ #ifdef CONFIG_CIFS_DFS_UPCALL #include "dfs_cache.h" #endif +#include "smb1proto.h" struct statfs; struct smb_rqst; @@ -25,16 +26,15 @@ struct smb3_fs_context; ***************************************************************** */ -extern struct smb_hdr *cifs_buf_get(void); -extern void cifs_buf_release(void *); -extern struct smb_hdr *cifs_small_buf_get(void); -extern void cifs_small_buf_release(void *); -extern void free_rsp_buf(int, void *); -extern int smb_send_kvec(struct TCP_Server_Info *server, - struct msghdr *msg, - size_t *sent); -extern unsigned int _get_xid(void); -extern void _free_xid(unsigned int); +void *cifs_buf_get(void); +void cifs_buf_release(void *buf_to_free); +void *cifs_small_buf_get(void); +void cifs_small_buf_release(void *buf_to_free); +void free_rsp_buf(int resp_buftype, void *rsp); +int smb_send_kvec(struct TCP_Server_Info *server, struct msghdr *smb_msg, + size_t *sent); +unsigned int _get_xid(void); +void _free_xid(unsigned int xid); #define get_xid() \ ({ \ unsigned int __xid = _get_xid(); \ @@ -55,16 +55,16 @@ do { \ else \ trace_smb3_exit_done(curr_xid, __func__); \ } while (0) -extern int init_cifs_idmap(void); -extern void exit_cifs_idmap(void); -extern int init_cifs_spnego(void); -extern void exit_cifs_spnego(void); -extern const char *build_path_from_dentry(struct dentry *, void *); -char *__build_path_from_dentry_optional_prefix(struct dentry *direntry, void *page, - const char *tree, int tree_len, - bool prefix); -extern char *build_path_from_dentry_optional_prefix(struct dentry *direntry, - void *page, bool prefix); +int init_cifs_idmap(void); +void exit_cifs_idmap(void); +int init_cifs_spnego(void); +void exit_cifs_spnego(void); +const char *build_path_from_dentry(struct dentry *direntry, void *page); +char *__build_path_from_dentry_optional_prefix(struct dentry *direntry, + void *page, const char *tree, + int tree_len, bool prefix); +char *build_path_from_dentry_optional_prefix(struct dentry *direntry, + void *page, bool prefix); static inline void *alloc_dentry_path(void) { return __getname(); @@ -76,57 +76,43 @@ static inline void free_dentry_path(void *page) __putname(page); } -extern char *cifs_build_path_to_root(struct smb3_fs_context *ctx, - struct cifs_sb_info *cifs_sb, - struct cifs_tcon *tcon, - int add_treename); +char *cifs_build_path_to_root(struct smb3_fs_context *ctx, + struct cifs_sb_info *cifs_sb, + struct cifs_tcon *tcon, int add_treename); char *cifs_build_devname(char *nodename, const char *prepath); void delete_mid(struct TCP_Server_Info *server, struct mid_q_entry *mid); -void __release_mid(struct TCP_Server_Info *server, struct mid_q_entry *mid); -void cifs_wake_up_task(struct TCP_Server_Info *server, struct mid_q_entry *mid); -extern int cifs_handle_standard(struct TCP_Server_Info *server, - struct mid_q_entry *mid); -extern char *smb3_fs_context_fullpath(const struct smb3_fs_context *ctx, - char dirsep); -extern int smb3_parse_devname(const char *devname, struct smb3_fs_context *ctx); -extern int smb3_parse_opt(const char *options, const char *key, char **val); -extern int cifs_ipaddr_cmp(struct sockaddr *srcaddr, struct sockaddr *rhs); -extern bool cifs_match_ipaddr(struct sockaddr *srcaddr, struct sockaddr *rhs); -extern int cifs_discard_remaining_data(struct TCP_Server_Info *server); -extern int cifs_call_async(struct TCP_Server_Info *server, - struct smb_rqst *rqst, - mid_receive_t receive, mid_callback_t callback, - mid_handle_t handle, void *cbdata, const int flags, - const struct cifs_credits *exist_credits); -extern struct TCP_Server_Info *cifs_pick_channel(struct cifs_ses *ses); -extern int cifs_send_recv(const unsigned int xid, struct cifs_ses *ses, - struct TCP_Server_Info *server, - struct smb_rqst *rqst, int *resp_buf_type, - const int flags, struct kvec *resp_iov); -extern int compound_send_recv(const unsigned int xid, struct cifs_ses *ses, - struct TCP_Server_Info *server, - const int flags, const int num_rqst, - struct smb_rqst *rqst, int *resp_buf_type, - struct kvec *resp_iov); -int SendReceive(const unsigned int xid, struct cifs_ses *ses, - struct smb_hdr *in_buf, unsigned int in_len, - struct smb_hdr *out_buf, int *pbytes_returned, const int flags); -int SendReceiveNoRsp(const unsigned int xid, struct cifs_ses *ses, - char *in_buf, unsigned int in_len, int flags); -int cifs_sync_mid_result(struct mid_q_entry *mid, struct TCP_Server_Info *server); -struct mid_q_entry *cifs_setup_request(struct cifs_ses *ses, struct TCP_Server_Info *ignored, - struct smb_rqst *rqst); -struct mid_q_entry *cifs_setup_async_request(struct TCP_Server_Info *server, - struct smb_rqst *rqst); +void __release_mid(struct TCP_Server_Info *server, + struct mid_q_entry *midEntry); +void cifs_wake_up_task(struct TCP_Server_Info *server, + struct mid_q_entry *mid); +int cifs_handle_standard(struct TCP_Server_Info *server, + struct mid_q_entry *mid); +char *smb3_fs_context_fullpath(const struct smb3_fs_context *ctx, char dirsep); +int smb3_parse_devname(const char *devname, struct smb3_fs_context *ctx); +int smb3_parse_opt(const char *options, const char *key, char **val); +int cifs_ipaddr_cmp(struct sockaddr *srcaddr, struct sockaddr *rhs); +bool cifs_match_ipaddr(struct sockaddr *srcaddr, struct sockaddr *rhs); +int cifs_discard_remaining_data(struct TCP_Server_Info *server); +int cifs_call_async(struct TCP_Server_Info *server, struct smb_rqst *rqst, + mid_receive_t receive, mid_callback_t callback, + mid_handle_t handle, void *cbdata, const int flags, + const struct cifs_credits *exist_credits); +struct TCP_Server_Info *cifs_pick_channel(struct cifs_ses *ses); +int cifs_send_recv(const unsigned int xid, struct cifs_ses *ses, + struct TCP_Server_Info *server, struct smb_rqst *rqst, + int *resp_buf_type, const int flags, struct kvec *resp_iov); +int compound_send_recv(const unsigned int xid, struct cifs_ses *ses, + struct TCP_Server_Info *server, const int flags, + const int num_rqst, struct smb_rqst *rqst, + int *resp_buf_type, struct kvec *resp_iov); +int cifs_sync_mid_result(struct mid_q_entry *mid, + struct TCP_Server_Info *server); int __smb_send_rqst(struct TCP_Server_Info *server, int num_rqst, struct smb_rqst *rqst); -extern int cifs_check_receive(struct mid_q_entry *mid, - struct TCP_Server_Info *server, bool log_error); int wait_for_free_request(struct TCP_Server_Info *server, const int flags, unsigned int *instance); -extern int cifs_wait_mtu_credits(struct TCP_Server_Info *server, - size_t size, size_t *num, - struct cifs_credits *credits); +int cifs_wait_mtu_credits(struct TCP_Server_Info *server, size_t size, + size_t *num, struct cifs_credits *credits); static inline int send_cancel(struct cifs_ses *ses, struct TCP_Server_Info *server, @@ -137,482 +123,216 @@ send_cancel(struct cifs_ses *ses, struct TCP_Server_Info *server, server->ops->send_cancel(ses, server, rqst, mid, xid) : 0; } -int wait_for_response(struct TCP_Server_Info *server, struct mid_q_entry *midQ); -extern int SendReceive2(const unsigned int /* xid */ , struct cifs_ses *, - struct kvec *, int /* nvec to send */, - int * /* type of buf returned */, const int flags, - struct kvec * /* resp vec */); +int wait_for_response(struct TCP_Server_Info *server, struct mid_q_entry *mid); void smb2_query_server_interfaces(struct work_struct *work); -void -cifs_signal_cifsd_for_reconnect(struct TCP_Server_Info *server, - bool all_channels); -void -cifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server, - bool mark_smb_session); -extern int cifs_reconnect(struct TCP_Server_Info *server, - bool mark_smb_session); -int checkSMB(char *buf, unsigned int pdu_len, unsigned int len, - struct TCP_Server_Info *srvr); -extern bool is_valid_oplock_break(char *, struct TCP_Server_Info *); -extern bool backup_cred(struct cifs_sb_info *); -extern bool is_size_safe_to_change(struct cifsInodeInfo *cifsInode, __u64 eof, - bool from_readdir); -void cifs_write_subrequest_terminated(struct cifs_io_subrequest *wdata, ssize_t result); -extern struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *, int); -extern int cifs_get_writable_file(struct cifsInodeInfo *cifs_inode, - int flags, - struct cifsFileInfo **ret_file); -extern int cifs_get_writable_path(struct cifs_tcon *tcon, const char *name, - int flags, - struct cifsFileInfo **ret_file); -extern struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *, bool); -extern int cifs_get_readable_path(struct cifs_tcon *tcon, const char *name, - struct cifsFileInfo **ret_file); -extern int cifs_get_hardlink_path(struct cifs_tcon *tcon, struct inode *inode, - struct file *file); -extern unsigned int smbCalcSize(void *buf); -extern int decode_negTokenInit(unsigned char *security_blob, int length, +void cifs_signal_cifsd_for_reconnect(struct TCP_Server_Info *server, + bool all_channels); +void cifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server, + bool mark_smb_session); +int cifs_reconnect(struct TCP_Server_Info *server, bool mark_smb_session); +bool backup_cred(struct cifs_sb_info *cifs_sb); +bool is_size_safe_to_change(struct cifsInodeInfo *cifsInode, __u64 end_of_file, + bool from_readdir); +void cifs_write_subrequest_terminated(struct cifs_io_subrequest *wdata, + ssize_t result); +struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode, + int flags); +int cifs_get_writable_file(struct cifsInodeInfo *cifs_inode, int flags, + struct cifsFileInfo **ret_file); +int cifs_get_writable_path(struct cifs_tcon *tcon, const char *name, int flags, + struct cifsFileInfo **ret_file); +struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode, + bool fsuid_only); +int cifs_get_readable_path(struct cifs_tcon *tcon, const char *name, + struct cifsFileInfo **ret_file); +int cifs_get_hardlink_path(struct cifs_tcon *tcon, struct inode *inode, + struct file *file); +int decode_negTokenInit(unsigned char *security_blob, int length, struct TCP_Server_Info *server); -extern int cifs_convert_address(struct sockaddr *dst, const char *src, int len); -extern void cifs_set_port(struct sockaddr *addr, const unsigned short int port); -extern int map_smb_to_linux_error(char *buf, bool logErr); -extern int map_and_check_smb_error(struct TCP_Server_Info *server, - struct mid_q_entry *mid, bool logErr); -unsigned int header_assemble(struct smb_hdr *buffer, char smb_command, - const struct cifs_tcon *treeCon, int word_count - /* length of fixed section word count in two byte units */); -extern int small_smb_init_no_tc(const int smb_cmd, const int wct, - struct cifs_ses *ses, - void **request_buf); -extern int CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses, - struct TCP_Server_Info *server, - const struct nls_table *nls_cp); -extern struct timespec64 cifs_NTtimeToUnix(__le64 utc_nanoseconds_since_1601); -extern u64 cifs_UnixTimeToNT(struct timespec64); -extern struct timespec64 cnvrtDosUnixTm(__le16 le_date, __le16 le_time, - int offset); -extern void cifs_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock); -extern int cifs_get_writer(struct cifsInodeInfo *cinode); -extern void cifs_put_writer(struct cifsInodeInfo *cinode); -extern void cifs_done_oplock_break(struct cifsInodeInfo *cinode); -extern int cifs_unlock_range(struct cifsFileInfo *cfile, - struct file_lock *flock, const unsigned int xid); -extern int cifs_push_mandatory_locks(struct cifsFileInfo *cfile); - -extern void cifs_down_write(struct rw_semaphore *sem); +int cifs_convert_address(struct sockaddr *dst, const char *src, int len); +void cifs_set_port(struct sockaddr *addr, const unsigned short int port); +struct timespec64 cifs_NTtimeToUnix(__le64 ntutc); +u64 cifs_UnixTimeToNT(struct timespec64 t); +struct timespec64 cnvrtDosUnixTm(__le16 le_date, __le16 le_time, int offset); +void cifs_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock); +int cifs_get_writer(struct cifsInodeInfo *cinode); +void cifs_put_writer(struct cifsInodeInfo *cinode); +void cifs_done_oplock_break(struct cifsInodeInfo *cinode); +int cifs_unlock_range(struct cifsFileInfo *cfile, struct file_lock *flock, + unsigned int xid); +int cifs_push_mandatory_locks(struct cifsFileInfo *cfile); + +void cifs_down_write(struct rw_semaphore *sem); struct cifsFileInfo *cifs_new_fileinfo(struct cifs_fid *fid, struct file *file, struct tcon_link *tlink, __u32 oplock, const char *symlink_target); -extern int cifs_posix_open(const char *full_path, struct inode **inode, - struct super_block *sb, int mode, - unsigned int f_flags, __u32 *oplock, __u16 *netfid, - unsigned int xid); +int cifs_posix_open(const char *full_path, struct inode **pinode, + struct super_block *sb, int mode, unsigned int f_flags, + __u32 *poplock, __u16 *pnetfid, unsigned int xid); void cifs_fill_uniqueid(struct super_block *sb, struct cifs_fattr *fattr); -extern void cifs_unix_basic_to_fattr(struct cifs_fattr *fattr, - FILE_UNIX_BASIC_INFO *info, - struct cifs_sb_info *cifs_sb); -extern void cifs_dir_info_to_fattr(struct cifs_fattr *, FILE_DIRECTORY_INFO *, - struct cifs_sb_info *); -extern int cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr, - bool from_readdir); -extern struct inode *cifs_iget(struct super_block *sb, - struct cifs_fattr *fattr); +void cifs_unix_basic_to_fattr(struct cifs_fattr *fattr, + FILE_UNIX_BASIC_INFO *info, + struct cifs_sb_info *cifs_sb); +void cifs_dir_info_to_fattr(struct cifs_fattr *fattr, + FILE_DIRECTORY_INFO *info, + struct cifs_sb_info *cifs_sb); +int cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr, + bool from_readdir); +struct inode *cifs_iget(struct super_block *sb, struct cifs_fattr *fattr); int cifs_get_inode_info(struct inode **inode, const char *full_path, - struct cifs_open_info_data *data, struct super_block *sb, int xid, + struct cifs_open_info_data *data, + struct super_block *sb, int xid, const struct cifs_fid *fid); -extern int smb311_posix_get_inode_info(struct inode **inode, - const char *full_path, - struct cifs_open_info_data *data, - struct super_block *sb, - const unsigned int xid); -extern int cifs_get_inode_info_unix(struct inode **pinode, - const unsigned char *search_path, - struct super_block *sb, unsigned int xid); -extern int cifs_set_file_info(struct inode *inode, struct iattr *attrs, - unsigned int xid, const char *full_path, __u32 dosattr); -extern int cifs_rename_pending_delete(const char *full_path, - struct dentry *dentry, - const unsigned int xid); -extern int sid_to_id(struct cifs_sb_info *cifs_sb, struct smb_sid *psid, - struct cifs_fattr *fattr, uint sidtype); -extern int cifs_acl_to_fattr(struct cifs_sb_info *cifs_sb, - struct cifs_fattr *fattr, struct inode *inode, - bool get_mode_from_special_sid, - const char *path, const struct cifs_fid *pfid); -extern int id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode, - kuid_t uid, kgid_t gid); -extern struct smb_ntsd *get_cifs_acl(struct cifs_sb_info *cifssmb, struct inode *ino, - const char *path, u32 *plen, u32 info); -extern struct smb_ntsd *get_cifs_acl_by_fid(struct cifs_sb_info *cifssb, - const struct cifs_fid *pfid, u32 *plen, u32 info); -extern struct posix_acl *cifs_get_acl(struct mnt_idmap *idmap, - struct dentry *dentry, int type); -extern int cifs_set_acl(struct mnt_idmap *idmap, - struct dentry *dentry, struct posix_acl *acl, int type); -extern int set_cifs_acl(struct smb_ntsd *pntsd, __u32 len, struct inode *ino, - const char *path, int flag); -extern unsigned int setup_authusers_ACE(struct smb_ace *pace); -extern unsigned int setup_special_mode_ACE(struct smb_ace *pace, - bool posix, - __u64 nmode); -extern unsigned int setup_special_user_owner_ACE(struct smb_ace *pace); - -void dequeue_mid(struct TCP_Server_Info *server, struct mid_q_entry *mid, bool malformed); -extern int cifs_read_from_socket(struct TCP_Server_Info *server, char *buf, - unsigned int to_read); -extern ssize_t cifs_discard_from_socket(struct TCP_Server_Info *server, - size_t to_read); +int smb311_posix_get_inode_info(struct inode **inode, const char *full_path, + struct cifs_open_info_data *data, + struct super_block *sb, + const unsigned int xid); +int cifs_get_inode_info_unix(struct inode **pinode, + const unsigned char *full_path, + struct super_block *sb, unsigned int xid); +int cifs_set_file_info(struct inode *inode, struct iattr *attrs, + unsigned int xid, const char *full_path, __u32 dosattr); +int cifs_rename_pending_delete(const char *full_path, struct dentry *dentry, + const unsigned int xid); +int sid_to_id(struct cifs_sb_info *cifs_sb, struct smb_sid *psid, + struct cifs_fattr *fattr, uint sidtype); +int cifs_acl_to_fattr(struct cifs_sb_info *cifs_sb, struct cifs_fattr *fattr, + struct inode *inode, bool mode_from_special_sid, + const char *path, const struct cifs_fid *pfid); +int id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode, + kuid_t uid, kgid_t gid); +struct smb_ntsd *get_cifs_acl(struct cifs_sb_info *cifs_sb, + struct inode *inode, const char *path, + u32 *pacllen, u32 info); +struct smb_ntsd *get_cifs_acl_by_fid(struct cifs_sb_info *cifs_sb, + const struct cifs_fid *cifsfid, + u32 *pacllen, u32 info); +struct posix_acl *cifs_get_acl(struct mnt_idmap *idmap, struct dentry *dentry, + int type); +int cifs_set_acl(struct mnt_idmap *idmap, struct dentry *dentry, + struct posix_acl *acl, int type); +int set_cifs_acl(struct smb_ntsd *pnntsd, __u32 acllen, struct inode *inode, + const char *path, int aclflag); +unsigned int setup_authusers_ACE(struct smb_ace *pntace); +unsigned int setup_special_mode_ACE(struct smb_ace *pntace, bool posix, + __u64 nmode); +unsigned int setup_special_user_owner_ACE(struct smb_ace *pntace); + +void dequeue_mid(struct TCP_Server_Info *server, struct mid_q_entry *mid, + bool malformed); +int cifs_read_from_socket(struct TCP_Server_Info *server, char *buf, + unsigned int to_read); +ssize_t cifs_discard_from_socket(struct TCP_Server_Info *server, + size_t to_read); int cifs_read_iter_from_socket(struct TCP_Server_Info *server, - struct iov_iter *iter, - unsigned int to_read); -extern int cifs_setup_cifs_sb(struct cifs_sb_info *cifs_sb); + struct iov_iter *iter, unsigned int to_read); +int cifs_setup_cifs_sb(struct cifs_sb_info *cifs_sb); void cifs_mount_put_conns(struct cifs_mount_ctx *mnt_ctx); int cifs_mount_get_session(struct cifs_mount_ctx *mnt_ctx); int cifs_is_path_remote(struct cifs_mount_ctx *mnt_ctx); int cifs_mount_get_tcon(struct cifs_mount_ctx *mnt_ctx); -extern int cifs_match_super(struct super_block *, void *); -extern int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx); -extern void cifs_umount(struct cifs_sb_info *); -extern void cifs_mark_open_files_invalid(struct cifs_tcon *tcon); -extern void cifs_reopen_persistent_handles(struct cifs_tcon *tcon); - -extern bool cifs_find_lock_conflict(struct cifsFileInfo *cfile, __u64 offset, - __u64 length, __u8 type, __u16 flags, - struct cifsLockInfo **conf_lock, - int rw_check); -extern void cifs_add_pending_open(struct cifs_fid *fid, +int cifs_match_super(struct super_block *sb, void *data); +int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx); +void cifs_umount(struct cifs_sb_info *cifs_sb); +void cifs_mark_open_files_invalid(struct cifs_tcon *tcon); +void cifs_reopen_persistent_handles(struct cifs_tcon *tcon); + +bool cifs_find_lock_conflict(struct cifsFileInfo *cfile, __u64 offset, + __u64 length, __u8 type, __u16 flags, + struct cifsLockInfo **conf_lock, int rw_check); +void cifs_add_pending_open(struct cifs_fid *fid, struct tcon_link *tlink, + struct cifs_pending_open *open); +void cifs_add_pending_open_locked(struct cifs_fid *fid, struct tcon_link *tlink, struct cifs_pending_open *open); -extern void cifs_add_pending_open_locked(struct cifs_fid *fid, - struct tcon_link *tlink, - struct cifs_pending_open *open); -extern void cifs_del_pending_open(struct cifs_pending_open *open); +void cifs_del_pending_open(struct cifs_pending_open *open); -extern bool cifs_is_deferred_close(struct cifsFileInfo *cfile, - struct cifs_deferred_close **dclose); +bool cifs_is_deferred_close(struct cifsFileInfo *cfile, + struct cifs_deferred_close **pdclose); -extern void cifs_add_deferred_close(struct cifsFileInfo *cfile, - struct cifs_deferred_close *dclose); +void cifs_add_deferred_close(struct cifsFileInfo *cfile, + struct cifs_deferred_close *dclose); -extern void cifs_del_deferred_close(struct cifsFileInfo *cfile); +void cifs_del_deferred_close(struct cifsFileInfo *cfile); -extern void cifs_close_deferred_file(struct cifsInodeInfo *cifs_inode); +void cifs_close_deferred_file(struct cifsInodeInfo *cifs_inode); -extern void cifs_close_all_deferred_files(struct cifs_tcon *cifs_tcon); +void cifs_close_all_deferred_files(struct cifs_tcon *tcon); -void cifs_close_deferred_file_under_dentry(struct cifs_tcon *cifs_tcon, +void cifs_close_deferred_file_under_dentry(struct cifs_tcon *tcon, struct dentry *dentry); -extern void cifs_mark_open_handles_for_deleted_file(struct inode *inode, - const char *path); +void cifs_mark_open_handles_for_deleted_file(struct inode *inode, + const char *path); -extern struct TCP_Server_Info * -cifs_get_tcp_session(struct smb3_fs_context *ctx, - struct TCP_Server_Info *primary_server); -extern void cifs_put_tcp_session(struct TCP_Server_Info *server, - int from_reconnect); -extern void cifs_put_tcon(struct cifs_tcon *tcon, enum smb3_tcon_ref_trace trace); +struct TCP_Server_Info *cifs_get_tcp_session(struct smb3_fs_context *ctx, + struct TCP_Server_Info *primary_server); +void cifs_put_tcp_session(struct TCP_Server_Info *server, int from_reconnect); +void cifs_put_tcon(struct cifs_tcon *tcon, enum smb3_tcon_ref_trace trace); -extern void cifs_release_automount_timer(void); +void cifs_release_automount_timer(void); void cifs_proc_init(void); void cifs_proc_clean(void); -extern void cifs_move_llist(struct list_head *source, struct list_head *dest); -extern void cifs_free_llist(struct list_head *llist); -extern void cifs_del_lock_waiters(struct cifsLockInfo *lock); +void cifs_move_llist(struct list_head *source, struct list_head *dest); +void cifs_free_llist(struct list_head *llist); +void cifs_del_lock_waiters(struct cifsLockInfo *lock); int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon); -extern int cifs_negotiate_protocol(const unsigned int xid, - struct cifs_ses *ses, - struct TCP_Server_Info *server); -extern int cifs_setup_session(const unsigned int xid, struct cifs_ses *ses, - struct TCP_Server_Info *server, - struct nls_table *nls_info); -extern int cifs_enable_signing(struct TCP_Server_Info *server, bool mnt_sign_required); -extern int CIFSSMBNegotiate(const unsigned int xid, - struct cifs_ses *ses, +int cifs_negotiate_protocol(const unsigned int xid, struct cifs_ses *ses, struct TCP_Server_Info *server); - -extern int CIFSTCon(const unsigned int xid, struct cifs_ses *ses, - const char *tree, struct cifs_tcon *tcon, - const struct nls_table *); - -extern int CIFSFindFirst(const unsigned int xid, struct cifs_tcon *tcon, - const char *searchName, struct cifs_sb_info *cifs_sb, - __u16 *searchHandle, __u16 search_flags, - struct cifs_search_info *psrch_inf, - bool msearch); - -extern int CIFSFindNext(const unsigned int xid, struct cifs_tcon *tcon, - __u16 searchHandle, __u16 search_flags, - struct cifs_search_info *psrch_inf); - -extern int CIFSFindClose(const unsigned int xid, struct cifs_tcon *tcon, - const __u16 search_handle); - -extern int CIFSSMBQFileInfo(const unsigned int xid, struct cifs_tcon *tcon, - u16 netfid, FILE_ALL_INFO *pFindData); -extern int CIFSSMBQPathInfo(const unsigned int xid, struct cifs_tcon *tcon, - const char *search_Name, FILE_ALL_INFO *data, - int legacy /* whether to use old info level */, - const struct nls_table *nls_codepage, int remap); -extern int SMBQueryInformation(const unsigned int xid, struct cifs_tcon *tcon, - const char *search_name, FILE_ALL_INFO *data, - const struct nls_table *nls_codepage, int remap); - -extern int CIFSSMBUnixQFileInfo(const unsigned int xid, struct cifs_tcon *tcon, - u16 netfid, FILE_UNIX_BASIC_INFO *pFindData); -extern int CIFSSMBUnixQPathInfo(const unsigned int xid, - struct cifs_tcon *tcon, - const unsigned char *searchName, - FILE_UNIX_BASIC_INFO *pFindData, - const struct nls_table *nls_codepage, int remap); - -extern int CIFSGetDFSRefer(const unsigned int xid, struct cifs_ses *ses, - const char *search_name, - struct dfs_info3_param **target_nodes, - unsigned int *num_of_nodes, - const struct nls_table *nls_codepage, int remap); - -extern int parse_dfs_referrals(struct get_dfs_referral_rsp *rsp, u32 rsp_size, - unsigned int *num_of_nodes, - struct dfs_info3_param **target_nodes, - const struct nls_table *nls_codepage, int remap, - const char *searchName, bool is_unicode); -extern void reset_cifs_unix_caps(unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, - struct smb3_fs_context *ctx); -extern int CIFSSMBQFSInfo(const unsigned int xid, struct cifs_tcon *tcon, - struct kstatfs *FSData); -extern int SMBOldQFSInfo(const unsigned int xid, struct cifs_tcon *tcon, - struct kstatfs *FSData); -extern int CIFSSMBSetFSUnixInfo(const unsigned int xid, struct cifs_tcon *tcon, - __u64 cap); - -extern int CIFSSMBQFSAttributeInfo(const unsigned int xid, - struct cifs_tcon *tcon); -extern int CIFSSMBQFSDeviceInfo(const unsigned int xid, struct cifs_tcon *tcon); -extern int CIFSSMBQFSUnixInfo(const unsigned int xid, struct cifs_tcon *tcon); -extern int CIFSSMBQFSPosixInfo(const unsigned int xid, struct cifs_tcon *tcon, - struct kstatfs *FSData); - -extern int SMBSetInformation(const unsigned int xid, struct cifs_tcon *tcon, - const char *fileName, __le32 attributes, __le64 write_time, - const struct nls_table *nls_codepage, - struct cifs_sb_info *cifs_sb); -extern int CIFSSMBSetPathInfo(const unsigned int xid, struct cifs_tcon *tcon, - const char *fileName, const FILE_BASIC_INFO *data, - const struct nls_table *nls_codepage, - struct cifs_sb_info *cifs_sb); -extern int CIFSSMBSetFileInfo(const unsigned int xid, struct cifs_tcon *tcon, - const FILE_BASIC_INFO *data, __u16 fid, - __u32 pid_of_opener); -extern int CIFSSMBSetFileDisposition(const unsigned int xid, - struct cifs_tcon *tcon, - bool delete_file, __u16 fid, - __u32 pid_of_opener); -extern int CIFSSMBSetEOF(const unsigned int xid, struct cifs_tcon *tcon, - const char *file_name, __u64 size, - struct cifs_sb_info *cifs_sb, bool set_allocation, - struct dentry *dentry); -extern int CIFSSMBSetFileSize(const unsigned int xid, struct cifs_tcon *tcon, - struct cifsFileInfo *cfile, __u64 size, - bool set_allocation); - -struct cifs_unix_set_info_args { - __u64 ctime; - __u64 atime; - __u64 mtime; - __u64 mode; - kuid_t uid; - kgid_t gid; - dev_t device; -}; - -extern int CIFSSMBUnixSetFileInfo(const unsigned int xid, - struct cifs_tcon *tcon, - const struct cifs_unix_set_info_args *args, - u16 fid, u32 pid_of_opener); - -extern int CIFSSMBUnixSetPathInfo(const unsigned int xid, - struct cifs_tcon *tcon, const char *file_name, - const struct cifs_unix_set_info_args *args, - const struct nls_table *nls_codepage, - int remap); - -extern int CIFSSMBMkDir(const unsigned int xid, struct inode *inode, - umode_t mode, struct cifs_tcon *tcon, - const char *name, struct cifs_sb_info *cifs_sb); -extern int CIFSSMBRmDir(const unsigned int xid, struct cifs_tcon *tcon, - const char *name, struct cifs_sb_info *cifs_sb); -extern int CIFSPOSIXDelFile(const unsigned int xid, struct cifs_tcon *tcon, - const char *name, __u16 type, - const struct nls_table *nls_codepage, - int remap_special_chars); -extern int CIFSSMBDelFile(const unsigned int xid, struct cifs_tcon *tcon, - const char *name, struct cifs_sb_info *cifs_sb, - struct dentry *dentry); -int CIFSSMBRename(const unsigned int xid, struct cifs_tcon *tcon, - struct dentry *source_dentry, - const char *from_name, const char *to_name, - struct cifs_sb_info *cifs_sb); -extern int CIFSSMBRenameOpenFile(const unsigned int xid, struct cifs_tcon *tcon, - int netfid, const char *target_name, - const struct nls_table *nls_codepage, - int remap_special_chars); -int CIFSCreateHardLink(const unsigned int xid, - struct cifs_tcon *tcon, - struct dentry *source_dentry, - const char *from_name, const char *to_name, - struct cifs_sb_info *cifs_sb); -extern int CIFSUnixCreateHardLink(const unsigned int xid, - struct cifs_tcon *tcon, - const char *fromName, const char *toName, - const struct nls_table *nls_codepage, - int remap_special_chars); -extern int CIFSUnixCreateSymLink(const unsigned int xid, - struct cifs_tcon *tcon, - const char *fromName, const char *toName, - const struct nls_table *nls_codepage, int remap); -extern int CIFSSMBUnixQuerySymLink(const unsigned int xid, - struct cifs_tcon *tcon, - const unsigned char *searchName, char **syminfo, - const struct nls_table *nls_codepage, int remap); -extern int cifs_query_reparse_point(const unsigned int xid, - struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, - const char *full_path, - u32 *tag, struct kvec *rsp, - int *rsp_buftype); -extern struct inode *cifs_create_reparse_inode(struct cifs_open_info_data *data, - struct super_block *sb, - const unsigned int xid, - struct cifs_tcon *tcon, - const char *full_path, - bool directory, - struct kvec *reparse_iov, - struct kvec *xattr_iov); -extern int CIFSSMB_set_compression(const unsigned int xid, - struct cifs_tcon *tcon, __u16 fid); -extern int CIFS_open(const unsigned int xid, struct cifs_open_parms *oparms, - int *oplock, FILE_ALL_INFO *buf); -extern int SMBLegacyOpen(const unsigned int xid, struct cifs_tcon *tcon, - const char *fileName, const int disposition, - const int access_flags, const int omode, - __u16 *netfid, int *pOplock, FILE_ALL_INFO *, - const struct nls_table *nls_codepage, int remap); -extern int CIFSPOSIXCreate(const unsigned int xid, struct cifs_tcon *tcon, - u32 posix_flags, __u64 mode, __u16 *netfid, - FILE_UNIX_BASIC_INFO *pRetData, - __u32 *pOplock, const char *name, - const struct nls_table *nls_codepage, int remap); -extern int CIFSSMBClose(const unsigned int xid, struct cifs_tcon *tcon, - const int smb_file_id); - -extern int CIFSSMBFlush(const unsigned int xid, struct cifs_tcon *tcon, - const int smb_file_id); - -extern int CIFSSMBRead(const unsigned int xid, struct cifs_io_parms *io_parms, - unsigned int *nbytes, char **buf, - int *return_buf_type); -extern int CIFSSMBWrite(const unsigned int xid, struct cifs_io_parms *io_parms, - unsigned int *nbytes, const char *buf); -extern int CIFSSMBWrite2(const unsigned int xid, struct cifs_io_parms *io_parms, - unsigned int *nbytes, struct kvec *iov, const int nvec); -extern int CIFSGetSrvInodeNumber(const unsigned int xid, struct cifs_tcon *tcon, - const char *search_name, __u64 *inode_number, - const struct nls_table *nls_codepage, - int remap); - -extern int cifs_lockv(const unsigned int xid, struct cifs_tcon *tcon, - const __u16 netfid, const __u8 lock_type, - const __u32 num_unlock, const __u32 num_lock, - LOCKING_ANDX_RANGE *buf); -extern int CIFSSMBLock(const unsigned int xid, struct cifs_tcon *tcon, - const __u16 netfid, const __u32 netpid, const __u64 len, - const __u64 offset, const __u32 numUnlock, - const __u32 numLock, const __u8 lockType, - const bool waitFlag, const __u8 oplock_level); -extern int CIFSSMBPosixLock(const unsigned int xid, struct cifs_tcon *tcon, - const __u16 smb_file_id, const __u32 netpid, - const loff_t start_offset, const __u64 len, - struct file_lock *, const __u16 lock_type, - const bool waitFlag); -extern int CIFSSMBTDis(const unsigned int xid, struct cifs_tcon *tcon); -extern int CIFSSMBEcho(struct TCP_Server_Info *server); -extern int CIFSSMBLogoff(const unsigned int xid, struct cifs_ses *ses); - -extern struct cifs_ses *sesInfoAlloc(void); -extern void sesInfoFree(struct cifs_ses *); -extern struct cifs_tcon *tcon_info_alloc(bool dir_leases_enabled, - enum smb3_tcon_ref_trace trace); -extern void tconInfoFree(struct cifs_tcon *tcon, enum smb3_tcon_ref_trace trace); - -extern int cifs_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server, - __u32 *pexpected_response_sequence_number); -int cifs_verify_signature(struct smb_rqst *rqst, - struct TCP_Server_Info *server, - __u32 expected_sequence_number); -extern int setup_ntlmv2_rsp(struct cifs_ses *, const struct nls_table *); -extern void cifs_crypto_secmech_release(struct TCP_Server_Info *server); -extern int calc_seckey(struct cifs_ses *); -extern int generate_smb30signingkey(struct cifs_ses *ses, - struct TCP_Server_Info *server); -extern int generate_smb311signingkey(struct cifs_ses *ses, - struct TCP_Server_Info *server); +int cifs_setup_session(const unsigned int xid, struct cifs_ses *ses, + struct TCP_Server_Info *server, + struct nls_table *nls_info); +int cifs_enable_signing(struct TCP_Server_Info *server, + bool mnt_sign_required); + +int parse_dfs_referrals(struct get_dfs_referral_rsp *rsp, u32 rsp_size, + unsigned int *num_of_nodes, + struct dfs_info3_param **target_nodes, + const struct nls_table *nls_codepage, int remap, + const char *searchName, bool is_unicode); + +struct cifs_ses *sesInfoAlloc(void); +void sesInfoFree(struct cifs_ses *buf_to_free); +struct cifs_tcon *tcon_info_alloc(bool dir_leases_enabled, + enum smb3_tcon_ref_trace trace); +void tconInfoFree(struct cifs_tcon *tcon, enum smb3_tcon_ref_trace trace); + +int setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp); +void cifs_crypto_secmech_release(struct TCP_Server_Info *server); +int calc_seckey(struct cifs_ses *ses); +int generate_smb30signingkey(struct cifs_ses *ses, + struct TCP_Server_Info *server); +int generate_smb311signingkey(struct cifs_ses *ses, + struct TCP_Server_Info *server); #ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY -extern ssize_t CIFSSMBQAllEAs(const unsigned int xid, struct cifs_tcon *tcon, - const unsigned char *searchName, - const unsigned char *ea_name, char *EAData, - size_t bufsize, struct cifs_sb_info *cifs_sb); -extern int CIFSSMBSetEA(const unsigned int xid, struct cifs_tcon *tcon, - const char *fileName, const char *ea_name, - const void *ea_value, const __u16 ea_value_len, - const struct nls_table *nls_codepage, - struct cifs_sb_info *cifs_sb); -extern int CIFSSMBGetCIFSACL(const unsigned int xid, struct cifs_tcon *tcon, - __u16 fid, struct smb_ntsd **acl_inf, __u32 *buflen, __u32 info); -extern int CIFSSMBSetCIFSACL(const unsigned int, struct cifs_tcon *, __u16, - struct smb_ntsd *pntsd, __u32 len, int aclflag); -extern int cifs_do_get_acl(const unsigned int xid, struct cifs_tcon *tcon, - const unsigned char *searchName, - struct posix_acl **acl, const int acl_type, - const struct nls_table *nls_codepage, int remap); -extern int cifs_do_set_acl(const unsigned int xid, struct cifs_tcon *tcon, - const unsigned char *fileName, - const struct posix_acl *acl, const int acl_type, - const struct nls_table *nls_codepage, int remap); -extern int CIFSGetExtAttr(const unsigned int xid, struct cifs_tcon *tcon, - const int netfid, __u64 *pExtAttrBits, __u64 *pMask); #endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ -extern void cifs_autodisable_serverino(struct cifs_sb_info *cifs_sb); -extern bool couldbe_mf_symlink(const struct cifs_fattr *fattr); -extern int check_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, - struct cifs_fattr *fattr, - const unsigned char *path); -extern int E_md4hash(const unsigned char *passwd, unsigned char *p16, - const struct nls_table *codepage); +void cifs_autodisable_serverino(struct cifs_sb_info *cifs_sb); +bool couldbe_mf_symlink(const struct cifs_fattr *fattr); +int check_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, struct cifs_fattr *fattr, + const unsigned char *path); +int E_md4hash(const unsigned char *passwd, unsigned char *p16, + const struct nls_table *codepage); -extern struct TCP_Server_Info * -cifs_find_tcp_session(struct smb3_fs_context *ctx); +struct TCP_Server_Info *cifs_find_tcp_session(struct smb3_fs_context *ctx); struct cifs_tcon *cifs_setup_ipc(struct cifs_ses *ses, bool seal); void __cifs_put_smb_ses(struct cifs_ses *ses); -extern struct cifs_ses * -cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx); +struct cifs_ses *cifs_get_smb_ses(struct TCP_Server_Info *server, + struct smb3_fs_context *ctx); -int cifs_async_readv(struct cifs_io_subrequest *rdata); -int cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid); +int cifs_readv_receive(struct TCP_Server_Info *server, + struct mid_q_entry *mid); -void cifs_async_writev(struct cifs_io_subrequest *wdata); int cifs_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, struct cifs_sb_info *cifs_sb, const unsigned char *path, char *pbuf, @@ -621,46 +341,41 @@ int cifs_create_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, struct cifs_sb_info *cifs_sb, const unsigned char *path, char *pbuf, unsigned int *pbytes_written); -int __cifs_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server, - char *signature, struct cifs_calc_sig_ctx *ctx); -enum securityEnum cifs_select_sectype(struct TCP_Server_Info *, - enum securityEnum); +int __cifs_calc_signature(struct smb_rqst *rqst, + struct TCP_Server_Info *server, char *signature, + struct cifs_calc_sig_ctx *ctx); +enum securityEnum cifs_select_sectype(struct TCP_Server_Info *server, + enum securityEnum requested); int cifs_alloc_hash(const char *name, struct shash_desc **sdesc); void cifs_free_hash(struct shash_desc **sdesc); int cifs_try_adding_channels(struct cifs_ses *ses); -int smb3_update_ses_channels(struct cifs_ses *ses, struct TCP_Server_Info *server, - bool from_reconnect, bool disable_mchan); +int smb3_update_ses_channels(struct cifs_ses *ses, + struct TCP_Server_Info *server, + bool from_reconnect, bool disable_mchan); bool is_ses_using_iface(struct cifs_ses *ses, struct cifs_server_iface *iface); -int -cifs_ses_get_chan_index(struct cifs_ses *ses, - struct TCP_Server_Info *server); -void -cifs_chan_set_in_reconnect(struct cifs_ses *ses, - struct TCP_Server_Info *server); -void -cifs_chan_clear_in_reconnect(struct cifs_ses *ses, +int cifs_ses_get_chan_index(struct cifs_ses *ses, + struct TCP_Server_Info *server); +void cifs_chan_set_in_reconnect(struct cifs_ses *ses, + struct TCP_Server_Info *server); +void cifs_chan_clear_in_reconnect(struct cifs_ses *ses, + struct TCP_Server_Info *server); +void cifs_chan_set_need_reconnect(struct cifs_ses *ses, + struct TCP_Server_Info *server); +void cifs_chan_clear_need_reconnect(struct cifs_ses *ses, + struct TCP_Server_Info *server); +bool cifs_chan_needs_reconnect(struct cifs_ses *ses, struct TCP_Server_Info *server); -void -cifs_chan_set_need_reconnect(struct cifs_ses *ses, - struct TCP_Server_Info *server); -void -cifs_chan_clear_need_reconnect(struct cifs_ses *ses, +bool cifs_chan_is_iface_active(struct cifs_ses *ses, struct TCP_Server_Info *server); -bool -cifs_chan_needs_reconnect(struct cifs_ses *ses, - struct TCP_Server_Info *server); -bool -cifs_chan_is_iface_active(struct cifs_ses *ses, - struct TCP_Server_Info *server); -void -cifs_decrease_secondary_channels(struct cifs_ses *ses, bool disable_mchan); -void -cifs_chan_update_iface(struct cifs_ses *ses, struct TCP_Server_Info *server); -int -SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon, bool in_mount); +void cifs_decrease_secondary_channels(struct cifs_ses *ses, + bool disable_mchan); +void cifs_chan_update_iface(struct cifs_ses *ses, + struct TCP_Server_Info *server); +int SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon, + bool in_mount); void extract_unc_hostname(const char *unc, const char **h, size_t *len); int copy_path_name(char *dst, const char *src); @@ -673,9 +388,8 @@ void cifs_put_tcp_super(struct super_block *sb); int cifs_update_super_prepath(struct cifs_sb_info *cifs_sb, char *prefix); char *extract_hostname(const char *unc); char *extract_sharename(const char *unc); -int parse_reparse_point(struct reparse_data_buffer *buf, - u32 plen, struct cifs_sb_info *cifs_sb, - const char *full_path, +int parse_reparse_point(struct reparse_data_buffer *buf, u32 plen, + struct cifs_sb_info *cifs_sb, const char *full_path, struct cifs_open_info_data *data); int __cifs_sfu_make_node(unsigned int xid, struct inode *inode, struct dentry *dentry, struct cifs_tcon *tcon, @@ -696,14 +410,12 @@ static inline int get_dfs_path(const unsigned int xid, struct cifs_ses *ses, referral, NULL); } -int match_target_ip(struct TCP_Server_Info *server, - const char *host, size_t hostlen, - bool *result); +int match_target_ip(struct TCP_Server_Info *server, const char *host, + size_t hostlen, bool *result); int cifs_inval_name_dfs_link_error(const unsigned int xid, struct cifs_tcon *tcon, struct cifs_sb_info *cifs_sb, - const char *full_path, - bool *islink); + const char *full_path, bool *islink); #else static inline int cifs_inval_name_dfs_link_error(const unsigned int xid, struct cifs_tcon *tcon, diff --git a/fs/smb/client/cifssmb.c b/fs/smb/client/cifssmb.c index 3db1a892c526..3990a9012264 100644 --- a/fs/smb/client/cifssmb.c +++ b/fs/smb/client/cifssmb.c @@ -26,11 +26,12 @@ #include <linux/uaccess.h> #include <linux/netfs.h> #include <trace/events/netfs.h> -#include "cifspdu.h" -#include "cifsfs.h" #include "cifsglob.h" -#include "cifsacl.h" #include "cifsproto.h" +#include "smb1proto.h" +#include "../common/smbfsctl.h" +#include "cifsfs.h" +#include "cifsacl.h" #include "cifs_unicode.h" #include "cifs_debug.h" #include "fscache.h" @@ -533,6 +534,146 @@ neg_err_exit: return rc; } +/* + * Issue a TREE_CONNECT request. + */ +int +CIFSTCon(const unsigned int xid, struct cifs_ses *ses, + const char *tree, struct cifs_tcon *tcon, + const struct nls_table *nls_codepage) +{ + struct smb_hdr *smb_buffer; + struct smb_hdr *smb_buffer_response; + TCONX_REQ *pSMB; + TCONX_RSP *pSMBr; + unsigned char *bcc_ptr; + int rc = 0; + int length, in_len; + __u16 bytes_left, count; + + if (ses == NULL) + return smb_EIO(smb_eio_trace_null_pointers); + + smb_buffer = cifs_buf_get(); + if (smb_buffer == NULL) + return -ENOMEM; + + smb_buffer_response = smb_buffer; + + in_len = header_assemble(smb_buffer, SMB_COM_TREE_CONNECT_ANDX, + NULL /*no tid */, 4 /*wct */); + + smb_buffer->Mid = get_next_mid(ses->server); + smb_buffer->Uid = ses->Suid; + pSMB = (TCONX_REQ *) smb_buffer; + pSMBr = (TCONX_RSP *) smb_buffer_response; + + pSMB->AndXCommand = 0xFF; + pSMB->Flags = cpu_to_le16(TCON_EXTENDED_SECINFO); + bcc_ptr = &pSMB->Password[0]; + + pSMB->PasswordLength = cpu_to_le16(1); /* minimum */ + *bcc_ptr = 0; /* password is null byte */ + bcc_ptr++; /* skip password */ + /* already aligned so no need to do it below */ + + if (ses->server->sign) + smb_buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE; + + if (ses->capabilities & CAP_STATUS32) + smb_buffer->Flags2 |= SMBFLG2_ERR_STATUS; + + if (ses->capabilities & CAP_DFS) + smb_buffer->Flags2 |= SMBFLG2_DFS; + + if (ses->capabilities & CAP_UNICODE) { + smb_buffer->Flags2 |= SMBFLG2_UNICODE; + length = + cifs_strtoUTF16((__le16 *) bcc_ptr, tree, + 6 /* max utf8 char length in bytes */ * + (/* server len*/ + 256 /* share len */), nls_codepage); + bcc_ptr += 2 * length; /* convert num 16 bit words to bytes */ + bcc_ptr += 2; /* skip trailing null */ + } else { /* ASCII */ + strcpy(bcc_ptr, tree); + bcc_ptr += strlen(tree) + 1; + } + strcpy(bcc_ptr, "?????"); + bcc_ptr += strlen("?????"); + bcc_ptr += 1; + count = bcc_ptr - &pSMB->Password[0]; + in_len += count; + pSMB->ByteCount = cpu_to_le16(count); + + rc = SendReceive(xid, ses, smb_buffer, in_len, smb_buffer_response, + &length, 0); + + /* above now done in SendReceive */ + if (rc == 0) { + bool is_unicode; + + tcon->tid = smb_buffer_response->Tid; + bcc_ptr = pByteArea(smb_buffer_response); + bytes_left = get_bcc(smb_buffer_response); + length = strnlen(bcc_ptr, bytes_left - 2); + if (smb_buffer->Flags2 & SMBFLG2_UNICODE) + is_unicode = true; + else + is_unicode = false; + + + /* skip service field (NB: this field is always ASCII) */ + if (length == 3) { + if ((bcc_ptr[0] == 'I') && (bcc_ptr[1] == 'P') && + (bcc_ptr[2] == 'C')) { + cifs_dbg(FYI, "IPC connection\n"); + tcon->ipc = true; + tcon->pipe = true; + } + } else if (length == 2) { + if ((bcc_ptr[0] == 'A') && (bcc_ptr[1] == ':')) { + /* the most common case */ + cifs_dbg(FYI, "disk share connection\n"); + } + } + bcc_ptr += length + 1; + bytes_left -= (length + 1); + strscpy(tcon->tree_name, tree, sizeof(tcon->tree_name)); + + /* mostly informational -- no need to fail on error here */ + kfree(tcon->nativeFileSystem); + tcon->nativeFileSystem = cifs_strndup_from_utf16(bcc_ptr, + bytes_left, is_unicode, + nls_codepage); + + cifs_dbg(FYI, "nativeFileSystem=%s\n", tcon->nativeFileSystem); + + if ((smb_buffer_response->WordCount == 3) || + (smb_buffer_response->WordCount == 7)) + /* field is in same location */ + tcon->Flags = le16_to_cpu(pSMBr->OptionalSupport); + else + tcon->Flags = 0; + cifs_dbg(FYI, "Tcon flags: 0x%x\n", tcon->Flags); + + /* + * reset_cifs_unix_caps calls QFSInfo which requires + * need_reconnect to be false, but we would not need to call + * reset_caps if this were not a reconnect case so must check + * need_reconnect flag here. The caller will also clear + * need_reconnect when tcon was successful but needed to be + * cleared earlier in the case of unix extensions reconnect + */ + if (tcon->need_reconnect && tcon->unix_ext) { + cifs_dbg(FYI, "resetting caps for %s\n", tcon->tree_name); + tcon->need_reconnect = false; + reset_cifs_unix_caps(xid, tcon, NULL, NULL); + } + } + cifs_buf_release(smb_buffer); + return rc; +} + int CIFSSMBTDis(const unsigned int xid, struct cifs_tcon *tcon) { diff --git a/fs/smb/client/cifstransport.c b/fs/smb/client/cifstransport.c deleted file mode 100644 index 28d1cee90625..000000000000 --- a/fs/smb/client/cifstransport.c +++ /dev/null @@ -1,263 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1 -/* - * - * Copyright (C) International Business Machines Corp., 2002,2008 - * Author(s): Steve French (sfrench@us.ibm.com) - * Jeremy Allison (jra@samba.org) 2006. - * - */ - -#include <linux/fs.h> -#include <linux/list.h> -#include <linux/gfp.h> -#include <linux/wait.h> -#include <linux/net.h> -#include <linux/delay.h> -#include <linux/freezer.h> -#include <linux/tcp.h> -#include <linux/bvec.h> -#include <linux/highmem.h> -#include <linux/uaccess.h> -#include <linux/processor.h> -#include <linux/mempool.h> -#include <linux/sched/signal.h> -#include <linux/task_io_accounting_ops.h> -#include "cifspdu.h" -#include "cifsglob.h" -#include "cifsproto.h" -#include "cifs_debug.h" -#include "smb2proto.h" -#include "smbdirect.h" -#include "compress.h" - -/* Max number of iovectors we can use off the stack when sending requests. */ -#define CIFS_MAX_IOV_SIZE 8 - -static struct mid_q_entry * -alloc_mid(const struct smb_hdr *smb_buffer, struct TCP_Server_Info *server) -{ - struct mid_q_entry *temp; - - if (server == NULL) { - cifs_dbg(VFS, "%s: null TCP session\n", __func__); - return NULL; - } - - temp = mempool_alloc(&cifs_mid_pool, GFP_NOFS); - memset(temp, 0, sizeof(struct mid_q_entry)); - refcount_set(&temp->refcount, 1); - spin_lock_init(&temp->mid_lock); - temp->mid = get_mid(smb_buffer); - temp->pid = current->pid; - temp->command = cpu_to_le16(smb_buffer->Command); - cifs_dbg(FYI, "For smb_command %d\n", smb_buffer->Command); - /* easier to use jiffies */ - /* when mid allocated can be before when sent */ - temp->when_alloc = jiffies; - - /* - * The default is for the mid to be synchronous, so the - * default callback just wakes up the current task. - */ - get_task_struct(current); - temp->creator = current; - temp->callback = cifs_wake_up_task; - temp->callback_data = current; - - atomic_inc(&mid_count); - temp->mid_state = MID_REQUEST_ALLOCATED; - return temp; -} - -static int allocate_mid(struct cifs_ses *ses, struct smb_hdr *in_buf, - struct mid_q_entry **ppmidQ) -{ - spin_lock(&ses->ses_lock); - if (ses->ses_status == SES_NEW) { - if ((in_buf->Command != SMB_COM_SESSION_SETUP_ANDX) && - (in_buf->Command != SMB_COM_NEGOTIATE)) { - spin_unlock(&ses->ses_lock); - return -EAGAIN; - } - /* else ok - we are setting up session */ - } - - if (ses->ses_status == SES_EXITING) { - /* check if SMB session is bad because we are setting it up */ - if (in_buf->Command != SMB_COM_LOGOFF_ANDX) { - spin_unlock(&ses->ses_lock); - return -EAGAIN; - } - /* else ok - we are shutting down session */ - } - spin_unlock(&ses->ses_lock); - - *ppmidQ = alloc_mid(in_buf, ses->server); - if (*ppmidQ == NULL) - return -ENOMEM; - spin_lock(&ses->server->mid_queue_lock); - list_add_tail(&(*ppmidQ)->qhead, &ses->server->pending_mid_q); - spin_unlock(&ses->server->mid_queue_lock); - return 0; -} - -struct mid_q_entry * -cifs_setup_async_request(struct TCP_Server_Info *server, struct smb_rqst *rqst) -{ - int rc; - struct smb_hdr *hdr = (struct smb_hdr *)rqst->rq_iov[0].iov_base; - struct mid_q_entry *mid; - - /* enable signing if server requires it */ - if (server->sign) - hdr->Flags2 |= SMBFLG2_SECURITY_SIGNATURE; - - mid = alloc_mid(hdr, server); - if (mid == NULL) - return ERR_PTR(-ENOMEM); - - rc = cifs_sign_rqst(rqst, server, &mid->sequence_number); - if (rc) { - release_mid(server, mid); - return ERR_PTR(rc); - } - - return mid; -} - -/* - * - * Send an SMB Request. No response info (other than return code) - * needs to be parsed. - * - * flags indicate the type of request buffer and how long to wait - * and whether to log NT STATUS code (error) before mapping it to POSIX error - * - */ -int -SendReceiveNoRsp(const unsigned int xid, struct cifs_ses *ses, - char *in_buf, unsigned int in_len, int flags) -{ - int rc; - struct kvec iov[1]; - struct kvec rsp_iov; - int resp_buf_type; - - iov[0].iov_base = in_buf; - iov[0].iov_len = in_len; - flags |= CIFS_NO_RSP_BUF; - rc = SendReceive2(xid, ses, iov, 1, &resp_buf_type, flags, &rsp_iov); - cifs_dbg(NOISY, "SendRcvNoRsp flags %d rc %d\n", flags, rc); - - return rc; -} - -int -cifs_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server, - bool log_error) -{ - unsigned int len = mid->response_pdu_len; - - dump_smb(mid->resp_buf, min_t(u32, 92, len)); - - /* convert the length into a more usable form */ - if (server->sign) { - struct kvec iov[1]; - int rc = 0; - struct smb_rqst rqst = { .rq_iov = iov, - .rq_nvec = ARRAY_SIZE(iov) }; - - iov[0].iov_base = mid->resp_buf; - iov[0].iov_len = len; - /* FIXME: add code to kill session */ - rc = cifs_verify_signature(&rqst, server, - mid->sequence_number); - if (rc) - cifs_server_dbg(VFS, "SMB signature verification returned error = %d\n", - rc); - } - - /* BB special case reconnect tid and uid here? */ - return map_and_check_smb_error(server, mid, log_error); -} - -struct mid_q_entry * -cifs_setup_request(struct cifs_ses *ses, struct TCP_Server_Info *server, - struct smb_rqst *rqst) -{ - int rc; - struct smb_hdr *hdr = (struct smb_hdr *)rqst->rq_iov[0].iov_base; - struct mid_q_entry *mid; - - rc = allocate_mid(ses, hdr, &mid); - if (rc) - return ERR_PTR(rc); - rc = cifs_sign_rqst(rqst, server, &mid->sequence_number); - if (rc) { - delete_mid(server, mid); - return ERR_PTR(rc); - } - return mid; -} - -int -SendReceive2(const unsigned int xid, struct cifs_ses *ses, - struct kvec *iov, int n_vec, int *resp_buf_type /* ret */, - const int flags, struct kvec *resp_iov) -{ - struct smb_rqst rqst = { - .rq_iov = iov, - .rq_nvec = n_vec, - }; - - return cifs_send_recv(xid, ses, ses->server, - &rqst, resp_buf_type, flags, resp_iov); -} - -int -SendReceive(const unsigned int xid, struct cifs_ses *ses, - struct smb_hdr *in_buf, unsigned int in_len, - struct smb_hdr *out_buf, int *pbytes_returned, const int flags) -{ - struct TCP_Server_Info *server; - struct kvec resp_iov = {}; - struct kvec iov = { .iov_base = in_buf, .iov_len = in_len }; - struct smb_rqst rqst = { .rq_iov = &iov, .rq_nvec = 1 }; - int resp_buf_type; - int rc = 0; - - if (WARN_ON_ONCE(in_len > 0xffffff)) - return smb_EIO1(smb_eio_trace_tx_too_long, in_len); - if (ses == NULL) { - cifs_dbg(VFS, "Null smb session\n"); - return smb_EIO(smb_eio_trace_null_pointers); - } - server = ses->server; - if (server == NULL) { - cifs_dbg(VFS, "Null tcp session\n"); - return smb_EIO(smb_eio_trace_null_pointers); - } - - /* Ensure that we do not send more than 50 overlapping requests - to the same server. We may make this configurable later or - use ses->maxReq */ - - if (in_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) { - cifs_server_dbg(VFS, "Invalid length, greater than maximum frame, %d\n", - in_len); - return smb_EIO1(smb_eio_trace_tx_too_long, in_len); - } - - rc = cifs_send_recv(xid, ses, ses->server, - &rqst, &resp_buf_type, flags, &resp_iov); - if (rc < 0) - return rc; - - if (out_buf) { - *pbytes_returned = resp_iov.iov_len; - if (resp_iov.iov_len) - memcpy(out_buf, resp_iov.iov_base, resp_iov.iov_len); - } - free_rsp_buf(resp_buf_type, resp_iov.iov_base); - return rc; -} diff --git a/fs/smb/client/compress.h b/fs/smb/client/compress.h index 63aea32fbe92..2679baca129b 100644 --- a/fs/smb/client/compress.h +++ b/fs/smb/client/compress.h @@ -30,7 +30,8 @@ typedef int (*compress_send_fn)(struct TCP_Server_Info *, int, struct smb_rqst *); -int smb_compress(struct TCP_Server_Info *server, struct smb_rqst *rq, compress_send_fn send_fn); +int smb_compress(struct TCP_Server_Info *server, struct smb_rqst *rq, + compress_send_fn send_fn); bool should_compress(const struct cifs_tcon *tcon, const struct smb_rqst *rq); /* diff --git a/fs/smb/client/connect.c b/fs/smb/client/connect.c index ce620503e9f7..1b479561cbf9 100644 --- a/fs/smb/client/connect.c +++ b/fs/smb/client/connect.c @@ -32,7 +32,6 @@ #include <net/ipv6.h> #include <linux/parser.h> #include <linux/bvec.h> -#include "cifspdu.h" #include "cifsglob.h" #include "cifsproto.h" #include "cifs_unicode.h" @@ -64,6 +63,10 @@ static int generic_ip_connect(struct TCP_Server_Info *server); static void tlink_rb_insert(struct rb_root *root, struct tcon_link *new_tlink); static void cifs_prune_tlinks(struct work_struct *work); +static struct mchan_mount *mchan_mount_alloc(struct cifs_ses *ses); +static void mchan_mount_free(struct mchan_mount *mchan_mount); +static void mchan_mount_work_fn(struct work_struct *work); + /* * Resolve hostname and set ip addr in tcp ses. Useful for hostnames that may * get their ip addresses changed at some point. @@ -3463,115 +3466,6 @@ ip_connect(struct TCP_Server_Info *server) return generic_ip_connect(server); } -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY -void reset_cifs_unix_caps(unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx) -{ - /* - * If we are reconnecting then should we check to see if - * any requested capabilities changed locally e.g. via - * remount but we can not do much about it here - * if they have (even if we could detect it by the following) - * Perhaps we could add a backpointer to array of sb from tcon - * or if we change to make all sb to same share the same - * sb as NFS - then we only have one backpointer to sb. - * What if we wanted to mount the server share twice once with - * and once without posixacls or posix paths? - */ - __u64 saved_cap = le64_to_cpu(tcon->fsUnixInfo.Capability); - - if (ctx && ctx->no_linux_ext) { - tcon->fsUnixInfo.Capability = 0; - tcon->unix_ext = 0; /* Unix Extensions disabled */ - cifs_dbg(FYI, "Linux protocol extensions disabled\n"); - return; - } else if (ctx) - tcon->unix_ext = 1; /* Unix Extensions supported */ - - if (!tcon->unix_ext) { - cifs_dbg(FYI, "Unix extensions disabled so not set on reconnect\n"); - return; - } - - if (!CIFSSMBQFSUnixInfo(xid, tcon)) { - __u64 cap = le64_to_cpu(tcon->fsUnixInfo.Capability); - - cifs_dbg(FYI, "unix caps which server supports %lld\n", cap); - /* - * check for reconnect case in which we do not - * want to change the mount behavior if we can avoid it - */ - if (ctx == NULL) { - /* - * turn off POSIX ACL and PATHNAMES if not set - * originally at mount time - */ - if ((saved_cap & CIFS_UNIX_POSIX_ACL_CAP) == 0) - cap &= ~CIFS_UNIX_POSIX_ACL_CAP; - if ((saved_cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) == 0) { - if (cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) - cifs_dbg(VFS, "POSIXPATH support change\n"); - cap &= ~CIFS_UNIX_POSIX_PATHNAMES_CAP; - } else if ((cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) == 0) { - cifs_dbg(VFS, "possible reconnect error\n"); - cifs_dbg(VFS, "server disabled POSIX path support\n"); - } - } - - if (cap & CIFS_UNIX_TRANSPORT_ENCRYPTION_MANDATORY_CAP) - cifs_dbg(VFS, "per-share encryption not supported yet\n"); - - cap &= CIFS_UNIX_CAP_MASK; - if (ctx && ctx->no_psx_acl) - cap &= ~CIFS_UNIX_POSIX_ACL_CAP; - else if (CIFS_UNIX_POSIX_ACL_CAP & cap) { - cifs_dbg(FYI, "negotiated posix acl support\n"); - if (cifs_sb) - cifs_sb->mnt_cifs_flags |= - CIFS_MOUNT_POSIXACL; - } - - if (ctx && ctx->posix_paths == 0) - cap &= ~CIFS_UNIX_POSIX_PATHNAMES_CAP; - else if (cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) { - cifs_dbg(FYI, "negotiate posix pathnames\n"); - if (cifs_sb) - cifs_sb->mnt_cifs_flags |= - CIFS_MOUNT_POSIX_PATHS; - } - - cifs_dbg(FYI, "Negotiate caps 0x%x\n", (int)cap); -#ifdef CONFIG_CIFS_DEBUG2 - if (cap & CIFS_UNIX_FCNTL_CAP) - cifs_dbg(FYI, "FCNTL cap\n"); - if (cap & CIFS_UNIX_EXTATTR_CAP) - cifs_dbg(FYI, "EXTATTR cap\n"); - if (cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) - cifs_dbg(FYI, "POSIX path cap\n"); - if (cap & CIFS_UNIX_XATTR_CAP) - cifs_dbg(FYI, "XATTR cap\n"); - if (cap & CIFS_UNIX_POSIX_ACL_CAP) - cifs_dbg(FYI, "POSIX ACL cap\n"); - if (cap & CIFS_UNIX_LARGE_READ_CAP) - cifs_dbg(FYI, "very large read cap\n"); - if (cap & CIFS_UNIX_LARGE_WRITE_CAP) - cifs_dbg(FYI, "very large write cap\n"); - if (cap & CIFS_UNIX_TRANSPORT_ENCRYPTION_CAP) - cifs_dbg(FYI, "transport encryption cap\n"); - if (cap & CIFS_UNIX_TRANSPORT_ENCRYPTION_MANDATORY_CAP) - cifs_dbg(FYI, "mandatory transport encryption cap\n"); -#endif /* CIFS_DEBUG2 */ - if (CIFSSMBSetFSUnixInfo(xid, tcon, cap)) { - if (ctx == NULL) - cifs_dbg(FYI, "resetting capabilities failed\n"); - else - cifs_dbg(VFS, "Negotiating Unix capabilities with the server failed. Consider mounting with the Unix Extensions disabled if problems are found by specifying the nounix mount option.\n"); - - } - } -} -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - int cifs_setup_cifs_sb(struct cifs_sb_info *cifs_sb) { struct smb3_fs_context *ctx = cifs_sb->ctx; @@ -3899,15 +3793,64 @@ out: return rc; } +static struct mchan_mount * +mchan_mount_alloc(struct cifs_ses *ses) +{ + struct mchan_mount *mchan_mount; + + mchan_mount = kzalloc(sizeof(*mchan_mount), GFP_KERNEL); + if (!mchan_mount) + return ERR_PTR(-ENOMEM); + + INIT_WORK(&mchan_mount->work, mchan_mount_work_fn); + + spin_lock(&cifs_tcp_ses_lock); + cifs_smb_ses_inc_refcount(ses); + spin_unlock(&cifs_tcp_ses_lock); + mchan_mount->ses = ses; + + return mchan_mount; +} + +static void +mchan_mount_free(struct mchan_mount *mchan_mount) +{ + cifs_put_smb_ses(mchan_mount->ses); + kfree(mchan_mount); +} + +static void +mchan_mount_work_fn(struct work_struct *work) +{ + struct mchan_mount *mchan_mount = container_of(work, struct mchan_mount, work); + + smb3_update_ses_channels(mchan_mount->ses, + mchan_mount->ses->server, + false /* from_reconnect */, + false /* disable_mchan */); + + mchan_mount_free(mchan_mount); +} + #ifdef CONFIG_CIFS_DFS_UPCALL int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx) { struct cifs_mount_ctx mnt_ctx = { .cifs_sb = cifs_sb, .fs_ctx = ctx, }; + struct mchan_mount *mchan_mount = NULL; int rc; rc = dfs_mount_share(&mnt_ctx); if (rc) goto error; + + if (ctx->multichannel) { + mchan_mount = mchan_mount_alloc(mnt_ctx.ses); + if (IS_ERR(mchan_mount)) { + rc = PTR_ERR(mchan_mount); + goto error; + } + } + if (!ctx->dfs_conn) goto out; @@ -3926,17 +3869,19 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx) ctx->prepath = NULL; out: - smb3_update_ses_channels(mnt_ctx.ses, mnt_ctx.server, - false /* from_reconnect */, - false /* disable_mchan */); rc = mount_setup_tlink(cifs_sb, mnt_ctx.ses, mnt_ctx.tcon); if (rc) goto error; + if (ctx->multichannel) + queue_work(cifsiod_wq, &mchan_mount->work); + free_xid(mnt_ctx.xid); return rc; error: + if (ctx->multichannel && !IS_ERR_OR_NULL(mchan_mount)) + mchan_mount_free(mchan_mount); cifs_mount_put_conns(&mnt_ctx); return rc; } @@ -3945,6 +3890,7 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx) { int rc = 0; struct cifs_mount_ctx mnt_ctx = { .cifs_sb = cifs_sb, .fs_ctx = ctx, }; + struct mchan_mount *mchan_mount = NULL; rc = cifs_mount_get_session(&mnt_ctx); if (rc) @@ -3972,161 +3918,32 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx) if (rc) goto error; + if (ctx->multichannel) { + mchan_mount = mchan_mount_alloc(mnt_ctx.ses); + if (IS_ERR(mchan_mount)) { + rc = PTR_ERR(mchan_mount); + goto error; + } + } + rc = mount_setup_tlink(cifs_sb, mnt_ctx.ses, mnt_ctx.tcon); if (rc) goto error; + if (ctx->multichannel) + queue_work(cifsiod_wq, &mchan_mount->work); + free_xid(mnt_ctx.xid); return rc; error: + if (ctx->multichannel && !IS_ERR_OR_NULL(mchan_mount)) + mchan_mount_free(mchan_mount); cifs_mount_put_conns(&mnt_ctx); return rc; } #endif -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY -/* - * Issue a TREE_CONNECT request. - */ -int -CIFSTCon(const unsigned int xid, struct cifs_ses *ses, - const char *tree, struct cifs_tcon *tcon, - const struct nls_table *nls_codepage) -{ - struct smb_hdr *smb_buffer; - struct smb_hdr *smb_buffer_response; - TCONX_REQ *pSMB; - TCONX_RSP *pSMBr; - unsigned char *bcc_ptr; - int rc = 0; - int length, in_len; - __u16 bytes_left, count; - - if (ses == NULL) - return smb_EIO(smb_eio_trace_null_pointers); - - smb_buffer = cifs_buf_get(); - if (smb_buffer == NULL) - return -ENOMEM; - - smb_buffer_response = smb_buffer; - - in_len = header_assemble(smb_buffer, SMB_COM_TREE_CONNECT_ANDX, - NULL /*no tid */, 4 /*wct */); - - smb_buffer->Mid = get_next_mid(ses->server); - smb_buffer->Uid = ses->Suid; - pSMB = (TCONX_REQ *) smb_buffer; - pSMBr = (TCONX_RSP *) smb_buffer_response; - - pSMB->AndXCommand = 0xFF; - pSMB->Flags = cpu_to_le16(TCON_EXTENDED_SECINFO); - bcc_ptr = &pSMB->Password[0]; - - pSMB->PasswordLength = cpu_to_le16(1); /* minimum */ - *bcc_ptr = 0; /* password is null byte */ - bcc_ptr++; /* skip password */ - /* already aligned so no need to do it below */ - - if (ses->server->sign) - smb_buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE; - - if (ses->capabilities & CAP_STATUS32) - smb_buffer->Flags2 |= SMBFLG2_ERR_STATUS; - - if (ses->capabilities & CAP_DFS) - smb_buffer->Flags2 |= SMBFLG2_DFS; - - if (ses->capabilities & CAP_UNICODE) { - smb_buffer->Flags2 |= SMBFLG2_UNICODE; - length = - cifs_strtoUTF16((__le16 *) bcc_ptr, tree, - 6 /* max utf8 char length in bytes */ * - (/* server len*/ + 256 /* share len */), nls_codepage); - bcc_ptr += 2 * length; /* convert num 16 bit words to bytes */ - bcc_ptr += 2; /* skip trailing null */ - } else { /* ASCII */ - strcpy(bcc_ptr, tree); - bcc_ptr += strlen(tree) + 1; - } - strcpy(bcc_ptr, "?????"); - bcc_ptr += strlen("?????"); - bcc_ptr += 1; - count = bcc_ptr - &pSMB->Password[0]; - in_len += count; - pSMB->ByteCount = cpu_to_le16(count); - - rc = SendReceive(xid, ses, smb_buffer, in_len, smb_buffer_response, - &length, 0); - - /* above now done in SendReceive */ - if (rc == 0) { - bool is_unicode; - - tcon->tid = smb_buffer_response->Tid; - bcc_ptr = pByteArea(smb_buffer_response); - bytes_left = get_bcc(smb_buffer_response); - length = strnlen(bcc_ptr, bytes_left - 2); - if (smb_buffer->Flags2 & SMBFLG2_UNICODE) - is_unicode = true; - else - is_unicode = false; - - - /* skip service field (NB: this field is always ASCII) */ - if (length == 3) { - if ((bcc_ptr[0] == 'I') && (bcc_ptr[1] == 'P') && - (bcc_ptr[2] == 'C')) { - cifs_dbg(FYI, "IPC connection\n"); - tcon->ipc = true; - tcon->pipe = true; - } - } else if (length == 2) { - if ((bcc_ptr[0] == 'A') && (bcc_ptr[1] == ':')) { - /* the most common case */ - cifs_dbg(FYI, "disk share connection\n"); - } - } - bcc_ptr += length + 1; - bytes_left -= (length + 1); - strscpy(tcon->tree_name, tree, sizeof(tcon->tree_name)); - - /* mostly informational -- no need to fail on error here */ - kfree(tcon->nativeFileSystem); - tcon->nativeFileSystem = cifs_strndup_from_utf16(bcc_ptr, - bytes_left, is_unicode, - nls_codepage); - - cifs_dbg(FYI, "nativeFileSystem=%s\n", tcon->nativeFileSystem); - - if ((smb_buffer_response->WordCount == 3) || - (smb_buffer_response->WordCount == 7)) - /* field is in same location */ - tcon->Flags = le16_to_cpu(pSMBr->OptionalSupport); - else - tcon->Flags = 0; - cifs_dbg(FYI, "Tcon flags: 0x%x\n", tcon->Flags); - - /* - * reset_cifs_unix_caps calls QFSInfo which requires - * need_reconnect to be false, but we would not need to call - * reset_caps if this were not a reconnect case so must check - * need_reconnect flag here. The caller will also clear - * need_reconnect when tcon was successful but needed to be - * cleared earlier in the case of unix extensions reconnect - */ - if (tcon->need_reconnect && tcon->unix_ext) { - cifs_dbg(FYI, "resetting caps for %s\n", tcon->tree_name); - tcon->need_reconnect = false; - reset_cifs_unix_caps(xid, tcon, NULL, NULL); - } - } - cifs_buf_release(smb_buffer); - return rc; -} -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - static void delayed_free(struct rcu_head *p) { struct cifs_sb_info *cifs_sb = container_of(p, struct cifs_sb_info, rcu); @@ -4270,7 +4087,9 @@ cifs_setup_session(const unsigned int xid, struct cifs_ses *ses, ses->ses_status = SES_IN_SETUP; /* force iface_list refresh */ + spin_lock(&ses->iface_lock); ses->iface_last_update = 0; + spin_unlock(&ses->iface_lock); } spin_unlock(&ses->ses_lock); diff --git a/fs/smb/client/dfs.h b/fs/smb/client/dfs.h index e60f0a24a8a1..6b5b5ca0f55c 100644 --- a/fs/smb/client/dfs.h +++ b/fs/smb/client/dfs.h @@ -151,7 +151,8 @@ static inline void ref_walk_mark_end(struct dfs_ref_walk *rw) ref->tit = ERR_PTR(-ENOENT); /* end marker */ } -int dfs_parse_target_referral(const char *full_path, const struct dfs_info3_param *ref, +int dfs_parse_target_referral(const char *full_path, + const struct dfs_info3_param *ref, struct smb3_fs_context *ctx); int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx); diff --git a/fs/smb/client/dfs_cache.h b/fs/smb/client/dfs_cache.h index 18a08a2ca93b..c99dc3546c70 100644 --- a/fs/smb/client/dfs_cache.h +++ b/fs/smb/client/dfs_cache.h @@ -37,17 +37,22 @@ int dfs_cache_init(void); void dfs_cache_destroy(void); extern const struct proc_ops dfscache_proc_ops; -int dfs_cache_find(const unsigned int xid, struct cifs_ses *ses, const struct nls_table *cp, - int remap, const char *path, struct dfs_info3_param *ref, +int dfs_cache_find(const unsigned int xid, struct cifs_ses *ses, + const struct nls_table *cp, int remap, const char *path, + struct dfs_info3_param *ref, struct dfs_cache_tgt_list *tgt_list); int dfs_cache_noreq_find(const char *path, struct dfs_info3_param *ref, struct dfs_cache_tgt_list *tgt_list); -void dfs_cache_noreq_update_tgthint(const char *path, const struct dfs_cache_tgt_iterator *it); -int dfs_cache_get_tgt_referral(const char *path, const struct dfs_cache_tgt_iterator *it, +void dfs_cache_noreq_update_tgthint(const char *path, + const struct dfs_cache_tgt_iterator *it); +int dfs_cache_get_tgt_referral(const char *path, + const struct dfs_cache_tgt_iterator *it, struct dfs_info3_param *ref); -int dfs_cache_get_tgt_share(char *path, const struct dfs_cache_tgt_iterator *it, char **share, - char **prefix); -char *dfs_cache_canonical_path(const char *path, const struct nls_table *cp, int remap); +int dfs_cache_get_tgt_share(char *path, + const struct dfs_cache_tgt_iterator *it, + char **share, char **prefix); +char *dfs_cache_canonical_path(const char *path, const struct nls_table *cp, + int remap); int dfs_cache_remount_fs(struct cifs_sb_info *cifs_sb); void dfs_cache_refresh(struct work_struct *work); diff --git a/fs/smb/client/dir.c b/fs/smb/client/dir.c index 747256025e49..cb10088197d2 100644 --- a/fs/smb/client/dir.c +++ b/fs/smb/client/dir.c @@ -14,7 +14,6 @@ #include <linux/mount.h> #include <linux/file.h> #include "cifsfs.h" -#include "cifspdu.h" #include "cifsglob.h" #include "cifsproto.h" #include "cifs_debug.h" diff --git a/fs/smb/client/dns_resolve.h b/fs/smb/client/dns_resolve.h index 36bc4a6a55bf..951fbab5e61d 100644 --- a/fs/smb/client/dns_resolve.h +++ b/fs/smb/client/dns_resolve.h @@ -15,8 +15,8 @@ #include "cifsglob.h" #include "cifsproto.h" -int dns_resolve_name(const char *dom, const char *name, - size_t namelen, struct sockaddr *ip_addr); +int dns_resolve_name(const char *dom, const char *name, size_t namelen, + struct sockaddr *ip_addr); static inline int dns_resolve_unc(const char *dom, const char *unc, struct sockaddr *ip_addr) diff --git a/fs/smb/client/file.c b/fs/smb/client/file.c index 7ff5cc9c5c5b..51360d64b7b2 100644 --- a/fs/smb/client/file.c +++ b/fs/smb/client/file.c @@ -25,7 +25,6 @@ #include <linux/mm.h> #include <asm/div64.h> #include "cifsfs.h" -#include "cifspdu.h" #include "cifsglob.h" #include "cifsproto.h" #include "smb2proto.h" diff --git a/fs/smb/client/fs_context.c b/fs/smb/client/fs_context.c index d4291d3a9a48..ec84204aee18 100644 --- a/fs/smb/client/fs_context.c +++ b/fs/smb/client/fs_context.c @@ -26,7 +26,6 @@ #include <linux/parser.h> #include <linux/utsname.h> #include "cifsfs.h" -#include "cifspdu.h" #include "cifsglob.h" #include "cifsproto.h" #include "cifs_unicode.h" diff --git a/fs/smb/client/fs_context.h b/fs/smb/client/fs_context.h index 7af7cbbe4208..49b2a6f09ca2 100644 --- a/fs/smb/client/fs_context.h +++ b/fs/smb/client/fs_context.h @@ -361,18 +361,20 @@ static inline enum cifs_symlink_type cifs_symlink_type(struct cifs_sb_info *cifs return CIFS_SYMLINK_TYPE_NONE; } -extern int smb3_init_fs_context(struct fs_context *fc); -extern void smb3_cleanup_fs_context_contents(struct smb3_fs_context *ctx); -extern void smb3_cleanup_fs_context(struct smb3_fs_context *ctx); +int smb3_init_fs_context(struct fs_context *fc); +void smb3_cleanup_fs_context_contents(struct smb3_fs_context *ctx); +void smb3_cleanup_fs_context(struct smb3_fs_context *ctx); static inline struct smb3_fs_context *smb3_fc2context(const struct fs_context *fc) { return fc->fs_private; } -extern int smb3_fs_context_dup(struct smb3_fs_context *new_ctx, struct smb3_fs_context *ctx); -extern int smb3_sync_session_ctx_passwords(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses); -extern void smb3_update_mnt_flags(struct cifs_sb_info *cifs_sb); +int smb3_fs_context_dup(struct smb3_fs_context *new_ctx, + struct smb3_fs_context *ctx); +int smb3_sync_session_ctx_passwords(struct cifs_sb_info *cifs_sb, + struct cifs_ses *ses); +void smb3_update_mnt_flags(struct cifs_sb_info *cifs_sb); /* * max deferred close timeout (jiffies) - 2^30 @@ -380,7 +382,7 @@ extern void smb3_update_mnt_flags(struct cifs_sb_info *cifs_sb); #define SMB3_MAX_DCLOSETIMEO (1 << 30) #define SMB3_DEF_DCLOSETIMEO (1 * HZ) /* even 1 sec enough to help eg open/write/close/open/read */ #define MAX_CACHED_FIDS 16 -extern char *cifs_sanitize_prepath(char *prepath, gfp_t gfp); +char *cifs_sanitize_prepath(char *prepath, gfp_t gfp); extern struct mutex cifs_mount_mutex; diff --git a/fs/smb/client/fscache.h b/fs/smb/client/fscache.h index f06cb24f5f3c..3521222886c1 100644 --- a/fs/smb/client/fscache.h +++ b/fs/smb/client/fscache.h @@ -38,12 +38,17 @@ struct cifs_fscache_inode_coherency_data { /* * fscache.c */ -extern int cifs_fscache_get_super_cookie(struct cifs_tcon *); -extern void cifs_fscache_release_super_cookie(struct cifs_tcon *); - -extern void cifs_fscache_get_inode_cookie(struct inode *inode); -extern void cifs_fscache_release_inode_cookie(struct inode *); -extern void cifs_fscache_unuse_inode_cookie(struct inode *inode, bool update); +int cifs_fscache_get_super_cookie(struct cifs_tcon *tcon); +void cifs_fscache_release_super_cookie(struct cifs_tcon *tcon); +void cifs_fscache_get_inode_cookie(struct inode *inode); +void cifs_fscache_unuse_inode_cookie(struct inode *inode, bool update); +void cifs_fscache_release_inode_cookie(struct inode *inode); +int cifs_fscache_get_super_cookie(struct cifs_tcon *tcon); +void cifs_fscache_release_super_cookie(struct cifs_tcon *tcon); + +void cifs_fscache_get_inode_cookie(struct inode *inode); +void cifs_fscache_release_inode_cookie(struct inode *inode); +void cifs_fscache_unuse_inode_cookie(struct inode *inode, bool update); static inline void cifs_fscache_fill_coherency(struct inode *inode, diff --git a/fs/smb/client/gen_smb2_mapping b/fs/smb/client/gen_smb2_mapping new file mode 100644 index 000000000000..eb9fa727ddd8 --- /dev/null +++ b/fs/smb/client/gen_smb2_mapping @@ -0,0 +1,86 @@ +#!/usr/bin/perl -w +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Generate an SMB2 status -> error mapping table, +# sorted by NT status code (cpu-endian, ascending). +# +# Copyright (C) 2025 Red Hat, Inc. All Rights Reserved. +# Written by David Howells (dhowells@redhat.com) +# +use strict; + +if ($#ARGV != 1) { + print STDERR "Format: ", $0, " <in-h-file> <out-c-file>\n"; + exit(2); +} + +my %statuses = (); +my @list = (); + +# +# Read the file +# +open IN_FILE, "<$ARGV[0]" || die; +while (<IN_FILE>) { + chomp; + + if (m!^#define\s*([A-Za-z0-9_]+)\s+cpu_to_le32[(]([0-9a-fA-Fx]+)[)]\s+//\s+([-A-Z0-9_]+)!) { + my $status = $1; + my $code = $2; + my $ncode = hex($2); + my $error = $3; + my $s; + + next if ($status =~ /^STATUS_SEVERITY/); + + die "Duplicate status $status" + if exists($statuses{$status}); + + my %s = ( + status => $status, + code => $code, + ncode => $ncode, + error => $error + ); + $statuses{$status} = \%s; + push @list, \%s; + } +} +close IN_FILE || die; + + +@list = sort( { $a->{ncode} <=> $b->{ncode} } @list); + +open OUT_FILE, ">$ARGV[1]" || die; +my $list_size = scalar @list; +my $full_status = ""; +for (my $i = 0; $i < $list_size; $i++) { + my $entry = $list[$i]; + my $status = $entry->{status}; + my $code = $entry->{code}; + my $ncode = $entry->{ncode}; + my $error = $entry->{error}; + + next if ($ncode == 0); + + $full_status .= $status; + # There may be synonyms + if ($i < $list_size - 1) { + my $next_entry = $list[$i + 1]; + my $next_ncode = $next_entry->{ncode}; + if ($next_ncode == $ncode) { + $full_status .= " or "; + next; + } + } + + my $pad = " "; + if (length($full_status) < 40) { + my $n = 40 - length($full_status); + $pad = "\t" x ((($n-1) / 8) + 1); + } + print(OUT_FILE "{ $code, $error, \"$full_status\" },\n"); + + $full_status = ""; +} +close OUT_FILE || die; diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c index f9ee95953fa4..c23c057162e6 100644 --- a/fs/smb/client/inode.c +++ b/fs/smb/client/inode.c @@ -16,7 +16,6 @@ #include <linux/fiemap.h> #include <asm/div64.h> #include "cifsfs.h" -#include "cifspdu.h" #include "cifsglob.h" #include "cifsproto.h" #include "smb2proto.h" diff --git a/fs/smb/client/ioctl.c b/fs/smb/client/ioctl.c index d1b153209424..a4aa063cf5ea 100644 --- a/fs/smb/client/ioctl.c +++ b/fs/smb/client/ioctl.c @@ -13,7 +13,6 @@ #include <linux/mount.h> #include <linux/mm.h> #include <linux/pagemap.h> -#include "cifspdu.h" #include "cifsglob.h" #include "cifsproto.h" #include "cifs_debug.h" diff --git a/fs/smb/client/link.c b/fs/smb/client/link.c index fdfdc9a3abdd..a2f7bfa8ad1e 100644 --- a/fs/smb/client/link.c +++ b/fs/smb/client/link.c @@ -11,7 +11,6 @@ #include <linux/slab.h> #include <linux/namei.h> #include "cifsfs.h" -#include "cifspdu.h" #include "cifsglob.h" #include "cifsproto.h" #include "cifs_debug.h" diff --git a/fs/smb/client/misc.c b/fs/smb/client/misc.c index 9529fa385938..1773e3b471aa 100644 --- a/fs/smb/client/misc.c +++ b/fs/smb/client/misc.c @@ -10,7 +10,6 @@ #include <linux/ctype.h> #include <linux/mempool.h> #include <linux/vmalloc.h> -#include "cifspdu.h" #include "cifsglob.h" #include "cifsproto.h" #include "cifs_debug.h" @@ -19,6 +18,7 @@ #include "cifs_unicode.h" #include "smb2pdu.h" #include "smb2proto.h" +#include "smb1proto.h" #include "cifsfs.h" #ifdef CONFIG_CIFS_DFS_UPCALL #include "dns_resolve.h" @@ -178,10 +178,10 @@ tconInfoFree(struct cifs_tcon *tcon, enum smb3_tcon_ref_trace trace) kfree(tcon); } -struct smb_hdr * +void * cifs_buf_get(void) { - struct smb_hdr *ret_buf = NULL; + void *ret_buf = NULL; /* * SMB2 header is bigger than CIFS one - no problems to clean some * more bytes for CIFS. @@ -220,10 +220,10 @@ cifs_buf_release(void *buf_to_free) return; } -struct smb_hdr * +void * cifs_small_buf_get(void) { - struct smb_hdr *ret_buf = NULL; + void *ret_buf = NULL; /* We could use negotiated size instead of max_msgsize - but it may be more efficient to always alloc same size @@ -231,7 +231,6 @@ cifs_small_buf_get(void) defaults to this and can not be bigger */ ret_buf = mempool_alloc(cifs_sm_req_poolp, GFP_NOFS); /* No need to clear memory here, cleared in header assemble */ - /* memset(ret_buf, 0, sizeof(struct smb_hdr) + 27);*/ atomic_inc(&small_buf_alloc_count); #ifdef CONFIG_CIFS_STATS2 atomic_inc(&total_small_buf_alloc_count); @@ -263,297 +262,6 @@ free_rsp_buf(int resp_buftype, void *rsp) cifs_buf_release(rsp); } -/* NB: MID can not be set if treeCon not passed in, in that - case it is responsibility of caller to set the mid */ -unsigned int -header_assemble(struct smb_hdr *buffer, char smb_command, - const struct cifs_tcon *treeCon, int word_count - /* length of fixed section (word count) in two byte units */) -{ - unsigned int in_len; - char *temp = (char *) buffer; - - memset(temp, 0, 256); /* bigger than MAX_CIFS_HDR_SIZE */ - - in_len = (2 * word_count) + sizeof(struct smb_hdr) + - 2 /* for bcc field itself */; - - buffer->Protocol[0] = 0xFF; - buffer->Protocol[1] = 'S'; - buffer->Protocol[2] = 'M'; - buffer->Protocol[3] = 'B'; - buffer->Command = smb_command; - buffer->Flags = 0x00; /* case sensitive */ - buffer->Flags2 = SMBFLG2_KNOWS_LONG_NAMES; - buffer->Pid = cpu_to_le16((__u16)current->tgid); - buffer->PidHigh = cpu_to_le16((__u16)(current->tgid >> 16)); - if (treeCon) { - buffer->Tid = treeCon->tid; - if (treeCon->ses) { - if (treeCon->ses->capabilities & CAP_UNICODE) - buffer->Flags2 |= SMBFLG2_UNICODE; - if (treeCon->ses->capabilities & CAP_STATUS32) - buffer->Flags2 |= SMBFLG2_ERR_STATUS; - - /* Uid is not converted */ - buffer->Uid = treeCon->ses->Suid; - if (treeCon->ses->server) - buffer->Mid = get_next_mid(treeCon->ses->server); - } - if (treeCon->Flags & SMB_SHARE_IS_IN_DFS) - buffer->Flags2 |= SMBFLG2_DFS; - if (treeCon->nocase) - buffer->Flags |= SMBFLG_CASELESS; - if ((treeCon->ses) && (treeCon->ses->server)) - if (treeCon->ses->server->sign) - buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE; - } - -/* endian conversion of flags is now done just before sending */ - buffer->WordCount = (char) word_count; - return in_len; -} - -static int -check_smb_hdr(struct smb_hdr *smb) -{ - /* does it have the right SMB "signature" ? */ - if (*(__le32 *) smb->Protocol != SMB1_PROTO_NUMBER) { - cifs_dbg(VFS, "Bad protocol string signature header 0x%x\n", - *(unsigned int *)smb->Protocol); - return 1; - } - - /* if it's a response then accept */ - if (smb->Flags & SMBFLG_RESPONSE) - return 0; - - /* only one valid case where server sends us request */ - if (smb->Command == SMB_COM_LOCKING_ANDX) - return 0; - - /* - * Windows NT server returns error resposne (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. - */ - if (smb->Command == SMB_COM_TRANSACTION2 && smb->Status.CifsError != 0) - return 0; - - cifs_dbg(VFS, "Server sent request, not response. mid=%u\n", - get_mid(smb)); - return 1; -} - -int -checkSMB(char *buf, unsigned int pdu_len, unsigned int total_read, - struct TCP_Server_Info *server) -{ - struct smb_hdr *smb = (struct smb_hdr *)buf; - __u32 rfclen = pdu_len; - __u32 clc_len; /* calculated length */ - cifs_dbg(FYI, "checkSMB Length: 0x%x, smb_buf_length: 0x%x\n", - total_read, rfclen); - - /* is this frame too small to even get to a BCC? */ - if (total_read < 2 + sizeof(struct smb_hdr)) { - if ((total_read >= sizeof(struct smb_hdr) - 1) - && (smb->Status.CifsError != 0)) { - /* it's an error return */ - smb->WordCount = 0; - /* some error cases do not return wct and bcc */ - return 0; - } else if ((total_read == sizeof(struct smb_hdr) + 1) && - (smb->WordCount == 0)) { - char *tmp = (char *)smb; - /* Need to work around a bug in two servers here */ - /* First, check if the part of bcc they sent was zero */ - if (tmp[sizeof(struct smb_hdr)] == 0) { - /* some servers return only half of bcc - * on simple responses (wct, bcc both zero) - * in particular have seen this on - * ulogoffX and FindClose. This leaves - * one byte of bcc potentially uninitialized - */ - /* zero rest of bcc */ - tmp[sizeof(struct smb_hdr)+1] = 0; - return 0; - } - cifs_dbg(VFS, "rcvd invalid byte count (bcc)\n"); - return smb_EIO1(smb_eio_trace_rx_inv_bcc, tmp[sizeof(struct smb_hdr)]); - } else { - cifs_dbg(VFS, "Length less than smb header size\n"); - return smb_EIO2(smb_eio_trace_rx_too_short, - total_read, smb->WordCount); - } - } else if (total_read < sizeof(*smb) + 2 * smb->WordCount) { - cifs_dbg(VFS, "%s: can't read BCC due to invalid WordCount(%u)\n", - __func__, smb->WordCount); - return smb_EIO2(smb_eio_trace_rx_check_rsp, - total_read, 2 + sizeof(struct smb_hdr)); - } - - /* otherwise, there is enough to get to the BCC */ - if (check_smb_hdr(smb)) - return smb_EIO1(smb_eio_trace_rx_rfc1002_magic, *(u32 *)smb->Protocol); - clc_len = smbCalcSize(smb); - - if (rfclen != total_read) { - cifs_dbg(VFS, "Length read does not match RFC1001 length %d/%d\n", - rfclen, total_read); - return smb_EIO2(smb_eio_trace_rx_check_rsp, - total_read, rfclen); - } - - if (rfclen != clc_len) { - __u16 mid = get_mid(smb); - /* check if bcc wrapped around for large read responses */ - if ((rfclen > 64 * 1024) && (rfclen > clc_len)) { - /* check if lengths match mod 64K */ - if (((rfclen) & 0xFFFF) == (clc_len & 0xFFFF)) - return 0; /* bcc wrapped */ - } - cifs_dbg(FYI, "Calculated size %u vs length %u mismatch for mid=%u\n", - clc_len, rfclen, mid); - - if (rfclen < clc_len) { - cifs_dbg(VFS, "RFC1001 size %u smaller than SMB for mid=%u\n", - rfclen, mid); - return smb_EIO2(smb_eio_trace_rx_calc_len_too_big, - rfclen, clc_len); - } else if (rfclen > clc_len + 512) { - /* - * Some servers (Windows XP in particular) send more - * data than the lengths in the SMB packet would - * indicate on certain calls (byte range locks and - * trans2 find first calls in particular). While the - * client can handle such a frame by ignoring the - * trailing data, we choose limit the amount of extra - * data to 512 bytes. - */ - cifs_dbg(VFS, "RFC1001 size %u more than 512 bytes larger than SMB for mid=%u\n", - rfclen, mid); - return smb_EIO2(smb_eio_trace_rx_overlong, - rfclen, clc_len + 512); - } - } - return 0; -} - -bool -is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv) -{ - struct smb_hdr *buf = (struct smb_hdr *)buffer; - struct smb_com_lock_req *pSMB = (struct smb_com_lock_req *)buf; - struct TCP_Server_Info *pserver; - struct cifs_ses *ses; - struct cifs_tcon *tcon; - struct cifsInodeInfo *pCifsInode; - struct cifsFileInfo *netfile; - - cifs_dbg(FYI, "Checking for oplock break or dnotify response\n"); - if ((pSMB->hdr.Command == SMB_COM_NT_TRANSACT) && - (pSMB->hdr.Flags & SMBFLG_RESPONSE)) { - struct smb_com_transaction_change_notify_rsp *pSMBr = - (struct smb_com_transaction_change_notify_rsp *)buf; - struct file_notify_information *pnotify; - __u32 data_offset = 0; - size_t len = srv->total_read - srv->pdu_size; - - if (get_bcc(buf) > sizeof(struct file_notify_information)) { - data_offset = le32_to_cpu(pSMBr->DataOffset); - - if (data_offset > - len - sizeof(struct file_notify_information)) { - cifs_dbg(FYI, "Invalid data_offset %u\n", - data_offset); - return true; - } - pnotify = (struct file_notify_information *) - ((char *)&pSMBr->hdr.Protocol + data_offset); - cifs_dbg(FYI, "dnotify on %s Action: 0x%x\n", - pnotify->FileName, pnotify->Action); - /* cifs_dump_mem("Rcvd notify Data: ",buf, - sizeof(struct smb_hdr)+60); */ - return true; - } - if (pSMBr->hdr.Status.CifsError) { - cifs_dbg(FYI, "notify err 0x%x\n", - pSMBr->hdr.Status.CifsError); - return true; - } - return false; - } - if (pSMB->hdr.Command != SMB_COM_LOCKING_ANDX) - return false; - if (pSMB->hdr.Flags & SMBFLG_RESPONSE) { - /* no sense logging error on invalid handle on oplock - break - harmless race between close request and oplock - break response is expected from time to time writing out - large dirty files cached on the client */ - if ((NT_STATUS_INVALID_HANDLE) == - le32_to_cpu(pSMB->hdr.Status.CifsError)) { - cifs_dbg(FYI, "Invalid handle on oplock break\n"); - return true; - } else if (ERRbadfid == - le16_to_cpu(pSMB->hdr.Status.DosError.Error)) { - return true; - } else { - return false; /* on valid oplock brk we get "request" */ - } - } - if (pSMB->hdr.WordCount != 8) - return false; - - cifs_dbg(FYI, "oplock type 0x%x level 0x%x\n", - pSMB->LockType, pSMB->OplockLevel); - if (!(pSMB->LockType & LOCKING_ANDX_OPLOCK_RELEASE)) - return false; - - /* If server is a channel, select the primary channel */ - pserver = SERVER_IS_CHAN(srv) ? srv->primary_server : srv; - - /* look up tcon based on tid & uid */ - spin_lock(&cifs_tcp_ses_lock); - list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) { - if (cifs_ses_exiting(ses)) - continue; - list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { - if (tcon->tid != buf->Tid) - continue; - - cifs_stats_inc(&tcon->stats.cifs_stats.num_oplock_brks); - spin_lock(&tcon->open_file_lock); - list_for_each_entry(netfile, &tcon->openFileList, tlist) { - if (pSMB->Fid != netfile->fid.netfid) - continue; - - cifs_dbg(FYI, "file id match, oplock break\n"); - pCifsInode = CIFS_I(d_inode(netfile->dentry)); - - set_bit(CIFS_INODE_PENDING_OPLOCK_BREAK, - &pCifsInode->flags); - - netfile->oplock_epoch = 0; - netfile->oplock_level = pSMB->OplockLevel; - netfile->oplock_break_cancelled = false; - cifs_queue_oplock_break(netfile); - - spin_unlock(&tcon->open_file_lock); - spin_unlock(&cifs_tcp_ses_lock); - return true; - } - spin_unlock(&tcon->open_file_lock); - spin_unlock(&cifs_tcp_ses_lock); - cifs_dbg(FYI, "No matching file for oplock break\n"); - return true; - } - } - spin_unlock(&cifs_tcp_ses_lock); - cifs_dbg(FYI, "Can not process oplock break for non-existent connection\n"); - return true; -} - void dump_smb(void *buf, int smb_buf_length) { diff --git a/fs/smb/client/netlink.h b/fs/smb/client/netlink.h index e2fa8ed24c54..d35eef981b6b 100644 --- a/fs/smb/client/netlink.h +++ b/fs/smb/client/netlink.h @@ -10,7 +10,7 @@ extern struct genl_family cifs_genl_family; -extern int cifs_genl_init(void); -extern void cifs_genl_exit(void); +int cifs_genl_init(void); +void cifs_genl_exit(void); #endif /* _CIFS_NETLINK_H */ diff --git a/fs/smb/client/netmisc.c b/fs/smb/client/netmisc.c index ae15f0bef009..bddadee82d0c 100644 --- a/fs/smb/client/netmisc.c +++ b/fs/smb/client/netmisc.c @@ -17,105 +17,13 @@ #include <asm/byteorder.h> #include <linux/inet.h> #include "cifsfs.h" -#include "cifspdu.h" #include "cifsglob.h" #include "cifsproto.h" +#include "smb1proto.h" #include "smberr.h" #include "cifs_debug.h" #include "nterr.h" -struct smb_to_posix_error { - __u16 smb_err; - int posix_code; -}; - -static const struct smb_to_posix_error mapping_table_ERRDOS[] = { - {ERRbadfunc, -EINVAL}, - {ERRbadfile, -ENOENT}, - {ERRbadpath, -ENOTDIR}, - {ERRnofids, -EMFILE}, - {ERRnoaccess, -EACCES}, - {ERRbadfid, -EBADF}, - {ERRbadmcb, -EIO}, - {ERRnomem, -EREMOTEIO}, - {ERRbadmem, -EFAULT}, - {ERRbadenv, -EFAULT}, - {ERRbadformat, -EINVAL}, - {ERRbadaccess, -EACCES}, - {ERRbaddata, -EIO}, - {ERRbaddrive, -ENXIO}, - {ERRremcd, -EACCES}, - {ERRdiffdevice, -EXDEV}, - {ERRnofiles, -ENOENT}, - {ERRwriteprot, -EROFS}, - {ERRbadshare, -EBUSY}, - {ERRlock, -EACCES}, - {ERRunsup, -EINVAL}, - {ERRnosuchshare, -ENXIO}, - {ERRfilexists, -EEXIST}, - {ERRinvparm, -EINVAL}, - {ERRdiskfull, -ENOSPC}, - {ERRinvname, -ENOENT}, - {ERRinvlevel, -EOPNOTSUPP}, - {ERRdirnotempty, -ENOTEMPTY}, - {ERRnotlocked, -ENOLCK}, - {ERRcancelviolation, -ENOLCK}, - {ERRalreadyexists, -EEXIST}, - {ERRmoredata, -EOVERFLOW}, - {ERReasnotsupported, -EOPNOTSUPP}, - {ErrQuota, -EDQUOT}, - {ErrNotALink, -ENOLINK}, - {ERRnetlogonNotStarted, -ENOPROTOOPT}, - {ERRsymlink, -EOPNOTSUPP}, - {ErrTooManyLinks, -EMLINK}, - {0, 0} -}; - -static const struct smb_to_posix_error mapping_table_ERRSRV[] = { - {ERRerror, -EIO}, - {ERRbadpw, -EACCES}, /* was EPERM */ - {ERRbadtype, -EREMOTE}, - {ERRaccess, -EACCES}, - {ERRinvtid, -ENXIO}, - {ERRinvnetname, -ENXIO}, - {ERRinvdevice, -ENXIO}, - {ERRqfull, -ENOSPC}, - {ERRqtoobig, -ENOSPC}, - {ERRqeof, -EIO}, - {ERRinvpfid, -EBADF}, - {ERRsmbcmd, -EBADRQC}, - {ERRsrverror, -EIO}, - {ERRbadBID, -EIO}, - {ERRfilespecs, -EINVAL}, - {ERRbadLink, -EIO}, - {ERRbadpermits, -EINVAL}, - {ERRbadPID, -ESRCH}, - {ERRsetattrmode, -EINVAL}, - {ERRpaused, -EHOSTDOWN}, - {ERRmsgoff, -EHOSTDOWN}, - {ERRnoroom, -ENOSPC}, - {ERRrmuns, -EUSERS}, - {ERRtimeout, -ETIME}, - {ERRnoresource, -EREMOTEIO}, - {ERRtoomanyuids, -EUSERS}, - {ERRbaduid, -EACCES}, - {ERRusempx, -EIO}, - {ERRusestd, -EIO}, - {ERR_NOTIFY_ENUM_DIR, -ENOBUFS}, - {ERRnoSuchUser, -EACCES}, -/* {ERRaccountexpired, -EACCES}, - {ERRbadclient, -EACCES}, - {ERRbadLogonTime, -EACCES}, - {ERRpasswordExpired, -EACCES},*/ - {ERRaccountexpired, -EKEYEXPIRED}, - {ERRbadclient, -EACCES}, - {ERRbadLogonTime, -EACCES}, - {ERRpasswordExpired, -EKEYEXPIRED}, - - {ERRnosupport, -EINVAL}, - {0, 0} -}; - /* * Convert a string containing text IPv4 or IPv6 address to binary form. * @@ -199,736 +107,6 @@ cifs_set_port(struct sockaddr *addr, const unsigned short int port) } } -/***************************************************************************** - *convert a NT status code to a dos class/code - *****************************************************************************/ -/* NT status -> dos error map */ -static const struct { - __u8 dos_class; - __u16 dos_code; - __u32 ntstatus; -} ntstatus_to_dos_map[] = { - { - ERRDOS, ERRgeneral, NT_STATUS_UNSUCCESSFUL}, { - ERRDOS, ERRbadfunc, NT_STATUS_NOT_IMPLEMENTED}, { - ERRDOS, ERRinvlevel, NT_STATUS_INVALID_INFO_CLASS}, { - ERRDOS, 24, NT_STATUS_INFO_LENGTH_MISMATCH}, { - ERRHRD, ERRgeneral, NT_STATUS_ACCESS_VIOLATION}, { - ERRHRD, ERRgeneral, NT_STATUS_IN_PAGE_ERROR}, { - ERRHRD, ERRgeneral, NT_STATUS_PAGEFILE_QUOTA}, { - ERRDOS, ERRbadfid, NT_STATUS_INVALID_HANDLE}, { - ERRHRD, ERRgeneral, NT_STATUS_BAD_INITIAL_STACK}, { - ERRDOS, 193, NT_STATUS_BAD_INITIAL_PC}, { - ERRDOS, 87, NT_STATUS_INVALID_CID}, { - ERRHRD, ERRgeneral, NT_STATUS_TIMER_NOT_CANCELED}, { - ERRDOS, 87, NT_STATUS_INVALID_PARAMETER}, { - ERRDOS, ERRbadfile, NT_STATUS_NO_SUCH_DEVICE}, { - ERRDOS, ERRbadfile, NT_STATUS_NO_SUCH_FILE}, { - ERRDOS, ERRbadfunc, NT_STATUS_INVALID_DEVICE_REQUEST}, { - ERRDOS, 38, NT_STATUS_END_OF_FILE}, { - ERRDOS, 34, NT_STATUS_WRONG_VOLUME}, { - ERRDOS, 21, NT_STATUS_NO_MEDIA_IN_DEVICE}, { - ERRHRD, ERRgeneral, NT_STATUS_UNRECOGNIZED_MEDIA}, { - ERRDOS, 27, NT_STATUS_NONEXISTENT_SECTOR}, -/* { This NT error code was 'sqashed' - from NT_STATUS_MORE_PROCESSING_REQUIRED to NT_STATUS_OK - during the session setup } */ - { - ERRDOS, ERRnomem, NT_STATUS_NO_MEMORY}, { - ERRDOS, 487, NT_STATUS_CONFLICTING_ADDRESSES}, { - ERRDOS, 487, NT_STATUS_NOT_MAPPED_VIEW}, { - ERRDOS, 87, NT_STATUS_UNABLE_TO_FREE_VM}, { - ERRDOS, 87, NT_STATUS_UNABLE_TO_DELETE_SECTION}, { - ERRDOS, 2142, NT_STATUS_INVALID_SYSTEM_SERVICE}, { - ERRHRD, ERRgeneral, NT_STATUS_ILLEGAL_INSTRUCTION}, { - ERRDOS, ERRnoaccess, NT_STATUS_INVALID_LOCK_SEQUENCE}, { - ERRDOS, ERRnoaccess, NT_STATUS_INVALID_VIEW_SIZE}, { - ERRDOS, 193, NT_STATUS_INVALID_FILE_FOR_SECTION}, { - ERRDOS, ERRnoaccess, NT_STATUS_ALREADY_COMMITTED}, -/* { This NT error code was 'sqashed' - from NT_STATUS_ACCESS_DENIED to NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE - during the session setup } */ - { - ERRDOS, ERRnoaccess, NT_STATUS_ACCESS_DENIED}, { - ERRDOS, 111, NT_STATUS_BUFFER_TOO_SMALL}, { - ERRDOS, ERRbadfid, NT_STATUS_OBJECT_TYPE_MISMATCH}, { - ERRHRD, ERRgeneral, NT_STATUS_NONCONTINUABLE_EXCEPTION}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_DISPOSITION}, { - ERRHRD, ERRgeneral, NT_STATUS_UNWIND}, { - ERRHRD, ERRgeneral, NT_STATUS_BAD_STACK}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_UNWIND_TARGET}, { - ERRDOS, 158, NT_STATUS_NOT_LOCKED}, { - ERRHRD, ERRgeneral, NT_STATUS_PARITY_ERROR}, { - ERRDOS, 487, NT_STATUS_UNABLE_TO_DECOMMIT_VM}, { - ERRDOS, 487, NT_STATUS_NOT_COMMITTED}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_PORT_ATTRIBUTES}, { - ERRHRD, ERRgeneral, NT_STATUS_PORT_MESSAGE_TOO_LONG}, { - ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_MIX}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_QUOTA_LOWER}, { - ERRHRD, ERRgeneral, NT_STATUS_DISK_CORRUPT_ERROR}, { - /* mapping changed since shell does lookup on * expects FileNotFound */ - ERRDOS, ERRbadfile, NT_STATUS_OBJECT_NAME_INVALID}, { - ERRDOS, ERRbadfile, NT_STATUS_OBJECT_NAME_NOT_FOUND}, { - ERRDOS, ERRalreadyexists, NT_STATUS_OBJECT_NAME_COLLISION}, { - ERRHRD, ERRgeneral, NT_STATUS_HANDLE_NOT_WAITABLE}, { - ERRDOS, ERRbadfid, NT_STATUS_PORT_DISCONNECTED}, { - ERRHRD, ERRgeneral, NT_STATUS_DEVICE_ALREADY_ATTACHED}, { - ERRDOS, 161, NT_STATUS_OBJECT_PATH_INVALID}, { - ERRDOS, ERRbadpath, NT_STATUS_OBJECT_PATH_NOT_FOUND}, { - ERRDOS, 161, NT_STATUS_OBJECT_PATH_SYNTAX_BAD}, { - ERRHRD, ERRgeneral, NT_STATUS_DATA_OVERRUN}, { - ERRHRD, ERRgeneral, NT_STATUS_DATA_LATE_ERROR}, { - ERRDOS, 23, NT_STATUS_DATA_ERROR}, { - ERRDOS, 23, NT_STATUS_CRC_ERROR}, { - ERRDOS, ERRnomem, NT_STATUS_SECTION_TOO_BIG}, { - ERRDOS, ERRnoaccess, NT_STATUS_PORT_CONNECTION_REFUSED}, { - ERRDOS, ERRbadfid, NT_STATUS_INVALID_PORT_HANDLE}, { - ERRDOS, ERRbadshare, NT_STATUS_SHARING_VIOLATION}, { - ERRHRD, ERRgeneral, NT_STATUS_QUOTA_EXCEEDED}, { - ERRDOS, 87, NT_STATUS_INVALID_PAGE_PROTECTION}, { - ERRDOS, 288, NT_STATUS_MUTANT_NOT_OWNED}, { - ERRDOS, 298, NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED}, { - ERRDOS, 87, NT_STATUS_PORT_ALREADY_SET}, { - ERRDOS, 87, NT_STATUS_SECTION_NOT_IMAGE}, { - ERRDOS, 156, NT_STATUS_SUSPEND_COUNT_EXCEEDED}, { - ERRDOS, ERRnoaccess, NT_STATUS_THREAD_IS_TERMINATING}, { - ERRDOS, 87, NT_STATUS_BAD_WORKING_SET_LIMIT}, { - ERRDOS, 87, NT_STATUS_INCOMPATIBLE_FILE_MAP}, { - ERRDOS, 87, NT_STATUS_SECTION_PROTECTION}, { - ERRDOS, ERReasnotsupported, NT_STATUS_EAS_NOT_SUPPORTED}, { - ERRDOS, 255, NT_STATUS_EA_TOO_LARGE}, { - ERRHRD, ERRgeneral, NT_STATUS_NONEXISTENT_EA_ENTRY}, { - ERRHRD, ERRgeneral, NT_STATUS_NO_EAS_ON_FILE}, { - ERRHRD, ERRgeneral, NT_STATUS_EA_CORRUPT_ERROR}, { - ERRDOS, ERRlock, NT_STATUS_FILE_LOCK_CONFLICT}, { - ERRDOS, ERRlock, NT_STATUS_LOCK_NOT_GRANTED}, { - ERRDOS, ERRbadfile, NT_STATUS_DELETE_PENDING}, { - ERRDOS, ERRunsup, NT_STATUS_CTL_FILE_NOT_SUPPORTED}, { - ERRHRD, ERRgeneral, NT_STATUS_UNKNOWN_REVISION}, { - ERRHRD, ERRgeneral, NT_STATUS_REVISION_MISMATCH}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_OWNER}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_PRIMARY_GROUP}, { - ERRHRD, ERRgeneral, NT_STATUS_NO_IMPERSONATION_TOKEN}, { - ERRHRD, ERRgeneral, NT_STATUS_CANT_DISABLE_MANDATORY}, { - ERRDOS, 2215, NT_STATUS_NO_LOGON_SERVERS}, { - ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_LOGON_SESSION}, { - ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_PRIVILEGE}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_ACCOUNT_NAME}, { - ERRHRD, ERRgeneral, NT_STATUS_USER_EXISTS}, -/* { This NT error code was 'sqashed' - from NT_STATUS_NO_SUCH_USER to NT_STATUS_LOGON_FAILURE - during the session setup } */ - { - ERRDOS, ERRnoaccess, NT_STATUS_NO_SUCH_USER}, { /* could map to 2238 */ - ERRHRD, ERRgeneral, NT_STATUS_GROUP_EXISTS}, { - ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_GROUP}, { - ERRHRD, ERRgeneral, NT_STATUS_MEMBER_IN_GROUP}, { - ERRHRD, ERRgeneral, NT_STATUS_MEMBER_NOT_IN_GROUP}, { - ERRHRD, ERRgeneral, NT_STATUS_LAST_ADMIN}, -/* { This NT error code was 'sqashed' - from NT_STATUS_WRONG_PASSWORD to NT_STATUS_LOGON_FAILURE - during the session setup } */ - { - ERRSRV, ERRbadpw, NT_STATUS_WRONG_PASSWORD}, { - ERRHRD, ERRgeneral, NT_STATUS_ILL_FORMED_PASSWORD}, { - ERRHRD, ERRgeneral, NT_STATUS_PASSWORD_RESTRICTION}, { - ERRDOS, ERRnoaccess, NT_STATUS_LOGON_FAILURE}, { - ERRHRD, ERRgeneral, NT_STATUS_ACCOUNT_RESTRICTION}, { - ERRSRV, ERRbadLogonTime, NT_STATUS_INVALID_LOGON_HOURS}, { - ERRSRV, ERRbadclient, NT_STATUS_INVALID_WORKSTATION}, { - ERRSRV, ERRpasswordExpired, NT_STATUS_PASSWORD_EXPIRED}, { - ERRSRV, ERRaccountexpired, NT_STATUS_ACCOUNT_DISABLED}, { - ERRHRD, ERRgeneral, NT_STATUS_NONE_MAPPED}, { - ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_LUIDS_REQUESTED}, { - ERRHRD, ERRgeneral, NT_STATUS_LUIDS_EXHAUSTED}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_SUB_AUTHORITY}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_ACL}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_SID}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_SECURITY_DESCR}, { - ERRDOS, 127, NT_STATUS_PROCEDURE_NOT_FOUND}, { - ERRDOS, 193, NT_STATUS_INVALID_IMAGE_FORMAT}, { - ERRHRD, ERRgeneral, NT_STATUS_NO_TOKEN}, { - ERRHRD, ERRgeneral, NT_STATUS_BAD_INHERITANCE_ACL}, { - ERRDOS, 158, NT_STATUS_RANGE_NOT_LOCKED}, { - ERRDOS, 112, NT_STATUS_DISK_FULL}, { - ERRHRD, ERRgeneral, NT_STATUS_SERVER_DISABLED}, { - ERRHRD, ERRgeneral, NT_STATUS_SERVER_NOT_DISABLED}, { - ERRDOS, 68, NT_STATUS_TOO_MANY_GUIDS_REQUESTED}, { - ERRDOS, 259, NT_STATUS_GUIDS_EXHAUSTED}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_ID_AUTHORITY}, { - ERRDOS, 259, NT_STATUS_AGENTS_EXHAUSTED}, { - ERRDOS, 154, NT_STATUS_INVALID_VOLUME_LABEL}, { - ERRDOS, 14, NT_STATUS_SECTION_NOT_EXTENDED}, { - ERRDOS, 487, NT_STATUS_NOT_MAPPED_DATA}, { - ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_DATA_NOT_FOUND}, { - ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_TYPE_NOT_FOUND}, { - ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_NAME_NOT_FOUND}, { - ERRHRD, ERRgeneral, NT_STATUS_ARRAY_BOUNDS_EXCEEDED}, { - ERRHRD, ERRgeneral, NT_STATUS_FLOAT_DENORMAL_OPERAND}, { - ERRHRD, ERRgeneral, NT_STATUS_FLOAT_DIVIDE_BY_ZERO}, { - ERRHRD, ERRgeneral, NT_STATUS_FLOAT_INEXACT_RESULT}, { - ERRHRD, ERRgeneral, NT_STATUS_FLOAT_INVALID_OPERATION}, { - ERRHRD, ERRgeneral, NT_STATUS_FLOAT_OVERFLOW}, { - ERRHRD, ERRgeneral, NT_STATUS_FLOAT_STACK_CHECK}, { - ERRHRD, ERRgeneral, NT_STATUS_FLOAT_UNDERFLOW}, { - ERRHRD, ERRgeneral, NT_STATUS_INTEGER_DIVIDE_BY_ZERO}, { - ERRDOS, 534, NT_STATUS_INTEGER_OVERFLOW}, { - ERRHRD, ERRgeneral, NT_STATUS_PRIVILEGED_INSTRUCTION}, { - ERRDOS, ERRnomem, NT_STATUS_TOO_MANY_PAGING_FILES}, { - ERRHRD, ERRgeneral, NT_STATUS_FILE_INVALID}, { - ERRHRD, ERRgeneral, NT_STATUS_ALLOTTED_SPACE_EXCEEDED}, -/* { This NT error code was 'sqashed' - from NT_STATUS_INSUFFICIENT_RESOURCES to - NT_STATUS_INSUFF_SERVER_RESOURCES during the session setup } */ - { - ERRDOS, ERRnoresource, NT_STATUS_INSUFFICIENT_RESOURCES}, { - ERRDOS, ERRbadpath, NT_STATUS_DFS_EXIT_PATH_FOUND}, { - ERRDOS, 23, NT_STATUS_DEVICE_DATA_ERROR}, { - ERRHRD, ERRgeneral, NT_STATUS_DEVICE_NOT_CONNECTED}, { - ERRDOS, 21, NT_STATUS_DEVICE_POWER_FAILURE}, { - ERRDOS, 487, NT_STATUS_FREE_VM_NOT_AT_BASE}, { - ERRDOS, 487, NT_STATUS_MEMORY_NOT_ALLOCATED}, { - ERRHRD, ERRgeneral, NT_STATUS_WORKING_SET_QUOTA}, { - ERRDOS, 19, NT_STATUS_MEDIA_WRITE_PROTECTED}, { - ERRDOS, 21, NT_STATUS_DEVICE_NOT_READY}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_GROUP_ATTRIBUTES}, { - ERRHRD, ERRgeneral, NT_STATUS_BAD_IMPERSONATION_LEVEL}, { - ERRHRD, ERRgeneral, NT_STATUS_CANT_OPEN_ANONYMOUS}, { - ERRHRD, ERRgeneral, NT_STATUS_BAD_VALIDATION_CLASS}, { - ERRHRD, ERRgeneral, NT_STATUS_BAD_TOKEN_TYPE}, { - ERRDOS, 87, NT_STATUS_BAD_MASTER_BOOT_RECORD}, { - ERRHRD, ERRgeneral, NT_STATUS_INSTRUCTION_MISALIGNMENT}, { - ERRDOS, ERRpipebusy, NT_STATUS_INSTANCE_NOT_AVAILABLE}, { - ERRDOS, ERRpipebusy, NT_STATUS_PIPE_NOT_AVAILABLE}, { - ERRDOS, ERRbadpipe, NT_STATUS_INVALID_PIPE_STATE}, { - ERRDOS, ERRpipebusy, NT_STATUS_PIPE_BUSY}, { - ERRDOS, ERRbadfunc, NT_STATUS_ILLEGAL_FUNCTION}, { - ERRDOS, ERRnotconnected, NT_STATUS_PIPE_DISCONNECTED}, { - ERRDOS, ERRpipeclosing, NT_STATUS_PIPE_CLOSING}, { - ERRHRD, ERRgeneral, NT_STATUS_PIPE_CONNECTED}, { - ERRHRD, ERRgeneral, NT_STATUS_PIPE_LISTENING}, { - ERRDOS, ERRbadpipe, NT_STATUS_INVALID_READ_MODE}, { - ERRDOS, 121, NT_STATUS_IO_TIMEOUT}, { - ERRDOS, 38, NT_STATUS_FILE_FORCED_CLOSED}, { - ERRHRD, ERRgeneral, NT_STATUS_PROFILING_NOT_STARTED}, { - ERRHRD, ERRgeneral, NT_STATUS_PROFILING_NOT_STOPPED}, { - ERRHRD, ERRgeneral, NT_STATUS_COULD_NOT_INTERPRET}, { - ERRDOS, ERRnoaccess, NT_STATUS_FILE_IS_A_DIRECTORY}, { - ERRDOS, ERRunsup, NT_STATUS_NOT_SUPPORTED}, { - ERRDOS, 51, NT_STATUS_REMOTE_NOT_LISTENING}, { - ERRDOS, 52, NT_STATUS_DUPLICATE_NAME}, { - ERRDOS, 53, NT_STATUS_BAD_NETWORK_PATH}, { - ERRDOS, 54, NT_STATUS_NETWORK_BUSY}, { - ERRDOS, 55, NT_STATUS_DEVICE_DOES_NOT_EXIST}, { - ERRDOS, 56, NT_STATUS_TOO_MANY_COMMANDS}, { - ERRDOS, 57, NT_STATUS_ADAPTER_HARDWARE_ERROR}, { - ERRDOS, 58, NT_STATUS_INVALID_NETWORK_RESPONSE}, { - ERRDOS, 59, NT_STATUS_UNEXPECTED_NETWORK_ERROR}, { - ERRDOS, 60, NT_STATUS_BAD_REMOTE_ADAPTER}, { - ERRDOS, 61, NT_STATUS_PRINT_QUEUE_FULL}, { - ERRDOS, 62, NT_STATUS_NO_SPOOL_SPACE}, { - ERRDOS, 63, NT_STATUS_PRINT_CANCELLED}, { - ERRDOS, 64, NT_STATUS_NETWORK_NAME_DELETED}, { - ERRDOS, 65, NT_STATUS_NETWORK_ACCESS_DENIED}, { - ERRDOS, 66, NT_STATUS_BAD_DEVICE_TYPE}, { - ERRDOS, ERRnosuchshare, NT_STATUS_BAD_NETWORK_NAME}, { - ERRDOS, 68, NT_STATUS_TOO_MANY_NAMES}, { - ERRDOS, 69, NT_STATUS_TOO_MANY_SESSIONS}, { - ERRDOS, 70, NT_STATUS_SHARING_PAUSED}, { - ERRDOS, 71, NT_STATUS_REQUEST_NOT_ACCEPTED}, { - ERRDOS, 72, NT_STATUS_REDIRECTOR_PAUSED}, { - ERRDOS, 88, NT_STATUS_NET_WRITE_FAULT}, { - ERRHRD, ERRgeneral, NT_STATUS_PROFILING_AT_LIMIT}, { - ERRDOS, ERRdiffdevice, NT_STATUS_NOT_SAME_DEVICE}, { - ERRDOS, ERRnoaccess, NT_STATUS_FILE_RENAMED}, { - ERRDOS, 240, NT_STATUS_VIRTUAL_CIRCUIT_CLOSED}, { - ERRHRD, ERRgeneral, NT_STATUS_NO_SECURITY_ON_OBJECT}, { - ERRHRD, ERRgeneral, NT_STATUS_CANT_WAIT}, { - ERRDOS, ERRpipeclosing, NT_STATUS_PIPE_EMPTY}, { - ERRHRD, ERRgeneral, NT_STATUS_CANT_ACCESS_DOMAIN_INFO}, { - ERRHRD, ERRgeneral, NT_STATUS_CANT_TERMINATE_SELF}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_SERVER_STATE}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_DOMAIN_STATE}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_DOMAIN_ROLE}, { - ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_DOMAIN}, { - ERRHRD, ERRgeneral, NT_STATUS_DOMAIN_EXISTS}, { - ERRHRD, ERRgeneral, NT_STATUS_DOMAIN_LIMIT_EXCEEDED}, { - ERRDOS, 300, NT_STATUS_OPLOCK_NOT_GRANTED}, { - ERRDOS, 301, NT_STATUS_INVALID_OPLOCK_PROTOCOL}, { - ERRHRD, ERRgeneral, NT_STATUS_INTERNAL_DB_CORRUPTION}, { - ERRHRD, ERRgeneral, NT_STATUS_INTERNAL_ERROR}, { - ERRHRD, ERRgeneral, NT_STATUS_GENERIC_NOT_MAPPED}, { - ERRHRD, ERRgeneral, NT_STATUS_BAD_DESCRIPTOR_FORMAT}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_USER_BUFFER}, { - ERRHRD, ERRgeneral, NT_STATUS_UNEXPECTED_IO_ERROR}, { - ERRHRD, ERRgeneral, NT_STATUS_UNEXPECTED_MM_CREATE_ERR}, { - ERRHRD, ERRgeneral, NT_STATUS_UNEXPECTED_MM_MAP_ERROR}, { - ERRHRD, ERRgeneral, NT_STATUS_UNEXPECTED_MM_EXTEND_ERR}, { - ERRHRD, ERRgeneral, NT_STATUS_NOT_LOGON_PROCESS}, { - ERRHRD, ERRgeneral, NT_STATUS_LOGON_SESSION_EXISTS}, { - ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_1}, { - ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_2}, { - ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_3}, { - ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_4}, { - ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_5}, { - ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_6}, { - ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_7}, { - ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_8}, { - ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_9}, { - ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_10}, { - ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_11}, { - ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_12}, { - ERRDOS, ERRbadpath, NT_STATUS_REDIRECTOR_NOT_STARTED}, { - ERRHRD, ERRgeneral, NT_STATUS_REDIRECTOR_STARTED}, { - ERRHRD, ERRgeneral, NT_STATUS_STACK_OVERFLOW}, { - ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_PACKAGE}, { - ERRHRD, ERRgeneral, NT_STATUS_BAD_FUNCTION_TABLE}, { - ERRDOS, 203, 0xc0000100}, { - ERRDOS, 145, NT_STATUS_DIRECTORY_NOT_EMPTY}, { - ERRHRD, ERRgeneral, NT_STATUS_FILE_CORRUPT_ERROR}, { - ERRDOS, 267, NT_STATUS_NOT_A_DIRECTORY}, { - ERRHRD, ERRgeneral, NT_STATUS_BAD_LOGON_SESSION_STATE}, { - ERRHRD, ERRgeneral, NT_STATUS_LOGON_SESSION_COLLISION}, { - ERRDOS, 206, NT_STATUS_NAME_TOO_LONG}, { - ERRDOS, 2401, NT_STATUS_FILES_OPEN}, { - ERRDOS, 2404, NT_STATUS_CONNECTION_IN_USE}, { - ERRHRD, ERRgeneral, NT_STATUS_MESSAGE_NOT_FOUND}, { - ERRDOS, ERRnoaccess, NT_STATUS_PROCESS_IS_TERMINATING}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_LOGON_TYPE}, { - ERRHRD, ERRgeneral, NT_STATUS_NO_GUID_TRANSLATION}, { - ERRHRD, ERRgeneral, NT_STATUS_CANNOT_IMPERSONATE}, { - ERRHRD, ERRgeneral, NT_STATUS_IMAGE_ALREADY_LOADED}, { - ERRHRD, ERRgeneral, NT_STATUS_ABIOS_NOT_PRESENT}, { - ERRHRD, ERRgeneral, NT_STATUS_ABIOS_LID_NOT_EXIST}, { - ERRHRD, ERRgeneral, NT_STATUS_ABIOS_LID_ALREADY_OWNED}, { - ERRHRD, ERRgeneral, NT_STATUS_ABIOS_NOT_LID_OWNER}, { - ERRHRD, ERRgeneral, NT_STATUS_ABIOS_INVALID_COMMAND}, { - ERRHRD, ERRgeneral, NT_STATUS_ABIOS_INVALID_LID}, { - ERRHRD, ERRgeneral, NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE}, { - ERRHRD, ERRgeneral, NT_STATUS_ABIOS_INVALID_SELECTOR}, { - ERRHRD, ERRgeneral, NT_STATUS_NO_LDT}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_LDT_SIZE}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_LDT_OFFSET}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_LDT_DESCRIPTOR}, { - ERRDOS, 193, NT_STATUS_INVALID_IMAGE_NE_FORMAT}, { - ERRHRD, ERRgeneral, NT_STATUS_RXACT_INVALID_STATE}, { - ERRHRD, ERRgeneral, NT_STATUS_RXACT_COMMIT_FAILURE}, { - ERRHRD, ERRgeneral, NT_STATUS_MAPPED_FILE_SIZE_ZERO}, { - ERRDOS, ERRnofids, NT_STATUS_TOO_MANY_OPENED_FILES}, { - ERRHRD, ERRgeneral, NT_STATUS_CANCELLED}, { - ERRDOS, ERRnoaccess, NT_STATUS_CANNOT_DELETE}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_COMPUTER_NAME}, { - ERRDOS, ERRnoaccess, NT_STATUS_FILE_DELETED}, { - ERRHRD, ERRgeneral, NT_STATUS_SPECIAL_ACCOUNT}, { - ERRHRD, ERRgeneral, NT_STATUS_SPECIAL_GROUP}, { - ERRHRD, ERRgeneral, NT_STATUS_SPECIAL_USER}, { - ERRHRD, ERRgeneral, NT_STATUS_MEMBERS_PRIMARY_GROUP}, { - ERRDOS, ERRbadfid, NT_STATUS_FILE_CLOSED}, { - ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_THREADS}, { - ERRHRD, ERRgeneral, NT_STATUS_THREAD_NOT_IN_PROCESS}, { - ERRHRD, ERRgeneral, NT_STATUS_TOKEN_ALREADY_IN_USE}, { - ERRHRD, ERRgeneral, NT_STATUS_PAGEFILE_QUOTA_EXCEEDED}, { - ERRHRD, ERRgeneral, NT_STATUS_COMMITMENT_LIMIT}, { - ERRDOS, 193, NT_STATUS_INVALID_IMAGE_LE_FORMAT}, { - ERRDOS, 193, NT_STATUS_INVALID_IMAGE_NOT_MZ}, { - ERRDOS, 193, NT_STATUS_INVALID_IMAGE_PROTECT}, { - ERRDOS, 193, NT_STATUS_INVALID_IMAGE_WIN_16}, { - ERRHRD, ERRgeneral, NT_STATUS_LOGON_SERVER_CONFLICT}, { - ERRHRD, ERRgeneral, NT_STATUS_TIME_DIFFERENCE_AT_DC}, { - ERRHRD, ERRgeneral, NT_STATUS_SYNCHRONIZATION_REQUIRED}, { - ERRDOS, 126, NT_STATUS_DLL_NOT_FOUND}, { - ERRHRD, ERRgeneral, NT_STATUS_OPEN_FAILED}, { - ERRHRD, ERRgeneral, NT_STATUS_IO_PRIVILEGE_FAILED}, { - ERRDOS, 182, NT_STATUS_ORDINAL_NOT_FOUND}, { - ERRDOS, 127, NT_STATUS_ENTRYPOINT_NOT_FOUND}, { - ERRHRD, ERRgeneral, NT_STATUS_CONTROL_C_EXIT}, { - ERRDOS, 64, NT_STATUS_LOCAL_DISCONNECT}, { - ERRDOS, 64, NT_STATUS_REMOTE_DISCONNECT}, { - ERRDOS, 51, NT_STATUS_REMOTE_RESOURCES}, { - ERRDOS, 59, NT_STATUS_LINK_FAILED}, { - ERRDOS, 59, NT_STATUS_LINK_TIMEOUT}, { - ERRDOS, 59, NT_STATUS_INVALID_CONNECTION}, { - ERRDOS, 59, NT_STATUS_INVALID_ADDRESS}, { - ERRHRD, ERRgeneral, NT_STATUS_DLL_INIT_FAILED}, { - ERRHRD, ERRgeneral, NT_STATUS_MISSING_SYSTEMFILE}, { - ERRHRD, ERRgeneral, NT_STATUS_UNHANDLED_EXCEPTION}, { - ERRHRD, ERRgeneral, NT_STATUS_APP_INIT_FAILURE}, { - ERRHRD, ERRgeneral, NT_STATUS_PAGEFILE_CREATE_FAILED}, { - ERRHRD, ERRgeneral, NT_STATUS_NO_PAGEFILE}, { - ERRDOS, 124, NT_STATUS_INVALID_LEVEL}, { - ERRDOS, 86, NT_STATUS_WRONG_PASSWORD_CORE}, { - ERRHRD, ERRgeneral, NT_STATUS_ILLEGAL_FLOAT_CONTEXT}, { - ERRDOS, 109, NT_STATUS_PIPE_BROKEN}, { - ERRHRD, ERRgeneral, NT_STATUS_REGISTRY_CORRUPT}, { - ERRHRD, ERRgeneral, NT_STATUS_REGISTRY_IO_FAILED}, { - ERRHRD, ERRgeneral, NT_STATUS_NO_EVENT_PAIR}, { - ERRHRD, ERRgeneral, NT_STATUS_UNRECOGNIZED_VOLUME}, { - ERRHRD, ERRgeneral, NT_STATUS_SERIAL_NO_DEVICE_INITED}, { - ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_ALIAS}, { - ERRHRD, ERRgeneral, NT_STATUS_MEMBER_NOT_IN_ALIAS}, { - ERRHRD, ERRgeneral, NT_STATUS_MEMBER_IN_ALIAS}, { - ERRHRD, ERRgeneral, NT_STATUS_ALIAS_EXISTS}, { - ERRHRD, ERRgeneral, NT_STATUS_LOGON_NOT_GRANTED}, { - ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_SECRETS}, { - ERRHRD, ERRgeneral, NT_STATUS_SECRET_TOO_LONG}, { - ERRHRD, ERRgeneral, NT_STATUS_INTERNAL_DB_ERROR}, { - ERRHRD, ERRgeneral, NT_STATUS_FULLSCREEN_MODE}, { - ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_CONTEXT_IDS}, { - ERRDOS, ERRnoaccess, NT_STATUS_LOGON_TYPE_NOT_GRANTED}, { - ERRHRD, ERRgeneral, NT_STATUS_NOT_REGISTRY_FILE}, { - ERRHRD, ERRgeneral, NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED}, { - ERRHRD, ERRgeneral, NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR}, { - ERRHRD, ERRgeneral, NT_STATUS_FT_MISSING_MEMBER}, { - ERRHRD, ERRgeneral, NT_STATUS_ILL_FORMED_SERVICE_ENTRY}, { - ERRHRD, ERRgeneral, NT_STATUS_ILLEGAL_CHARACTER}, { - ERRHRD, ERRgeneral, NT_STATUS_UNMAPPABLE_CHARACTER}, { - ERRHRD, ERRgeneral, NT_STATUS_UNDEFINED_CHARACTER}, { - ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_VOLUME}, { - ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND}, { - ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_WRONG_CYLINDER}, { - ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_UNKNOWN_ERROR}, { - ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_BAD_REGISTERS}, { - ERRHRD, ERRgeneral, NT_STATUS_DISK_RECALIBRATE_FAILED}, { - ERRHRD, ERRgeneral, NT_STATUS_DISK_OPERATION_FAILED}, { - ERRHRD, ERRgeneral, NT_STATUS_DISK_RESET_FAILED}, { - ERRHRD, ERRgeneral, NT_STATUS_SHARED_IRQ_BUSY}, { - ERRHRD, ERRgeneral, NT_STATUS_FT_ORPHANING}, { - ERRHRD, ERRgeneral, 0xc000016e}, { - ERRHRD, ERRgeneral, 0xc000016f}, { - ERRHRD, ERRgeneral, 0xc0000170}, { - ERRHRD, ERRgeneral, 0xc0000171}, { - ERRHRD, ERRgeneral, NT_STATUS_PARTITION_FAILURE}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_BLOCK_LENGTH}, { - ERRHRD, ERRgeneral, NT_STATUS_DEVICE_NOT_PARTITIONED}, { - ERRHRD, ERRgeneral, NT_STATUS_UNABLE_TO_LOCK_MEDIA}, { - ERRHRD, ERRgeneral, NT_STATUS_UNABLE_TO_UNLOAD_MEDIA}, { - ERRHRD, ERRgeneral, NT_STATUS_EOM_OVERFLOW}, { - ERRHRD, ERRgeneral, NT_STATUS_NO_MEDIA}, { - ERRHRD, ERRgeneral, 0xc0000179}, { - ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_MEMBER}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_MEMBER}, { - ERRHRD, ERRgeneral, NT_STATUS_KEY_DELETED}, { - ERRHRD, ERRgeneral, NT_STATUS_NO_LOG_SPACE}, { - ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_SIDS}, { - ERRHRD, ERRgeneral, NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED}, { - ERRHRD, ERRgeneral, NT_STATUS_KEY_HAS_CHILDREN}, { - ERRHRD, ERRgeneral, NT_STATUS_CHILD_MUST_BE_VOLATILE}, { - ERRDOS, 87, NT_STATUS_DEVICE_CONFIGURATION_ERROR}, { - ERRHRD, ERRgeneral, NT_STATUS_DRIVER_INTERNAL_ERROR}, { - ERRDOS, 22, NT_STATUS_INVALID_DEVICE_STATE}, { - ERRHRD, ERRgeneral, NT_STATUS_IO_DEVICE_ERROR}, { - ERRHRD, ERRgeneral, NT_STATUS_DEVICE_PROTOCOL_ERROR}, { - ERRHRD, ERRgeneral, NT_STATUS_BACKUP_CONTROLLER}, { - ERRHRD, ERRgeneral, NT_STATUS_LOG_FILE_FULL}, { - ERRDOS, 19, NT_STATUS_TOO_LATE}, { - ERRDOS, ERRnoaccess, NT_STATUS_NO_TRUST_LSA_SECRET}, -/* { This NT error code was 'sqashed' - from NT_STATUS_NO_TRUST_SAM_ACCOUNT to - NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE during the session setup } */ - { - ERRDOS, ERRnoaccess, NT_STATUS_NO_TRUST_SAM_ACCOUNT}, { - ERRDOS, ERRnoaccess, NT_STATUS_TRUSTED_DOMAIN_FAILURE}, { - ERRDOS, ERRnoaccess, NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE}, { - ERRHRD, ERRgeneral, NT_STATUS_EVENTLOG_FILE_CORRUPT}, { - ERRHRD, ERRgeneral, NT_STATUS_EVENTLOG_CANT_START}, { - ERRDOS, ERRnoaccess, NT_STATUS_TRUST_FAILURE}, { - ERRHRD, ERRgeneral, NT_STATUS_MUTANT_LIMIT_EXCEEDED}, { - ERRDOS, ERRnetlogonNotStarted, NT_STATUS_NETLOGON_NOT_STARTED}, { - ERRSRV, ERRaccountexpired, NT_STATUS_ACCOUNT_EXPIRED}, { - ERRHRD, ERRgeneral, NT_STATUS_POSSIBLE_DEADLOCK}, { - ERRHRD, ERRgeneral, NT_STATUS_NETWORK_CREDENTIAL_CONFLICT}, { - ERRHRD, ERRgeneral, NT_STATUS_REMOTE_SESSION_LIMIT}, { - ERRHRD, ERRgeneral, NT_STATUS_EVENTLOG_FILE_CHANGED}, { - ERRDOS, ERRnoaccess, NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT}, { - ERRDOS, ERRnoaccess, NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT}, { - ERRDOS, ERRnoaccess, NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT}, -/* { This NT error code was 'sqashed' - from NT_STATUS_DOMAIN_TRUST_INCONSISTENT to NT_STATUS_LOGON_FAILURE - during the session setup } */ - { - ERRDOS, ERRnoaccess, NT_STATUS_DOMAIN_TRUST_INCONSISTENT}, { - ERRHRD, ERRgeneral, NT_STATUS_FS_DRIVER_REQUIRED}, { - ERRHRD, ERRgeneral, NT_STATUS_NO_USER_SESSION_KEY}, { - ERRDOS, 59, NT_STATUS_USER_SESSION_DELETED}, { - ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_LANG_NOT_FOUND}, { - ERRDOS, ERRnoresource, NT_STATUS_INSUFF_SERVER_RESOURCES}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_BUFFER_SIZE}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_ADDRESS_COMPONENT}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_ADDRESS_WILDCARD}, { - ERRDOS, 68, NT_STATUS_TOO_MANY_ADDRESSES}, { - ERRDOS, 52, NT_STATUS_ADDRESS_ALREADY_EXISTS}, { - ERRDOS, 64, NT_STATUS_ADDRESS_CLOSED}, { - ERRDOS, 64, NT_STATUS_CONNECTION_DISCONNECTED}, { - ERRDOS, 64, NT_STATUS_CONNECTION_RESET}, { - ERRDOS, 68, NT_STATUS_TOO_MANY_NODES}, { - ERRDOS, 59, NT_STATUS_TRANSACTION_ABORTED}, { - ERRDOS, 59, NT_STATUS_TRANSACTION_TIMED_OUT}, { - ERRDOS, 59, NT_STATUS_TRANSACTION_NO_RELEASE}, { - ERRDOS, 59, NT_STATUS_TRANSACTION_NO_MATCH}, { - ERRDOS, 59, NT_STATUS_TRANSACTION_RESPONDED}, { - ERRDOS, 59, NT_STATUS_TRANSACTION_INVALID_ID}, { - ERRDOS, 59, NT_STATUS_TRANSACTION_INVALID_TYPE}, { - ERRDOS, ERRunsup, NT_STATUS_NOT_SERVER_SESSION}, { - ERRDOS, ERRunsup, NT_STATUS_NOT_CLIENT_SESSION}, { - ERRHRD, ERRgeneral, NT_STATUS_CANNOT_LOAD_REGISTRY_FILE}, { - ERRHRD, ERRgeneral, NT_STATUS_DEBUG_ATTACH_FAILED}, { - ERRHRD, ERRgeneral, NT_STATUS_SYSTEM_PROCESS_TERMINATED}, { - ERRHRD, ERRgeneral, NT_STATUS_DATA_NOT_ACCEPTED}, { - ERRHRD, ERRgeneral, NT_STATUS_NO_BROWSER_SERVERS_FOUND}, { - ERRHRD, ERRgeneral, NT_STATUS_VDM_HARD_ERROR}, { - ERRHRD, ERRgeneral, NT_STATUS_DRIVER_CANCEL_TIMEOUT}, { - ERRHRD, ERRgeneral, NT_STATUS_REPLY_MESSAGE_MISMATCH}, { - ERRHRD, ERRgeneral, NT_STATUS_MAPPED_ALIGNMENT}, { - ERRDOS, 193, NT_STATUS_IMAGE_CHECKSUM_MISMATCH}, { - ERRHRD, ERRgeneral, NT_STATUS_LOST_WRITEBEHIND_DATA}, { - ERRHRD, ERRgeneral, NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID}, { - ERRSRV, ERRpasswordExpired, NT_STATUS_PASSWORD_MUST_CHANGE}, { - ERRHRD, ERRgeneral, NT_STATUS_NOT_FOUND}, { - ERRHRD, ERRgeneral, NT_STATUS_NOT_TINY_STREAM}, { - ERRHRD, ERRgeneral, NT_STATUS_RECOVERY_FAILURE}, { - ERRHRD, ERRgeneral, NT_STATUS_STACK_OVERFLOW_READ}, { - ERRHRD, ERRgeneral, NT_STATUS_FAIL_CHECK}, { - ERRHRD, ERRgeneral, NT_STATUS_DUPLICATE_OBJECTID}, { - ERRHRD, ERRgeneral, NT_STATUS_OBJECTID_EXISTS}, { - ERRHRD, ERRgeneral, NT_STATUS_CONVERT_TO_LARGE}, { - ERRHRD, ERRgeneral, NT_STATUS_RETRY}, { - ERRHRD, ERRgeneral, NT_STATUS_FOUND_OUT_OF_SCOPE}, { - ERRHRD, ERRgeneral, NT_STATUS_ALLOCATE_BUCKET}, { - ERRHRD, ERRgeneral, NT_STATUS_PROPSET_NOT_FOUND}, { - ERRHRD, ERRgeneral, NT_STATUS_MARSHALL_OVERFLOW}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_VARIANT}, { - ERRHRD, ERRgeneral, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND}, { - ERRDOS, ERRnoaccess, NT_STATUS_ACCOUNT_LOCKED_OUT}, { - ERRDOS, ERRbadfid, NT_STATUS_HANDLE_NOT_CLOSABLE}, { - ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_REFUSED}, { - ERRHRD, ERRgeneral, NT_STATUS_GRACEFUL_DISCONNECT}, { - ERRHRD, ERRgeneral, NT_STATUS_ADDRESS_ALREADY_ASSOCIATED}, { - ERRHRD, ERRgeneral, NT_STATUS_ADDRESS_NOT_ASSOCIATED}, { - ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_INVALID}, { - ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_ACTIVE}, { - ERRHRD, ERRgeneral, NT_STATUS_NETWORK_UNREACHABLE}, { - ERRHRD, ERRgeneral, NT_STATUS_HOST_UNREACHABLE}, { - ERRHRD, ERRgeneral, NT_STATUS_PROTOCOL_UNREACHABLE}, { - ERRHRD, ERRgeneral, NT_STATUS_PORT_UNREACHABLE}, { - ERRHRD, ERRgeneral, NT_STATUS_REQUEST_ABORTED}, { - ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_ABORTED}, { - ERRHRD, ERRgeneral, NT_STATUS_BAD_COMPRESSION_BUFFER}, { - ERRHRD, ERRgeneral, NT_STATUS_USER_MAPPED_FILE}, { - ERRHRD, ERRgeneral, NT_STATUS_AUDIT_FAILED}, { - ERRHRD, ERRgeneral, NT_STATUS_TIMER_RESOLUTION_NOT_SET}, { - ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_COUNT_LIMIT}, { - ERRHRD, ERRgeneral, NT_STATUS_LOGIN_TIME_RESTRICTION}, { - ERRHRD, ERRgeneral, NT_STATUS_LOGIN_WKSTA_RESTRICTION}, { - ERRDOS, 193, NT_STATUS_IMAGE_MP_UP_MISMATCH}, { - ERRHRD, ERRgeneral, 0xc000024a}, { - ERRHRD, ERRgeneral, 0xc000024b}, { - ERRHRD, ERRgeneral, 0xc000024c}, { - ERRHRD, ERRgeneral, 0xc000024d}, { - ERRHRD, ERRgeneral, 0xc000024e}, { - ERRHRD, ERRgeneral, 0xc000024f}, { - ERRHRD, ERRgeneral, NT_STATUS_INSUFFICIENT_LOGON_INFO}, { - ERRHRD, ERRgeneral, NT_STATUS_BAD_DLL_ENTRYPOINT}, { - ERRHRD, ERRgeneral, NT_STATUS_BAD_SERVICE_ENTRYPOINT}, { - ERRHRD, ERRgeneral, NT_STATUS_LPC_REPLY_LOST}, { - ERRHRD, ERRgeneral, NT_STATUS_IP_ADDRESS_CONFLICT1}, { - ERRHRD, ERRgeneral, NT_STATUS_IP_ADDRESS_CONFLICT2}, { - ERRHRD, ERRgeneral, NT_STATUS_REGISTRY_QUOTA_LIMIT}, { - ERRSRV, 3, NT_STATUS_PATH_NOT_COVERED}, { - ERRHRD, ERRgeneral, NT_STATUS_NO_CALLBACK_ACTIVE}, { - ERRHRD, ERRgeneral, NT_STATUS_LICENSE_QUOTA_EXCEEDED}, { - ERRHRD, ERRgeneral, NT_STATUS_PWD_TOO_SHORT}, { - ERRHRD, ERRgeneral, NT_STATUS_PWD_TOO_RECENT}, { - ERRHRD, ERRgeneral, NT_STATUS_PWD_HISTORY_CONFLICT}, { - ERRHRD, ERRgeneral, 0xc000025d}, { - ERRHRD, ERRgeneral, NT_STATUS_PLUGPLAY_NO_DEVICE}, { - ERRHRD, ERRgeneral, NT_STATUS_UNSUPPORTED_COMPRESSION}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_HW_PROFILE}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH}, { - ERRDOS, 182, NT_STATUS_DRIVER_ORDINAL_NOT_FOUND}, { - ERRDOS, 127, NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND}, { - ERRDOS, 288, NT_STATUS_RESOURCE_NOT_OWNED}, { - ERRDOS, ErrTooManyLinks, NT_STATUS_TOO_MANY_LINKS}, { - ERRHRD, ERRgeneral, NT_STATUS_QUOTA_LIST_INCONSISTENT}, { - ERRHRD, ERRgeneral, NT_STATUS_FILE_IS_OFFLINE}, { - ERRDOS, 21, 0xc000026e}, { - ERRDOS, 161, 0xc0000281}, { - ERRDOS, ERRnoaccess, 0xc000028a}, { - ERRDOS, ERRnoaccess, 0xc000028b}, { - ERRHRD, ERRgeneral, 0xc000028c}, { - ERRDOS, ERRnoaccess, 0xc000028d}, { - ERRDOS, ERRnoaccess, 0xc000028e}, { - ERRDOS, ERRnoaccess, 0xc000028f}, { - ERRDOS, ERRnoaccess, 0xc0000290}, { - ERRDOS, ERRbadfunc, 0xc000029c}, { - ERRDOS, ERRsymlink, NT_STATUS_STOPPED_ON_SYMLINK}, { - ERRDOS, ERRinvlevel, 0x007c0001}, { - 0, 0, 0 } -}; - -/***************************************************************************** - Print an error message from the status code - *****************************************************************************/ -static void -cifs_print_status(__u32 status_code) -{ - int idx = 0; - - while (nt_errs[idx].nt_errstr != NULL) { - if (nt_errs[idx].nt_errcode == status_code) { - pr_notice("Status code returned 0x%08x %s\n", - status_code, nt_errs[idx].nt_errstr); - return; - } - idx++; - } - return; -} - - -static void -ntstatus_to_dos(__u32 ntstatus, __u8 *eclass, __u16 *ecode) -{ - int i; - if (ntstatus == 0) { - *eclass = 0; - *ecode = 0; - return; - } - for (i = 0; ntstatus_to_dos_map[i].ntstatus; i++) { - if (ntstatus == ntstatus_to_dos_map[i].ntstatus) { - *eclass = ntstatus_to_dos_map[i].dos_class; - *ecode = ntstatus_to_dos_map[i].dos_code; - return; - } - } - *eclass = ERRHRD; - *ecode = ERRgeneral; -} - -int -map_smb_to_linux_error(char *buf, bool logErr) -{ - struct smb_hdr *smb = (struct smb_hdr *)buf; - unsigned int i; - int rc = -EIO; /* if transport error smb error may not be set */ - __u8 smberrclass; - __u16 smberrcode; - - /* BB if NT Status codes - map NT BB */ - - /* old style smb error codes */ - if (smb->Status.CifsError == 0) - return 0; - - if (smb->Flags2 & SMBFLG2_ERR_STATUS) { - /* translate the newer STATUS codes to old style SMB errors - * and then to POSIX errors */ - __u32 err = le32_to_cpu(smb->Status.CifsError); - if (logErr && (err != (NT_STATUS_MORE_PROCESSING_REQUIRED))) - cifs_print_status(err); - else if (cifsFYI & CIFS_RC) - cifs_print_status(err); - ntstatus_to_dos(err, &smberrclass, &smberrcode); - } else { - smberrclass = smb->Status.DosError.ErrorClass; - smberrcode = le16_to_cpu(smb->Status.DosError.Error); - } - - /* old style errors */ - - /* DOS class smb error codes - map DOS */ - if (smberrclass == ERRDOS) { - /* 1 byte field no need to byte reverse */ - for (i = 0; - i < - sizeof(mapping_table_ERRDOS) / - sizeof(struct smb_to_posix_error); i++) { - if (mapping_table_ERRDOS[i].smb_err == 0) - break; - else if (mapping_table_ERRDOS[i].smb_err == - smberrcode) { - rc = mapping_table_ERRDOS[i].posix_code; - break; - } - /* else try next error mapping one to see if match */ - } - } else if (smberrclass == ERRSRV) { - /* server class of error codes */ - for (i = 0; - i < - sizeof(mapping_table_ERRSRV) / - sizeof(struct smb_to_posix_error); i++) { - if (mapping_table_ERRSRV[i].smb_err == 0) - break; - else if (mapping_table_ERRSRV[i].smb_err == - smberrcode) { - rc = mapping_table_ERRSRV[i].posix_code; - break; - } - /* else try next error mapping to see if match */ - } - } - /* else ERRHRD class errors or junk - return EIO */ - - /* special cases for NT status codes which cannot be translated to DOS codes */ - if (smb->Flags2 & SMBFLG2_ERR_STATUS) { - __u32 err = le32_to_cpu(smb->Status.CifsError); - if (err == (NT_STATUS_NOT_A_REPARSE_POINT)) - rc = -ENODATA; - else if (err == (NT_STATUS_PRIVILEGE_NOT_HELD)) - rc = -EPERM; - } - - cifs_dbg(FYI, "Mapping smb error code 0x%x to POSIX err %d\n", - le32_to_cpu(smb->Status.CifsError), rc); - - /* generic corrective action e.g. reconnect SMB session on - * ERRbaduid could be added */ - - if (rc == -EIO) - smb_EIO2(smb_eio_trace_smb1_received_error, - le32_to_cpu(smb->Status.CifsError), - le16_to_cpu(smb->Flags2)); - return rc; -} - -int -map_and_check_smb_error(struct TCP_Server_Info *server, - struct mid_q_entry *mid, bool logErr) -{ - int rc; - struct smb_hdr *smb = (struct smb_hdr *)mid->resp_buf; - - rc = map_smb_to_linux_error((char *)smb, logErr); - if (rc == -EACCES && !(smb->Flags2 & SMBFLG2_ERR_STATUS)) { - /* possible ERRBaduid */ - __u8 class = smb->Status.DosError.ErrorClass; - __u16 code = le16_to_cpu(smb->Status.DosError.Error); - - /* switch can be used to handle different errors */ - if (class == ERRSRV && code == ERRbaduid) { - cifs_dbg(FYI, "Server returned 0x%x, reconnecting session...\n", - code); - cifs_signal_cifsd_for_reconnect(server, false); - } - } - - return rc; -} - - -/* - * calculate the size of the SMB message based on the fixed header - * portion, the number of word parameters and the data portion of the message - */ -unsigned int -smbCalcSize(void *buf) -{ - struct smb_hdr *ptr = buf; - return (sizeof(struct smb_hdr) + (2 * ptr->WordCount) + - 2 /* size of the bcc field */ + get_bcc(ptr)); -} - /* The following are taken from fs/ntfs/util.c */ #define NTFS_TIME_OFFSET ((u64)(369*365 + 89) * 24 * 3600 * 10000000) diff --git a/fs/smb/client/nterr.c b/fs/smb/client/nterr.c index 77f84767b7df..ab5a2119efd0 100644 --- a/fs/smb/client/nterr.c +++ b/fs/smb/client/nterr.c @@ -346,6 +346,7 @@ const struct nt_err_code_struct nt_errs[] = { {"NT_STATUS_STACK_OVERFLOW", NT_STATUS_STACK_OVERFLOW}, {"NT_STATUS_NO_SUCH_PACKAGE", NT_STATUS_NO_SUCH_PACKAGE}, {"NT_STATUS_BAD_FUNCTION_TABLE", NT_STATUS_BAD_FUNCTION_TABLE}, + {"NT_STATUS_VARIABLE_NOT_FOUND", NT_STATUS_VARIABLE_NOT_FOUND}, {"NT_STATUS_DIRECTORY_NOT_EMPTY", NT_STATUS_DIRECTORY_NOT_EMPTY}, {"NT_STATUS_FILE_CORRUPT_ERROR", NT_STATUS_FILE_CORRUPT_ERROR}, {"NT_STATUS_NOT_A_DIRECTORY", NT_STATUS_NOT_A_DIRECTORY}, @@ -486,6 +487,8 @@ const struct nt_err_code_struct nt_errs[] = { {"NT_STATUS_DISK_RESET_FAILED", NT_STATUS_DISK_RESET_FAILED}, {"NT_STATUS_SHARED_IRQ_BUSY", NT_STATUS_SHARED_IRQ_BUSY}, {"NT_STATUS_FT_ORPHANING", NT_STATUS_FT_ORPHANING}, + {"NT_STATUS_BIOS_FAILED_TO_CONNECT_INTERRUPT", + NT_STATUS_BIOS_FAILED_TO_CONNECT_INTERRUPT}, {"NT_STATUS_PARTITION_FAILURE", NT_STATUS_PARTITION_FAILURE}, {"NT_STATUS_INVALID_BLOCK_LENGTH", NT_STATUS_INVALID_BLOCK_LENGTH}, {"NT_STATUS_DEVICE_NOT_PARTITIONED", @@ -676,7 +679,18 @@ const struct nt_err_code_struct nt_errs[] = { {"NT_STATUS_QUOTA_LIST_INCONSISTENT", NT_STATUS_QUOTA_LIST_INCONSISTENT}, {"NT_STATUS_FILE_IS_OFFLINE", NT_STATUS_FILE_IS_OFFLINE}, + {"NT_STATUS_VOLUME_DISMOUNTED", NT_STATUS_VOLUME_DISMOUNTED}, {"NT_STATUS_NOT_A_REPARSE_POINT", NT_STATUS_NOT_A_REPARSE_POINT}, + {"NT_STATUS_DIRECTORY_IS_A_REPARSE_POINT", + NT_STATUS_DIRECTORY_IS_A_REPARSE_POINT}, + {"NT_STATUS_ENCRYPTION_FAILED", NT_STATUS_ENCRYPTION_FAILED}, + {"NT_STATUS_DECRYPTION_FAILED", NT_STATUS_DECRYPTION_FAILED}, + {"NT_STATUS_RANGE_NOT_FOUND", NT_STATUS_RANGE_NOT_FOUND}, + {"NT_STATUS_NO_RECOVERY_POLICY", NT_STATUS_NO_RECOVERY_POLICY}, + {"NT_STATUS_NO_EFS", NT_STATUS_NO_EFS}, + {"NT_STATUS_WRONG_EFS", NT_STATUS_WRONG_EFS}, + {"NT_STATUS_NO_USER_KEYS", NT_STATUS_NO_USER_KEYS}, + {"NT_STATUS_VOLUME_NOT_UPGRADED", NT_STATUS_VOLUME_NOT_UPGRADED}, {"NT_STATUS_NETWORK_SESSION_EXPIRED", NT_STATUS_NETWORK_SESSION_EXPIRED}, {"NT_STATUS_NO_MORE_ENTRIES", NT_STATUS_NO_MORE_ENTRIES}, {"NT_STATUS_MORE_ENTRIES", NT_STATUS_MORE_ENTRIES}, @@ -684,5 +698,6 @@ const struct nt_err_code_struct nt_errs[] = { {"NT_STATUS_NO_SUCH_JOB", NT_STATUS_NO_SUCH_JOB}, {"NT_STATUS_NO_PREAUTH_INTEGRITY_HASH_OVERLAP", NT_STATUS_NO_PREAUTH_INTEGRITY_HASH_OVERLAP}, + {"NT_STATUS_OS2_INVALID_LEVEL", NT_STATUS_OS2_INVALID_LEVEL}, {NULL, 0} }; diff --git a/fs/smb/client/nterr.h b/fs/smb/client/nterr.h index 81f1a78cf41f..b04cd0d786b1 100644 --- a/fs/smb/client/nterr.h +++ b/fs/smb/client/nterr.h @@ -301,6 +301,7 @@ extern const struct nt_err_code_struct nt_errs[]; #define NT_STATUS_STACK_OVERFLOW (0xC0000000 | 0x00fd) #define NT_STATUS_NO_SUCH_PACKAGE (0xC0000000 | 0x00fe) #define NT_STATUS_BAD_FUNCTION_TABLE (0xC0000000 | 0x00ff) +#define NT_STATUS_VARIABLE_NOT_FOUND (0xC0000000 | 0x0100) #define NT_STATUS_DIRECTORY_NOT_EMPTY (0xC0000000 | 0x0101) #define NT_STATUS_FILE_CORRUPT_ERROR (0xC0000000 | 0x0102) #define NT_STATUS_NOT_A_DIRECTORY (0xC0000000 | 0x0103) @@ -410,6 +411,7 @@ extern const struct nt_err_code_struct nt_errs[]; #define NT_STATUS_DISK_RESET_FAILED (0xC0000000 | 0x016b) #define NT_STATUS_SHARED_IRQ_BUSY (0xC0000000 | 0x016c) #define NT_STATUS_FT_ORPHANING (0xC0000000 | 0x016d) +#define NT_STATUS_BIOS_FAILED_TO_CONNECT_INTERRUPT (0xC0000000 | 0x016e) #define NT_STATUS_PARTITION_FAILURE (0xC0000000 | 0x0172) #define NT_STATUS_INVALID_BLOCK_LENGTH (0xC0000000 | 0x0173) #define NT_STATUS_DEVICE_NOT_PARTITIONED (0xC0000000 | 0x0174) @@ -548,9 +550,20 @@ extern const struct nt_err_code_struct nt_errs[]; #define NT_STATUS_TOO_MANY_LINKS (0xC0000000 | 0x0265) #define NT_STATUS_QUOTA_LIST_INCONSISTENT (0xC0000000 | 0x0266) #define NT_STATUS_FILE_IS_OFFLINE (0xC0000000 | 0x0267) +#define NT_STATUS_VOLUME_DISMOUNTED (0xC0000000 | 0x026e) #define NT_STATUS_NOT_A_REPARSE_POINT (0xC0000000 | 0x0275) +#define NT_STATUS_DIRECTORY_IS_A_REPARSE_POINT (0xC0000000 | 0x0281) +#define NT_STATUS_ENCRYPTION_FAILED (0xC0000000 | 0x028a) +#define NT_STATUS_DECRYPTION_FAILED (0xC0000000 | 0x028b) +#define NT_STATUS_RANGE_NOT_FOUND (0xC0000000 | 0x028c) +#define NT_STATUS_NO_RECOVERY_POLICY (0xC0000000 | 0x028d) +#define NT_STATUS_NO_EFS (0xC0000000 | 0x028e) +#define NT_STATUS_WRONG_EFS (0xC0000000 | 0x028f) +#define NT_STATUS_NO_USER_KEYS (0xC0000000 | 0x0290) +#define NT_STATUS_VOLUME_NOT_UPGRADED (0xC0000000 | 0x029c) #define NT_STATUS_NETWORK_SESSION_EXPIRED (0xC0000000 | 0x035c) #define NT_STATUS_NO_SUCH_JOB (0xC0000000 | 0xEDE) /* scheduler */ #define NT_STATUS_NO_PREAUTH_INTEGRITY_HASH_OVERLAP (0xC0000000 | 0x5D0000) +#define NT_STATUS_OS2_INVALID_LEVEL 0x007c0001 #endif /* _NTERR_H */ diff --git a/fs/smb/client/ntlmssp.h b/fs/smb/client/ntlmssp.h index a11fddc321f6..be0365f08396 100644 --- a/fs/smb/client/ntlmssp.h +++ b/fs/smb/client/ntlmssp.h @@ -142,16 +142,17 @@ typedef struct _AUTHENTICATE_MESSAGE { * Size of the session key (crypto key encrypted with the password */ -int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len, struct cifs_ses *ses); +int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len, + struct cifs_ses *ses); int build_ntlmssp_negotiate_blob(unsigned char **pbuffer, u16 *buflen, struct cifs_ses *ses, struct TCP_Server_Info *server, const struct nls_table *nls_cp); int build_ntlmssp_smb3_negotiate_blob(unsigned char **pbuffer, u16 *buflen, - struct cifs_ses *ses, - struct TCP_Server_Info *server, - const struct nls_table *nls_cp); + struct cifs_ses *ses, + struct TCP_Server_Info *server, + const struct nls_table *nls_cp); int build_ntlmssp_auth_blob(unsigned char **pbuffer, u16 *buflen, - struct cifs_ses *ses, - struct TCP_Server_Info *server, - const struct nls_table *nls_cp); + struct cifs_ses *ses, + struct TCP_Server_Info *server, + const struct nls_table *nls_cp); diff --git a/fs/smb/client/readdir.c b/fs/smb/client/readdir.c index 6844f1dc3921..67a8555efa1e 100644 --- a/fs/smb/client/readdir.c +++ b/fs/smb/client/readdir.c @@ -13,7 +13,6 @@ #include <linux/pagemap.h> #include <linux/slab.h> #include <linux/stat.h> -#include "cifspdu.h" #include "cifsglob.h" #include "cifsproto.h" #include "cifs_unicode.h" diff --git a/fs/smb/client/reparse.h b/fs/smb/client/reparse.h index 19caab2fd11e..570b0d25aeba 100644 --- a/fs/smb/client/reparse.h +++ b/fs/smb/client/reparse.h @@ -11,6 +11,7 @@ #include <linux/uidgid.h> #include "fs_context.h" #include "cifsglob.h" +#include "../common/smbfsctl.h" #define REPARSE_SYM_PATH_MAX 4060 @@ -130,11 +131,12 @@ bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb, struct cifs_fattr *fattr, struct cifs_open_info_data *data); int create_reparse_symlink(const unsigned int xid, struct inode *inode, - struct dentry *dentry, struct cifs_tcon *tcon, - const char *full_path, const char *symname); -int mknod_reparse(unsigned int xid, struct inode *inode, - struct dentry *dentry, struct cifs_tcon *tcon, - const char *full_path, umode_t mode, dev_t dev); -struct reparse_data_buffer *smb2_get_reparse_point_buffer(const struct kvec *rsp_iov, u32 *len); + struct dentry *dentry, struct cifs_tcon *tcon, + const char *full_path, const char *symname); +int mknod_reparse(unsigned int xid, struct inode *inode, struct dentry *dentry, + struct cifs_tcon *tcon, const char *full_path, umode_t mode, + dev_t dev); +struct reparse_data_buffer *smb2_get_reparse_point_buffer(const struct kvec *rsp_iov, + u32 *plen); #endif /* _CIFS_REPARSE_H */ diff --git a/fs/smb/client/sess.c b/fs/smb/client/sess.c index a72d6a6d20f0..d523540565ef 100644 --- a/fs/smb/client/sess.c +++ b/fs/smb/client/sess.c @@ -8,7 +8,6 @@ * */ -#include "cifspdu.h" #include "cifsglob.h" #include "cifsproto.h" #include "cifs_unicode.h" @@ -639,279 +638,6 @@ out_free_xid: return rc; } -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY -static __u32 cifs_ssetup_hdr(struct cifs_ses *ses, - struct TCP_Server_Info *server, - SESSION_SETUP_ANDX *pSMB) -{ - __u32 capabilities = 0; - - /* init fields common to all four types of SessSetup */ - /* Note that offsets for first seven fields in req struct are same */ - /* in CIFS Specs so does not matter which of 3 forms of struct */ - /* that we use in next few lines */ - /* Note that header is initialized to zero in header_assemble */ - pSMB->req.AndXCommand = 0xFF; - pSMB->req.MaxBufferSize = cpu_to_le16(min_t(u32, - CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4, - USHRT_MAX)); - pSMB->req.MaxMpxCount = cpu_to_le16(server->maxReq); - pSMB->req.VcNumber = cpu_to_le16(1); - pSMB->req.SessionKey = server->session_key_id; - - /* Now no need to set SMBFLG_CASELESS or obsolete CANONICAL PATH */ - - /* BB verify whether signing required on neg or just auth frame (and NTLM case) */ - - capabilities = CAP_LARGE_FILES | CAP_NT_SMBS | CAP_LEVEL_II_OPLOCKS | - CAP_LARGE_WRITE_X | CAP_LARGE_READ_X; - - if (server->sign) - pSMB->req.hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE; - - if (ses->capabilities & CAP_UNICODE) { - pSMB->req.hdr.Flags2 |= SMBFLG2_UNICODE; - capabilities |= CAP_UNICODE; - } - if (ses->capabilities & CAP_STATUS32) { - pSMB->req.hdr.Flags2 |= SMBFLG2_ERR_STATUS; - capabilities |= CAP_STATUS32; - } - if (ses->capabilities & CAP_DFS) { - pSMB->req.hdr.Flags2 |= SMBFLG2_DFS; - capabilities |= CAP_DFS; - } - if (ses->capabilities & CAP_UNIX) - capabilities |= CAP_UNIX; - - return capabilities; -} - -static void -unicode_oslm_strings(char **pbcc_area, const struct nls_table *nls_cp) -{ - char *bcc_ptr = *pbcc_area; - int bytes_ret = 0; - - /* Copy OS version */ - bytes_ret = cifs_strtoUTF16((__le16 *)bcc_ptr, "Linux version ", 32, - nls_cp); - bcc_ptr += 2 * bytes_ret; - bytes_ret = cifs_strtoUTF16((__le16 *) bcc_ptr, init_utsname()->release, - 32, nls_cp); - bcc_ptr += 2 * bytes_ret; - bcc_ptr += 2; /* trailing null */ - - bytes_ret = cifs_strtoUTF16((__le16 *) bcc_ptr, CIFS_NETWORK_OPSYS, - 32, nls_cp); - bcc_ptr += 2 * bytes_ret; - bcc_ptr += 2; /* trailing null */ - - *pbcc_area = bcc_ptr; -} - -static void -ascii_oslm_strings(char **pbcc_area, const struct nls_table *nls_cp) -{ - char *bcc_ptr = *pbcc_area; - - strcpy(bcc_ptr, "Linux version "); - bcc_ptr += strlen("Linux version "); - strcpy(bcc_ptr, init_utsname()->release); - bcc_ptr += strlen(init_utsname()->release) + 1; - - strcpy(bcc_ptr, CIFS_NETWORK_OPSYS); - bcc_ptr += strlen(CIFS_NETWORK_OPSYS) + 1; - - *pbcc_area = bcc_ptr; -} - -static void unicode_domain_string(char **pbcc_area, struct cifs_ses *ses, - const struct nls_table *nls_cp) -{ - char *bcc_ptr = *pbcc_area; - int bytes_ret = 0; - - /* copy domain */ - if (ses->domainName == NULL) { - /* - * Sending null domain better than using a bogus domain name (as - * we did briefly in 2.6.18) since server will use its default - */ - *bcc_ptr = 0; - *(bcc_ptr+1) = 0; - bytes_ret = 0; - } else - bytes_ret = cifs_strtoUTF16((__le16 *) bcc_ptr, ses->domainName, - CIFS_MAX_DOMAINNAME_LEN, nls_cp); - bcc_ptr += 2 * bytes_ret; - bcc_ptr += 2; /* account for null terminator */ - - *pbcc_area = bcc_ptr; -} - -static void ascii_domain_string(char **pbcc_area, struct cifs_ses *ses, - const struct nls_table *nls_cp) -{ - char *bcc_ptr = *pbcc_area; - int len; - - /* copy domain */ - if (ses->domainName != NULL) { - len = strscpy(bcc_ptr, ses->domainName, CIFS_MAX_DOMAINNAME_LEN); - if (WARN_ON_ONCE(len < 0)) - len = CIFS_MAX_DOMAINNAME_LEN - 1; - bcc_ptr += len; - } /* else we send a null domain name so server will default to its own domain */ - *bcc_ptr = 0; - bcc_ptr++; - - *pbcc_area = bcc_ptr; -} - -static void unicode_ssetup_strings(char **pbcc_area, struct cifs_ses *ses, - const struct nls_table *nls_cp) -{ - char *bcc_ptr = *pbcc_area; - int bytes_ret = 0; - - /* BB FIXME add check that strings less than 335 or will need to send as arrays */ - - /* copy user */ - if (ses->user_name == NULL) { - /* null user mount */ - *bcc_ptr = 0; - *(bcc_ptr+1) = 0; - } else { - bytes_ret = cifs_strtoUTF16((__le16 *) bcc_ptr, ses->user_name, - CIFS_MAX_USERNAME_LEN, nls_cp); - } - bcc_ptr += 2 * bytes_ret; - bcc_ptr += 2; /* account for null termination */ - - unicode_domain_string(&bcc_ptr, ses, nls_cp); - unicode_oslm_strings(&bcc_ptr, nls_cp); - - *pbcc_area = bcc_ptr; -} - -static void ascii_ssetup_strings(char **pbcc_area, struct cifs_ses *ses, - const struct nls_table *nls_cp) -{ - char *bcc_ptr = *pbcc_area; - int len; - - /* copy user */ - /* BB what about null user mounts - check that we do this BB */ - /* copy user */ - if (ses->user_name != NULL) { - len = strscpy(bcc_ptr, ses->user_name, CIFS_MAX_USERNAME_LEN); - if (WARN_ON_ONCE(len < 0)) - len = CIFS_MAX_USERNAME_LEN - 1; - bcc_ptr += len; - } - /* else null user mount */ - *bcc_ptr = 0; - bcc_ptr++; /* account for null termination */ - - /* BB check for overflow here */ - - ascii_domain_string(&bcc_ptr, ses, nls_cp); - ascii_oslm_strings(&bcc_ptr, nls_cp); - - *pbcc_area = bcc_ptr; -} - -static void -decode_unicode_ssetup(char **pbcc_area, int bleft, struct cifs_ses *ses, - const struct nls_table *nls_cp) -{ - int len; - char *data = *pbcc_area; - - cifs_dbg(FYI, "bleft %d\n", bleft); - - kfree(ses->serverOS); - ses->serverOS = cifs_strndup_from_utf16(data, bleft, true, nls_cp); - cifs_dbg(FYI, "serverOS=%s\n", ses->serverOS); - len = (UniStrnlen((wchar_t *) data, bleft / 2) * 2) + 2; - data += len; - bleft -= len; - if (bleft <= 0) - return; - - kfree(ses->serverNOS); - ses->serverNOS = cifs_strndup_from_utf16(data, bleft, true, nls_cp); - cifs_dbg(FYI, "serverNOS=%s\n", ses->serverNOS); - len = (UniStrnlen((wchar_t *) data, bleft / 2) * 2) + 2; - data += len; - bleft -= len; - if (bleft <= 0) - return; - - kfree(ses->serverDomain); - ses->serverDomain = cifs_strndup_from_utf16(data, bleft, true, nls_cp); - cifs_dbg(FYI, "serverDomain=%s\n", ses->serverDomain); - - return; -} - -static void decode_ascii_ssetup(char **pbcc_area, __u16 bleft, - struct cifs_ses *ses, - const struct nls_table *nls_cp) -{ - int len; - char *bcc_ptr = *pbcc_area; - - cifs_dbg(FYI, "decode sessetup ascii. bleft %d\n", bleft); - - len = strnlen(bcc_ptr, bleft); - if (len >= bleft) - return; - - kfree(ses->serverOS); - - ses->serverOS = kmalloc(len + 1, GFP_KERNEL); - if (ses->serverOS) { - memcpy(ses->serverOS, bcc_ptr, len); - ses->serverOS[len] = 0; - if (strncmp(ses->serverOS, "OS/2", 4) == 0) - cifs_dbg(FYI, "OS/2 server\n"); - } - - bcc_ptr += len + 1; - bleft -= len + 1; - - len = strnlen(bcc_ptr, bleft); - if (len >= bleft) - return; - - kfree(ses->serverNOS); - - ses->serverNOS = kmalloc(len + 1, GFP_KERNEL); - if (ses->serverNOS) { - memcpy(ses->serverNOS, bcc_ptr, len); - ses->serverNOS[len] = 0; - } - - bcc_ptr += len + 1; - bleft -= len + 1; - - len = strnlen(bcc_ptr, bleft); - if (len > bleft) - return; - - /* - * No domain field in LANMAN case. Domain is - * returned by old servers in the SMB negprot response - * - * BB For newer servers which do not support Unicode, - * but thus do return domain here, we could add parsing - * for it later, but it is not very important - */ - cifs_dbg(FYI, "ascii: bytes left %d\n", bleft); -} -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len, struct cifs_ses *ses) @@ -1322,711 +1048,3 @@ cifs_select_sectype(struct TCP_Server_Info *server, enum securityEnum requested) return Unspecified; } } - -struct sess_data { - unsigned int xid; - struct cifs_ses *ses; - struct TCP_Server_Info *server; - struct nls_table *nls_cp; - void (*func)(struct sess_data *); - int result; - unsigned int in_len; - - /* we will send the SMB in three pieces: - * a fixed length beginning part, an optional - * SPNEGO blob (which can be zero length), and a - * last part which will include the strings - * and rest of bcc area. This allows us to avoid - * a large buffer 17K allocation - */ - int buf0_type; - struct kvec iov[3]; -}; - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY -static int -sess_alloc_buffer(struct sess_data *sess_data, int wct) -{ - int rc; - struct cifs_ses *ses = sess_data->ses; - struct smb_hdr *smb_buf; - - rc = small_smb_init_no_tc(SMB_COM_SESSION_SETUP_ANDX, wct, ses, - (void **)&smb_buf); - - if (rc < 0) - return rc; - - sess_data->in_len = rc; - sess_data->iov[0].iov_base = (char *)smb_buf; - sess_data->iov[0].iov_len = sess_data->in_len; - /* - * This variable will be used to clear the buffer - * allocated above in case of any error in the calling function. - */ - sess_data->buf0_type = CIFS_SMALL_BUFFER; - - /* 2000 big enough to fit max user, domain, NOS name etc. */ - sess_data->iov[2].iov_base = kmalloc(2000, GFP_KERNEL); - if (!sess_data->iov[2].iov_base) { - rc = -ENOMEM; - goto out_free_smb_buf; - } - - return 0; - -out_free_smb_buf: - cifs_small_buf_release(smb_buf); - sess_data->iov[0].iov_base = NULL; - sess_data->iov[0].iov_len = 0; - sess_data->buf0_type = CIFS_NO_BUFFER; - return rc; -} - -static void -sess_free_buffer(struct sess_data *sess_data) -{ - struct kvec *iov = sess_data->iov; - - /* - * Zero the session data before freeing, as it might contain sensitive info (keys, etc). - * Note that iov[1] is already freed by caller. - */ - if (sess_data->buf0_type != CIFS_NO_BUFFER && iov[0].iov_base) - memzero_explicit(iov[0].iov_base, iov[0].iov_len); - - free_rsp_buf(sess_data->buf0_type, iov[0].iov_base); - sess_data->buf0_type = CIFS_NO_BUFFER; - kfree_sensitive(iov[2].iov_base); -} - -static int -sess_establish_session(struct sess_data *sess_data) -{ - struct cifs_ses *ses = sess_data->ses; - struct TCP_Server_Info *server = sess_data->server; - - cifs_server_lock(server); - if (!server->session_estab) { - if (server->sign) { - server->session_key.response = - kmemdup(ses->auth_key.response, - ses->auth_key.len, GFP_KERNEL); - if (!server->session_key.response) { - cifs_server_unlock(server); - return -ENOMEM; - } - server->session_key.len = - ses->auth_key.len; - } - server->sequence_number = 0x2; - server->session_estab = true; - } - cifs_server_unlock(server); - - cifs_dbg(FYI, "CIFS session established successfully\n"); - return 0; -} - -static int -sess_sendreceive(struct sess_data *sess_data) -{ - int rc; - struct smb_hdr *smb_buf = (struct smb_hdr *) sess_data->iov[0].iov_base; - __u16 count; - struct kvec rsp_iov = { NULL, 0 }; - - count = sess_data->iov[1].iov_len + sess_data->iov[2].iov_len; - sess_data->in_len += count; - put_bcc(count, smb_buf); - - rc = SendReceive2(sess_data->xid, sess_data->ses, - sess_data->iov, 3 /* num_iovecs */, - &sess_data->buf0_type, - CIFS_LOG_ERROR, &rsp_iov); - cifs_small_buf_release(sess_data->iov[0].iov_base); - memcpy(&sess_data->iov[0], &rsp_iov, sizeof(struct kvec)); - - return rc; -} - -static void -sess_auth_ntlmv2(struct sess_data *sess_data) -{ - int rc = 0; - struct smb_hdr *smb_buf; - SESSION_SETUP_ANDX *pSMB; - char *bcc_ptr; - struct cifs_ses *ses = sess_data->ses; - struct TCP_Server_Info *server = sess_data->server; - __u32 capabilities; - __u16 bytes_remaining; - - /* old style NTLM sessionsetup */ - /* wct = 13 */ - rc = sess_alloc_buffer(sess_data, 13); - if (rc) - goto out; - - pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base; - bcc_ptr = sess_data->iov[2].iov_base; - capabilities = cifs_ssetup_hdr(ses, server, pSMB); - - pSMB->req_no_secext.Capabilities = cpu_to_le32(capabilities); - - /* LM2 password would be here if we supported it */ - pSMB->req_no_secext.CaseInsensitivePasswordLength = 0; - - if (ses->user_name != NULL) { - /* calculate nlmv2 response and session key */ - rc = setup_ntlmv2_rsp(ses, sess_data->nls_cp); - if (rc) { - cifs_dbg(VFS, "Error %d during NTLMv2 authentication\n", rc); - goto out; - } - - memcpy(bcc_ptr, ses->auth_key.response + CIFS_SESS_KEY_SIZE, - ses->auth_key.len - CIFS_SESS_KEY_SIZE); - bcc_ptr += ses->auth_key.len - CIFS_SESS_KEY_SIZE; - - /* set case sensitive password length after tilen may get - * assigned, tilen is 0 otherwise. - */ - pSMB->req_no_secext.CaseSensitivePasswordLength = - cpu_to_le16(ses->auth_key.len - CIFS_SESS_KEY_SIZE); - } else { - pSMB->req_no_secext.CaseSensitivePasswordLength = 0; - } - - if (ses->capabilities & CAP_UNICODE) { - if (!IS_ALIGNED(sess_data->iov[0].iov_len, 2)) { - *bcc_ptr = 0; - bcc_ptr++; - } - unicode_ssetup_strings(&bcc_ptr, ses, sess_data->nls_cp); - } else { - ascii_ssetup_strings(&bcc_ptr, ses, sess_data->nls_cp); - } - - - sess_data->iov[2].iov_len = (long) bcc_ptr - - (long) sess_data->iov[2].iov_base; - - rc = sess_sendreceive(sess_data); - if (rc) - goto out; - - pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base; - smb_buf = (struct smb_hdr *)sess_data->iov[0].iov_base; - - if (smb_buf->WordCount != 3) { - rc = smb_EIO1(smb_eio_trace_sess_nl2_wcc, smb_buf->WordCount); - cifs_dbg(VFS, "bad word count %d\n", smb_buf->WordCount); - goto out; - } - - if (le16_to_cpu(pSMB->resp.Action) & GUEST_LOGIN) - cifs_dbg(FYI, "Guest login\n"); /* BB mark SesInfo struct? */ - - ses->Suid = smb_buf->Uid; /* UID left in wire format (le) */ - cifs_dbg(FYI, "UID = %llu\n", ses->Suid); - - bytes_remaining = get_bcc(smb_buf); - bcc_ptr = pByteArea(smb_buf); - - /* BB check if Unicode and decode strings */ - if (bytes_remaining == 0) { - /* no string area to decode, do nothing */ - } else if (smb_buf->Flags2 & SMBFLG2_UNICODE) { - /* unicode string area must be word-aligned */ - if (!IS_ALIGNED((unsigned long)bcc_ptr - (unsigned long)smb_buf, 2)) { - ++bcc_ptr; - --bytes_remaining; - } - decode_unicode_ssetup(&bcc_ptr, bytes_remaining, ses, - sess_data->nls_cp); - } else { - decode_ascii_ssetup(&bcc_ptr, bytes_remaining, ses, - sess_data->nls_cp); - } - - rc = sess_establish_session(sess_data); -out: - sess_data->result = rc; - sess_data->func = NULL; - sess_free_buffer(sess_data); - kfree_sensitive(ses->auth_key.response); - ses->auth_key.response = NULL; -} - -#ifdef CONFIG_CIFS_UPCALL -static void -sess_auth_kerberos(struct sess_data *sess_data) -{ - int rc = 0; - struct smb_hdr *smb_buf; - SESSION_SETUP_ANDX *pSMB; - char *bcc_ptr; - struct cifs_ses *ses = sess_data->ses; - struct TCP_Server_Info *server = sess_data->server; - __u32 capabilities; - __u16 bytes_remaining; - struct key *spnego_key = NULL; - struct cifs_spnego_msg *msg; - u16 blob_len; - - /* extended security */ - /* wct = 12 */ - rc = sess_alloc_buffer(sess_data, 12); - if (rc) - goto out; - - pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base; - bcc_ptr = sess_data->iov[2].iov_base; - capabilities = cifs_ssetup_hdr(ses, server, pSMB); - - spnego_key = cifs_get_spnego_key(ses, server); - if (IS_ERR(spnego_key)) { - rc = PTR_ERR(spnego_key); - spnego_key = NULL; - goto out; - } - - msg = spnego_key->payload.data[0]; - /* - * check version field to make sure that cifs.upcall is - * sending us a response in an expected form - */ - if (msg->version != CIFS_SPNEGO_UPCALL_VERSION) { - cifs_dbg(VFS, "incorrect version of cifs.upcall (expected %d but got %d)\n", - CIFS_SPNEGO_UPCALL_VERSION, msg->version); - rc = -EKEYREJECTED; - goto out_put_spnego_key; - } - - kfree_sensitive(ses->auth_key.response); - ses->auth_key.response = kmemdup(msg->data, msg->sesskey_len, - GFP_KERNEL); - if (!ses->auth_key.response) { - cifs_dbg(VFS, "Kerberos can't allocate (%u bytes) memory\n", - msg->sesskey_len); - rc = -ENOMEM; - goto out_put_spnego_key; - } - ses->auth_key.len = msg->sesskey_len; - - pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC; - capabilities |= CAP_EXTENDED_SECURITY; - pSMB->req.Capabilities = cpu_to_le32(capabilities); - sess_data->iov[1].iov_base = msg->data + msg->sesskey_len; - sess_data->iov[1].iov_len = msg->secblob_len; - pSMB->req.SecurityBlobLength = cpu_to_le16(sess_data->iov[1].iov_len); - - if (pSMB->req.hdr.Flags2 & SMBFLG2_UNICODE) { - /* unicode strings must be word aligned */ - if (!IS_ALIGNED(sess_data->iov[0].iov_len + sess_data->iov[1].iov_len, 2)) { - *bcc_ptr = 0; - bcc_ptr++; - } - unicode_oslm_strings(&bcc_ptr, sess_data->nls_cp); - unicode_domain_string(&bcc_ptr, ses, sess_data->nls_cp); - } else { - ascii_oslm_strings(&bcc_ptr, sess_data->nls_cp); - ascii_domain_string(&bcc_ptr, ses, sess_data->nls_cp); - } - - sess_data->iov[2].iov_len = (long) bcc_ptr - - (long) sess_data->iov[2].iov_base; - - rc = sess_sendreceive(sess_data); - if (rc) - goto out_put_spnego_key; - - pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base; - smb_buf = (struct smb_hdr *)sess_data->iov[0].iov_base; - - if (smb_buf->WordCount != 4) { - rc = smb_EIO1(smb_eio_trace_sess_krb_wcc, smb_buf->WordCount); - cifs_dbg(VFS, "bad word count %d\n", smb_buf->WordCount); - goto out_put_spnego_key; - } - - if (le16_to_cpu(pSMB->resp.Action) & GUEST_LOGIN) - cifs_dbg(FYI, "Guest login\n"); /* BB mark SesInfo struct? */ - - ses->Suid = smb_buf->Uid; /* UID left in wire format (le) */ - cifs_dbg(FYI, "UID = %llu\n", ses->Suid); - - bytes_remaining = get_bcc(smb_buf); - bcc_ptr = pByteArea(smb_buf); - - blob_len = le16_to_cpu(pSMB->resp.SecurityBlobLength); - if (blob_len > bytes_remaining) { - cifs_dbg(VFS, "bad security blob length %d\n", - blob_len); - rc = -EINVAL; - goto out_put_spnego_key; - } - bcc_ptr += blob_len; - bytes_remaining -= blob_len; - - /* BB check if Unicode and decode strings */ - if (bytes_remaining == 0) { - /* no string area to decode, do nothing */ - } else if (smb_buf->Flags2 & SMBFLG2_UNICODE) { - /* unicode string area must be word-aligned */ - if (!IS_ALIGNED((unsigned long)bcc_ptr - (unsigned long)smb_buf, 2)) { - ++bcc_ptr; - --bytes_remaining; - } - decode_unicode_ssetup(&bcc_ptr, bytes_remaining, ses, - sess_data->nls_cp); - } else { - decode_ascii_ssetup(&bcc_ptr, bytes_remaining, ses, - sess_data->nls_cp); - } - - rc = sess_establish_session(sess_data); -out_put_spnego_key: - key_invalidate(spnego_key); - key_put(spnego_key); -out: - sess_data->result = rc; - sess_data->func = NULL; - sess_free_buffer(sess_data); - kfree_sensitive(ses->auth_key.response); - ses->auth_key.response = NULL; -} - -#endif /* ! CONFIG_CIFS_UPCALL */ - -/* - * The required kvec buffers have to be allocated before calling this - * function. - */ -static int -_sess_auth_rawntlmssp_assemble_req(struct sess_data *sess_data) -{ - SESSION_SETUP_ANDX *pSMB; - struct cifs_ses *ses = sess_data->ses; - struct TCP_Server_Info *server = sess_data->server; - __u32 capabilities; - char *bcc_ptr; - - pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base; - - capabilities = cifs_ssetup_hdr(ses, server, pSMB); - pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC; - capabilities |= CAP_EXTENDED_SECURITY; - pSMB->req.Capabilities |= cpu_to_le32(capabilities); - - bcc_ptr = sess_data->iov[2].iov_base; - - if (pSMB->req.hdr.Flags2 & SMBFLG2_UNICODE) { - /* unicode strings must be word aligned */ - if (!IS_ALIGNED(sess_data->iov[0].iov_len + sess_data->iov[1].iov_len, 2)) { - *bcc_ptr = 0; - bcc_ptr++; - } - unicode_oslm_strings(&bcc_ptr, sess_data->nls_cp); - } else { - ascii_oslm_strings(&bcc_ptr, sess_data->nls_cp); - } - - sess_data->iov[2].iov_len = (long) bcc_ptr - - (long) sess_data->iov[2].iov_base; - - return 0; -} - -static void -sess_auth_rawntlmssp_authenticate(struct sess_data *sess_data); - -static void -sess_auth_rawntlmssp_negotiate(struct sess_data *sess_data) -{ - int rc; - struct smb_hdr *smb_buf; - SESSION_SETUP_ANDX *pSMB; - struct cifs_ses *ses = sess_data->ses; - struct TCP_Server_Info *server = sess_data->server; - __u16 bytes_remaining; - char *bcc_ptr; - unsigned char *ntlmsspblob = NULL; - u16 blob_len; - - cifs_dbg(FYI, "rawntlmssp session setup negotiate phase\n"); - - /* - * if memory allocation is successful, caller of this function - * frees it. - */ - ses->ntlmssp = kmalloc(sizeof(struct ntlmssp_auth), GFP_KERNEL); - if (!ses->ntlmssp) { - rc = -ENOMEM; - goto out; - } - ses->ntlmssp->sesskey_per_smbsess = false; - - /* wct = 12 */ - rc = sess_alloc_buffer(sess_data, 12); - if (rc) - goto out; - - pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base; - - /* Build security blob before we assemble the request */ - rc = build_ntlmssp_negotiate_blob(&ntlmsspblob, - &blob_len, ses, server, - sess_data->nls_cp); - if (rc) - goto out_free_ntlmsspblob; - - sess_data->iov[1].iov_len = blob_len; - sess_data->iov[1].iov_base = ntlmsspblob; - pSMB->req.SecurityBlobLength = cpu_to_le16(blob_len); - - rc = _sess_auth_rawntlmssp_assemble_req(sess_data); - if (rc) - goto out_free_ntlmsspblob; - - rc = sess_sendreceive(sess_data); - - pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base; - smb_buf = (struct smb_hdr *)sess_data->iov[0].iov_base; - - /* If true, rc here is expected and not an error */ - if (sess_data->buf0_type != CIFS_NO_BUFFER && - smb_buf->Status.CifsError == - cpu_to_le32(NT_STATUS_MORE_PROCESSING_REQUIRED)) - rc = 0; - - if (rc) - goto out_free_ntlmsspblob; - - cifs_dbg(FYI, "rawntlmssp session setup challenge phase\n"); - - if (smb_buf->WordCount != 4) { - rc = smb_EIO1(smb_eio_trace_sess_rawnl_neg_wcc, smb_buf->WordCount); - cifs_dbg(VFS, "bad word count %d\n", smb_buf->WordCount); - goto out_free_ntlmsspblob; - } - - ses->Suid = smb_buf->Uid; /* UID left in wire format (le) */ - cifs_dbg(FYI, "UID = %llu\n", ses->Suid); - - bytes_remaining = get_bcc(smb_buf); - bcc_ptr = pByteArea(smb_buf); - - blob_len = le16_to_cpu(pSMB->resp.SecurityBlobLength); - if (blob_len > bytes_remaining) { - cifs_dbg(VFS, "bad security blob length %d\n", - blob_len); - rc = -EINVAL; - goto out_free_ntlmsspblob; - } - - rc = decode_ntlmssp_challenge(bcc_ptr, blob_len, ses); - -out_free_ntlmsspblob: - kfree_sensitive(ntlmsspblob); -out: - sess_free_buffer(sess_data); - - if (!rc) { - sess_data->func = sess_auth_rawntlmssp_authenticate; - return; - } - - /* Else error. Cleanup */ - kfree_sensitive(ses->auth_key.response); - ses->auth_key.response = NULL; - kfree_sensitive(ses->ntlmssp); - ses->ntlmssp = NULL; - - sess_data->func = NULL; - sess_data->result = rc; -} - -static void -sess_auth_rawntlmssp_authenticate(struct sess_data *sess_data) -{ - int rc; - struct smb_hdr *smb_buf; - SESSION_SETUP_ANDX *pSMB; - struct cifs_ses *ses = sess_data->ses; - struct TCP_Server_Info *server = sess_data->server; - __u16 bytes_remaining; - char *bcc_ptr; - unsigned char *ntlmsspblob = NULL; - u16 blob_len; - - cifs_dbg(FYI, "rawntlmssp session setup authenticate phase\n"); - - /* wct = 12 */ - rc = sess_alloc_buffer(sess_data, 12); - if (rc) - goto out; - - /* Build security blob before we assemble the request */ - pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base; - smb_buf = (struct smb_hdr *)pSMB; - rc = build_ntlmssp_auth_blob(&ntlmsspblob, - &blob_len, ses, server, - sess_data->nls_cp); - if (rc) - goto out_free_ntlmsspblob; - sess_data->iov[1].iov_len = blob_len; - sess_data->iov[1].iov_base = ntlmsspblob; - pSMB->req.SecurityBlobLength = cpu_to_le16(blob_len); - /* - * Make sure that we tell the server that we are using - * the uid that it just gave us back on the response - * (challenge) - */ - smb_buf->Uid = ses->Suid; - - rc = _sess_auth_rawntlmssp_assemble_req(sess_data); - if (rc) - goto out_free_ntlmsspblob; - - rc = sess_sendreceive(sess_data); - if (rc) - goto out_free_ntlmsspblob; - - pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base; - smb_buf = (struct smb_hdr *)sess_data->iov[0].iov_base; - if (smb_buf->WordCount != 4) { - rc = smb_EIO1(smb_eio_trace_sess_rawnl_auth_wcc, smb_buf->WordCount); - cifs_dbg(VFS, "bad word count %d\n", smb_buf->WordCount); - goto out_free_ntlmsspblob; - } - - if (le16_to_cpu(pSMB->resp.Action) & GUEST_LOGIN) - cifs_dbg(FYI, "Guest login\n"); /* BB mark SesInfo struct? */ - - if (ses->Suid != smb_buf->Uid) { - ses->Suid = smb_buf->Uid; - cifs_dbg(FYI, "UID changed! new UID = %llu\n", ses->Suid); - } - - bytes_remaining = get_bcc(smb_buf); - bcc_ptr = pByteArea(smb_buf); - blob_len = le16_to_cpu(pSMB->resp.SecurityBlobLength); - if (blob_len > bytes_remaining) { - cifs_dbg(VFS, "bad security blob length %d\n", - blob_len); - rc = -EINVAL; - goto out_free_ntlmsspblob; - } - bcc_ptr += blob_len; - bytes_remaining -= blob_len; - - - /* BB check if Unicode and decode strings */ - if (bytes_remaining == 0) { - /* no string area to decode, do nothing */ - } else if (smb_buf->Flags2 & SMBFLG2_UNICODE) { - /* unicode string area must be word-aligned */ - if (!IS_ALIGNED((unsigned long)bcc_ptr - (unsigned long)smb_buf, 2)) { - ++bcc_ptr; - --bytes_remaining; - } - decode_unicode_ssetup(&bcc_ptr, bytes_remaining, ses, - sess_data->nls_cp); - } else { - decode_ascii_ssetup(&bcc_ptr, bytes_remaining, ses, - sess_data->nls_cp); - } - -out_free_ntlmsspblob: - kfree_sensitive(ntlmsspblob); -out: - sess_free_buffer(sess_data); - - if (!rc) - rc = sess_establish_session(sess_data); - - /* Cleanup */ - kfree_sensitive(ses->auth_key.response); - ses->auth_key.response = NULL; - kfree_sensitive(ses->ntlmssp); - ses->ntlmssp = NULL; - - sess_data->func = NULL; - sess_data->result = rc; -} - -static int select_sec(struct sess_data *sess_data) -{ - int type; - struct cifs_ses *ses = sess_data->ses; - struct TCP_Server_Info *server = sess_data->server; - - type = cifs_select_sectype(server, ses->sectype); - cifs_dbg(FYI, "sess setup type %d\n", type); - if (type == Unspecified) { - cifs_dbg(VFS, "Unable to select appropriate authentication method!\n"); - return -EINVAL; - } - - switch (type) { - case NTLMv2: - sess_data->func = sess_auth_ntlmv2; - break; - case Kerberos: -#ifdef CONFIG_CIFS_UPCALL - sess_data->func = sess_auth_kerberos; - break; -#else - cifs_dbg(VFS, "Kerberos negotiated but upcall support disabled!\n"); - return -ENOSYS; -#endif /* CONFIG_CIFS_UPCALL */ - case RawNTLMSSP: - sess_data->func = sess_auth_rawntlmssp_negotiate; - break; - default: - cifs_dbg(VFS, "secType %d not supported!\n", type); - return -ENOSYS; - } - - return 0; -} - -int CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses, - struct TCP_Server_Info *server, - const struct nls_table *nls_cp) -{ - int rc = 0; - struct sess_data *sess_data; - - if (ses == NULL) { - WARN(1, "%s: ses == NULL!", __func__); - return -EINVAL; - } - - sess_data = kzalloc(sizeof(struct sess_data), GFP_KERNEL); - if (!sess_data) - return -ENOMEM; - - sess_data->xid = xid; - sess_data->ses = ses; - sess_data->server = server; - sess_data->buf0_type = CIFS_NO_BUFFER; - sess_data->nls_cp = (struct nls_table *) nls_cp; - - rc = select_sec(sess_data); - if (rc) - goto out; - - while (sess_data->func) - sess_data->func(sess_data); - - /* Store result before we free sess_data */ - rc = sess_data->result; - -out: - kfree_sensitive(sess_data); - return rc; -} -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ diff --git a/fs/smb/client/smb1debug.c b/fs/smb/client/smb1debug.c new file mode 100644 index 000000000000..e2d013e751e5 --- /dev/null +++ b/fs/smb/client/smb1debug.c @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * + * Copyright (C) International Business Machines Corp., 2000,2005 + * + * Modified by Steve French (sfrench@us.ibm.com) + */ +#include "cifsproto.h" +#include "smb1proto.h" +#include "cifs_debug.h" + +void cifs_dump_detail(void *buf, size_t buf_len, struct TCP_Server_Info *server) +{ +#ifdef CONFIG_CIFS_DEBUG2 + struct smb_hdr *smb = buf; + + cifs_dbg(VFS, "Cmd: %d Err: 0x%x Flags: 0x%x Flgs2: 0x%x Mid: %d Pid: %d Wct: %d\n", + smb->Command, smb->Status.CifsError, smb->Flags, + smb->Flags2, smb->Mid, smb->Pid, smb->WordCount); + if (!server->ops->check_message(buf, buf_len, server->total_read, server)) { + cifs_dbg(VFS, "smb buf %p len %u\n", smb, + server->ops->calc_smb_size(smb)); + } +#endif /* CONFIG_CIFS_DEBUG2 */ +} diff --git a/fs/smb/client/smb1encrypt.c b/fs/smb/client/smb1encrypt.c new file mode 100644 index 000000000000..0dbbce2431ff --- /dev/null +++ b/fs/smb/client/smb1encrypt.c @@ -0,0 +1,139 @@ +// SPDX-License-Identifier: LGPL-2.1 +/* + * + * Encryption and hashing operations relating to NTLM, NTLMv2. See MS-NLMP + * for more detailed information + * + * Copyright (C) International Business Machines Corp., 2005,2013 + * Author(s): Steve French (sfrench@us.ibm.com) + * + */ + +#include <linux/fips.h> +#include <crypto/md5.h> +#include "cifsproto.h" +#include "smb1proto.h" +#include "cifs_debug.h" + +/* + * Calculate and return the CIFS signature based on the mac key and SMB PDU. + * The 16 byte signature must be allocated by the caller. Note we only use the + * 1st eight bytes and that the smb header signature field on input contains + * the sequence number before this function is called. Also, this function + * should be called with the server->srv_mutex held. + */ +static int cifs_calc_signature(struct smb_rqst *rqst, + struct TCP_Server_Info *server, char *signature) +{ + struct md5_ctx ctx; + + if (!rqst->rq_iov || !signature || !server) + return -EINVAL; + if (fips_enabled) { + cifs_dbg(VFS, + "MD5 signature support is disabled due to FIPS\n"); + return -EOPNOTSUPP; + } + + md5_init(&ctx); + md5_update(&ctx, server->session_key.response, server->session_key.len); + + return __cifs_calc_signature( + rqst, server, signature, + &(struct cifs_calc_sig_ctx){ .md5 = &ctx }); +} + +/* must be called with server->srv_mutex held */ +int cifs_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server, + __u32 *pexpected_response_sequence_number) +{ + int rc = 0; + char smb_signature[20]; + struct smb_hdr *cifs_pdu = (struct smb_hdr *)rqst->rq_iov[0].iov_base; + + if ((cifs_pdu == NULL) || (server == NULL)) + return -EINVAL; + + spin_lock(&server->srv_lock); + if (!(cifs_pdu->Flags2 & SMBFLG2_SECURITY_SIGNATURE) || + server->tcpStatus == CifsNeedNegotiate) { + spin_unlock(&server->srv_lock); + return rc; + } + spin_unlock(&server->srv_lock); + + if (!server->session_estab) { + memcpy(cifs_pdu->Signature.SecuritySignature, "BSRSPYL", 8); + return rc; + } + + cifs_pdu->Signature.Sequence.SequenceNumber = + cpu_to_le32(server->sequence_number); + cifs_pdu->Signature.Sequence.Reserved = 0; + + *pexpected_response_sequence_number = ++server->sequence_number; + ++server->sequence_number; + + rc = cifs_calc_signature(rqst, server, smb_signature); + if (rc) + memset(cifs_pdu->Signature.SecuritySignature, 0, 8); + else + memcpy(cifs_pdu->Signature.SecuritySignature, smb_signature, 8); + + return rc; +} + +int cifs_verify_signature(struct smb_rqst *rqst, + struct TCP_Server_Info *server, + __u32 expected_sequence_number) +{ + unsigned int rc; + char server_response_sig[8]; + char what_we_think_sig_should_be[20]; + struct smb_hdr *cifs_pdu = (struct smb_hdr *)rqst->rq_iov[0].iov_base; + + if (cifs_pdu == NULL || server == NULL) + return -EINVAL; + + if (!server->session_estab) + return 0; + + if (cifs_pdu->Command == SMB_COM_LOCKING_ANDX) { + struct smb_com_lock_req *pSMB = + (struct smb_com_lock_req *)cifs_pdu; + if (pSMB->LockType & LOCKING_ANDX_OPLOCK_RELEASE) + return 0; + } + + /* BB what if signatures are supposed to be on for session but + server does not send one? BB */ + + /* Do not need to verify session setups with signature "BSRSPYL " */ + if (memcmp(cifs_pdu->Signature.SecuritySignature, "BSRSPYL ", 8) == 0) + cifs_dbg(FYI, "dummy signature received for smb command 0x%x\n", + cifs_pdu->Command); + + /* save off the original signature so we can modify the smb and check + its signature against what the server sent */ + memcpy(server_response_sig, cifs_pdu->Signature.SecuritySignature, 8); + + cifs_pdu->Signature.Sequence.SequenceNumber = + cpu_to_le32(expected_sequence_number); + cifs_pdu->Signature.Sequence.Reserved = 0; + + cifs_server_lock(server); + rc = cifs_calc_signature(rqst, server, what_we_think_sig_should_be); + cifs_server_unlock(server); + + if (rc) + return rc; + +/* cifs_dump_mem("what we think it should be: ", + what_we_think_sig_should_be, 16); */ + + if (memcmp(server_response_sig, what_we_think_sig_should_be, 8)) + return -EACCES; + else + return 0; + +} diff --git a/fs/smb/client/smb1maperror.c b/fs/smb/client/smb1maperror.c new file mode 100644 index 000000000000..277ef0865be0 --- /dev/null +++ b/fs/smb/client/smb1maperror.c @@ -0,0 +1,809 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * + * Copyright (c) International Business Machines Corp., 2002,2008 + * Author(s): Steve French (sfrench@us.ibm.com) + * + * Error mapping routines from Samba libsmb/errormap.c + * Copyright (C) Andrew Tridgell 2001 + */ + +#include "cifsproto.h" +#include "smb1proto.h" +#include "smberr.h" +#include "nterr.h" +#include "cifs_debug.h" + +struct smb_to_posix_error { + __u16 smb_err; + int posix_code; +}; + +static const struct smb_to_posix_error mapping_table_ERRDOS[] = { + {ERRbadfunc, -EINVAL}, + {ERRbadfile, -ENOENT}, + {ERRbadpath, -ENOTDIR}, + {ERRnofids, -EMFILE}, + {ERRnoaccess, -EACCES}, + {ERRbadfid, -EBADF}, + {ERRbadmcb, -EIO}, + {ERRnomem, -EREMOTEIO}, + {ERRbadmem, -EFAULT}, + {ERRbadenv, -EFAULT}, + {ERRbadformat, -EINVAL}, + {ERRbadaccess, -EACCES}, + {ERRbaddata, -EIO}, + {ERRbaddrive, -ENXIO}, + {ERRremcd, -EACCES}, + {ERRdiffdevice, -EXDEV}, + {ERRnofiles, -ENOENT}, + {ERRwriteprot, -EROFS}, + {ERRbadshare, -EBUSY}, + {ERRlock, -EACCES}, + {ERRunsup, -EINVAL}, + {ERRnosuchshare, -ENXIO}, + {ERRfilexists, -EEXIST}, + {ERRinvparm, -EINVAL}, + {ERRdiskfull, -ENOSPC}, + {ERRinvname, -ENOENT}, + {ERRunknownlevel, -EOPNOTSUPP}, + {ERRdirnotempty, -ENOTEMPTY}, + {ERRnotlocked, -ENOLCK}, + {ERRcancelviolation, -ENOLCK}, + {ERRalreadyexists, -EEXIST}, + {ERRmoredata, -EOVERFLOW}, + {ERReasnotsupported, -EOPNOTSUPP}, + {ErrQuota, -EDQUOT}, + {ErrNotALink, -ENOLINK}, + {ERRnetlogonNotStarted, -ENOPROTOOPT}, + {ERRsymlink, -EOPNOTSUPP}, + {ErrTooManyLinks, -EMLINK}, + {0, 0} +}; + +static const struct smb_to_posix_error mapping_table_ERRSRV[] = { + {ERRerror, -EIO}, + {ERRbadpw, -EACCES}, /* was EPERM */ + {ERRbadtype, -EREMOTE}, + {ERRaccess, -EACCES}, + {ERRinvtid, -ENXIO}, + {ERRinvnetname, -ENXIO}, + {ERRinvdevice, -ENXIO}, + {ERRqfull, -ENOSPC}, + {ERRqtoobig, -ENOSPC}, + {ERRqeof, -EIO}, + {ERRinvpfid, -EBADF}, + {ERRsmbcmd, -EBADRQC}, + {ERRsrverror, -EIO}, + {ERRbadBID, -EIO}, + {ERRfilespecs, -EINVAL}, + {ERRbadLink, -EIO}, + {ERRbadpermits, -EINVAL}, + {ERRbadPID, -ESRCH}, + {ERRsetattrmode, -EINVAL}, + {ERRpaused, -EHOSTDOWN}, + {ERRmsgoff, -EHOSTDOWN}, + {ERRnoroom, -ENOSPC}, + {ERRrmuns, -EUSERS}, + {ERRtimeout, -ETIME}, + {ERRnoresource, -EREMOTEIO}, + {ERRtoomanyuids, -EUSERS}, + {ERRbaduid, -EACCES}, + {ERRusempx, -EIO}, + {ERRusestd, -EIO}, + {ERR_NOTIFY_ENUM_DIR, -ENOBUFS}, + {ERRnoSuchUser, -EACCES}, + {ERRaccountexpired, -EKEYEXPIRED}, + {ERRbadclient, -EACCES}, + {ERRbadLogonTime, -EACCES}, + {ERRpasswordExpired, -EKEYEXPIRED}, + + {ERRnosupport, -EINVAL}, + {0, 0} +}; + +/***************************************************************************** + *convert a NT status code to a dos class/code + *****************************************************************************/ +/* NT status -> dos error map */ +static const struct { + __u8 dos_class; + __u16 dos_code; + __u32 ntstatus; +} ntstatus_to_dos_map[] = { + { + ERRDOS, ERRgeneral, NT_STATUS_UNSUCCESSFUL}, { + ERRDOS, ERRbadfunc, NT_STATUS_NOT_IMPLEMENTED}, { + ERRDOS, ERRbadpipe, NT_STATUS_INVALID_INFO_CLASS}, { + ERRDOS, 24, NT_STATUS_INFO_LENGTH_MISMATCH}, { + ERRHRD, ERRgeneral, NT_STATUS_ACCESS_VIOLATION}, { + ERRHRD, ERRgeneral, NT_STATUS_IN_PAGE_ERROR}, { + ERRHRD, ERRgeneral, NT_STATUS_PAGEFILE_QUOTA}, { + ERRDOS, ERRbadfid, NT_STATUS_INVALID_HANDLE}, { + ERRHRD, ERRgeneral, NT_STATUS_BAD_INITIAL_STACK}, { + ERRDOS, 193, NT_STATUS_BAD_INITIAL_PC}, { + ERRDOS, 87, NT_STATUS_INVALID_CID}, { + ERRHRD, ERRgeneral, NT_STATUS_TIMER_NOT_CANCELED}, { + ERRDOS, 87, NT_STATUS_INVALID_PARAMETER}, { + ERRDOS, ERRbadfile, NT_STATUS_NO_SUCH_DEVICE}, { + ERRDOS, ERRbadfile, NT_STATUS_NO_SUCH_FILE}, { + ERRDOS, ERRbadfunc, NT_STATUS_INVALID_DEVICE_REQUEST}, { + ERRDOS, 38, NT_STATUS_END_OF_FILE}, { + ERRDOS, 34, NT_STATUS_WRONG_VOLUME}, { + ERRDOS, 21, NT_STATUS_NO_MEDIA_IN_DEVICE}, { + ERRHRD, ERRgeneral, NT_STATUS_UNRECOGNIZED_MEDIA}, { + ERRDOS, 27, NT_STATUS_NONEXISTENT_SECTOR}, +/* { This NT error code was 'sqashed' + from NT_STATUS_MORE_PROCESSING_REQUIRED to NT_STATUS_OK + during the session setup } */ + { + ERRDOS, ERRnomem, NT_STATUS_NO_MEMORY}, { + ERRDOS, 487, NT_STATUS_CONFLICTING_ADDRESSES}, { + ERRDOS, 487, NT_STATUS_NOT_MAPPED_VIEW}, { + ERRDOS, 87, NT_STATUS_UNABLE_TO_FREE_VM}, { + ERRDOS, 87, NT_STATUS_UNABLE_TO_DELETE_SECTION}, { + ERRDOS, 2142, NT_STATUS_INVALID_SYSTEM_SERVICE}, { + ERRHRD, ERRgeneral, NT_STATUS_ILLEGAL_INSTRUCTION}, { + ERRDOS, ERRnoaccess, NT_STATUS_INVALID_LOCK_SEQUENCE}, { + ERRDOS, ERRnoaccess, NT_STATUS_INVALID_VIEW_SIZE}, { + ERRDOS, 193, NT_STATUS_INVALID_FILE_FOR_SECTION}, { + ERRDOS, ERRnoaccess, NT_STATUS_ALREADY_COMMITTED}, +/* { This NT error code was 'sqashed' + from NT_STATUS_ACCESS_DENIED to NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE + during the session setup } */ + { + ERRDOS, ERRnoaccess, NT_STATUS_ACCESS_DENIED}, { + ERRDOS, 111, NT_STATUS_BUFFER_TOO_SMALL}, { + ERRDOS, ERRbadfid, NT_STATUS_OBJECT_TYPE_MISMATCH}, { + ERRHRD, ERRgeneral, NT_STATUS_NONCONTINUABLE_EXCEPTION}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_DISPOSITION}, { + ERRHRD, ERRgeneral, NT_STATUS_UNWIND}, { + ERRHRD, ERRgeneral, NT_STATUS_BAD_STACK}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_UNWIND_TARGET}, { + ERRDOS, 158, NT_STATUS_NOT_LOCKED}, { + ERRHRD, ERRgeneral, NT_STATUS_PARITY_ERROR}, { + ERRDOS, 487, NT_STATUS_UNABLE_TO_DECOMMIT_VM}, { + ERRDOS, 487, NT_STATUS_NOT_COMMITTED}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_PORT_ATTRIBUTES}, { + ERRHRD, ERRgeneral, NT_STATUS_PORT_MESSAGE_TOO_LONG}, { + ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_MIX}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_QUOTA_LOWER}, { + ERRHRD, ERRgeneral, NT_STATUS_DISK_CORRUPT_ERROR}, { + /* mapping changed since shell does lookup on * expects FileNotFound */ + ERRDOS, ERRbadfile, NT_STATUS_OBJECT_NAME_INVALID}, { + ERRDOS, ERRbadfile, NT_STATUS_OBJECT_NAME_NOT_FOUND}, { + ERRDOS, ERRalreadyexists, NT_STATUS_OBJECT_NAME_COLLISION}, { + ERRHRD, ERRgeneral, NT_STATUS_HANDLE_NOT_WAITABLE}, { + ERRDOS, ERRbadfid, NT_STATUS_PORT_DISCONNECTED}, { + ERRHRD, ERRgeneral, NT_STATUS_DEVICE_ALREADY_ATTACHED}, { + ERRDOS, 161, NT_STATUS_OBJECT_PATH_INVALID}, { + ERRDOS, ERRbadpath, NT_STATUS_OBJECT_PATH_NOT_FOUND}, { + ERRDOS, 161, NT_STATUS_OBJECT_PATH_SYNTAX_BAD}, { + ERRHRD, ERRgeneral, NT_STATUS_DATA_OVERRUN}, { + ERRHRD, ERRgeneral, NT_STATUS_DATA_LATE_ERROR}, { + ERRDOS, 23, NT_STATUS_DATA_ERROR}, { + ERRDOS, 23, NT_STATUS_CRC_ERROR}, { + ERRDOS, ERRnomem, NT_STATUS_SECTION_TOO_BIG}, { + ERRDOS, ERRnoaccess, NT_STATUS_PORT_CONNECTION_REFUSED}, { + ERRDOS, ERRbadfid, NT_STATUS_INVALID_PORT_HANDLE}, { + ERRDOS, ERRbadshare, NT_STATUS_SHARING_VIOLATION}, { + ERRHRD, ERRgeneral, NT_STATUS_QUOTA_EXCEEDED}, { + ERRDOS, 87, NT_STATUS_INVALID_PAGE_PROTECTION}, { + ERRDOS, 288, NT_STATUS_MUTANT_NOT_OWNED}, { + ERRDOS, 298, NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED}, { + ERRDOS, 87, NT_STATUS_PORT_ALREADY_SET}, { + ERRDOS, 87, NT_STATUS_SECTION_NOT_IMAGE}, { + ERRDOS, 156, NT_STATUS_SUSPEND_COUNT_EXCEEDED}, { + ERRDOS, ERRnoaccess, NT_STATUS_THREAD_IS_TERMINATING}, { + ERRDOS, 87, NT_STATUS_BAD_WORKING_SET_LIMIT}, { + ERRDOS, 87, NT_STATUS_INCOMPATIBLE_FILE_MAP}, { + ERRDOS, 87, NT_STATUS_SECTION_PROTECTION}, { + ERRDOS, ERReasnotsupported, NT_STATUS_EAS_NOT_SUPPORTED}, { + ERRDOS, 255, NT_STATUS_EA_TOO_LARGE}, { + ERRHRD, ERRgeneral, NT_STATUS_NONEXISTENT_EA_ENTRY}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_EAS_ON_FILE}, { + ERRHRD, ERRgeneral, NT_STATUS_EA_CORRUPT_ERROR}, { + ERRDOS, ERRlock, NT_STATUS_FILE_LOCK_CONFLICT}, { + ERRDOS, ERRlock, NT_STATUS_LOCK_NOT_GRANTED}, { + ERRDOS, ERRbadfile, NT_STATUS_DELETE_PENDING}, { + ERRDOS, ERRunsup, NT_STATUS_CTL_FILE_NOT_SUPPORTED}, { + ERRHRD, ERRgeneral, NT_STATUS_UNKNOWN_REVISION}, { + ERRHRD, ERRgeneral, NT_STATUS_REVISION_MISMATCH}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_OWNER}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_PRIMARY_GROUP}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_IMPERSONATION_TOKEN}, { + ERRHRD, ERRgeneral, NT_STATUS_CANT_DISABLE_MANDATORY}, { + ERRDOS, 2215, NT_STATUS_NO_LOGON_SERVERS}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_LOGON_SESSION}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_PRIVILEGE}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_ACCOUNT_NAME}, { + ERRHRD, ERRgeneral, NT_STATUS_USER_EXISTS}, +/* { This NT error code was 'sqashed' + from NT_STATUS_NO_SUCH_USER to NT_STATUS_LOGON_FAILURE + during the session setup } */ + { + ERRDOS, ERRnoaccess, NT_STATUS_NO_SUCH_USER}, { /* could map to 2238 */ + ERRHRD, ERRgeneral, NT_STATUS_GROUP_EXISTS}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_GROUP}, { + ERRHRD, ERRgeneral, NT_STATUS_MEMBER_IN_GROUP}, { + ERRHRD, ERRgeneral, NT_STATUS_MEMBER_NOT_IN_GROUP}, { + ERRHRD, ERRgeneral, NT_STATUS_LAST_ADMIN}, +/* { This NT error code was 'sqashed' + from NT_STATUS_WRONG_PASSWORD to NT_STATUS_LOGON_FAILURE + during the session setup } */ + { + ERRSRV, ERRbadpw, NT_STATUS_WRONG_PASSWORD}, { + ERRHRD, ERRgeneral, NT_STATUS_ILL_FORMED_PASSWORD}, { + ERRHRD, ERRgeneral, NT_STATUS_PASSWORD_RESTRICTION}, { + ERRDOS, ERRnoaccess, NT_STATUS_LOGON_FAILURE}, { + ERRHRD, ERRgeneral, NT_STATUS_ACCOUNT_RESTRICTION}, { + ERRSRV, ERRbadLogonTime, NT_STATUS_INVALID_LOGON_HOURS}, { + ERRSRV, ERRbadclient, NT_STATUS_INVALID_WORKSTATION}, { + ERRSRV, ERRpasswordExpired, NT_STATUS_PASSWORD_EXPIRED}, { + ERRSRV, ERRaccountexpired, NT_STATUS_ACCOUNT_DISABLED}, { + ERRHRD, ERRgeneral, NT_STATUS_NONE_MAPPED}, { + ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_LUIDS_REQUESTED}, { + ERRHRD, ERRgeneral, NT_STATUS_LUIDS_EXHAUSTED}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_SUB_AUTHORITY}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_ACL}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_SID}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_SECURITY_DESCR}, { + ERRDOS, 127, NT_STATUS_PROCEDURE_NOT_FOUND}, { + ERRDOS, 193, NT_STATUS_INVALID_IMAGE_FORMAT}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_TOKEN}, { + ERRHRD, ERRgeneral, NT_STATUS_BAD_INHERITANCE_ACL}, { + ERRDOS, 158, NT_STATUS_RANGE_NOT_LOCKED}, { + ERRDOS, 112, NT_STATUS_DISK_FULL}, { + ERRHRD, ERRgeneral, NT_STATUS_SERVER_DISABLED}, { + ERRHRD, ERRgeneral, NT_STATUS_SERVER_NOT_DISABLED}, { + ERRDOS, 68, NT_STATUS_TOO_MANY_GUIDS_REQUESTED}, { + ERRDOS, 259, NT_STATUS_GUIDS_EXHAUSTED}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_ID_AUTHORITY}, { + ERRDOS, 259, NT_STATUS_AGENTS_EXHAUSTED}, { + ERRDOS, 154, NT_STATUS_INVALID_VOLUME_LABEL}, { + ERRDOS, 14, NT_STATUS_SECTION_NOT_EXTENDED}, { + ERRDOS, 487, NT_STATUS_NOT_MAPPED_DATA}, { + ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_DATA_NOT_FOUND}, { + ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_TYPE_NOT_FOUND}, { + ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_NAME_NOT_FOUND}, { + ERRHRD, ERRgeneral, NT_STATUS_ARRAY_BOUNDS_EXCEEDED}, { + ERRHRD, ERRgeneral, NT_STATUS_FLOAT_DENORMAL_OPERAND}, { + ERRHRD, ERRgeneral, NT_STATUS_FLOAT_DIVIDE_BY_ZERO}, { + ERRHRD, ERRgeneral, NT_STATUS_FLOAT_INEXACT_RESULT}, { + ERRHRD, ERRgeneral, NT_STATUS_FLOAT_INVALID_OPERATION}, { + ERRHRD, ERRgeneral, NT_STATUS_FLOAT_OVERFLOW}, { + ERRHRD, ERRgeneral, NT_STATUS_FLOAT_STACK_CHECK}, { + ERRHRD, ERRgeneral, NT_STATUS_FLOAT_UNDERFLOW}, { + ERRHRD, ERRgeneral, NT_STATUS_INTEGER_DIVIDE_BY_ZERO}, { + ERRDOS, 534, NT_STATUS_INTEGER_OVERFLOW}, { + ERRHRD, ERRgeneral, NT_STATUS_PRIVILEGED_INSTRUCTION}, { + ERRDOS, ERRnomem, NT_STATUS_TOO_MANY_PAGING_FILES}, { + ERRHRD, ERRgeneral, NT_STATUS_FILE_INVALID}, { + ERRHRD, ERRgeneral, NT_STATUS_ALLOTTED_SPACE_EXCEEDED}, +/* { This NT error code was 'sqashed' + from NT_STATUS_INSUFFICIENT_RESOURCES to + NT_STATUS_INSUFF_SERVER_RESOURCES during the session setup } */ + { + ERRDOS, ERRnoresource, NT_STATUS_INSUFFICIENT_RESOURCES}, { + ERRDOS, ERRbadpath, NT_STATUS_DFS_EXIT_PATH_FOUND}, { + ERRDOS, 23, NT_STATUS_DEVICE_DATA_ERROR}, { + ERRHRD, ERRgeneral, NT_STATUS_DEVICE_NOT_CONNECTED}, { + ERRDOS, 21, NT_STATUS_DEVICE_POWER_FAILURE}, { + ERRDOS, 487, NT_STATUS_FREE_VM_NOT_AT_BASE}, { + ERRDOS, 487, NT_STATUS_MEMORY_NOT_ALLOCATED}, { + ERRHRD, ERRgeneral, NT_STATUS_WORKING_SET_QUOTA}, { + ERRDOS, 19, NT_STATUS_MEDIA_WRITE_PROTECTED}, { + ERRDOS, 21, NT_STATUS_DEVICE_NOT_READY}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_GROUP_ATTRIBUTES}, { + ERRHRD, ERRgeneral, NT_STATUS_BAD_IMPERSONATION_LEVEL}, { + ERRHRD, ERRgeneral, NT_STATUS_CANT_OPEN_ANONYMOUS}, { + ERRHRD, ERRgeneral, NT_STATUS_BAD_VALIDATION_CLASS}, { + ERRHRD, ERRgeneral, NT_STATUS_BAD_TOKEN_TYPE}, { + ERRDOS, 87, NT_STATUS_BAD_MASTER_BOOT_RECORD}, { + ERRHRD, ERRgeneral, NT_STATUS_INSTRUCTION_MISALIGNMENT}, { + ERRDOS, ERRpipebusy, NT_STATUS_INSTANCE_NOT_AVAILABLE}, { + ERRDOS, ERRpipebusy, NT_STATUS_PIPE_NOT_AVAILABLE}, { + ERRDOS, ERRbadpipe, NT_STATUS_INVALID_PIPE_STATE}, { + ERRDOS, ERRpipebusy, NT_STATUS_PIPE_BUSY}, { + ERRDOS, ERRbadfunc, NT_STATUS_ILLEGAL_FUNCTION}, { + ERRDOS, ERRnotconnected, NT_STATUS_PIPE_DISCONNECTED}, { + ERRDOS, ERRpipeclosing, NT_STATUS_PIPE_CLOSING}, { + ERRHRD, ERRgeneral, NT_STATUS_PIPE_CONNECTED}, { + ERRHRD, ERRgeneral, NT_STATUS_PIPE_LISTENING}, { + ERRDOS, ERRbadpipe, NT_STATUS_INVALID_READ_MODE}, { + ERRDOS, 121, NT_STATUS_IO_TIMEOUT}, { + ERRDOS, 38, NT_STATUS_FILE_FORCED_CLOSED}, { + ERRHRD, ERRgeneral, NT_STATUS_PROFILING_NOT_STARTED}, { + ERRHRD, ERRgeneral, NT_STATUS_PROFILING_NOT_STOPPED}, { + ERRHRD, ERRgeneral, NT_STATUS_COULD_NOT_INTERPRET}, { + ERRDOS, ERRnoaccess, NT_STATUS_FILE_IS_A_DIRECTORY}, { + ERRDOS, ERRunsup, NT_STATUS_NOT_SUPPORTED}, { + ERRDOS, 51, NT_STATUS_REMOTE_NOT_LISTENING}, { + ERRDOS, 52, NT_STATUS_DUPLICATE_NAME}, { + ERRDOS, 53, NT_STATUS_BAD_NETWORK_PATH}, { + ERRDOS, 54, NT_STATUS_NETWORK_BUSY}, { + ERRDOS, 55, NT_STATUS_DEVICE_DOES_NOT_EXIST}, { + ERRDOS, 56, NT_STATUS_TOO_MANY_COMMANDS}, { + ERRDOS, 57, NT_STATUS_ADAPTER_HARDWARE_ERROR}, { + ERRDOS, 58, NT_STATUS_INVALID_NETWORK_RESPONSE}, { + ERRDOS, 59, NT_STATUS_UNEXPECTED_NETWORK_ERROR}, { + ERRDOS, 60, NT_STATUS_BAD_REMOTE_ADAPTER}, { + ERRDOS, 61, NT_STATUS_PRINT_QUEUE_FULL}, { + ERRDOS, 62, NT_STATUS_NO_SPOOL_SPACE}, { + ERRDOS, 63, NT_STATUS_PRINT_CANCELLED}, { + ERRDOS, 64, NT_STATUS_NETWORK_NAME_DELETED}, { + ERRDOS, 65, NT_STATUS_NETWORK_ACCESS_DENIED}, { + ERRDOS, 66, NT_STATUS_BAD_DEVICE_TYPE}, { + ERRDOS, ERRnosuchshare, NT_STATUS_BAD_NETWORK_NAME}, { + ERRDOS, 68, NT_STATUS_TOO_MANY_NAMES}, { + ERRDOS, 69, NT_STATUS_TOO_MANY_SESSIONS}, { + ERRDOS, 70, NT_STATUS_SHARING_PAUSED}, { + ERRDOS, 71, NT_STATUS_REQUEST_NOT_ACCEPTED}, { + ERRDOS, 72, NT_STATUS_REDIRECTOR_PAUSED}, { + ERRDOS, 88, NT_STATUS_NET_WRITE_FAULT}, { + ERRHRD, ERRgeneral, NT_STATUS_PROFILING_AT_LIMIT}, { + ERRDOS, ERRdiffdevice, NT_STATUS_NOT_SAME_DEVICE}, { + ERRDOS, ERRnoaccess, NT_STATUS_FILE_RENAMED}, { + ERRDOS, 240, NT_STATUS_VIRTUAL_CIRCUIT_CLOSED}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_SECURITY_ON_OBJECT}, { + ERRHRD, ERRgeneral, NT_STATUS_CANT_WAIT}, { + ERRDOS, ERRpipeclosing, NT_STATUS_PIPE_EMPTY}, { + ERRHRD, ERRgeneral, NT_STATUS_CANT_ACCESS_DOMAIN_INFO}, { + ERRHRD, ERRgeneral, NT_STATUS_CANT_TERMINATE_SELF}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_SERVER_STATE}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_DOMAIN_STATE}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_DOMAIN_ROLE}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_DOMAIN}, { + ERRHRD, ERRgeneral, NT_STATUS_DOMAIN_EXISTS}, { + ERRHRD, ERRgeneral, NT_STATUS_DOMAIN_LIMIT_EXCEEDED}, { + ERRDOS, 300, NT_STATUS_OPLOCK_NOT_GRANTED}, { + ERRDOS, 301, NT_STATUS_INVALID_OPLOCK_PROTOCOL}, { + ERRHRD, ERRgeneral, NT_STATUS_INTERNAL_DB_CORRUPTION}, { + ERRHRD, ERRgeneral, NT_STATUS_INTERNAL_ERROR}, { + ERRHRD, ERRgeneral, NT_STATUS_GENERIC_NOT_MAPPED}, { + ERRHRD, ERRgeneral, NT_STATUS_BAD_DESCRIPTOR_FORMAT}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_USER_BUFFER}, { + ERRHRD, ERRgeneral, NT_STATUS_UNEXPECTED_IO_ERROR}, { + ERRHRD, ERRgeneral, NT_STATUS_UNEXPECTED_MM_CREATE_ERR}, { + ERRHRD, ERRgeneral, NT_STATUS_UNEXPECTED_MM_MAP_ERROR}, { + ERRHRD, ERRgeneral, NT_STATUS_UNEXPECTED_MM_EXTEND_ERR}, { + ERRHRD, ERRgeneral, NT_STATUS_NOT_LOGON_PROCESS}, { + ERRHRD, ERRgeneral, NT_STATUS_LOGON_SESSION_EXISTS}, { + ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_1}, { + ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_2}, { + ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_3}, { + ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_4}, { + ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_5}, { + ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_6}, { + ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_7}, { + ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_8}, { + ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_9}, { + ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_10}, { + ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_11}, { + ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_12}, { + ERRDOS, ERRbadpath, NT_STATUS_REDIRECTOR_NOT_STARTED}, { + ERRHRD, ERRgeneral, NT_STATUS_REDIRECTOR_STARTED}, { + ERRHRD, ERRgeneral, NT_STATUS_STACK_OVERFLOW}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_PACKAGE}, { + ERRHRD, ERRgeneral, NT_STATUS_BAD_FUNCTION_TABLE}, { + ERRDOS, 203, NT_STATUS_VARIABLE_NOT_FOUND}, { + ERRDOS, 145, NT_STATUS_DIRECTORY_NOT_EMPTY}, { + ERRHRD, ERRgeneral, NT_STATUS_FILE_CORRUPT_ERROR}, { + ERRDOS, 267, NT_STATUS_NOT_A_DIRECTORY}, { + ERRHRD, ERRgeneral, NT_STATUS_BAD_LOGON_SESSION_STATE}, { + ERRHRD, ERRgeneral, NT_STATUS_LOGON_SESSION_COLLISION}, { + ERRDOS, 206, NT_STATUS_NAME_TOO_LONG}, { + ERRDOS, 2401, NT_STATUS_FILES_OPEN}, { + ERRDOS, 2404, NT_STATUS_CONNECTION_IN_USE}, { + ERRHRD, ERRgeneral, NT_STATUS_MESSAGE_NOT_FOUND}, { + ERRDOS, ERRnoaccess, NT_STATUS_PROCESS_IS_TERMINATING}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_LOGON_TYPE}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_GUID_TRANSLATION}, { + ERRHRD, ERRgeneral, NT_STATUS_CANNOT_IMPERSONATE}, { + ERRHRD, ERRgeneral, NT_STATUS_IMAGE_ALREADY_LOADED}, { + ERRHRD, ERRgeneral, NT_STATUS_ABIOS_NOT_PRESENT}, { + ERRHRD, ERRgeneral, NT_STATUS_ABIOS_LID_NOT_EXIST}, { + ERRHRD, ERRgeneral, NT_STATUS_ABIOS_LID_ALREADY_OWNED}, { + ERRHRD, ERRgeneral, NT_STATUS_ABIOS_NOT_LID_OWNER}, { + ERRHRD, ERRgeneral, NT_STATUS_ABIOS_INVALID_COMMAND}, { + ERRHRD, ERRgeneral, NT_STATUS_ABIOS_INVALID_LID}, { + ERRHRD, ERRgeneral, NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE}, { + ERRHRD, ERRgeneral, NT_STATUS_ABIOS_INVALID_SELECTOR}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_LDT}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_LDT_SIZE}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_LDT_OFFSET}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_LDT_DESCRIPTOR}, { + ERRDOS, 193, NT_STATUS_INVALID_IMAGE_NE_FORMAT}, { + ERRHRD, ERRgeneral, NT_STATUS_RXACT_INVALID_STATE}, { + ERRHRD, ERRgeneral, NT_STATUS_RXACT_COMMIT_FAILURE}, { + ERRHRD, ERRgeneral, NT_STATUS_MAPPED_FILE_SIZE_ZERO}, { + ERRDOS, ERRnofids, NT_STATUS_TOO_MANY_OPENED_FILES}, { + ERRHRD, ERRgeneral, NT_STATUS_CANCELLED}, { + ERRDOS, ERRnoaccess, NT_STATUS_CANNOT_DELETE}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_COMPUTER_NAME}, { + ERRDOS, ERRnoaccess, NT_STATUS_FILE_DELETED}, { + ERRHRD, ERRgeneral, NT_STATUS_SPECIAL_ACCOUNT}, { + ERRHRD, ERRgeneral, NT_STATUS_SPECIAL_GROUP}, { + ERRHRD, ERRgeneral, NT_STATUS_SPECIAL_USER}, { + ERRHRD, ERRgeneral, NT_STATUS_MEMBERS_PRIMARY_GROUP}, { + ERRDOS, ERRbadfid, NT_STATUS_FILE_CLOSED}, { + ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_THREADS}, { + ERRHRD, ERRgeneral, NT_STATUS_THREAD_NOT_IN_PROCESS}, { + ERRHRD, ERRgeneral, NT_STATUS_TOKEN_ALREADY_IN_USE}, { + ERRHRD, ERRgeneral, NT_STATUS_PAGEFILE_QUOTA_EXCEEDED}, { + ERRHRD, ERRgeneral, NT_STATUS_COMMITMENT_LIMIT}, { + ERRDOS, 193, NT_STATUS_INVALID_IMAGE_LE_FORMAT}, { + ERRDOS, 193, NT_STATUS_INVALID_IMAGE_NOT_MZ}, { + ERRDOS, 193, NT_STATUS_INVALID_IMAGE_PROTECT}, { + ERRDOS, 193, NT_STATUS_INVALID_IMAGE_WIN_16}, { + ERRHRD, ERRgeneral, NT_STATUS_LOGON_SERVER_CONFLICT}, { + ERRHRD, ERRgeneral, NT_STATUS_TIME_DIFFERENCE_AT_DC}, { + ERRHRD, ERRgeneral, NT_STATUS_SYNCHRONIZATION_REQUIRED}, { + ERRDOS, 126, NT_STATUS_DLL_NOT_FOUND}, { + ERRHRD, ERRgeneral, NT_STATUS_OPEN_FAILED}, { + ERRHRD, ERRgeneral, NT_STATUS_IO_PRIVILEGE_FAILED}, { + ERRDOS, 182, NT_STATUS_ORDINAL_NOT_FOUND}, { + ERRDOS, 127, NT_STATUS_ENTRYPOINT_NOT_FOUND}, { + ERRHRD, ERRgeneral, NT_STATUS_CONTROL_C_EXIT}, { + ERRDOS, 64, NT_STATUS_LOCAL_DISCONNECT}, { + ERRDOS, 64, NT_STATUS_REMOTE_DISCONNECT}, { + ERRDOS, 51, NT_STATUS_REMOTE_RESOURCES}, { + ERRDOS, 59, NT_STATUS_LINK_FAILED}, { + ERRDOS, 59, NT_STATUS_LINK_TIMEOUT}, { + ERRDOS, 59, NT_STATUS_INVALID_CONNECTION}, { + ERRDOS, 59, NT_STATUS_INVALID_ADDRESS}, { + ERRHRD, ERRgeneral, NT_STATUS_DLL_INIT_FAILED}, { + ERRHRD, ERRgeneral, NT_STATUS_MISSING_SYSTEMFILE}, { + ERRHRD, ERRgeneral, NT_STATUS_UNHANDLED_EXCEPTION}, { + ERRHRD, ERRgeneral, NT_STATUS_APP_INIT_FAILURE}, { + ERRHRD, ERRgeneral, NT_STATUS_PAGEFILE_CREATE_FAILED}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_PAGEFILE}, { + ERRDOS, 124, NT_STATUS_INVALID_LEVEL}, { + ERRDOS, 86, NT_STATUS_WRONG_PASSWORD_CORE}, { + ERRHRD, ERRgeneral, NT_STATUS_ILLEGAL_FLOAT_CONTEXT}, { + ERRDOS, 109, NT_STATUS_PIPE_BROKEN}, { + ERRHRD, ERRgeneral, NT_STATUS_REGISTRY_CORRUPT}, { + ERRHRD, ERRgeneral, NT_STATUS_REGISTRY_IO_FAILED}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_EVENT_PAIR}, { + ERRHRD, ERRgeneral, NT_STATUS_UNRECOGNIZED_VOLUME}, { + ERRHRD, ERRgeneral, NT_STATUS_SERIAL_NO_DEVICE_INITED}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_ALIAS}, { + ERRHRD, ERRgeneral, NT_STATUS_MEMBER_NOT_IN_ALIAS}, { + ERRHRD, ERRgeneral, NT_STATUS_MEMBER_IN_ALIAS}, { + ERRHRD, ERRgeneral, NT_STATUS_ALIAS_EXISTS}, { + ERRHRD, ERRgeneral, NT_STATUS_LOGON_NOT_GRANTED}, { + ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_SECRETS}, { + ERRHRD, ERRgeneral, NT_STATUS_SECRET_TOO_LONG}, { + ERRHRD, ERRgeneral, NT_STATUS_INTERNAL_DB_ERROR}, { + ERRHRD, ERRgeneral, NT_STATUS_FULLSCREEN_MODE}, { + ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_CONTEXT_IDS}, { + ERRDOS, ERRnoaccess, NT_STATUS_LOGON_TYPE_NOT_GRANTED}, { + ERRHRD, ERRgeneral, NT_STATUS_NOT_REGISTRY_FILE}, { + ERRHRD, ERRgeneral, NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED}, { + ERRHRD, ERRgeneral, NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR}, { + ERRHRD, ERRgeneral, NT_STATUS_FT_MISSING_MEMBER}, { + ERRHRD, ERRgeneral, NT_STATUS_ILL_FORMED_SERVICE_ENTRY}, { + ERRHRD, ERRgeneral, NT_STATUS_ILLEGAL_CHARACTER}, { + ERRHRD, ERRgeneral, NT_STATUS_UNMAPPABLE_CHARACTER}, { + ERRHRD, ERRgeneral, NT_STATUS_UNDEFINED_CHARACTER}, { + ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_VOLUME}, { + ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND}, { + ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_WRONG_CYLINDER}, { + ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_UNKNOWN_ERROR}, { + ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_BAD_REGISTERS}, { + ERRHRD, ERRgeneral, NT_STATUS_DISK_RECALIBRATE_FAILED}, { + ERRHRD, ERRgeneral, NT_STATUS_DISK_OPERATION_FAILED}, { + ERRHRD, ERRgeneral, NT_STATUS_DISK_RESET_FAILED}, { + ERRHRD, ERRgeneral, NT_STATUS_SHARED_IRQ_BUSY}, { + ERRHRD, ERRgeneral, NT_STATUS_FT_ORPHANING}, { + ERRHRD, ERRgeneral, NT_STATUS_BIOS_FAILED_TO_CONNECT_INTERRUPT}, { + ERRHRD, ERRgeneral, NT_STATUS_PARTITION_FAILURE}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_BLOCK_LENGTH}, { + ERRHRD, ERRgeneral, NT_STATUS_DEVICE_NOT_PARTITIONED}, { + ERRHRD, ERRgeneral, NT_STATUS_UNABLE_TO_LOCK_MEDIA}, { + ERRHRD, ERRgeneral, NT_STATUS_UNABLE_TO_UNLOAD_MEDIA}, { + ERRHRD, ERRgeneral, NT_STATUS_EOM_OVERFLOW}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_MEDIA}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_MEMBER}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_MEMBER}, { + ERRHRD, ERRgeneral, NT_STATUS_KEY_DELETED}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_LOG_SPACE}, { + ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_SIDS}, { + ERRHRD, ERRgeneral, NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED}, { + ERRHRD, ERRgeneral, NT_STATUS_KEY_HAS_CHILDREN}, { + ERRHRD, ERRgeneral, NT_STATUS_CHILD_MUST_BE_VOLATILE}, { + ERRDOS, 87, NT_STATUS_DEVICE_CONFIGURATION_ERROR}, { + ERRHRD, ERRgeneral, NT_STATUS_DRIVER_INTERNAL_ERROR}, { + ERRDOS, 22, NT_STATUS_INVALID_DEVICE_STATE}, { + ERRHRD, ERRgeneral, NT_STATUS_IO_DEVICE_ERROR}, { + ERRHRD, ERRgeneral, NT_STATUS_DEVICE_PROTOCOL_ERROR}, { + ERRHRD, ERRgeneral, NT_STATUS_BACKUP_CONTROLLER}, { + ERRHRD, ERRgeneral, NT_STATUS_LOG_FILE_FULL}, { + ERRDOS, 19, NT_STATUS_TOO_LATE}, { + ERRDOS, ERRnoaccess, NT_STATUS_NO_TRUST_LSA_SECRET}, +/* { This NT error code was 'sqashed' + from NT_STATUS_NO_TRUST_SAM_ACCOUNT to + NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE during the session setup } */ + { + ERRDOS, ERRnoaccess, NT_STATUS_NO_TRUST_SAM_ACCOUNT}, { + ERRDOS, ERRnoaccess, NT_STATUS_TRUSTED_DOMAIN_FAILURE}, { + ERRDOS, ERRnoaccess, NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE}, { + ERRHRD, ERRgeneral, NT_STATUS_EVENTLOG_FILE_CORRUPT}, { + ERRHRD, ERRgeneral, NT_STATUS_EVENTLOG_CANT_START}, { + ERRDOS, ERRnoaccess, NT_STATUS_TRUST_FAILURE}, { + ERRHRD, ERRgeneral, NT_STATUS_MUTANT_LIMIT_EXCEEDED}, { + ERRDOS, ERRnetlogonNotStarted, NT_STATUS_NETLOGON_NOT_STARTED}, { + ERRSRV, ERRaccountexpired, NT_STATUS_ACCOUNT_EXPIRED}, { + ERRHRD, ERRgeneral, NT_STATUS_POSSIBLE_DEADLOCK}, { + ERRHRD, ERRgeneral, NT_STATUS_NETWORK_CREDENTIAL_CONFLICT}, { + ERRHRD, ERRgeneral, NT_STATUS_REMOTE_SESSION_LIMIT}, { + ERRHRD, ERRgeneral, NT_STATUS_EVENTLOG_FILE_CHANGED}, { + ERRDOS, ERRnoaccess, NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT}, { + ERRDOS, ERRnoaccess, NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT}, { + ERRDOS, ERRnoaccess, NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT}, +/* { This NT error code was 'sqashed' + from NT_STATUS_DOMAIN_TRUST_INCONSISTENT to NT_STATUS_LOGON_FAILURE + during the session setup } */ + { + ERRDOS, ERRnoaccess, NT_STATUS_DOMAIN_TRUST_INCONSISTENT}, { + ERRHRD, ERRgeneral, NT_STATUS_FS_DRIVER_REQUIRED}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_USER_SESSION_KEY}, { + ERRDOS, 59, NT_STATUS_USER_SESSION_DELETED}, { + ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_LANG_NOT_FOUND}, { + ERRDOS, ERRnoresource, NT_STATUS_INSUFF_SERVER_RESOURCES}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_BUFFER_SIZE}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_ADDRESS_COMPONENT}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_ADDRESS_WILDCARD}, { + ERRDOS, 68, NT_STATUS_TOO_MANY_ADDRESSES}, { + ERRDOS, 52, NT_STATUS_ADDRESS_ALREADY_EXISTS}, { + ERRDOS, 64, NT_STATUS_ADDRESS_CLOSED}, { + ERRDOS, 64, NT_STATUS_CONNECTION_DISCONNECTED}, { + ERRDOS, 64, NT_STATUS_CONNECTION_RESET}, { + ERRDOS, 68, NT_STATUS_TOO_MANY_NODES}, { + ERRDOS, 59, NT_STATUS_TRANSACTION_ABORTED}, { + ERRDOS, 59, NT_STATUS_TRANSACTION_TIMED_OUT}, { + ERRDOS, 59, NT_STATUS_TRANSACTION_NO_RELEASE}, { + ERRDOS, 59, NT_STATUS_TRANSACTION_NO_MATCH}, { + ERRDOS, 59, NT_STATUS_TRANSACTION_RESPONDED}, { + ERRDOS, 59, NT_STATUS_TRANSACTION_INVALID_ID}, { + ERRDOS, 59, NT_STATUS_TRANSACTION_INVALID_TYPE}, { + ERRDOS, ERRunsup, NT_STATUS_NOT_SERVER_SESSION}, { + ERRDOS, ERRunsup, NT_STATUS_NOT_CLIENT_SESSION}, { + ERRHRD, ERRgeneral, NT_STATUS_CANNOT_LOAD_REGISTRY_FILE}, { + ERRHRD, ERRgeneral, NT_STATUS_DEBUG_ATTACH_FAILED}, { + ERRHRD, ERRgeneral, NT_STATUS_SYSTEM_PROCESS_TERMINATED}, { + ERRHRD, ERRgeneral, NT_STATUS_DATA_NOT_ACCEPTED}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_BROWSER_SERVERS_FOUND}, { + ERRHRD, ERRgeneral, NT_STATUS_VDM_HARD_ERROR}, { + ERRHRD, ERRgeneral, NT_STATUS_DRIVER_CANCEL_TIMEOUT}, { + ERRHRD, ERRgeneral, NT_STATUS_REPLY_MESSAGE_MISMATCH}, { + ERRHRD, ERRgeneral, NT_STATUS_MAPPED_ALIGNMENT}, { + ERRDOS, 193, NT_STATUS_IMAGE_CHECKSUM_MISMATCH}, { + ERRHRD, ERRgeneral, NT_STATUS_LOST_WRITEBEHIND_DATA}, { + ERRHRD, ERRgeneral, NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID}, { + ERRSRV, ERRpasswordExpired, NT_STATUS_PASSWORD_MUST_CHANGE}, { + ERRHRD, ERRgeneral, NT_STATUS_NOT_FOUND}, { + ERRHRD, ERRgeneral, NT_STATUS_NOT_TINY_STREAM}, { + ERRHRD, ERRgeneral, NT_STATUS_RECOVERY_FAILURE}, { + ERRHRD, ERRgeneral, NT_STATUS_STACK_OVERFLOW_READ}, { + ERRHRD, ERRgeneral, NT_STATUS_FAIL_CHECK}, { + ERRHRD, ERRgeneral, NT_STATUS_DUPLICATE_OBJECTID}, { + ERRHRD, ERRgeneral, NT_STATUS_OBJECTID_EXISTS}, { + ERRHRD, ERRgeneral, NT_STATUS_CONVERT_TO_LARGE}, { + ERRHRD, ERRgeneral, NT_STATUS_RETRY}, { + ERRHRD, ERRgeneral, NT_STATUS_FOUND_OUT_OF_SCOPE}, { + ERRHRD, ERRgeneral, NT_STATUS_ALLOCATE_BUCKET}, { + ERRHRD, ERRgeneral, NT_STATUS_PROPSET_NOT_FOUND}, { + ERRHRD, ERRgeneral, NT_STATUS_MARSHALL_OVERFLOW}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_VARIANT}, { + ERRHRD, ERRgeneral, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND}, { + ERRDOS, ERRnoaccess, NT_STATUS_ACCOUNT_LOCKED_OUT}, { + ERRDOS, ERRbadfid, NT_STATUS_HANDLE_NOT_CLOSABLE}, { + ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_REFUSED}, { + ERRHRD, ERRgeneral, NT_STATUS_GRACEFUL_DISCONNECT}, { + ERRHRD, ERRgeneral, NT_STATUS_ADDRESS_ALREADY_ASSOCIATED}, { + ERRHRD, ERRgeneral, NT_STATUS_ADDRESS_NOT_ASSOCIATED}, { + ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_INVALID}, { + ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_ACTIVE}, { + ERRHRD, ERRgeneral, NT_STATUS_NETWORK_UNREACHABLE}, { + ERRHRD, ERRgeneral, NT_STATUS_HOST_UNREACHABLE}, { + ERRHRD, ERRgeneral, NT_STATUS_PROTOCOL_UNREACHABLE}, { + ERRHRD, ERRgeneral, NT_STATUS_PORT_UNREACHABLE}, { + ERRHRD, ERRgeneral, NT_STATUS_REQUEST_ABORTED}, { + ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_ABORTED}, { + ERRHRD, ERRgeneral, NT_STATUS_BAD_COMPRESSION_BUFFER}, { + ERRHRD, ERRgeneral, NT_STATUS_USER_MAPPED_FILE}, { + ERRHRD, ERRgeneral, NT_STATUS_AUDIT_FAILED}, { + ERRHRD, ERRgeneral, NT_STATUS_TIMER_RESOLUTION_NOT_SET}, { + ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_COUNT_LIMIT}, { + ERRHRD, ERRgeneral, NT_STATUS_LOGIN_TIME_RESTRICTION}, { + ERRHRD, ERRgeneral, NT_STATUS_LOGIN_WKSTA_RESTRICTION}, { + ERRDOS, 193, NT_STATUS_IMAGE_MP_UP_MISMATCH}, { + ERRHRD, ERRgeneral, NT_STATUS_INSUFFICIENT_LOGON_INFO}, { + ERRHRD, ERRgeneral, NT_STATUS_BAD_DLL_ENTRYPOINT}, { + ERRHRD, ERRgeneral, NT_STATUS_BAD_SERVICE_ENTRYPOINT}, { + ERRHRD, ERRgeneral, NT_STATUS_LPC_REPLY_LOST}, { + ERRHRD, ERRgeneral, NT_STATUS_IP_ADDRESS_CONFLICT1}, { + ERRHRD, ERRgeneral, NT_STATUS_IP_ADDRESS_CONFLICT2}, { + ERRHRD, ERRgeneral, NT_STATUS_REGISTRY_QUOTA_LIMIT}, { + ERRSRV, 3, NT_STATUS_PATH_NOT_COVERED}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_CALLBACK_ACTIVE}, { + ERRHRD, ERRgeneral, NT_STATUS_LICENSE_QUOTA_EXCEEDED}, { + ERRHRD, ERRgeneral, NT_STATUS_PWD_TOO_SHORT}, { + ERRHRD, ERRgeneral, NT_STATUS_PWD_TOO_RECENT}, { + ERRHRD, ERRgeneral, NT_STATUS_PWD_HISTORY_CONFLICT}, { + ERRHRD, ERRgeneral, NT_STATUS_PLUGPLAY_NO_DEVICE}, { + ERRHRD, ERRgeneral, NT_STATUS_UNSUPPORTED_COMPRESSION}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_HW_PROFILE}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH}, { + ERRDOS, 182, NT_STATUS_DRIVER_ORDINAL_NOT_FOUND}, { + ERRDOS, 127, NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND}, { + ERRDOS, 288, NT_STATUS_RESOURCE_NOT_OWNED}, { + ERRDOS, ErrTooManyLinks, NT_STATUS_TOO_MANY_LINKS}, { + ERRHRD, ERRgeneral, NT_STATUS_QUOTA_LIST_INCONSISTENT}, { + ERRHRD, ERRgeneral, NT_STATUS_FILE_IS_OFFLINE}, { + ERRDOS, 21, NT_STATUS_VOLUME_DISMOUNTED}, { + ERRDOS, 161, NT_STATUS_DIRECTORY_IS_A_REPARSE_POINT}, { + ERRDOS, ERRnoaccess, NT_STATUS_ENCRYPTION_FAILED}, { + ERRDOS, ERRnoaccess, NT_STATUS_DECRYPTION_FAILED}, { + ERRHRD, ERRgeneral, NT_STATUS_RANGE_NOT_FOUND}, { + ERRDOS, ERRnoaccess, NT_STATUS_NO_RECOVERY_POLICY}, { + ERRDOS, ERRnoaccess, NT_STATUS_NO_EFS}, { + ERRDOS, ERRnoaccess, NT_STATUS_WRONG_EFS}, { + ERRDOS, ERRnoaccess, NT_STATUS_NO_USER_KEYS}, { + ERRDOS, ERRbadfunc, NT_STATUS_VOLUME_NOT_UPGRADED}, { + ERRDOS, ERRsymlink, NT_STATUS_STOPPED_ON_SYMLINK}, { + ERRDOS, ERRunknownlevel, NT_STATUS_OS2_INVALID_LEVEL}, { + 0, 0, 0 } +}; + +/***************************************************************************** + Print an error message from the status code + *****************************************************************************/ +static void +cifs_print_status(__u32 status_code) +{ + int idx = 0; + + while (nt_errs[idx].nt_errstr != NULL) { + if (nt_errs[idx].nt_errcode == status_code) { + pr_notice("Status code returned 0x%08x %s\n", + status_code, nt_errs[idx].nt_errstr); + return; + } + idx++; + } + return; +} + + +static void +ntstatus_to_dos(__u32 ntstatus, __u8 *eclass, __u16 *ecode) +{ + int i; + if (ntstatus == 0) { + *eclass = 0; + *ecode = 0; + return; + } + for (i = 0; ntstatus_to_dos_map[i].ntstatus; i++) { + if (ntstatus == ntstatus_to_dos_map[i].ntstatus) { + *eclass = ntstatus_to_dos_map[i].dos_class; + *ecode = ntstatus_to_dos_map[i].dos_code; + return; + } + } + *eclass = ERRHRD; + *ecode = ERRgeneral; +} + +int +map_smb_to_linux_error(char *buf, bool logErr) +{ + struct smb_hdr *smb = (struct smb_hdr *)buf; + unsigned int i; + int rc = -EIO; /* if transport error smb error may not be set */ + __u8 smberrclass; + __u16 smberrcode; + + /* BB if NT Status codes - map NT BB */ + + /* old style smb error codes */ + if (smb->Status.CifsError == 0) + return 0; + + if (smb->Flags2 & SMBFLG2_ERR_STATUS) { + /* translate the newer STATUS codes to old style SMB errors + * and then to POSIX errors */ + __u32 err = le32_to_cpu(smb->Status.CifsError); + if (logErr && (err != (NT_STATUS_MORE_PROCESSING_REQUIRED))) + cifs_print_status(err); + else if (cifsFYI & CIFS_RC) + cifs_print_status(err); + ntstatus_to_dos(err, &smberrclass, &smberrcode); + } else { + smberrclass = smb->Status.DosError.ErrorClass; + smberrcode = le16_to_cpu(smb->Status.DosError.Error); + } + + /* old style errors */ + + /* DOS class smb error codes - map DOS */ + if (smberrclass == ERRDOS) { + /* 1 byte field no need to byte reverse */ + for (i = 0; + i < + sizeof(mapping_table_ERRDOS) / + sizeof(struct smb_to_posix_error); i++) { + if (mapping_table_ERRDOS[i].smb_err == 0) + break; + else if (mapping_table_ERRDOS[i].smb_err == + smberrcode) { + rc = mapping_table_ERRDOS[i].posix_code; + break; + } + /* else try next error mapping one to see if match */ + } + } else if (smberrclass == ERRSRV) { + /* server class of error codes */ + for (i = 0; + i < + sizeof(mapping_table_ERRSRV) / + sizeof(struct smb_to_posix_error); i++) { + if (mapping_table_ERRSRV[i].smb_err == 0) + break; + else if (mapping_table_ERRSRV[i].smb_err == + smberrcode) { + rc = mapping_table_ERRSRV[i].posix_code; + break; + } + /* else try next error mapping to see if match */ + } + } + /* else ERRHRD class errors or junk - return EIO */ + + /* special cases for NT status codes which cannot be translated to DOS codes */ + if (smb->Flags2 & SMBFLG2_ERR_STATUS) { + __u32 err = le32_to_cpu(smb->Status.CifsError); + if (err == (NT_STATUS_NOT_A_REPARSE_POINT)) + rc = -ENODATA; + else if (err == (NT_STATUS_PRIVILEGE_NOT_HELD)) + rc = -EPERM; + } + + cifs_dbg(FYI, "Mapping smb error code 0x%x to POSIX err %d\n", + le32_to_cpu(smb->Status.CifsError), rc); + + /* generic corrective action e.g. reconnect SMB session on + * ERRbaduid could be added */ + + if (rc == -EIO) + smb_EIO2(smb_eio_trace_smb1_received_error, + le32_to_cpu(smb->Status.CifsError), + le16_to_cpu(smb->Flags2)); + return rc; +} + +int +map_and_check_smb_error(struct TCP_Server_Info *server, + struct mid_q_entry *mid, bool logErr) +{ + int rc; + struct smb_hdr *smb = (struct smb_hdr *)mid->resp_buf; + + rc = map_smb_to_linux_error((char *)smb, logErr); + if (rc == -EACCES && !(smb->Flags2 & SMBFLG2_ERR_STATUS)) { + /* possible ERRBaduid */ + __u8 class = smb->Status.DosError.ErrorClass; + __u16 code = le16_to_cpu(smb->Status.DosError.Error); + + /* switch can be used to handle different errors */ + if (class == ERRSRV && code == ERRbaduid) { + cifs_dbg(FYI, "Server returned 0x%x, reconnecting session...\n", + code); + cifs_signal_cifsd_for_reconnect(server, false); + } + } + + return rc; +} diff --git a/fs/smb/client/smb1misc.c b/fs/smb/client/smb1misc.c new file mode 100644 index 000000000000..ba56023010d8 --- /dev/null +++ b/fs/smb/client/smb1misc.c @@ -0,0 +1,189 @@ +// SPDX-License-Identifier: LGPL-2.1 +/* + * + * Copyright (C) International Business Machines Corp., 2002,2008 + * Author(s): Steve French (sfrench@us.ibm.com) + * + */ + +#include "smb1proto.h" +#include "smberr.h" +#include "nterr.h" +#include "cifs_debug.h" + +/* NB: MID can not be set if treeCon not passed in, in that + case it is responsibility of caller to set the mid */ +unsigned int +header_assemble(struct smb_hdr *buffer, char smb_command, + const struct cifs_tcon *treeCon, int word_count + /* length of fixed section (word count) in two byte units */) +{ + unsigned int in_len; + char *temp = (char *) buffer; + + memset(temp, 0, 256); /* bigger than MAX_CIFS_HDR_SIZE */ + + in_len = (2 * word_count) + sizeof(struct smb_hdr) + + 2 /* for bcc field itself */; + + buffer->Protocol[0] = 0xFF; + buffer->Protocol[1] = 'S'; + buffer->Protocol[2] = 'M'; + buffer->Protocol[3] = 'B'; + buffer->Command = smb_command; + buffer->Flags = 0x00; /* case sensitive */ + buffer->Flags2 = SMBFLG2_KNOWS_LONG_NAMES; + buffer->Pid = cpu_to_le16((__u16)current->tgid); + buffer->PidHigh = cpu_to_le16((__u16)(current->tgid >> 16)); + if (treeCon) { + buffer->Tid = treeCon->tid; + if (treeCon->ses) { + if (treeCon->ses->capabilities & CAP_UNICODE) + buffer->Flags2 |= SMBFLG2_UNICODE; + if (treeCon->ses->capabilities & CAP_STATUS32) + buffer->Flags2 |= SMBFLG2_ERR_STATUS; + + /* Uid is not converted */ + buffer->Uid = treeCon->ses->Suid; + if (treeCon->ses->server) + buffer->Mid = get_next_mid(treeCon->ses->server); + } + if (treeCon->Flags & SMB_SHARE_IS_IN_DFS) + buffer->Flags2 |= SMBFLG2_DFS; + if (treeCon->nocase) + buffer->Flags |= SMBFLG_CASELESS; + if ((treeCon->ses) && (treeCon->ses->server)) + if (treeCon->ses->server->sign) + buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE; + } + +/* endian conversion of flags is now done just before sending */ + buffer->WordCount = (char) word_count; + return in_len; +} + +bool +is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv) +{ + struct smb_hdr *buf = (struct smb_hdr *)buffer; + struct smb_com_lock_req *pSMB = (struct smb_com_lock_req *)buf; + struct TCP_Server_Info *pserver; + struct cifs_ses *ses; + struct cifs_tcon *tcon; + struct cifsInodeInfo *pCifsInode; + struct cifsFileInfo *netfile; + + cifs_dbg(FYI, "Checking for oplock break or dnotify response\n"); + if ((pSMB->hdr.Command == SMB_COM_NT_TRANSACT) && + (pSMB->hdr.Flags & SMBFLG_RESPONSE)) { + struct smb_com_transaction_change_notify_rsp *pSMBr = + (struct smb_com_transaction_change_notify_rsp *)buf; + struct file_notify_information *pnotify; + __u32 data_offset = 0; + size_t len = srv->total_read - srv->pdu_size; + + if (get_bcc(buf) > sizeof(struct file_notify_information)) { + data_offset = le32_to_cpu(pSMBr->DataOffset); + + if (data_offset > + len - sizeof(struct file_notify_information)) { + cifs_dbg(FYI, "Invalid data_offset %u\n", + data_offset); + return true; + } + pnotify = (struct file_notify_information *) + ((char *)&pSMBr->hdr.Protocol + data_offset); + cifs_dbg(FYI, "dnotify on %s Action: 0x%x\n", + pnotify->FileName, pnotify->Action); + /* cifs_dump_mem("Rcvd notify Data: ",buf, + sizeof(struct smb_hdr)+60); */ + return true; + } + if (pSMBr->hdr.Status.CifsError) { + cifs_dbg(FYI, "notify err 0x%x\n", + pSMBr->hdr.Status.CifsError); + return true; + } + return false; + } + if (pSMB->hdr.Command != SMB_COM_LOCKING_ANDX) + return false; + if (pSMB->hdr.Flags & SMBFLG_RESPONSE) { + /* no sense logging error on invalid handle on oplock + break - harmless race between close request and oplock + break response is expected from time to time writing out + large dirty files cached on the client */ + if ((NT_STATUS_INVALID_HANDLE) == + le32_to_cpu(pSMB->hdr.Status.CifsError)) { + cifs_dbg(FYI, "Invalid handle on oplock break\n"); + return true; + } else if (ERRbadfid == + le16_to_cpu(pSMB->hdr.Status.DosError.Error)) { + return true; + } else { + return false; /* on valid oplock brk we get "request" */ + } + } + if (pSMB->hdr.WordCount != 8) + return false; + + cifs_dbg(FYI, "oplock type 0x%x level 0x%x\n", + pSMB->LockType, pSMB->OplockLevel); + if (!(pSMB->LockType & LOCKING_ANDX_OPLOCK_RELEASE)) + return false; + + /* If server is a channel, select the primary channel */ + pserver = SERVER_IS_CHAN(srv) ? srv->primary_server : srv; + + /* look up tcon based on tid & uid */ + spin_lock(&cifs_tcp_ses_lock); + list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) { + if (cifs_ses_exiting(ses)) + continue; + list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { + if (tcon->tid != buf->Tid) + continue; + + cifs_stats_inc(&tcon->stats.cifs_stats.num_oplock_brks); + spin_lock(&tcon->open_file_lock); + list_for_each_entry(netfile, &tcon->openFileList, tlist) { + if (pSMB->Fid != netfile->fid.netfid) + continue; + + cifs_dbg(FYI, "file id match, oplock break\n"); + pCifsInode = CIFS_I(d_inode(netfile->dentry)); + + set_bit(CIFS_INODE_PENDING_OPLOCK_BREAK, + &pCifsInode->flags); + + netfile->oplock_epoch = 0; + netfile->oplock_level = pSMB->OplockLevel; + netfile->oplock_break_cancelled = false; + cifs_queue_oplock_break(netfile); + + spin_unlock(&tcon->open_file_lock); + spin_unlock(&cifs_tcp_ses_lock); + return true; + } + spin_unlock(&tcon->open_file_lock); + spin_unlock(&cifs_tcp_ses_lock); + cifs_dbg(FYI, "No matching file for oplock break\n"); + return true; + } + } + spin_unlock(&cifs_tcp_ses_lock); + cifs_dbg(FYI, "Can not process oplock break for non-existent connection\n"); + return true; +} + +/* + * calculate the size of the SMB message based on the fixed header + * portion, the number of word parameters and the data portion of the message + */ +unsigned int +smbCalcSize(void *buf) +{ + struct smb_hdr *ptr = buf; + return (sizeof(struct smb_hdr) + (2 * ptr->WordCount) + + 2 /* size of the bcc field */ + get_bcc(ptr)); +} diff --git a/fs/smb/client/smb1ops.c b/fs/smb/client/smb1ops.c index 9729b56bd9d4..9c3b97d2a20a 100644 --- a/fs/smb/client/smb1ops.c +++ b/fs/smb/client/smb1ops.c @@ -12,13 +12,119 @@ #include "cifsglob.h" #include "cifsproto.h" #include "cifs_debug.h" -#include "cifspdu.h" #include "cifs_unicode.h" #include "fs_context.h" #include "nterr.h" #include "smberr.h" #include "reparse.h" +void reset_cifs_unix_caps(unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx) +{ + /* + * If we are reconnecting then should we check to see if + * any requested capabilities changed locally e.g. via + * remount but we can not do much about it here + * if they have (even if we could detect it by the following) + * Perhaps we could add a backpointer to array of sb from tcon + * or if we change to make all sb to same share the same + * sb as NFS - then we only have one backpointer to sb. + * What if we wanted to mount the server share twice once with + * and once without posixacls or posix paths? + */ + __u64 saved_cap = le64_to_cpu(tcon->fsUnixInfo.Capability); + + if (ctx && ctx->no_linux_ext) { + tcon->fsUnixInfo.Capability = 0; + tcon->unix_ext = 0; /* Unix Extensions disabled */ + cifs_dbg(FYI, "Linux protocol extensions disabled\n"); + return; + } else if (ctx) + tcon->unix_ext = 1; /* Unix Extensions supported */ + + if (!tcon->unix_ext) { + cifs_dbg(FYI, "Unix extensions disabled so not set on reconnect\n"); + return; + } + + if (!CIFSSMBQFSUnixInfo(xid, tcon)) { + __u64 cap = le64_to_cpu(tcon->fsUnixInfo.Capability); + + cifs_dbg(FYI, "unix caps which server supports %lld\n", cap); + /* + * check for reconnect case in which we do not + * want to change the mount behavior if we can avoid it + */ + if (ctx == NULL) { + /* + * turn off POSIX ACL and PATHNAMES if not set + * originally at mount time + */ + if ((saved_cap & CIFS_UNIX_POSIX_ACL_CAP) == 0) + cap &= ~CIFS_UNIX_POSIX_ACL_CAP; + if ((saved_cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) == 0) { + if (cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) + cifs_dbg(VFS, "POSIXPATH support change\n"); + cap &= ~CIFS_UNIX_POSIX_PATHNAMES_CAP; + } else if ((cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) == 0) { + cifs_dbg(VFS, "possible reconnect error\n"); + cifs_dbg(VFS, "server disabled POSIX path support\n"); + } + } + + if (cap & CIFS_UNIX_TRANSPORT_ENCRYPTION_MANDATORY_CAP) + cifs_dbg(VFS, "per-share encryption not supported yet\n"); + + cap &= CIFS_UNIX_CAP_MASK; + if (ctx && ctx->no_psx_acl) + cap &= ~CIFS_UNIX_POSIX_ACL_CAP; + else if (CIFS_UNIX_POSIX_ACL_CAP & cap) { + cifs_dbg(FYI, "negotiated posix acl support\n"); + if (cifs_sb) + cifs_sb->mnt_cifs_flags |= + CIFS_MOUNT_POSIXACL; + } + + if (ctx && ctx->posix_paths == 0) + cap &= ~CIFS_UNIX_POSIX_PATHNAMES_CAP; + else if (cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) { + cifs_dbg(FYI, "negotiate posix pathnames\n"); + if (cifs_sb) + cifs_sb->mnt_cifs_flags |= + CIFS_MOUNT_POSIX_PATHS; + } + + cifs_dbg(FYI, "Negotiate caps 0x%x\n", (int)cap); +#ifdef CONFIG_CIFS_DEBUG2 + if (cap & CIFS_UNIX_FCNTL_CAP) + cifs_dbg(FYI, "FCNTL cap\n"); + if (cap & CIFS_UNIX_EXTATTR_CAP) + cifs_dbg(FYI, "EXTATTR cap\n"); + if (cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) + cifs_dbg(FYI, "POSIX path cap\n"); + if (cap & CIFS_UNIX_XATTR_CAP) + cifs_dbg(FYI, "XATTR cap\n"); + if (cap & CIFS_UNIX_POSIX_ACL_CAP) + cifs_dbg(FYI, "POSIX ACL cap\n"); + if (cap & CIFS_UNIX_LARGE_READ_CAP) + cifs_dbg(FYI, "very large read cap\n"); + if (cap & CIFS_UNIX_LARGE_WRITE_CAP) + cifs_dbg(FYI, "very large write cap\n"); + if (cap & CIFS_UNIX_TRANSPORT_ENCRYPTION_CAP) + cifs_dbg(FYI, "transport encryption cap\n"); + if (cap & CIFS_UNIX_TRANSPORT_ENCRYPTION_MANDATORY_CAP) + cifs_dbg(FYI, "mandatory transport encryption cap\n"); +#endif /* CIFS_DEBUG2 */ + if (CIFSSMBSetFSUnixInfo(xid, tcon, cap)) { + if (ctx == NULL) + cifs_dbg(FYI, "resetting capabilities failed\n"); + else + cifs_dbg(VFS, "Negotiating Unix capabilities with the server failed. Consider mounting with the Unix Extensions disabled if problems are found by specifying the nounix mount option.\n"); + + } + } +} + /* * An NT cancel request header looks just like the original request except: * @@ -284,146 +390,6 @@ cifs_get_next_mid(struct TCP_Server_Info *server) return mid; } -/* - return codes: - 0 not a transact2, or all data present - >0 transact2 with that much data missing - -EINVAL invalid transact2 - */ -static int -check2ndT2(char *buf) -{ - struct smb_hdr *pSMB = (struct smb_hdr *)buf; - struct smb_t2_rsp *pSMBt; - int remaining; - __u16 total_data_size, data_in_this_rsp; - - if (pSMB->Command != SMB_COM_TRANSACTION2) - return 0; - - /* check for plausible wct, bcc and t2 data and parm sizes */ - /* check for parm and data offset going beyond end of smb */ - if (pSMB->WordCount != 10) { /* coalesce_t2 depends on this */ - cifs_dbg(FYI, "Invalid transact2 word count\n"); - return -EINVAL; - } - - pSMBt = (struct smb_t2_rsp *)pSMB; - - total_data_size = get_unaligned_le16(&pSMBt->t2_rsp.TotalDataCount); - data_in_this_rsp = get_unaligned_le16(&pSMBt->t2_rsp.DataCount); - - if (total_data_size == data_in_this_rsp) - return 0; - else if (total_data_size < data_in_this_rsp) { - cifs_dbg(FYI, "total data %d smaller than data in frame %d\n", - total_data_size, data_in_this_rsp); - return -EINVAL; - } - - remaining = total_data_size - data_in_this_rsp; - - cifs_dbg(FYI, "missing %d bytes from transact2, check next response\n", - remaining); - if (total_data_size > CIFSMaxBufSize) { - cifs_dbg(VFS, "TotalDataSize %d is over maximum buffer %d\n", - total_data_size, CIFSMaxBufSize); - return -EINVAL; - } - return remaining; -} - -static int -coalesce_t2(char *second_buf, struct smb_hdr *target_hdr, unsigned int *pdu_len) -{ - struct smb_t2_rsp *pSMBs = (struct smb_t2_rsp *)second_buf; - struct smb_t2_rsp *pSMBt = (struct smb_t2_rsp *)target_hdr; - char *data_area_of_tgt; - char *data_area_of_src; - int remaining; - unsigned int byte_count, total_in_tgt; - __u16 tgt_total_cnt, src_total_cnt, total_in_src; - - src_total_cnt = get_unaligned_le16(&pSMBs->t2_rsp.TotalDataCount); - tgt_total_cnt = get_unaligned_le16(&pSMBt->t2_rsp.TotalDataCount); - - if (tgt_total_cnt != src_total_cnt) - cifs_dbg(FYI, "total data count of primary and secondary t2 differ source=%hu target=%hu\n", - src_total_cnt, tgt_total_cnt); - - total_in_tgt = get_unaligned_le16(&pSMBt->t2_rsp.DataCount); - - remaining = tgt_total_cnt - total_in_tgt; - - if (remaining < 0) { - cifs_dbg(FYI, "Server sent too much data. tgt_total_cnt=%hu total_in_tgt=%u\n", - tgt_total_cnt, total_in_tgt); - return -EPROTO; - } - - if (remaining == 0) { - /* nothing to do, ignore */ - cifs_dbg(FYI, "no more data remains\n"); - return 0; - } - - total_in_src = get_unaligned_le16(&pSMBs->t2_rsp.DataCount); - if (remaining < total_in_src) - cifs_dbg(FYI, "transact2 2nd response contains too much data\n"); - - /* find end of first SMB data area */ - data_area_of_tgt = (char *)&pSMBt->hdr.Protocol + - get_unaligned_le16(&pSMBt->t2_rsp.DataOffset); - - /* validate target area */ - data_area_of_src = (char *)&pSMBs->hdr.Protocol + - get_unaligned_le16(&pSMBs->t2_rsp.DataOffset); - - data_area_of_tgt += total_in_tgt; - - total_in_tgt += total_in_src; - /* is the result too big for the field? */ - if (total_in_tgt > USHRT_MAX) { - cifs_dbg(FYI, "coalesced DataCount too large (%u)\n", - total_in_tgt); - return -EPROTO; - } - put_unaligned_le16(total_in_tgt, &pSMBt->t2_rsp.DataCount); - - /* fix up the BCC */ - byte_count = get_bcc(target_hdr); - byte_count += total_in_src; - /* is the result too big for the field? */ - if (byte_count > USHRT_MAX) { - cifs_dbg(FYI, "coalesced BCC too large (%u)\n", byte_count); - return -EPROTO; - } - put_bcc(byte_count, target_hdr); - - byte_count = *pdu_len; - byte_count += total_in_src; - /* don't allow buffer to overflow */ - if (byte_count > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) { - cifs_dbg(FYI, "coalesced BCC exceeds buffer size (%u)\n", - byte_count); - return -ENOBUFS; - } - *pdu_len = byte_count; - - /* copy second buffer into end of first buffer */ - memcpy(data_area_of_tgt, data_area_of_src, total_in_src); - - if (remaining != total_in_src) { - /* more responses to go */ - cifs_dbg(FYI, "waiting for more secondary responses\n"); - return 1; - } - - /* we are done */ - cifs_dbg(FYI, "found the last secondary response\n"); - return 0; -} - static void cifs_downgrade_oplock(struct TCP_Server_Info *server, struct cifsInodeInfo *cinode, __u32 oplock, @@ -433,37 +399,6 @@ cifs_downgrade_oplock(struct TCP_Server_Info *server, } static bool -cifs_check_trans2(struct mid_q_entry *mid, struct TCP_Server_Info *server, - char *buf, int malformed) -{ - if (malformed) - return false; - if (check2ndT2(buf) <= 0) - return false; - mid->multiRsp = true; - if (mid->resp_buf) { - /* merge response - fix up 1st*/ - malformed = coalesce_t2(buf, mid->resp_buf, &mid->response_pdu_len); - if (malformed > 0) - return true; - /* All parts received or packet is malformed. */ - mid->multiEnd = true; - dequeue_mid(server, mid, malformed); - return true; - } - if (!server->large_buf) { - /*FIXME: switch to already allocated largebuf?*/ - cifs_dbg(VFS, "1st trans2 resp needs bigbuf\n"); - } else { - /* Have first buffer */ - mid->resp_buf = buf; - mid->large_buf = true; - server->bigbuf = NULL; - } - return true; -} - -static bool cifs_need_neg(struct TCP_Server_Info *server) { return server->maxBuf == 0; diff --git a/fs/smb/client/smb1pdu.h b/fs/smb/client/smb1pdu.h new file mode 100644 index 000000000000..97f7e1244a8b --- /dev/null +++ b/fs/smb/client/smb1pdu.h @@ -0,0 +1,2354 @@ +/* SPDX-License-Identifier: LGPL-2.1 */ +/* + * + * Copyright (c) International Business Machines Corp., 2002,2009 + * Author(s): Steve French (sfrench@us.ibm.com) + * + */ + +#ifndef _SMB1PDU_H +#define _SMB1PDU_H + +#include "../common/smb1pdu.h" + +#define CIFS_PROT 0 +#define POSIX_PROT (CIFS_PROT+1) +#define BAD_PROT 0xFFFF + +/* SMB command codes: + * See MS-CIFS 2.2.2.1 + * Note some commands have minimal (wct=0,bcc=0), or uninteresting, responses + * (ie which include no useful data other than the SMB error code itself). + * This can allow us to avoid response buffer allocations and copy in some cases + */ +#define SMB_COM_CREATE_DIRECTORY 0x00 /* trivial response */ +#define SMB_COM_DELETE_DIRECTORY 0x01 /* trivial response */ +#define SMB_COM_CLOSE 0x04 /* triv req/rsp, timestamp ignored */ +#define SMB_COM_FLUSH 0x05 /* triv req/rsp */ +#define SMB_COM_DELETE 0x06 /* trivial response */ +#define SMB_COM_RENAME 0x07 /* trivial response */ +#define SMB_COM_QUERY_INFORMATION 0x08 /* aka getattr */ +#define SMB_COM_SETATTR 0x09 /* trivial response */ +#define SMB_COM_LOCKING_ANDX 0x24 /* trivial response */ +#define SMB_COM_COPY 0x29 /* trivial rsp, fail filename ignrd*/ +#define SMB_COM_ECHO 0x2B /* echo request */ +#define SMB_COM_OPEN_ANDX 0x2D /* Legacy open for old servers */ +#define SMB_COM_READ_ANDX 0x2E +#define SMB_COM_WRITE_ANDX 0x2F +#define SMB_COM_TRANSACTION2 0x32 +#define SMB_COM_TRANSACTION2_SECONDARY 0x33 +#define SMB_COM_FIND_CLOSE2 0x34 /* trivial response */ +#define SMB_COM_TREE_DISCONNECT 0x71 /* trivial response */ +#define SMB_COM_NEGOTIATE 0x72 +#define SMB_COM_SESSION_SETUP_ANDX 0x73 +#define SMB_COM_LOGOFF_ANDX 0x74 /* trivial response */ +#define SMB_COM_TREE_CONNECT_ANDX 0x75 +#define SMB_COM_NT_TRANSACT 0xA0 +#define SMB_COM_NT_TRANSACT_SECONDARY 0xA1 +#define SMB_COM_NT_CREATE_ANDX 0xA2 +#define SMB_COM_NT_CANCEL 0xA4 /* no response */ +#define SMB_COM_NT_RENAME 0xA5 /* trivial response */ + +/* Transact2 subcommand codes */ +#define TRANS2_OPEN 0x00 +#define TRANS2_FIND_FIRST 0x01 +#define TRANS2_FIND_NEXT 0x02 +#define TRANS2_QUERY_FS_INFORMATION 0x03 +#define TRANS2_SET_FS_INFORMATION 0x04 +#define TRANS2_QUERY_PATH_INFORMATION 0x05 +#define TRANS2_SET_PATH_INFORMATION 0x06 +#define TRANS2_QUERY_FILE_INFORMATION 0x07 +#define TRANS2_SET_FILE_INFORMATION 0x08 +#define TRANS2_GET_DFS_REFERRAL 0x10 +#define TRANS2_REPORT_DFS_INCOSISTENCY 0x11 + +/* SMB Transact (Named Pipe) subcommand codes */ +#define TRANS_SET_NMPIPE_STATE 0x0001 +#define TRANS_RAW_READ_NMPIPE 0x0011 +#define TRANS_QUERY_NMPIPE_STATE 0x0021 +#define TRANS_QUERY_NMPIPE_INFO 0x0022 +#define TRANS_PEEK_NMPIPE 0x0023 +#define TRANS_TRANSACT_NMPIPE 0x0026 +#define TRANS_RAW_WRITE_NMPIPE 0x0031 +#define TRANS_READ_NMPIPE 0x0036 +#define TRANS_WRITE_NMPIPE 0x0037 +#define TRANS_WAIT_NMPIPE 0x0053 +#define TRANS_CALL_NMPIPE 0x0054 + +/* NT Transact subcommand codes */ +#define NT_TRANSACT_CREATE 0x01 +#define NT_TRANSACT_IOCTL 0x02 +#define NT_TRANSACT_SET_SECURITY_DESC 0x03 +#define NT_TRANSACT_NOTIFY_CHANGE 0x04 +#define NT_TRANSACT_RENAME 0x05 +#define NT_TRANSACT_QUERY_SECURITY_DESC 0x06 +#define NT_TRANSACT_GET_USER_QUOTA 0x07 +#define NT_TRANSACT_SET_USER_QUOTA 0x08 + +/* future chained NTCreateXReadX bigger, but for time being NTCreateX biggest */ +/* among the requests (NTCreateX response is bigger with wct of 34) */ +#define MAX_CIFS_HDR_SIZE 0x54 /* 32 hdr + (2*24 wct) + 2 bct + 2 pad */ +#define CIFS_SMALL_PATH 120 /* allows for (448-88)/3 */ + +/* internal cifs vfs structures */ +/***************************************************************** + * All constants go here + ***************************************************************** + */ + +/* + * Starting value for maximum SMB size negotiation + */ +#define CIFS_MAX_MSGSIZE (4*4096) + +/* + * Size of encrypted user password in bytes + */ +#define CIFS_ENCPWD_SIZE (16) + +/* + * Size of the crypto key returned on the negotiate SMB in bytes + */ +#define CIFS_CRYPTO_KEY_SIZE (8) + +/* + * Size of the ntlm client response + */ +#define CIFS_AUTH_RESP_SIZE (24) + +/* + * Size of the session key (crypto key encrypted with the password + */ +#define CIFS_SESS_KEY_SIZE (16) + +#define CIFS_SERVER_CHALLENGE_SIZE (8) +#define CIFS_HMAC_MD5_HASH_SIZE (16) +#define CIFS_CPHTXT_SIZE (16) +#define CIFS_NTHASH_SIZE (16) + +/* + * Maximum user name length + */ +#define CIFS_UNLEN (20) + +/* + * Flags on SMB open + */ +#define SMBOPEN_WRITE_THROUGH 0x4000 +#define SMBOPEN_DENY_ALL 0x0010 +#define SMBOPEN_DENY_WRITE 0x0020 +#define SMBOPEN_DENY_READ 0x0030 +#define SMBOPEN_DENY_NONE 0x0040 +#define SMBOPEN_READ 0x0000 +#define SMBOPEN_WRITE 0x0001 +#define SMBOPEN_READWRITE 0x0002 +#define SMBOPEN_EXECUTE 0x0003 + +#define SMBOPEN_OCREATE 0x0010 +#define SMBOPEN_OTRUNC 0x0002 +#define SMBOPEN_OAPPEND 0x0001 + +/* + * SMB flag definitions + * See MS-CIFS 2.2.3.1 + */ +#define SMBFLG_EXTD_LOCK 0x01 /* server supports lock-read write-unlock smb */ +#define SMBFLG_RCV_POSTED 0x02 /* obsolete */ +#define SMBFLG_RSVD 0x04 +#define SMBFLG_CASELESS 0x08 /* all pathnames treated as caseless (off + implies case sensitive file handling request) */ +#define SMBFLG_CANONICAL_PATH_FORMAT 0x10 /* obsolete */ +#define SMBFLG_OLD_OPLOCK 0x20 /* obsolete */ +#define SMBFLG_OLD_OPLOCK_NOTIFY 0x40 /* obsolete */ +#define SMBFLG_RESPONSE 0x80 /* this PDU is a response from server */ + +/* + * SMB flag2 definitions + * See MS-CIFS 2.2.3.1 + * MS-SMB 2.2.3.1 + */ +#define SMBFLG2_KNOWS_LONG_NAMES cpu_to_le16(1) /* can send long (non-8.3) + path names in response */ +#define SMBFLG2_KNOWS_EAS cpu_to_le16(2) +#define SMBFLG2_SECURITY_SIGNATURE cpu_to_le16(4) +#define SMBFLG2_COMPRESSED (8) +#define SMBFLG2_SECURITY_SIGNATURE_REQUIRED (0x10) +#define SMBFLG2_IS_LONG_NAME cpu_to_le16(0x40) +#define SMBFLG2_REPARSE_PATH (0x400) +#define SMBFLG2_EXT_SEC cpu_to_le16(0x800) +#define SMBFLG2_DFS cpu_to_le16(0x1000) +#define SMBFLG2_PAGING_IO cpu_to_le16(0x2000) +#define SMBFLG2_ERR_STATUS cpu_to_le16(0x4000) +#define SMBFLG2_UNICODE cpu_to_le16(0x8000) + +/* Combinations of file access permission bits */ +#define SET_FILE_READ_RIGHTS (FILE_READ_DATA | FILE_READ_EA | FILE_WRITE_EA \ + | FILE_READ_ATTRIBUTES \ + | FILE_WRITE_ATTRIBUTES \ + | DELETE | READ_CONTROL | WRITE_DAC \ + | WRITE_OWNER | SYNCHRONIZE) +#define SET_FILE_WRITE_RIGHTS (FILE_WRITE_DATA | FILE_APPEND_DATA \ + | FILE_READ_EA | FILE_WRITE_EA \ + | FILE_READ_ATTRIBUTES \ + | FILE_WRITE_ATTRIBUTES \ + | DELETE | READ_CONTROL | WRITE_DAC \ + | WRITE_OWNER | SYNCHRONIZE) + +/* + * Invalid readdir handle + */ +#define CIFS_NO_HANDLE 0xFFFF + +#define NO_CHANGE_64 0xFFFFFFFFFFFFFFFFULL + +/* IPC$ in ASCII */ +#define CIFS_IPC_RESOURCE "\x49\x50\x43\x24" + +/* IPC$ in Unicode */ +#define CIFS_IPC_UNICODE_RESOURCE "\x00\x49\x00\x50\x00\x43\x00\x24\x00\x00" + +/* Unicode Null terminate 2 bytes of 0 */ +#define UNICODE_NULL "\x00\x00" +#define ASCII_NULL 0x00 + +/* + * Server type values (returned on EnumServer API + */ +#define CIFS_SV_TYPE_DC 0x00000008 +#define CIFS_SV_TYPE_BACKDC 0x00000010 + +/* + * Alias type flags (From EnumAlias API call + */ +#define CIFS_ALIAS_TYPE_FILE 0x0001 +#define CIFS_SHARE_TYPE_FILE 0x0000 + +/* + * File Attribute flags + */ +#define ATTR_READONLY 0x0001 /* See MS-CIFS 2.2.1.2.3 */ +#define ATTR_HIDDEN 0x0002 /* See MS-CIFS 2.2.1.2.3 */ +#define ATTR_SYSTEM 0x0004 /* See MS-CIFS 2.2.1.2.3 */ +#define ATTR_VOLUME 0x0008 +#define ATTR_DIRECTORY 0x0010 /* See MS-CIFS 2.2.1.2.3 */ +#define ATTR_ARCHIVE 0x0020 /* See MS-CIFS 2.2.1.2.3 */ +#define ATTR_DEVICE 0x0040 +#define ATTR_NORMAL 0x0080 /* See MS-CIFS 2.2.1.2.3 */ +#define ATTR_TEMPORARY 0x0100 /* See MS-CIFS 2.2.1.2.3 */ +#define ATTR_SPARSE 0x0200 /* See MS-SMB 2.2.1.2.1 */ +#define ATTR_REPARSE_POINT 0x0400 /* See MS-SMB 2.2.1.2.1 */ +#define ATTR_COMPRESSED 0x0800 /* See MS-CIFS 2.2.1.2.3 */ +#define ATTR_OFFLINE 0x1000 /* See MS-SMB 2.2.1.2.1 + ie file not immediately available - + on offline storage */ +#define ATTR_NOT_CONTENT_INDEXED 0x2000 /* See MS-SMB 2.2.1.2.1 */ +#define ATTR_ENCRYPTED 0x4000 /* See MS-SMB 2.2.1.2.1 */ +#define ATTR_POSIX_SEMANTICS 0x0100000 /* See MS-CIFS 2.2.1.2.3 */ +#define ATTR_BACKUP_SEMANTICS 0x0200000 /* See MS-CIFS 2.2.1.2.3 */ +#define ATTR_DELETE_ON_CLOSE 0x0400000 /* See MS-CIFS 2.2.1.2.3 */ +#define ATTR_SEQUENTIAL_SCAN 0x0800000 /* See MS-CIFS 2.2.1.2.3 */ +#define ATTR_RANDOM_ACCESS 0x1000000 /* See MS-CIFS 2.2.1.2.3 */ +#define ATTR_NO_BUFFERING 0x2000000 /* See MS-CIFS 2.2.1.2.3 */ +#define ATTR_WRITE_THROUGH 0x8000000 /* See MS-CIFS 2.2.1.2.3 */ + +/* ShareAccess flags */ +#define FILE_NO_SHARE 0x00000000 +#define FILE_SHARE_READ 0x00000001 +#define FILE_SHARE_WRITE 0x00000002 +#define FILE_SHARE_DELETE 0x00000004 +#define FILE_SHARE_ALL 0x00000007 + +/* CreateDisposition flags, similar to CreateAction as well */ +#define FILE_SUPERSEDE 0x00000000 +#define FILE_OPEN 0x00000001 +#define FILE_CREATE 0x00000002 +#define FILE_OPEN_IF 0x00000003 +#define FILE_OVERWRITE 0x00000004 +#define FILE_OVERWRITE_IF 0x00000005 + +/* CreateOptions */ +#define CREATE_NOT_FILE 0x00000001 /* if set must not be file */ +#define CREATE_WRITE_THROUGH 0x00000002 +#define CREATE_SEQUENTIAL 0x00000004 +#define CREATE_NO_BUFFER 0x00000008 /* should not buffer on srv */ +#define CREATE_SYNC_ALERT 0x00000010 /* MBZ */ +#define CREATE_ASYNC_ALERT 0x00000020 /* MBZ */ +#define CREATE_NOT_DIR 0x00000040 /* if set must not be directory */ +#define CREATE_TREE_CONNECTION 0x00000080 /* should be zero */ +#define CREATE_COMPLETE_IF_OPLK 0x00000100 /* should be zero */ +#define CREATE_NO_EA_KNOWLEDGE 0x00000200 +#define CREATE_EIGHT_DOT_THREE 0x00000400 /* doc says this is obsolete + "open for recovery" flag should + be zero in any case */ +#define CREATE_OPEN_FOR_RECOVERY 0x00000400 +#define CREATE_RANDOM_ACCESS 0x00000800 +#define CREATE_DELETE_ON_CLOSE 0x00001000 +#define CREATE_OPEN_BY_ID 0x00002000 +#define CREATE_OPEN_BACKUP_INTENT 0x00004000 +#define CREATE_NO_COMPRESSION 0x00008000 +#define CREATE_RESERVE_OPFILTER 0x00100000 /* should be zero */ +#define OPEN_REPARSE_POINT 0x00200000 +#define OPEN_NO_RECALL 0x00400000 +#define OPEN_FREE_SPACE_QUERY 0x00800000 /* should be zero */ +#define CREATE_OPTIONS_MASK 0x007FFFFF +#define CREATE_OPTION_READONLY 0x10000000 +#define CREATE_OPTION_SPECIAL 0x20000000 /* system. NB not sent over wire */ + +/* ImpersonationLevel flags */ +#define SECURITY_ANONYMOUS 0 +#define SECURITY_IDENTIFICATION 1 +#define SECURITY_IMPERSONATION 2 +#define SECURITY_DELEGATION 3 + +/* SecurityFlags */ +#define SECURITY_CONTEXT_TRACKING 0x01 +#define SECURITY_EFFECTIVE_ONLY 0x02 + +/* + * Default PID value, used in all SMBs where the PID is not important + */ +#define CIFS_DFT_PID 0x1234 + +/* + * We use the same routine for Copy and Move SMBs. This flag is used to + * distinguish + */ +#define CIFS_COPY_OP 1 +#define CIFS_RENAME_OP 2 + +/* + * Computer Name Length (since Netbios name was length 16 with last byte 0x20) + * No longer as important, now that TCP names are more commonly used to + * resolve hosts. + */ +#define CNLEN 15 + +/* + * Share Name Length (SNLEN) + * Note: This length was limited by the SMB used to get + * the Share info. NetShareEnum only returned 13 + * chars, including the null termination. + * This was removed because it no longer is limiting. + */ + +/* + * Comment Length + */ +#define MAXCOMMENTLEN 40 + +/* + * The OS/2 maximum path name + */ +#define MAX_PATHCONF 256 + +/* + * SMB frame definitions (following must be packed structs) + * See the SNIA CIFS Specification for details. + * + * The Naming convention is the lower case version of the + * smb command code name for the struct and this is typedef to the + * uppercase version of the same name with the prefix SMB_ removed + * for brevity. Although typedefs are not commonly used for + * structure definitions in the Linux kernel, their use in the + * CIFS standards document, which this code is based on, may + * make this one of the cases where typedefs for structures make + * sense to improve readability for readers of the standards doc. + * Typedefs can always be removed later if they are too distracting + * and they are only used for the CIFSs PDUs themselves, not + * internal cifs vfs structures + * + */ + +#define MIN_TZ_ADJ (15 * 60) /* minimum grid for timezones in seconds */ + +#define READ_RAW_ENABLE 1 +#define WRITE_RAW_ENABLE 2 +#define RAW_ENABLE (READ_RAW_ENABLE | WRITE_RAW_ENABLE) +#define SMB1_CLIENT_GUID_SIZE (16) + +/* See MS-CIFS 2.2.4.52.2 */ +typedef struct smb_negotiate_rsp { + struct smb_hdr hdr; /* wct = 17 */ + __le16 DialectIndex; /* 0xFFFF = no dialect acceptable */ + __u8 SecurityMode; + __le16 MaxMpxCount; + __le16 MaxNumberVcs; + __le32 MaxBufferSize; + __le32 MaxRawSize; + __le32 SessionKey; + __le32 Capabilities; /* see below */ + __le32 SystemTimeLow; + __le32 SystemTimeHigh; + __le16 ServerTimeZone; + __u8 EncryptionKeyLength; + __u16 ByteCount; + union { + /* cap extended security off */ + DECLARE_FLEX_ARRAY(unsigned char, EncryptionKey); + /* followed by Domain name - if extended security is off */ + /* followed by 16 bytes of server GUID */ + /* then security blob if cap_extended_security negotiated */ + struct { + unsigned char GUID[SMB1_CLIENT_GUID_SIZE]; + unsigned char SecurityBlob[]; + } __packed extended_response; + } __packed u; +} __packed SMB_NEGOTIATE_RSP; + +/* SecurityMode bits */ +#define SECMODE_USER 0x01 /* off indicates share level security */ +#define SECMODE_PW_ENCRYPT 0x02 +#define SECMODE_SIGN_ENABLED 0x04 /* SMB security signatures enabled */ +#define SECMODE_SIGN_REQUIRED 0x08 /* SMB security signatures required */ + +/* Negotiate response Capabilities */ +#define CAP_RAW_MODE 0x00000001 +#define CAP_MPX_MODE 0x00000002 +#define CAP_UNICODE 0x00000004 +#define CAP_LARGE_FILES 0x00000008 +#define CAP_NT_SMBS 0x00000010 /* implies CAP_NT_FIND */ +#define CAP_RPC_REMOTE_APIS 0x00000020 +#define CAP_STATUS32 0x00000040 +#define CAP_LEVEL_II_OPLOCKS 0x00000080 +#define CAP_LOCK_AND_READ 0x00000100 +#define CAP_NT_FIND 0x00000200 +#define CAP_DFS 0x00001000 +#define CAP_INFOLEVEL_PASSTHRU 0x00002000 +#define CAP_LARGE_READ_X 0x00004000 +#define CAP_LARGE_WRITE_X 0x00008000 +#define CAP_LWIO 0x00010000 /* support fctl_srv_req_resume_key */ +#define CAP_UNIX 0x00800000 +#define CAP_COMPRESSED_DATA 0x02000000 +#define CAP_DYNAMIC_REAUTH 0x20000000 +#define CAP_PERSISTENT_HANDLES 0x40000000 +#define CAP_EXTENDED_SECURITY 0x80000000 + +typedef union smb_com_session_setup_andx { + struct { /* request format */ + struct smb_hdr hdr; /* wct = 12 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __le16 MaxBufferSize; + __le16 MaxMpxCount; + __le16 VcNumber; + __le32 SessionKey; + __le16 SecurityBlobLength; + __u32 Reserved; + __le32 Capabilities; /* see below */ + __le16 ByteCount; + unsigned char SecurityBlob[]; /* followed by */ + /* STRING NativeOS */ + /* STRING NativeLanMan */ + } __packed req; /* NTLM request format (with + extended security */ + + struct { /* request format */ + struct smb_hdr hdr; /* wct = 13 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __le16 MaxBufferSize; + __le16 MaxMpxCount; + __le16 VcNumber; + __le32 SessionKey; + __le16 CaseInsensitivePasswordLength; /* ASCII password len */ + __le16 CaseSensitivePasswordLength; /* Unicode password length*/ + __u32 Reserved; /* see below */ + __le32 Capabilities; + __le16 ByteCount; + unsigned char CaseInsensitivePassword[]; /* followed by: */ + /* unsigned char * CaseSensitivePassword; */ + /* STRING AccountName */ + /* STRING PrimaryDomain */ + /* STRING NativeOS */ + /* STRING NativeLanMan */ + } __packed req_no_secext; /* NTLM request format (without + extended security */ + + struct { /* default (NTLM) response format */ + struct smb_hdr hdr; /* wct = 4 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __le16 Action; /* see below */ + __le16 SecurityBlobLength; + __u16 ByteCount; + unsigned char SecurityBlob[]; /* followed by */ +/* unsigned char * NativeOS; */ +/* unsigned char * NativeLanMan; */ +/* unsigned char * PrimaryDomain; */ + } __packed resp; /* NTLM response + (with or without extended sec) */ + + struct { /* request format */ + struct smb_hdr hdr; /* wct = 10 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __le16 MaxBufferSize; + __le16 MaxMpxCount; + __le16 VcNumber; + __le32 SessionKey; + __le16 PasswordLength; + __u32 Reserved; /* encrypt key len and offset */ + __le16 ByteCount; + unsigned char AccountPassword[]; /* followed by */ + /* STRING AccountName */ + /* STRING PrimaryDomain */ + /* STRING NativeOS */ + /* STRING NativeLanMan */ + } __packed old_req; /* pre-NTLM (LANMAN2.1) req format */ + + struct { /* default (NTLM) response format */ + struct smb_hdr hdr; /* wct = 3 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __le16 Action; /* see below */ + __u16 ByteCount; + unsigned char NativeOS[]; /* followed by */ +/* unsigned char * NativeLanMan; */ +/* unsigned char * PrimaryDomain; */ + } __packed old_resp; /* pre-NTLM (LANMAN2.1) response */ +} __packed SESSION_SETUP_ANDX; + +/* format of NLTMv2 Response ie "case sensitive password" hash when NTLMv2 */ + +#define NTLMSSP_SERVER_TYPE 1 +#define NTLMSSP_DOMAIN_TYPE 2 +#define NTLMSSP_FQ_DOMAIN_TYPE 3 +#define NTLMSSP_DNS_DOMAIN_TYPE 4 +#define NTLMSSP_DNS_PARENT_TYPE 5 + +struct ntlmssp2_name { + __le16 type; + __le16 length; + __u8 data[]; +} __packed; + +struct ntlmv2_resp { + union { + char ntlmv2_hash[CIFS_ENCPWD_SIZE]; + struct { + __u8 reserved[8]; + __u8 key[CIFS_SERVER_CHALLENGE_SIZE]; + } __packed challenge; + } __packed; + __le32 blob_signature; + __u32 reserved; + __le64 time; + __u64 client_chal; /* random */ + __u32 reserved2; + /* array of name entries could follow ending in minimum 4 byte struct */ +} __packed; + + +#define CIFS_NETWORK_OPSYS "CIFS VFS Client for Linux" + + +/* + * Capabilities bits (for NTLM SessSetup request) + * See MS-CIFS 2.2.4.52.2 + * MS-SMB 2.2.4.5.2.1 + */ +#define CAP_UNICODE 0x00000004 +#define CAP_LARGE_FILES 0x00000008 +#define CAP_NT_SMBS 0x00000010 +#define CAP_STATUS32 0x00000040 +#define CAP_LEVEL_II_OPLOCKS 0x00000080 +#define CAP_NT_FIND 0x00000200 /* reserved should be zero + (because NT_SMBs implies the same thing?) */ +#define CAP_BULK_TRANSFER 0x00000400 +#define CAP_EXTENDED_SECURITY 0x80000000 + +/* Action bits */ +#define GUEST_LOGIN 1 + +typedef struct smb_com_tconx_req { + struct smb_hdr hdr; /* wct = 4 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __le16 Flags; /* see below */ + __le16 PasswordLength; + __le16 ByteCount; + unsigned char Password[]; /* followed by */ +/* STRING Path *//* \\server\share name */ + /* STRING Service */ +} __packed TCONX_REQ; + +typedef struct smb_com_tconx_rsp { + struct smb_hdr hdr; /* wct = 3 , not extended response */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __le16 OptionalSupport; /* see below */ + __u16 ByteCount; + unsigned char Service[]; /* always ASCII, not Unicode */ + /* STRING NativeFileSystem */ +} __packed TCONX_RSP; + +typedef struct smb_com_tconx_rsp_ext { + struct smb_hdr hdr; /* wct = 7, extended response */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __le16 OptionalSupport; /* see below */ + __le32 MaximalShareAccessRights; + __le32 GuestMaximalShareAccessRights; + __u16 ByteCount; + unsigned char Service[]; /* always ASCII, not Unicode */ + /* STRING NativeFileSystem */ +} __packed TCONX_RSP_EXT; + + +/* tree connect Flags */ +#define DISCONNECT_TID 0x0001 +#define TCON_EXTENDED_SIGNATURES 0x0004 +#define TCON_EXTENDED_SECINFO 0x0008 + +/* OptionalSupport bits */ +#define SMB_SUPPORT_SEARCH_BITS 0x0001 /* "must have" directory search bits + (exclusive searches supported) */ +#define SMB_SHARE_IS_IN_DFS 0x0002 +#define SMB_CSC_MASK 0x000C +/* CSC flags defined as follows */ +#define SMB_CSC_CACHE_MANUAL_REINT 0x0000 +#define SMB_CSC_CACHE_AUTO_REINT 0x0004 +#define SMB_CSC_CACHE_VDO 0x0008 +#define SMB_CSC_NO_CACHING 0x000C +#define SMB_UNIQUE_FILE_NAME 0x0010 +#define SMB_EXTENDED_SIGNATURES 0x0020 + +/* services + * + * A: ie disk + * LPT1: ie printer + * IPC ie named pipe + * COMM + * ????? ie any type + * + */ + +typedef struct smb_com_echo_req { + struct smb_hdr hdr; + __le16 EchoCount; + __le16 ByteCount; + char Data[]; +} __packed ECHO_REQ; + +typedef struct smb_com_echo_rsp { + struct smb_hdr hdr; + __le16 SequenceNumber; + __le16 ByteCount; + char Data[]; +} __packed ECHO_RSP; + +typedef struct smb_com_logoff_andx_req { + struct smb_hdr hdr; /* wct = 2 */ + __u8 AndXCommand; + __u8 AndXReserved; + __u16 AndXOffset; + __u16 ByteCount; +} __packed LOGOFF_ANDX_REQ; + +typedef struct smb_com_logoff_andx_rsp { + struct smb_hdr hdr; /* wct = 2 */ + __u8 AndXCommand; + __u8 AndXReserved; + __u16 AndXOffset; + __u16 ByteCount; +} __packed LOGOFF_ANDX_RSP; + +typedef union smb_com_tree_disconnect { /* as an alternative can use flag on + tree_connect PDU to effect disconnect */ + /* tdis is probably simplest SMB PDU */ + struct { + struct smb_hdr hdr; /* wct = 0 */ + __u16 ByteCount; /* bcc = 0 */ + } __packed req; + struct { + struct smb_hdr hdr; /* wct = 0 */ + __u16 ByteCount; /* bcc = 0 */ + } __packed resp; +} __packed TREE_DISCONNECT; + +typedef struct smb_com_close_req { + struct smb_hdr hdr; /* wct = 3 */ + __u16 FileID; + __u32 LastWriteTime; /* should be zero or -1 */ + __u16 ByteCount; /* 0 */ +} __packed CLOSE_REQ; + +typedef struct smb_com_close_rsp { + struct smb_hdr hdr; /* wct = 0 */ + __u16 ByteCount; /* bct = 0 */ +} __packed CLOSE_RSP; + +typedef struct smb_com_flush_req { + struct smb_hdr hdr; /* wct = 1 */ + __u16 FileID; + __u16 ByteCount; /* 0 */ +} __packed FLUSH_REQ; + +typedef struct smb_com_findclose_req { + struct smb_hdr hdr; /* wct = 1 */ + __u16 FileID; + __u16 ByteCount; /* 0 */ +} __packed FINDCLOSE_REQ; + +/* OpenFlags */ +#define REQ_MORE_INFO 0x00000001 /* legacy (OPEN_AND_X) only */ +#define REQ_OPLOCK 0x00000002 +#define REQ_BATCHOPLOCK 0x00000004 +#define REQ_OPENDIRONLY 0x00000008 +#define REQ_EXTENDED_INFO 0x00000010 + +/* File type */ +#define DISK_TYPE 0x0000 +#define BYTE_PIPE_TYPE 0x0001 +#define MESSAGE_PIPE_TYPE 0x0002 +#define PRINTER_TYPE 0x0003 +#define COMM_DEV_TYPE 0x0004 +#define UNKNOWN_TYPE 0xFFFF + +/* Device Type or File Status Flags */ +#define NO_EAS 0x0001 +#define NO_SUBSTREAMS 0x0002 +#define NO_REPARSETAG 0x0004 +/* following flags can apply if pipe */ +#define ICOUNT_MASK 0x00FF +#define PIPE_READ_MODE 0x0100 +#define NAMED_PIPE_TYPE 0x0400 +#define PIPE_END_POINT 0x4000 +#define BLOCKING_NAMED_PIPE 0x8000 + +typedef struct smb_com_open_req { /* also handles create */ + struct smb_hdr hdr; /* wct = 24 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __u8 Reserved; /* Must Be Zero */ + __le16 NameLength; + __le32 OpenFlags; + __u32 RootDirectoryFid; + __le32 DesiredAccess; + __le64 AllocationSize; + __le32 FileAttributes; + __le32 ShareAccess; + __le32 CreateDisposition; + __le32 CreateOptions; + __le32 ImpersonationLevel; + __u8 SecurityFlags; + __le16 ByteCount; + char fileName[]; +} __packed OPEN_REQ; + +/* open response: oplock levels */ +#define OPLOCK_NONE 0 +#define OPLOCK_EXCLUSIVE 1 +#define OPLOCK_BATCH 2 +#define OPLOCK_READ 3 /* level 2 oplock */ + +/* open response for CreateAction shifted left */ +#define CIFS_CREATE_ACTION 0x20000 /* file created */ + +typedef struct smb_com_open_rsp { + struct smb_hdr hdr; /* wct = 34 BB */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __u8 OplockLevel; + __u16 Fid; + __le32 CreateAction; + struct_group_attr(common_attributes, __packed, + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le32 FileAttributes; + ); + __le64 AllocationSize; + __le64 EndOfFile; + __le16 FileType; + __le16 DeviceState; + __u8 DirectoryFlag; + __u16 ByteCount; /* bct = 0 */ +} __packed OPEN_RSP; + +typedef struct smb_com_open_rsp_ext { + struct smb_hdr hdr; /* wct = 42 but meaningless due to MS bug? */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __u8 OplockLevel; + __u16 Fid; + __le32 CreateAction; + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le32 FileAttributes; + __le64 AllocationSize; + __le64 EndOfFile; + __le16 FileType; + __le16 DeviceState; + __u8 DirectoryFlag; + __u8 VolumeGUID[16]; + __u64 FileId; /* note no endian conversion - is opaque UniqueID */ + __le32 MaximalAccessRights; + __le32 GuestMaximalAccessRights; + __u16 ByteCount; /* bct = 0 */ +} __packed OPEN_RSP_EXT; + + +/* format of legacy open request */ +typedef struct smb_com_openx_req { + struct smb_hdr hdr; /* wct = 15 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __le16 OpenFlags; + __le16 Mode; + __le16 Sattr; /* search attributes */ + __le16 FileAttributes; /* dos attrs */ + __le32 CreateTime; /* os2 format */ + __le16 OpenFunction; + __le32 EndOfFile; + __le32 Timeout; + __le32 Reserved; + __le16 ByteCount; /* file name follows */ + char fileName[]; +} __packed OPENX_REQ; + +typedef struct smb_com_openx_rsp { + struct smb_hdr hdr; /* wct = 15 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __u16 Fid; + __le16 FileAttributes; + __le32 LastWriteTime; /* os2 format */ + __le32 EndOfFile; + __le16 Access; + __le16 FileType; + __le16 IPCState; + __le16 Action; + __u32 FileId; + __u16 Reserved; + __u16 ByteCount; +} __packed OPENX_RSP; + +/* For encoding of POSIX Open Request - see trans2 function 0x209 data struct */ + +/* Legacy write request for older servers */ +typedef struct smb_com_writex_req { + struct smb_hdr hdr; /* wct = 12 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __u16 Fid; + __le32 OffsetLow; + __u32 Reserved; /* Timeout */ + __le16 WriteMode; /* 1 = write through */ + __le16 Remaining; + __le16 Reserved2; + __le16 DataLengthLow; + __le16 DataOffset; + __le16 ByteCount; + __u8 Pad; /* BB check for whether padded to DWORD + boundary and optimum performance here */ + char Data[]; +} __packed WRITEX_REQ; + +typedef struct smb_com_write_req { + struct smb_hdr hdr; /* wct = 14 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __u16 Fid; + __le32 OffsetLow; + __u32 Reserved; + __le16 WriteMode; + __le16 Remaining; + __le16 DataLengthHigh; + __le16 DataLengthLow; + __le16 DataOffset; + __le32 OffsetHigh; + __le16 ByteCount; + __u8 Pad; /* BB check for whether padded to DWORD + boundary and optimum performance here */ + char Data[]; +} __packed WRITE_REQ; + +typedef struct smb_com_write_rsp { + struct smb_hdr hdr; /* wct = 6 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __le16 Count; + __le16 Remaining; + __le16 CountHigh; + __u16 Reserved; + __u16 ByteCount; +} __packed WRITE_RSP; + +/* legacy read request for older servers */ +typedef struct smb_com_readx_req { + struct smb_hdr hdr; /* wct = 10 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __u16 Fid; + __le32 OffsetLow; + __le16 MaxCount; + __le16 MinCount; /* obsolete */ + __le32 Reserved; + __le16 Remaining; + __le16 ByteCount; +} __packed READX_REQ; + +typedef struct smb_com_read_req { + struct smb_hdr hdr; /* wct = 12 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __u16 Fid; + __le32 OffsetLow; + __le16 MaxCount; + __le16 MinCount; /* obsolete */ + __le32 MaxCountHigh; + __le16 Remaining; + __le32 OffsetHigh; + __le16 ByteCount; +} __packed READ_REQ; + +typedef struct smb_com_read_rsp { + struct smb_hdr hdr; /* wct = 12 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __le16 Remaining; + __le16 DataCompactionMode; + __le16 Reserved; + __le16 DataLength; + __le16 DataOffset; + __le16 DataLengthHigh; + __u64 Reserved2; + __u16 ByteCount; + /* read response data immediately follows */ +} __packed READ_RSP; + +typedef struct locking_andx_range { + __le16 Pid; + __le16 Pad; + __le32 OffsetHigh; + __le32 OffsetLow; + __le32 LengthHigh; + __le32 LengthLow; +} __packed LOCKING_ANDX_RANGE; + +#define LOCKING_ANDX_SHARED_LOCK 0x01 +#define LOCKING_ANDX_OPLOCK_RELEASE 0x02 +#define LOCKING_ANDX_CHANGE_LOCKTYPE 0x04 +#define LOCKING_ANDX_CANCEL_LOCK 0x08 +#define LOCKING_ANDX_LARGE_FILES 0x10 /* always on for us */ + +typedef struct smb_com_lock_req { + struct smb_hdr hdr; /* wct = 8 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __u16 Fid; + __u8 LockType; + __u8 OplockLevel; + __le32 Timeout; + __le16 NumberOfUnlocks; + __le16 NumberOfLocks; + __le16 ByteCount; + LOCKING_ANDX_RANGE Locks[]; +} __packed LOCK_REQ; + +/* lock type */ +#define CIFS_RDLCK 0 +#define CIFS_WRLCK 1 +#define CIFS_UNLCK 2 +typedef struct cifs_posix_lock { + __le16 lock_type; /* 0 = Read, 1 = Write, 2 = Unlock */ + __le16 lock_flags; /* 1 = Wait (only valid for setlock) */ + __le32 pid; + __le64 start; + __le64 length; + /* BB what about additional owner info to identify network client */ +} __packed CIFS_POSIX_LOCK; + +typedef struct smb_com_lock_rsp { + struct smb_hdr hdr; /* wct = 2 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __u16 ByteCount; +} __packed LOCK_RSP; + +typedef struct smb_com_rename_req { + struct smb_hdr hdr; /* wct = 1 */ + __le16 SearchAttributes; /* target file attributes */ + __le16 ByteCount; + __u8 BufferFormat; /* 4 = ASCII or Unicode */ + unsigned char OldFileName[]; + /* followed by __u8 BufferFormat2 */ + /* followed by NewFileName */ +} __packed RENAME_REQ; + + /* copy request flags */ +#define COPY_MUST_BE_FILE 0x0001 +#define COPY_MUST_BE_DIR 0x0002 +#define COPY_TARGET_MODE_ASCII 0x0004 /* if not set, binary */ +#define COPY_SOURCE_MODE_ASCII 0x0008 /* if not set, binary */ +#define COPY_VERIFY_WRITES 0x0010 +#define COPY_TREE 0x0020 + +typedef struct smb_com_copy_req { + struct smb_hdr hdr; /* wct = 3 */ + __u16 Tid2; + __le16 OpenFunction; + __le16 Flags; + __le16 ByteCount; + __u8 BufferFormat; /* 4 = ASCII or Unicode */ + unsigned char OldFileName[]; + /* followed by __u8 BufferFormat2 */ + /* followed by NewFileName string */ +} __packed COPY_REQ; + +typedef struct smb_com_copy_rsp { + struct smb_hdr hdr; /* wct = 1 */ + __le16 CopyCount; /* number of files copied */ + __u16 ByteCount; /* may be zero */ + __u8 BufferFormat; /* 0x04 - only present if errored file follows */ + unsigned char ErrorFileName[]; /* only present if error in copy */ +} __packed COPY_RSP; + +#define CREATE_HARD_LINK 0x103 +#define MOVEFILE_COPY_ALLOWED 0x0002 +#define MOVEFILE_REPLACE_EXISTING 0x0001 + +typedef struct smb_com_nt_rename_req { /* A5 - also used for create hardlink */ + struct smb_hdr hdr; /* wct = 4 */ + __le16 SearchAttributes; /* target file attributes */ + __le16 Flags; /* spec says Information Level */ + __le32 ClusterCount; + __le16 ByteCount; + __u8 BufferFormat; /* 4 = ASCII or Unicode */ + unsigned char OldFileName[]; + /* followed by __u8 BufferFormat2 */ + /* followed by NewFileName */ +} __packed NT_RENAME_REQ; + +typedef struct smb_com_rename_rsp { + struct smb_hdr hdr; /* wct = 0 */ + __u16 ByteCount; /* bct = 0 */ +} __packed RENAME_RSP; + +typedef struct smb_com_delete_file_req { + struct smb_hdr hdr; /* wct = 1 */ + __le16 SearchAttributes; + __le16 ByteCount; + __u8 BufferFormat; /* 4 = ASCII */ + unsigned char fileName[]; +} __packed DELETE_FILE_REQ; + +typedef struct smb_com_delete_file_rsp { + struct smb_hdr hdr; /* wct = 0 */ + __u16 ByteCount; /* bct = 0 */ +} __packed DELETE_FILE_RSP; + +typedef struct smb_com_delete_directory_req { + struct smb_hdr hdr; /* wct = 0 */ + __le16 ByteCount; + __u8 BufferFormat; /* 4 = ASCII */ + unsigned char DirName[]; +} __packed DELETE_DIRECTORY_REQ; + +typedef struct smb_com_delete_directory_rsp { + struct smb_hdr hdr; /* wct = 0 */ + __u16 ByteCount; /* bct = 0 */ +} __packed DELETE_DIRECTORY_RSP; + +typedef struct smb_com_create_directory_req { + struct smb_hdr hdr; /* wct = 0 */ + __le16 ByteCount; + __u8 BufferFormat; /* 4 = ASCII */ + unsigned char DirName[]; +} __packed CREATE_DIRECTORY_REQ; + +typedef struct smb_com_create_directory_rsp { + struct smb_hdr hdr; /* wct = 0 */ + __u16 ByteCount; /* bct = 0 */ +} __packed CREATE_DIRECTORY_RSP; + +typedef struct smb_com_query_information_req { + struct smb_hdr hdr; /* wct = 0 */ + __le16 ByteCount; /* 1 + namelen + 1 */ + __u8 BufferFormat; /* 4 = ASCII */ + unsigned char FileName[]; +} __packed QUERY_INFORMATION_REQ; + +typedef struct smb_com_query_information_rsp { + struct smb_hdr hdr; /* wct = 10 */ + __le16 attr; + __le32 last_write_time; + __le32 size; + __u16 reserved[5]; + __le16 ByteCount; /* bcc = 0 */ +} __packed QUERY_INFORMATION_RSP; + +typedef struct smb_com_setattr_req { + struct smb_hdr hdr; /* wct = 8 */ + __le16 attr; + __le32 last_write_time; + __le16 reserved[5]; /* must be zero */ + __le16 ByteCount; + __u8 BufferFormat; /* 4 = ASCII */ + unsigned char fileName[]; +} __packed SETATTR_REQ; + +typedef struct smb_com_setattr_rsp { + struct smb_hdr hdr; /* wct = 0 */ + __u16 ByteCount; /* bct = 0 */ +} __packed SETATTR_RSP; + +/* empty wct response to setattr */ + +/*******************************************************/ +/* NT Transact structure definitions follow */ +/* Currently only ioctl, acl (get security descriptor) */ +/* and notify are implemented */ +/*******************************************************/ +typedef struct smb_com_ntransact_req { + struct smb_hdr hdr; /* wct >= 19 */ + __u8 MaxSetupCount; + __u16 Reserved; + __le32 TotalParameterCount; + __le32 TotalDataCount; + __le32 MaxParameterCount; + __le32 MaxDataCount; + __le32 ParameterCount; + __le32 ParameterOffset; + __le32 DataCount; + __le32 DataOffset; + __u8 SetupCount; /* four setup words follow subcommand */ + /* SNIA spec incorrectly included spurious pad here */ + __le16 SubCommand; /* 2 = IOCTL/FSCTL */ + /* SetupCount words follow then */ + __le16 ByteCount; + __u8 Pad[3]; + __u8 Parms[]; +} __packed NTRANSACT_REQ; + +typedef struct smb_com_ntransact_rsp { + struct smb_hdr hdr; /* wct = 18 */ + __u8 Reserved[3]; + __le32 TotalParameterCount; + __le32 TotalDataCount; + __le32 ParameterCount; + __le32 ParameterOffset; + __le32 ParameterDisplacement; + __le32 DataCount; + __le32 DataOffset; + __le32 DataDisplacement; + __u8 SetupCount; /* 0 */ + __u16 ByteCount; + /* __u8 Pad[3]; */ + /* parms and data follow */ +} __packed NTRANSACT_RSP; + +typedef struct smb_com_transaction_ioctl_req { + struct smb_hdr hdr; /* wct = 23 */ + __u8 MaxSetupCount; + __u16 Reserved; + __le32 TotalParameterCount; + __le32 TotalDataCount; + __le32 MaxParameterCount; + __le32 MaxDataCount; + __le32 ParameterCount; + __le32 ParameterOffset; + __le32 DataCount; + __le32 DataOffset; + __u8 SetupCount; /* four setup words follow subcommand */ + /* SNIA spec incorrectly included spurious pad here */ + __le16 SubCommand; /* 2 = IOCTL/FSCTL */ + __le32 FunctionCode; + __u16 Fid; + __u8 IsFsctl; /* 1 = File System Control 0 = device control (IOCTL) */ + __u8 IsRootFlag; /* 1 = apply command to root of share (must be DFS) */ + __le16 ByteCount; + __u8 Pad[3]; + __u8 Data[]; +} __packed TRANSACT_IOCTL_REQ; + +typedef struct smb_com_transaction_compr_ioctl_req { + struct smb_hdr hdr; /* wct = 23 */ + __u8 MaxSetupCount; + __u16 Reserved; + __le32 TotalParameterCount; + __le32 TotalDataCount; + __le32 MaxParameterCount; + __le32 MaxDataCount; + __le32 ParameterCount; + __le32 ParameterOffset; + __le32 DataCount; + __le32 DataOffset; + __u8 SetupCount; /* four setup words follow subcommand */ + /* SNIA spec incorrectly included spurious pad here */ + __le16 SubCommand; /* 2 = IOCTL/FSCTL */ + __le32 FunctionCode; + __u16 Fid; + __u8 IsFsctl; /* 1 = File System Control 0 = device control (IOCTL) */ + __u8 IsRootFlag; /* 1 = apply command to root of share (must be DFS) */ + __le16 ByteCount; + __u8 Pad[3]; + __le16 compression_state; /* See below for valid flags */ +} __packed TRANSACT_COMPR_IOCTL_REQ; + +/* compression state flags */ +#define COMPRESSION_FORMAT_NONE 0x0000 +#define COMPRESSION_FORMAT_DEFAULT 0x0001 +#define COMPRESSION_FORMAT_LZNT1 0x0002 + +typedef struct smb_com_transaction_ioctl_rsp { + struct smb_hdr hdr; /* wct = 19 */ + __u8 Reserved[3]; + __le32 TotalParameterCount; + __le32 TotalDataCount; + __le32 ParameterCount; + __le32 ParameterOffset; + __le32 ParameterDisplacement; + __le32 DataCount; + __le32 DataOffset; + __le32 DataDisplacement; + __u8 SetupCount; /* 1 */ + __le16 ReturnedDataLen; + __le16 ByteCount; +} __packed TRANSACT_IOCTL_RSP; + +#define CIFS_ACL_OWNER 1 +#define CIFS_ACL_GROUP 2 +#define CIFS_ACL_DACL 4 +#define CIFS_ACL_SACL 8 + +typedef struct smb_com_transaction_qsec_req { + struct smb_hdr hdr; /* wct = 19 */ + __u8 MaxSetupCount; + __u16 Reserved; + __le32 TotalParameterCount; + __le32 TotalDataCount; + __le32 MaxParameterCount; + __le32 MaxDataCount; + __le32 ParameterCount; + __le32 ParameterOffset; + __le32 DataCount; + __le32 DataOffset; + __u8 SetupCount; /* no setup words follow subcommand */ + /* SNIA spec incorrectly included spurious pad here */ + __le16 SubCommand; /* 6 = QUERY_SECURITY_DESC */ + __le16 ByteCount; /* bcc = 3 + 8 */ + __u8 Pad[3]; + __u16 Fid; + __u16 Reserved2; + __le32 AclFlags; +} __packed QUERY_SEC_DESC_REQ; + + +typedef struct smb_com_transaction_ssec_req { + struct smb_hdr hdr; /* wct = 19 */ + __u8 MaxSetupCount; + __u16 Reserved; + __le32 TotalParameterCount; + __le32 TotalDataCount; + __le32 MaxParameterCount; + __le32 MaxDataCount; + __le32 ParameterCount; + __le32 ParameterOffset; + __le32 DataCount; + __le32 DataOffset; + __u8 SetupCount; /* no setup words follow subcommand */ + /* SNIA spec incorrectly included spurious pad here */ + __le16 SubCommand; /* 3 = SET_SECURITY_DESC */ + __le16 ByteCount; /* bcc = 3 + 8 */ + __u8 Pad[3]; + __u16 Fid; + __u16 Reserved2; + __le32 AclFlags; +} __packed SET_SEC_DESC_REQ; + +typedef struct smb_com_transaction_change_notify_req { + struct smb_hdr hdr; /* wct = 23 */ + __u8 MaxSetupCount; + __u16 Reserved; + __le32 TotalParameterCount; + __le32 TotalDataCount; + __le32 MaxParameterCount; + __le32 MaxDataCount; + __le32 ParameterCount; + __le32 ParameterOffset; + __le32 DataCount; + __le32 DataOffset; + __u8 SetupCount; /* four setup words follow subcommand */ + /* SNIA spec incorrectly included spurious pad here */ + __le16 SubCommand;/* 4 = Change Notify */ + __le32 CompletionFilter; /* operation to monitor */ + __u16 Fid; + __u8 WatchTree; /* 1 = Monitor subdirectories */ + __u8 Reserved2; + __le16 ByteCount; +/* __u8 Pad[3];*/ +/* __u8 Data[];*/ +} __packed TRANSACT_CHANGE_NOTIFY_REQ; + +/* BB eventually change to use generic ntransact rsp struct + and validation routine */ +typedef struct smb_com_transaction_change_notify_rsp { + struct smb_hdr hdr; /* wct = 18 */ + __u8 Reserved[3]; + __le32 TotalParameterCount; + __le32 TotalDataCount; + __le32 ParameterCount; + __le32 ParameterOffset; + __le32 ParameterDisplacement; + __le32 DataCount; + __le32 DataOffset; + __le32 DataDisplacement; + __u8 SetupCount; /* 0 */ + __u16 ByteCount; + /* __u8 Pad[3]; */ +} __packed TRANSACT_CHANGE_NOTIFY_RSP; + +struct cifs_quota_data { + __u32 rsrvd1; /* 0 */ + __u32 sid_size; + __u64 rsrvd2; /* 0 */ + __u64 space_used; + __u64 soft_limit; + __u64 hard_limit; + char sid[]; /* variable size? */ +} __packed; + +/* quota sub commands */ +#define QUOTA_LIST_CONTINUE 0 +#define QUOTA_LIST_START 0x100 +#define QUOTA_FOR_SID 0x101 + +struct trans2_req { + /* struct smb_hdr hdr precedes. Set wct = 14+ */ + __le16 TotalParameterCount; + __le16 TotalDataCount; + __le16 MaxParameterCount; + __le16 MaxDataCount; + __u8 MaxSetupCount; + __u8 Reserved; + __le16 Flags; + __le32 Timeout; + __u16 Reserved2; + __le16 ParameterCount; + __le16 ParameterOffset; + __le16 DataCount; + __le16 DataOffset; + __u8 SetupCount; + __u8 Reserved3; + __le16 SubCommand; /* 1st setup word - SetupCount words follow */ + __le16 ByteCount; +} __packed; + +struct smb_t2_req { + struct smb_hdr hdr; + struct trans2_req t2_req; +} __packed; + +struct trans2_resp { + /* struct smb_hdr hdr precedes. Note wct = 10 + setup count */ + __le16 TotalParameterCount; + __le16 TotalDataCount; + __u16 Reserved; + __le16 ParameterCount; + __le16 ParameterOffset; + __le16 ParameterDisplacement; + __le16 DataCount; + __le16 DataOffset; + __le16 DataDisplacement; + __u8 SetupCount; + __u8 Reserved1; + /* SetupWords[SetupCount]; + __u16 ByteCount; + __u16 Reserved2;*/ + /* data area follows */ +} __packed; + +struct smb_t2_rsp { + struct smb_hdr hdr; + struct trans2_resp t2_rsp; +} __packed; + +/* PathInfo/FileInfo infolevels */ +#define SMB_INFO_STANDARD 1 +#define SMB_SET_FILE_EA 2 +#define SMB_QUERY_FILE_EA_SIZE 2 +#define SMB_INFO_QUERY_EAS_FROM_LIST 3 +#define SMB_INFO_QUERY_ALL_EAS 4 +#define SMB_INFO_IS_NAME_VALID 6 +#define SMB_QUERY_FILE_BASIC_INFO 0x101 +#define SMB_QUERY_FILE_STANDARD_INFO 0x102 +#define SMB_QUERY_FILE_EA_INFO 0x103 +#define SMB_QUERY_FILE_NAME_INFO 0x104 +#define SMB_QUERY_FILE_ALLOCATION_INFO 0x105 +#define SMB_QUERY_FILE_END_OF_FILEINFO 0x106 +#define SMB_QUERY_FILE_ALL_INFO 0x107 +#define SMB_QUERY_ALT_NAME_INFO 0x108 +#define SMB_QUERY_FILE_STREAM_INFO 0x109 +#define SMB_QUERY_FILE_COMPRESSION_INFO 0x10B +#define SMB_QUERY_FILE_UNIX_BASIC 0x200 +#define SMB_QUERY_FILE_UNIX_LINK 0x201 +#define SMB_QUERY_POSIX_ACL 0x204 +#define SMB_QUERY_XATTR 0x205 /* e.g. system EA name space */ +#define SMB_QUERY_ATTR_FLAGS 0x206 /* append,immutable etc. */ +#define SMB_QUERY_POSIX_PERMISSION 0x207 +#define SMB_QUERY_POSIX_LOCK 0x208 +/* #define SMB_POSIX_OPEN 0x209 */ +/* #define SMB_POSIX_UNLINK 0x20a */ +#define SMB_QUERY_FILE__UNIX_INFO2 0x20b +#define SMB_QUERY_FILE_INTERNAL_INFO 0x3ee +#define SMB_QUERY_FILE_ACCESS_INFO 0x3f0 +#define SMB_QUERY_FILE_NAME_INFO2 0x3f1 /* 0x30 bytes */ +#define SMB_QUERY_FILE_POSITION_INFO 0x3f6 +#define SMB_QUERY_FILE_MODE_INFO 0x3f8 +#define SMB_QUERY_FILE_ALGN_INFO 0x3f9 + + +#define SMB_SET_FILE_BASIC_INFO 0x101 +#define SMB_SET_FILE_DISPOSITION_INFO 0x102 +#define SMB_SET_FILE_ALLOCATION_INFO 0x103 +#define SMB_SET_FILE_END_OF_FILE_INFO 0x104 +#define SMB_SET_FILE_UNIX_BASIC 0x200 +#define SMB_SET_FILE_UNIX_LINK 0x201 +#define SMB_SET_FILE_UNIX_HLINK 0x203 +#define SMB_SET_POSIX_ACL 0x204 +#define SMB_SET_XATTR 0x205 +#define SMB_SET_ATTR_FLAGS 0x206 /* append, immutable etc. */ +#define SMB_SET_POSIX_LOCK 0x208 +#define SMB_POSIX_OPEN 0x209 +#define SMB_POSIX_UNLINK 0x20a +#define SMB_SET_FILE_UNIX_INFO2 0x20b +#define SMB_SET_FILE_BASIC_INFO2 0x3ec +#define SMB_SET_FILE_RENAME_INFORMATION 0x3f2 /* BB check if qpathinfo too */ +#define SMB_FILE_ALL_INFO2 0x3fa +#define SMB_SET_FILE_ALLOCATION_INFO2 0x3fb +#define SMB_SET_FILE_END_OF_FILE_INFO2 0x3fc +#define SMB_FILE_MOVE_CLUSTER_INFO 0x407 +#define SMB_FILE_QUOTA_INFO 0x408 +#define SMB_FILE_REPARSEPOINT_INFO 0x409 +#define SMB_FILE_MAXIMUM_INFO 0x40d + +/* Find File infolevels */ +#define SMB_FIND_FILE_INFO_STANDARD 0x001 +#define SMB_FIND_FILE_QUERY_EA_SIZE 0x002 +#define SMB_FIND_FILE_QUERY_EAS_FROM_LIST 0x003 +#define SMB_FIND_FILE_DIRECTORY_INFO 0x101 +#define SMB_FIND_FILE_FULL_DIRECTORY_INFO 0x102 +#define SMB_FIND_FILE_NAMES_INFO 0x103 +#define SMB_FIND_FILE_BOTH_DIRECTORY_INFO 0x104 +#define SMB_FIND_FILE_ID_FULL_DIR_INFO 0x105 +#define SMB_FIND_FILE_ID_BOTH_DIR_INFO 0x106 +#define SMB_FIND_FILE_UNIX 0x202 +/* #define SMB_FIND_FILE_POSIX_INFO 0x064 */ + +typedef struct smb_com_transaction2_qpi_req { + struct smb_hdr hdr; /* wct = 14+ */ + __le16 TotalParameterCount; + __le16 TotalDataCount; + __le16 MaxParameterCount; + __le16 MaxDataCount; + __u8 MaxSetupCount; + __u8 Reserved; + __le16 Flags; + __le32 Timeout; + __u16 Reserved2; + __le16 ParameterCount; + __le16 ParameterOffset; + __le16 DataCount; + __le16 DataOffset; + __u8 SetupCount; + __u8 Reserved3; + __le16 SubCommand; /* one setup word */ + __le16 ByteCount; + __u8 Pad; + __le16 InformationLevel; + __u32 Reserved4; + char FileName[]; +} __packed TRANSACTION2_QPI_REQ; + +typedef struct smb_com_transaction2_qpi_rsp { + struct smb_hdr hdr; /* wct = 10 + SetupCount */ + struct trans2_resp t2; + __u16 ByteCount; + __u16 Reserved2; /* parameter word is present for infolevels > 100 */ +} __packed TRANSACTION2_QPI_RSP; + +typedef struct smb_com_transaction2_spi_req { + struct smb_hdr hdr; /* wct = 15 */ + __le16 TotalParameterCount; + __le16 TotalDataCount; + __le16 MaxParameterCount; + __le16 MaxDataCount; + __u8 MaxSetupCount; + __u8 Reserved; + __le16 Flags; + __le32 Timeout; + __u16 Reserved2; + __le16 ParameterCount; + __le16 ParameterOffset; + __le16 DataCount; + __le16 DataOffset; + __u8 SetupCount; + __u8 Reserved3; + __le16 SubCommand; /* one setup word */ + __le16 ByteCount; + __u8 Pad; + __u16 Pad1; + __le16 InformationLevel; + __u32 Reserved4; + char FileName[]; +} __packed TRANSACTION2_SPI_REQ; + +typedef struct smb_com_transaction2_spi_rsp { + struct smb_hdr hdr; /* wct = 10 + SetupCount */ + struct trans2_resp t2; + __u16 ByteCount; + __u16 Reserved2; /* parameter word is present for infolevels > 100 */ +} __packed TRANSACTION2_SPI_RSP; + +struct set_file_rename { + __le32 overwrite; /* 1 = overwrite dest */ + __u32 root_fid; /* zero */ + __le32 target_name_len; + char target_name[]; /* Must be unicode */ +} __packed; + +struct smb_com_transaction2_sfi_req { + struct smb_hdr hdr; /* wct = 15 */ + __le16 TotalParameterCount; + __le16 TotalDataCount; + __le16 MaxParameterCount; + __le16 MaxDataCount; + __u8 MaxSetupCount; + __u8 Reserved; + __le16 Flags; + __le32 Timeout; + __u16 Reserved2; + __le16 ParameterCount; + __le16 ParameterOffset; + __le16 DataCount; + __le16 DataOffset; + __u8 SetupCount; + __u8 Reserved3; + __le16 SubCommand; /* one setup word */ + __le16 ByteCount; + __u8 Pad; + __u16 Pad1; + __u16 Fid; + __le16 InformationLevel; + __u16 Reserved4; + __u8 payload[]; +} __packed; + +struct smb_com_transaction2_sfi_rsp { + struct smb_hdr hdr; /* wct = 10 + SetupCount */ + struct trans2_resp t2; + __u16 ByteCount; + __u16 Reserved2; /* parameter word reserved - present for infolevels > 100 */ +} __packed; + +struct smb_t2_qfi_req { + struct smb_hdr hdr; + struct trans2_req t2; + __u8 Pad; + __u16 Fid; + __le16 InformationLevel; +} __packed; + +struct smb_t2_qfi_rsp { + struct smb_hdr hdr; /* wct = 10 + SetupCount */ + struct trans2_resp t2; + __u16 ByteCount; + __u16 Reserved2; /* parameter word reserved - present for infolevels > 100 */ +} __packed; + +/* + * Flags on T2 FINDFIRST and FINDNEXT + */ +#define CIFS_SEARCH_CLOSE_ALWAYS 0x0001 +#define CIFS_SEARCH_CLOSE_AT_END 0x0002 +#define CIFS_SEARCH_RETURN_RESUME 0x0004 +#define CIFS_SEARCH_CONTINUE_FROM_LAST 0x0008 +#define CIFS_SEARCH_BACKUP_SEARCH 0x0010 + +/* + * Size of the resume key on FINDFIRST and FINDNEXT calls + */ +#define CIFS_SMB_RESUME_KEY_SIZE 4 + +typedef struct smb_com_transaction2_ffirst_req { + struct smb_hdr hdr; /* wct = 15 */ + __le16 TotalParameterCount; + __le16 TotalDataCount; + __le16 MaxParameterCount; + __le16 MaxDataCount; + __u8 MaxSetupCount; + __u8 Reserved; + __le16 Flags; + __le32 Timeout; + __u16 Reserved2; + __le16 ParameterCount; + __le16 ParameterOffset; + __le16 DataCount; + __le16 DataOffset; + __u8 SetupCount; /* one */ + __u8 Reserved3; + __le16 SubCommand; /* TRANS2_FIND_FIRST */ + __le16 ByteCount; + __u8 Pad; + __le16 SearchAttributes; + __le16 SearchCount; + __le16 SearchFlags; + __le16 InformationLevel; + __le32 SearchStorageType; + char FileName[]; +} __packed TRANSACTION2_FFIRST_REQ; + +typedef struct smb_com_transaction2_ffirst_rsp { + struct smb_hdr hdr; /* wct = 10 */ + struct trans2_resp t2; + __u16 ByteCount; +} __packed TRANSACTION2_FFIRST_RSP; + +typedef struct smb_com_transaction2_ffirst_rsp_parms { + __u16 SearchHandle; + __le16 SearchCount; + __le16 EndofSearch; + __le16 EAErrorOffset; + __le16 LastNameOffset; +} __packed T2_FFIRST_RSP_PARMS; + +typedef struct smb_com_transaction2_fnext_req { + struct smb_hdr hdr; /* wct = 15 */ + __le16 TotalParameterCount; + __le16 TotalDataCount; + __le16 MaxParameterCount; + __le16 MaxDataCount; + __u8 MaxSetupCount; + __u8 Reserved; + __le16 Flags; + __le32 Timeout; + __u16 Reserved2; + __le16 ParameterCount; + __le16 ParameterOffset; + __le16 DataCount; + __le16 DataOffset; + __u8 SetupCount; /* one */ + __u8 Reserved3; + __le16 SubCommand; /* TRANS2_FIND_NEXT */ + __le16 ByteCount; + __u8 Pad; + __u16 SearchHandle; + __le16 SearchCount; + __le16 InformationLevel; + __u32 ResumeKey; + __le16 SearchFlags; + char ResumeFileName[]; +} __packed TRANSACTION2_FNEXT_REQ; + +typedef struct smb_com_transaction2_fnext_rsp { + struct smb_hdr hdr; /* wct = 10 */ + struct trans2_resp t2; + __u16 ByteCount; +} __packed TRANSACTION2_FNEXT_RSP; + +typedef struct smb_com_transaction2_fnext_rsp_parms { + __le16 SearchCount; + __le16 EndofSearch; + __le16 EAErrorOffset; + __le16 LastNameOffset; +} __packed T2_FNEXT_RSP_PARMS; + +/* QFSInfo Levels */ +#define SMB_INFO_ALLOCATION 1 +#define SMB_INFO_VOLUME 2 +#define SMB_QUERY_FS_VOLUME_INFO 0x102 +#define SMB_QUERY_FS_SIZE_INFO 0x103 +#define SMB_QUERY_FS_DEVICE_INFO 0x104 +#define SMB_QUERY_FS_ATTRIBUTE_INFO 0x105 +#define SMB_QUERY_CIFS_UNIX_INFO 0x200 +#define SMB_QUERY_POSIX_FS_INFO 0x201 +#define SMB_QUERY_POSIX_WHO_AM_I 0x202 +#define SMB_REQUEST_TRANSPORT_ENCRYPTION 0x203 +#define SMB_QUERY_FS_PROXY 0x204 /* WAFS enabled. Returns structure + FILE_SYSTEM__UNIX_INFO to tell + whether new NTIOCTL available + (0xACE) for WAN friendly SMB + operations to be carried */ +#define SMB_QUERY_LABEL_INFO 0x3ea +#define SMB_QUERY_FS_QUOTA_INFO 0x3ee +#define SMB_QUERY_FS_FULL_SIZE_INFO 0x3ef +#define SMB_QUERY_OBJECTID_INFO 0x3f0 + +typedef struct smb_com_transaction2_qfsi_req { + struct smb_hdr hdr; /* wct = 14+ */ + __le16 TotalParameterCount; + __le16 TotalDataCount; + __le16 MaxParameterCount; + __le16 MaxDataCount; + __u8 MaxSetupCount; + __u8 Reserved; + __le16 Flags; + __le32 Timeout; + __u16 Reserved2; + __le16 ParameterCount; + __le16 ParameterOffset; + __le16 DataCount; + __le16 DataOffset; + __u8 SetupCount; + __u8 Reserved3; + __le16 SubCommand; /* one setup word */ + __le16 ByteCount; + __u8 Pad; + __le16 InformationLevel; +} __packed TRANSACTION2_QFSI_REQ; + +typedef struct smb_com_transaction_qfsi_rsp { + struct smb_hdr hdr; /* wct = 10 + SetupCount */ + struct trans2_resp t2; + __u16 ByteCount; + __u8 Pad; /* may be three bytes? *//* followed by data area */ +} __packed TRANSACTION2_QFSI_RSP; + +typedef struct whoami_rsp_data { /* Query level 0x202 */ + __u32 flags; /* 0 = Authenticated user 1 = GUEST */ + __u32 mask; /* which flags bits server understands ie 0x0001 */ + __u64 unix_user_id; + __u64 unix_user_gid; + __u32 number_of_supplementary_gids; /* may be zero */ + __u32 number_of_sids; /* may be zero */ + __u32 length_of_sid_array; /* in bytes - may be zero */ + __u32 pad; /* reserved - MBZ */ + /* __u64 gid_array[0]; */ /* may be empty */ + /* __u8 * psid_list */ /* may be empty */ +} __packed WHOAMI_RSP_DATA; + +/* SETFSInfo Levels */ +#define SMB_SET_CIFS_UNIX_INFO 0x200 +/* level 0x203 is defined above in list of QFS info levels */ +/* #define SMB_REQUEST_TRANSPORT_ENCRYPTION 0x203 */ + +/* Level 0x200 request structure follows */ +typedef struct smb_com_transaction2_setfsi_req { + struct smb_hdr hdr; /* wct = 15 */ + __le16 TotalParameterCount; + __le16 TotalDataCount; + __le16 MaxParameterCount; + __le16 MaxDataCount; + __u8 MaxSetupCount; + __u8 Reserved; + __le16 Flags; + __le32 Timeout; + __u16 Reserved2; + __le16 ParameterCount; /* 4 */ + __le16 ParameterOffset; + __le16 DataCount; /* 12 */ + __le16 DataOffset; + __u8 SetupCount; /* one */ + __u8 Reserved3; + __le16 SubCommand; /* TRANS2_SET_FS_INFORMATION */ + __le16 ByteCount; + __u8 Pad; + __u16 FileNum; /* Parameters start. */ + __le16 InformationLevel;/* Parameters end. */ + __le16 ClientUnixMajor; /* Data start. */ + __le16 ClientUnixMinor; + __le64 ClientUnixCap; /* Data end */ +} __packed TRANSACTION2_SETFSI_REQ; + +/* level 0x203 request structure follows */ +typedef struct smb_com_transaction2_setfs_enc_req { + struct smb_hdr hdr; /* wct = 15 */ + __le16 TotalParameterCount; + __le16 TotalDataCount; + __le16 MaxParameterCount; + __le16 MaxDataCount; + __u8 MaxSetupCount; + __u8 Reserved; + __le16 Flags; + __le32 Timeout; + __u16 Reserved2; + __le16 ParameterCount; /* 4 */ + __le16 ParameterOffset; + __le16 DataCount; /* 12 */ + __le16 DataOffset; + __u8 SetupCount; /* one */ + __u8 Reserved3; + __le16 SubCommand; /* TRANS2_SET_FS_INFORMATION */ + __le16 ByteCount; + __u8 Pad; + __u16 Reserved4; /* Parameters start. */ + __le16 InformationLevel;/* Parameters end. */ + /* NTLMSSP Blob, Data start. */ +} __packed TRANSACTION2_SETFSI_ENC_REQ; + +/* response for setfsinfo levels 0x200 and 0x203 */ +typedef struct smb_com_transaction2_setfsi_rsp { + struct smb_hdr hdr; /* wct = 10 */ + struct trans2_resp t2; + __u16 ByteCount; +} __packed TRANSACTION2_SETFSI_RSP; + +typedef struct smb_com_transaction2_get_dfs_refer_req { + struct smb_hdr hdr; /* wct = 15 */ + __le16 TotalParameterCount; + __le16 TotalDataCount; + __le16 MaxParameterCount; + __le16 MaxDataCount; + __u8 MaxSetupCount; + __u8 Reserved; + __le16 Flags; + __le32 Timeout; + __u16 Reserved2; + __le16 ParameterCount; + __le16 ParameterOffset; + __le16 DataCount; + __le16 DataOffset; + __u8 SetupCount; + __u8 Reserved3; + __le16 SubCommand; /* one setup word */ + __le16 ByteCount; + __u8 Pad[3]; /* Win2K has sent 0x0F01 (max response length + perhaps?) followed by one byte pad - doesn't + seem to matter though */ + __le16 MaxReferralLevel; + char RequestFileName[]; +} __packed TRANSACTION2_GET_DFS_REFER_REQ; + +#define DFS_VERSION cpu_to_le16(0x0003) + +/* DFS server target type */ +#define DFS_TYPE_LINK 0x0000 /* also for sysvol targets */ +#define DFS_TYPE_ROOT 0x0001 + +/* Referral Entry Flags */ +#define DFS_NAME_LIST_REF 0x0200 /* set for domain or DC referral responses */ +#define DFS_TARGET_SET_BOUNDARY 0x0400 /* only valid with version 4 dfs req */ + +typedef struct dfs_referral_level_3 { /* version 4 is same, + one flag bit */ + __le16 VersionNumber; /* must be 3 or 4 */ + __le16 Size; + __le16 ServerType; /* 0x0001 = root targets; 0x0000 = link targets */ + __le16 ReferralEntryFlags; + __le32 TimeToLive; + __le16 DfsPathOffset; + __le16 DfsAlternatePathOffset; + __le16 NetworkAddressOffset; /* offset of the link target */ + __u8 ServiceSiteGuid[16]; /* MBZ, ignored */ +} __packed REFERRAL3; + +struct get_dfs_referral_rsp { + __le16 PathConsumed; + __le16 NumberOfReferrals; + __le32 DFSFlags; + REFERRAL3 referrals[]; /* array of level 3 dfs_referral structures */ + /* followed by the strings pointed to by the referral structures */ +} __packed; + +typedef struct smb_com_transaction_get_dfs_refer_rsp { + struct smb_hdr hdr; /* wct = 10 */ + struct trans2_resp t2; + __u16 ByteCount; + __u8 Pad; + struct get_dfs_referral_rsp dfs_data; +} __packed TRANSACTION2_GET_DFS_REFER_RSP; + +/* DFS Flags */ +#define DFSREF_REFERRAL_SERVER 0x00000001 /* all targets are DFS roots */ +#define DFSREF_STORAGE_SERVER 0x00000002 /* no further ref requests needed */ +#define DFSREF_TARGET_FAILBACK 0x00000004 /* only for DFS referral version 4 */ + +/* + ************************************************************************ + * All structs for everything above the SMB PDUs themselves + * (such as the T2 level specific data) go here + ************************************************************************ + */ + +/* + * Information on a server + */ + +struct serverInfo { + char name[16]; + unsigned char versionMajor; + unsigned char versionMinor; + unsigned long type; + unsigned int commentOffset; +} __packed; + +/* + * The following structure is the format of the data returned on a NetShareEnum + * with level "90" (x5A) + */ + +struct shareInfo { + char shareName[13]; + char pad; + unsigned short type; + unsigned int commentOffset; +} __packed; + +struct aliasInfo { + char aliasName[9]; + char pad; + unsigned int commentOffset; + unsigned char type[2]; +} __packed; + +struct aliasInfo92 { + int aliasNameOffset; + int serverNameOffset; + int shareNameOffset; +} __packed; + +typedef struct { + __le32 fsid; + __le32 SectorsPerAllocationUnit; + __le32 TotalAllocationUnits; + __le32 FreeAllocationUnits; + __le16 BytesPerSector; +} __packed FILE_SYSTEM_ALLOC_INFO; + +typedef struct { + __le16 MajorVersionNumber; + __le16 MinorVersionNumber; + __le64 Capability; +} __packed FILE_SYSTEM_UNIX_INFO; /* Unix extension level 0x200*/ + +/* Version numbers for CIFS UNIX major and minor. */ +#define CIFS_UNIX_MAJOR_VERSION 1 +#define CIFS_UNIX_MINOR_VERSION 0 + +/* Linux/Unix extensions capability flags */ +#define CIFS_UNIX_FCNTL_CAP 0x00000001 /* support for fcntl locks */ +#define CIFS_UNIX_POSIX_ACL_CAP 0x00000002 /* support getfacl/setfacl */ +#define CIFS_UNIX_XATTR_CAP 0x00000004 /* support new namespace */ +#define CIFS_UNIX_EXTATTR_CAP 0x00000008 /* support chattr/chflag */ +#define CIFS_UNIX_POSIX_PATHNAMES_CAP 0x00000010 /* Allow POSIX path chars */ +#define CIFS_UNIX_POSIX_PATH_OPS_CAP 0x00000020 /* Allow new POSIX path based + calls including posix open + and posix unlink */ +#define CIFS_UNIX_LARGE_READ_CAP 0x00000040 /* support reads >128K (up to 0xFFFF00 */ +#define CIFS_UNIX_LARGE_WRITE_CAP 0x00000080 +#define CIFS_UNIX_TRANSPORT_ENCRYPTION_CAP 0x00000100 /* can do SPNEGO crypt */ +#define CIFS_UNIX_TRANSPORT_ENCRYPTION_MANDATORY_CAP 0x00000200 /* must do */ +#define CIFS_UNIX_PROXY_CAP 0x00000400 /* Proxy cap: 0xACE ioctl and QFS PROXY call */ +#ifdef CONFIG_CIFS_POSIX +/* presumably don't need the 0x20 POSIX_PATH_OPS_CAP since we never send + LockingX instead of posix locking call on unix sess (and we do not expect + LockingX to use different (ie Windows) semantics than posix locking on + the same session (if WINE needs to do this later, we can add this cap + back in later */ +/* #define CIFS_UNIX_CAP_MASK 0x000000fb */ +#define CIFS_UNIX_CAP_MASK 0x000003db +#else +#define CIFS_UNIX_CAP_MASK 0x00000013 +#endif /* CONFIG_CIFS_POSIX */ + + +#define CIFS_POSIX_EXTENSIONS 0x00000010 /* support for new QFSInfo */ + +/******************************************************************************/ +/* QueryFileInfo/QueryPathinfo (also for SetPath/SetFile) data buffer formats */ +/******************************************************************************/ +typedef struct { /* data block encoding of response to level 263 QPathInfo */ + struct_group_attr(common_attributes, __packed, + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le32 Attributes; + ); + __u32 Pad1; + __le64 AllocationSize; + __le64 EndOfFile; /* size ie offset to first free byte in file */ + __le32 NumberOfLinks; /* hard links */ + __u8 DeletePending; + __u8 Directory; + __u16 Pad2; + __le32 EASize; + __le32 FileNameLength; + union { + char __pad; + DECLARE_FLEX_ARRAY(char, FileName); + }; +} __packed FILE_ALL_INFO; /* level 0x107 QPathInfo */ + +typedef struct { + __le64 AllocationSize; + __le64 EndOfFile; /* size ie offset to first free byte in file */ + __le32 NumberOfLinks; /* hard links */ + __u8 DeletePending; + __u8 Directory; + __u16 Pad; +} __packed FILE_STANDARD_INFO; /* level 0x102 QPathInfo */ + + +/* defines for enumerating possible values of the Unix type field below */ +#define UNIX_FILE 0 +#define UNIX_DIR 1 +#define UNIX_SYMLINK 2 +#define UNIX_CHARDEV 3 +#define UNIX_BLOCKDEV 4 +#define UNIX_FIFO 5 +#define UNIX_SOCKET 6 +typedef struct { + __le64 EndOfFile; + __le64 NumOfBytes; + __le64 LastStatusChange; /*SNIA specs DCE time for the 3 time fields */ + __le64 LastAccessTime; + __le64 LastModificationTime; + __le64 Uid; + __le64 Gid; + __le32 Type; + __le64 DevMajor; + __le64 DevMinor; + __le64 UniqueId; + __le64 Permissions; + __le64 Nlinks; +} __packed FILE_UNIX_BASIC_INFO; /* level 0x200 QPathInfo */ + +typedef struct { + DECLARE_FLEX_ARRAY(char, LinkDest); +} __packed FILE_UNIX_LINK_INFO; /* level 0x201 QPathInfo */ + +/* The following three structures are needed only for + setting time to NT4 and some older servers via + the primitive DOS time format */ +typedef struct { + __u16 Day:5; + __u16 Month:4; + __u16 Year:7; +} __packed SMB_DATE; + +typedef struct { + __u16 TwoSeconds:5; + __u16 Minutes:6; + __u16 Hours:5; +} __packed SMB_TIME; + +typedef struct { + __le16 CreationDate; /* SMB Date see above */ + __le16 CreationTime; /* SMB Time */ + __le16 LastAccessDate; + __le16 LastAccessTime; + __le16 LastWriteDate; + __le16 LastWriteTime; + __le32 DataSize; /* File Size (EOF) */ + __le32 AllocationSize; + __le16 Attributes; /* verify not u32 */ + __le32 EASize; +} __packed FILE_INFO_STANDARD; /* level 1 SetPath/FileInfo */ + +typedef struct { + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le32 Attributes; + __u32 Pad; +} __packed FILE_BASIC_INFO; /* size info, level 0x101 */ + +struct file_allocation_info { + __le64 AllocationSize; /* Note old Samba srvr rounds this up too much */ +} __packed; /* size used on disk, for level 0x103 for set, 0x105 for query */ + +struct file_end_of_file_info { + __le64 FileSize; /* offset to end of file */ +} __packed; /* size info, level 0x104 for set, 0x106 for query */ + +struct file_alt_name_info { + DECLARE_FLEX_ARRAY(__u8, alt_name); +} __packed; /* level 0x0108 */ + +struct file_stream_info { + __le32 number_of_streams; /* BB check sizes and verify location */ + /* followed by info on streams themselves + u64 size; + u64 allocation_size + stream info */ +}; /* level 0x109 */ + +struct file_compression_info { + __le64 compressed_size; + __le16 format; + __u8 unit_shift; + __u8 ch_shift; + __u8 cl_shift; + __u8 pad[3]; +} __packed; /* level 0x10b */ + +/* POSIX ACL set/query path info structures */ +#define CIFS_ACL_VERSION 1 +struct cifs_posix_ace { /* access control entry (ACE) */ + __u8 cifs_e_tag; + __u8 cifs_e_perm; + __le64 cifs_uid; /* or gid */ +} __packed; + +struct cifs_posix_acl { /* access control list (ACL) */ + __le16 version; + __le16 access_entry_count; /* access ACL - count of entries */ + __le16 default_entry_count; /* default ACL - count of entries */ + struct cifs_posix_ace ace_array[]; + /* followed by struct cifs_posix_ace default_ace_array[] */ +} __packed; /* level 0x204 */ + +/* types of access control entries already defined in posix_acl.h */ +/* #define CIFS_POSIX_ACL_USER_OBJ 0x01 +#define CIFS_POSIX_ACL_USER 0x02 +#define CIFS_POSIX_ACL_GROUP_OBJ 0x04 +#define CIFS_POSIX_ACL_GROUP 0x08 +#define CIFS_POSIX_ACL_MASK 0x10 +#define CIFS_POSIX_ACL_OTHER 0x20 */ + +/* types of perms */ +/* #define CIFS_POSIX_ACL_EXECUTE 0x01 +#define CIFS_POSIX_ACL_WRITE 0x02 +#define CIFS_POSIX_ACL_READ 0x04 */ + +/* end of POSIX ACL definitions */ + +/* POSIX Open Flags */ +#define SMB_O_RDONLY 0x1 +#define SMB_O_WRONLY 0x2 +#define SMB_O_RDWR 0x4 +#define SMB_O_CREAT 0x10 +#define SMB_O_EXCL 0x20 +#define SMB_O_TRUNC 0x40 +#define SMB_O_APPEND 0x80 +#define SMB_O_SYNC 0x100 +#define SMB_O_DIRECTORY 0x200 +#define SMB_O_NOFOLLOW 0x400 +#define SMB_O_DIRECT 0x800 + +typedef struct { + __le32 OpenFlags; /* same as NT CreateX */ + __le32 PosixOpenFlags; + __le64 Permissions; + __le16 Level; /* reply level requested (see QPathInfo levels) */ +} __packed OPEN_PSX_REQ; /* level 0x209 SetPathInfo data */ + +typedef struct { + __le16 OplockFlags; + __u16 Fid; + __le32 CreateAction; + __le16 ReturnedLevel; + __le16 Pad; + /* struct following varies based on requested level */ +} __packed OPEN_PSX_RSP; /* level 0x209 SetPathInfo data */ + +#define SMB_POSIX_UNLINK_FILE_TARGET 0 +#define SMB_POSIX_UNLINK_DIRECTORY_TARGET 1 + +struct unlink_psx_rq { /* level 0x20a SetPathInfo */ + __le16 type; +} __packed; + +struct file_internal_info { + __le64 UniqueId; /* inode number */ +} __packed; /* level 0x3ee */ + +struct file_mode_info { + __le32 Mode; +} __packed; /* level 0x3f8 */ + +struct file_attrib_tag { + __le32 Attribute; + __le32 ReparseTag; +} __packed; /* level 0x40b */ + + +/********************************************************/ +/* FindFirst/FindNext transact2 data buffer formats */ +/********************************************************/ + +typedef struct { + __le32 NextEntryOffset; + __u32 ResumeKey; /* as with FileIndex - no need to convert */ + FILE_UNIX_BASIC_INFO basic; + union { + char __pad; + DECLARE_FLEX_ARRAY(char, FileName); + }; +} __packed FILE_UNIX_INFO; /* level 0x202 */ + +typedef struct { + __u32 ResumeKey; + __le16 CreationDate; /* SMB Date */ + __le16 CreationTime; /* SMB Time */ + __le16 LastAccessDate; + __le16 LastAccessTime; + __le16 LastWriteDate; + __le16 LastWriteTime; + __le32 DataSize; /* File Size (EOF) */ + __le32 AllocationSize; + __le16 Attributes; /* verify not u32 */ + __u8 FileNameLength; + char FileName[]; +} __packed FIND_FILE_STANDARD_INFO; /* level 0x1 FF resp data */ + + +struct fea { + unsigned char EA_flags; + __u8 name_len; + __le16 value_len; + char name[]; + /* optionally followed by value */ +} __packed; +/* flags for _FEA.fEA */ +#define FEA_NEEDEA 0x80 /* need EA bit */ + +struct fealist { + __le32 list_len; + struct fea list; +} __packed; + +/* used to hold an arbitrary blob of data */ +struct data_blob { + __u8 *data; + size_t length; + void (*free) (struct data_blob *data_blob); +} __packed; + + +#ifdef CONFIG_CIFS_POSIX +/* + For better POSIX semantics from Linux client, (even better + than the existing CIFS Unix Extensions) we need updated PDUs for: + + 1) PosixCreateX - to set and return the mode, inode#, device info and + perhaps add a CreateDevice - to create Pipes and other special .inodes + Also note POSIX open flags + 2) Close - to return the last write time to do cache across close + more safely + 3) FindFirst return unique inode number - what about resume key, two + forms short (matches readdir) and full (enough info to cache inodes) + 4) Mkdir - set mode + + And under consideration: + 5) FindClose2 (return nanosecond timestamp ??) + 6) Use nanosecond timestamps throughout all time fields if + corresponding attribute flag is set + 7) sendfile - handle based copy + + what about fixing 64 bit alignment + + There are also various legacy SMB/CIFS requests used as is + + From existing Lanman and NTLM dialects: + -------------------------------------- + NEGOTIATE + SESSION_SETUP_ANDX (BB which?) + TREE_CONNECT_ANDX (BB which wct?) + TREE_DISCONNECT (BB add volume timestamp on response) + LOGOFF_ANDX + DELETE (note delete open file behavior) + DELETE_DIRECTORY + READ_AND_X + WRITE_AND_X + LOCKING_AND_X (note posix lock semantics) + RENAME (note rename across dirs and open file rename posix behaviors) + NT_RENAME (for hardlinks) Is this good enough for all features? + FIND_CLOSE2 + TRANSACTION2 (18 cases) + SMB_SET_FILE_END_OF_FILE_INFO2 SMB_SET_PATH_END_OF_FILE_INFO2 + (BB verify that never need to set allocation size) + SMB_SET_FILE_BASIC_INFO2 (setting times - BB can it be done via + Unix ext?) + + COPY (note support for copy across directories) - FUTURE, OPTIONAL + setting/getting OS/2 EAs - FUTURE (BB can this handle + setting Linux xattrs perfectly) - OPTIONAL + dnotify - FUTURE, OPTIONAL + quota - FUTURE, OPTIONAL + + Note that various requests implemented for NT interop such as + NT_TRANSACT (IOCTL) QueryReparseInfo + are unneeded to servers compliant with the CIFS POSIX extensions + + From CIFS Unix Extensions: + ------------------------- + T2 SET_PATH_INFO (SMB_SET_FILE_UNIX_LINK) for symlinks + T2 SET_PATH_INFO (SMB_SET_FILE_BASIC_INFO2) + T2 QUERY_PATH_INFO (SMB_QUERY_FILE_UNIX_LINK) + T2 QUERY_PATH_INFO (SMB_QUERY_FILE_UNIX_BASIC) BB check for missing + inode fields + Actually a need QUERY_FILE_UNIX_INFO + since has inode num + BB what about a) blksize/blkbits/blocks + b) i_version + c) i_rdev + d) notify mask? + e) generation + f) size_seqcount + T2 FIND_FIRST/FIND_NEXT FIND_FILE_UNIX + TRANS2_GET_DFS_REFERRAL - OPTIONAL but recommended + T2_QFS_INFO QueryDevice/AttributeInfo - OPTIONAL + */ + +/* xsymlink is a symlink format (used by MacOS) that can be used + to save symlink info in a regular file when + mounted to operating systems that do not + support the cifs Unix extensions or EAs (for xattr + based symlinks). For such a file to be recognized + as containing symlink data: + + 1) file size must be 1067, + 2) signature must begin file data, + 3) length field must be set to ASCII representation + of a number which is less than or equal to 1024, + 4) md5 must match that of the path data */ + +struct xsymlink { + /* 1067 bytes */ + char signature[4]; /* XSym */ /* not null terminated */ + char cr0; /* \n */ +/* ASCII representation of length (4 bytes decimal) terminated by \n not null */ + char length[4]; + char cr1; /* \n */ +/* md5 of valid subset of path ie path[0] through path[length-1] */ + __u8 md5[32]; + char cr2; /* \n */ +/* if room left, then end with \n then 0x20s by convention but not required */ + char path[1024]; +} __packed; + +typedef struct file_xattr_info { + /* BB do we need another field for flags? BB */ + __u32 xattr_name_len; + __u32 xattr_value_len; + char xattr_name[]; + /* followed by xattr_value[xattr_value_len], no pad */ +} __packed FILE_XATTR_INFO; /* extended attribute info level 0x205 */ + +/* flags for lsattr and chflags commands removed arein uapi/linux/fs.h */ + +typedef struct file_chattr_info { + __le64 mask; /* list of all possible attribute bits */ + __le64 mode; /* list of actual attribute bits on this inode */ +} __packed FILE_CHATTR_INFO; /* ext attributes (chattr, chflags) level 0x206 */ +#endif /* POSIX */ + +#endif /* _SMB1PDU_H */ diff --git a/fs/smb/client/smb1proto.h b/fs/smb/client/smb1proto.h new file mode 100644 index 000000000000..42569bbcf6fd --- /dev/null +++ b/fs/smb/client/smb1proto.h @@ -0,0 +1,335 @@ +/* SPDX-License-Identifier: LGPL-2.1 */ +/* + * + * Copyright (c) International Business Machines Corp., 2002,2008 + * Author(s): Steve French (sfrench@us.ibm.com) + * + */ +#ifndef _SMB1PROTO_H +#define _SMB1PROTO_H + +#include <linux/uidgid_types.h> +#include <linux/unaligned.h> +#include "../common/smb2pdu.h" +#include "cifsglob.h" + +struct cifs_unix_set_info_args { + __u64 ctime; + __u64 atime; + __u64 mtime; + __u64 mode; + kuid_t uid; + kgid_t gid; + dev_t device; +}; + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY + +/* + * cifssmb.c + */ +int small_smb_init_no_tc(const int smb_command, const int wct, + struct cifs_ses *ses, void **request_buf); +int CIFSSMBNegotiate(const unsigned int xid, struct cifs_ses *ses, + struct TCP_Server_Info *server); +int CIFSTCon(const unsigned int xid, struct cifs_ses *ses, const char *tree, + struct cifs_tcon *tcon, const struct nls_table *nls_codepage); +int CIFSSMBTDis(const unsigned int xid, struct cifs_tcon *tcon); +int CIFSSMBEcho(struct TCP_Server_Info *server); +int CIFSSMBLogoff(const unsigned int xid, struct cifs_ses *ses); +int CIFSPOSIXDelFile(const unsigned int xid, struct cifs_tcon *tcon, + const char *fileName, __u16 type, + const struct nls_table *nls_codepage, int remap); +int CIFSSMBDelFile(const unsigned int xid, struct cifs_tcon *tcon, + const char *name, struct cifs_sb_info *cifs_sb, + struct dentry *dentry); +int CIFSSMBRmDir(const unsigned int xid, struct cifs_tcon *tcon, + const char *name, struct cifs_sb_info *cifs_sb); +int CIFSSMBMkDir(const unsigned int xid, struct inode *inode, umode_t mode, + struct cifs_tcon *tcon, const char *name, + struct cifs_sb_info *cifs_sb); +int CIFSPOSIXCreate(const unsigned int xid, struct cifs_tcon *tcon, + __u32 posix_flags, __u64 mode, __u16 *netfid, + FILE_UNIX_BASIC_INFO *pRetData, __u32 *pOplock, + const char *name, const struct nls_table *nls_codepage, + int remap); +int SMBLegacyOpen(const unsigned int xid, struct cifs_tcon *tcon, + const char *fileName, const int openDisposition, + const int access_flags, const int create_options, + __u16 *netfid, int *pOplock, FILE_ALL_INFO *pfile_info, + const struct nls_table *nls_codepage, int remap); +int CIFS_open(const unsigned int xid, struct cifs_open_parms *oparms, + int *oplock, FILE_ALL_INFO *buf); +int cifs_async_readv(struct cifs_io_subrequest *rdata); +int CIFSSMBRead(const unsigned int xid, struct cifs_io_parms *io_parms, + unsigned int *nbytes, char **buf, int *pbuf_type); +int CIFSSMBWrite(const unsigned int xid, struct cifs_io_parms *io_parms, + unsigned int *nbytes, const char *buf); +void cifs_async_writev(struct cifs_io_subrequest *wdata); +int CIFSSMBWrite2(const unsigned int xid, struct cifs_io_parms *io_parms, + unsigned int *nbytes, struct kvec *iov, int n_vec); +int cifs_lockv(const unsigned int xid, struct cifs_tcon *tcon, + const __u16 netfid, const __u8 lock_type, + const __u32 num_unlock, const __u32 num_lock, + LOCKING_ANDX_RANGE *buf); +int CIFSSMBLock(const unsigned int xid, struct cifs_tcon *tcon, + const __u16 smb_file_id, const __u32 netpid, const __u64 len, + const __u64 offset, const __u32 numUnlock, const __u32 numLock, + const __u8 lockType, const bool waitFlag, + const __u8 oplock_level); +int CIFSSMBPosixLock(const unsigned int xid, struct cifs_tcon *tcon, + const __u16 smb_file_id, const __u32 netpid, + const loff_t start_offset, const __u64 len, + struct file_lock *pLockData, const __u16 lock_type, + const bool waitFlag); +int CIFSSMBClose(const unsigned int xid, struct cifs_tcon *tcon, + int smb_file_id); +int CIFSSMBFlush(const unsigned int xid, struct cifs_tcon *tcon, + int smb_file_id); +int CIFSSMBRename(const unsigned int xid, struct cifs_tcon *tcon, + struct dentry *source_dentry, const char *from_name, + const char *to_name, struct cifs_sb_info *cifs_sb); +int CIFSSMBRenameOpenFile(const unsigned int xid, struct cifs_tcon *pTcon, + int netfid, const char *target_name, + const struct nls_table *nls_codepage, int remap); +int CIFSUnixCreateSymLink(const unsigned int xid, struct cifs_tcon *tcon, + const char *fromName, const char *toName, + const struct nls_table *nls_codepage, int remap); +int CIFSUnixCreateHardLink(const unsigned int xid, struct cifs_tcon *tcon, + const char *fromName, const char *toName, + const struct nls_table *nls_codepage, int remap); +int CIFSCreateHardLink(const unsigned int xid, struct cifs_tcon *tcon, + struct dentry *source_dentry, const char *from_name, + const char *to_name, struct cifs_sb_info *cifs_sb); +int CIFSSMBUnixQuerySymLink(const unsigned int xid, struct cifs_tcon *tcon, + const unsigned char *searchName, + char **symlinkinfo, + const struct nls_table *nls_codepage, int remap); +int cifs_query_reparse_point(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, + const char *full_path, u32 *tag, struct kvec *rsp, + int *rsp_buftype); +struct inode *cifs_create_reparse_inode(struct cifs_open_info_data *data, + struct super_block *sb, + const unsigned int xid, + struct cifs_tcon *tcon, + const char *full_path, bool directory, + struct kvec *reparse_iov, + struct kvec *xattr_iov); +int CIFSSMB_set_compression(const unsigned int xid, struct cifs_tcon *tcon, + __u16 fid); +int cifs_do_get_acl(const unsigned int xid, struct cifs_tcon *tcon, + const unsigned char *searchName, struct posix_acl **acl, + const int acl_type, const struct nls_table *nls_codepage, + int remap); +int cifs_do_set_acl(const unsigned int xid, struct cifs_tcon *tcon, + const unsigned char *fileName, const struct posix_acl *acl, + const int acl_type, const struct nls_table *nls_codepage, + int remap); +int CIFSGetExtAttr(const unsigned int xid, struct cifs_tcon *tcon, + const int netfid, __u64 *pExtAttrBits, __u64 *pMask); +int CIFSSMBGetCIFSACL(const unsigned int xid, struct cifs_tcon *tcon, + __u16 fid, struct smb_ntsd **acl_inf, __u32 *pbuflen, + __u32 info); +int CIFSSMBSetCIFSACL(const unsigned int xid, struct cifs_tcon *tcon, + __u16 fid, struct smb_ntsd *pntsd, __u32 acllen, + int aclflag); +int SMBQueryInformation(const unsigned int xid, struct cifs_tcon *tcon, + const char *search_name, FILE_ALL_INFO *data, + const struct nls_table *nls_codepage, int remap); +int CIFSSMBQFileInfo(const unsigned int xid, struct cifs_tcon *tcon, + u16 netfid, FILE_ALL_INFO *pFindData); +int CIFSSMBQPathInfo(const unsigned int xid, struct cifs_tcon *tcon, + const char *search_name, FILE_ALL_INFO *data, + int legacy /* old style infolevel */, + const struct nls_table *nls_codepage, int remap); +int CIFSSMBUnixQFileInfo(const unsigned int xid, struct cifs_tcon *tcon, + u16 netfid, FILE_UNIX_BASIC_INFO *pFindData); +int CIFSSMBUnixQPathInfo(const unsigned int xid, struct cifs_tcon *tcon, + const unsigned char *searchName, + FILE_UNIX_BASIC_INFO *pFindData, + const struct nls_table *nls_codepage, int remap); +int CIFSFindFirst(const unsigned int xid, struct cifs_tcon *tcon, + const char *searchName, struct cifs_sb_info *cifs_sb, + __u16 *pnetfid, __u16 search_flags, + struct cifs_search_info *psrch_inf, bool msearch); +int CIFSFindNext(const unsigned int xid, struct cifs_tcon *tcon, + __u16 searchHandle, __u16 search_flags, + struct cifs_search_info *psrch_inf); +int CIFSFindClose(const unsigned int xid, struct cifs_tcon *tcon, + const __u16 searchHandle); +int CIFSGetSrvInodeNumber(const unsigned int xid, struct cifs_tcon *tcon, + const char *search_name, __u64 *inode_number, + const struct nls_table *nls_codepage, int remap); +int CIFSGetDFSRefer(const unsigned int xid, struct cifs_ses *ses, + const char *search_name, + struct dfs_info3_param **target_nodes, + unsigned int *num_of_nodes, + const struct nls_table *nls_codepage, int remap); +int SMBOldQFSInfo(const unsigned int xid, struct cifs_tcon *tcon, + struct kstatfs *FSData); +int CIFSSMBQFSInfo(const unsigned int xid, struct cifs_tcon *tcon, + struct kstatfs *FSData); +int CIFSSMBQFSAttributeInfo(const unsigned int xid, struct cifs_tcon *tcon); +int CIFSSMBQFSDeviceInfo(const unsigned int xid, struct cifs_tcon *tcon); +int CIFSSMBQFSUnixInfo(const unsigned int xid, struct cifs_tcon *tcon); +int CIFSSMBSetFSUnixInfo(const unsigned int xid, struct cifs_tcon *tcon, + __u64 cap); +int CIFSSMBQFSPosixInfo(const unsigned int xid, struct cifs_tcon *tcon, + struct kstatfs *FSData); +int CIFSSMBSetEOF(const unsigned int xid, struct cifs_tcon *tcon, + const char *file_name, __u64 size, + struct cifs_sb_info *cifs_sb, bool set_allocation, + struct dentry *dentry); +int CIFSSMBSetFileSize(const unsigned int xid, struct cifs_tcon *tcon, + struct cifsFileInfo *cfile, __u64 size, + bool set_allocation); +int SMBSetInformation(const unsigned int xid, struct cifs_tcon *tcon, + const char *fileName, __le32 attributes, + __le64 write_time, const struct nls_table *nls_codepage, + struct cifs_sb_info *cifs_sb); +int CIFSSMBSetFileInfo(const unsigned int xid, struct cifs_tcon *tcon, + const FILE_BASIC_INFO *data, __u16 fid, + __u32 pid_of_opener); +int CIFSSMBSetFileDisposition(const unsigned int xid, struct cifs_tcon *tcon, + bool delete_file, __u16 fid, + __u32 pid_of_opener); +int CIFSSMBSetPathInfo(const unsigned int xid, struct cifs_tcon *tcon, + const char *fileName, const FILE_BASIC_INFO *data, + const struct nls_table *nls_codepage, + struct cifs_sb_info *cifs_sb); +int CIFSSMBUnixSetFileInfo(const unsigned int xid, struct cifs_tcon *tcon, + const struct cifs_unix_set_info_args *args, u16 fid, + u32 pid_of_opener); +int CIFSSMBUnixSetPathInfo(const unsigned int xid, struct cifs_tcon *tcon, + const char *file_name, + const struct cifs_unix_set_info_args *args, + const struct nls_table *nls_codepage, int remap); +ssize_t CIFSSMBQAllEAs(const unsigned int xid, struct cifs_tcon *tcon, + const unsigned char *searchName, + const unsigned char *ea_name, char *EAData, + size_t buf_size, struct cifs_sb_info *cifs_sb); +int CIFSSMBSetEA(const unsigned int xid, struct cifs_tcon *tcon, + const char *fileName, const char *ea_name, + const void *ea_value, const __u16 ea_value_len, + const struct nls_table *nls_codepage, + struct cifs_sb_info *cifs_sb); + +/* + * smb1debug.c + */ +void cifs_dump_detail(void *buf, size_t buf_len, + struct TCP_Server_Info *server); + +/* + * smb1encrypt.c + */ +int cifs_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server, + __u32 *pexpected_response_sequence_number); +int cifs_verify_signature(struct smb_rqst *rqst, + struct TCP_Server_Info *server, + __u32 expected_sequence_number); + +/* + * smb1maperror.c + */ +int map_smb_to_linux_error(char *buf, bool logErr); +int map_and_check_smb_error(struct TCP_Server_Info *server, + struct mid_q_entry *mid, bool logErr); + +/* + * smb1misc.c + */ +unsigned int header_assemble(struct smb_hdr *buffer, char smb_command, + const struct cifs_tcon *treeCon, int word_count); +bool is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv); +unsigned int smbCalcSize(void *buf); + +/* + * smb1ops.c + */ +extern struct smb_version_operations smb1_operations; +extern struct smb_version_values smb1_values; + +void reset_cifs_unix_caps(unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, + struct smb3_fs_context *ctx); + +/* + * smb1session.c + */ +int CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses, + struct TCP_Server_Info *server, + const struct nls_table *nls_cp); + +/* + * smb1transport.c + */ +struct mid_q_entry *cifs_setup_async_request(struct TCP_Server_Info *server, + struct smb_rqst *rqst); +int SendReceiveNoRsp(const unsigned int xid, struct cifs_ses *ses, + char *in_buf, unsigned int in_len, int flags); +int cifs_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server, + bool log_error); +struct mid_q_entry *cifs_setup_request(struct cifs_ses *ses, + struct TCP_Server_Info *server, + struct smb_rqst *rqst); +int SendReceive2(const unsigned int xid, struct cifs_ses *ses, + struct kvec *iov, int n_vec, int *resp_buf_type /* ret */, + const int flags, struct kvec *resp_iov); +int SendReceive(const unsigned int xid, struct cifs_ses *ses, + struct smb_hdr *in_buf, unsigned int in_len, + struct smb_hdr *out_buf, int *pbytes_returned, + const int flags); +bool cifs_check_trans2(struct mid_q_entry *mid, struct TCP_Server_Info *server, + char *buf, int malformed); +int checkSMB(char *buf, unsigned int pdu_len, unsigned int total_read, + struct TCP_Server_Info *server); + + +static inline __u16 +get_mid(const struct smb_hdr *smb) +{ + return le16_to_cpu(smb->Mid); +} + +static inline bool +compare_mid(__u16 mid, const struct smb_hdr *smb) +{ + return mid == le16_to_cpu(smb->Mid); +} + +#define GETU16(var) (*((__u16 *)var)) /* BB check for endian issues */ +#define GETU32(var) (*((__u32 *)var)) /* BB check for endian issues */ + +/* given a pointer to an smb_hdr, retrieve a void pointer to the ByteCount */ +static inline void * +BCC(struct smb_hdr *smb) +{ + return (void *)smb + sizeof(*smb) + 2 * smb->WordCount; +} + +/* given a pointer to an smb_hdr retrieve the pointer to the byte area */ +#define pByteArea(smb_var) (BCC(smb_var) + 2) + +/* get the unconverted ByteCount for a SMB packet and return it */ +static inline __u16 +get_bcc(struct smb_hdr *hdr) +{ + __le16 *bc_ptr = (__le16 *)BCC(hdr); + + return get_unaligned_le16(bc_ptr); +} + +/* set the ByteCount for a SMB packet in little-endian */ +static inline void +put_bcc(__u16 count, struct smb_hdr *hdr) +{ + __le16 *bc_ptr = (__le16 *)BCC(hdr); + + put_unaligned_le16(count, bc_ptr); +} + +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + +#endif /* _SMB1PROTO_H */ diff --git a/fs/smb/client/smb1session.c b/fs/smb/client/smb1session.c new file mode 100644 index 000000000000..1cf6bd640fde --- /dev/null +++ b/fs/smb/client/smb1session.c @@ -0,0 +1,995 @@ +// SPDX-License-Identifier: LGPL-2.1 +/* + * + * SMB/CIFS session setup handling routines + * + * Copyright (c) International Business Machines Corp., 2006, 2009 + * Author(s): Steve French (sfrench@us.ibm.com) + * + */ + +#include "cifsproto.h" +#include "smb1proto.h" +#include "ntlmssp.h" +#include "nterr.h" +#include "cifs_spnego.h" +#include "cifs_unicode.h" +#include "cifs_debug.h" + +struct sess_data { + unsigned int xid; + struct cifs_ses *ses; + struct TCP_Server_Info *server; + struct nls_table *nls_cp; + void (*func)(struct sess_data *); + int result; + unsigned int in_len; + + /* we will send the SMB in three pieces: + * a fixed length beginning part, an optional + * SPNEGO blob (which can be zero length), and a + * last part which will include the strings + * and rest of bcc area. This allows us to avoid + * a large buffer 17K allocation + */ + int buf0_type; + struct kvec iov[3]; +}; + +static __u32 cifs_ssetup_hdr(struct cifs_ses *ses, + struct TCP_Server_Info *server, + SESSION_SETUP_ANDX *pSMB) +{ + __u32 capabilities = 0; + + /* init fields common to all four types of SessSetup */ + /* Note that offsets for first seven fields in req struct are same */ + /* in CIFS Specs so does not matter which of 3 forms of struct */ + /* that we use in next few lines */ + /* Note that header is initialized to zero in header_assemble */ + pSMB->req.AndXCommand = 0xFF; + pSMB->req.MaxBufferSize = cpu_to_le16(min_t(u32, + CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4, + USHRT_MAX)); + pSMB->req.MaxMpxCount = cpu_to_le16(server->maxReq); + pSMB->req.VcNumber = cpu_to_le16(1); + pSMB->req.SessionKey = server->session_key_id; + + /* Now no need to set SMBFLG_CASELESS or obsolete CANONICAL PATH */ + + /* BB verify whether signing required on neg or just auth frame (and NTLM case) */ + + capabilities = CAP_LARGE_FILES | CAP_NT_SMBS | CAP_LEVEL_II_OPLOCKS | + CAP_LARGE_WRITE_X | CAP_LARGE_READ_X; + + if (server->sign) + pSMB->req.hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE; + + if (ses->capabilities & CAP_UNICODE) { + pSMB->req.hdr.Flags2 |= SMBFLG2_UNICODE; + capabilities |= CAP_UNICODE; + } + if (ses->capabilities & CAP_STATUS32) { + pSMB->req.hdr.Flags2 |= SMBFLG2_ERR_STATUS; + capabilities |= CAP_STATUS32; + } + if (ses->capabilities & CAP_DFS) { + pSMB->req.hdr.Flags2 |= SMBFLG2_DFS; + capabilities |= CAP_DFS; + } + if (ses->capabilities & CAP_UNIX) + capabilities |= CAP_UNIX; + + return capabilities; +} + +static void +unicode_oslm_strings(char **pbcc_area, const struct nls_table *nls_cp) +{ + char *bcc_ptr = *pbcc_area; + int bytes_ret = 0; + + /* Copy OS version */ + bytes_ret = cifs_strtoUTF16((__le16 *)bcc_ptr, "Linux version ", 32, + nls_cp); + bcc_ptr += 2 * bytes_ret; + bytes_ret = cifs_strtoUTF16((__le16 *) bcc_ptr, init_utsname()->release, + 32, nls_cp); + bcc_ptr += 2 * bytes_ret; + bcc_ptr += 2; /* trailing null */ + + bytes_ret = cifs_strtoUTF16((__le16 *) bcc_ptr, CIFS_NETWORK_OPSYS, + 32, nls_cp); + bcc_ptr += 2 * bytes_ret; + bcc_ptr += 2; /* trailing null */ + + *pbcc_area = bcc_ptr; +} + +static void +ascii_oslm_strings(char **pbcc_area, const struct nls_table *nls_cp) +{ + char *bcc_ptr = *pbcc_area; + + strcpy(bcc_ptr, "Linux version "); + bcc_ptr += strlen("Linux version "); + strcpy(bcc_ptr, init_utsname()->release); + bcc_ptr += strlen(init_utsname()->release) + 1; + + strcpy(bcc_ptr, CIFS_NETWORK_OPSYS); + bcc_ptr += strlen(CIFS_NETWORK_OPSYS) + 1; + + *pbcc_area = bcc_ptr; +} + +static void unicode_domain_string(char **pbcc_area, struct cifs_ses *ses, + const struct nls_table *nls_cp) +{ + char *bcc_ptr = *pbcc_area; + int bytes_ret = 0; + + /* copy domain */ + if (ses->domainName == NULL) { + /* + * Sending null domain better than using a bogus domain name (as + * we did briefly in 2.6.18) since server will use its default + */ + *bcc_ptr = 0; + *(bcc_ptr+1) = 0; + bytes_ret = 0; + } else + bytes_ret = cifs_strtoUTF16((__le16 *) bcc_ptr, ses->domainName, + CIFS_MAX_DOMAINNAME_LEN, nls_cp); + bcc_ptr += 2 * bytes_ret; + bcc_ptr += 2; /* account for null terminator */ + + *pbcc_area = bcc_ptr; +} + +static void ascii_domain_string(char **pbcc_area, struct cifs_ses *ses, + const struct nls_table *nls_cp) +{ + char *bcc_ptr = *pbcc_area; + int len; + + /* copy domain */ + if (ses->domainName != NULL) { + len = strscpy(bcc_ptr, ses->domainName, CIFS_MAX_DOMAINNAME_LEN); + if (WARN_ON_ONCE(len < 0)) + len = CIFS_MAX_DOMAINNAME_LEN - 1; + bcc_ptr += len; + } /* else we send a null domain name so server will default to its own domain */ + *bcc_ptr = 0; + bcc_ptr++; + + *pbcc_area = bcc_ptr; +} + +static void unicode_ssetup_strings(char **pbcc_area, struct cifs_ses *ses, + const struct nls_table *nls_cp) +{ + char *bcc_ptr = *pbcc_area; + int bytes_ret = 0; + + /* BB FIXME add check that strings less than 335 or will need to send as arrays */ + + /* copy user */ + if (ses->user_name == NULL) { + /* null user mount */ + *bcc_ptr = 0; + *(bcc_ptr+1) = 0; + } else { + bytes_ret = cifs_strtoUTF16((__le16 *) bcc_ptr, ses->user_name, + CIFS_MAX_USERNAME_LEN, nls_cp); + } + bcc_ptr += 2 * bytes_ret; + bcc_ptr += 2; /* account for null termination */ + + unicode_domain_string(&bcc_ptr, ses, nls_cp); + unicode_oslm_strings(&bcc_ptr, nls_cp); + + *pbcc_area = bcc_ptr; +} + +static void ascii_ssetup_strings(char **pbcc_area, struct cifs_ses *ses, + const struct nls_table *nls_cp) +{ + char *bcc_ptr = *pbcc_area; + int len; + + /* copy user */ + /* BB what about null user mounts - check that we do this BB */ + /* copy user */ + if (ses->user_name != NULL) { + len = strscpy(bcc_ptr, ses->user_name, CIFS_MAX_USERNAME_LEN); + if (WARN_ON_ONCE(len < 0)) + len = CIFS_MAX_USERNAME_LEN - 1; + bcc_ptr += len; + } + /* else null user mount */ + *bcc_ptr = 0; + bcc_ptr++; /* account for null termination */ + + /* BB check for overflow here */ + + ascii_domain_string(&bcc_ptr, ses, nls_cp); + ascii_oslm_strings(&bcc_ptr, nls_cp); + + *pbcc_area = bcc_ptr; +} + +static void +decode_unicode_ssetup(char **pbcc_area, int bleft, struct cifs_ses *ses, + const struct nls_table *nls_cp) +{ + int len; + char *data = *pbcc_area; + + cifs_dbg(FYI, "bleft %d\n", bleft); + + kfree(ses->serverOS); + ses->serverOS = cifs_strndup_from_utf16(data, bleft, true, nls_cp); + cifs_dbg(FYI, "serverOS=%s\n", ses->serverOS); + len = (UniStrnlen((wchar_t *) data, bleft / 2) * 2) + 2; + data += len; + bleft -= len; + if (bleft <= 0) + return; + + kfree(ses->serverNOS); + ses->serverNOS = cifs_strndup_from_utf16(data, bleft, true, nls_cp); + cifs_dbg(FYI, "serverNOS=%s\n", ses->serverNOS); + len = (UniStrnlen((wchar_t *) data, bleft / 2) * 2) + 2; + data += len; + bleft -= len; + if (bleft <= 0) + return; + + kfree(ses->serverDomain); + ses->serverDomain = cifs_strndup_from_utf16(data, bleft, true, nls_cp); + cifs_dbg(FYI, "serverDomain=%s\n", ses->serverDomain); + + return; +} + +static void decode_ascii_ssetup(char **pbcc_area, __u16 bleft, + struct cifs_ses *ses, + const struct nls_table *nls_cp) +{ + int len; + char *bcc_ptr = *pbcc_area; + + cifs_dbg(FYI, "decode sessetup ascii. bleft %d\n", bleft); + + len = strnlen(bcc_ptr, bleft); + if (len >= bleft) + return; + + kfree(ses->serverOS); + + ses->serverOS = kmalloc(len + 1, GFP_KERNEL); + if (ses->serverOS) { + memcpy(ses->serverOS, bcc_ptr, len); + ses->serverOS[len] = 0; + if (strncmp(ses->serverOS, "OS/2", 4) == 0) + cifs_dbg(FYI, "OS/2 server\n"); + } + + bcc_ptr += len + 1; + bleft -= len + 1; + + len = strnlen(bcc_ptr, bleft); + if (len >= bleft) + return; + + kfree(ses->serverNOS); + + ses->serverNOS = kmalloc(len + 1, GFP_KERNEL); + if (ses->serverNOS) { + memcpy(ses->serverNOS, bcc_ptr, len); + ses->serverNOS[len] = 0; + } + + bcc_ptr += len + 1; + bleft -= len + 1; + + len = strnlen(bcc_ptr, bleft); + if (len > bleft) + return; + + /* + * No domain field in LANMAN case. Domain is + * returned by old servers in the SMB negprot response + * + * BB For newer servers which do not support Unicode, + * but thus do return domain here, we could add parsing + * for it later, but it is not very important + */ + cifs_dbg(FYI, "ascii: bytes left %d\n", bleft); +} + +static int +sess_alloc_buffer(struct sess_data *sess_data, int wct) +{ + int rc; + struct cifs_ses *ses = sess_data->ses; + struct smb_hdr *smb_buf; + + rc = small_smb_init_no_tc(SMB_COM_SESSION_SETUP_ANDX, wct, ses, + (void **)&smb_buf); + + if (rc < 0) + return rc; + + sess_data->in_len = rc; + sess_data->iov[0].iov_base = (char *)smb_buf; + sess_data->iov[0].iov_len = sess_data->in_len; + /* + * This variable will be used to clear the buffer + * allocated above in case of any error in the calling function. + */ + sess_data->buf0_type = CIFS_SMALL_BUFFER; + + /* 2000 big enough to fit max user, domain, NOS name etc. */ + sess_data->iov[2].iov_base = kmalloc(2000, GFP_KERNEL); + if (!sess_data->iov[2].iov_base) { + rc = -ENOMEM; + goto out_free_smb_buf; + } + + return 0; + +out_free_smb_buf: + cifs_small_buf_release(smb_buf); + sess_data->iov[0].iov_base = NULL; + sess_data->iov[0].iov_len = 0; + sess_data->buf0_type = CIFS_NO_BUFFER; + return rc; +} + +static void +sess_free_buffer(struct sess_data *sess_data) +{ + struct kvec *iov = sess_data->iov; + + /* + * Zero the session data before freeing, as it might contain sensitive info (keys, etc). + * Note that iov[1] is already freed by caller. + */ + if (sess_data->buf0_type != CIFS_NO_BUFFER && iov[0].iov_base) + memzero_explicit(iov[0].iov_base, iov[0].iov_len); + + free_rsp_buf(sess_data->buf0_type, iov[0].iov_base); + sess_data->buf0_type = CIFS_NO_BUFFER; + kfree_sensitive(iov[2].iov_base); +} + +static int +sess_establish_session(struct sess_data *sess_data) +{ + struct cifs_ses *ses = sess_data->ses; + struct TCP_Server_Info *server = sess_data->server; + + cifs_server_lock(server); + if (!server->session_estab) { + if (server->sign) { + server->session_key.response = + kmemdup(ses->auth_key.response, + ses->auth_key.len, GFP_KERNEL); + if (!server->session_key.response) { + cifs_server_unlock(server); + return -ENOMEM; + } + server->session_key.len = + ses->auth_key.len; + } + server->sequence_number = 0x2; + server->session_estab = true; + } + cifs_server_unlock(server); + + cifs_dbg(FYI, "CIFS session established successfully\n"); + return 0; +} + +static int +sess_sendreceive(struct sess_data *sess_data) +{ + int rc; + struct smb_hdr *smb_buf = (struct smb_hdr *) sess_data->iov[0].iov_base; + __u16 count; + struct kvec rsp_iov = { NULL, 0 }; + + count = sess_data->iov[1].iov_len + sess_data->iov[2].iov_len; + sess_data->in_len += count; + put_bcc(count, smb_buf); + + rc = SendReceive2(sess_data->xid, sess_data->ses, + sess_data->iov, 3 /* num_iovecs */, + &sess_data->buf0_type, + CIFS_LOG_ERROR, &rsp_iov); + cifs_small_buf_release(sess_data->iov[0].iov_base); + memcpy(&sess_data->iov[0], &rsp_iov, sizeof(struct kvec)); + + return rc; +} + +static void +sess_auth_ntlmv2(struct sess_data *sess_data) +{ + int rc = 0; + struct smb_hdr *smb_buf; + SESSION_SETUP_ANDX *pSMB; + char *bcc_ptr; + struct cifs_ses *ses = sess_data->ses; + struct TCP_Server_Info *server = sess_data->server; + __u32 capabilities; + __u16 bytes_remaining; + + /* old style NTLM sessionsetup */ + /* wct = 13 */ + rc = sess_alloc_buffer(sess_data, 13); + if (rc) + goto out; + + pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base; + bcc_ptr = sess_data->iov[2].iov_base; + capabilities = cifs_ssetup_hdr(ses, server, pSMB); + + pSMB->req_no_secext.Capabilities = cpu_to_le32(capabilities); + + /* LM2 password would be here if we supported it */ + pSMB->req_no_secext.CaseInsensitivePasswordLength = 0; + + if (ses->user_name != NULL) { + /* calculate nlmv2 response and session key */ + rc = setup_ntlmv2_rsp(ses, sess_data->nls_cp); + if (rc) { + cifs_dbg(VFS, "Error %d during NTLMv2 authentication\n", rc); + goto out; + } + + memcpy(bcc_ptr, ses->auth_key.response + CIFS_SESS_KEY_SIZE, + ses->auth_key.len - CIFS_SESS_KEY_SIZE); + bcc_ptr += ses->auth_key.len - CIFS_SESS_KEY_SIZE; + + /* set case sensitive password length after tilen may get + * assigned, tilen is 0 otherwise. + */ + pSMB->req_no_secext.CaseSensitivePasswordLength = + cpu_to_le16(ses->auth_key.len - CIFS_SESS_KEY_SIZE); + } else { + pSMB->req_no_secext.CaseSensitivePasswordLength = 0; + } + + if (ses->capabilities & CAP_UNICODE) { + if (!IS_ALIGNED(sess_data->iov[0].iov_len, 2)) { + *bcc_ptr = 0; + bcc_ptr++; + } + unicode_ssetup_strings(&bcc_ptr, ses, sess_data->nls_cp); + } else { + ascii_ssetup_strings(&bcc_ptr, ses, sess_data->nls_cp); + } + + + sess_data->iov[2].iov_len = (long) bcc_ptr - + (long) sess_data->iov[2].iov_base; + + rc = sess_sendreceive(sess_data); + if (rc) + goto out; + + pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base; + smb_buf = (struct smb_hdr *)sess_data->iov[0].iov_base; + + if (smb_buf->WordCount != 3) { + rc = smb_EIO1(smb_eio_trace_sess_nl2_wcc, smb_buf->WordCount); + cifs_dbg(VFS, "bad word count %d\n", smb_buf->WordCount); + goto out; + } + + if (le16_to_cpu(pSMB->resp.Action) & GUEST_LOGIN) + cifs_dbg(FYI, "Guest login\n"); /* BB mark SesInfo struct? */ + + ses->Suid = smb_buf->Uid; /* UID left in wire format (le) */ + cifs_dbg(FYI, "UID = %llu\n", ses->Suid); + + bytes_remaining = get_bcc(smb_buf); + bcc_ptr = pByteArea(smb_buf); + + /* BB check if Unicode and decode strings */ + if (bytes_remaining == 0) { + /* no string area to decode, do nothing */ + } else if (smb_buf->Flags2 & SMBFLG2_UNICODE) { + /* unicode string area must be word-aligned */ + if (!IS_ALIGNED((unsigned long)bcc_ptr - (unsigned long)smb_buf, 2)) { + ++bcc_ptr; + --bytes_remaining; + } + decode_unicode_ssetup(&bcc_ptr, bytes_remaining, ses, + sess_data->nls_cp); + } else { + decode_ascii_ssetup(&bcc_ptr, bytes_remaining, ses, + sess_data->nls_cp); + } + + rc = sess_establish_session(sess_data); +out: + sess_data->result = rc; + sess_data->func = NULL; + sess_free_buffer(sess_data); + kfree_sensitive(ses->auth_key.response); + ses->auth_key.response = NULL; +} + +#ifdef CONFIG_CIFS_UPCALL +static void +sess_auth_kerberos(struct sess_data *sess_data) +{ + int rc = 0; + struct smb_hdr *smb_buf; + SESSION_SETUP_ANDX *pSMB; + char *bcc_ptr; + struct cifs_ses *ses = sess_data->ses; + struct TCP_Server_Info *server = sess_data->server; + __u32 capabilities; + __u16 bytes_remaining; + struct key *spnego_key = NULL; + struct cifs_spnego_msg *msg; + u16 blob_len; + + /* extended security */ + /* wct = 12 */ + rc = sess_alloc_buffer(sess_data, 12); + if (rc) + goto out; + + pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base; + bcc_ptr = sess_data->iov[2].iov_base; + capabilities = cifs_ssetup_hdr(ses, server, pSMB); + + spnego_key = cifs_get_spnego_key(ses, server); + if (IS_ERR(spnego_key)) { + rc = PTR_ERR(spnego_key); + spnego_key = NULL; + goto out; + } + + msg = spnego_key->payload.data[0]; + /* + * check version field to make sure that cifs.upcall is + * sending us a response in an expected form + */ + if (msg->version != CIFS_SPNEGO_UPCALL_VERSION) { + cifs_dbg(VFS, "incorrect version of cifs.upcall (expected %d but got %d)\n", + CIFS_SPNEGO_UPCALL_VERSION, msg->version); + rc = -EKEYREJECTED; + goto out_put_spnego_key; + } + + kfree_sensitive(ses->auth_key.response); + ses->auth_key.response = kmemdup(msg->data, msg->sesskey_len, + GFP_KERNEL); + if (!ses->auth_key.response) { + cifs_dbg(VFS, "Kerberos can't allocate (%u bytes) memory\n", + msg->sesskey_len); + rc = -ENOMEM; + goto out_put_spnego_key; + } + ses->auth_key.len = msg->sesskey_len; + + pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC; + capabilities |= CAP_EXTENDED_SECURITY; + pSMB->req.Capabilities = cpu_to_le32(capabilities); + sess_data->iov[1].iov_base = msg->data + msg->sesskey_len; + sess_data->iov[1].iov_len = msg->secblob_len; + pSMB->req.SecurityBlobLength = cpu_to_le16(sess_data->iov[1].iov_len); + + if (pSMB->req.hdr.Flags2 & SMBFLG2_UNICODE) { + /* unicode strings must be word aligned */ + if (!IS_ALIGNED(sess_data->iov[0].iov_len + sess_data->iov[1].iov_len, 2)) { + *bcc_ptr = 0; + bcc_ptr++; + } + unicode_oslm_strings(&bcc_ptr, sess_data->nls_cp); + unicode_domain_string(&bcc_ptr, ses, sess_data->nls_cp); + } else { + ascii_oslm_strings(&bcc_ptr, sess_data->nls_cp); + ascii_domain_string(&bcc_ptr, ses, sess_data->nls_cp); + } + + sess_data->iov[2].iov_len = (long) bcc_ptr - + (long) sess_data->iov[2].iov_base; + + rc = sess_sendreceive(sess_data); + if (rc) + goto out_put_spnego_key; + + pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base; + smb_buf = (struct smb_hdr *)sess_data->iov[0].iov_base; + + if (smb_buf->WordCount != 4) { + rc = smb_EIO1(smb_eio_trace_sess_krb_wcc, smb_buf->WordCount); + cifs_dbg(VFS, "bad word count %d\n", smb_buf->WordCount); + goto out_put_spnego_key; + } + + if (le16_to_cpu(pSMB->resp.Action) & GUEST_LOGIN) + cifs_dbg(FYI, "Guest login\n"); /* BB mark SesInfo struct? */ + + ses->Suid = smb_buf->Uid; /* UID left in wire format (le) */ + cifs_dbg(FYI, "UID = %llu\n", ses->Suid); + + bytes_remaining = get_bcc(smb_buf); + bcc_ptr = pByteArea(smb_buf); + + blob_len = le16_to_cpu(pSMB->resp.SecurityBlobLength); + if (blob_len > bytes_remaining) { + cifs_dbg(VFS, "bad security blob length %d\n", + blob_len); + rc = -EINVAL; + goto out_put_spnego_key; + } + bcc_ptr += blob_len; + bytes_remaining -= blob_len; + + /* BB check if Unicode and decode strings */ + if (bytes_remaining == 0) { + /* no string area to decode, do nothing */ + } else if (smb_buf->Flags2 & SMBFLG2_UNICODE) { + /* unicode string area must be word-aligned */ + if (!IS_ALIGNED((unsigned long)bcc_ptr - (unsigned long)smb_buf, 2)) { + ++bcc_ptr; + --bytes_remaining; + } + decode_unicode_ssetup(&bcc_ptr, bytes_remaining, ses, + sess_data->nls_cp); + } else { + decode_ascii_ssetup(&bcc_ptr, bytes_remaining, ses, + sess_data->nls_cp); + } + + rc = sess_establish_session(sess_data); +out_put_spnego_key: + key_invalidate(spnego_key); + key_put(spnego_key); +out: + sess_data->result = rc; + sess_data->func = NULL; + sess_free_buffer(sess_data); + kfree_sensitive(ses->auth_key.response); + ses->auth_key.response = NULL; +} + +#endif /* ! CONFIG_CIFS_UPCALL */ + +/* + * The required kvec buffers have to be allocated before calling this + * function. + */ +static int +_sess_auth_rawntlmssp_assemble_req(struct sess_data *sess_data) +{ + SESSION_SETUP_ANDX *pSMB; + struct cifs_ses *ses = sess_data->ses; + struct TCP_Server_Info *server = sess_data->server; + __u32 capabilities; + char *bcc_ptr; + + pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base; + + capabilities = cifs_ssetup_hdr(ses, server, pSMB); + pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC; + capabilities |= CAP_EXTENDED_SECURITY; + pSMB->req.Capabilities |= cpu_to_le32(capabilities); + + bcc_ptr = sess_data->iov[2].iov_base; + + if (pSMB->req.hdr.Flags2 & SMBFLG2_UNICODE) { + /* unicode strings must be word aligned */ + if (!IS_ALIGNED(sess_data->iov[0].iov_len + sess_data->iov[1].iov_len, 2)) { + *bcc_ptr = 0; + bcc_ptr++; + } + unicode_oslm_strings(&bcc_ptr, sess_data->nls_cp); + } else { + ascii_oslm_strings(&bcc_ptr, sess_data->nls_cp); + } + + sess_data->iov[2].iov_len = (long) bcc_ptr - + (long) sess_data->iov[2].iov_base; + + return 0; +} + +static void +sess_auth_rawntlmssp_authenticate(struct sess_data *sess_data); + +static void +sess_auth_rawntlmssp_negotiate(struct sess_data *sess_data) +{ + int rc; + struct smb_hdr *smb_buf; + SESSION_SETUP_ANDX *pSMB; + struct cifs_ses *ses = sess_data->ses; + struct TCP_Server_Info *server = sess_data->server; + __u16 bytes_remaining; + char *bcc_ptr; + unsigned char *ntlmsspblob = NULL; + u16 blob_len; + + cifs_dbg(FYI, "rawntlmssp session setup negotiate phase\n"); + + /* + * if memory allocation is successful, caller of this function + * frees it. + */ + ses->ntlmssp = kmalloc(sizeof(struct ntlmssp_auth), GFP_KERNEL); + if (!ses->ntlmssp) { + rc = -ENOMEM; + goto out; + } + ses->ntlmssp->sesskey_per_smbsess = false; + + /* wct = 12 */ + rc = sess_alloc_buffer(sess_data, 12); + if (rc) + goto out; + + pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base; + + /* Build security blob before we assemble the request */ + rc = build_ntlmssp_negotiate_blob(&ntlmsspblob, + &blob_len, ses, server, + sess_data->nls_cp); + if (rc) + goto out_free_ntlmsspblob; + + sess_data->iov[1].iov_len = blob_len; + sess_data->iov[1].iov_base = ntlmsspblob; + pSMB->req.SecurityBlobLength = cpu_to_le16(blob_len); + + rc = _sess_auth_rawntlmssp_assemble_req(sess_data); + if (rc) + goto out_free_ntlmsspblob; + + rc = sess_sendreceive(sess_data); + + pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base; + smb_buf = (struct smb_hdr *)sess_data->iov[0].iov_base; + + /* If true, rc here is expected and not an error */ + if (sess_data->buf0_type != CIFS_NO_BUFFER && + smb_buf->Status.CifsError == + cpu_to_le32(NT_STATUS_MORE_PROCESSING_REQUIRED)) + rc = 0; + + if (rc) + goto out_free_ntlmsspblob; + + cifs_dbg(FYI, "rawntlmssp session setup challenge phase\n"); + + if (smb_buf->WordCount != 4) { + rc = smb_EIO1(smb_eio_trace_sess_rawnl_neg_wcc, smb_buf->WordCount); + cifs_dbg(VFS, "bad word count %d\n", smb_buf->WordCount); + goto out_free_ntlmsspblob; + } + + ses->Suid = smb_buf->Uid; /* UID left in wire format (le) */ + cifs_dbg(FYI, "UID = %llu\n", ses->Suid); + + bytes_remaining = get_bcc(smb_buf); + bcc_ptr = pByteArea(smb_buf); + + blob_len = le16_to_cpu(pSMB->resp.SecurityBlobLength); + if (blob_len > bytes_remaining) { + cifs_dbg(VFS, "bad security blob length %d\n", + blob_len); + rc = -EINVAL; + goto out_free_ntlmsspblob; + } + + rc = decode_ntlmssp_challenge(bcc_ptr, blob_len, ses); + +out_free_ntlmsspblob: + kfree_sensitive(ntlmsspblob); +out: + sess_free_buffer(sess_data); + + if (!rc) { + sess_data->func = sess_auth_rawntlmssp_authenticate; + return; + } + + /* Else error. Cleanup */ + kfree_sensitive(ses->auth_key.response); + ses->auth_key.response = NULL; + kfree_sensitive(ses->ntlmssp); + ses->ntlmssp = NULL; + + sess_data->func = NULL; + sess_data->result = rc; +} + +static void +sess_auth_rawntlmssp_authenticate(struct sess_data *sess_data) +{ + int rc; + struct smb_hdr *smb_buf; + SESSION_SETUP_ANDX *pSMB; + struct cifs_ses *ses = sess_data->ses; + struct TCP_Server_Info *server = sess_data->server; + __u16 bytes_remaining; + char *bcc_ptr; + unsigned char *ntlmsspblob = NULL; + u16 blob_len; + + cifs_dbg(FYI, "rawntlmssp session setup authenticate phase\n"); + + /* wct = 12 */ + rc = sess_alloc_buffer(sess_data, 12); + if (rc) + goto out; + + /* Build security blob before we assemble the request */ + pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base; + smb_buf = (struct smb_hdr *)pSMB; + rc = build_ntlmssp_auth_blob(&ntlmsspblob, + &blob_len, ses, server, + sess_data->nls_cp); + if (rc) + goto out_free_ntlmsspblob; + sess_data->iov[1].iov_len = blob_len; + sess_data->iov[1].iov_base = ntlmsspblob; + pSMB->req.SecurityBlobLength = cpu_to_le16(blob_len); + /* + * Make sure that we tell the server that we are using + * the uid that it just gave us back on the response + * (challenge) + */ + smb_buf->Uid = ses->Suid; + + rc = _sess_auth_rawntlmssp_assemble_req(sess_data); + if (rc) + goto out_free_ntlmsspblob; + + rc = sess_sendreceive(sess_data); + if (rc) + goto out_free_ntlmsspblob; + + pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base; + smb_buf = (struct smb_hdr *)sess_data->iov[0].iov_base; + if (smb_buf->WordCount != 4) { + rc = smb_EIO1(smb_eio_trace_sess_rawnl_auth_wcc, smb_buf->WordCount); + cifs_dbg(VFS, "bad word count %d\n", smb_buf->WordCount); + goto out_free_ntlmsspblob; + } + + if (le16_to_cpu(pSMB->resp.Action) & GUEST_LOGIN) + cifs_dbg(FYI, "Guest login\n"); /* BB mark SesInfo struct? */ + + if (ses->Suid != smb_buf->Uid) { + ses->Suid = smb_buf->Uid; + cifs_dbg(FYI, "UID changed! new UID = %llu\n", ses->Suid); + } + + bytes_remaining = get_bcc(smb_buf); + bcc_ptr = pByteArea(smb_buf); + blob_len = le16_to_cpu(pSMB->resp.SecurityBlobLength); + if (blob_len > bytes_remaining) { + cifs_dbg(VFS, "bad security blob length %d\n", + blob_len); + rc = -EINVAL; + goto out_free_ntlmsspblob; + } + bcc_ptr += blob_len; + bytes_remaining -= blob_len; + + + /* BB check if Unicode and decode strings */ + if (bytes_remaining == 0) { + /* no string area to decode, do nothing */ + } else if (smb_buf->Flags2 & SMBFLG2_UNICODE) { + /* unicode string area must be word-aligned */ + if (!IS_ALIGNED((unsigned long)bcc_ptr - (unsigned long)smb_buf, 2)) { + ++bcc_ptr; + --bytes_remaining; + } + decode_unicode_ssetup(&bcc_ptr, bytes_remaining, ses, + sess_data->nls_cp); + } else { + decode_ascii_ssetup(&bcc_ptr, bytes_remaining, ses, + sess_data->nls_cp); + } + +out_free_ntlmsspblob: + kfree_sensitive(ntlmsspblob); +out: + sess_free_buffer(sess_data); + + if (!rc) + rc = sess_establish_session(sess_data); + + /* Cleanup */ + kfree_sensitive(ses->auth_key.response); + ses->auth_key.response = NULL; + kfree_sensitive(ses->ntlmssp); + ses->ntlmssp = NULL; + + sess_data->func = NULL; + sess_data->result = rc; +} + +static int select_sec(struct sess_data *sess_data) +{ + int type; + struct cifs_ses *ses = sess_data->ses; + struct TCP_Server_Info *server = sess_data->server; + + type = cifs_select_sectype(server, ses->sectype); + cifs_dbg(FYI, "sess setup type %d\n", type); + if (type == Unspecified) { + cifs_dbg(VFS, "Unable to select appropriate authentication method!\n"); + return -EINVAL; + } + + switch (type) { + case NTLMv2: + sess_data->func = sess_auth_ntlmv2; + break; + case Kerberos: +#ifdef CONFIG_CIFS_UPCALL + sess_data->func = sess_auth_kerberos; + break; +#else + cifs_dbg(VFS, "Kerberos negotiated but upcall support disabled!\n"); + return -ENOSYS; +#endif /* CONFIG_CIFS_UPCALL */ + case RawNTLMSSP: + sess_data->func = sess_auth_rawntlmssp_negotiate; + break; + default: + cifs_dbg(VFS, "secType %d not supported!\n", type); + return -ENOSYS; + } + + return 0; +} + +int CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses, + struct TCP_Server_Info *server, + const struct nls_table *nls_cp) +{ + int rc = 0; + struct sess_data *sess_data; + + if (ses == NULL) { + WARN(1, "%s: ses == NULL!", __func__); + return -EINVAL; + } + + sess_data = kzalloc(sizeof(struct sess_data), GFP_KERNEL); + if (!sess_data) + return -ENOMEM; + + sess_data->xid = xid; + sess_data->ses = ses; + sess_data->server = server; + sess_data->buf0_type = CIFS_NO_BUFFER; + sess_data->nls_cp = (struct nls_table *) nls_cp; + + rc = select_sec(sess_data); + if (rc) + goto out; + + while (sess_data->func) + sess_data->func(sess_data); + + /* Store result before we free sess_data */ + rc = sess_data->result; + +out: + kfree_sensitive(sess_data); + return rc; +} diff --git a/fs/smb/client/smb1transport.c b/fs/smb/client/smb1transport.c new file mode 100644 index 000000000000..0b8b852cfc0d --- /dev/null +++ b/fs/smb/client/smb1transport.c @@ -0,0 +1,563 @@ +// SPDX-License-Identifier: LGPL-2.1 +/* + * + * Copyright (C) International Business Machines Corp., 2002,2008 + * Author(s): Steve French (sfrench@us.ibm.com) + * Jeremy Allison (jra@samba.org) 2006. + * + */ + +#include <linux/fs.h> +#include <linux/list.h> +#include <linux/gfp.h> +#include <linux/wait.h> +#include <linux/net.h> +#include <linux/delay.h> +#include <linux/freezer.h> +#include <linux/tcp.h> +#include <linux/bvec.h> +#include <linux/highmem.h> +#include <linux/uaccess.h> +#include <linux/processor.h> +#include <linux/mempool.h> +#include <linux/sched/signal.h> +#include <linux/task_io_accounting_ops.h> +#include "cifsglob.h" +#include "cifsproto.h" +#include "smb1proto.h" +#include "smb2proto.h" +#include "cifs_debug.h" +#include "smbdirect.h" +#include "compress.h" +#include "cifs_debug.h" + +/* Max number of iovectors we can use off the stack when sending requests. */ +#define CIFS_MAX_IOV_SIZE 8 + +static struct mid_q_entry * +alloc_mid(const struct smb_hdr *smb_buffer, struct TCP_Server_Info *server) +{ + struct mid_q_entry *temp; + + if (server == NULL) { + cifs_dbg(VFS, "%s: null TCP session\n", __func__); + return NULL; + } + + temp = mempool_alloc(&cifs_mid_pool, GFP_NOFS); + memset(temp, 0, sizeof(struct mid_q_entry)); + refcount_set(&temp->refcount, 1); + spin_lock_init(&temp->mid_lock); + temp->mid = get_mid(smb_buffer); + temp->pid = current->pid; + temp->command = cpu_to_le16(smb_buffer->Command); + cifs_dbg(FYI, "For smb_command %d\n", smb_buffer->Command); + /* easier to use jiffies */ + /* when mid allocated can be before when sent */ + temp->when_alloc = jiffies; + + /* + * The default is for the mid to be synchronous, so the + * default callback just wakes up the current task. + */ + get_task_struct(current); + temp->creator = current; + temp->callback = cifs_wake_up_task; + temp->callback_data = current; + + atomic_inc(&mid_count); + temp->mid_state = MID_REQUEST_ALLOCATED; + return temp; +} + +static int allocate_mid(struct cifs_ses *ses, struct smb_hdr *in_buf, + struct mid_q_entry **ppmidQ) +{ + spin_lock(&ses->ses_lock); + if (ses->ses_status == SES_NEW) { + if ((in_buf->Command != SMB_COM_SESSION_SETUP_ANDX) && + (in_buf->Command != SMB_COM_NEGOTIATE)) { + spin_unlock(&ses->ses_lock); + return -EAGAIN; + } + /* else ok - we are setting up session */ + } + + if (ses->ses_status == SES_EXITING) { + /* check if SMB session is bad because we are setting it up */ + if (in_buf->Command != SMB_COM_LOGOFF_ANDX) { + spin_unlock(&ses->ses_lock); + return -EAGAIN; + } + /* else ok - we are shutting down session */ + } + spin_unlock(&ses->ses_lock); + + *ppmidQ = alloc_mid(in_buf, ses->server); + if (*ppmidQ == NULL) + return -ENOMEM; + spin_lock(&ses->server->mid_queue_lock); + list_add_tail(&(*ppmidQ)->qhead, &ses->server->pending_mid_q); + spin_unlock(&ses->server->mid_queue_lock); + return 0; +} + +struct mid_q_entry * +cifs_setup_async_request(struct TCP_Server_Info *server, struct smb_rqst *rqst) +{ + int rc; + struct smb_hdr *hdr = (struct smb_hdr *)rqst->rq_iov[0].iov_base; + struct mid_q_entry *mid; + + /* enable signing if server requires it */ + if (server->sign) + hdr->Flags2 |= SMBFLG2_SECURITY_SIGNATURE; + + mid = alloc_mid(hdr, server); + if (mid == NULL) + return ERR_PTR(-ENOMEM); + + rc = cifs_sign_rqst(rqst, server, &mid->sequence_number); + if (rc) { + release_mid(server, mid); + return ERR_PTR(rc); + } + + return mid; +} + +/* + * + * Send an SMB Request. No response info (other than return code) + * needs to be parsed. + * + * flags indicate the type of request buffer and how long to wait + * and whether to log NT STATUS code (error) before mapping it to POSIX error + * + */ +int +SendReceiveNoRsp(const unsigned int xid, struct cifs_ses *ses, + char *in_buf, unsigned int in_len, int flags) +{ + int rc; + struct kvec iov[1]; + struct kvec rsp_iov; + int resp_buf_type; + + iov[0].iov_base = in_buf; + iov[0].iov_len = in_len; + flags |= CIFS_NO_RSP_BUF; + rc = SendReceive2(xid, ses, iov, 1, &resp_buf_type, flags, &rsp_iov); + cifs_dbg(NOISY, "SendRcvNoRsp flags %d rc %d\n", flags, rc); + + return rc; +} + +int +cifs_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server, + bool log_error) +{ + unsigned int len = mid->response_pdu_len; + + dump_smb(mid->resp_buf, min_t(u32, 92, len)); + + /* convert the length into a more usable form */ + if (server->sign) { + struct kvec iov[1]; + int rc = 0; + struct smb_rqst rqst = { .rq_iov = iov, + .rq_nvec = ARRAY_SIZE(iov) }; + + iov[0].iov_base = mid->resp_buf; + iov[0].iov_len = len; + /* FIXME: add code to kill session */ + rc = cifs_verify_signature(&rqst, server, + mid->sequence_number); + if (rc) + cifs_server_dbg(VFS, "SMB signature verification returned error = %d\n", + rc); + } + + /* BB special case reconnect tid and uid here? */ + return map_and_check_smb_error(server, mid, log_error); +} + +struct mid_q_entry * +cifs_setup_request(struct cifs_ses *ses, struct TCP_Server_Info *server, + struct smb_rqst *rqst) +{ + int rc; + struct smb_hdr *hdr = (struct smb_hdr *)rqst->rq_iov[0].iov_base; + struct mid_q_entry *mid; + + rc = allocate_mid(ses, hdr, &mid); + if (rc) + return ERR_PTR(rc); + rc = cifs_sign_rqst(rqst, server, &mid->sequence_number); + if (rc) { + delete_mid(server, mid); + return ERR_PTR(rc); + } + return mid; +} + +int +SendReceive2(const unsigned int xid, struct cifs_ses *ses, + struct kvec *iov, int n_vec, int *resp_buf_type /* ret */, + const int flags, struct kvec *resp_iov) +{ + struct smb_rqst rqst = { + .rq_iov = iov, + .rq_nvec = n_vec, + }; + + return cifs_send_recv(xid, ses, ses->server, + &rqst, resp_buf_type, flags, resp_iov); +} + +int +SendReceive(const unsigned int xid, struct cifs_ses *ses, + struct smb_hdr *in_buf, unsigned int in_len, + struct smb_hdr *out_buf, int *pbytes_returned, const int flags) +{ + struct TCP_Server_Info *server; + struct kvec resp_iov = {}; + struct kvec iov = { .iov_base = in_buf, .iov_len = in_len }; + struct smb_rqst rqst = { .rq_iov = &iov, .rq_nvec = 1 }; + int resp_buf_type; + int rc = 0; + + if (WARN_ON_ONCE(in_len > 0xffffff)) + return smb_EIO1(smb_eio_trace_tx_too_long, in_len); + if (ses == NULL) { + cifs_dbg(VFS, "Null smb session\n"); + return smb_EIO(smb_eio_trace_null_pointers); + } + server = ses->server; + if (server == NULL) { + cifs_dbg(VFS, "Null tcp session\n"); + return smb_EIO(smb_eio_trace_null_pointers); + } + + /* Ensure that we do not send more than 50 overlapping requests + to the same server. We may make this configurable later or + use ses->maxReq */ + + if (in_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) { + cifs_server_dbg(VFS, "Invalid length, greater than maximum frame, %d\n", + in_len); + return smb_EIO1(smb_eio_trace_tx_too_long, in_len); + } + + rc = cifs_send_recv(xid, ses, ses->server, + &rqst, &resp_buf_type, flags, &resp_iov); + if (rc < 0) + goto out; + + if (out_buf) { + *pbytes_returned = resp_iov.iov_len; + if (resp_iov.iov_len) + memcpy(out_buf, resp_iov.iov_base, resp_iov.iov_len); + } + +out: + free_rsp_buf(resp_buf_type, resp_iov.iov_base); + return rc; +} + +/* + return codes: + 0 not a transact2, or all data present + >0 transact2 with that much data missing + -EINVAL invalid transact2 + */ +static int +check2ndT2(char *buf) +{ + struct smb_hdr *pSMB = (struct smb_hdr *)buf; + struct smb_t2_rsp *pSMBt; + int remaining; + __u16 total_data_size, data_in_this_rsp; + + if (pSMB->Command != SMB_COM_TRANSACTION2) + return 0; + + /* check for plausible wct, bcc and t2 data and parm sizes */ + /* check for parm and data offset going beyond end of smb */ + if (pSMB->WordCount != 10) { /* coalesce_t2 depends on this */ + cifs_dbg(FYI, "Invalid transact2 word count\n"); + return -EINVAL; + } + + pSMBt = (struct smb_t2_rsp *)pSMB; + + total_data_size = get_unaligned_le16(&pSMBt->t2_rsp.TotalDataCount); + data_in_this_rsp = get_unaligned_le16(&pSMBt->t2_rsp.DataCount); + + if (total_data_size == data_in_this_rsp) + return 0; + else if (total_data_size < data_in_this_rsp) { + cifs_dbg(FYI, "total data %d smaller than data in frame %d\n", + total_data_size, data_in_this_rsp); + return -EINVAL; + } + + remaining = total_data_size - data_in_this_rsp; + + cifs_dbg(FYI, "missing %d bytes from transact2, check next response\n", + remaining); + if (total_data_size > CIFSMaxBufSize) { + cifs_dbg(VFS, "TotalDataSize %d is over maximum buffer %d\n", + total_data_size, CIFSMaxBufSize); + return -EINVAL; + } + return remaining; +} + +static int +coalesce_t2(char *second_buf, struct smb_hdr *target_hdr, unsigned int *pdu_len) +{ + struct smb_t2_rsp *pSMBs = (struct smb_t2_rsp *)second_buf; + struct smb_t2_rsp *pSMBt = (struct smb_t2_rsp *)target_hdr; + char *data_area_of_tgt; + char *data_area_of_src; + int remaining; + unsigned int byte_count, total_in_tgt; + __u16 tgt_total_cnt, src_total_cnt, total_in_src; + + src_total_cnt = get_unaligned_le16(&pSMBs->t2_rsp.TotalDataCount); + tgt_total_cnt = get_unaligned_le16(&pSMBt->t2_rsp.TotalDataCount); + + if (tgt_total_cnt != src_total_cnt) + cifs_dbg(FYI, "total data count of primary and secondary t2 differ source=%hu target=%hu\n", + src_total_cnt, tgt_total_cnt); + + total_in_tgt = get_unaligned_le16(&pSMBt->t2_rsp.DataCount); + + remaining = tgt_total_cnt - total_in_tgt; + + if (remaining < 0) { + cifs_dbg(FYI, "Server sent too much data. tgt_total_cnt=%hu total_in_tgt=%u\n", + tgt_total_cnt, total_in_tgt); + return -EPROTO; + } + + if (remaining == 0) { + /* nothing to do, ignore */ + cifs_dbg(FYI, "no more data remains\n"); + return 0; + } + + total_in_src = get_unaligned_le16(&pSMBs->t2_rsp.DataCount); + if (remaining < total_in_src) + cifs_dbg(FYI, "transact2 2nd response contains too much data\n"); + + /* find end of first SMB data area */ + data_area_of_tgt = (char *)&pSMBt->hdr.Protocol + + get_unaligned_le16(&pSMBt->t2_rsp.DataOffset); + + /* validate target area */ + data_area_of_src = (char *)&pSMBs->hdr.Protocol + + get_unaligned_le16(&pSMBs->t2_rsp.DataOffset); + + data_area_of_tgt += total_in_tgt; + + total_in_tgt += total_in_src; + /* is the result too big for the field? */ + if (total_in_tgt > USHRT_MAX) { + cifs_dbg(FYI, "coalesced DataCount too large (%u)\n", + total_in_tgt); + return -EPROTO; + } + put_unaligned_le16(total_in_tgt, &pSMBt->t2_rsp.DataCount); + + /* fix up the BCC */ + byte_count = get_bcc(target_hdr); + byte_count += total_in_src; + /* is the result too big for the field? */ + if (byte_count > USHRT_MAX) { + cifs_dbg(FYI, "coalesced BCC too large (%u)\n", byte_count); + return -EPROTO; + } + put_bcc(byte_count, target_hdr); + + byte_count = *pdu_len; + byte_count += total_in_src; + /* don't allow buffer to overflow */ + if (byte_count > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) { + cifs_dbg(FYI, "coalesced BCC exceeds buffer size (%u)\n", + byte_count); + return -ENOBUFS; + } + *pdu_len = byte_count; + + /* copy second buffer into end of first buffer */ + memcpy(data_area_of_tgt, data_area_of_src, total_in_src); + + if (remaining != total_in_src) { + /* more responses to go */ + cifs_dbg(FYI, "waiting for more secondary responses\n"); + return 1; + } + + /* we are done */ + cifs_dbg(FYI, "found the last secondary response\n"); + return 0; +} + +bool +cifs_check_trans2(struct mid_q_entry *mid, struct TCP_Server_Info *server, + char *buf, int malformed) +{ + if (malformed) + return false; + if (check2ndT2(buf) <= 0) + return false; + mid->multiRsp = true; + if (mid->resp_buf) { + /* merge response - fix up 1st*/ + malformed = coalesce_t2(buf, mid->resp_buf, &mid->response_pdu_len); + if (malformed > 0) + return true; + /* All parts received or packet is malformed. */ + mid->multiEnd = true; + dequeue_mid(server, mid, malformed); + return true; + } + if (!server->large_buf) { + /*FIXME: switch to already allocated largebuf?*/ + cifs_dbg(VFS, "1st trans2 resp needs bigbuf\n"); + } else { + /* Have first buffer */ + mid->resp_buf = buf; + mid->large_buf = true; + server->bigbuf = NULL; + } + return true; +} + +static int +check_smb_hdr(struct smb_hdr *smb) +{ + /* does it have the right SMB "signature" ? */ + if (*(__le32 *) smb->Protocol != SMB1_PROTO_NUMBER) { + cifs_dbg(VFS, "Bad protocol string signature header 0x%x\n", + *(unsigned int *)smb->Protocol); + return 1; + } + + /* if it's a response then accept */ + if (smb->Flags & SMBFLG_RESPONSE) + return 0; + + /* only one valid case where server sends us request */ + if (smb->Command == SMB_COM_LOCKING_ANDX) + return 0; + + /* + * Windows NT server returns error resposne (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. + */ + if (smb->Command == SMB_COM_TRANSACTION2 && smb->Status.CifsError != 0) + return 0; + + cifs_dbg(VFS, "Server sent request, not response. mid=%u\n", + get_mid(smb)); + return 1; +} + +int +checkSMB(char *buf, unsigned int pdu_len, unsigned int total_read, + struct TCP_Server_Info *server) +{ + struct smb_hdr *smb = (struct smb_hdr *)buf; + __u32 rfclen = pdu_len; + __u32 clc_len; /* calculated length */ + cifs_dbg(FYI, "checkSMB Length: 0x%x, smb_buf_length: 0x%x\n", + total_read, rfclen); + + /* is this frame too small to even get to a BCC? */ + if (total_read < 2 + sizeof(struct smb_hdr)) { + if ((total_read >= sizeof(struct smb_hdr) - 1) + && (smb->Status.CifsError != 0)) { + /* it's an error return */ + smb->WordCount = 0; + /* some error cases do not return wct and bcc */ + return 0; + } else if ((total_read == sizeof(struct smb_hdr) + 1) && + (smb->WordCount == 0)) { + char *tmp = (char *)smb; + /* Need to work around a bug in two servers here */ + /* First, check if the part of bcc they sent was zero */ + if (tmp[sizeof(struct smb_hdr)] == 0) { + /* some servers return only half of bcc + * on simple responses (wct, bcc both zero) + * in particular have seen this on + * ulogoffX and FindClose. This leaves + * one byte of bcc potentially uninitialized + */ + /* zero rest of bcc */ + tmp[sizeof(struct smb_hdr)+1] = 0; + return 0; + } + cifs_dbg(VFS, "rcvd invalid byte count (bcc)\n"); + return smb_EIO1(smb_eio_trace_rx_inv_bcc, tmp[sizeof(struct smb_hdr)]); + } else { + cifs_dbg(VFS, "Length less than smb header size\n"); + return smb_EIO2(smb_eio_trace_rx_too_short, + total_read, smb->WordCount); + } + } else if (total_read < sizeof(*smb) + 2 * smb->WordCount) { + cifs_dbg(VFS, "%s: can't read BCC due to invalid WordCount(%u)\n", + __func__, smb->WordCount); + return smb_EIO2(smb_eio_trace_rx_check_rsp, + total_read, 2 + sizeof(struct smb_hdr)); + } + + /* otherwise, there is enough to get to the BCC */ + if (check_smb_hdr(smb)) + return smb_EIO1(smb_eio_trace_rx_rfc1002_magic, *(u32 *)smb->Protocol); + clc_len = smbCalcSize(smb); + + if (rfclen != total_read) { + cifs_dbg(VFS, "Length read does not match RFC1001 length %d/%d\n", + rfclen, total_read); + return smb_EIO2(smb_eio_trace_rx_check_rsp, + total_read, rfclen); + } + + if (rfclen != clc_len) { + __u16 mid = get_mid(smb); + /* check if bcc wrapped around for large read responses */ + if ((rfclen > 64 * 1024) && (rfclen > clc_len)) { + /* check if lengths match mod 64K */ + if (((rfclen) & 0xFFFF) == (clc_len & 0xFFFF)) + return 0; /* bcc wrapped */ + } + cifs_dbg(FYI, "Calculated size %u vs length %u mismatch for mid=%u\n", + clc_len, rfclen, mid); + + if (rfclen < clc_len) { + cifs_dbg(VFS, "RFC1001 size %u smaller than SMB for mid=%u\n", + rfclen, mid); + return smb_EIO2(smb_eio_trace_rx_calc_len_too_big, + rfclen, clc_len); + } else if (rfclen > clc_len + 512) { + /* + * Some servers (Windows XP in particular) send more + * data than the lengths in the SMB packet would + * indicate on certain calls (byte range locks and + * trans2 find first calls in particular). While the + * client can handle such a frame by ignoring the + * trailing data, we choose limit the amount of extra + * data to 512 bytes. + */ + cifs_dbg(VFS, "RFC1001 size %u more than 512 bytes larger than SMB for mid=%u\n", + rfclen, mid); + return smb_EIO2(smb_eio_trace_rx_overlong, + rfclen, clc_len + 512); + } + } + return 0; +} diff --git a/fs/smb/client/smb2file.c b/fs/smb/client/smb2file.c index 7f11ae6bb785..476c53b3711f 100644 --- a/fs/smb/client/smb2file.c +++ b/fs/smb/client/smb2file.c @@ -13,7 +13,6 @@ #include <linux/pagemap.h> #include <asm/div64.h> #include "cifsfs.h" -#include "cifspdu.h" #include "cifsglob.h" #include "cifsproto.h" #include "cifs_debug.h" @@ -22,6 +21,7 @@ #include "fscache.h" #include "smb2proto.h" #include "../common/smb2status.h" +#include "../common/smbfsctl.h" static struct smb2_symlink_err_rsp *symlink_data(const struct kvec *iov) { @@ -178,6 +178,9 @@ int smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms, rc = SMB2_open(xid, oparms, smb2_path, &smb2_oplock, smb2_data, NULL, &err_iov, &err_buftype); if (rc == -EACCES && retry_without_read_attributes) { + free_rsp_buf(err_buftype, err_iov.iov_base); + memset(&err_iov, 0, sizeof(err_iov)); + err_buftype = CIFS_NO_BUFFER; oparms->desired_access &= ~FILE_READ_ATTRIBUTES; rc = SMB2_open(xid, oparms, smb2_path, &smb2_oplock, smb2_data, NULL, &err_iov, &err_buftype); diff --git a/fs/smb/client/smb2inode.c b/fs/smb/client/smb2inode.c index 2ded3246600c..8a30088fed15 100644 --- a/fs/smb/client/smb2inode.c +++ b/fs/smb/client/smb2inode.c @@ -13,7 +13,6 @@ #include <linux/pagemap.h> #include <asm/div64.h> #include "cifsfs.h" -#include "cifspdu.h" #include "cifsglob.h" #include "cifsproto.h" #include "cifs_debug.h" @@ -24,6 +23,7 @@ #include "smb2proto.h" #include "cached_dir.h" #include "../common/smb2status.h" +#include "../common/smbfsctl.h" static struct reparse_data_buffer *reparse_buf_ptr(struct kvec *iov) { @@ -188,7 +188,7 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, struct reparse_data_buffer *rbuf; struct TCP_Server_Info *server; int resp_buftype[MAX_COMPOUND]; - int retries = 0, cur_sleep = 1; + int retries = 0, cur_sleep = 0; __u8 delete_pending[8] = {1,}; struct kvec *rsp_iov, *iov; struct inode *inode = NULL; @@ -638,18 +638,26 @@ replay_again: num_rqst++; if (cfile) { - if (retries) + if (retries) { + /* Back-off before retry */ + if (cur_sleep) + msleep(cur_sleep); for (i = 1; i < num_rqst - 2; i++) smb2_set_replay(server, &rqst[i]); + } rc = compound_send_recv(xid, ses, server, flags, num_rqst - 2, &rqst[1], &resp_buftype[1], &rsp_iov[1]); } else { - if (retries) + if (retries) { + /* Back-off before retry */ + if (cur_sleep) + msleep(cur_sleep); for (i = 0; i < num_rqst; i++) smb2_set_replay(server, &rqst[i]); + } rc = compound_send_recv(xid, ses, server, flags, num_rqst, @@ -1180,7 +1188,7 @@ smb2_unlink(const unsigned int xid, struct cifs_tcon *tcon, const char *name, { struct kvec open_iov[SMB2_CREATE_IOV_SIZE]; __le16 *utf16_path __free(kfree) = NULL; - int retries = 0, cur_sleep = 1; + int retries = 0, cur_sleep = 0; struct TCP_Server_Info *server; struct cifs_open_parms oparms; struct smb2_create_req *creq; @@ -1242,6 +1250,9 @@ again: goto err_free; if (retries) { + /* Back-off before retry */ + if (cur_sleep) + msleep(cur_sleep); for (int i = 0; i < ARRAY_SIZE(rqst); i++) smb2_set_replay(server, &rqst[i]); } @@ -1262,7 +1273,7 @@ again: if (rc == -EINVAL && dentry) { dentry = NULL; retries = 0; - cur_sleep = 1; + cur_sleep = 0; goto again; } /* diff --git a/fs/smb/client/smb2maperror.c b/fs/smb/client/smb2maperror.c index 4e1db02d22cb..cd036365201f 100644 --- a/fs/smb/client/smb2maperror.c +++ b/fs/smb/client/smb2maperror.c @@ -17,2416 +17,49 @@ #include "trace.h" struct status_to_posix_error { - __le32 smb2_status; + __u32 smb2_status; int posix_error; char *status_string; }; static const struct status_to_posix_error smb2_error_map_table[] = { - {STATUS_WAIT_1, -EIO, "STATUS_WAIT_1"}, - {STATUS_WAIT_2, -EIO, "STATUS_WAIT_2"}, - {STATUS_WAIT_3, -EIO, "STATUS_WAIT_3"}, - {STATUS_WAIT_63, -EIO, "STATUS_WAIT_63"}, - {STATUS_ABANDONED, -EIO, "STATUS_ABANDONED or STATUS_ABANDONED_WAIT_0"}, - {STATUS_ABANDONED_WAIT_0, -EIO, - "STATUS_ABANDONED or STATUS_ABANDONED_WAIT_0"}, - {STATUS_ABANDONED_WAIT_63, -EIO, "STATUS_ABANDONED_WAIT_63"}, - {STATUS_USER_APC, -EIO, "STATUS_USER_APC"}, - {STATUS_KERNEL_APC, -EIO, "STATUS_KERNEL_APC"}, - {STATUS_ALERTED, -EIO, "STATUS_ALERTED"}, - {STATUS_TIMEOUT, -ETIMEDOUT, "STATUS_TIMEOUT"}, - {STATUS_PENDING, -EIO, "STATUS_PENDING"}, - {STATUS_REPARSE, -EIO, "STATUS_REPARSE"}, - {STATUS_MORE_ENTRIES, -EIO, "STATUS_MORE_ENTRIES"}, - {STATUS_NOT_ALL_ASSIGNED, -EIO, "STATUS_NOT_ALL_ASSIGNED"}, - {STATUS_SOME_NOT_MAPPED, -EIO, "STATUS_SOME_NOT_MAPPED"}, - {STATUS_OPLOCK_BREAK_IN_PROGRESS, -EIO, - "STATUS_OPLOCK_BREAK_IN_PROGRESS"}, - {STATUS_VOLUME_MOUNTED, -EIO, "STATUS_VOLUME_MOUNTED"}, - {STATUS_RXACT_COMMITTED, -EIO, "STATUS_RXACT_COMMITTED"}, - {STATUS_NOTIFY_CLEANUP, -EIO, "STATUS_NOTIFY_CLEANUP"}, - {STATUS_NOTIFY_ENUM_DIR, -EIO, "STATUS_NOTIFY_ENUM_DIR"}, - {STATUS_NO_QUOTAS_FOR_ACCOUNT, -EIO, "STATUS_NO_QUOTAS_FOR_ACCOUNT"}, - {STATUS_PRIMARY_TRANSPORT_CONNECT_FAILED, -EIO, - "STATUS_PRIMARY_TRANSPORT_CONNECT_FAILED"}, - {STATUS_PAGE_FAULT_TRANSITION, -EIO, "STATUS_PAGE_FAULT_TRANSITION"}, - {STATUS_PAGE_FAULT_DEMAND_ZERO, -EIO, "STATUS_PAGE_FAULT_DEMAND_ZERO"}, - {STATUS_PAGE_FAULT_COPY_ON_WRITE, -EIO, - "STATUS_PAGE_FAULT_COPY_ON_WRITE"}, - {STATUS_PAGE_FAULT_GUARD_PAGE, -EIO, "STATUS_PAGE_FAULT_GUARD_PAGE"}, - {STATUS_PAGE_FAULT_PAGING_FILE, -EIO, "STATUS_PAGE_FAULT_PAGING_FILE"}, - {STATUS_CACHE_PAGE_LOCKED, -EIO, "STATUS_CACHE_PAGE_LOCKED"}, - {STATUS_CRASH_DUMP, -EIO, "STATUS_CRASH_DUMP"}, - {STATUS_BUFFER_ALL_ZEROS, -EIO, "STATUS_BUFFER_ALL_ZEROS"}, - {STATUS_REPARSE_OBJECT, -EIO, "STATUS_REPARSE_OBJECT"}, - {STATUS_RESOURCE_REQUIREMENTS_CHANGED, -EIO, - "STATUS_RESOURCE_REQUIREMENTS_CHANGED"}, - {STATUS_TRANSLATION_COMPLETE, -EIO, "STATUS_TRANSLATION_COMPLETE"}, - {STATUS_DS_MEMBERSHIP_EVALUATED_LOCALLY, -EIO, - "STATUS_DS_MEMBERSHIP_EVALUATED_LOCALLY"}, - {STATUS_NOTHING_TO_TERMINATE, -EIO, "STATUS_NOTHING_TO_TERMINATE"}, - {STATUS_PROCESS_NOT_IN_JOB, -EIO, "STATUS_PROCESS_NOT_IN_JOB"}, - {STATUS_PROCESS_IN_JOB, -EIO, "STATUS_PROCESS_IN_JOB"}, - {STATUS_VOLSNAP_HIBERNATE_READY, -EIO, - "STATUS_VOLSNAP_HIBERNATE_READY"}, - {STATUS_FSFILTER_OP_COMPLETED_SUCCESSFULLY, -EIO, - "STATUS_FSFILTER_OP_COMPLETED_SUCCESSFULLY"}, - {STATUS_INTERRUPT_VECTOR_ALREADY_CONNECTED, -EIO, - "STATUS_INTERRUPT_VECTOR_ALREADY_CONNECTED"}, - {STATUS_INTERRUPT_STILL_CONNECTED, -EIO, - "STATUS_INTERRUPT_STILL_CONNECTED"}, - {STATUS_PROCESS_CLONED, -EIO, "STATUS_PROCESS_CLONED"}, - {STATUS_FILE_LOCKED_WITH_ONLY_READERS, -EIO, - "STATUS_FILE_LOCKED_WITH_ONLY_READERS"}, - {STATUS_FILE_LOCKED_WITH_WRITERS, -EIO, - "STATUS_FILE_LOCKED_WITH_WRITERS"}, - {STATUS_RESOURCEMANAGER_READ_ONLY, -EROFS, - "STATUS_RESOURCEMANAGER_READ_ONLY"}, - {STATUS_WAIT_FOR_OPLOCK, -EIO, "STATUS_WAIT_FOR_OPLOCK"}, - {DBG_EXCEPTION_HANDLED, -EIO, "DBG_EXCEPTION_HANDLED"}, - {DBG_CONTINUE, -EIO, "DBG_CONTINUE"}, - {STATUS_FLT_IO_COMPLETE, -EIO, "STATUS_FLT_IO_COMPLETE"}, - {STATUS_OBJECT_NAME_EXISTS, -EIO, "STATUS_OBJECT_NAME_EXISTS"}, - {STATUS_THREAD_WAS_SUSPENDED, -EIO, "STATUS_THREAD_WAS_SUSPENDED"}, - {STATUS_WORKING_SET_LIMIT_RANGE, -EIO, - "STATUS_WORKING_SET_LIMIT_RANGE"}, - {STATUS_IMAGE_NOT_AT_BASE, -EIO, "STATUS_IMAGE_NOT_AT_BASE"}, - {STATUS_RXACT_STATE_CREATED, -EIO, "STATUS_RXACT_STATE_CREATED"}, - {STATUS_SEGMENT_NOTIFICATION, -EIO, "STATUS_SEGMENT_NOTIFICATION"}, - {STATUS_LOCAL_USER_SESSION_KEY, -EIO, "STATUS_LOCAL_USER_SESSION_KEY"}, - {STATUS_BAD_CURRENT_DIRECTORY, -EIO, "STATUS_BAD_CURRENT_DIRECTORY"}, - {STATUS_SERIAL_MORE_WRITES, -EIO, "STATUS_SERIAL_MORE_WRITES"}, - {STATUS_REGISTRY_RECOVERED, -EIO, "STATUS_REGISTRY_RECOVERED"}, - {STATUS_FT_READ_RECOVERY_FROM_BACKUP, -EIO, - "STATUS_FT_READ_RECOVERY_FROM_BACKUP"}, - {STATUS_FT_WRITE_RECOVERY, -EIO, "STATUS_FT_WRITE_RECOVERY"}, - {STATUS_SERIAL_COUNTER_TIMEOUT, -ETIMEDOUT, - "STATUS_SERIAL_COUNTER_TIMEOUT"}, - {STATUS_NULL_LM_PASSWORD, -EIO, "STATUS_NULL_LM_PASSWORD"}, - {STATUS_IMAGE_MACHINE_TYPE_MISMATCH, -EIO, - "STATUS_IMAGE_MACHINE_TYPE_MISMATCH"}, - {STATUS_RECEIVE_PARTIAL, -EIO, "STATUS_RECEIVE_PARTIAL"}, - {STATUS_RECEIVE_EXPEDITED, -EIO, "STATUS_RECEIVE_EXPEDITED"}, - {STATUS_RECEIVE_PARTIAL_EXPEDITED, -EIO, - "STATUS_RECEIVE_PARTIAL_EXPEDITED"}, - {STATUS_EVENT_DONE, -EIO, "STATUS_EVENT_DONE"}, - {STATUS_EVENT_PENDING, -EIO, "STATUS_EVENT_PENDING"}, - {STATUS_CHECKING_FILE_SYSTEM, -EIO, "STATUS_CHECKING_FILE_SYSTEM"}, - {STATUS_FATAL_APP_EXIT, -EIO, "STATUS_FATAL_APP_EXIT"}, - {STATUS_PREDEFINED_HANDLE, -EIO, "STATUS_PREDEFINED_HANDLE"}, - {STATUS_WAS_UNLOCKED, -EIO, "STATUS_WAS_UNLOCKED"}, - {STATUS_SERVICE_NOTIFICATION, -EIO, "STATUS_SERVICE_NOTIFICATION"}, - {STATUS_WAS_LOCKED, -EIO, "STATUS_WAS_LOCKED"}, - {STATUS_LOG_HARD_ERROR, -EIO, "STATUS_LOG_HARD_ERROR"}, - {STATUS_ALREADY_WIN32, -EIO, "STATUS_ALREADY_WIN32"}, - {STATUS_WX86_UNSIMULATE, -EIO, "STATUS_WX86_UNSIMULATE"}, - {STATUS_WX86_CONTINUE, -EIO, "STATUS_WX86_CONTINUE"}, - {STATUS_WX86_SINGLE_STEP, -EIO, "STATUS_WX86_SINGLE_STEP"}, - {STATUS_WX86_BREAKPOINT, -EIO, "STATUS_WX86_BREAKPOINT"}, - {STATUS_WX86_EXCEPTION_CONTINUE, -EIO, - "STATUS_WX86_EXCEPTION_CONTINUE"}, - {STATUS_WX86_EXCEPTION_LASTCHANCE, -EIO, - "STATUS_WX86_EXCEPTION_LASTCHANCE"}, - {STATUS_WX86_EXCEPTION_CHAIN, -EIO, "STATUS_WX86_EXCEPTION_CHAIN"}, - {STATUS_IMAGE_MACHINE_TYPE_MISMATCH_EXE, -EIO, - "STATUS_IMAGE_MACHINE_TYPE_MISMATCH_EXE"}, - {STATUS_NO_YIELD_PERFORMED, -EIO, "STATUS_NO_YIELD_PERFORMED"}, - {STATUS_TIMER_RESUME_IGNORED, -EIO, "STATUS_TIMER_RESUME_IGNORED"}, - {STATUS_ARBITRATION_UNHANDLED, -EIO, "STATUS_ARBITRATION_UNHANDLED"}, - {STATUS_CARDBUS_NOT_SUPPORTED, -ENOSYS, "STATUS_CARDBUS_NOT_SUPPORTED"}, - {STATUS_WX86_CREATEWX86TIB, -EIO, "STATUS_WX86_CREATEWX86TIB"}, - {STATUS_MP_PROCESSOR_MISMATCH, -EIO, "STATUS_MP_PROCESSOR_MISMATCH"}, - {STATUS_HIBERNATED, -EIO, "STATUS_HIBERNATED"}, - {STATUS_RESUME_HIBERNATION, -EIO, "STATUS_RESUME_HIBERNATION"}, - {STATUS_FIRMWARE_UPDATED, -EIO, "STATUS_FIRMWARE_UPDATED"}, - {STATUS_DRIVERS_LEAKING_LOCKED_PAGES, -EIO, - "STATUS_DRIVERS_LEAKING_LOCKED_PAGES"}, - {STATUS_MESSAGE_RETRIEVED, -EIO, "STATUS_MESSAGE_RETRIEVED"}, - {STATUS_SYSTEM_POWERSTATE_TRANSITION, -EIO, - "STATUS_SYSTEM_POWERSTATE_TRANSITION"}, - {STATUS_ALPC_CHECK_COMPLETION_LIST, -EIO, - "STATUS_ALPC_CHECK_COMPLETION_LIST"}, - {STATUS_SYSTEM_POWERSTATE_COMPLEX_TRANSITION, -EIO, - "STATUS_SYSTEM_POWERSTATE_COMPLEX_TRANSITION"}, - {STATUS_ACCESS_AUDIT_BY_POLICY, -EIO, "STATUS_ACCESS_AUDIT_BY_POLICY"}, - {STATUS_ABANDON_HIBERFILE, -EIO, "STATUS_ABANDON_HIBERFILE"}, - {STATUS_BIZRULES_NOT_ENABLED, -EIO, "STATUS_BIZRULES_NOT_ENABLED"}, - {STATUS_WAKE_SYSTEM, -EIO, "STATUS_WAKE_SYSTEM"}, - {STATUS_DS_SHUTTING_DOWN, -EIO, "STATUS_DS_SHUTTING_DOWN"}, - {DBG_REPLY_LATER, -EIO, "DBG_REPLY_LATER"}, - {DBG_UNABLE_TO_PROVIDE_HANDLE, -EIO, "DBG_UNABLE_TO_PROVIDE_HANDLE"}, - {DBG_TERMINATE_THREAD, -EIO, "DBG_TERMINATE_THREAD"}, - {DBG_TERMINATE_PROCESS, -EIO, "DBG_TERMINATE_PROCESS"}, - {DBG_CONTROL_C, -EIO, "DBG_CONTROL_C"}, - {DBG_PRINTEXCEPTION_C, -EIO, "DBG_PRINTEXCEPTION_C"}, - {DBG_RIPEXCEPTION, -EIO, "DBG_RIPEXCEPTION"}, - {DBG_CONTROL_BREAK, -EIO, "DBG_CONTROL_BREAK"}, - {DBG_COMMAND_EXCEPTION, -EIO, "DBG_COMMAND_EXCEPTION"}, - {RPC_NT_UUID_LOCAL_ONLY, -EIO, "RPC_NT_UUID_LOCAL_ONLY"}, - {RPC_NT_SEND_INCOMPLETE, -EIO, "RPC_NT_SEND_INCOMPLETE"}, - {STATUS_CTX_CDM_CONNECT, -EIO, "STATUS_CTX_CDM_CONNECT"}, - {STATUS_CTX_CDM_DISCONNECT, -EIO, "STATUS_CTX_CDM_DISCONNECT"}, - {STATUS_SXS_RELEASE_ACTIVATION_CONTEXT, -EIO, - "STATUS_SXS_RELEASE_ACTIVATION_CONTEXT"}, - {STATUS_RECOVERY_NOT_NEEDED, -EIO, "STATUS_RECOVERY_NOT_NEEDED"}, - {STATUS_RM_ALREADY_STARTED, -EIO, "STATUS_RM_ALREADY_STARTED"}, - {STATUS_LOG_NO_RESTART, -EIO, "STATUS_LOG_NO_RESTART"}, - {STATUS_VIDEO_DRIVER_DEBUG_REPORT_REQUEST, -EIO, - "STATUS_VIDEO_DRIVER_DEBUG_REPORT_REQUEST"}, - {STATUS_GRAPHICS_PARTIAL_DATA_POPULATED, -EIO, - "STATUS_GRAPHICS_PARTIAL_DATA_POPULATED"}, - {STATUS_GRAPHICS_DRIVER_MISMATCH, -EIO, - "STATUS_GRAPHICS_DRIVER_MISMATCH"}, - {STATUS_GRAPHICS_MODE_NOT_PINNED, -EIO, - "STATUS_GRAPHICS_MODE_NOT_PINNED"}, - {STATUS_GRAPHICS_NO_PREFERRED_MODE, -EIO, - "STATUS_GRAPHICS_NO_PREFERRED_MODE"}, - {STATUS_GRAPHICS_DATASET_IS_EMPTY, -EIO, - "STATUS_GRAPHICS_DATASET_IS_EMPTY"}, - {STATUS_GRAPHICS_NO_MORE_ELEMENTS_IN_DATASET, -EIO, - "STATUS_GRAPHICS_NO_MORE_ELEMENTS_IN_DATASET"}, - {STATUS_GRAPHICS_PATH_CONTENT_GEOMETRY_TRANSFORMATION_NOT_PINNED, -EIO, - "STATUS_GRAPHICS_PATH_CONTENT_GEOMETRY_TRANSFORMATION_NOT_PINNED"}, - {STATUS_GRAPHICS_UNKNOWN_CHILD_STATUS, -EIO, - "STATUS_GRAPHICS_UNKNOWN_CHILD_STATUS"}, - {STATUS_GRAPHICS_LEADLINK_START_DEFERRED, -EIO, - "STATUS_GRAPHICS_LEADLINK_START_DEFERRED"}, - {STATUS_GRAPHICS_POLLING_TOO_FREQUENTLY, -EIO, - "STATUS_GRAPHICS_POLLING_TOO_FREQUENTLY"}, - {STATUS_GRAPHICS_START_DEFERRED, -EIO, - "STATUS_GRAPHICS_START_DEFERRED"}, - {STATUS_NDIS_INDICATION_REQUIRED, -EIO, - "STATUS_NDIS_INDICATION_REQUIRED"}, - {STATUS_GUARD_PAGE_VIOLATION, -EIO, "STATUS_GUARD_PAGE_VIOLATION"}, - {STATUS_DATATYPE_MISALIGNMENT, -EIO, "STATUS_DATATYPE_MISALIGNMENT"}, - {STATUS_BREAKPOINT, -EIO, "STATUS_BREAKPOINT"}, - {STATUS_SINGLE_STEP, -EIO, "STATUS_SINGLE_STEP"}, - {STATUS_BUFFER_OVERFLOW, -E2BIG, "STATUS_BUFFER_OVERFLOW"}, - {STATUS_NO_MORE_FILES, -ENODATA, "STATUS_NO_MORE_FILES"}, - {STATUS_WAKE_SYSTEM_DEBUGGER, -EIO, "STATUS_WAKE_SYSTEM_DEBUGGER"}, - {STATUS_HANDLES_CLOSED, -EIO, "STATUS_HANDLES_CLOSED"}, - {STATUS_NO_INHERITANCE, -EIO, "STATUS_NO_INHERITANCE"}, - {STATUS_GUID_SUBSTITUTION_MADE, -EIO, "STATUS_GUID_SUBSTITUTION_MADE"}, - {STATUS_PARTIAL_COPY, -EIO, "STATUS_PARTIAL_COPY"}, - {STATUS_DEVICE_PAPER_EMPTY, -EIO, "STATUS_DEVICE_PAPER_EMPTY"}, - {STATUS_DEVICE_POWERED_OFF, -EIO, "STATUS_DEVICE_POWERED_OFF"}, - {STATUS_DEVICE_OFF_LINE, -EIO, "STATUS_DEVICE_OFF_LINE"}, - {STATUS_DEVICE_BUSY, -EBUSY, "STATUS_DEVICE_BUSY"}, - {STATUS_NO_MORE_EAS, -EIO, "STATUS_NO_MORE_EAS"}, - {STATUS_INVALID_EA_NAME, -EINVAL, "STATUS_INVALID_EA_NAME"}, - {STATUS_EA_LIST_INCONSISTENT, -EIO, "STATUS_EA_LIST_INCONSISTENT"}, - {STATUS_INVALID_EA_FLAG, -EINVAL, "STATUS_INVALID_EA_FLAG"}, - {STATUS_VERIFY_REQUIRED, -EIO, "STATUS_VERIFY_REQUIRED"}, - {STATUS_EXTRANEOUS_INFORMATION, -EIO, "STATUS_EXTRANEOUS_INFORMATION"}, - {STATUS_RXACT_COMMIT_NECESSARY, -EIO, "STATUS_RXACT_COMMIT_NECESSARY"}, - {STATUS_NO_MORE_ENTRIES, -EIO, "STATUS_NO_MORE_ENTRIES"}, - {STATUS_FILEMARK_DETECTED, -EIO, "STATUS_FILEMARK_DETECTED"}, - {STATUS_MEDIA_CHANGED, -EIO, "STATUS_MEDIA_CHANGED"}, - {STATUS_BUS_RESET, -EIO, "STATUS_BUS_RESET"}, - {STATUS_END_OF_MEDIA, -EIO, "STATUS_END_OF_MEDIA"}, - {STATUS_BEGINNING_OF_MEDIA, -EIO, "STATUS_BEGINNING_OF_MEDIA"}, - {STATUS_MEDIA_CHECK, -EIO, "STATUS_MEDIA_CHECK"}, - {STATUS_SETMARK_DETECTED, -EIO, "STATUS_SETMARK_DETECTED"}, - {STATUS_NO_DATA_DETECTED, -EIO, "STATUS_NO_DATA_DETECTED"}, - {STATUS_REDIRECTOR_HAS_OPEN_HANDLES, -EIO, - "STATUS_REDIRECTOR_HAS_OPEN_HANDLES"}, - {STATUS_SERVER_HAS_OPEN_HANDLES, -EIO, - "STATUS_SERVER_HAS_OPEN_HANDLES"}, - {STATUS_ALREADY_DISCONNECTED, -EIO, "STATUS_ALREADY_DISCONNECTED"}, - {STATUS_LONGJUMP, -EIO, "STATUS_LONGJUMP"}, - {STATUS_CLEANER_CARTRIDGE_INSTALLED, -EIO, - "STATUS_CLEANER_CARTRIDGE_INSTALLED"}, - {STATUS_PLUGPLAY_QUERY_VETOED, -EIO, "STATUS_PLUGPLAY_QUERY_VETOED"}, - {STATUS_UNWIND_CONSOLIDATE, -EIO, "STATUS_UNWIND_CONSOLIDATE"}, - {STATUS_REGISTRY_HIVE_RECOVERED, -EIO, - "STATUS_REGISTRY_HIVE_RECOVERED"}, - {STATUS_DLL_MIGHT_BE_INSECURE, -EIO, "STATUS_DLL_MIGHT_BE_INSECURE"}, - {STATUS_DLL_MIGHT_BE_INCOMPATIBLE, -EIO, - "STATUS_DLL_MIGHT_BE_INCOMPATIBLE"}, - {STATUS_STOPPED_ON_SYMLINK, -EOPNOTSUPP, "STATUS_STOPPED_ON_SYMLINK"}, - {STATUS_IO_REPARSE_TAG_NOT_HANDLED, -EOPNOTSUPP, - "STATUS_REPARSE_NOT_HANDLED"}, - {STATUS_DEVICE_REQUIRES_CLEANING, -EIO, - "STATUS_DEVICE_REQUIRES_CLEANING"}, - {STATUS_DEVICE_DOOR_OPEN, -EIO, "STATUS_DEVICE_DOOR_OPEN"}, - {STATUS_DATA_LOST_REPAIR, -EIO, "STATUS_DATA_LOST_REPAIR"}, - {DBG_EXCEPTION_NOT_HANDLED, -EIO, "DBG_EXCEPTION_NOT_HANDLED"}, - {STATUS_CLUSTER_NODE_ALREADY_UP, -EIO, - "STATUS_CLUSTER_NODE_ALREADY_UP"}, - {STATUS_CLUSTER_NODE_ALREADY_DOWN, -EIO, - "STATUS_CLUSTER_NODE_ALREADY_DOWN"}, - {STATUS_CLUSTER_NETWORK_ALREADY_ONLINE, -EIO, - "STATUS_CLUSTER_NETWORK_ALREADY_ONLINE"}, - {STATUS_CLUSTER_NETWORK_ALREADY_OFFLINE, -EIO, - "STATUS_CLUSTER_NETWORK_ALREADY_OFFLINE"}, - {STATUS_CLUSTER_NODE_ALREADY_MEMBER, -EIO, - "STATUS_CLUSTER_NODE_ALREADY_MEMBER"}, - {STATUS_COULD_NOT_RESIZE_LOG, -EIO, "STATUS_COULD_NOT_RESIZE_LOG"}, - {STATUS_NO_TXF_METADATA, -EIO, "STATUS_NO_TXF_METADATA"}, - {STATUS_CANT_RECOVER_WITH_HANDLE_OPEN, -EIO, - "STATUS_CANT_RECOVER_WITH_HANDLE_OPEN"}, - {STATUS_TXF_METADATA_ALREADY_PRESENT, -EIO, - "STATUS_TXF_METADATA_ALREADY_PRESENT"}, - {STATUS_TRANSACTION_SCOPE_CALLBACKS_NOT_SET, -EIO, - "STATUS_TRANSACTION_SCOPE_CALLBACKS_NOT_SET"}, - {STATUS_VIDEO_HUNG_DISPLAY_DRIVER_THREAD_RECOVERED, -EIO, - "STATUS_VIDEO_HUNG_DISPLAY_DRIVER_THREAD_RECOVERED"}, - {STATUS_FLT_BUFFER_TOO_SMALL, -ENOBUFS, "STATUS_FLT_BUFFER_TOO_SMALL"}, - {STATUS_FVE_PARTIAL_METADATA, -EIO, "STATUS_FVE_PARTIAL_METADATA"}, - {STATUS_UNSUCCESSFUL, -EIO, "STATUS_UNSUCCESSFUL"}, - {STATUS_NOT_IMPLEMENTED, -EOPNOTSUPP, "STATUS_NOT_IMPLEMENTED"}, - {STATUS_INVALID_INFO_CLASS, -EIO, "STATUS_INVALID_INFO_CLASS"}, - {STATUS_INFO_LENGTH_MISMATCH, -EIO, "STATUS_INFO_LENGTH_MISMATCH"}, - {STATUS_ACCESS_VIOLATION, -EACCES, "STATUS_ACCESS_VIOLATION"}, - {STATUS_IN_PAGE_ERROR, -EFAULT, "STATUS_IN_PAGE_ERROR"}, - {STATUS_PAGEFILE_QUOTA, -EDQUOT, "STATUS_PAGEFILE_QUOTA"}, - {STATUS_INVALID_HANDLE, -EBADF, "STATUS_INVALID_HANDLE"}, - {STATUS_BAD_INITIAL_STACK, -EIO, "STATUS_BAD_INITIAL_STACK"}, - {STATUS_BAD_INITIAL_PC, -EIO, "STATUS_BAD_INITIAL_PC"}, - {STATUS_INVALID_CID, -EIO, "STATUS_INVALID_CID"}, - {STATUS_TIMER_NOT_CANCELED, -EIO, "STATUS_TIMER_NOT_CANCELED"}, - {STATUS_INVALID_PARAMETER, -EINVAL, "STATUS_INVALID_PARAMETER"}, - {STATUS_NO_SUCH_DEVICE, -ENODEV, "STATUS_NO_SUCH_DEVICE"}, - {STATUS_NO_SUCH_FILE, -ENOENT, "STATUS_NO_SUCH_FILE"}, - {STATUS_INVALID_DEVICE_REQUEST, -EOPNOTSUPP, "STATUS_INVALID_DEVICE_REQUEST"}, - {STATUS_END_OF_FILE, -ENODATA, "STATUS_END_OF_FILE"}, - {STATUS_WRONG_VOLUME, -EIO, "STATUS_WRONG_VOLUME"}, - {STATUS_NO_MEDIA_IN_DEVICE, -EIO, "STATUS_NO_MEDIA_IN_DEVICE"}, - {STATUS_UNRECOGNIZED_MEDIA, -EIO, "STATUS_UNRECOGNIZED_MEDIA"}, - {STATUS_NONEXISTENT_SECTOR, -EIO, "STATUS_NONEXISTENT_SECTOR"}, - {STATUS_MORE_PROCESSING_REQUIRED, -EIO, - "STATUS_MORE_PROCESSING_REQUIRED"}, - {STATUS_NO_MEMORY, -EREMOTEIO, "STATUS_NO_MEMORY"}, - {STATUS_CONFLICTING_ADDRESSES, -EADDRINUSE, - "STATUS_CONFLICTING_ADDRESSES"}, - {STATUS_NOT_MAPPED_VIEW, -EIO, "STATUS_NOT_MAPPED_VIEW"}, - {STATUS_UNABLE_TO_FREE_VM, -EIO, "STATUS_UNABLE_TO_FREE_VM"}, - {STATUS_UNABLE_TO_DELETE_SECTION, -EIO, - "STATUS_UNABLE_TO_DELETE_SECTION"}, - {STATUS_INVALID_SYSTEM_SERVICE, -EIO, "STATUS_INVALID_SYSTEM_SERVICE"}, - {STATUS_ILLEGAL_INSTRUCTION, -EIO, "STATUS_ILLEGAL_INSTRUCTION"}, - {STATUS_INVALID_LOCK_SEQUENCE, -EIO, "STATUS_INVALID_LOCK_SEQUENCE"}, - {STATUS_INVALID_VIEW_SIZE, -EIO, "STATUS_INVALID_VIEW_SIZE"}, - {STATUS_INVALID_FILE_FOR_SECTION, -EIO, - "STATUS_INVALID_FILE_FOR_SECTION"}, - {STATUS_ALREADY_COMMITTED, -EIO, "STATUS_ALREADY_COMMITTED"}, - {STATUS_ACCESS_DENIED, -EACCES, "STATUS_ACCESS_DENIED"}, - {STATUS_BUFFER_TOO_SMALL, -EIO, "STATUS_BUFFER_TOO_SMALL"}, - {STATUS_OBJECT_TYPE_MISMATCH, -EIO, "STATUS_OBJECT_TYPE_MISMATCH"}, - {STATUS_NONCONTINUABLE_EXCEPTION, -EIO, - "STATUS_NONCONTINUABLE_EXCEPTION"}, - {STATUS_INVALID_DISPOSITION, -EIO, "STATUS_INVALID_DISPOSITION"}, - {STATUS_UNWIND, -EIO, "STATUS_UNWIND"}, - {STATUS_BAD_STACK, -EIO, "STATUS_BAD_STACK"}, - {STATUS_INVALID_UNWIND_TARGET, -EIO, "STATUS_INVALID_UNWIND_TARGET"}, - {STATUS_NOT_LOCKED, -EIO, "STATUS_NOT_LOCKED"}, - {STATUS_PARITY_ERROR, -EIO, "STATUS_PARITY_ERROR"}, - {STATUS_UNABLE_TO_DECOMMIT_VM, -EIO, "STATUS_UNABLE_TO_DECOMMIT_VM"}, - {STATUS_NOT_COMMITTED, -EIO, "STATUS_NOT_COMMITTED"}, - {STATUS_INVALID_PORT_ATTRIBUTES, -EIO, - "STATUS_INVALID_PORT_ATTRIBUTES"}, - {STATUS_PORT_MESSAGE_TOO_LONG, -EIO, "STATUS_PORT_MESSAGE_TOO_LONG"}, - {STATUS_INVALID_PARAMETER_MIX, -EINVAL, "STATUS_INVALID_PARAMETER_MIX"}, - {STATUS_INVALID_QUOTA_LOWER, -EIO, "STATUS_INVALID_QUOTA_LOWER"}, - {STATUS_DISK_CORRUPT_ERROR, -EIO, "STATUS_DISK_CORRUPT_ERROR"}, - {STATUS_OBJECT_NAME_INVALID, -ENOENT, "STATUS_OBJECT_NAME_INVALID"}, - {STATUS_OBJECT_NAME_NOT_FOUND, -ENOENT, "STATUS_OBJECT_NAME_NOT_FOUND"}, - {STATUS_OBJECT_NAME_COLLISION, -EEXIST, "STATUS_OBJECT_NAME_COLLISION"}, - {STATUS_PORT_DISCONNECTED, -EIO, "STATUS_PORT_DISCONNECTED"}, - {STATUS_DEVICE_ALREADY_ATTACHED, -EIO, - "STATUS_DEVICE_ALREADY_ATTACHED"}, - {STATUS_OBJECT_PATH_INVALID, -ENOTDIR, "STATUS_OBJECT_PATH_INVALID"}, - {STATUS_OBJECT_PATH_NOT_FOUND, -ENOENT, "STATUS_OBJECT_PATH_NOT_FOUND"}, - {STATUS_OBJECT_PATH_SYNTAX_BAD, -EIO, "STATUS_OBJECT_PATH_SYNTAX_BAD"}, - {STATUS_DATA_OVERRUN, -EIO, "STATUS_DATA_OVERRUN"}, - {STATUS_DATA_LATE_ERROR, -EIO, "STATUS_DATA_LATE_ERROR"}, - {STATUS_DATA_ERROR, -EIO, "STATUS_DATA_ERROR"}, - {STATUS_CRC_ERROR, -EIO, "STATUS_CRC_ERROR"}, - {STATUS_SECTION_TOO_BIG, -EIO, "STATUS_SECTION_TOO_BIG"}, - {STATUS_PORT_CONNECTION_REFUSED, -ECONNREFUSED, - "STATUS_PORT_CONNECTION_REFUSED"}, - {STATUS_INVALID_PORT_HANDLE, -EIO, "STATUS_INVALID_PORT_HANDLE"}, - {STATUS_SHARING_VIOLATION, -EBUSY, "STATUS_SHARING_VIOLATION"}, - {STATUS_QUOTA_EXCEEDED, -EDQUOT, "STATUS_QUOTA_EXCEEDED"}, - {STATUS_INVALID_PAGE_PROTECTION, -EIO, - "STATUS_INVALID_PAGE_PROTECTION"}, - {STATUS_MUTANT_NOT_OWNED, -EIO, "STATUS_MUTANT_NOT_OWNED"}, - {STATUS_SEMAPHORE_LIMIT_EXCEEDED, -EIO, - "STATUS_SEMAPHORE_LIMIT_EXCEEDED"}, - {STATUS_PORT_ALREADY_SET, -EIO, "STATUS_PORT_ALREADY_SET"}, - {STATUS_SECTION_NOT_IMAGE, -EIO, "STATUS_SECTION_NOT_IMAGE"}, - {STATUS_SUSPEND_COUNT_EXCEEDED, -EIO, "STATUS_SUSPEND_COUNT_EXCEEDED"}, - {STATUS_THREAD_IS_TERMINATING, -EIO, "STATUS_THREAD_IS_TERMINATING"}, - {STATUS_BAD_WORKING_SET_LIMIT, -EIO, "STATUS_BAD_WORKING_SET_LIMIT"}, - {STATUS_INCOMPATIBLE_FILE_MAP, -EIO, "STATUS_INCOMPATIBLE_FILE_MAP"}, - {STATUS_SECTION_PROTECTION, -EIO, "STATUS_SECTION_PROTECTION"}, - {STATUS_EAS_NOT_SUPPORTED, -EOPNOTSUPP, "STATUS_EAS_NOT_SUPPORTED"}, - {STATUS_EA_TOO_LARGE, -EIO, "STATUS_EA_TOO_LARGE"}, - {STATUS_NONEXISTENT_EA_ENTRY, -EIO, "STATUS_NONEXISTENT_EA_ENTRY"}, - {STATUS_NO_EAS_ON_FILE, -ENODATA, "STATUS_NO_EAS_ON_FILE"}, - {STATUS_EA_CORRUPT_ERROR, -EIO, "STATUS_EA_CORRUPT_ERROR"}, - {STATUS_FILE_LOCK_CONFLICT, -EACCES, "STATUS_FILE_LOCK_CONFLICT"}, - {STATUS_LOCK_NOT_GRANTED, -EACCES, "STATUS_LOCK_NOT_GRANTED"}, - {STATUS_DELETE_PENDING, -ENOENT, "STATUS_DELETE_PENDING"}, - {STATUS_CTL_FILE_NOT_SUPPORTED, -ENOSYS, - "STATUS_CTL_FILE_NOT_SUPPORTED"}, - {STATUS_UNKNOWN_REVISION, -EIO, "STATUS_UNKNOWN_REVISION"}, - {STATUS_REVISION_MISMATCH, -EIO, "STATUS_REVISION_MISMATCH"}, - {STATUS_INVALID_OWNER, -EIO, "STATUS_INVALID_OWNER"}, - {STATUS_INVALID_PRIMARY_GROUP, -EIO, "STATUS_INVALID_PRIMARY_GROUP"}, - {STATUS_NO_IMPERSONATION_TOKEN, -EIO, "STATUS_NO_IMPERSONATION_TOKEN"}, - {STATUS_CANT_DISABLE_MANDATORY, -EIO, "STATUS_CANT_DISABLE_MANDATORY"}, - {STATUS_NO_LOGON_SERVERS, -EIO, "STATUS_NO_LOGON_SERVERS"}, - {STATUS_NO_SUCH_LOGON_SESSION, -EIO, "STATUS_NO_SUCH_LOGON_SESSION"}, - {STATUS_NO_SUCH_PRIVILEGE, -EIO, "STATUS_NO_SUCH_PRIVILEGE"}, - {STATUS_PRIVILEGE_NOT_HELD, -EPERM, "STATUS_PRIVILEGE_NOT_HELD"}, - {STATUS_INVALID_ACCOUNT_NAME, -EIO, "STATUS_INVALID_ACCOUNT_NAME"}, - {STATUS_USER_EXISTS, -EIO, "STATUS_USER_EXISTS"}, - {STATUS_NO_SUCH_USER, -EIO, "STATUS_NO_SUCH_USER"}, - {STATUS_GROUP_EXISTS, -EIO, "STATUS_GROUP_EXISTS"}, - {STATUS_NO_SUCH_GROUP, -EIO, "STATUS_NO_SUCH_GROUP"}, - {STATUS_MEMBER_IN_GROUP, -EIO, "STATUS_MEMBER_IN_GROUP"}, - {STATUS_MEMBER_NOT_IN_GROUP, -EIO, "STATUS_MEMBER_NOT_IN_GROUP"}, - {STATUS_LAST_ADMIN, -EIO, "STATUS_LAST_ADMIN"}, - {STATUS_WRONG_PASSWORD, -EACCES, "STATUS_WRONG_PASSWORD"}, - {STATUS_ILL_FORMED_PASSWORD, -EINVAL, "STATUS_ILL_FORMED_PASSWORD"}, - {STATUS_PASSWORD_RESTRICTION, -EACCES, "STATUS_PASSWORD_RESTRICTION"}, - {STATUS_LOGON_FAILURE, -EACCES, "STATUS_LOGON_FAILURE"}, - {STATUS_ACCOUNT_RESTRICTION, -EACCES, "STATUS_ACCOUNT_RESTRICTION"}, - {STATUS_INVALID_LOGON_HOURS, -EACCES, "STATUS_INVALID_LOGON_HOURS"}, - {STATUS_INVALID_WORKSTATION, -EACCES, "STATUS_INVALID_WORKSTATION"}, - {STATUS_PASSWORD_EXPIRED, -EKEYEXPIRED, "STATUS_PASSWORD_EXPIRED"}, - {STATUS_ACCOUNT_DISABLED, -EKEYREVOKED, "STATUS_ACCOUNT_DISABLED"}, - {STATUS_NONE_MAPPED, -EIO, "STATUS_NONE_MAPPED"}, - {STATUS_TOO_MANY_LUIDS_REQUESTED, -EIO, - "STATUS_TOO_MANY_LUIDS_REQUESTED"}, - {STATUS_LUIDS_EXHAUSTED, -EIO, "STATUS_LUIDS_EXHAUSTED"}, - {STATUS_INVALID_SUB_AUTHORITY, -EIO, "STATUS_INVALID_SUB_AUTHORITY"}, - {STATUS_INVALID_ACL, -EIO, "STATUS_INVALID_ACL"}, - {STATUS_INVALID_SID, -EIO, "STATUS_INVALID_SID"}, - {STATUS_INVALID_SECURITY_DESCR, -EIO, "STATUS_INVALID_SECURITY_DESCR"}, - {STATUS_PROCEDURE_NOT_FOUND, -EIO, "STATUS_PROCEDURE_NOT_FOUND"}, - {STATUS_INVALID_IMAGE_FORMAT, -EIO, "STATUS_INVALID_IMAGE_FORMAT"}, - {STATUS_NO_TOKEN, -EIO, "STATUS_NO_TOKEN"}, - {STATUS_BAD_INHERITANCE_ACL, -EIO, "STATUS_BAD_INHERITANCE_ACL"}, - {STATUS_RANGE_NOT_LOCKED, -EIO, "STATUS_RANGE_NOT_LOCKED"}, - {STATUS_DISK_FULL, -ENOSPC, "STATUS_DISK_FULL"}, - {STATUS_SERVER_DISABLED, -EIO, "STATUS_SERVER_DISABLED"}, - {STATUS_SERVER_NOT_DISABLED, -EIO, "STATUS_SERVER_NOT_DISABLED"}, - {STATUS_TOO_MANY_GUIDS_REQUESTED, -EIO, - "STATUS_TOO_MANY_GUIDS_REQUESTED"}, - {STATUS_GUIDS_EXHAUSTED, -EIO, "STATUS_GUIDS_EXHAUSTED"}, - {STATUS_INVALID_ID_AUTHORITY, -EIO, "STATUS_INVALID_ID_AUTHORITY"}, - {STATUS_AGENTS_EXHAUSTED, -EIO, "STATUS_AGENTS_EXHAUSTED"}, - {STATUS_INVALID_VOLUME_LABEL, -EIO, "STATUS_INVALID_VOLUME_LABEL"}, - {STATUS_SECTION_NOT_EXTENDED, -EIO, "STATUS_SECTION_NOT_EXTENDED"}, - {STATUS_NOT_MAPPED_DATA, -EIO, "STATUS_NOT_MAPPED_DATA"}, - {STATUS_RESOURCE_DATA_NOT_FOUND, -EIO, - "STATUS_RESOURCE_DATA_NOT_FOUND"}, - {STATUS_RESOURCE_TYPE_NOT_FOUND, -EIO, - "STATUS_RESOURCE_TYPE_NOT_FOUND"}, - {STATUS_RESOURCE_NAME_NOT_FOUND, -EIO, - "STATUS_RESOURCE_NAME_NOT_FOUND"}, - {STATUS_ARRAY_BOUNDS_EXCEEDED, -EIO, "STATUS_ARRAY_BOUNDS_EXCEEDED"}, - {STATUS_FLOAT_DENORMAL_OPERAND, -EIO, "STATUS_FLOAT_DENORMAL_OPERAND"}, - {STATUS_FLOAT_DIVIDE_BY_ZERO, -EIO, "STATUS_FLOAT_DIVIDE_BY_ZERO"}, - {STATUS_FLOAT_INEXACT_RESULT, -EIO, "STATUS_FLOAT_INEXACT_RESULT"}, - {STATUS_FLOAT_INVALID_OPERATION, -EIO, - "STATUS_FLOAT_INVALID_OPERATION"}, - {STATUS_FLOAT_OVERFLOW, -EIO, "STATUS_FLOAT_OVERFLOW"}, - {STATUS_FLOAT_STACK_CHECK, -EIO, "STATUS_FLOAT_STACK_CHECK"}, - {STATUS_FLOAT_UNDERFLOW, -EIO, "STATUS_FLOAT_UNDERFLOW"}, - {STATUS_INTEGER_DIVIDE_BY_ZERO, -EIO, "STATUS_INTEGER_DIVIDE_BY_ZERO"}, - {STATUS_INTEGER_OVERFLOW, -EIO, "STATUS_INTEGER_OVERFLOW"}, - {STATUS_PRIVILEGED_INSTRUCTION, -EIO, "STATUS_PRIVILEGED_INSTRUCTION"}, - {STATUS_TOO_MANY_PAGING_FILES, -EIO, "STATUS_TOO_MANY_PAGING_FILES"}, - {STATUS_FILE_INVALID, -EIO, "STATUS_FILE_INVALID"}, - {STATUS_ALLOTTED_SPACE_EXCEEDED, -EIO, - "STATUS_ALLOTTED_SPACE_EXCEEDED"}, - {STATUS_INSUFFICIENT_RESOURCES, -EAGAIN, - "STATUS_INSUFFICIENT_RESOURCES"}, - {STATUS_DFS_EXIT_PATH_FOUND, -EIO, "STATUS_DFS_EXIT_PATH_FOUND"}, - {STATUS_DEVICE_DATA_ERROR, -EIO, "STATUS_DEVICE_DATA_ERROR"}, - {STATUS_DEVICE_NOT_CONNECTED, -EIO, "STATUS_DEVICE_NOT_CONNECTED"}, - {STATUS_DEVICE_POWER_FAILURE, -EIO, "STATUS_DEVICE_POWER_FAILURE"}, - {STATUS_FREE_VM_NOT_AT_BASE, -EIO, "STATUS_FREE_VM_NOT_AT_BASE"}, - {STATUS_MEMORY_NOT_ALLOCATED, -EFAULT, "STATUS_MEMORY_NOT_ALLOCATED"}, - {STATUS_WORKING_SET_QUOTA, -EIO, "STATUS_WORKING_SET_QUOTA"}, - {STATUS_MEDIA_WRITE_PROTECTED, -EROFS, "STATUS_MEDIA_WRITE_PROTECTED"}, - {STATUS_DEVICE_NOT_READY, -EIO, "STATUS_DEVICE_NOT_READY"}, - {STATUS_INVALID_GROUP_ATTRIBUTES, -EIO, - "STATUS_INVALID_GROUP_ATTRIBUTES"}, - {STATUS_BAD_IMPERSONATION_LEVEL, -EIO, - "STATUS_BAD_IMPERSONATION_LEVEL"}, - {STATUS_CANT_OPEN_ANONYMOUS, -EIO, "STATUS_CANT_OPEN_ANONYMOUS"}, - {STATUS_BAD_VALIDATION_CLASS, -EIO, "STATUS_BAD_VALIDATION_CLASS"}, - {STATUS_BAD_TOKEN_TYPE, -EIO, "STATUS_BAD_TOKEN_TYPE"}, - {STATUS_BAD_MASTER_BOOT_RECORD, -EIO, "STATUS_BAD_MASTER_BOOT_RECORD"}, - {STATUS_INSTRUCTION_MISALIGNMENT, -EIO, - "STATUS_INSTRUCTION_MISALIGNMENT"}, - {STATUS_INSTANCE_NOT_AVAILABLE, -EIO, "STATUS_INSTANCE_NOT_AVAILABLE"}, - {STATUS_PIPE_NOT_AVAILABLE, -EIO, "STATUS_PIPE_NOT_AVAILABLE"}, - {STATUS_INVALID_PIPE_STATE, -EIO, "STATUS_INVALID_PIPE_STATE"}, - {STATUS_PIPE_BUSY, -EBUSY, "STATUS_PIPE_BUSY"}, - {STATUS_ILLEGAL_FUNCTION, -EIO, "STATUS_ILLEGAL_FUNCTION"}, - {STATUS_PIPE_DISCONNECTED, -EPIPE, "STATUS_PIPE_DISCONNECTED"}, - {STATUS_PIPE_CLOSING, -EIO, "STATUS_PIPE_CLOSING"}, - {STATUS_PIPE_CONNECTED, -EIO, "STATUS_PIPE_CONNECTED"}, - {STATUS_PIPE_LISTENING, -EIO, "STATUS_PIPE_LISTENING"}, - {STATUS_INVALID_READ_MODE, -EIO, "STATUS_INVALID_READ_MODE"}, - {STATUS_IO_TIMEOUT, -EAGAIN, "STATUS_IO_TIMEOUT"}, - {STATUS_FILE_FORCED_CLOSED, -EIO, "STATUS_FILE_FORCED_CLOSED"}, - {STATUS_PROFILING_NOT_STARTED, -EIO, "STATUS_PROFILING_NOT_STARTED"}, - {STATUS_PROFILING_NOT_STOPPED, -EIO, "STATUS_PROFILING_NOT_STOPPED"}, - {STATUS_COULD_NOT_INTERPRET, -EIO, "STATUS_COULD_NOT_INTERPRET"}, - {STATUS_FILE_IS_A_DIRECTORY, -EISDIR, "STATUS_FILE_IS_A_DIRECTORY"}, - {STATUS_NOT_SUPPORTED, -EOPNOTSUPP, "STATUS_NOT_SUPPORTED"}, - {STATUS_REMOTE_NOT_LISTENING, -EHOSTDOWN, - "STATUS_REMOTE_NOT_LISTENING"}, - {STATUS_DUPLICATE_NAME, -ENOTUNIQ, "STATUS_DUPLICATE_NAME"}, - {STATUS_BAD_NETWORK_PATH, -EINVAL, "STATUS_BAD_NETWORK_PATH"}, - {STATUS_NETWORK_BUSY, -EBUSY, "STATUS_NETWORK_BUSY"}, - {STATUS_DEVICE_DOES_NOT_EXIST, -ENODEV, "STATUS_DEVICE_DOES_NOT_EXIST"}, - {STATUS_TOO_MANY_COMMANDS, -EIO, "STATUS_TOO_MANY_COMMANDS"}, - {STATUS_ADAPTER_HARDWARE_ERROR, -EIO, "STATUS_ADAPTER_HARDWARE_ERROR"}, - {STATUS_INVALID_NETWORK_RESPONSE, -EIO, - "STATUS_INVALID_NETWORK_RESPONSE"}, - {STATUS_UNEXPECTED_NETWORK_ERROR, -EIO, - "STATUS_UNEXPECTED_NETWORK_ERROR"}, - {STATUS_BAD_REMOTE_ADAPTER, -EIO, "STATUS_BAD_REMOTE_ADAPTER"}, - {STATUS_PRINT_QUEUE_FULL, -EIO, "STATUS_PRINT_QUEUE_FULL"}, - {STATUS_NO_SPOOL_SPACE, -EIO, "STATUS_NO_SPOOL_SPACE"}, - {STATUS_PRINT_CANCELLED, -EIO, "STATUS_PRINT_CANCELLED"}, - {STATUS_NETWORK_NAME_DELETED, -EREMCHG, "STATUS_NETWORK_NAME_DELETED"}, - {STATUS_NETWORK_ACCESS_DENIED, -EACCES, "STATUS_NETWORK_ACCESS_DENIED"}, - {STATUS_BAD_DEVICE_TYPE, -EIO, "STATUS_BAD_DEVICE_TYPE"}, - {STATUS_BAD_NETWORK_NAME, -ENOENT, "STATUS_BAD_NETWORK_NAME"}, - {STATUS_TOO_MANY_NAMES, -EIO, "STATUS_TOO_MANY_NAMES"}, - {STATUS_TOO_MANY_SESSIONS, -EIO, "STATUS_TOO_MANY_SESSIONS"}, - {STATUS_SHARING_PAUSED, -EIO, "STATUS_SHARING_PAUSED"}, - {STATUS_REQUEST_NOT_ACCEPTED, -EIO, "STATUS_REQUEST_NOT_ACCEPTED"}, - {STATUS_REDIRECTOR_PAUSED, -EIO, "STATUS_REDIRECTOR_PAUSED"}, - {STATUS_NET_WRITE_FAULT, -EIO, "STATUS_NET_WRITE_FAULT"}, - {STATUS_PROFILING_AT_LIMIT, -EIO, "STATUS_PROFILING_AT_LIMIT"}, - {STATUS_NOT_SAME_DEVICE, -EXDEV, "STATUS_NOT_SAME_DEVICE"}, - {STATUS_FILE_RENAMED, -EIO, "STATUS_FILE_RENAMED"}, - {STATUS_VIRTUAL_CIRCUIT_CLOSED, -EIO, "STATUS_VIRTUAL_CIRCUIT_CLOSED"}, - {STATUS_NO_SECURITY_ON_OBJECT, -EIO, "STATUS_NO_SECURITY_ON_OBJECT"}, - {STATUS_CANT_WAIT, -EIO, "STATUS_CANT_WAIT"}, - {STATUS_PIPE_EMPTY, -EIO, "STATUS_PIPE_EMPTY"}, - {STATUS_CANT_ACCESS_DOMAIN_INFO, -EIO, - "STATUS_CANT_ACCESS_DOMAIN_INFO"}, - {STATUS_CANT_TERMINATE_SELF, -EIO, "STATUS_CANT_TERMINATE_SELF"}, - {STATUS_INVALID_SERVER_STATE, -EIO, "STATUS_INVALID_SERVER_STATE"}, - {STATUS_INVALID_DOMAIN_STATE, -EIO, "STATUS_INVALID_DOMAIN_STATE"}, - {STATUS_INVALID_DOMAIN_ROLE, -EIO, "STATUS_INVALID_DOMAIN_ROLE"}, - {STATUS_NO_SUCH_DOMAIN, -EIO, "STATUS_NO_SUCH_DOMAIN"}, - {STATUS_DOMAIN_EXISTS, -EIO, "STATUS_DOMAIN_EXISTS"}, - {STATUS_DOMAIN_LIMIT_EXCEEDED, -EIO, "STATUS_DOMAIN_LIMIT_EXCEEDED"}, - {STATUS_OPLOCK_NOT_GRANTED, -EIO, "STATUS_OPLOCK_NOT_GRANTED"}, - {STATUS_INVALID_OPLOCK_PROTOCOL, -EIO, - "STATUS_INVALID_OPLOCK_PROTOCOL"}, - {STATUS_INTERNAL_DB_CORRUPTION, -EIO, "STATUS_INTERNAL_DB_CORRUPTION"}, - {STATUS_INTERNAL_ERROR, -EIO, "STATUS_INTERNAL_ERROR"}, - {STATUS_GENERIC_NOT_MAPPED, -EIO, "STATUS_GENERIC_NOT_MAPPED"}, - {STATUS_BAD_DESCRIPTOR_FORMAT, -EIO, "STATUS_BAD_DESCRIPTOR_FORMAT"}, - {STATUS_INVALID_USER_BUFFER, -EIO, "STATUS_INVALID_USER_BUFFER"}, - {STATUS_UNEXPECTED_IO_ERROR, -EIO, "STATUS_UNEXPECTED_IO_ERROR"}, - {STATUS_UNEXPECTED_MM_CREATE_ERR, -EIO, - "STATUS_UNEXPECTED_MM_CREATE_ERR"}, - {STATUS_UNEXPECTED_MM_MAP_ERROR, -EIO, - "STATUS_UNEXPECTED_MM_MAP_ERROR"}, - {STATUS_UNEXPECTED_MM_EXTEND_ERR, -EIO, - "STATUS_UNEXPECTED_MM_EXTEND_ERR"}, - {STATUS_NOT_LOGON_PROCESS, -EIO, "STATUS_NOT_LOGON_PROCESS"}, - {STATUS_LOGON_SESSION_EXISTS, -EIO, "STATUS_LOGON_SESSION_EXISTS"}, - {STATUS_INVALID_PARAMETER_1, -EINVAL, "STATUS_INVALID_PARAMETER_1"}, - {STATUS_INVALID_PARAMETER_2, -EINVAL, "STATUS_INVALID_PARAMETER_2"}, - {STATUS_INVALID_PARAMETER_3, -EINVAL, "STATUS_INVALID_PARAMETER_3"}, - {STATUS_INVALID_PARAMETER_4, -EINVAL, "STATUS_INVALID_PARAMETER_4"}, - {STATUS_INVALID_PARAMETER_5, -EINVAL, "STATUS_INVALID_PARAMETER_5"}, - {STATUS_INVALID_PARAMETER_6, -EINVAL, "STATUS_INVALID_PARAMETER_6"}, - {STATUS_INVALID_PARAMETER_7, -EINVAL, "STATUS_INVALID_PARAMETER_7"}, - {STATUS_INVALID_PARAMETER_8, -EINVAL, "STATUS_INVALID_PARAMETER_8"}, - {STATUS_INVALID_PARAMETER_9, -EINVAL, "STATUS_INVALID_PARAMETER_9"}, - {STATUS_INVALID_PARAMETER_10, -EINVAL, "STATUS_INVALID_PARAMETER_10"}, - {STATUS_INVALID_PARAMETER_11, -EINVAL, "STATUS_INVALID_PARAMETER_11"}, - {STATUS_INVALID_PARAMETER_12, -EINVAL, "STATUS_INVALID_PARAMETER_12"}, - {STATUS_REDIRECTOR_NOT_STARTED, -EIO, "STATUS_REDIRECTOR_NOT_STARTED"}, - {STATUS_REDIRECTOR_STARTED, -EIO, "STATUS_REDIRECTOR_STARTED"}, - {STATUS_STACK_OVERFLOW, -EIO, "STATUS_STACK_OVERFLOW"}, - {STATUS_NO_SUCH_PACKAGE, -EIO, "STATUS_NO_SUCH_PACKAGE"}, - {STATUS_BAD_FUNCTION_TABLE, -EIO, "STATUS_BAD_FUNCTION_TABLE"}, - {STATUS_VARIABLE_NOT_FOUND, -EIO, "STATUS_VARIABLE_NOT_FOUND"}, - {STATUS_DIRECTORY_NOT_EMPTY, -ENOTEMPTY, "STATUS_DIRECTORY_NOT_EMPTY"}, - {STATUS_FILE_CORRUPT_ERROR, -EIO, "STATUS_FILE_CORRUPT_ERROR"}, - {STATUS_NOT_A_DIRECTORY, -ENOTDIR, "STATUS_NOT_A_DIRECTORY"}, - {STATUS_BAD_LOGON_SESSION_STATE, -EIO, - "STATUS_BAD_LOGON_SESSION_STATE"}, - {STATUS_LOGON_SESSION_COLLISION, -EIO, - "STATUS_LOGON_SESSION_COLLISION"}, - {STATUS_NAME_TOO_LONG, -ENAMETOOLONG, "STATUS_NAME_TOO_LONG"}, - {STATUS_FILES_OPEN, -EIO, "STATUS_FILES_OPEN"}, - {STATUS_CONNECTION_IN_USE, -EIO, "STATUS_CONNECTION_IN_USE"}, - {STATUS_MESSAGE_NOT_FOUND, -EIO, "STATUS_MESSAGE_NOT_FOUND"}, - {STATUS_PROCESS_IS_TERMINATING, -EIO, "STATUS_PROCESS_IS_TERMINATING"}, - {STATUS_INVALID_LOGON_TYPE, -EIO, "STATUS_INVALID_LOGON_TYPE"}, - {STATUS_NO_GUID_TRANSLATION, -EIO, "STATUS_NO_GUID_TRANSLATION"}, - {STATUS_CANNOT_IMPERSONATE, -EIO, "STATUS_CANNOT_IMPERSONATE"}, - {STATUS_IMAGE_ALREADY_LOADED, -EIO, "STATUS_IMAGE_ALREADY_LOADED"}, - {STATUS_ABIOS_NOT_PRESENT, -EIO, "STATUS_ABIOS_NOT_PRESENT"}, - {STATUS_ABIOS_LID_NOT_EXIST, -EIO, "STATUS_ABIOS_LID_NOT_EXIST"}, - {STATUS_ABIOS_LID_ALREADY_OWNED, -EIO, - "STATUS_ABIOS_LID_ALREADY_OWNED"}, - {STATUS_ABIOS_NOT_LID_OWNER, -EIO, "STATUS_ABIOS_NOT_LID_OWNER"}, - {STATUS_ABIOS_INVALID_COMMAND, -EIO, "STATUS_ABIOS_INVALID_COMMAND"}, - {STATUS_ABIOS_INVALID_LID, -EIO, "STATUS_ABIOS_INVALID_LID"}, - {STATUS_ABIOS_SELECTOR_NOT_AVAILABLE, -EIO, - "STATUS_ABIOS_SELECTOR_NOT_AVAILABLE"}, - {STATUS_ABIOS_INVALID_SELECTOR, -EIO, "STATUS_ABIOS_INVALID_SELECTOR"}, - {STATUS_NO_LDT, -EIO, "STATUS_NO_LDT"}, - {STATUS_INVALID_LDT_SIZE, -EIO, "STATUS_INVALID_LDT_SIZE"}, - {STATUS_INVALID_LDT_OFFSET, -EIO, "STATUS_INVALID_LDT_OFFSET"}, - {STATUS_INVALID_LDT_DESCRIPTOR, -EIO, "STATUS_INVALID_LDT_DESCRIPTOR"}, - {STATUS_INVALID_IMAGE_NE_FORMAT, -EIO, - "STATUS_INVALID_IMAGE_NE_FORMAT"}, - {STATUS_RXACT_INVALID_STATE, -EIO, "STATUS_RXACT_INVALID_STATE"}, - {STATUS_RXACT_COMMIT_FAILURE, -EIO, "STATUS_RXACT_COMMIT_FAILURE"}, - {STATUS_MAPPED_FILE_SIZE_ZERO, -EIO, "STATUS_MAPPED_FILE_SIZE_ZERO"}, - {STATUS_TOO_MANY_OPENED_FILES, -EMFILE, "STATUS_TOO_MANY_OPENED_FILES"}, - {STATUS_CANCELLED, -EIO, "STATUS_CANCELLED"}, - {STATUS_CANNOT_DELETE, -EACCES, "STATUS_CANNOT_DELETE"}, - {STATUS_INVALID_COMPUTER_NAME, -EIO, "STATUS_INVALID_COMPUTER_NAME"}, - {STATUS_FILE_DELETED, -EIO, "STATUS_FILE_DELETED"}, - {STATUS_SPECIAL_ACCOUNT, -EIO, "STATUS_SPECIAL_ACCOUNT"}, - {STATUS_SPECIAL_GROUP, -EIO, "STATUS_SPECIAL_GROUP"}, - {STATUS_SPECIAL_USER, -EIO, "STATUS_SPECIAL_USER"}, - {STATUS_MEMBERS_PRIMARY_GROUP, -EIO, "STATUS_MEMBERS_PRIMARY_GROUP"}, - {STATUS_FILE_CLOSED, -EBADF, "STATUS_FILE_CLOSED"}, - {STATUS_TOO_MANY_THREADS, -EIO, "STATUS_TOO_MANY_THREADS"}, - {STATUS_THREAD_NOT_IN_PROCESS, -EIO, "STATUS_THREAD_NOT_IN_PROCESS"}, - {STATUS_TOKEN_ALREADY_IN_USE, -EIO, "STATUS_TOKEN_ALREADY_IN_USE"}, - {STATUS_PAGEFILE_QUOTA_EXCEEDED, -EDQUOT, - "STATUS_PAGEFILE_QUOTA_EXCEEDED"}, - {STATUS_COMMITMENT_LIMIT, -EIO, "STATUS_COMMITMENT_LIMIT"}, - {STATUS_INVALID_IMAGE_LE_FORMAT, -EIO, - "STATUS_INVALID_IMAGE_LE_FORMAT"}, - {STATUS_INVALID_IMAGE_NOT_MZ, -EIO, "STATUS_INVALID_IMAGE_NOT_MZ"}, - {STATUS_INVALID_IMAGE_PROTECT, -EIO, "STATUS_INVALID_IMAGE_PROTECT"}, - {STATUS_INVALID_IMAGE_WIN_16, -EIO, "STATUS_INVALID_IMAGE_WIN_16"}, - {STATUS_LOGON_SERVER_CONFLICT, -EIO, "STATUS_LOGON_SERVER_CONFLICT"}, - {STATUS_TIME_DIFFERENCE_AT_DC, -EIO, "STATUS_TIME_DIFFERENCE_AT_DC"}, - {STATUS_SYNCHRONIZATION_REQUIRED, -EIO, - "STATUS_SYNCHRONIZATION_REQUIRED"}, - {STATUS_DLL_NOT_FOUND, -ENOENT, "STATUS_DLL_NOT_FOUND"}, - {STATUS_OPEN_FAILED, -EIO, "STATUS_OPEN_FAILED"}, - {STATUS_IO_PRIVILEGE_FAILED, -EIO, "STATUS_IO_PRIVILEGE_FAILED"}, - {STATUS_ORDINAL_NOT_FOUND, -EIO, "STATUS_ORDINAL_NOT_FOUND"}, - {STATUS_ENTRYPOINT_NOT_FOUND, -EIO, "STATUS_ENTRYPOINT_NOT_FOUND"}, - {STATUS_CONTROL_C_EXIT, -EIO, "STATUS_CONTROL_C_EXIT"}, - {STATUS_LOCAL_DISCONNECT, -EIO, "STATUS_LOCAL_DISCONNECT"}, - {STATUS_REMOTE_DISCONNECT, -ESHUTDOWN, "STATUS_REMOTE_DISCONNECT"}, - {STATUS_REMOTE_RESOURCES, -EIO, "STATUS_REMOTE_RESOURCES"}, - {STATUS_LINK_FAILED, -EXDEV, "STATUS_LINK_FAILED"}, - {STATUS_LINK_TIMEOUT, -ETIMEDOUT, "STATUS_LINK_TIMEOUT"}, - {STATUS_INVALID_CONNECTION, -EIO, "STATUS_INVALID_CONNECTION"}, - {STATUS_INVALID_ADDRESS, -EIO, "STATUS_INVALID_ADDRESS"}, - {STATUS_DLL_INIT_FAILED, -EIO, "STATUS_DLL_INIT_FAILED"}, - {STATUS_MISSING_SYSTEMFILE, -EIO, "STATUS_MISSING_SYSTEMFILE"}, - {STATUS_UNHANDLED_EXCEPTION, -EIO, "STATUS_UNHANDLED_EXCEPTION"}, - {STATUS_APP_INIT_FAILURE, -EIO, "STATUS_APP_INIT_FAILURE"}, - {STATUS_PAGEFILE_CREATE_FAILED, -EIO, "STATUS_PAGEFILE_CREATE_FAILED"}, - {STATUS_NO_PAGEFILE, -EIO, "STATUS_NO_PAGEFILE"}, - {STATUS_INVALID_LEVEL, -EIO, "STATUS_INVALID_LEVEL"}, - {STATUS_WRONG_PASSWORD_CORE, -EIO, "STATUS_WRONG_PASSWORD_CORE"}, - {STATUS_ILLEGAL_FLOAT_CONTEXT, -EIO, "STATUS_ILLEGAL_FLOAT_CONTEXT"}, - {STATUS_PIPE_BROKEN, -EPIPE, "STATUS_PIPE_BROKEN"}, - {STATUS_REGISTRY_CORRUPT, -EIO, "STATUS_REGISTRY_CORRUPT"}, - {STATUS_REGISTRY_IO_FAILED, -EIO, "STATUS_REGISTRY_IO_FAILED"}, - {STATUS_NO_EVENT_PAIR, -EIO, "STATUS_NO_EVENT_PAIR"}, - {STATUS_UNRECOGNIZED_VOLUME, -EIO, "STATUS_UNRECOGNIZED_VOLUME"}, - {STATUS_SERIAL_NO_DEVICE_INITED, -EIO, - "STATUS_SERIAL_NO_DEVICE_INITED"}, - {STATUS_NO_SUCH_ALIAS, -EIO, "STATUS_NO_SUCH_ALIAS"}, - {STATUS_MEMBER_NOT_IN_ALIAS, -EIO, "STATUS_MEMBER_NOT_IN_ALIAS"}, - {STATUS_MEMBER_IN_ALIAS, -EIO, "STATUS_MEMBER_IN_ALIAS"}, - {STATUS_ALIAS_EXISTS, -EIO, "STATUS_ALIAS_EXISTS"}, - {STATUS_LOGON_NOT_GRANTED, -EIO, "STATUS_LOGON_NOT_GRANTED"}, - {STATUS_TOO_MANY_SECRETS, -EIO, "STATUS_TOO_MANY_SECRETS"}, - {STATUS_SECRET_TOO_LONG, -EIO, "STATUS_SECRET_TOO_LONG"}, - {STATUS_INTERNAL_DB_ERROR, -EIO, "STATUS_INTERNAL_DB_ERROR"}, - {STATUS_FULLSCREEN_MODE, -EIO, "STATUS_FULLSCREEN_MODE"}, - {STATUS_TOO_MANY_CONTEXT_IDS, -EIO, "STATUS_TOO_MANY_CONTEXT_IDS"}, - {STATUS_LOGON_TYPE_NOT_GRANTED, -EIO, "STATUS_LOGON_TYPE_NOT_GRANTED"}, - {STATUS_NOT_REGISTRY_FILE, -EIO, "STATUS_NOT_REGISTRY_FILE"}, - {STATUS_NT_CROSS_ENCRYPTION_REQUIRED, -EIO, - "STATUS_NT_CROSS_ENCRYPTION_REQUIRED"}, - {STATUS_DOMAIN_CTRLR_CONFIG_ERROR, -EIO, - "STATUS_DOMAIN_CTRLR_CONFIG_ERROR"}, - {STATUS_FT_MISSING_MEMBER, -EIO, "STATUS_FT_MISSING_MEMBER"}, - {STATUS_ILL_FORMED_SERVICE_ENTRY, -EIO, - "STATUS_ILL_FORMED_SERVICE_ENTRY"}, - {STATUS_ILLEGAL_CHARACTER, -EIO, "STATUS_ILLEGAL_CHARACTER"}, - {STATUS_UNMAPPABLE_CHARACTER, -EIO, "STATUS_UNMAPPABLE_CHARACTER"}, - {STATUS_UNDEFINED_CHARACTER, -EIO, "STATUS_UNDEFINED_CHARACTER"}, - {STATUS_FLOPPY_VOLUME, -EIO, "STATUS_FLOPPY_VOLUME"}, - {STATUS_FLOPPY_ID_MARK_NOT_FOUND, -EIO, - "STATUS_FLOPPY_ID_MARK_NOT_FOUND"}, - {STATUS_FLOPPY_WRONG_CYLINDER, -EIO, "STATUS_FLOPPY_WRONG_CYLINDER"}, - {STATUS_FLOPPY_UNKNOWN_ERROR, -EIO, "STATUS_FLOPPY_UNKNOWN_ERROR"}, - {STATUS_FLOPPY_BAD_REGISTERS, -EIO, "STATUS_FLOPPY_BAD_REGISTERS"}, - {STATUS_DISK_RECALIBRATE_FAILED, -EIO, - "STATUS_DISK_RECALIBRATE_FAILED"}, - {STATUS_DISK_OPERATION_FAILED, -EIO, "STATUS_DISK_OPERATION_FAILED"}, - {STATUS_DISK_RESET_FAILED, -EIO, "STATUS_DISK_RESET_FAILED"}, - {STATUS_SHARED_IRQ_BUSY, -EBUSY, "STATUS_SHARED_IRQ_BUSY"}, - {STATUS_FT_ORPHANING, -EIO, "STATUS_FT_ORPHANING"}, - {STATUS_BIOS_FAILED_TO_CONNECT_INTERRUPT, -EIO, - "STATUS_BIOS_FAILED_TO_CONNECT_INTERRUPT"}, - {STATUS_PARTITION_FAILURE, -EIO, "STATUS_PARTITION_FAILURE"}, - {STATUS_INVALID_BLOCK_LENGTH, -EIO, "STATUS_INVALID_BLOCK_LENGTH"}, - {STATUS_DEVICE_NOT_PARTITIONED, -EIO, "STATUS_DEVICE_NOT_PARTITIONED"}, - {STATUS_UNABLE_TO_LOCK_MEDIA, -EIO, "STATUS_UNABLE_TO_LOCK_MEDIA"}, - {STATUS_UNABLE_TO_UNLOAD_MEDIA, -EIO, "STATUS_UNABLE_TO_UNLOAD_MEDIA"}, - {STATUS_EOM_OVERFLOW, -EIO, "STATUS_EOM_OVERFLOW"}, - {STATUS_NO_MEDIA, -EIO, "STATUS_NO_MEDIA"}, - {STATUS_NO_SUCH_MEMBER, -EIO, "STATUS_NO_SUCH_MEMBER"}, - {STATUS_INVALID_MEMBER, -EIO, "STATUS_INVALID_MEMBER"}, - {STATUS_KEY_DELETED, -EIO, "STATUS_KEY_DELETED"}, - {STATUS_NO_LOG_SPACE, -EIO, "STATUS_NO_LOG_SPACE"}, - {STATUS_TOO_MANY_SIDS, -EIO, "STATUS_TOO_MANY_SIDS"}, - {STATUS_LM_CROSS_ENCRYPTION_REQUIRED, -EIO, - "STATUS_LM_CROSS_ENCRYPTION_REQUIRED"}, - {STATUS_KEY_HAS_CHILDREN, -EIO, "STATUS_KEY_HAS_CHILDREN"}, - {STATUS_CHILD_MUST_BE_VOLATILE, -EIO, "STATUS_CHILD_MUST_BE_VOLATILE"}, - {STATUS_DEVICE_CONFIGURATION_ERROR, -EIO, - "STATUS_DEVICE_CONFIGURATION_ERROR"}, - {STATUS_DRIVER_INTERNAL_ERROR, -EIO, "STATUS_DRIVER_INTERNAL_ERROR"}, - {STATUS_INVALID_DEVICE_STATE, -EIO, "STATUS_INVALID_DEVICE_STATE"}, - {STATUS_IO_DEVICE_ERROR, -EIO, "STATUS_IO_DEVICE_ERROR"}, - {STATUS_DEVICE_PROTOCOL_ERROR, -EIO, "STATUS_DEVICE_PROTOCOL_ERROR"}, - {STATUS_BACKUP_CONTROLLER, -EIO, "STATUS_BACKUP_CONTROLLER"}, - {STATUS_LOG_FILE_FULL, -EIO, "STATUS_LOG_FILE_FULL"}, - {STATUS_TOO_LATE, -EIO, "STATUS_TOO_LATE"}, - {STATUS_NO_TRUST_LSA_SECRET, -EIO, "STATUS_NO_TRUST_LSA_SECRET"}, - {STATUS_NO_TRUST_SAM_ACCOUNT, -EIO, "STATUS_NO_TRUST_SAM_ACCOUNT"}, - {STATUS_TRUSTED_DOMAIN_FAILURE, -EIO, "STATUS_TRUSTED_DOMAIN_FAILURE"}, - {STATUS_TRUSTED_RELATIONSHIP_FAILURE, -EIO, - "STATUS_TRUSTED_RELATIONSHIP_FAILURE"}, - {STATUS_EVENTLOG_FILE_CORRUPT, -EIO, "STATUS_EVENTLOG_FILE_CORRUPT"}, - {STATUS_EVENTLOG_CANT_START, -EIO, "STATUS_EVENTLOG_CANT_START"}, - {STATUS_TRUST_FAILURE, -EIO, "STATUS_TRUST_FAILURE"}, - {STATUS_MUTANT_LIMIT_EXCEEDED, -EIO, "STATUS_MUTANT_LIMIT_EXCEEDED"}, - {STATUS_NETLOGON_NOT_STARTED, -EIO, "STATUS_NETLOGON_NOT_STARTED"}, - {STATUS_ACCOUNT_EXPIRED, -EKEYEXPIRED, "STATUS_ACCOUNT_EXPIRED"}, - {STATUS_POSSIBLE_DEADLOCK, -EIO, "STATUS_POSSIBLE_DEADLOCK"}, - {STATUS_NETWORK_CREDENTIAL_CONFLICT, -EIO, - "STATUS_NETWORK_CREDENTIAL_CONFLICT"}, - {STATUS_REMOTE_SESSION_LIMIT, -EIO, "STATUS_REMOTE_SESSION_LIMIT"}, - {STATUS_EVENTLOG_FILE_CHANGED, -EIO, "STATUS_EVENTLOG_FILE_CHANGED"}, - {STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT, -EIO, - "STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT"}, - {STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT, -EIO, - "STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT"}, - {STATUS_NOLOGON_SERVER_TRUST_ACCOUNT, -EIO, - "STATUS_NOLOGON_SERVER_TRUST_ACCOUNT"}, - {STATUS_DOMAIN_TRUST_INCONSISTENT, -EIO, - "STATUS_DOMAIN_TRUST_INCONSISTENT"}, - {STATUS_FS_DRIVER_REQUIRED, -EOPNOTSUPP, "STATUS_FS_DRIVER_REQUIRED"}, - {STATUS_IMAGE_ALREADY_LOADED_AS_DLL, -EIO, - "STATUS_IMAGE_ALREADY_LOADED_AS_DLL"}, - {STATUS_INVALID_LOCK_RANGE, -EIO, "STATUS_INVALID_LOCK_RANGE"}, - {STATUS_NETWORK_OPEN_RESTRICTION, -EIO, - "STATUS_NETWORK_OPEN_RESTRICTION"}, - {STATUS_NO_USER_SESSION_KEY, -EIO, "STATUS_NO_USER_SESSION_KEY"}, - {STATUS_USER_SESSION_DELETED, -EIO, "STATUS_USER_SESSION_DELETED"}, - {STATUS_RESOURCE_LANG_NOT_FOUND, -EIO, - "STATUS_RESOURCE_LANG_NOT_FOUND"}, - {STATUS_INSUFF_SERVER_RESOURCES, -EIO, - "STATUS_INSUFF_SERVER_RESOURCES"}, - {STATUS_INVALID_BUFFER_SIZE, -EIO, "STATUS_INVALID_BUFFER_SIZE"}, - {STATUS_INVALID_ADDRESS_COMPONENT, -EIO, - "STATUS_INVALID_ADDRESS_COMPONENT"}, - {STATUS_INVALID_ADDRESS_WILDCARD, -EIO, - "STATUS_INVALID_ADDRESS_WILDCARD"}, - {STATUS_TOO_MANY_ADDRESSES, -EIO, "STATUS_TOO_MANY_ADDRESSES"}, - {STATUS_ADDRESS_ALREADY_EXISTS, -EADDRINUSE, - "STATUS_ADDRESS_ALREADY_EXISTS"}, - {STATUS_ADDRESS_CLOSED, -EIO, "STATUS_ADDRESS_CLOSED"}, - {STATUS_CONNECTION_DISCONNECTED, -ECONNABORTED, - "STATUS_CONNECTION_DISCONNECTED"}, - {STATUS_CONNECTION_RESET, -ENETRESET, "STATUS_CONNECTION_RESET"}, - {STATUS_TOO_MANY_NODES, -EIO, "STATUS_TOO_MANY_NODES"}, - {STATUS_TRANSACTION_ABORTED, -EIO, "STATUS_TRANSACTION_ABORTED"}, - {STATUS_TRANSACTION_TIMED_OUT, -EIO, "STATUS_TRANSACTION_TIMED_OUT"}, - {STATUS_TRANSACTION_NO_RELEASE, -EIO, "STATUS_TRANSACTION_NO_RELEASE"}, - {STATUS_TRANSACTION_NO_MATCH, -EIO, "STATUS_TRANSACTION_NO_MATCH"}, - {STATUS_TRANSACTION_RESPONDED, -EIO, "STATUS_TRANSACTION_RESPONDED"}, - {STATUS_TRANSACTION_INVALID_ID, -EIO, "STATUS_TRANSACTION_INVALID_ID"}, - {STATUS_TRANSACTION_INVALID_TYPE, -EIO, - "STATUS_TRANSACTION_INVALID_TYPE"}, - {STATUS_NOT_SERVER_SESSION, -EIO, "STATUS_NOT_SERVER_SESSION"}, - {STATUS_NOT_CLIENT_SESSION, -EIO, "STATUS_NOT_CLIENT_SESSION"}, - {STATUS_CANNOT_LOAD_REGISTRY_FILE, -EIO, - "STATUS_CANNOT_LOAD_REGISTRY_FILE"}, - {STATUS_DEBUG_ATTACH_FAILED, -EIO, "STATUS_DEBUG_ATTACH_FAILED"}, - {STATUS_SYSTEM_PROCESS_TERMINATED, -EIO, - "STATUS_SYSTEM_PROCESS_TERMINATED"}, - {STATUS_DATA_NOT_ACCEPTED, -EIO, "STATUS_DATA_NOT_ACCEPTED"}, - {STATUS_NO_BROWSER_SERVERS_FOUND, -EIO, - "STATUS_NO_BROWSER_SERVERS_FOUND"}, - {STATUS_VDM_HARD_ERROR, -EIO, "STATUS_VDM_HARD_ERROR"}, - {STATUS_DRIVER_CANCEL_TIMEOUT, -EIO, "STATUS_DRIVER_CANCEL_TIMEOUT"}, - {STATUS_REPLY_MESSAGE_MISMATCH, -EIO, "STATUS_REPLY_MESSAGE_MISMATCH"}, - {STATUS_MAPPED_ALIGNMENT, -EIO, "STATUS_MAPPED_ALIGNMENT"}, - {STATUS_IMAGE_CHECKSUM_MISMATCH, -EIO, - "STATUS_IMAGE_CHECKSUM_MISMATCH"}, - {STATUS_LOST_WRITEBEHIND_DATA, -EIO, "STATUS_LOST_WRITEBEHIND_DATA"}, - {STATUS_CLIENT_SERVER_PARAMETERS_INVALID, -EIO, - "STATUS_CLIENT_SERVER_PARAMETERS_INVALID"}, - {STATUS_PASSWORD_MUST_CHANGE, -EIO, "STATUS_PASSWORD_MUST_CHANGE"}, - {STATUS_NOT_FOUND, -ENOENT, "STATUS_NOT_FOUND"}, - {STATUS_NOT_TINY_STREAM, -EIO, "STATUS_NOT_TINY_STREAM"}, - {STATUS_RECOVERY_FAILURE, -EIO, "STATUS_RECOVERY_FAILURE"}, - {STATUS_STACK_OVERFLOW_READ, -EIO, "STATUS_STACK_OVERFLOW_READ"}, - {STATUS_FAIL_CHECK, -EIO, "STATUS_FAIL_CHECK"}, - {STATUS_DUPLICATE_OBJECTID, -EIO, "STATUS_DUPLICATE_OBJECTID"}, - {STATUS_OBJECTID_EXISTS, -EIO, "STATUS_OBJECTID_EXISTS"}, - {STATUS_CONVERT_TO_LARGE, -EIO, "STATUS_CONVERT_TO_LARGE"}, - {STATUS_RETRY, -EAGAIN, "STATUS_RETRY"}, - {STATUS_FOUND_OUT_OF_SCOPE, -EIO, "STATUS_FOUND_OUT_OF_SCOPE"}, - {STATUS_ALLOCATE_BUCKET, -EIO, "STATUS_ALLOCATE_BUCKET"}, - {STATUS_PROPSET_NOT_FOUND, -EIO, "STATUS_PROPSET_NOT_FOUND"}, - {STATUS_MARSHALL_OVERFLOW, -EIO, "STATUS_MARSHALL_OVERFLOW"}, - {STATUS_INVALID_VARIANT, -EIO, "STATUS_INVALID_VARIANT"}, - {STATUS_DOMAIN_CONTROLLER_NOT_FOUND, -EIO, - "STATUS_DOMAIN_CONTROLLER_NOT_FOUND"}, - {STATUS_ACCOUNT_LOCKED_OUT, -EACCES, "STATUS_ACCOUNT_LOCKED_OUT"}, - {STATUS_HANDLE_NOT_CLOSABLE, -EIO, "STATUS_HANDLE_NOT_CLOSABLE"}, - {STATUS_CONNECTION_REFUSED, -EIO, "STATUS_CONNECTION_REFUSED"}, - {STATUS_GRACEFUL_DISCONNECT, -EIO, "STATUS_GRACEFUL_DISCONNECT"}, - {STATUS_ADDRESS_ALREADY_ASSOCIATED, -EIO, - "STATUS_ADDRESS_ALREADY_ASSOCIATED"}, - {STATUS_ADDRESS_NOT_ASSOCIATED, -EIO, "STATUS_ADDRESS_NOT_ASSOCIATED"}, - {STATUS_CONNECTION_INVALID, -EIO, "STATUS_CONNECTION_INVALID"}, - {STATUS_CONNECTION_ACTIVE, -EIO, "STATUS_CONNECTION_ACTIVE"}, - {STATUS_NETWORK_UNREACHABLE, -ENETUNREACH, - "STATUS_NETWORK_UNREACHABLE"}, - {STATUS_HOST_UNREACHABLE, -EHOSTDOWN, "STATUS_HOST_UNREACHABLE"}, - {STATUS_PROTOCOL_UNREACHABLE, -ENETUNREACH, - "STATUS_PROTOCOL_UNREACHABLE"}, - {STATUS_PORT_UNREACHABLE, -ENETUNREACH, "STATUS_PORT_UNREACHABLE"}, - {STATUS_REQUEST_ABORTED, -EIO, "STATUS_REQUEST_ABORTED"}, - {STATUS_CONNECTION_ABORTED, -ECONNABORTED, "STATUS_CONNECTION_ABORTED"}, - {STATUS_BAD_COMPRESSION_BUFFER, -EIO, "STATUS_BAD_COMPRESSION_BUFFER"}, - {STATUS_USER_MAPPED_FILE, -EIO, "STATUS_USER_MAPPED_FILE"}, - {STATUS_AUDIT_FAILED, -EIO, "STATUS_AUDIT_FAILED"}, - {STATUS_TIMER_RESOLUTION_NOT_SET, -EIO, - "STATUS_TIMER_RESOLUTION_NOT_SET"}, - {STATUS_CONNECTION_COUNT_LIMIT, -EIO, "STATUS_CONNECTION_COUNT_LIMIT"}, - {STATUS_LOGIN_TIME_RESTRICTION, -EACCES, - "STATUS_LOGIN_TIME_RESTRICTION"}, - {STATUS_LOGIN_WKSTA_RESTRICTION, -EACCES, - "STATUS_LOGIN_WKSTA_RESTRICTION"}, - {STATUS_IMAGE_MP_UP_MISMATCH, -EIO, "STATUS_IMAGE_MP_UP_MISMATCH"}, - {STATUS_INSUFFICIENT_LOGON_INFO, -EIO, - "STATUS_INSUFFICIENT_LOGON_INFO"}, - {STATUS_BAD_DLL_ENTRYPOINT, -EIO, "STATUS_BAD_DLL_ENTRYPOINT"}, - {STATUS_BAD_SERVICE_ENTRYPOINT, -EIO, "STATUS_BAD_SERVICE_ENTRYPOINT"}, - {STATUS_LPC_REPLY_LOST, -EIO, "STATUS_LPC_REPLY_LOST"}, - {STATUS_IP_ADDRESS_CONFLICT1, -EIO, "STATUS_IP_ADDRESS_CONFLICT1"}, - {STATUS_IP_ADDRESS_CONFLICT2, -EIO, "STATUS_IP_ADDRESS_CONFLICT2"}, - {STATUS_REGISTRY_QUOTA_LIMIT, -EDQUOT, "STATUS_REGISTRY_QUOTA_LIMIT"}, - {STATUS_PATH_NOT_COVERED, -EREMOTE, "STATUS_PATH_NOT_COVERED"}, - {STATUS_NO_CALLBACK_ACTIVE, -EIO, "STATUS_NO_CALLBACK_ACTIVE"}, - {STATUS_LICENSE_QUOTA_EXCEEDED, -EACCES, - "STATUS_LICENSE_QUOTA_EXCEEDED"}, - {STATUS_PWD_TOO_SHORT, -EIO, "STATUS_PWD_TOO_SHORT"}, - {STATUS_PWD_TOO_RECENT, -EIO, "STATUS_PWD_TOO_RECENT"}, - {STATUS_PWD_HISTORY_CONFLICT, -EIO, "STATUS_PWD_HISTORY_CONFLICT"}, - {STATUS_PLUGPLAY_NO_DEVICE, -EIO, "STATUS_PLUGPLAY_NO_DEVICE"}, - {STATUS_UNSUPPORTED_COMPRESSION, -EIO, - "STATUS_UNSUPPORTED_COMPRESSION"}, - {STATUS_INVALID_HW_PROFILE, -EIO, "STATUS_INVALID_HW_PROFILE"}, - {STATUS_INVALID_PLUGPLAY_DEVICE_PATH, -EIO, - "STATUS_INVALID_PLUGPLAY_DEVICE_PATH"}, - {STATUS_DRIVER_ORDINAL_NOT_FOUND, -EIO, - "STATUS_DRIVER_ORDINAL_NOT_FOUND"}, - {STATUS_DRIVER_ENTRYPOINT_NOT_FOUND, -EIO, - "STATUS_DRIVER_ENTRYPOINT_NOT_FOUND"}, - {STATUS_RESOURCE_NOT_OWNED, -EIO, "STATUS_RESOURCE_NOT_OWNED"}, - {STATUS_TOO_MANY_LINKS, -EMLINK, "STATUS_TOO_MANY_LINKS"}, - {STATUS_QUOTA_LIST_INCONSISTENT, -EIO, - "STATUS_QUOTA_LIST_INCONSISTENT"}, - {STATUS_FILE_IS_OFFLINE, -EIO, "STATUS_FILE_IS_OFFLINE"}, - {STATUS_EVALUATION_EXPIRATION, -EIO, "STATUS_EVALUATION_EXPIRATION"}, - {STATUS_ILLEGAL_DLL_RELOCATION, -EIO, "STATUS_ILLEGAL_DLL_RELOCATION"}, - {STATUS_LICENSE_VIOLATION, -EIO, "STATUS_LICENSE_VIOLATION"}, - {STATUS_DLL_INIT_FAILED_LOGOFF, -EIO, "STATUS_DLL_INIT_FAILED_LOGOFF"}, - {STATUS_DRIVER_UNABLE_TO_LOAD, -EIO, "STATUS_DRIVER_UNABLE_TO_LOAD"}, - {STATUS_DFS_UNAVAILABLE, -EIO, "STATUS_DFS_UNAVAILABLE"}, - {STATUS_VOLUME_DISMOUNTED, -EIO, "STATUS_VOLUME_DISMOUNTED"}, - {STATUS_WX86_INTERNAL_ERROR, -EIO, "STATUS_WX86_INTERNAL_ERROR"}, - {STATUS_WX86_FLOAT_STACK_CHECK, -EIO, "STATUS_WX86_FLOAT_STACK_CHECK"}, - {STATUS_VALIDATE_CONTINUE, -EIO, "STATUS_VALIDATE_CONTINUE"}, - {STATUS_NO_MATCH, -EIO, "STATUS_NO_MATCH"}, - {STATUS_NO_MORE_MATCHES, -EIO, "STATUS_NO_MORE_MATCHES"}, - {STATUS_NOT_A_REPARSE_POINT, -ENODATA, "STATUS_NOT_A_REPARSE_POINT"}, - {STATUS_IO_REPARSE_TAG_INVALID, -EIO, "STATUS_IO_REPARSE_TAG_INVALID"}, - {STATUS_IO_REPARSE_TAG_MISMATCH, -EIO, - "STATUS_IO_REPARSE_TAG_MISMATCH"}, - {STATUS_IO_REPARSE_DATA_INVALID, -EIO, - "STATUS_IO_REPARSE_DATA_INVALID"}, - {STATUS_REPARSE_POINT_NOT_RESOLVED, -EIO, - "STATUS_REPARSE_POINT_NOT_RESOLVED"}, - {STATUS_DIRECTORY_IS_A_REPARSE_POINT, -EIO, - "STATUS_DIRECTORY_IS_A_REPARSE_POINT"}, - {STATUS_RANGE_LIST_CONFLICT, -EIO, "STATUS_RANGE_LIST_CONFLICT"}, - {STATUS_SOURCE_ELEMENT_EMPTY, -EIO, "STATUS_SOURCE_ELEMENT_EMPTY"}, - {STATUS_DESTINATION_ELEMENT_FULL, -EIO, - "STATUS_DESTINATION_ELEMENT_FULL"}, - {STATUS_ILLEGAL_ELEMENT_ADDRESS, -EIO, - "STATUS_ILLEGAL_ELEMENT_ADDRESS"}, - {STATUS_MAGAZINE_NOT_PRESENT, -EIO, "STATUS_MAGAZINE_NOT_PRESENT"}, - {STATUS_REINITIALIZATION_NEEDED, -EIO, - "STATUS_REINITIALIZATION_NEEDED"}, - {STATUS_ENCRYPTION_FAILED, -EIO, "STATUS_ENCRYPTION_FAILED"}, - {STATUS_DECRYPTION_FAILED, -EIO, "STATUS_DECRYPTION_FAILED"}, - {STATUS_RANGE_NOT_FOUND, -EIO, "STATUS_RANGE_NOT_FOUND"}, - {STATUS_NO_RECOVERY_POLICY, -EIO, "STATUS_NO_RECOVERY_POLICY"}, - {STATUS_NO_EFS, -EIO, "STATUS_NO_EFS"}, - {STATUS_WRONG_EFS, -EIO, "STATUS_WRONG_EFS"}, - {STATUS_NO_USER_KEYS, -EIO, "STATUS_NO_USER_KEYS"}, - {STATUS_FILE_NOT_ENCRYPTED, -EIO, "STATUS_FILE_NOT_ENCRYPTED"}, - {STATUS_NOT_EXPORT_FORMAT, -EIO, "STATUS_NOT_EXPORT_FORMAT"}, - {STATUS_FILE_ENCRYPTED, -EIO, "STATUS_FILE_ENCRYPTED"}, - {STATUS_WMI_GUID_NOT_FOUND, -EIO, "STATUS_WMI_GUID_NOT_FOUND"}, - {STATUS_WMI_INSTANCE_NOT_FOUND, -EIO, "STATUS_WMI_INSTANCE_NOT_FOUND"}, - {STATUS_WMI_ITEMID_NOT_FOUND, -EIO, "STATUS_WMI_ITEMID_NOT_FOUND"}, - {STATUS_WMI_TRY_AGAIN, -EIO, "STATUS_WMI_TRY_AGAIN"}, - {STATUS_SHARED_POLICY, -EIO, "STATUS_SHARED_POLICY"}, - {STATUS_POLICY_OBJECT_NOT_FOUND, -EIO, - "STATUS_POLICY_OBJECT_NOT_FOUND"}, - {STATUS_POLICY_ONLY_IN_DS, -EIO, "STATUS_POLICY_ONLY_IN_DS"}, - {STATUS_VOLUME_NOT_UPGRADED, -EIO, "STATUS_VOLUME_NOT_UPGRADED"}, - {STATUS_REMOTE_STORAGE_NOT_ACTIVE, -EIO, - "STATUS_REMOTE_STORAGE_NOT_ACTIVE"}, - {STATUS_REMOTE_STORAGE_MEDIA_ERROR, -EIO, - "STATUS_REMOTE_STORAGE_MEDIA_ERROR"}, - {STATUS_NO_TRACKING_SERVICE, -EIO, "STATUS_NO_TRACKING_SERVICE"}, - {STATUS_SERVER_SID_MISMATCH, -EIO, "STATUS_SERVER_SID_MISMATCH"}, - {STATUS_DS_NO_ATTRIBUTE_OR_VALUE, -EIO, - "STATUS_DS_NO_ATTRIBUTE_OR_VALUE"}, - {STATUS_DS_INVALID_ATTRIBUTE_SYNTAX, -EIO, - "STATUS_DS_INVALID_ATTRIBUTE_SYNTAX"}, - {STATUS_DS_ATTRIBUTE_TYPE_UNDEFINED, -EIO, - "STATUS_DS_ATTRIBUTE_TYPE_UNDEFINED"}, - {STATUS_DS_ATTRIBUTE_OR_VALUE_EXISTS, -EIO, - "STATUS_DS_ATTRIBUTE_OR_VALUE_EXISTS"}, - {STATUS_DS_BUSY, -EBUSY, "STATUS_DS_BUSY"}, - {STATUS_DS_UNAVAILABLE, -EIO, "STATUS_DS_UNAVAILABLE"}, - {STATUS_DS_NO_RIDS_ALLOCATED, -EIO, "STATUS_DS_NO_RIDS_ALLOCATED"}, - {STATUS_DS_NO_MORE_RIDS, -EIO, "STATUS_DS_NO_MORE_RIDS"}, - {STATUS_DS_INCORRECT_ROLE_OWNER, -EIO, - "STATUS_DS_INCORRECT_ROLE_OWNER"}, - {STATUS_DS_RIDMGR_INIT_ERROR, -EIO, "STATUS_DS_RIDMGR_INIT_ERROR"}, - {STATUS_DS_OBJ_CLASS_VIOLATION, -EIO, "STATUS_DS_OBJ_CLASS_VIOLATION"}, - {STATUS_DS_CANT_ON_NON_LEAF, -EIO, "STATUS_DS_CANT_ON_NON_LEAF"}, - {STATUS_DS_CANT_ON_RDN, -EIO, "STATUS_DS_CANT_ON_RDN"}, - {STATUS_DS_CANT_MOD_OBJ_CLASS, -EIO, "STATUS_DS_CANT_MOD_OBJ_CLASS"}, - {STATUS_DS_CROSS_DOM_MOVE_FAILED, -EIO, - "STATUS_DS_CROSS_DOM_MOVE_FAILED"}, - {STATUS_DS_GC_NOT_AVAILABLE, -EIO, "STATUS_DS_GC_NOT_AVAILABLE"}, - {STATUS_DIRECTORY_SERVICE_REQUIRED, -EIO, - "STATUS_DIRECTORY_SERVICE_REQUIRED"}, - {STATUS_REPARSE_ATTRIBUTE_CONFLICT, -EIO, - "STATUS_REPARSE_ATTRIBUTE_CONFLICT"}, - {STATUS_CANT_ENABLE_DENY_ONLY, -EIO, "STATUS_CANT_ENABLE_DENY_ONLY"}, - {STATUS_FLOAT_MULTIPLE_FAULTS, -EIO, "STATUS_FLOAT_MULTIPLE_FAULTS"}, - {STATUS_FLOAT_MULTIPLE_TRAPS, -EIO, "STATUS_FLOAT_MULTIPLE_TRAPS"}, - {STATUS_DEVICE_REMOVED, -EIO, "STATUS_DEVICE_REMOVED"}, - {STATUS_JOURNAL_DELETE_IN_PROGRESS, -EIO, - "STATUS_JOURNAL_DELETE_IN_PROGRESS"}, - {STATUS_JOURNAL_NOT_ACTIVE, -EIO, "STATUS_JOURNAL_NOT_ACTIVE"}, - {STATUS_NOINTERFACE, -EIO, "STATUS_NOINTERFACE"}, - {STATUS_DS_ADMIN_LIMIT_EXCEEDED, -EIO, - "STATUS_DS_ADMIN_LIMIT_EXCEEDED"}, - {STATUS_DRIVER_FAILED_SLEEP, -EIO, "STATUS_DRIVER_FAILED_SLEEP"}, - {STATUS_MUTUAL_AUTHENTICATION_FAILED, -EIO, - "STATUS_MUTUAL_AUTHENTICATION_FAILED"}, - {STATUS_CORRUPT_SYSTEM_FILE, -EIO, "STATUS_CORRUPT_SYSTEM_FILE"}, - {STATUS_DATATYPE_MISALIGNMENT_ERROR, -EIO, - "STATUS_DATATYPE_MISALIGNMENT_ERROR"}, - {STATUS_WMI_READ_ONLY, -EROFS, "STATUS_WMI_READ_ONLY"}, - {STATUS_WMI_SET_FAILURE, -EIO, "STATUS_WMI_SET_FAILURE"}, - {STATUS_COMMITMENT_MINIMUM, -EIO, "STATUS_COMMITMENT_MINIMUM"}, - {STATUS_REG_NAT_CONSUMPTION, -EIO, "STATUS_REG_NAT_CONSUMPTION"}, - {STATUS_TRANSPORT_FULL, -EIO, "STATUS_TRANSPORT_FULL"}, - {STATUS_DS_SAM_INIT_FAILURE, -EIO, "STATUS_DS_SAM_INIT_FAILURE"}, - {STATUS_ONLY_IF_CONNECTED, -EIO, "STATUS_ONLY_IF_CONNECTED"}, - {STATUS_DS_SENSITIVE_GROUP_VIOLATION, -EIO, - "STATUS_DS_SENSITIVE_GROUP_VIOLATION"}, - {STATUS_PNP_RESTART_ENUMERATION, -EIO, - "STATUS_PNP_RESTART_ENUMERATION"}, - {STATUS_JOURNAL_ENTRY_DELETED, -EIO, "STATUS_JOURNAL_ENTRY_DELETED"}, - {STATUS_DS_CANT_MOD_PRIMARYGROUPID, -EIO, - "STATUS_DS_CANT_MOD_PRIMARYGROUPID"}, - {STATUS_SYSTEM_IMAGE_BAD_SIGNATURE, -EIO, - "STATUS_SYSTEM_IMAGE_BAD_SIGNATURE"}, - {STATUS_PNP_REBOOT_REQUIRED, -EIO, "STATUS_PNP_REBOOT_REQUIRED"}, - {STATUS_POWER_STATE_INVALID, -EIO, "STATUS_POWER_STATE_INVALID"}, - {STATUS_DS_INVALID_GROUP_TYPE, -EIO, "STATUS_DS_INVALID_GROUP_TYPE"}, - {STATUS_DS_NO_NEST_GLOBALGROUP_IN_MIXEDDOMAIN, -EIO, - "STATUS_DS_NO_NEST_GLOBALGROUP_IN_MIXEDDOMAIN"}, - {STATUS_DS_NO_NEST_LOCALGROUP_IN_MIXEDDOMAIN, -EIO, - "STATUS_DS_NO_NEST_LOCALGROUP_IN_MIXEDDOMAIN"}, - {STATUS_DS_GLOBAL_CANT_HAVE_LOCAL_MEMBER, -EIO, - "STATUS_DS_GLOBAL_CANT_HAVE_LOCAL_MEMBER"}, - {STATUS_DS_GLOBAL_CANT_HAVE_UNIVERSAL_MEMBER, -EIO, - "STATUS_DS_GLOBAL_CANT_HAVE_UNIVERSAL_MEMBER"}, - {STATUS_DS_UNIVERSAL_CANT_HAVE_LOCAL_MEMBER, -EIO, - "STATUS_DS_UNIVERSAL_CANT_HAVE_LOCAL_MEMBER"}, - {STATUS_DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER, -EIO, - "STATUS_DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER"}, - {STATUS_DS_LOCAL_CANT_HAVE_CROSSDOMAIN_LOCAL_MEMBER, -EIO, - "STATUS_DS_LOCAL_CANT_HAVE_CROSSDOMAIN_LOCAL_MEMBER"}, - {STATUS_DS_HAVE_PRIMARY_MEMBERS, -EIO, - "STATUS_DS_HAVE_PRIMARY_MEMBERS"}, - {STATUS_WMI_NOT_SUPPORTED, -EOPNOTSUPP, "STATUS_WMI_NOT_SUPPORTED"}, - {STATUS_INSUFFICIENT_POWER, -EIO, "STATUS_INSUFFICIENT_POWER"}, - {STATUS_SAM_NEED_BOOTKEY_PASSWORD, -EIO, - "STATUS_SAM_NEED_BOOTKEY_PASSWORD"}, - {STATUS_SAM_NEED_BOOTKEY_FLOPPY, -EIO, - "STATUS_SAM_NEED_BOOTKEY_FLOPPY"}, - {STATUS_DS_CANT_START, -EIO, "STATUS_DS_CANT_START"}, - {STATUS_DS_INIT_FAILURE, -EIO, "STATUS_DS_INIT_FAILURE"}, - {STATUS_SAM_INIT_FAILURE, -EIO, "STATUS_SAM_INIT_FAILURE"}, - {STATUS_DS_GC_REQUIRED, -EIO, "STATUS_DS_GC_REQUIRED"}, - {STATUS_DS_LOCAL_MEMBER_OF_LOCAL_ONLY, -EIO, - "STATUS_DS_LOCAL_MEMBER_OF_LOCAL_ONLY"}, - {STATUS_DS_NO_FPO_IN_UNIVERSAL_GROUPS, -EIO, - "STATUS_DS_NO_FPO_IN_UNIVERSAL_GROUPS"}, - {STATUS_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED, -EDQUOT, - "STATUS_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED"}, - {STATUS_MULTIPLE_FAULT_VIOLATION, -EIO, - "STATUS_MULTIPLE_FAULT_VIOLATION"}, - {STATUS_CURRENT_DOMAIN_NOT_ALLOWED, -EIO, - "STATUS_CURRENT_DOMAIN_NOT_ALLOWED"}, - {STATUS_CANNOT_MAKE, -EIO, "STATUS_CANNOT_MAKE"}, - {STATUS_SYSTEM_SHUTDOWN, -EIO, "STATUS_SYSTEM_SHUTDOWN"}, - {STATUS_DS_INIT_FAILURE_CONSOLE, -EIO, - "STATUS_DS_INIT_FAILURE_CONSOLE"}, - {STATUS_DS_SAM_INIT_FAILURE_CONSOLE, -EIO, - "STATUS_DS_SAM_INIT_FAILURE_CONSOLE"}, - {STATUS_UNFINISHED_CONTEXT_DELETED, -EIO, - "STATUS_UNFINISHED_CONTEXT_DELETED"}, - {STATUS_NO_TGT_REPLY, -EIO, "STATUS_NO_TGT_REPLY"}, - /* Note that ENOATTTR and ENODATA are the same errno */ - {STATUS_OBJECTID_NOT_FOUND, -ENODATA, "STATUS_OBJECTID_NOT_FOUND"}, - {STATUS_NO_IP_ADDRESSES, -EIO, "STATUS_NO_IP_ADDRESSES"}, - {STATUS_WRONG_CREDENTIAL_HANDLE, -EIO, - "STATUS_WRONG_CREDENTIAL_HANDLE"}, - {STATUS_CRYPTO_SYSTEM_INVALID, -EIO, "STATUS_CRYPTO_SYSTEM_INVALID"}, - {STATUS_MAX_REFERRALS_EXCEEDED, -EIO, "STATUS_MAX_REFERRALS_EXCEEDED"}, - {STATUS_MUST_BE_KDC, -EIO, "STATUS_MUST_BE_KDC"}, - {STATUS_STRONG_CRYPTO_NOT_SUPPORTED, -EIO, - "STATUS_STRONG_CRYPTO_NOT_SUPPORTED"}, - {STATUS_TOO_MANY_PRINCIPALS, -EIO, "STATUS_TOO_MANY_PRINCIPALS"}, - {STATUS_NO_PA_DATA, -EIO, "STATUS_NO_PA_DATA"}, - {STATUS_PKINIT_NAME_MISMATCH, -EIO, "STATUS_PKINIT_NAME_MISMATCH"}, - {STATUS_SMARTCARD_LOGON_REQUIRED, -EIO, - "STATUS_SMARTCARD_LOGON_REQUIRED"}, - {STATUS_KDC_INVALID_REQUEST, -EIO, "STATUS_KDC_INVALID_REQUEST"}, - {STATUS_KDC_UNABLE_TO_REFER, -EIO, "STATUS_KDC_UNABLE_TO_REFER"}, - {STATUS_KDC_UNKNOWN_ETYPE, -EIO, "STATUS_KDC_UNKNOWN_ETYPE"}, - {STATUS_SHUTDOWN_IN_PROGRESS, -EIO, "STATUS_SHUTDOWN_IN_PROGRESS"}, - {STATUS_SERVER_SHUTDOWN_IN_PROGRESS, -EIO, - "STATUS_SERVER_SHUTDOWN_IN_PROGRESS"}, - {STATUS_NOT_SUPPORTED_ON_SBS, -EOPNOTSUPP, - "STATUS_NOT_SUPPORTED_ON_SBS"}, - {STATUS_WMI_GUID_DISCONNECTED, -EIO, "STATUS_WMI_GUID_DISCONNECTED"}, - {STATUS_WMI_ALREADY_DISABLED, -EIO, "STATUS_WMI_ALREADY_DISABLED"}, - {STATUS_WMI_ALREADY_ENABLED, -EIO, "STATUS_WMI_ALREADY_ENABLED"}, - {STATUS_MFT_TOO_FRAGMENTED, -EIO, "STATUS_MFT_TOO_FRAGMENTED"}, - {STATUS_COPY_PROTECTION_FAILURE, -EIO, - "STATUS_COPY_PROTECTION_FAILURE"}, - {STATUS_CSS_AUTHENTICATION_FAILURE, -EIO, - "STATUS_CSS_AUTHENTICATION_FAILURE"}, - {STATUS_CSS_KEY_NOT_PRESENT, -EIO, "STATUS_CSS_KEY_NOT_PRESENT"}, - {STATUS_CSS_KEY_NOT_ESTABLISHED, -EIO, - "STATUS_CSS_KEY_NOT_ESTABLISHED"}, - {STATUS_CSS_SCRAMBLED_SECTOR, -EIO, "STATUS_CSS_SCRAMBLED_SECTOR"}, - {STATUS_CSS_REGION_MISMATCH, -EIO, "STATUS_CSS_REGION_MISMATCH"}, - {STATUS_CSS_RESETS_EXHAUSTED, -EIO, "STATUS_CSS_RESETS_EXHAUSTED"}, - {STATUS_PKINIT_FAILURE, -EIO, "STATUS_PKINIT_FAILURE"}, - {STATUS_SMARTCARD_SUBSYSTEM_FAILURE, -EIO, - "STATUS_SMARTCARD_SUBSYSTEM_FAILURE"}, - {STATUS_NO_KERB_KEY, -EIO, "STATUS_NO_KERB_KEY"}, - {STATUS_HOST_DOWN, -EIO, "STATUS_HOST_DOWN"}, - {STATUS_UNSUPPORTED_PREAUTH, -EIO, "STATUS_UNSUPPORTED_PREAUTH"}, - {STATUS_EFS_ALG_BLOB_TOO_BIG, -EIO, "STATUS_EFS_ALG_BLOB_TOO_BIG"}, - {STATUS_PORT_NOT_SET, -EIO, "STATUS_PORT_NOT_SET"}, - {STATUS_DEBUGGER_INACTIVE, -EIO, "STATUS_DEBUGGER_INACTIVE"}, - {STATUS_DS_VERSION_CHECK_FAILURE, -EIO, - "STATUS_DS_VERSION_CHECK_FAILURE"}, - {STATUS_AUDITING_DISABLED, -EIO, "STATUS_AUDITING_DISABLED"}, - {STATUS_PRENT4_MACHINE_ACCOUNT, -EIO, "STATUS_PRENT4_MACHINE_ACCOUNT"}, - {STATUS_DS_AG_CANT_HAVE_UNIVERSAL_MEMBER, -EIO, - "STATUS_DS_AG_CANT_HAVE_UNIVERSAL_MEMBER"}, - {STATUS_INVALID_IMAGE_WIN_32, -EIO, "STATUS_INVALID_IMAGE_WIN_32"}, - {STATUS_INVALID_IMAGE_WIN_64, -EIO, "STATUS_INVALID_IMAGE_WIN_64"}, - {STATUS_BAD_BINDINGS, -EIO, "STATUS_BAD_BINDINGS"}, - {STATUS_NETWORK_SESSION_EXPIRED, -EIO, - "STATUS_NETWORK_SESSION_EXPIRED"}, - {STATUS_APPHELP_BLOCK, -EIO, "STATUS_APPHELP_BLOCK"}, - {STATUS_ALL_SIDS_FILTERED, -EIO, "STATUS_ALL_SIDS_FILTERED"}, - {STATUS_NOT_SAFE_MODE_DRIVER, -EIO, "STATUS_NOT_SAFE_MODE_DRIVER"}, - {STATUS_ACCESS_DISABLED_BY_POLICY_DEFAULT, -EACCES, - "STATUS_ACCESS_DISABLED_BY_POLICY_DEFAULT"}, - {STATUS_ACCESS_DISABLED_BY_POLICY_PATH, -EACCES, - "STATUS_ACCESS_DISABLED_BY_POLICY_PATH"}, - {STATUS_ACCESS_DISABLED_BY_POLICY_PUBLISHER, -EACCES, - "STATUS_ACCESS_DISABLED_BY_POLICY_PUBLISHER"}, - {STATUS_ACCESS_DISABLED_BY_POLICY_OTHER, -EACCES, - "STATUS_ACCESS_DISABLED_BY_POLICY_OTHER"}, - {STATUS_FAILED_DRIVER_ENTRY, -EIO, "STATUS_FAILED_DRIVER_ENTRY"}, - {STATUS_DEVICE_ENUMERATION_ERROR, -EIO, - "STATUS_DEVICE_ENUMERATION_ERROR"}, - {STATUS_MOUNT_POINT_NOT_RESOLVED, -EIO, - "STATUS_MOUNT_POINT_NOT_RESOLVED"}, - {STATUS_INVALID_DEVICE_OBJECT_PARAMETER, -EIO, - "STATUS_INVALID_DEVICE_OBJECT_PARAMETER"}, - {STATUS_MCA_OCCURED, -EIO, "STATUS_MCA_OCCURED"}, - {STATUS_DRIVER_BLOCKED_CRITICAL, -EIO, - "STATUS_DRIVER_BLOCKED_CRITICAL"}, - {STATUS_DRIVER_BLOCKED, -EIO, "STATUS_DRIVER_BLOCKED"}, - {STATUS_DRIVER_DATABASE_ERROR, -EIO, "STATUS_DRIVER_DATABASE_ERROR"}, - {STATUS_SYSTEM_HIVE_TOO_LARGE, -EIO, "STATUS_SYSTEM_HIVE_TOO_LARGE"}, - {STATUS_INVALID_IMPORT_OF_NON_DLL, -EIO, - "STATUS_INVALID_IMPORT_OF_NON_DLL"}, - {STATUS_NO_SECRETS, -EIO, "STATUS_NO_SECRETS"}, - {STATUS_ACCESS_DISABLED_NO_SAFER_UI_BY_POLICY, -EACCES, - "STATUS_ACCESS_DISABLED_NO_SAFER_UI_BY_POLICY"}, - {STATUS_FAILED_STACK_SWITCH, -EIO, "STATUS_FAILED_STACK_SWITCH"}, - {STATUS_HEAP_CORRUPTION, -EIO, "STATUS_HEAP_CORRUPTION"}, - {STATUS_SMARTCARD_WRONG_PIN, -EIO, "STATUS_SMARTCARD_WRONG_PIN"}, - {STATUS_SMARTCARD_CARD_BLOCKED, -EIO, "STATUS_SMARTCARD_CARD_BLOCKED"}, - {STATUS_SMARTCARD_CARD_NOT_AUTHENTICATED, -EIO, - "STATUS_SMARTCARD_CARD_NOT_AUTHENTICATED"}, - {STATUS_SMARTCARD_NO_CARD, -EIO, "STATUS_SMARTCARD_NO_CARD"}, - {STATUS_SMARTCARD_NO_KEY_CONTAINER, -EIO, - "STATUS_SMARTCARD_NO_KEY_CONTAINER"}, - {STATUS_SMARTCARD_NO_CERTIFICATE, -EIO, - "STATUS_SMARTCARD_NO_CERTIFICATE"}, - {STATUS_SMARTCARD_NO_KEYSET, -EIO, "STATUS_SMARTCARD_NO_KEYSET"}, - {STATUS_SMARTCARD_IO_ERROR, -EIO, "STATUS_SMARTCARD_IO_ERROR"}, - {STATUS_DOWNGRADE_DETECTED, -EIO, "STATUS_DOWNGRADE_DETECTED"}, - {STATUS_SMARTCARD_CERT_REVOKED, -EIO, "STATUS_SMARTCARD_CERT_REVOKED"}, - {STATUS_ISSUING_CA_UNTRUSTED, -EIO, "STATUS_ISSUING_CA_UNTRUSTED"}, - {STATUS_REVOCATION_OFFLINE_C, -EIO, "STATUS_REVOCATION_OFFLINE_C"}, - {STATUS_PKINIT_CLIENT_FAILURE, -EIO, "STATUS_PKINIT_CLIENT_FAILURE"}, - {STATUS_SMARTCARD_CERT_EXPIRED, -EIO, "STATUS_SMARTCARD_CERT_EXPIRED"}, - {STATUS_DRIVER_FAILED_PRIOR_UNLOAD, -EIO, - "STATUS_DRIVER_FAILED_PRIOR_UNLOAD"}, - {STATUS_SMARTCARD_SILENT_CONTEXT, -EIO, - "STATUS_SMARTCARD_SILENT_CONTEXT"}, - {STATUS_PER_USER_TRUST_QUOTA_EXCEEDED, -EDQUOT, - "STATUS_PER_USER_TRUST_QUOTA_EXCEEDED"}, - {STATUS_ALL_USER_TRUST_QUOTA_EXCEEDED, -EDQUOT, - "STATUS_ALL_USER_TRUST_QUOTA_EXCEEDED"}, - {STATUS_USER_DELETE_TRUST_QUOTA_EXCEEDED, -EDQUOT, - "STATUS_USER_DELETE_TRUST_QUOTA_EXCEEDED"}, - {STATUS_DS_NAME_NOT_UNIQUE, -EIO, "STATUS_DS_NAME_NOT_UNIQUE"}, - {STATUS_DS_DUPLICATE_ID_FOUND, -EIO, "STATUS_DS_DUPLICATE_ID_FOUND"}, - {STATUS_DS_GROUP_CONVERSION_ERROR, -EIO, - "STATUS_DS_GROUP_CONVERSION_ERROR"}, - {STATUS_VOLSNAP_PREPARE_HIBERNATE, -EIO, - "STATUS_VOLSNAP_PREPARE_HIBERNATE"}, - {STATUS_USER2USER_REQUIRED, -EIO, "STATUS_USER2USER_REQUIRED"}, - {STATUS_STACK_BUFFER_OVERRUN, -EIO, "STATUS_STACK_BUFFER_OVERRUN"}, - {STATUS_NO_S4U_PROT_SUPPORT, -EIO, "STATUS_NO_S4U_PROT_SUPPORT"}, - {STATUS_CROSSREALM_DELEGATION_FAILURE, -EIO, - "STATUS_CROSSREALM_DELEGATION_FAILURE"}, - {STATUS_REVOCATION_OFFLINE_KDC, -EIO, "STATUS_REVOCATION_OFFLINE_KDC"}, - {STATUS_ISSUING_CA_UNTRUSTED_KDC, -EIO, - "STATUS_ISSUING_CA_UNTRUSTED_KDC"}, - {STATUS_KDC_CERT_EXPIRED, -EIO, "STATUS_KDC_CERT_EXPIRED"}, - {STATUS_KDC_CERT_REVOKED, -EIO, "STATUS_KDC_CERT_REVOKED"}, - {STATUS_PARAMETER_QUOTA_EXCEEDED, -EDQUOT, - "STATUS_PARAMETER_QUOTA_EXCEEDED"}, - {STATUS_HIBERNATION_FAILURE, -EIO, "STATUS_HIBERNATION_FAILURE"}, - {STATUS_DELAY_LOAD_FAILED, -EIO, "STATUS_DELAY_LOAD_FAILED"}, - {STATUS_AUTHENTICATION_FIREWALL_FAILED, -EIO, - "STATUS_AUTHENTICATION_FIREWALL_FAILED"}, - {STATUS_VDM_DISALLOWED, -EIO, "STATUS_VDM_DISALLOWED"}, - {STATUS_HUNG_DISPLAY_DRIVER_THREAD, -EIO, - "STATUS_HUNG_DISPLAY_DRIVER_THREAD"}, - {STATUS_INSUFFICIENT_RESOURCE_FOR_SPECIFIED_SHARED_SECTION_SIZE, -EIO, - "STATUS_INSUFFICIENT_RESOURCE_FOR_SPECIFIED_SHARED_SECTION_SIZE"}, - {STATUS_INVALID_CRUNTIME_PARAMETER, -EIO, - "STATUS_INVALID_CRUNTIME_PARAMETER"}, - {STATUS_NTLM_BLOCKED, -EIO, "STATUS_NTLM_BLOCKED"}, - {STATUS_ASSERTION_FAILURE, -EIO, "STATUS_ASSERTION_FAILURE"}, - {STATUS_VERIFIER_STOP, -EIO, "STATUS_VERIFIER_STOP"}, - {STATUS_CALLBACK_POP_STACK, -EIO, "STATUS_CALLBACK_POP_STACK"}, - {STATUS_INCOMPATIBLE_DRIVER_BLOCKED, -EIO, - "STATUS_INCOMPATIBLE_DRIVER_BLOCKED"}, - {STATUS_HIVE_UNLOADED, -EIO, "STATUS_HIVE_UNLOADED"}, - {STATUS_COMPRESSION_DISABLED, -EIO, "STATUS_COMPRESSION_DISABLED"}, - {STATUS_FILE_SYSTEM_LIMITATION, -EIO, "STATUS_FILE_SYSTEM_LIMITATION"}, - {STATUS_INVALID_IMAGE_HASH, -EIO, "STATUS_INVALID_IMAGE_HASH"}, - {STATUS_NOT_CAPABLE, -EIO, "STATUS_NOT_CAPABLE"}, - {STATUS_REQUEST_OUT_OF_SEQUENCE, -EIO, - "STATUS_REQUEST_OUT_OF_SEQUENCE"}, - {STATUS_IMPLEMENTATION_LIMIT, -EIO, "STATUS_IMPLEMENTATION_LIMIT"}, - {STATUS_ELEVATION_REQUIRED, -EIO, "STATUS_ELEVATION_REQUIRED"}, - {STATUS_BEYOND_VDL, -EIO, "STATUS_BEYOND_VDL"}, - {STATUS_ENCOUNTERED_WRITE_IN_PROGRESS, -EIO, - "STATUS_ENCOUNTERED_WRITE_IN_PROGRESS"}, - {STATUS_PTE_CHANGED, -EIO, "STATUS_PTE_CHANGED"}, - {STATUS_PURGE_FAILED, -EIO, "STATUS_PURGE_FAILED"}, - {STATUS_CRED_REQUIRES_CONFIRMATION, -EIO, - "STATUS_CRED_REQUIRES_CONFIRMATION"}, - {STATUS_CS_ENCRYPTION_INVALID_SERVER_RESPONSE, -EIO, - "STATUS_CS_ENCRYPTION_INVALID_SERVER_RESPONSE"}, - {STATUS_CS_ENCRYPTION_UNSUPPORTED_SERVER, -EIO, - "STATUS_CS_ENCRYPTION_UNSUPPORTED_SERVER"}, - {STATUS_CS_ENCRYPTION_EXISTING_ENCRYPTED_FILE, -EIO, - "STATUS_CS_ENCRYPTION_EXISTING_ENCRYPTED_FILE"}, - {STATUS_CS_ENCRYPTION_NEW_ENCRYPTED_FILE, -EIO, - "STATUS_CS_ENCRYPTION_NEW_ENCRYPTED_FILE"}, - {STATUS_CS_ENCRYPTION_FILE_NOT_CSE, -EIO, - "STATUS_CS_ENCRYPTION_FILE_NOT_CSE"}, - {STATUS_INVALID_LABEL, -EIO, "STATUS_INVALID_LABEL"}, - {STATUS_DRIVER_PROCESS_TERMINATED, -EIO, - "STATUS_DRIVER_PROCESS_TERMINATED"}, - {STATUS_AMBIGUOUS_SYSTEM_DEVICE, -EIO, - "STATUS_AMBIGUOUS_SYSTEM_DEVICE"}, - {STATUS_SYSTEM_DEVICE_NOT_FOUND, -EIO, - "STATUS_SYSTEM_DEVICE_NOT_FOUND"}, - {STATUS_RESTART_BOOT_APPLICATION, -EIO, - "STATUS_RESTART_BOOT_APPLICATION"}, - {STATUS_INVALID_TASK_NAME, -EIO, "STATUS_INVALID_TASK_NAME"}, - {STATUS_INVALID_TASK_INDEX, -EIO, "STATUS_INVALID_TASK_INDEX"}, - {STATUS_THREAD_ALREADY_IN_TASK, -EIO, "STATUS_THREAD_ALREADY_IN_TASK"}, - {STATUS_CALLBACK_BYPASS, -EIO, "STATUS_CALLBACK_BYPASS"}, - {STATUS_SERVER_UNAVAILABLE, -EAGAIN, "STATUS_SERVER_UNAVAILABLE"}, - {STATUS_FILE_NOT_AVAILABLE, -EAGAIN, "STATUS_FILE_NOT_AVAILABLE"}, - {STATUS_PORT_CLOSED, -EIO, "STATUS_PORT_CLOSED"}, - {STATUS_MESSAGE_LOST, -EIO, "STATUS_MESSAGE_LOST"}, - {STATUS_INVALID_MESSAGE, -EIO, "STATUS_INVALID_MESSAGE"}, - {STATUS_REQUEST_CANCELED, -EIO, "STATUS_REQUEST_CANCELED"}, - {STATUS_RECURSIVE_DISPATCH, -EIO, "STATUS_RECURSIVE_DISPATCH"}, - {STATUS_LPC_RECEIVE_BUFFER_EXPECTED, -EIO, - "STATUS_LPC_RECEIVE_BUFFER_EXPECTED"}, - {STATUS_LPC_INVALID_CONNECTION_USAGE, -EIO, - "STATUS_LPC_INVALID_CONNECTION_USAGE"}, - {STATUS_LPC_REQUESTS_NOT_ALLOWED, -EIO, - "STATUS_LPC_REQUESTS_NOT_ALLOWED"}, - {STATUS_RESOURCE_IN_USE, -EIO, "STATUS_RESOURCE_IN_USE"}, - {STATUS_HARDWARE_MEMORY_ERROR, -EIO, "STATUS_HARDWARE_MEMORY_ERROR"}, - {STATUS_THREADPOOL_HANDLE_EXCEPTION, -EIO, - "STATUS_THREADPOOL_HANDLE_EXCEPTION"}, - {STATUS_THREADPOOL_SET_EVENT_ON_COMPLETION_FAILED, -EIO, - "STATUS_THREADPOOL_SET_EVENT_ON_COMPLETION_FAILED"}, - {STATUS_THREADPOOL_RELEASE_SEMAPHORE_ON_COMPLETION_FAILED, -EIO, - "STATUS_THREADPOOL_RELEASE_SEMAPHORE_ON_COMPLETION_FAILED"}, - {STATUS_THREADPOOL_RELEASE_MUTEX_ON_COMPLETION_FAILED, -EIO, - "STATUS_THREADPOOL_RELEASE_MUTEX_ON_COMPLETION_FAILED"}, - {STATUS_THREADPOOL_FREE_LIBRARY_ON_COMPLETION_FAILED, -EIO, - "STATUS_THREADPOOL_FREE_LIBRARY_ON_COMPLETION_FAILED"}, - {STATUS_THREADPOOL_RELEASED_DURING_OPERATION, -EIO, - "STATUS_THREADPOOL_RELEASED_DURING_OPERATION"}, - {STATUS_CALLBACK_RETURNED_WHILE_IMPERSONATING, -EIO, - "STATUS_CALLBACK_RETURNED_WHILE_IMPERSONATING"}, - {STATUS_APC_RETURNED_WHILE_IMPERSONATING, -EIO, - "STATUS_APC_RETURNED_WHILE_IMPERSONATING"}, - {STATUS_PROCESS_IS_PROTECTED, -EIO, "STATUS_PROCESS_IS_PROTECTED"}, - {STATUS_MCA_EXCEPTION, -EIO, "STATUS_MCA_EXCEPTION"}, - {STATUS_CERTIFICATE_MAPPING_NOT_UNIQUE, -EIO, - "STATUS_CERTIFICATE_MAPPING_NOT_UNIQUE"}, - {STATUS_SYMLINK_CLASS_DISABLED, -EIO, "STATUS_SYMLINK_CLASS_DISABLED"}, - {STATUS_INVALID_IDN_NORMALIZATION, -EIO, - "STATUS_INVALID_IDN_NORMALIZATION"}, - {STATUS_NO_UNICODE_TRANSLATION, -EIO, "STATUS_NO_UNICODE_TRANSLATION"}, - {STATUS_ALREADY_REGISTERED, -EIO, "STATUS_ALREADY_REGISTERED"}, - {STATUS_CONTEXT_MISMATCH, -EIO, "STATUS_CONTEXT_MISMATCH"}, - {STATUS_PORT_ALREADY_HAS_COMPLETION_LIST, -EIO, - "STATUS_PORT_ALREADY_HAS_COMPLETION_LIST"}, - {STATUS_CALLBACK_RETURNED_THREAD_PRIORITY, -EIO, - "STATUS_CALLBACK_RETURNED_THREAD_PRIORITY"}, - {STATUS_INVALID_THREAD, -EIO, "STATUS_INVALID_THREAD"}, - {STATUS_CALLBACK_RETURNED_TRANSACTION, -EIO, - "STATUS_CALLBACK_RETURNED_TRANSACTION"}, - {STATUS_CALLBACK_RETURNED_LDR_LOCK, -EIO, - "STATUS_CALLBACK_RETURNED_LDR_LOCK"}, - {STATUS_CALLBACK_RETURNED_LANG, -EIO, "STATUS_CALLBACK_RETURNED_LANG"}, - {STATUS_CALLBACK_RETURNED_PRI_BACK, -EIO, - "STATUS_CALLBACK_RETURNED_PRI_BACK"}, - {STATUS_CALLBACK_RETURNED_THREAD_AFFINITY, -EIO, - "STATUS_CALLBACK_RETURNED_THREAD_AFFINITY"}, - {STATUS_DISK_REPAIR_DISABLED, -EIO, "STATUS_DISK_REPAIR_DISABLED"}, - {STATUS_DS_DOMAIN_RENAME_IN_PROGRESS, -EIO, - "STATUS_DS_DOMAIN_RENAME_IN_PROGRESS"}, - {STATUS_DISK_QUOTA_EXCEEDED, -EDQUOT, "STATUS_DISK_QUOTA_EXCEEDED"}, - {STATUS_CONTENT_BLOCKED, -EIO, "STATUS_CONTENT_BLOCKED"}, - {STATUS_BAD_CLUSTERS, -EIO, "STATUS_BAD_CLUSTERS"}, - {STATUS_VOLUME_DIRTY, -EIO, "STATUS_VOLUME_DIRTY"}, - {STATUS_FILE_CHECKED_OUT, -EIO, "STATUS_FILE_CHECKED_OUT"}, - {STATUS_CHECKOUT_REQUIRED, -EIO, "STATUS_CHECKOUT_REQUIRED"}, - {STATUS_BAD_FILE_TYPE, -EIO, "STATUS_BAD_FILE_TYPE"}, - {STATUS_FILE_TOO_LARGE, -EIO, "STATUS_FILE_TOO_LARGE"}, - {STATUS_FORMS_AUTH_REQUIRED, -EIO, "STATUS_FORMS_AUTH_REQUIRED"}, - {STATUS_VIRUS_INFECTED, -EIO, "STATUS_VIRUS_INFECTED"}, - {STATUS_VIRUS_DELETED, -EIO, "STATUS_VIRUS_DELETED"}, - {STATUS_BAD_MCFG_TABLE, -EIO, "STATUS_BAD_MCFG_TABLE"}, - {STATUS_WOW_ASSERTION, -EIO, "STATUS_WOW_ASSERTION"}, - {STATUS_INVALID_SIGNATURE, -EIO, "STATUS_INVALID_SIGNATURE"}, - {STATUS_HMAC_NOT_SUPPORTED, -EIO, "STATUS_HMAC_NOT_SUPPORTED"}, - {STATUS_IPSEC_QUEUE_OVERFLOW, -EIO, "STATUS_IPSEC_QUEUE_OVERFLOW"}, - {STATUS_ND_QUEUE_OVERFLOW, -EIO, "STATUS_ND_QUEUE_OVERFLOW"}, - {STATUS_HOPLIMIT_EXCEEDED, -EIO, "STATUS_HOPLIMIT_EXCEEDED"}, - {STATUS_PROTOCOL_NOT_SUPPORTED, -EOPNOTSUPP, - "STATUS_PROTOCOL_NOT_SUPPORTED"}, - {STATUS_LOST_WRITEBEHIND_DATA_NETWORK_DISCONNECTED, -EIO, - "STATUS_LOST_WRITEBEHIND_DATA_NETWORK_DISCONNECTED"}, - {STATUS_LOST_WRITEBEHIND_DATA_NETWORK_SERVER_ERROR, -EIO, - "STATUS_LOST_WRITEBEHIND_DATA_NETWORK_SERVER_ERROR"}, - {STATUS_LOST_WRITEBEHIND_DATA_LOCAL_DISK_ERROR, -EIO, - "STATUS_LOST_WRITEBEHIND_DATA_LOCAL_DISK_ERROR"}, - {STATUS_XML_PARSE_ERROR, -EIO, "STATUS_XML_PARSE_ERROR"}, - {STATUS_XMLDSIG_ERROR, -EIO, "STATUS_XMLDSIG_ERROR"}, - {STATUS_WRONG_COMPARTMENT, -EIO, "STATUS_WRONG_COMPARTMENT"}, - {STATUS_AUTHIP_FAILURE, -EIO, "STATUS_AUTHIP_FAILURE"}, - {DBG_NO_STATE_CHANGE, -EIO, "DBG_NO_STATE_CHANGE"}, - {DBG_APP_NOT_IDLE, -EIO, "DBG_APP_NOT_IDLE"}, - {RPC_NT_INVALID_STRING_BINDING, -EIO, "RPC_NT_INVALID_STRING_BINDING"}, - {RPC_NT_WRONG_KIND_OF_BINDING, -EIO, "RPC_NT_WRONG_KIND_OF_BINDING"}, - {RPC_NT_INVALID_BINDING, -EIO, "RPC_NT_INVALID_BINDING"}, - {RPC_NT_PROTSEQ_NOT_SUPPORTED, -EOPNOTSUPP, - "RPC_NT_PROTSEQ_NOT_SUPPORTED"}, - {RPC_NT_INVALID_RPC_PROTSEQ, -EIO, "RPC_NT_INVALID_RPC_PROTSEQ"}, - {RPC_NT_INVALID_STRING_UUID, -EIO, "RPC_NT_INVALID_STRING_UUID"}, - {RPC_NT_INVALID_ENDPOINT_FORMAT, -EIO, - "RPC_NT_INVALID_ENDPOINT_FORMAT"}, - {RPC_NT_INVALID_NET_ADDR, -EIO, "RPC_NT_INVALID_NET_ADDR"}, - {RPC_NT_NO_ENDPOINT_FOUND, -EIO, "RPC_NT_NO_ENDPOINT_FOUND"}, - {RPC_NT_INVALID_TIMEOUT, -EINVAL, "RPC_NT_INVALID_TIMEOUT"}, - {RPC_NT_OBJECT_NOT_FOUND, -ENOENT, "RPC_NT_OBJECT_NOT_FOUND"}, - {RPC_NT_ALREADY_REGISTERED, -EIO, "RPC_NT_ALREADY_REGISTERED"}, - {RPC_NT_TYPE_ALREADY_REGISTERED, -EIO, - "RPC_NT_TYPE_ALREADY_REGISTERED"}, - {RPC_NT_ALREADY_LISTENING, -EIO, "RPC_NT_ALREADY_LISTENING"}, - {RPC_NT_NO_PROTSEQS_REGISTERED, -EIO, "RPC_NT_NO_PROTSEQS_REGISTERED"}, - {RPC_NT_NOT_LISTENING, -EIO, "RPC_NT_NOT_LISTENING"}, - {RPC_NT_UNKNOWN_MGR_TYPE, -EIO, "RPC_NT_UNKNOWN_MGR_TYPE"}, - {RPC_NT_UNKNOWN_IF, -EIO, "RPC_NT_UNKNOWN_IF"}, - {RPC_NT_NO_BINDINGS, -EIO, "RPC_NT_NO_BINDINGS"}, - {RPC_NT_NO_PROTSEQS, -EIO, "RPC_NT_NO_PROTSEQS"}, - {RPC_NT_CANT_CREATE_ENDPOINT, -EIO, "RPC_NT_CANT_CREATE_ENDPOINT"}, - {RPC_NT_OUT_OF_RESOURCES, -EIO, "RPC_NT_OUT_OF_RESOURCES"}, - {RPC_NT_SERVER_UNAVAILABLE, -EIO, "RPC_NT_SERVER_UNAVAILABLE"}, - {RPC_NT_SERVER_TOO_BUSY, -EBUSY, "RPC_NT_SERVER_TOO_BUSY"}, - {RPC_NT_INVALID_NETWORK_OPTIONS, -EIO, - "RPC_NT_INVALID_NETWORK_OPTIONS"}, - {RPC_NT_NO_CALL_ACTIVE, -EIO, "RPC_NT_NO_CALL_ACTIVE"}, - {RPC_NT_CALL_FAILED, -EIO, "RPC_NT_CALL_FAILED"}, - {RPC_NT_CALL_FAILED_DNE, -EIO, "RPC_NT_CALL_FAILED_DNE"}, - {RPC_NT_PROTOCOL_ERROR, -EIO, "RPC_NT_PROTOCOL_ERROR"}, - {RPC_NT_UNSUPPORTED_TRANS_SYN, -EIO, "RPC_NT_UNSUPPORTED_TRANS_SYN"}, - {RPC_NT_UNSUPPORTED_TYPE, -EIO, "RPC_NT_UNSUPPORTED_TYPE"}, - {RPC_NT_INVALID_TAG, -EIO, "RPC_NT_INVALID_TAG"}, - {RPC_NT_INVALID_BOUND, -EIO, "RPC_NT_INVALID_BOUND"}, - {RPC_NT_NO_ENTRY_NAME, -EIO, "RPC_NT_NO_ENTRY_NAME"}, - {RPC_NT_INVALID_NAME_SYNTAX, -EIO, "RPC_NT_INVALID_NAME_SYNTAX"}, - {RPC_NT_UNSUPPORTED_NAME_SYNTAX, -EIO, - "RPC_NT_UNSUPPORTED_NAME_SYNTAX"}, - {RPC_NT_UUID_NO_ADDRESS, -EIO, "RPC_NT_UUID_NO_ADDRESS"}, - {RPC_NT_DUPLICATE_ENDPOINT, -ENOTUNIQ, "RPC_NT_DUPLICATE_ENDPOINT"}, - {RPC_NT_UNKNOWN_AUTHN_TYPE, -EIO, "RPC_NT_UNKNOWN_AUTHN_TYPE"}, - {RPC_NT_MAX_CALLS_TOO_SMALL, -EIO, "RPC_NT_MAX_CALLS_TOO_SMALL"}, - {RPC_NT_STRING_TOO_LONG, -EIO, "RPC_NT_STRING_TOO_LONG"}, - {RPC_NT_PROTSEQ_NOT_FOUND, -EIO, "RPC_NT_PROTSEQ_NOT_FOUND"}, - {RPC_NT_PROCNUM_OUT_OF_RANGE, -EIO, "RPC_NT_PROCNUM_OUT_OF_RANGE"}, - {RPC_NT_BINDING_HAS_NO_AUTH, -EIO, "RPC_NT_BINDING_HAS_NO_AUTH"}, - {RPC_NT_UNKNOWN_AUTHN_SERVICE, -EIO, "RPC_NT_UNKNOWN_AUTHN_SERVICE"}, - {RPC_NT_UNKNOWN_AUTHN_LEVEL, -EIO, "RPC_NT_UNKNOWN_AUTHN_LEVEL"}, - {RPC_NT_INVALID_AUTH_IDENTITY, -EIO, "RPC_NT_INVALID_AUTH_IDENTITY"}, - {RPC_NT_UNKNOWN_AUTHZ_SERVICE, -EIO, "RPC_NT_UNKNOWN_AUTHZ_SERVICE"}, - {EPT_NT_INVALID_ENTRY, -EIO, "EPT_NT_INVALID_ENTRY"}, - {EPT_NT_CANT_PERFORM_OP, -EIO, "EPT_NT_CANT_PERFORM_OP"}, - {EPT_NT_NOT_REGISTERED, -EIO, "EPT_NT_NOT_REGISTERED"}, - {RPC_NT_NOTHING_TO_EXPORT, -EIO, "RPC_NT_NOTHING_TO_EXPORT"}, - {RPC_NT_INCOMPLETE_NAME, -EIO, "RPC_NT_INCOMPLETE_NAME"}, - {RPC_NT_INVALID_VERS_OPTION, -EIO, "RPC_NT_INVALID_VERS_OPTION"}, - {RPC_NT_NO_MORE_MEMBERS, -EIO, "RPC_NT_NO_MORE_MEMBERS"}, - {RPC_NT_NOT_ALL_OBJS_UNEXPORTED, -EIO, - "RPC_NT_NOT_ALL_OBJS_UNEXPORTED"}, - {RPC_NT_INTERFACE_NOT_FOUND, -EIO, "RPC_NT_INTERFACE_NOT_FOUND"}, - {RPC_NT_ENTRY_ALREADY_EXISTS, -EIO, "RPC_NT_ENTRY_ALREADY_EXISTS"}, - {RPC_NT_ENTRY_NOT_FOUND, -EIO, "RPC_NT_ENTRY_NOT_FOUND"}, - {RPC_NT_NAME_SERVICE_UNAVAILABLE, -EIO, - "RPC_NT_NAME_SERVICE_UNAVAILABLE"}, - {RPC_NT_INVALID_NAF_ID, -EIO, "RPC_NT_INVALID_NAF_ID"}, - {RPC_NT_CANNOT_SUPPORT, -EOPNOTSUPP, "RPC_NT_CANNOT_SUPPORT"}, - {RPC_NT_NO_CONTEXT_AVAILABLE, -EIO, "RPC_NT_NO_CONTEXT_AVAILABLE"}, - {RPC_NT_INTERNAL_ERROR, -EIO, "RPC_NT_INTERNAL_ERROR"}, - {RPC_NT_ZERO_DIVIDE, -EIO, "RPC_NT_ZERO_DIVIDE"}, - {RPC_NT_ADDRESS_ERROR, -EIO, "RPC_NT_ADDRESS_ERROR"}, - {RPC_NT_FP_DIV_ZERO, -EIO, "RPC_NT_FP_DIV_ZERO"}, - {RPC_NT_FP_UNDERFLOW, -EIO, "RPC_NT_FP_UNDERFLOW"}, - {RPC_NT_FP_OVERFLOW, -EIO, "RPC_NT_FP_OVERFLOW"}, - {RPC_NT_CALL_IN_PROGRESS, -EIO, "RPC_NT_CALL_IN_PROGRESS"}, - {RPC_NT_NO_MORE_BINDINGS, -EIO, "RPC_NT_NO_MORE_BINDINGS"}, - {RPC_NT_GROUP_MEMBER_NOT_FOUND, -EIO, "RPC_NT_GROUP_MEMBER_NOT_FOUND"}, - {EPT_NT_CANT_CREATE, -EIO, "EPT_NT_CANT_CREATE"}, - {RPC_NT_INVALID_OBJECT, -EIO, "RPC_NT_INVALID_OBJECT"}, - {RPC_NT_NO_INTERFACES, -EIO, "RPC_NT_NO_INTERFACES"}, - {RPC_NT_CALL_CANCELLED, -EIO, "RPC_NT_CALL_CANCELLED"}, - {RPC_NT_BINDING_INCOMPLETE, -EIO, "RPC_NT_BINDING_INCOMPLETE"}, - {RPC_NT_COMM_FAILURE, -EIO, "RPC_NT_COMM_FAILURE"}, - {RPC_NT_UNSUPPORTED_AUTHN_LEVEL, -EIO, - "RPC_NT_UNSUPPORTED_AUTHN_LEVEL"}, - {RPC_NT_NO_PRINC_NAME, -EIO, "RPC_NT_NO_PRINC_NAME"}, - {RPC_NT_NOT_RPC_ERROR, -EIO, "RPC_NT_NOT_RPC_ERROR"}, - {RPC_NT_SEC_PKG_ERROR, -EIO, "RPC_NT_SEC_PKG_ERROR"}, - {RPC_NT_NOT_CANCELLED, -EIO, "RPC_NT_NOT_CANCELLED"}, - {RPC_NT_INVALID_ASYNC_HANDLE, -EIO, "RPC_NT_INVALID_ASYNC_HANDLE"}, - {RPC_NT_INVALID_ASYNC_CALL, -EIO, "RPC_NT_INVALID_ASYNC_CALL"}, - {RPC_NT_PROXY_ACCESS_DENIED, -EACCES, "RPC_NT_PROXY_ACCESS_DENIED"}, - {RPC_NT_NO_MORE_ENTRIES, -EIO, "RPC_NT_NO_MORE_ENTRIES"}, - {RPC_NT_SS_CHAR_TRANS_OPEN_FAIL, -EIO, - "RPC_NT_SS_CHAR_TRANS_OPEN_FAIL"}, - {RPC_NT_SS_CHAR_TRANS_SHORT_FILE, -EIO, - "RPC_NT_SS_CHAR_TRANS_SHORT_FILE"}, - {RPC_NT_SS_IN_NULL_CONTEXT, -EIO, "RPC_NT_SS_IN_NULL_CONTEXT"}, - {RPC_NT_SS_CONTEXT_MISMATCH, -EIO, "RPC_NT_SS_CONTEXT_MISMATCH"}, - {RPC_NT_SS_CONTEXT_DAMAGED, -EIO, "RPC_NT_SS_CONTEXT_DAMAGED"}, - {RPC_NT_SS_HANDLES_MISMATCH, -EIO, "RPC_NT_SS_HANDLES_MISMATCH"}, - {RPC_NT_SS_CANNOT_GET_CALL_HANDLE, -EIO, - "RPC_NT_SS_CANNOT_GET_CALL_HANDLE"}, - {RPC_NT_NULL_REF_POINTER, -EIO, "RPC_NT_NULL_REF_POINTER"}, - {RPC_NT_ENUM_VALUE_OUT_OF_RANGE, -EIO, - "RPC_NT_ENUM_VALUE_OUT_OF_RANGE"}, - {RPC_NT_BYTE_COUNT_TOO_SMALL, -EIO, "RPC_NT_BYTE_COUNT_TOO_SMALL"}, - {RPC_NT_BAD_STUB_DATA, -EIO, "RPC_NT_BAD_STUB_DATA"}, - {RPC_NT_INVALID_ES_ACTION, -EIO, "RPC_NT_INVALID_ES_ACTION"}, - {RPC_NT_WRONG_ES_VERSION, -EIO, "RPC_NT_WRONG_ES_VERSION"}, - {RPC_NT_WRONG_STUB_VERSION, -EIO, "RPC_NT_WRONG_STUB_VERSION"}, - {RPC_NT_INVALID_PIPE_OBJECT, -EIO, "RPC_NT_INVALID_PIPE_OBJECT"}, - {RPC_NT_INVALID_PIPE_OPERATION, -EIO, "RPC_NT_INVALID_PIPE_OPERATION"}, - {RPC_NT_WRONG_PIPE_VERSION, -EIO, "RPC_NT_WRONG_PIPE_VERSION"}, - {RPC_NT_PIPE_CLOSED, -EIO, "RPC_NT_PIPE_CLOSED"}, - {RPC_NT_PIPE_DISCIPLINE_ERROR, -EIO, "RPC_NT_PIPE_DISCIPLINE_ERROR"}, - {RPC_NT_PIPE_EMPTY, -EIO, "RPC_NT_PIPE_EMPTY"}, - {STATUS_PNP_BAD_MPS_TABLE, -EIO, "STATUS_PNP_BAD_MPS_TABLE"}, - {STATUS_PNP_TRANSLATION_FAILED, -EIO, "STATUS_PNP_TRANSLATION_FAILED"}, - {STATUS_PNP_IRQ_TRANSLATION_FAILED, -EIO, - "STATUS_PNP_IRQ_TRANSLATION_FAILED"}, - {STATUS_PNP_INVALID_ID, -EIO, "STATUS_PNP_INVALID_ID"}, - {STATUS_IO_REISSUE_AS_CACHED, -EIO, "STATUS_IO_REISSUE_AS_CACHED"}, - {STATUS_CTX_WINSTATION_NAME_INVALID, -EIO, - "STATUS_CTX_WINSTATION_NAME_INVALID"}, - {STATUS_CTX_INVALID_PD, -EIO, "STATUS_CTX_INVALID_PD"}, - {STATUS_CTX_PD_NOT_FOUND, -EIO, "STATUS_CTX_PD_NOT_FOUND"}, - {STATUS_CTX_CLOSE_PENDING, -EIO, "STATUS_CTX_CLOSE_PENDING"}, - {STATUS_CTX_NO_OUTBUF, -EIO, "STATUS_CTX_NO_OUTBUF"}, - {STATUS_CTX_MODEM_INF_NOT_FOUND, -EIO, - "STATUS_CTX_MODEM_INF_NOT_FOUND"}, - {STATUS_CTX_INVALID_MODEMNAME, -EIO, "STATUS_CTX_INVALID_MODEMNAME"}, - {STATUS_CTX_RESPONSE_ERROR, -EIO, "STATUS_CTX_RESPONSE_ERROR"}, - {STATUS_CTX_MODEM_RESPONSE_TIMEOUT, -ETIMEDOUT, - "STATUS_CTX_MODEM_RESPONSE_TIMEOUT"}, - {STATUS_CTX_MODEM_RESPONSE_NO_CARRIER, -EIO, - "STATUS_CTX_MODEM_RESPONSE_NO_CARRIER"}, - {STATUS_CTX_MODEM_RESPONSE_NO_DIALTONE, -EIO, - "STATUS_CTX_MODEM_RESPONSE_NO_DIALTONE"}, - {STATUS_CTX_MODEM_RESPONSE_BUSY, -EBUSY, - "STATUS_CTX_MODEM_RESPONSE_BUSY"}, - {STATUS_CTX_MODEM_RESPONSE_VOICE, -EIO, - "STATUS_CTX_MODEM_RESPONSE_VOICE"}, - {STATUS_CTX_TD_ERROR, -EIO, "STATUS_CTX_TD_ERROR"}, - {STATUS_CTX_LICENSE_CLIENT_INVALID, -EIO, - "STATUS_CTX_LICENSE_CLIENT_INVALID"}, - {STATUS_CTX_LICENSE_NOT_AVAILABLE, -EIO, - "STATUS_CTX_LICENSE_NOT_AVAILABLE"}, - {STATUS_CTX_LICENSE_EXPIRED, -EIO, "STATUS_CTX_LICENSE_EXPIRED"}, - {STATUS_CTX_WINSTATION_NOT_FOUND, -EIO, - "STATUS_CTX_WINSTATION_NOT_FOUND"}, - {STATUS_CTX_WINSTATION_NAME_COLLISION, -EIO, - "STATUS_CTX_WINSTATION_NAME_COLLISION"}, - {STATUS_CTX_WINSTATION_BUSY, -EBUSY, "STATUS_CTX_WINSTATION_BUSY"}, - {STATUS_CTX_BAD_VIDEO_MODE, -EIO, "STATUS_CTX_BAD_VIDEO_MODE"}, - {STATUS_CTX_GRAPHICS_INVALID, -EIO, "STATUS_CTX_GRAPHICS_INVALID"}, - {STATUS_CTX_NOT_CONSOLE, -EIO, "STATUS_CTX_NOT_CONSOLE"}, - {STATUS_CTX_CLIENT_QUERY_TIMEOUT, -EIO, - "STATUS_CTX_CLIENT_QUERY_TIMEOUT"}, - {STATUS_CTX_CONSOLE_DISCONNECT, -EIO, "STATUS_CTX_CONSOLE_DISCONNECT"}, - {STATUS_CTX_CONSOLE_CONNECT, -EIO, "STATUS_CTX_CONSOLE_CONNECT"}, - {STATUS_CTX_SHADOW_DENIED, -EIO, "STATUS_CTX_SHADOW_DENIED"}, - {STATUS_CTX_WINSTATION_ACCESS_DENIED, -EACCES, - "STATUS_CTX_WINSTATION_ACCESS_DENIED"}, - {STATUS_CTX_INVALID_WD, -EIO, "STATUS_CTX_INVALID_WD"}, - {STATUS_CTX_WD_NOT_FOUND, -EIO, "STATUS_CTX_WD_NOT_FOUND"}, - {STATUS_CTX_SHADOW_INVALID, -EIO, "STATUS_CTX_SHADOW_INVALID"}, - {STATUS_CTX_SHADOW_DISABLED, -EIO, "STATUS_CTX_SHADOW_DISABLED"}, - {STATUS_RDP_PROTOCOL_ERROR, -EIO, "STATUS_RDP_PROTOCOL_ERROR"}, - {STATUS_CTX_CLIENT_LICENSE_NOT_SET, -EIO, - "STATUS_CTX_CLIENT_LICENSE_NOT_SET"}, - {STATUS_CTX_CLIENT_LICENSE_IN_USE, -EIO, - "STATUS_CTX_CLIENT_LICENSE_IN_USE"}, - {STATUS_CTX_SHADOW_ENDED_BY_MODE_CHANGE, -EIO, - "STATUS_CTX_SHADOW_ENDED_BY_MODE_CHANGE"}, - {STATUS_CTX_SHADOW_NOT_RUNNING, -EIO, "STATUS_CTX_SHADOW_NOT_RUNNING"}, - {STATUS_CTX_LOGON_DISABLED, -EIO, "STATUS_CTX_LOGON_DISABLED"}, - {STATUS_CTX_SECURITY_LAYER_ERROR, -EIO, - "STATUS_CTX_SECURITY_LAYER_ERROR"}, - {STATUS_TS_INCOMPATIBLE_SESSIONS, -EIO, - "STATUS_TS_INCOMPATIBLE_SESSIONS"}, - {STATUS_MUI_FILE_NOT_FOUND, -EIO, "STATUS_MUI_FILE_NOT_FOUND"}, - {STATUS_MUI_INVALID_FILE, -EIO, "STATUS_MUI_INVALID_FILE"}, - {STATUS_MUI_INVALID_RC_CONFIG, -EIO, "STATUS_MUI_INVALID_RC_CONFIG"}, - {STATUS_MUI_INVALID_LOCALE_NAME, -EIO, - "STATUS_MUI_INVALID_LOCALE_NAME"}, - {STATUS_MUI_INVALID_ULTIMATEFALLBACK_NAME, -EIO, - "STATUS_MUI_INVALID_ULTIMATEFALLBACK_NAME"}, - {STATUS_MUI_FILE_NOT_LOADED, -EIO, "STATUS_MUI_FILE_NOT_LOADED"}, - {STATUS_RESOURCE_ENUM_USER_STOP, -EIO, - "STATUS_RESOURCE_ENUM_USER_STOP"}, - {STATUS_CLUSTER_INVALID_NODE, -EIO, "STATUS_CLUSTER_INVALID_NODE"}, - {STATUS_CLUSTER_NODE_EXISTS, -EIO, "STATUS_CLUSTER_NODE_EXISTS"}, - {STATUS_CLUSTER_JOIN_IN_PROGRESS, -EIO, - "STATUS_CLUSTER_JOIN_IN_PROGRESS"}, - {STATUS_CLUSTER_NODE_NOT_FOUND, -EIO, "STATUS_CLUSTER_NODE_NOT_FOUND"}, - {STATUS_CLUSTER_LOCAL_NODE_NOT_FOUND, -EIO, - "STATUS_CLUSTER_LOCAL_NODE_NOT_FOUND"}, - {STATUS_CLUSTER_NETWORK_EXISTS, -EIO, "STATUS_CLUSTER_NETWORK_EXISTS"}, - {STATUS_CLUSTER_NETWORK_NOT_FOUND, -EIO, - "STATUS_CLUSTER_NETWORK_NOT_FOUND"}, - {STATUS_CLUSTER_NETINTERFACE_EXISTS, -EIO, - "STATUS_CLUSTER_NETINTERFACE_EXISTS"}, - {STATUS_CLUSTER_NETINTERFACE_NOT_FOUND, -EIO, - "STATUS_CLUSTER_NETINTERFACE_NOT_FOUND"}, - {STATUS_CLUSTER_INVALID_REQUEST, -EIO, - "STATUS_CLUSTER_INVALID_REQUEST"}, - {STATUS_CLUSTER_INVALID_NETWORK_PROVIDER, -EIO, - "STATUS_CLUSTER_INVALID_NETWORK_PROVIDER"}, - {STATUS_CLUSTER_NODE_DOWN, -EIO, "STATUS_CLUSTER_NODE_DOWN"}, - {STATUS_CLUSTER_NODE_UNREACHABLE, -EIO, - "STATUS_CLUSTER_NODE_UNREACHABLE"}, - {STATUS_CLUSTER_NODE_NOT_MEMBER, -EIO, - "STATUS_CLUSTER_NODE_NOT_MEMBER"}, - {STATUS_CLUSTER_JOIN_NOT_IN_PROGRESS, -EIO, - "STATUS_CLUSTER_JOIN_NOT_IN_PROGRESS"}, - {STATUS_CLUSTER_INVALID_NETWORK, -EIO, - "STATUS_CLUSTER_INVALID_NETWORK"}, - {STATUS_CLUSTER_NO_NET_ADAPTERS, -EIO, - "STATUS_CLUSTER_NO_NET_ADAPTERS"}, - {STATUS_CLUSTER_NODE_UP, -EIO, "STATUS_CLUSTER_NODE_UP"}, - {STATUS_CLUSTER_NODE_PAUSED, -EIO, "STATUS_CLUSTER_NODE_PAUSED"}, - {STATUS_CLUSTER_NODE_NOT_PAUSED, -EIO, - "STATUS_CLUSTER_NODE_NOT_PAUSED"}, - {STATUS_CLUSTER_NO_SECURITY_CONTEXT, -EIO, - "STATUS_CLUSTER_NO_SECURITY_CONTEXT"}, - {STATUS_CLUSTER_NETWORK_NOT_INTERNAL, -EIO, - "STATUS_CLUSTER_NETWORK_NOT_INTERNAL"}, - {STATUS_CLUSTER_POISONED, -EIO, "STATUS_CLUSTER_POISONED"}, - {STATUS_ACPI_INVALID_OPCODE, -EIO, "STATUS_ACPI_INVALID_OPCODE"}, - {STATUS_ACPI_STACK_OVERFLOW, -EIO, "STATUS_ACPI_STACK_OVERFLOW"}, - {STATUS_ACPI_ASSERT_FAILED, -EIO, "STATUS_ACPI_ASSERT_FAILED"}, - {STATUS_ACPI_INVALID_INDEX, -EIO, "STATUS_ACPI_INVALID_INDEX"}, - {STATUS_ACPI_INVALID_ARGUMENT, -EIO, "STATUS_ACPI_INVALID_ARGUMENT"}, - {STATUS_ACPI_FATAL, -EIO, "STATUS_ACPI_FATAL"}, - {STATUS_ACPI_INVALID_SUPERNAME, -EIO, "STATUS_ACPI_INVALID_SUPERNAME"}, - {STATUS_ACPI_INVALID_ARGTYPE, -EIO, "STATUS_ACPI_INVALID_ARGTYPE"}, - {STATUS_ACPI_INVALID_OBJTYPE, -EIO, "STATUS_ACPI_INVALID_OBJTYPE"}, - {STATUS_ACPI_INVALID_TARGETTYPE, -EIO, - "STATUS_ACPI_INVALID_TARGETTYPE"}, - {STATUS_ACPI_INCORRECT_ARGUMENT_COUNT, -EIO, - "STATUS_ACPI_INCORRECT_ARGUMENT_COUNT"}, - {STATUS_ACPI_ADDRESS_NOT_MAPPED, -EIO, - "STATUS_ACPI_ADDRESS_NOT_MAPPED"}, - {STATUS_ACPI_INVALID_EVENTTYPE, -EIO, "STATUS_ACPI_INVALID_EVENTTYPE"}, - {STATUS_ACPI_HANDLER_COLLISION, -EIO, "STATUS_ACPI_HANDLER_COLLISION"}, - {STATUS_ACPI_INVALID_DATA, -EIO, "STATUS_ACPI_INVALID_DATA"}, - {STATUS_ACPI_INVALID_REGION, -EIO, "STATUS_ACPI_INVALID_REGION"}, - {STATUS_ACPI_INVALID_ACCESS_SIZE, -EIO, - "STATUS_ACPI_INVALID_ACCESS_SIZE"}, - {STATUS_ACPI_ACQUIRE_GLOBAL_LOCK, -EIO, - "STATUS_ACPI_ACQUIRE_GLOBAL_LOCK"}, - {STATUS_ACPI_ALREADY_INITIALIZED, -EIO, - "STATUS_ACPI_ALREADY_INITIALIZED"}, - {STATUS_ACPI_NOT_INITIALIZED, -EIO, "STATUS_ACPI_NOT_INITIALIZED"}, - {STATUS_ACPI_INVALID_MUTEX_LEVEL, -EIO, - "STATUS_ACPI_INVALID_MUTEX_LEVEL"}, - {STATUS_ACPI_MUTEX_NOT_OWNED, -EIO, "STATUS_ACPI_MUTEX_NOT_OWNED"}, - {STATUS_ACPI_MUTEX_NOT_OWNER, -EIO, "STATUS_ACPI_MUTEX_NOT_OWNER"}, - {STATUS_ACPI_RS_ACCESS, -EIO, "STATUS_ACPI_RS_ACCESS"}, - {STATUS_ACPI_INVALID_TABLE, -EIO, "STATUS_ACPI_INVALID_TABLE"}, - {STATUS_ACPI_REG_HANDLER_FAILED, -EIO, - "STATUS_ACPI_REG_HANDLER_FAILED"}, - {STATUS_ACPI_POWER_REQUEST_FAILED, -EIO, - "STATUS_ACPI_POWER_REQUEST_FAILED"}, - {STATUS_SXS_SECTION_NOT_FOUND, -EIO, "STATUS_SXS_SECTION_NOT_FOUND"}, - {STATUS_SXS_CANT_GEN_ACTCTX, -EIO, "STATUS_SXS_CANT_GEN_ACTCTX"}, - {STATUS_SXS_INVALID_ACTCTXDATA_FORMAT, -EIO, - "STATUS_SXS_INVALID_ACTCTXDATA_FORMAT"}, - {STATUS_SXS_ASSEMBLY_NOT_FOUND, -EIO, "STATUS_SXS_ASSEMBLY_NOT_FOUND"}, - {STATUS_SXS_MANIFEST_FORMAT_ERROR, -EIO, - "STATUS_SXS_MANIFEST_FORMAT_ERROR"}, - {STATUS_SXS_MANIFEST_PARSE_ERROR, -EIO, - "STATUS_SXS_MANIFEST_PARSE_ERROR"}, - {STATUS_SXS_ACTIVATION_CONTEXT_DISABLED, -EIO, - "STATUS_SXS_ACTIVATION_CONTEXT_DISABLED"}, - {STATUS_SXS_KEY_NOT_FOUND, -EIO, "STATUS_SXS_KEY_NOT_FOUND"}, - {STATUS_SXS_VERSION_CONFLICT, -EIO, "STATUS_SXS_VERSION_CONFLICT"}, - {STATUS_SXS_WRONG_SECTION_TYPE, -EIO, "STATUS_SXS_WRONG_SECTION_TYPE"}, - {STATUS_SXS_THREAD_QUERIES_DISABLED, -EIO, - "STATUS_SXS_THREAD_QUERIES_DISABLED"}, - {STATUS_SXS_ASSEMBLY_MISSING, -EIO, "STATUS_SXS_ASSEMBLY_MISSING"}, - {STATUS_SXS_PROCESS_DEFAULT_ALREADY_SET, -EIO, - "STATUS_SXS_PROCESS_DEFAULT_ALREADY_SET"}, - {STATUS_SXS_EARLY_DEACTIVATION, -EIO, "STATUS_SXS_EARLY_DEACTIVATION"}, - {STATUS_SXS_INVALID_DEACTIVATION, -EIO, - "STATUS_SXS_INVALID_DEACTIVATION"}, - {STATUS_SXS_MULTIPLE_DEACTIVATION, -EIO, - "STATUS_SXS_MULTIPLE_DEACTIVATION"}, - {STATUS_SXS_SYSTEM_DEFAULT_ACTIVATION_CONTEXT_EMPTY, -EIO, - "STATUS_SXS_SYSTEM_DEFAULT_ACTIVATION_CONTEXT_EMPTY"}, - {STATUS_SXS_PROCESS_TERMINATION_REQUESTED, -EIO, - "STATUS_SXS_PROCESS_TERMINATION_REQUESTED"}, - {STATUS_SXS_CORRUPT_ACTIVATION_STACK, -EIO, - "STATUS_SXS_CORRUPT_ACTIVATION_STACK"}, - {STATUS_SXS_CORRUPTION, -EIO, "STATUS_SXS_CORRUPTION"}, - {STATUS_SXS_INVALID_IDENTITY_ATTRIBUTE_VALUE, -EIO, - "STATUS_SXS_INVALID_IDENTITY_ATTRIBUTE_VALUE"}, - {STATUS_SXS_INVALID_IDENTITY_ATTRIBUTE_NAME, -EIO, - "STATUS_SXS_INVALID_IDENTITY_ATTRIBUTE_NAME"}, - {STATUS_SXS_IDENTITY_DUPLICATE_ATTRIBUTE, -EIO, - "STATUS_SXS_IDENTITY_DUPLICATE_ATTRIBUTE"}, - {STATUS_SXS_IDENTITY_PARSE_ERROR, -EIO, - "STATUS_SXS_IDENTITY_PARSE_ERROR"}, - {STATUS_SXS_COMPONENT_STORE_CORRUPT, -EIO, - "STATUS_SXS_COMPONENT_STORE_CORRUPT"}, - {STATUS_SXS_FILE_HASH_MISMATCH, -EIO, "STATUS_SXS_FILE_HASH_MISMATCH"}, - {STATUS_SXS_MANIFEST_IDENTITY_SAME_BUT_CONTENTS_DIFFERENT, -EIO, - "STATUS_SXS_MANIFEST_IDENTITY_SAME_BUT_CONTENTS_DIFFERENT"}, - {STATUS_SXS_IDENTITIES_DIFFERENT, -EIO, - "STATUS_SXS_IDENTITIES_DIFFERENT"}, - {STATUS_SXS_ASSEMBLY_IS_NOT_A_DEPLOYMENT, -EIO, - "STATUS_SXS_ASSEMBLY_IS_NOT_A_DEPLOYMENT"}, - {STATUS_SXS_FILE_NOT_PART_OF_ASSEMBLY, -EIO, - "STATUS_SXS_FILE_NOT_PART_OF_ASSEMBLY"}, - {STATUS_ADVANCED_INSTALLER_FAILED, -EIO, - "STATUS_ADVANCED_INSTALLER_FAILED"}, - {STATUS_XML_ENCODING_MISMATCH, -EIO, "STATUS_XML_ENCODING_MISMATCH"}, - {STATUS_SXS_MANIFEST_TOO_BIG, -EIO, "STATUS_SXS_MANIFEST_TOO_BIG"}, - {STATUS_SXS_SETTING_NOT_REGISTERED, -EIO, - "STATUS_SXS_SETTING_NOT_REGISTERED"}, - {STATUS_SXS_TRANSACTION_CLOSURE_INCOMPLETE, -EIO, - "STATUS_SXS_TRANSACTION_CLOSURE_INCOMPLETE"}, - {STATUS_SMI_PRIMITIVE_INSTALLER_FAILED, -EIO, - "STATUS_SMI_PRIMITIVE_INSTALLER_FAILED"}, - {STATUS_GENERIC_COMMAND_FAILED, -EIO, "STATUS_GENERIC_COMMAND_FAILED"}, - {STATUS_SXS_FILE_HASH_MISSING, -EIO, "STATUS_SXS_FILE_HASH_MISSING"}, - {STATUS_TRANSACTIONAL_CONFLICT, -EIO, "STATUS_TRANSACTIONAL_CONFLICT"}, - {STATUS_INVALID_TRANSACTION, -EIO, "STATUS_INVALID_TRANSACTION"}, - {STATUS_TRANSACTION_NOT_ACTIVE, -EIO, "STATUS_TRANSACTION_NOT_ACTIVE"}, - {STATUS_TM_INITIALIZATION_FAILED, -EIO, - "STATUS_TM_INITIALIZATION_FAILED"}, - {STATUS_RM_NOT_ACTIVE, -EIO, "STATUS_RM_NOT_ACTIVE"}, - {STATUS_RM_METADATA_CORRUPT, -EIO, "STATUS_RM_METADATA_CORRUPT"}, - {STATUS_TRANSACTION_NOT_JOINED, -EIO, "STATUS_TRANSACTION_NOT_JOINED"}, - {STATUS_DIRECTORY_NOT_RM, -EIO, "STATUS_DIRECTORY_NOT_RM"}, - {STATUS_TRANSACTIONS_UNSUPPORTED_REMOTE, -EIO, - "STATUS_TRANSACTIONS_UNSUPPORTED_REMOTE"}, - {STATUS_LOG_RESIZE_INVALID_SIZE, -EIO, - "STATUS_LOG_RESIZE_INVALID_SIZE"}, - {STATUS_REMOTE_FILE_VERSION_MISMATCH, -EIO, - "STATUS_REMOTE_FILE_VERSION_MISMATCH"}, - {STATUS_CRM_PROTOCOL_ALREADY_EXISTS, -EIO, - "STATUS_CRM_PROTOCOL_ALREADY_EXISTS"}, - {STATUS_TRANSACTION_PROPAGATION_FAILED, -EIO, - "STATUS_TRANSACTION_PROPAGATION_FAILED"}, - {STATUS_CRM_PROTOCOL_NOT_FOUND, -EIO, "STATUS_CRM_PROTOCOL_NOT_FOUND"}, - {STATUS_TRANSACTION_SUPERIOR_EXISTS, -EIO, - "STATUS_TRANSACTION_SUPERIOR_EXISTS"}, - {STATUS_TRANSACTION_REQUEST_NOT_VALID, -EIO, - "STATUS_TRANSACTION_REQUEST_NOT_VALID"}, - {STATUS_TRANSACTION_NOT_REQUESTED, -EIO, - "STATUS_TRANSACTION_NOT_REQUESTED"}, - {STATUS_TRANSACTION_ALREADY_ABORTED, -EIO, - "STATUS_TRANSACTION_ALREADY_ABORTED"}, - {STATUS_TRANSACTION_ALREADY_COMMITTED, -EIO, - "STATUS_TRANSACTION_ALREADY_COMMITTED"}, - {STATUS_TRANSACTION_INVALID_MARSHALL_BUFFER, -EIO, - "STATUS_TRANSACTION_INVALID_MARSHALL_BUFFER"}, - {STATUS_CURRENT_TRANSACTION_NOT_VALID, -EIO, - "STATUS_CURRENT_TRANSACTION_NOT_VALID"}, - {STATUS_LOG_GROWTH_FAILED, -EIO, "STATUS_LOG_GROWTH_FAILED"}, - {STATUS_OBJECT_NO_LONGER_EXISTS, -EIO, - "STATUS_OBJECT_NO_LONGER_EXISTS"}, - {STATUS_STREAM_MINIVERSION_NOT_FOUND, -EIO, - "STATUS_STREAM_MINIVERSION_NOT_FOUND"}, - {STATUS_STREAM_MINIVERSION_NOT_VALID, -EIO, - "STATUS_STREAM_MINIVERSION_NOT_VALID"}, - {STATUS_MINIVERSION_INACCESSIBLE_FROM_SPECIFIED_TRANSACTION, -EIO, - "STATUS_MINIVERSION_INACCESSIBLE_FROM_SPECIFIED_TRANSACTION"}, - {STATUS_CANT_OPEN_MINIVERSION_WITH_MODIFY_INTENT, -EIO, - "STATUS_CANT_OPEN_MINIVERSION_WITH_MODIFY_INTENT"}, - {STATUS_CANT_CREATE_MORE_STREAM_MINIVERSIONS, -EIO, - "STATUS_CANT_CREATE_MORE_STREAM_MINIVERSIONS"}, - {STATUS_HANDLE_NO_LONGER_VALID, -EIO, "STATUS_HANDLE_NO_LONGER_VALID"}, - {STATUS_LOG_CORRUPTION_DETECTED, -EIO, - "STATUS_LOG_CORRUPTION_DETECTED"}, - {STATUS_RM_DISCONNECTED, -EIO, "STATUS_RM_DISCONNECTED"}, - {STATUS_ENLISTMENT_NOT_SUPERIOR, -EIO, - "STATUS_ENLISTMENT_NOT_SUPERIOR"}, - {STATUS_FILE_IDENTITY_NOT_PERSISTENT, -EIO, - "STATUS_FILE_IDENTITY_NOT_PERSISTENT"}, - {STATUS_CANT_BREAK_TRANSACTIONAL_DEPENDENCY, -EIO, - "STATUS_CANT_BREAK_TRANSACTIONAL_DEPENDENCY"}, - {STATUS_CANT_CROSS_RM_BOUNDARY, -EIO, "STATUS_CANT_CROSS_RM_BOUNDARY"}, - {STATUS_TXF_DIR_NOT_EMPTY, -EIO, "STATUS_TXF_DIR_NOT_EMPTY"}, - {STATUS_INDOUBT_TRANSACTIONS_EXIST, -EIO, - "STATUS_INDOUBT_TRANSACTIONS_EXIST"}, - {STATUS_TM_VOLATILE, -EIO, "STATUS_TM_VOLATILE"}, - {STATUS_ROLLBACK_TIMER_EXPIRED, -EIO, "STATUS_ROLLBACK_TIMER_EXPIRED"}, - {STATUS_TXF_ATTRIBUTE_CORRUPT, -EIO, "STATUS_TXF_ATTRIBUTE_CORRUPT"}, - {STATUS_EFS_NOT_ALLOWED_IN_TRANSACTION, -EIO, - "STATUS_EFS_NOT_ALLOWED_IN_TRANSACTION"}, - {STATUS_TRANSACTIONAL_OPEN_NOT_ALLOWED, -EIO, - "STATUS_TRANSACTIONAL_OPEN_NOT_ALLOWED"}, - {STATUS_TRANSACTED_MAPPING_UNSUPPORTED_REMOTE, -EIO, - "STATUS_TRANSACTED_MAPPING_UNSUPPORTED_REMOTE"}, - {STATUS_TRANSACTION_REQUIRED_PROMOTION, -EIO, - "STATUS_TRANSACTION_REQUIRED_PROMOTION"}, - {STATUS_CANNOT_EXECUTE_FILE_IN_TRANSACTION, -EIO, - "STATUS_CANNOT_EXECUTE_FILE_IN_TRANSACTION"}, - {STATUS_TRANSACTIONS_NOT_FROZEN, -EIO, - "STATUS_TRANSACTIONS_NOT_FROZEN"}, - {STATUS_TRANSACTION_FREEZE_IN_PROGRESS, -EIO, - "STATUS_TRANSACTION_FREEZE_IN_PROGRESS"}, - {STATUS_NOT_SNAPSHOT_VOLUME, -EIO, "STATUS_NOT_SNAPSHOT_VOLUME"}, - {STATUS_NO_SAVEPOINT_WITH_OPEN_FILES, -EIO, - "STATUS_NO_SAVEPOINT_WITH_OPEN_FILES"}, - {STATUS_SPARSE_NOT_ALLOWED_IN_TRANSACTION, -EIO, - "STATUS_SPARSE_NOT_ALLOWED_IN_TRANSACTION"}, - {STATUS_TM_IDENTITY_MISMATCH, -EIO, "STATUS_TM_IDENTITY_MISMATCH"}, - {STATUS_FLOATED_SECTION, -EIO, "STATUS_FLOATED_SECTION"}, - {STATUS_CANNOT_ACCEPT_TRANSACTED_WORK, -EIO, - "STATUS_CANNOT_ACCEPT_TRANSACTED_WORK"}, - {STATUS_CANNOT_ABORT_TRANSACTIONS, -EIO, - "STATUS_CANNOT_ABORT_TRANSACTIONS"}, - {STATUS_TRANSACTION_NOT_FOUND, -EIO, "STATUS_TRANSACTION_NOT_FOUND"}, - {STATUS_RESOURCEMANAGER_NOT_FOUND, -EIO, - "STATUS_RESOURCEMANAGER_NOT_FOUND"}, - {STATUS_ENLISTMENT_NOT_FOUND, -EIO, "STATUS_ENLISTMENT_NOT_FOUND"}, - {STATUS_TRANSACTIONMANAGER_NOT_FOUND, -EIO, - "STATUS_TRANSACTIONMANAGER_NOT_FOUND"}, - {STATUS_TRANSACTIONMANAGER_NOT_ONLINE, -EIO, - "STATUS_TRANSACTIONMANAGER_NOT_ONLINE"}, - {STATUS_TRANSACTIONMANAGER_RECOVERY_NAME_COLLISION, -EIO, - "STATUS_TRANSACTIONMANAGER_RECOVERY_NAME_COLLISION"}, - {STATUS_TRANSACTION_NOT_ROOT, -EIO, "STATUS_TRANSACTION_NOT_ROOT"}, - {STATUS_TRANSACTION_OBJECT_EXPIRED, -EIO, - "STATUS_TRANSACTION_OBJECT_EXPIRED"}, - {STATUS_COMPRESSION_NOT_ALLOWED_IN_TRANSACTION, -EIO, - "STATUS_COMPRESSION_NOT_ALLOWED_IN_TRANSACTION"}, - {STATUS_TRANSACTION_RESPONSE_NOT_ENLISTED, -EIO, - "STATUS_TRANSACTION_RESPONSE_NOT_ENLISTED"}, - {STATUS_TRANSACTION_RECORD_TOO_LONG, -EIO, - "STATUS_TRANSACTION_RECORD_TOO_LONG"}, - {STATUS_NO_LINK_TRACKING_IN_TRANSACTION, -EIO, - "STATUS_NO_LINK_TRACKING_IN_TRANSACTION"}, - {STATUS_OPERATION_NOT_SUPPORTED_IN_TRANSACTION, -EOPNOTSUPP, - "STATUS_OPERATION_NOT_SUPPORTED_IN_TRANSACTION"}, - {STATUS_TRANSACTION_INTEGRITY_VIOLATED, -EIO, - "STATUS_TRANSACTION_INTEGRITY_VIOLATED"}, - {STATUS_LOG_SECTOR_INVALID, -EIO, "STATUS_LOG_SECTOR_INVALID"}, - {STATUS_LOG_SECTOR_PARITY_INVALID, -EIO, - "STATUS_LOG_SECTOR_PARITY_INVALID"}, - {STATUS_LOG_SECTOR_REMAPPED, -EIO, "STATUS_LOG_SECTOR_REMAPPED"}, - {STATUS_LOG_BLOCK_INCOMPLETE, -EIO, "STATUS_LOG_BLOCK_INCOMPLETE"}, - {STATUS_LOG_INVALID_RANGE, -EIO, "STATUS_LOG_INVALID_RANGE"}, - {STATUS_LOG_BLOCKS_EXHAUSTED, -EIO, "STATUS_LOG_BLOCKS_EXHAUSTED"}, - {STATUS_LOG_READ_CONTEXT_INVALID, -EIO, - "STATUS_LOG_READ_CONTEXT_INVALID"}, - {STATUS_LOG_RESTART_INVALID, -EIO, "STATUS_LOG_RESTART_INVALID"}, - {STATUS_LOG_BLOCK_VERSION, -EIO, "STATUS_LOG_BLOCK_VERSION"}, - {STATUS_LOG_BLOCK_INVALID, -EIO, "STATUS_LOG_BLOCK_INVALID"}, - {STATUS_LOG_READ_MODE_INVALID, -EIO, "STATUS_LOG_READ_MODE_INVALID"}, - {STATUS_LOG_METADATA_CORRUPT, -EIO, "STATUS_LOG_METADATA_CORRUPT"}, - {STATUS_LOG_METADATA_INVALID, -EIO, "STATUS_LOG_METADATA_INVALID"}, - {STATUS_LOG_METADATA_INCONSISTENT, -EIO, - "STATUS_LOG_METADATA_INCONSISTENT"}, - {STATUS_LOG_RESERVATION_INVALID, -EIO, - "STATUS_LOG_RESERVATION_INVALID"}, - {STATUS_LOG_CANT_DELETE, -EIO, "STATUS_LOG_CANT_DELETE"}, - {STATUS_LOG_CONTAINER_LIMIT_EXCEEDED, -EIO, - "STATUS_LOG_CONTAINER_LIMIT_EXCEEDED"}, - {STATUS_LOG_START_OF_LOG, -EIO, "STATUS_LOG_START_OF_LOG"}, - {STATUS_LOG_POLICY_ALREADY_INSTALLED, -EIO, - "STATUS_LOG_POLICY_ALREADY_INSTALLED"}, - {STATUS_LOG_POLICY_NOT_INSTALLED, -EIO, - "STATUS_LOG_POLICY_NOT_INSTALLED"}, - {STATUS_LOG_POLICY_INVALID, -EIO, "STATUS_LOG_POLICY_INVALID"}, - {STATUS_LOG_POLICY_CONFLICT, -EIO, "STATUS_LOG_POLICY_CONFLICT"}, - {STATUS_LOG_PINNED_ARCHIVE_TAIL, -EIO, - "STATUS_LOG_PINNED_ARCHIVE_TAIL"}, - {STATUS_LOG_RECORD_NONEXISTENT, -EIO, "STATUS_LOG_RECORD_NONEXISTENT"}, - {STATUS_LOG_RECORDS_RESERVED_INVALID, -EIO, - "STATUS_LOG_RECORDS_RESERVED_INVALID"}, - {STATUS_LOG_SPACE_RESERVED_INVALID, -EIO, - "STATUS_LOG_SPACE_RESERVED_INVALID"}, - {STATUS_LOG_TAIL_INVALID, -EIO, "STATUS_LOG_TAIL_INVALID"}, - {STATUS_LOG_FULL, -EIO, "STATUS_LOG_FULL"}, - {STATUS_LOG_MULTIPLEXED, -EIO, "STATUS_LOG_MULTIPLEXED"}, - {STATUS_LOG_DEDICATED, -EIO, "STATUS_LOG_DEDICATED"}, - {STATUS_LOG_ARCHIVE_NOT_IN_PROGRESS, -EIO, - "STATUS_LOG_ARCHIVE_NOT_IN_PROGRESS"}, - {STATUS_LOG_ARCHIVE_IN_PROGRESS, -EIO, - "STATUS_LOG_ARCHIVE_IN_PROGRESS"}, - {STATUS_LOG_EPHEMERAL, -EIO, "STATUS_LOG_EPHEMERAL"}, - {STATUS_LOG_NOT_ENOUGH_CONTAINERS, -EIO, - "STATUS_LOG_NOT_ENOUGH_CONTAINERS"}, - {STATUS_LOG_CLIENT_ALREADY_REGISTERED, -EIO, - "STATUS_LOG_CLIENT_ALREADY_REGISTERED"}, - {STATUS_LOG_CLIENT_NOT_REGISTERED, -EIO, - "STATUS_LOG_CLIENT_NOT_REGISTERED"}, - {STATUS_LOG_FULL_HANDLER_IN_PROGRESS, -EIO, - "STATUS_LOG_FULL_HANDLER_IN_PROGRESS"}, - {STATUS_LOG_CONTAINER_READ_FAILED, -EIO, - "STATUS_LOG_CONTAINER_READ_FAILED"}, - {STATUS_LOG_CONTAINER_WRITE_FAILED, -EIO, - "STATUS_LOG_CONTAINER_WRITE_FAILED"}, - {STATUS_LOG_CONTAINER_OPEN_FAILED, -EIO, - "STATUS_LOG_CONTAINER_OPEN_FAILED"}, - {STATUS_LOG_CONTAINER_STATE_INVALID, -EIO, - "STATUS_LOG_CONTAINER_STATE_INVALID"}, - {STATUS_LOG_STATE_INVALID, -EIO, "STATUS_LOG_STATE_INVALID"}, - {STATUS_LOG_PINNED, -EIO, "STATUS_LOG_PINNED"}, - {STATUS_LOG_METADATA_FLUSH_FAILED, -EIO, - "STATUS_LOG_METADATA_FLUSH_FAILED"}, - {STATUS_LOG_INCONSISTENT_SECURITY, -EIO, - "STATUS_LOG_INCONSISTENT_SECURITY"}, - {STATUS_LOG_APPENDED_FLUSH_FAILED, -EIO, - "STATUS_LOG_APPENDED_FLUSH_FAILED"}, - {STATUS_LOG_PINNED_RESERVATION, -EIO, "STATUS_LOG_PINNED_RESERVATION"}, - {STATUS_VIDEO_HUNG_DISPLAY_DRIVER_THREAD, -EIO, - "STATUS_VIDEO_HUNG_DISPLAY_DRIVER_THREAD"}, - {STATUS_FLT_NO_HANDLER_DEFINED, -EIO, "STATUS_FLT_NO_HANDLER_DEFINED"}, - {STATUS_FLT_CONTEXT_ALREADY_DEFINED, -EIO, - "STATUS_FLT_CONTEXT_ALREADY_DEFINED"}, - {STATUS_FLT_INVALID_ASYNCHRONOUS_REQUEST, -EIO, - "STATUS_FLT_INVALID_ASYNCHRONOUS_REQUEST"}, - {STATUS_FLT_DISALLOW_FAST_IO, -EIO, "STATUS_FLT_DISALLOW_FAST_IO"}, - {STATUS_FLT_INVALID_NAME_REQUEST, -EIO, - "STATUS_FLT_INVALID_NAME_REQUEST"}, - {STATUS_FLT_NOT_SAFE_TO_POST_OPERATION, -EIO, - "STATUS_FLT_NOT_SAFE_TO_POST_OPERATION"}, - {STATUS_FLT_NOT_INITIALIZED, -EIO, "STATUS_FLT_NOT_INITIALIZED"}, - {STATUS_FLT_FILTER_NOT_READY, -EIO, "STATUS_FLT_FILTER_NOT_READY"}, - {STATUS_FLT_POST_OPERATION_CLEANUP, -EIO, - "STATUS_FLT_POST_OPERATION_CLEANUP"}, - {STATUS_FLT_INTERNAL_ERROR, -EIO, "STATUS_FLT_INTERNAL_ERROR"}, - {STATUS_FLT_DELETING_OBJECT, -EIO, "STATUS_FLT_DELETING_OBJECT"}, - {STATUS_FLT_MUST_BE_NONPAGED_POOL, -EIO, - "STATUS_FLT_MUST_BE_NONPAGED_POOL"}, - {STATUS_FLT_DUPLICATE_ENTRY, -EIO, "STATUS_FLT_DUPLICATE_ENTRY"}, - {STATUS_FLT_CBDQ_DISABLED, -EIO, "STATUS_FLT_CBDQ_DISABLED"}, - {STATUS_FLT_DO_NOT_ATTACH, -EIO, "STATUS_FLT_DO_NOT_ATTACH"}, - {STATUS_FLT_DO_NOT_DETACH, -EIO, "STATUS_FLT_DO_NOT_DETACH"}, - {STATUS_FLT_INSTANCE_ALTITUDE_COLLISION, -EIO, - "STATUS_FLT_INSTANCE_ALTITUDE_COLLISION"}, - {STATUS_FLT_INSTANCE_NAME_COLLISION, -EIO, - "STATUS_FLT_INSTANCE_NAME_COLLISION"}, - {STATUS_FLT_FILTER_NOT_FOUND, -EIO, "STATUS_FLT_FILTER_NOT_FOUND"}, - {STATUS_FLT_VOLUME_NOT_FOUND, -EIO, "STATUS_FLT_VOLUME_NOT_FOUND"}, - {STATUS_FLT_INSTANCE_NOT_FOUND, -EIO, "STATUS_FLT_INSTANCE_NOT_FOUND"}, - {STATUS_FLT_CONTEXT_ALLOCATION_NOT_FOUND, -EIO, - "STATUS_FLT_CONTEXT_ALLOCATION_NOT_FOUND"}, - {STATUS_FLT_INVALID_CONTEXT_REGISTRATION, -EIO, - "STATUS_FLT_INVALID_CONTEXT_REGISTRATION"}, - {STATUS_FLT_NAME_CACHE_MISS, -EIO, "STATUS_FLT_NAME_CACHE_MISS"}, - {STATUS_FLT_NO_DEVICE_OBJECT, -EIO, "STATUS_FLT_NO_DEVICE_OBJECT"}, - {STATUS_FLT_VOLUME_ALREADY_MOUNTED, -EIO, - "STATUS_FLT_VOLUME_ALREADY_MOUNTED"}, - {STATUS_FLT_ALREADY_ENLISTED, -EIO, "STATUS_FLT_ALREADY_ENLISTED"}, - {STATUS_FLT_CONTEXT_ALREADY_LINKED, -EIO, - "STATUS_FLT_CONTEXT_ALREADY_LINKED"}, - {STATUS_FLT_NO_WAITER_FOR_REPLY, -EIO, - "STATUS_FLT_NO_WAITER_FOR_REPLY"}, - {STATUS_MONITOR_NO_DESCRIPTOR, -EIO, "STATUS_MONITOR_NO_DESCRIPTOR"}, - {STATUS_MONITOR_UNKNOWN_DESCRIPTOR_FORMAT, -EIO, - "STATUS_MONITOR_UNKNOWN_DESCRIPTOR_FORMAT"}, - {STATUS_MONITOR_INVALID_DESCRIPTOR_CHECKSUM, -EIO, - "STATUS_MONITOR_INVALID_DESCRIPTOR_CHECKSUM"}, - {STATUS_MONITOR_INVALID_STANDARD_TIMING_BLOCK, -EIO, - "STATUS_MONITOR_INVALID_STANDARD_TIMING_BLOCK"}, - {STATUS_MONITOR_WMI_DATABLOCK_REGISTRATION_FAILED, -EIO, - "STATUS_MONITOR_WMI_DATABLOCK_REGISTRATION_FAILED"}, - {STATUS_MONITOR_INVALID_SERIAL_NUMBER_MONDSC_BLOCK, -EIO, - "STATUS_MONITOR_INVALID_SERIAL_NUMBER_MONDSC_BLOCK"}, - {STATUS_MONITOR_INVALID_USER_FRIENDLY_MONDSC_BLOCK, -EIO, - "STATUS_MONITOR_INVALID_USER_FRIENDLY_MONDSC_BLOCK"}, - {STATUS_MONITOR_NO_MORE_DESCRIPTOR_DATA, -EIO, - "STATUS_MONITOR_NO_MORE_DESCRIPTOR_DATA"}, - {STATUS_MONITOR_INVALID_DETAILED_TIMING_BLOCK, -EIO, - "STATUS_MONITOR_INVALID_DETAILED_TIMING_BLOCK"}, - {STATUS_GRAPHICS_NOT_EXCLUSIVE_MODE_OWNER, -EIO, - "STATUS_GRAPHICS_NOT_EXCLUSIVE_MODE_OWNER"}, - {STATUS_GRAPHICS_INSUFFICIENT_DMA_BUFFER, -EIO, - "STATUS_GRAPHICS_INSUFFICIENT_DMA_BUFFER"}, - {STATUS_GRAPHICS_INVALID_DISPLAY_ADAPTER, -EIO, - "STATUS_GRAPHICS_INVALID_DISPLAY_ADAPTER"}, - {STATUS_GRAPHICS_ADAPTER_WAS_RESET, -EIO, - "STATUS_GRAPHICS_ADAPTER_WAS_RESET"}, - {STATUS_GRAPHICS_INVALID_DRIVER_MODEL, -EIO, - "STATUS_GRAPHICS_INVALID_DRIVER_MODEL"}, - {STATUS_GRAPHICS_PRESENT_MODE_CHANGED, -EIO, - "STATUS_GRAPHICS_PRESENT_MODE_CHANGED"}, - {STATUS_GRAPHICS_PRESENT_OCCLUDED, -EIO, - "STATUS_GRAPHICS_PRESENT_OCCLUDED"}, - {STATUS_GRAPHICS_PRESENT_DENIED, -EIO, - "STATUS_GRAPHICS_PRESENT_DENIED"}, - {STATUS_GRAPHICS_CANNOTCOLORCONVERT, -EIO, - "STATUS_GRAPHICS_CANNOTCOLORCONVERT"}, - {STATUS_GRAPHICS_NO_VIDEO_MEMORY, -EIO, - "STATUS_GRAPHICS_NO_VIDEO_MEMORY"}, - {STATUS_GRAPHICS_CANT_LOCK_MEMORY, -EIO, - "STATUS_GRAPHICS_CANT_LOCK_MEMORY"}, - {STATUS_GRAPHICS_ALLOCATION_BUSY, -EBUSY, - "STATUS_GRAPHICS_ALLOCATION_BUSY"}, - {STATUS_GRAPHICS_TOO_MANY_REFERENCES, -EIO, - "STATUS_GRAPHICS_TOO_MANY_REFERENCES"}, - {STATUS_GRAPHICS_TRY_AGAIN_LATER, -EIO, - "STATUS_GRAPHICS_TRY_AGAIN_LATER"}, - {STATUS_GRAPHICS_TRY_AGAIN_NOW, -EIO, "STATUS_GRAPHICS_TRY_AGAIN_NOW"}, - {STATUS_GRAPHICS_ALLOCATION_INVALID, -EIO, - "STATUS_GRAPHICS_ALLOCATION_INVALID"}, - {STATUS_GRAPHICS_UNSWIZZLING_APERTURE_UNAVAILABLE, -EIO, - "STATUS_GRAPHICS_UNSWIZZLING_APERTURE_UNAVAILABLE"}, - {STATUS_GRAPHICS_UNSWIZZLING_APERTURE_UNSUPPORTED, -EIO, - "STATUS_GRAPHICS_UNSWIZZLING_APERTURE_UNSUPPORTED"}, - {STATUS_GRAPHICS_CANT_EVICT_PINNED_ALLOCATION, -EIO, - "STATUS_GRAPHICS_CANT_EVICT_PINNED_ALLOCATION"}, - {STATUS_GRAPHICS_INVALID_ALLOCATION_USAGE, -EIO, - "STATUS_GRAPHICS_INVALID_ALLOCATION_USAGE"}, - {STATUS_GRAPHICS_CANT_RENDER_LOCKED_ALLOCATION, -EIO, - "STATUS_GRAPHICS_CANT_RENDER_LOCKED_ALLOCATION"}, - {STATUS_GRAPHICS_ALLOCATION_CLOSED, -EIO, - "STATUS_GRAPHICS_ALLOCATION_CLOSED"}, - {STATUS_GRAPHICS_INVALID_ALLOCATION_INSTANCE, -EIO, - "STATUS_GRAPHICS_INVALID_ALLOCATION_INSTANCE"}, - {STATUS_GRAPHICS_INVALID_ALLOCATION_HANDLE, -EIO, - "STATUS_GRAPHICS_INVALID_ALLOCATION_HANDLE"}, - {STATUS_GRAPHICS_WRONG_ALLOCATION_DEVICE, -EIO, - "STATUS_GRAPHICS_WRONG_ALLOCATION_DEVICE"}, - {STATUS_GRAPHICS_ALLOCATION_CONTENT_LOST, -EIO, - "STATUS_GRAPHICS_ALLOCATION_CONTENT_LOST"}, - {STATUS_GRAPHICS_GPU_EXCEPTION_ON_DEVICE, -EIO, - "STATUS_GRAPHICS_GPU_EXCEPTION_ON_DEVICE"}, - {STATUS_GRAPHICS_INVALID_VIDPN_TOPOLOGY, -EIO, - "STATUS_GRAPHICS_INVALID_VIDPN_TOPOLOGY"}, - {STATUS_GRAPHICS_VIDPN_TOPOLOGY_NOT_SUPPORTED, -EIO, - "STATUS_GRAPHICS_VIDPN_TOPOLOGY_NOT_SUPPORTED"}, - {STATUS_GRAPHICS_VIDPN_TOPOLOGY_CURRENTLY_NOT_SUPPORTED, -EIO, - "STATUS_GRAPHICS_VIDPN_TOPOLOGY_CURRENTLY_NOT_SUPPORTED"}, - {STATUS_GRAPHICS_INVALID_VIDPN, -EIO, "STATUS_GRAPHICS_INVALID_VIDPN"}, - {STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE, -EIO, - "STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE"}, - {STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_TARGET, -EIO, - "STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_TARGET"}, - {STATUS_GRAPHICS_VIDPN_MODALITY_NOT_SUPPORTED, -EIO, - "STATUS_GRAPHICS_VIDPN_MODALITY_NOT_SUPPORTED"}, - {STATUS_GRAPHICS_INVALID_VIDPN_SOURCEMODESET, -EIO, - "STATUS_GRAPHICS_INVALID_VIDPN_SOURCEMODESET"}, - {STATUS_GRAPHICS_INVALID_VIDPN_TARGETMODESET, -EIO, - "STATUS_GRAPHICS_INVALID_VIDPN_TARGETMODESET"}, - {STATUS_GRAPHICS_INVALID_FREQUENCY, -EIO, - "STATUS_GRAPHICS_INVALID_FREQUENCY"}, - {STATUS_GRAPHICS_INVALID_ACTIVE_REGION, -EIO, - "STATUS_GRAPHICS_INVALID_ACTIVE_REGION"}, - {STATUS_GRAPHICS_INVALID_TOTAL_REGION, -EIO, - "STATUS_GRAPHICS_INVALID_TOTAL_REGION"}, - {STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE_MODE, -EIO, - "STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE_MODE"}, - {STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_TARGET_MODE, -EIO, - "STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_TARGET_MODE"}, - {STATUS_GRAPHICS_PINNED_MODE_MUST_REMAIN_IN_SET, -EIO, - "STATUS_GRAPHICS_PINNED_MODE_MUST_REMAIN_IN_SET"}, - {STATUS_GRAPHICS_PATH_ALREADY_IN_TOPOLOGY, -EIO, - "STATUS_GRAPHICS_PATH_ALREADY_IN_TOPOLOGY"}, - {STATUS_GRAPHICS_MODE_ALREADY_IN_MODESET, -EIO, - "STATUS_GRAPHICS_MODE_ALREADY_IN_MODESET"}, - {STATUS_GRAPHICS_INVALID_VIDEOPRESENTSOURCESET, -EIO, - "STATUS_GRAPHICS_INVALID_VIDEOPRESENTSOURCESET"}, - {STATUS_GRAPHICS_INVALID_VIDEOPRESENTTARGETSET, -EIO, - "STATUS_GRAPHICS_INVALID_VIDEOPRESENTTARGETSET"}, - {STATUS_GRAPHICS_SOURCE_ALREADY_IN_SET, -EIO, - "STATUS_GRAPHICS_SOURCE_ALREADY_IN_SET"}, - {STATUS_GRAPHICS_TARGET_ALREADY_IN_SET, -EIO, - "STATUS_GRAPHICS_TARGET_ALREADY_IN_SET"}, - {STATUS_GRAPHICS_INVALID_VIDPN_PRESENT_PATH, -EIO, - "STATUS_GRAPHICS_INVALID_VIDPN_PRESENT_PATH"}, - {STATUS_GRAPHICS_NO_RECOMMENDED_VIDPN_TOPOLOGY, -EIO, - "STATUS_GRAPHICS_NO_RECOMMENDED_VIDPN_TOPOLOGY"}, - {STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGESET, -EIO, - "STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGESET"}, - {STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGE, -EIO, - "STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGE"}, - {STATUS_GRAPHICS_FREQUENCYRANGE_NOT_IN_SET, -EIO, - "STATUS_GRAPHICS_FREQUENCYRANGE_NOT_IN_SET"}, - {STATUS_GRAPHICS_FREQUENCYRANGE_ALREADY_IN_SET, -EIO, - "STATUS_GRAPHICS_FREQUENCYRANGE_ALREADY_IN_SET"}, - {STATUS_GRAPHICS_STALE_MODESET, -EIO, "STATUS_GRAPHICS_STALE_MODESET"}, - {STATUS_GRAPHICS_INVALID_MONITOR_SOURCEMODESET, -EIO, - "STATUS_GRAPHICS_INVALID_MONITOR_SOURCEMODESET"}, - {STATUS_GRAPHICS_INVALID_MONITOR_SOURCE_MODE, -EIO, - "STATUS_GRAPHICS_INVALID_MONITOR_SOURCE_MODE"}, - {STATUS_GRAPHICS_NO_RECOMMENDED_FUNCTIONAL_VIDPN, -EIO, - "STATUS_GRAPHICS_NO_RECOMMENDED_FUNCTIONAL_VIDPN"}, - {STATUS_GRAPHICS_MODE_ID_MUST_BE_UNIQUE, -EIO, - "STATUS_GRAPHICS_MODE_ID_MUST_BE_UNIQUE"}, - {STATUS_GRAPHICS_EMPTY_ADAPTER_MONITOR_MODE_SUPPORT_INTERSECTION, -EIO, - "STATUS_GRAPHICS_EMPTY_ADAPTER_MONITOR_MODE_SUPPORT_INTERSECTION"}, - {STATUS_GRAPHICS_VIDEO_PRESENT_TARGETS_LESS_THAN_SOURCES, -EIO, - "STATUS_GRAPHICS_VIDEO_PRESENT_TARGETS_LESS_THAN_SOURCES"}, - {STATUS_GRAPHICS_PATH_NOT_IN_TOPOLOGY, -EIO, - "STATUS_GRAPHICS_PATH_NOT_IN_TOPOLOGY"}, - {STATUS_GRAPHICS_ADAPTER_MUST_HAVE_AT_LEAST_ONE_SOURCE, -EIO, - "STATUS_GRAPHICS_ADAPTER_MUST_HAVE_AT_LEAST_ONE_SOURCE"}, - {STATUS_GRAPHICS_ADAPTER_MUST_HAVE_AT_LEAST_ONE_TARGET, -EIO, - "STATUS_GRAPHICS_ADAPTER_MUST_HAVE_AT_LEAST_ONE_TARGET"}, - {STATUS_GRAPHICS_INVALID_MONITORDESCRIPTORSET, -EIO, - "STATUS_GRAPHICS_INVALID_MONITORDESCRIPTORSET"}, - {STATUS_GRAPHICS_INVALID_MONITORDESCRIPTOR, -EIO, - "STATUS_GRAPHICS_INVALID_MONITORDESCRIPTOR"}, - {STATUS_GRAPHICS_MONITORDESCRIPTOR_NOT_IN_SET, -EIO, - "STATUS_GRAPHICS_MONITORDESCRIPTOR_NOT_IN_SET"}, - {STATUS_GRAPHICS_MONITORDESCRIPTOR_ALREADY_IN_SET, -EIO, - "STATUS_GRAPHICS_MONITORDESCRIPTOR_ALREADY_IN_SET"}, - {STATUS_GRAPHICS_MONITORDESCRIPTOR_ID_MUST_BE_UNIQUE, -EIO, - "STATUS_GRAPHICS_MONITORDESCRIPTOR_ID_MUST_BE_UNIQUE"}, - {STATUS_GRAPHICS_INVALID_VIDPN_TARGET_SUBSET_TYPE, -EIO, - "STATUS_GRAPHICS_INVALID_VIDPN_TARGET_SUBSET_TYPE"}, - {STATUS_GRAPHICS_RESOURCES_NOT_RELATED, -EIO, - "STATUS_GRAPHICS_RESOURCES_NOT_RELATED"}, - {STATUS_GRAPHICS_SOURCE_ID_MUST_BE_UNIQUE, -EIO, - "STATUS_GRAPHICS_SOURCE_ID_MUST_BE_UNIQUE"}, - {STATUS_GRAPHICS_TARGET_ID_MUST_BE_UNIQUE, -EIO, - "STATUS_GRAPHICS_TARGET_ID_MUST_BE_UNIQUE"}, - {STATUS_GRAPHICS_NO_AVAILABLE_VIDPN_TARGET, -EIO, - "STATUS_GRAPHICS_NO_AVAILABLE_VIDPN_TARGET"}, - {STATUS_GRAPHICS_MONITOR_COULD_NOT_BE_ASSOCIATED_WITH_ADAPTER, -EIO, - "STATUS_GRAPHICS_MONITOR_COULD_NOT_BE_ASSOCIATED_WITH_ADAPTER"}, - {STATUS_GRAPHICS_NO_VIDPNMGR, -EIO, "STATUS_GRAPHICS_NO_VIDPNMGR"}, - {STATUS_GRAPHICS_NO_ACTIVE_VIDPN, -EIO, - "STATUS_GRAPHICS_NO_ACTIVE_VIDPN"}, - {STATUS_GRAPHICS_STALE_VIDPN_TOPOLOGY, -EIO, - "STATUS_GRAPHICS_STALE_VIDPN_TOPOLOGY"}, - {STATUS_GRAPHICS_MONITOR_NOT_CONNECTED, -EIO, - "STATUS_GRAPHICS_MONITOR_NOT_CONNECTED"}, - {STATUS_GRAPHICS_SOURCE_NOT_IN_TOPOLOGY, -EIO, - "STATUS_GRAPHICS_SOURCE_NOT_IN_TOPOLOGY"}, - {STATUS_GRAPHICS_INVALID_PRIMARYSURFACE_SIZE, -EIO, - "STATUS_GRAPHICS_INVALID_PRIMARYSURFACE_SIZE"}, - {STATUS_GRAPHICS_INVALID_VISIBLEREGION_SIZE, -EIO, - "STATUS_GRAPHICS_INVALID_VISIBLEREGION_SIZE"}, - {STATUS_GRAPHICS_INVALID_STRIDE, -EIO, - "STATUS_GRAPHICS_INVALID_STRIDE"}, - {STATUS_GRAPHICS_INVALID_PIXELFORMAT, -EIO, - "STATUS_GRAPHICS_INVALID_PIXELFORMAT"}, - {STATUS_GRAPHICS_INVALID_COLORBASIS, -EIO, - "STATUS_GRAPHICS_INVALID_COLORBASIS"}, - {STATUS_GRAPHICS_INVALID_PIXELVALUEACCESSMODE, -EIO, - "STATUS_GRAPHICS_INVALID_PIXELVALUEACCESSMODE"}, - {STATUS_GRAPHICS_TARGET_NOT_IN_TOPOLOGY, -EIO, - "STATUS_GRAPHICS_TARGET_NOT_IN_TOPOLOGY"}, - {STATUS_GRAPHICS_NO_DISPLAY_MODE_MANAGEMENT_SUPPORT, -EIO, - "STATUS_GRAPHICS_NO_DISPLAY_MODE_MANAGEMENT_SUPPORT"}, - {STATUS_GRAPHICS_VIDPN_SOURCE_IN_USE, -EIO, - "STATUS_GRAPHICS_VIDPN_SOURCE_IN_USE"}, - {STATUS_GRAPHICS_CANT_ACCESS_ACTIVE_VIDPN, -EIO, - "STATUS_GRAPHICS_CANT_ACCESS_ACTIVE_VIDPN"}, - {STATUS_GRAPHICS_INVALID_PATH_IMPORTANCE_ORDINAL, -EIO, - "STATUS_GRAPHICS_INVALID_PATH_IMPORTANCE_ORDINAL"}, - {STATUS_GRAPHICS_INVALID_PATH_CONTENT_GEOMETRY_TRANSFORMATION, -EIO, - "STATUS_GRAPHICS_INVALID_PATH_CONTENT_GEOMETRY_TRANSFORMATION"}, - {STATUS_GRAPHICS_PATH_CONTENT_GEOMETRY_TRANSFORMATION_NOT_SUPPORTED, - -EIO, - "STATUS_GRAPHICS_PATH_CONTENT_GEOMETRY_TRANSFORMATION_NOT_SUPPORTED"}, - {STATUS_GRAPHICS_INVALID_GAMMA_RAMP, -EIO, - "STATUS_GRAPHICS_INVALID_GAMMA_RAMP"}, - {STATUS_GRAPHICS_GAMMA_RAMP_NOT_SUPPORTED, -EIO, - "STATUS_GRAPHICS_GAMMA_RAMP_NOT_SUPPORTED"}, - {STATUS_GRAPHICS_MULTISAMPLING_NOT_SUPPORTED, -EIO, - "STATUS_GRAPHICS_MULTISAMPLING_NOT_SUPPORTED"}, - {STATUS_GRAPHICS_MODE_NOT_IN_MODESET, -EIO, - "STATUS_GRAPHICS_MODE_NOT_IN_MODESET"}, - {STATUS_GRAPHICS_INVALID_VIDPN_TOPOLOGY_RECOMMENDATION_REASON, -EIO, - "STATUS_GRAPHICS_INVALID_VIDPN_TOPOLOGY_RECOMMENDATION_REASON"}, - {STATUS_GRAPHICS_INVALID_PATH_CONTENT_TYPE, -EIO, - "STATUS_GRAPHICS_INVALID_PATH_CONTENT_TYPE"}, - {STATUS_GRAPHICS_INVALID_COPYPROTECTION_TYPE, -EIO, - "STATUS_GRAPHICS_INVALID_COPYPROTECTION_TYPE"}, - {STATUS_GRAPHICS_UNASSIGNED_MODESET_ALREADY_EXISTS, -EIO, - "STATUS_GRAPHICS_UNASSIGNED_MODESET_ALREADY_EXISTS"}, - {STATUS_GRAPHICS_INVALID_SCANLINE_ORDERING, -EIO, - "STATUS_GRAPHICS_INVALID_SCANLINE_ORDERING"}, - {STATUS_GRAPHICS_TOPOLOGY_CHANGES_NOT_ALLOWED, -EIO, - "STATUS_GRAPHICS_TOPOLOGY_CHANGES_NOT_ALLOWED"}, - {STATUS_GRAPHICS_NO_AVAILABLE_IMPORTANCE_ORDINALS, -EIO, - "STATUS_GRAPHICS_NO_AVAILABLE_IMPORTANCE_ORDINALS"}, - {STATUS_GRAPHICS_INCOMPATIBLE_PRIVATE_FORMAT, -EIO, - "STATUS_GRAPHICS_INCOMPATIBLE_PRIVATE_FORMAT"}, - {STATUS_GRAPHICS_INVALID_MODE_PRUNING_ALGORITHM, -EIO, - "STATUS_GRAPHICS_INVALID_MODE_PRUNING_ALGORITHM"}, - {STATUS_GRAPHICS_INVALID_MONITOR_CAPABILITY_ORIGIN, -EIO, - "STATUS_GRAPHICS_INVALID_MONITOR_CAPABILITY_ORIGIN"}, - {STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGE_CONSTRAINT, -EIO, - "STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGE_CONSTRAINT"}, - {STATUS_GRAPHICS_MAX_NUM_PATHS_REACHED, -EIO, - "STATUS_GRAPHICS_MAX_NUM_PATHS_REACHED"}, - {STATUS_GRAPHICS_CANCEL_VIDPN_TOPOLOGY_AUGMENTATION, -EIO, - "STATUS_GRAPHICS_CANCEL_VIDPN_TOPOLOGY_AUGMENTATION"}, - {STATUS_GRAPHICS_INVALID_CLIENT_TYPE, -EIO, - "STATUS_GRAPHICS_INVALID_CLIENT_TYPE"}, - {STATUS_GRAPHICS_CLIENTVIDPN_NOT_SET, -EIO, - "STATUS_GRAPHICS_CLIENTVIDPN_NOT_SET"}, - {STATUS_GRAPHICS_SPECIFIED_CHILD_ALREADY_CONNECTED, -EIO, - "STATUS_GRAPHICS_SPECIFIED_CHILD_ALREADY_CONNECTED"}, - {STATUS_GRAPHICS_CHILD_DESCRIPTOR_NOT_SUPPORTED, -EIO, - "STATUS_GRAPHICS_CHILD_DESCRIPTOR_NOT_SUPPORTED"}, - {STATUS_GRAPHICS_NOT_A_LINKED_ADAPTER, -EIO, - "STATUS_GRAPHICS_NOT_A_LINKED_ADAPTER"}, - {STATUS_GRAPHICS_LEADLINK_NOT_ENUMERATED, -EIO, - "STATUS_GRAPHICS_LEADLINK_NOT_ENUMERATED"}, - {STATUS_GRAPHICS_CHAINLINKS_NOT_ENUMERATED, -EIO, - "STATUS_GRAPHICS_CHAINLINKS_NOT_ENUMERATED"}, - {STATUS_GRAPHICS_ADAPTER_CHAIN_NOT_READY, -EIO, - "STATUS_GRAPHICS_ADAPTER_CHAIN_NOT_READY"}, - {STATUS_GRAPHICS_CHAINLINKS_NOT_STARTED, -EIO, - "STATUS_GRAPHICS_CHAINLINKS_NOT_STARTED"}, - {STATUS_GRAPHICS_CHAINLINKS_NOT_POWERED_ON, -EIO, - "STATUS_GRAPHICS_CHAINLINKS_NOT_POWERED_ON"}, - {STATUS_GRAPHICS_INCONSISTENT_DEVICE_LINK_STATE, -EIO, - "STATUS_GRAPHICS_INCONSISTENT_DEVICE_LINK_STATE"}, - {STATUS_GRAPHICS_NOT_POST_DEVICE_DRIVER, -EIO, - "STATUS_GRAPHICS_NOT_POST_DEVICE_DRIVER"}, - {STATUS_GRAPHICS_ADAPTER_ACCESS_NOT_EXCLUDED, -EIO, - "STATUS_GRAPHICS_ADAPTER_ACCESS_NOT_EXCLUDED"}, - {STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_DOES_NOT_HAVE_COPP_SEMANTICS, - -EIO, - "STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_DOES_NOT_HAVE_COPP_SEMANTICS"}, - {STATUS_GRAPHICS_OPM_INVALID_INFORMATION_REQUEST, -EIO, - "STATUS_GRAPHICS_OPM_INVALID_INFORMATION_REQUEST"}, - {STATUS_GRAPHICS_OPM_DRIVER_INTERNAL_ERROR, -EIO, - "STATUS_GRAPHICS_OPM_DRIVER_INTERNAL_ERROR"}, - {STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_DOES_NOT_HAVE_OPM_SEMANTICS, -EIO, - "STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_DOES_NOT_HAVE_OPM_SEMANTICS"}, - {STATUS_GRAPHICS_OPM_SIGNALING_NOT_SUPPORTED, -EIO, - "STATUS_GRAPHICS_OPM_SIGNALING_NOT_SUPPORTED"}, - {STATUS_GRAPHICS_OPM_INVALID_CONFIGURATION_REQUEST, -EIO, - "STATUS_GRAPHICS_OPM_INVALID_CONFIGURATION_REQUEST"}, - {STATUS_GRAPHICS_OPM_NOT_SUPPORTED, -EIO, - "STATUS_GRAPHICS_OPM_NOT_SUPPORTED"}, - {STATUS_GRAPHICS_COPP_NOT_SUPPORTED, -EIO, - "STATUS_GRAPHICS_COPP_NOT_SUPPORTED"}, - {STATUS_GRAPHICS_UAB_NOT_SUPPORTED, -EIO, - "STATUS_GRAPHICS_UAB_NOT_SUPPORTED"}, - {STATUS_GRAPHICS_OPM_INVALID_ENCRYPTED_PARAMETERS, -EIO, - "STATUS_GRAPHICS_OPM_INVALID_ENCRYPTED_PARAMETERS"}, - {STATUS_GRAPHICS_OPM_PARAMETER_ARRAY_TOO_SMALL, -EIO, - "STATUS_GRAPHICS_OPM_PARAMETER_ARRAY_TOO_SMALL"}, - {STATUS_GRAPHICS_OPM_NO_PROTECTED_OUTPUTS_EXIST, -EIO, - "STATUS_GRAPHICS_OPM_NO_PROTECTED_OUTPUTS_EXIST"}, - {STATUS_GRAPHICS_PVP_NO_DISPLAY_DEVICE_CORRESPONDS_TO_NAME, -EIO, - "STATUS_GRAPHICS_PVP_NO_DISPLAY_DEVICE_CORRESPONDS_TO_NAME"}, - {STATUS_GRAPHICS_PVP_DISPLAY_DEVICE_NOT_ATTACHED_TO_DESKTOP, -EIO, - "STATUS_GRAPHICS_PVP_DISPLAY_DEVICE_NOT_ATTACHED_TO_DESKTOP"}, - {STATUS_GRAPHICS_PVP_MIRRORING_DEVICES_NOT_SUPPORTED, -EIO, - "STATUS_GRAPHICS_PVP_MIRRORING_DEVICES_NOT_SUPPORTED"}, - {STATUS_GRAPHICS_OPM_INVALID_POINTER, -EIO, - "STATUS_GRAPHICS_OPM_INVALID_POINTER"}, - {STATUS_GRAPHICS_OPM_INTERNAL_ERROR, -EIO, - "STATUS_GRAPHICS_OPM_INTERNAL_ERROR"}, - {STATUS_GRAPHICS_OPM_INVALID_HANDLE, -EIO, - "STATUS_GRAPHICS_OPM_INVALID_HANDLE"}, - {STATUS_GRAPHICS_PVP_NO_MONITORS_CORRESPOND_TO_DISPLAY_DEVICE, -EIO, - "STATUS_GRAPHICS_PVP_NO_MONITORS_CORRESPOND_TO_DISPLAY_DEVICE"}, - {STATUS_GRAPHICS_PVP_INVALID_CERTIFICATE_LENGTH, -EIO, - "STATUS_GRAPHICS_PVP_INVALID_CERTIFICATE_LENGTH"}, - {STATUS_GRAPHICS_OPM_SPANNING_MODE_ENABLED, -EIO, - "STATUS_GRAPHICS_OPM_SPANNING_MODE_ENABLED"}, - {STATUS_GRAPHICS_OPM_THEATER_MODE_ENABLED, -EIO, - "STATUS_GRAPHICS_OPM_THEATER_MODE_ENABLED"}, - {STATUS_GRAPHICS_PVP_HFS_FAILED, -EIO, - "STATUS_GRAPHICS_PVP_HFS_FAILED"}, - {STATUS_GRAPHICS_OPM_INVALID_SRM, -EIO, - "STATUS_GRAPHICS_OPM_INVALID_SRM"}, - {STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_HDCP, -EIO, - "STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_HDCP"}, - {STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_ACP, -EIO, - "STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_ACP"}, - {STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_CGMSA, -EIO, - "STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_CGMSA"}, - {STATUS_GRAPHICS_OPM_HDCP_SRM_NEVER_SET, -EIO, - "STATUS_GRAPHICS_OPM_HDCP_SRM_NEVER_SET"}, - {STATUS_GRAPHICS_OPM_RESOLUTION_TOO_HIGH, -EIO, - "STATUS_GRAPHICS_OPM_RESOLUTION_TOO_HIGH"}, - {STATUS_GRAPHICS_OPM_ALL_HDCP_HARDWARE_ALREADY_IN_USE, -EIO, - "STATUS_GRAPHICS_OPM_ALL_HDCP_HARDWARE_ALREADY_IN_USE"}, - {STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_NO_LONGER_EXISTS, -EIO, - "STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_NO_LONGER_EXISTS"}, - {STATUS_GRAPHICS_OPM_SESSION_TYPE_CHANGE_IN_PROGRESS, -EIO, - "STATUS_GRAPHICS_OPM_SESSION_TYPE_CHANGE_IN_PROGRESS"}, - {STATUS_GRAPHICS_I2C_NOT_SUPPORTED, -EIO, - "STATUS_GRAPHICS_I2C_NOT_SUPPORTED"}, - {STATUS_GRAPHICS_I2C_DEVICE_DOES_NOT_EXIST, -EIO, - "STATUS_GRAPHICS_I2C_DEVICE_DOES_NOT_EXIST"}, - {STATUS_GRAPHICS_I2C_ERROR_TRANSMITTING_DATA, -EIO, - "STATUS_GRAPHICS_I2C_ERROR_TRANSMITTING_DATA"}, - {STATUS_GRAPHICS_I2C_ERROR_RECEIVING_DATA, -EIO, - "STATUS_GRAPHICS_I2C_ERROR_RECEIVING_DATA"}, - {STATUS_GRAPHICS_DDCCI_VCP_NOT_SUPPORTED, -EIO, - "STATUS_GRAPHICS_DDCCI_VCP_NOT_SUPPORTED"}, - {STATUS_GRAPHICS_DDCCI_INVALID_DATA, -EIO, - "STATUS_GRAPHICS_DDCCI_INVALID_DATA"}, - {STATUS_GRAPHICS_DDCCI_MONITOR_RETURNED_INVALID_TIMING_STATUS_BYTE, - -EIO, - "STATUS_GRAPHICS_DDCCI_MONITOR_RETURNED_INVALID_TIMING_STATUS_BYTE"}, - {STATUS_GRAPHICS_DDCCI_INVALID_CAPABILITIES_STRING, -EIO, - "STATUS_GRAPHICS_DDCCI_INVALID_CAPABILITIES_STRING"}, - {STATUS_GRAPHICS_MCA_INTERNAL_ERROR, -EIO, - "STATUS_GRAPHICS_MCA_INTERNAL_ERROR"}, - {STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_COMMAND, -EIO, - "STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_COMMAND"}, - {STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_LENGTH, -EIO, - "STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_LENGTH"}, - {STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_CHECKSUM, -EIO, - "STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_CHECKSUM"}, - {STATUS_GRAPHICS_INVALID_PHYSICAL_MONITOR_HANDLE, -EIO, - "STATUS_GRAPHICS_INVALID_PHYSICAL_MONITOR_HANDLE"}, - {STATUS_GRAPHICS_MONITOR_NO_LONGER_EXISTS, -EIO, - "STATUS_GRAPHICS_MONITOR_NO_LONGER_EXISTS"}, - {STATUS_GRAPHICS_ONLY_CONSOLE_SESSION_SUPPORTED, -EIO, - "STATUS_GRAPHICS_ONLY_CONSOLE_SESSION_SUPPORTED"}, - {STATUS_GRAPHICS_NO_DISPLAY_DEVICE_CORRESPONDS_TO_NAME, -EIO, - "STATUS_GRAPHICS_NO_DISPLAY_DEVICE_CORRESPONDS_TO_NAME"}, - {STATUS_GRAPHICS_DISPLAY_DEVICE_NOT_ATTACHED_TO_DESKTOP, -EIO, - "STATUS_GRAPHICS_DISPLAY_DEVICE_NOT_ATTACHED_TO_DESKTOP"}, - {STATUS_GRAPHICS_MIRRORING_DEVICES_NOT_SUPPORTED, -EIO, - "STATUS_GRAPHICS_MIRRORING_DEVICES_NOT_SUPPORTED"}, - {STATUS_GRAPHICS_INVALID_POINTER, -EIO, - "STATUS_GRAPHICS_INVALID_POINTER"}, - {STATUS_GRAPHICS_NO_MONITORS_CORRESPOND_TO_DISPLAY_DEVICE, -EIO, - "STATUS_GRAPHICS_NO_MONITORS_CORRESPOND_TO_DISPLAY_DEVICE"}, - {STATUS_GRAPHICS_PARAMETER_ARRAY_TOO_SMALL, -EIO, - "STATUS_GRAPHICS_PARAMETER_ARRAY_TOO_SMALL"}, - {STATUS_GRAPHICS_INTERNAL_ERROR, -EIO, - "STATUS_GRAPHICS_INTERNAL_ERROR"}, - {STATUS_GRAPHICS_SESSION_TYPE_CHANGE_IN_PROGRESS, -EIO, - "STATUS_GRAPHICS_SESSION_TYPE_CHANGE_IN_PROGRESS"}, - {STATUS_FVE_LOCKED_VOLUME, -EIO, "STATUS_FVE_LOCKED_VOLUME"}, - {STATUS_FVE_NOT_ENCRYPTED, -EIO, "STATUS_FVE_NOT_ENCRYPTED"}, - {STATUS_FVE_BAD_INFORMATION, -EIO, "STATUS_FVE_BAD_INFORMATION"}, - {STATUS_FVE_TOO_SMALL, -EIO, "STATUS_FVE_TOO_SMALL"}, - {STATUS_FVE_FAILED_WRONG_FS, -EIO, "STATUS_FVE_FAILED_WRONG_FS"}, - {STATUS_FVE_FAILED_BAD_FS, -EIO, "STATUS_FVE_FAILED_BAD_FS"}, - {STATUS_FVE_FS_NOT_EXTENDED, -EIO, "STATUS_FVE_FS_NOT_EXTENDED"}, - {STATUS_FVE_FS_MOUNTED, -EIO, "STATUS_FVE_FS_MOUNTED"}, - {STATUS_FVE_NO_LICENSE, -EIO, "STATUS_FVE_NO_LICENSE"}, - {STATUS_FVE_ACTION_NOT_ALLOWED, -EIO, "STATUS_FVE_ACTION_NOT_ALLOWED"}, - {STATUS_FVE_BAD_DATA, -EIO, "STATUS_FVE_BAD_DATA"}, - {STATUS_FVE_VOLUME_NOT_BOUND, -EIO, "STATUS_FVE_VOLUME_NOT_BOUND"}, - {STATUS_FVE_NOT_DATA_VOLUME, -EIO, "STATUS_FVE_NOT_DATA_VOLUME"}, - {STATUS_FVE_CONV_READ_ERROR, -EIO, "STATUS_FVE_CONV_READ_ERROR"}, - {STATUS_FVE_CONV_WRITE_ERROR, -EIO, "STATUS_FVE_CONV_WRITE_ERROR"}, - {STATUS_FVE_OVERLAPPED_UPDATE, -EIO, "STATUS_FVE_OVERLAPPED_UPDATE"}, - {STATUS_FVE_FAILED_SECTOR_SIZE, -EIO, "STATUS_FVE_FAILED_SECTOR_SIZE"}, - {STATUS_FVE_FAILED_AUTHENTICATION, -EIO, - "STATUS_FVE_FAILED_AUTHENTICATION"}, - {STATUS_FVE_NOT_OS_VOLUME, -EIO, "STATUS_FVE_NOT_OS_VOLUME"}, - {STATUS_FVE_KEYFILE_NOT_FOUND, -EIO, "STATUS_FVE_KEYFILE_NOT_FOUND"}, - {STATUS_FVE_KEYFILE_INVALID, -EIO, "STATUS_FVE_KEYFILE_INVALID"}, - {STATUS_FVE_KEYFILE_NO_VMK, -EIO, "STATUS_FVE_KEYFILE_NO_VMK"}, - {STATUS_FVE_TPM_DISABLED, -EIO, "STATUS_FVE_TPM_DISABLED"}, - {STATUS_FVE_TPM_SRK_AUTH_NOT_ZERO, -EIO, - "STATUS_FVE_TPM_SRK_AUTH_NOT_ZERO"}, - {STATUS_FVE_TPM_INVALID_PCR, -EIO, "STATUS_FVE_TPM_INVALID_PCR"}, - {STATUS_FVE_TPM_NO_VMK, -EIO, "STATUS_FVE_TPM_NO_VMK"}, - {STATUS_FVE_PIN_INVALID, -EIO, "STATUS_FVE_PIN_INVALID"}, - {STATUS_FVE_AUTH_INVALID_APPLICATION, -EIO, - "STATUS_FVE_AUTH_INVALID_APPLICATION"}, - {STATUS_FVE_AUTH_INVALID_CONFIG, -EIO, - "STATUS_FVE_AUTH_INVALID_CONFIG"}, - {STATUS_FVE_DEBUGGER_ENABLED, -EIO, "STATUS_FVE_DEBUGGER_ENABLED"}, - {STATUS_FVE_DRY_RUN_FAILED, -EIO, "STATUS_FVE_DRY_RUN_FAILED"}, - {STATUS_FVE_BAD_METADATA_POINTER, -EIO, - "STATUS_FVE_BAD_METADATA_POINTER"}, - {STATUS_FVE_OLD_METADATA_COPY, -EIO, "STATUS_FVE_OLD_METADATA_COPY"}, - {STATUS_FVE_REBOOT_REQUIRED, -EIO, "STATUS_FVE_REBOOT_REQUIRED"}, - {STATUS_FVE_RAW_ACCESS, -EIO, "STATUS_FVE_RAW_ACCESS"}, - {STATUS_FVE_RAW_BLOCKED, -EIO, "STATUS_FVE_RAW_BLOCKED"}, - {STATUS_FWP_CALLOUT_NOT_FOUND, -EIO, "STATUS_FWP_CALLOUT_NOT_FOUND"}, - {STATUS_FWP_CONDITION_NOT_FOUND, -EIO, - "STATUS_FWP_CONDITION_NOT_FOUND"}, - {STATUS_FWP_FILTER_NOT_FOUND, -EIO, "STATUS_FWP_FILTER_NOT_FOUND"}, - {STATUS_FWP_LAYER_NOT_FOUND, -EIO, "STATUS_FWP_LAYER_NOT_FOUND"}, - {STATUS_FWP_PROVIDER_NOT_FOUND, -EIO, "STATUS_FWP_PROVIDER_NOT_FOUND"}, - {STATUS_FWP_PROVIDER_CONTEXT_NOT_FOUND, -EIO, - "STATUS_FWP_PROVIDER_CONTEXT_NOT_FOUND"}, - {STATUS_FWP_SUBLAYER_NOT_FOUND, -EIO, "STATUS_FWP_SUBLAYER_NOT_FOUND"}, - {STATUS_FWP_NOT_FOUND, -EIO, "STATUS_FWP_NOT_FOUND"}, - {STATUS_FWP_ALREADY_EXISTS, -EIO, "STATUS_FWP_ALREADY_EXISTS"}, - {STATUS_FWP_IN_USE, -EIO, "STATUS_FWP_IN_USE"}, - {STATUS_FWP_DYNAMIC_SESSION_IN_PROGRESS, -EIO, - "STATUS_FWP_DYNAMIC_SESSION_IN_PROGRESS"}, - {STATUS_FWP_WRONG_SESSION, -EIO, "STATUS_FWP_WRONG_SESSION"}, - {STATUS_FWP_NO_TXN_IN_PROGRESS, -EIO, "STATUS_FWP_NO_TXN_IN_PROGRESS"}, - {STATUS_FWP_TXN_IN_PROGRESS, -EIO, "STATUS_FWP_TXN_IN_PROGRESS"}, - {STATUS_FWP_TXN_ABORTED, -EIO, "STATUS_FWP_TXN_ABORTED"}, - {STATUS_FWP_SESSION_ABORTED, -EIO, "STATUS_FWP_SESSION_ABORTED"}, - {STATUS_FWP_INCOMPATIBLE_TXN, -EIO, "STATUS_FWP_INCOMPATIBLE_TXN"}, - {STATUS_FWP_TIMEOUT, -ETIMEDOUT, "STATUS_FWP_TIMEOUT"}, - {STATUS_FWP_NET_EVENTS_DISABLED, -EIO, - "STATUS_FWP_NET_EVENTS_DISABLED"}, - {STATUS_FWP_INCOMPATIBLE_LAYER, -EIO, "STATUS_FWP_INCOMPATIBLE_LAYER"}, - {STATUS_FWP_KM_CLIENTS_ONLY, -EIO, "STATUS_FWP_KM_CLIENTS_ONLY"}, - {STATUS_FWP_LIFETIME_MISMATCH, -EIO, "STATUS_FWP_LIFETIME_MISMATCH"}, - {STATUS_FWP_BUILTIN_OBJECT, -EIO, "STATUS_FWP_BUILTIN_OBJECT"}, - {STATUS_FWP_TOO_MANY_BOOTTIME_FILTERS, -EIO, - "STATUS_FWP_TOO_MANY_BOOTTIME_FILTERS or STATUS_FWP_TOO_MANY_CALLOUTS"}, - {STATUS_FWP_TOO_MANY_CALLOUTS, -EIO, - "STATUS_FWP_TOO_MANY_BOOTTIME_FILTERS or STATUS_FWP_TOO_MANY_CALLOUTS"}, - {STATUS_FWP_NOTIFICATION_DROPPED, -EIO, - "STATUS_FWP_NOTIFICATION_DROPPED"}, - {STATUS_FWP_TRAFFIC_MISMATCH, -EIO, "STATUS_FWP_TRAFFIC_MISMATCH"}, - {STATUS_FWP_INCOMPATIBLE_SA_STATE, -EIO, - "STATUS_FWP_INCOMPATIBLE_SA_STATE"}, - {STATUS_FWP_NULL_POINTER, -EIO, "STATUS_FWP_NULL_POINTER"}, - {STATUS_FWP_INVALID_ENUMERATOR, -EIO, "STATUS_FWP_INVALID_ENUMERATOR"}, - {STATUS_FWP_INVALID_FLAGS, -EIO, "STATUS_FWP_INVALID_FLAGS"}, - {STATUS_FWP_INVALID_NET_MASK, -EIO, "STATUS_FWP_INVALID_NET_MASK"}, - {STATUS_FWP_INVALID_RANGE, -EIO, "STATUS_FWP_INVALID_RANGE"}, - {STATUS_FWP_INVALID_INTERVAL, -EIO, "STATUS_FWP_INVALID_INTERVAL"}, - {STATUS_FWP_ZERO_LENGTH_ARRAY, -EIO, "STATUS_FWP_ZERO_LENGTH_ARRAY"}, - {STATUS_FWP_NULL_DISPLAY_NAME, -EIO, "STATUS_FWP_NULL_DISPLAY_NAME"}, - {STATUS_FWP_INVALID_ACTION_TYPE, -EIO, - "STATUS_FWP_INVALID_ACTION_TYPE"}, - {STATUS_FWP_INVALID_WEIGHT, -EIO, "STATUS_FWP_INVALID_WEIGHT"}, - {STATUS_FWP_MATCH_TYPE_MISMATCH, -EIO, - "STATUS_FWP_MATCH_TYPE_MISMATCH"}, - {STATUS_FWP_TYPE_MISMATCH, -EIO, "STATUS_FWP_TYPE_MISMATCH"}, - {STATUS_FWP_OUT_OF_BOUNDS, -EIO, "STATUS_FWP_OUT_OF_BOUNDS"}, - {STATUS_FWP_RESERVED, -EIO, "STATUS_FWP_RESERVED"}, - {STATUS_FWP_DUPLICATE_CONDITION, -EIO, - "STATUS_FWP_DUPLICATE_CONDITION"}, - {STATUS_FWP_DUPLICATE_KEYMOD, -EIO, "STATUS_FWP_DUPLICATE_KEYMOD"}, - {STATUS_FWP_ACTION_INCOMPATIBLE_WITH_LAYER, -EIO, - "STATUS_FWP_ACTION_INCOMPATIBLE_WITH_LAYER"}, - {STATUS_FWP_ACTION_INCOMPATIBLE_WITH_SUBLAYER, -EIO, - "STATUS_FWP_ACTION_INCOMPATIBLE_WITH_SUBLAYER"}, - {STATUS_FWP_CONTEXT_INCOMPATIBLE_WITH_LAYER, -EIO, - "STATUS_FWP_CONTEXT_INCOMPATIBLE_WITH_LAYER"}, - {STATUS_FWP_CONTEXT_INCOMPATIBLE_WITH_CALLOUT, -EIO, - "STATUS_FWP_CONTEXT_INCOMPATIBLE_WITH_CALLOUT"}, - {STATUS_FWP_INCOMPATIBLE_AUTH_METHOD, -EIO, - "STATUS_FWP_INCOMPATIBLE_AUTH_METHOD"}, - {STATUS_FWP_INCOMPATIBLE_DH_GROUP, -EIO, - "STATUS_FWP_INCOMPATIBLE_DH_GROUP"}, - {STATUS_FWP_EM_NOT_SUPPORTED, -EOPNOTSUPP, - "STATUS_FWP_EM_NOT_SUPPORTED"}, - {STATUS_FWP_NEVER_MATCH, -EIO, "STATUS_FWP_NEVER_MATCH"}, - {STATUS_FWP_PROVIDER_CONTEXT_MISMATCH, -EIO, - "STATUS_FWP_PROVIDER_CONTEXT_MISMATCH"}, - {STATUS_FWP_INVALID_PARAMETER, -EIO, "STATUS_FWP_INVALID_PARAMETER"}, - {STATUS_FWP_TOO_MANY_SUBLAYERS, -EIO, "STATUS_FWP_TOO_MANY_SUBLAYERS"}, - {STATUS_FWP_CALLOUT_NOTIFICATION_FAILED, -EIO, - "STATUS_FWP_CALLOUT_NOTIFICATION_FAILED"}, - {STATUS_FWP_INCOMPATIBLE_AUTH_CONFIG, -EIO, - "STATUS_FWP_INCOMPATIBLE_AUTH_CONFIG"}, - {STATUS_FWP_INCOMPATIBLE_CIPHER_CONFIG, -EIO, - "STATUS_FWP_INCOMPATIBLE_CIPHER_CONFIG"}, - {STATUS_FWP_TCPIP_NOT_READY, -EIO, "STATUS_FWP_TCPIP_NOT_READY"}, - {STATUS_FWP_INJECT_HANDLE_CLOSING, -EIO, - "STATUS_FWP_INJECT_HANDLE_CLOSING"}, - {STATUS_FWP_INJECT_HANDLE_STALE, -EIO, - "STATUS_FWP_INJECT_HANDLE_STALE"}, - {STATUS_FWP_CANNOT_PEND, -EIO, "STATUS_FWP_CANNOT_PEND"}, - {STATUS_NDIS_CLOSING, -EIO, "STATUS_NDIS_CLOSING"}, - {STATUS_NDIS_BAD_VERSION, -EIO, "STATUS_NDIS_BAD_VERSION"}, - {STATUS_NDIS_BAD_CHARACTERISTICS, -EIO, - "STATUS_NDIS_BAD_CHARACTERISTICS"}, - {STATUS_NDIS_ADAPTER_NOT_FOUND, -EIO, "STATUS_NDIS_ADAPTER_NOT_FOUND"}, - {STATUS_NDIS_OPEN_FAILED, -EIO, "STATUS_NDIS_OPEN_FAILED"}, - {STATUS_NDIS_DEVICE_FAILED, -EIO, "STATUS_NDIS_DEVICE_FAILED"}, - {STATUS_NDIS_MULTICAST_FULL, -EIO, "STATUS_NDIS_MULTICAST_FULL"}, - {STATUS_NDIS_MULTICAST_EXISTS, -EIO, "STATUS_NDIS_MULTICAST_EXISTS"}, - {STATUS_NDIS_MULTICAST_NOT_FOUND, -EIO, - "STATUS_NDIS_MULTICAST_NOT_FOUND"}, - {STATUS_NDIS_REQUEST_ABORTED, -EIO, "STATUS_NDIS_REQUEST_ABORTED"}, - {STATUS_NDIS_RESET_IN_PROGRESS, -EIO, "STATUS_NDIS_RESET_IN_PROGRESS"}, - {STATUS_NDIS_INVALID_PACKET, -EIO, "STATUS_NDIS_INVALID_PACKET"}, - {STATUS_NDIS_INVALID_DEVICE_REQUEST, -EIO, - "STATUS_NDIS_INVALID_DEVICE_REQUEST"}, - {STATUS_NDIS_ADAPTER_NOT_READY, -EIO, "STATUS_NDIS_ADAPTER_NOT_READY"}, - {STATUS_NDIS_INVALID_LENGTH, -EIO, "STATUS_NDIS_INVALID_LENGTH"}, - {STATUS_NDIS_INVALID_DATA, -EIO, "STATUS_NDIS_INVALID_DATA"}, - {STATUS_NDIS_BUFFER_TOO_SHORT, -ENOBUFS, - "STATUS_NDIS_BUFFER_TOO_SHORT"}, - {STATUS_NDIS_INVALID_OID, -EIO, "STATUS_NDIS_INVALID_OID"}, - {STATUS_NDIS_ADAPTER_REMOVED, -EIO, "STATUS_NDIS_ADAPTER_REMOVED"}, - {STATUS_NDIS_UNSUPPORTED_MEDIA, -EIO, "STATUS_NDIS_UNSUPPORTED_MEDIA"}, - {STATUS_NDIS_GROUP_ADDRESS_IN_USE, -EIO, - "STATUS_NDIS_GROUP_ADDRESS_IN_USE"}, - {STATUS_NDIS_FILE_NOT_FOUND, -EIO, "STATUS_NDIS_FILE_NOT_FOUND"}, - {STATUS_NDIS_ERROR_READING_FILE, -EIO, - "STATUS_NDIS_ERROR_READING_FILE"}, - {STATUS_NDIS_ALREADY_MAPPED, -EIO, "STATUS_NDIS_ALREADY_MAPPED"}, - {STATUS_NDIS_RESOURCE_CONFLICT, -EIO, "STATUS_NDIS_RESOURCE_CONFLICT"}, - {STATUS_NDIS_MEDIA_DISCONNECTED, -EIO, - "STATUS_NDIS_MEDIA_DISCONNECTED"}, - {STATUS_NDIS_INVALID_ADDRESS, -EIO, "STATUS_NDIS_INVALID_ADDRESS"}, - {STATUS_NDIS_PAUSED, -EIO, "STATUS_NDIS_PAUSED"}, - {STATUS_NDIS_INTERFACE_NOT_FOUND, -EIO, - "STATUS_NDIS_INTERFACE_NOT_FOUND"}, - {STATUS_NDIS_UNSUPPORTED_REVISION, -EIO, - "STATUS_NDIS_UNSUPPORTED_REVISION"}, - {STATUS_NDIS_INVALID_PORT, -EIO, "STATUS_NDIS_INVALID_PORT"}, - {STATUS_NDIS_INVALID_PORT_STATE, -EIO, - "STATUS_NDIS_INVALID_PORT_STATE"}, - {STATUS_NDIS_LOW_POWER_STATE, -EIO, "STATUS_NDIS_LOW_POWER_STATE"}, - {STATUS_NDIS_NOT_SUPPORTED, -ENOSYS, "STATUS_NDIS_NOT_SUPPORTED"}, - {STATUS_NDIS_DOT11_AUTO_CONFIG_ENABLED, -EIO, - "STATUS_NDIS_DOT11_AUTO_CONFIG_ENABLED"}, - {STATUS_NDIS_DOT11_MEDIA_IN_USE, -EIO, - "STATUS_NDIS_DOT11_MEDIA_IN_USE"}, - {STATUS_NDIS_DOT11_POWER_STATE_INVALID, -EIO, - "STATUS_NDIS_DOT11_POWER_STATE_INVALID"}, - {STATUS_IPSEC_BAD_SPI, -EIO, "STATUS_IPSEC_BAD_SPI"}, - {STATUS_IPSEC_SA_LIFETIME_EXPIRED, -EIO, - "STATUS_IPSEC_SA_LIFETIME_EXPIRED"}, - {STATUS_IPSEC_WRONG_SA, -EIO, "STATUS_IPSEC_WRONG_SA"}, - {STATUS_IPSEC_REPLAY_CHECK_FAILED, -EIO, - "STATUS_IPSEC_REPLAY_CHECK_FAILED"}, - {STATUS_IPSEC_INVALID_PACKET, -EIO, "STATUS_IPSEC_INVALID_PACKET"}, - {STATUS_IPSEC_INTEGRITY_CHECK_FAILED, -EIO, - "STATUS_IPSEC_INTEGRITY_CHECK_FAILED"}, - {STATUS_IPSEC_CLEAR_TEXT_DROP, -EIO, "STATUS_IPSEC_CLEAR_TEXT_DROP"}, - {STATUS_SMB_NO_PREAUTH_INTEGRITY_HASH_OVERLAP, -EIO, - "STATUS_SMB_NO_PREAUTH_INTEGRITY_HASH_OVERLAP"}, +/* + * Automatically generated by the `gen_smb2_mapping` script, + * sorted by NT status code (cpu-endian, ascending) + */ +#include "smb2_mapping_table.c" }; +static __always_inline int cmp_smb2_status(const void *_key, const void *_pivot) +{ + __u32 key = *(__u32 *)_key; + const struct status_to_posix_error *pivot = _pivot; + + if (key < pivot->smb2_status) + return -1; + if (key > pivot->smb2_status) + return 1; + return 0; +} + +static const struct status_to_posix_error *smb2_get_err_map(__u32 smb2_status) +{ + const struct status_to_posix_error *err_map; + + err_map = __inline_bsearch(&smb2_status, smb2_error_map_table, + ARRAY_SIZE(smb2_error_map_table), + sizeof(struct status_to_posix_error), + cmp_smb2_status); + return err_map; +} + int map_smb2_to_linux_error(char *buf, bool log_err) { struct smb2_hdr *shdr = (struct smb2_hdr *)buf; - unsigned int i; int rc = -EIO; __le32 smb2err = shdr->Status; + const struct status_to_posix_error *err_map; if (smb2err == 0) { trace_smb3_cmd_done(le32_to_cpu(shdr->Id.SyncId.TreeId), @@ -2440,21 +73,20 @@ map_smb2_to_linux_error(char *buf, bool log_err) (smb2err != STATUS_END_OF_FILE)) || (cifsFYI & CIFS_RC); - for (i = 0; i < sizeof(smb2_error_map_table) / - sizeof(struct status_to_posix_error); i++) { - if (smb2_error_map_table[i].smb2_status == smb2err) { - if (log_err) - pr_notice("Status code returned 0x%08x %s\n", smb2err, - smb2_error_map_table[i].status_string); - rc = smb2_error_map_table[i].posix_error; - break; - } - } + err_map = smb2_get_err_map(le32_to_cpu(smb2err)); + if (!err_map) + goto out; + rc = err_map->posix_error; + if (log_err) + pr_notice("Status code returned 0x%08x %s\n", + err_map->smb2_status, err_map->status_string); + +out: /* on error mapping not found - return EIO */ cifs_dbg(FYI, "Mapping SMB2 status code 0x%08x to POSIX err %d\n", - __le32_to_cpu(smb2err), rc); + le32_to_cpu(smb2err), rc); trace_smb3_cmd_err(le32_to_cpu(shdr->Id.SyncId.TreeId), le64_to_cpu(shdr->SessionId), @@ -2465,3 +97,28 @@ map_smb2_to_linux_error(char *buf, bool log_err) smb_EIO1(smb_eio_trace_smb2_received_error, le32_to_cpu(smb2err)); return rc; } + +int __init smb2_init_maperror(void) +{ + unsigned int i; + + /* Check whether the array is sorted in ascending order */ + for (i = 1; i < ARRAY_SIZE(smb2_error_map_table); i++) { + if (smb2_error_map_table[i].smb2_status >= + smb2_error_map_table[i - 1].smb2_status) + continue; + + pr_err("smb2_error_map_table array order is incorrect\n"); + return -EINVAL; + } + + return 0; +} + +#define SMB_CLIENT_KUNIT_AVAILABLE \ + ((IS_MODULE(CONFIG_CIFS) && IS_ENABLED(CONFIG_KUNIT)) || \ + (IS_BUILTIN(CONFIG_CIFS) && IS_BUILTIN(CONFIG_KUNIT))) + +#if SMB_CLIENT_KUNIT_AVAILABLE && IS_ENABLED(CONFIG_SMB_KUNIT_TESTS) +#include "smb2maperror_test.c" +#endif /* CONFIG_SMB_KUNIT_TESTS */ diff --git a/fs/smb/client/smb2maperror_test.c b/fs/smb/client/smb2maperror_test.c new file mode 100644 index 000000000000..38ea6b846a99 --- /dev/null +++ b/fs/smb/client/smb2maperror_test.c @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: LGPL-2.1 +/* + * + * KUnit tests of SMB2 maperror + * + * Copyright (C) 2025 KylinSoft Co., Ltd. All rights reserved. + * Author(s): ChenXiaoSong <chenxiaosong@kylinos.cn> + * + */ + +#include <kunit/test.h> + +static void +test_cmp_map(struct kunit *test, const struct status_to_posix_error *expect) +{ + const struct status_to_posix_error *result; + + result = smb2_get_err_map(expect->smb2_status); + KUNIT_EXPECT_PTR_NE(test, NULL, result); + KUNIT_EXPECT_EQ(test, expect->smb2_status, result->smb2_status); + KUNIT_EXPECT_EQ(test, expect->posix_error, result->posix_error); + KUNIT_EXPECT_STREQ(test, expect->status_string, result->status_string); +} + +static void maperror_test_check_search(struct kunit *test) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(smb2_error_map_table); i++) + test_cmp_map(test, &smb2_error_map_table[i]); +} + +static struct kunit_case maperror_test_cases[] = { + KUNIT_CASE(maperror_test_check_search), + {} +}; + +static struct kunit_suite maperror_suite = { + .name = "smb2_maperror", + .test_cases = maperror_test_cases, +}; + +kunit_test_suite(maperror_suite); + +MODULE_LICENSE("GPL"); diff --git a/fs/smb/client/smb2misc.c b/fs/smb/client/smb2misc.c index f3cb62d91450..0871b9f1f86a 100644 --- a/fs/smb/client/smb2misc.c +++ b/fs/smb/client/smb2misc.c @@ -820,14 +820,14 @@ smb2_handle_cancelled_close(struct cifs_tcon *tcon, __u64 persistent_fid, int rc; cifs_dbg(FYI, "%s: tc_count=%d\n", __func__, tcon->tc_count); - spin_lock(&cifs_tcp_ses_lock); + spin_lock(&tcon->tc_lock); if (tcon->tc_count <= 0) { struct TCP_Server_Info *server = NULL; trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count, netfs_trace_tcon_ref_see_cancelled_close); WARN_ONCE(tcon->tc_count < 0, "tcon refcount is negative"); - spin_unlock(&cifs_tcp_ses_lock); + spin_unlock(&tcon->tc_lock); if (tcon->ses) { server = tcon->ses->server; @@ -841,7 +841,7 @@ smb2_handle_cancelled_close(struct cifs_tcon *tcon, __u64 persistent_fid, tcon->tc_count++; trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count, netfs_trace_tcon_ref_get_cancelled_close); - spin_unlock(&cifs_tcp_ses_lock); + spin_unlock(&tcon->tc_lock); rc = __smb2_handle_cancelled_cmd(tcon, SMB2_CLOSE_HE, 0, persistent_fid, volatile_fid); diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c index c1aaf77e187b..262df6d2c2c8 100644 --- a/fs/smb/client/smb2ops.c +++ b/fs/smb/client/smb2ops.c @@ -637,13 +637,6 @@ parse_server_interfaces(struct network_interface_info_ioctl_rsp *buf, p = buf; spin_lock(&ses->iface_lock); - /* do not query too frequently, this time with lock held */ - if (ses->iface_last_update && - time_before(jiffies, ses->iface_last_update + - (SMB_INTERFACE_POLL_INTERVAL * HZ))) { - spin_unlock(&ses->iface_lock); - return 0; - } /* * Go through iface_list and mark them as inactive @@ -666,7 +659,6 @@ parse_server_interfaces(struct network_interface_info_ioctl_rsp *buf, "Empty network interface list returned by server %s\n", ses->server->hostname); rc = -EOPNOTSUPP; - ses->iface_last_update = jiffies; goto out; } @@ -795,8 +787,6 @@ next_iface: + sizeof(p->Next) && p->Next)) cifs_dbg(VFS, "%s: incomplete interface info\n", __func__); - ses->iface_last_update = jiffies; - out: /* * Go through the list again and put the inactive entries @@ -825,10 +815,17 @@ SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon, bool in_ struct TCP_Server_Info *pserver; /* do not query too frequently */ + spin_lock(&ses->iface_lock); if (ses->iface_last_update && time_before(jiffies, ses->iface_last_update + - (SMB_INTERFACE_POLL_INTERVAL * HZ))) + (SMB_INTERFACE_POLL_INTERVAL * HZ))) { + spin_unlock(&ses->iface_lock); return 0; + } + + ses->iface_last_update = jiffies; + + spin_unlock(&ses->iface_lock); rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID, FSCTL_QUERY_NETWORK_INTERFACE_INFO, @@ -1184,7 +1181,7 @@ smb2_set_ea(const unsigned int xid, struct cifs_tcon *tcon, struct smb2_file_full_ea_info *ea; struct smb2_query_info_rsp *rsp; int rc, used_len = 0; - int retries = 0, cur_sleep = 1; + int retries = 0, cur_sleep = 0; replay_again: /* reinitialize for possible replay */ @@ -1314,6 +1311,9 @@ replay_again: smb2_set_related(&rqst[2]); if (retries) { + /* Back-off before retry */ + if (cur_sleep) + msleep(cur_sleep); smb2_set_replay(server, &rqst[0]); smb2_set_replay(server, &rqst[1]); smb2_set_replay(server, &rqst[2]); @@ -1582,7 +1582,7 @@ smb2_ioctl_query_info(const unsigned int xid, void *data[2]; int create_options = is_dir ? CREATE_NOT_FILE : CREATE_NOT_DIR; void (*free_req1_func)(struct smb_rqst *r); - int retries = 0, cur_sleep = 1; + int retries = 0, cur_sleep = 0; replay_again: /* reinitialize for possible replay */ @@ -1731,6 +1731,9 @@ replay_again: smb2_set_related(&rqst[2]); if (retries) { + /* Back-off before retry */ + if (cur_sleep) + msleep(cur_sleep); smb2_set_replay(server, &rqst[0]); smb2_set_replay(server, &rqst[1]); smb2_set_replay(server, &rqst[2]); @@ -2446,7 +2449,7 @@ smb2_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon, struct smb2_query_directory_rsp *qd_rsp = NULL; struct smb2_create_rsp *op_rsp = NULL; struct TCP_Server_Info *server; - int retries = 0, cur_sleep = 1; + int retries = 0, cur_sleep = 0; replay_again: /* reinitialize for possible replay */ @@ -2504,6 +2507,9 @@ replay_again: smb2_set_related(&rqst[1]); if (retries) { + /* Back-off before retry */ + if (cur_sleep) + msleep(cur_sleep); smb2_set_replay(server, &rqst[0]); smb2_set_replay(server, &rqst[1]); } @@ -2780,10 +2786,14 @@ bool smb2_should_replay(struct cifs_tcon *tcon, return false; if (tcon->retry || (*pretries)++ < tcon->ses->server->retrans) { - msleep(*pcur_sleep); - (*pcur_sleep) = ((*pcur_sleep) << 1); - if ((*pcur_sleep) > CIFS_MAX_SLEEP) - (*pcur_sleep) = CIFS_MAX_SLEEP; + /* Update sleep time for exponential backoff */ + if (!(*pcur_sleep)) + (*pcur_sleep) = 1; + else { + (*pcur_sleep) = ((*pcur_sleep) << 1); + if ((*pcur_sleep) > CIFS_MAX_SLEEP) + (*pcur_sleep) = CIFS_MAX_SLEEP; + } return true; } @@ -2814,7 +2824,7 @@ smb2_query_info_compound(const unsigned int xid, struct cifs_tcon *tcon, int rc; __le16 *utf16_path; struct cached_fid *cfid; - int retries = 0, cur_sleep = 1; + int retries = 0, cur_sleep = 0; replay_again: /* reinitialize for possible replay */ @@ -2904,6 +2914,9 @@ replay_again: smb2_set_related(&rqst[2]); if (retries) { + /* Back-off before retry */ + if (cur_sleep) + msleep(cur_sleep); if (!cfid) { smb2_set_replay(server, &rqst[0]); smb2_set_replay(server, &rqst[2]); @@ -3091,7 +3104,9 @@ smb2_get_dfs_refer(const unsigned int xid, struct cifs_ses *ses, struct cifs_tcon, tcon_list); if (tcon) { + spin_lock(&tcon->tc_lock); tcon->tc_count++; + spin_unlock(&tcon->tc_lock); trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count, netfs_trace_tcon_ref_get_dfs_refer); } @@ -3160,13 +3175,9 @@ smb2_get_dfs_refer(const unsigned int xid, struct cifs_ses *ses, out: if (tcon && !tcon->ipc) { /* ipc tcons are not refcounted */ - spin_lock(&cifs_tcp_ses_lock); - tcon->tc_count--; + cifs_put_tcon(tcon, netfs_trace_tcon_ref_put_dfs_refer); trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count, netfs_trace_tcon_ref_dec_dfs_refer); - /* tc_count can never go negative */ - WARN_ON(tcon->tc_count < 0); - spin_unlock(&cifs_tcp_ses_lock); } kfree(utf16_path); kfree(dfs_req); diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c index 5d57c895ca37..4602b4dfe832 100644 --- a/fs/smb/client/smb2pdu.c +++ b/fs/smb/client/smb2pdu.c @@ -32,9 +32,9 @@ #include "cifs_unicode.h" #include "cifs_debug.h" #include "ntlmssp.h" +#include "../common/smbfsctl.h" #include "../common/smb2status.h" #include "smb2glob.h" -#include "cifspdu.h" #include "cifs_spnego.h" #include "../common/smbdirect/smbdirect.h" #include "smbdirect.h" @@ -2904,7 +2904,7 @@ int smb311_posix_mkdir(const unsigned int xid, struct inode *inode, unsigned int total_len; __le16 *utf16_path = NULL; struct TCP_Server_Info *server; - int retries = 0, cur_sleep = 1; + int retries = 0, cur_sleep = 0; replay_again: /* reinitialize for possible replay */ @@ -3016,8 +3016,12 @@ replay_again: trace_smb3_posix_mkdir_enter(xid, tcon->tid, ses->Suid, full_path, CREATE_NOT_FILE, FILE_WRITE_ATTRIBUTES); - if (retries) + if (retries) { + /* Back-off before retry */ + if (cur_sleep) + msleep(cur_sleep); smb2_set_replay(server, &rqst); + } /* resource #4: response buffer */ rc = cifs_send_recv(xid, ses, server, @@ -3265,7 +3269,7 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path, int resp_buftype = CIFS_NO_BUFFER; int rc = 0; int flags = 0; - int retries = 0, cur_sleep = 1; + int retries = 0, cur_sleep = 0; replay_again: /* reinitialize for possible replay */ @@ -3293,8 +3297,12 @@ replay_again: trace_smb3_open_enter(xid, tcon->tid, tcon->ses->Suid, oparms->path, oparms->create_options, oparms->desired_access); - if (retries) + if (retries) { + /* Back-off before retry */ + if (cur_sleep) + msleep(cur_sleep); smb2_set_replay(server, &rqst); + } rc = cifs_send_recv(xid, ses, server, &rqst, &resp_buftype, flags, @@ -3478,7 +3486,7 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, int resp_buftype = CIFS_NO_BUFFER; int rc = 0; int flags = 0; - int retries = 0, cur_sleep = 1; + int retries = 0, cur_sleep = 0; if (!tcon) return smb_EIO(smb_eio_trace_null_pointers); @@ -3518,8 +3526,12 @@ replay_again: if (rc) goto ioctl_exit; - if (retries) + if (retries) { + /* Back-off before retry */ + if (cur_sleep) + msleep(cur_sleep); smb2_set_replay(server, &rqst); + } rc = cifs_send_recv(xid, ses, server, &rqst, &resp_buftype, flags, @@ -3675,7 +3687,7 @@ __SMB2_close(const unsigned int xid, struct cifs_tcon *tcon, int rc = 0; int flags = 0; bool query_attrs = false; - int retries = 0, cur_sleep = 1; + int retries = 0, cur_sleep = 0; replay_again: /* reinitialize for possible replay */ @@ -3707,8 +3719,12 @@ replay_again: if (rc) goto close_exit; - if (retries) + if (retries) { + /* Back-off before retry */ + if (cur_sleep) + msleep(cur_sleep); smb2_set_replay(server, &rqst); + } rc = cifs_send_recv(xid, ses, server, &rqst, &resp_buftype, flags, &rsp_iov); @@ -3878,7 +3894,7 @@ query_info(const unsigned int xid, struct cifs_tcon *tcon, struct TCP_Server_Info *server; int flags = 0; bool allocated = false; - int retries = 0, cur_sleep = 1; + int retries = 0, cur_sleep = 0; cifs_dbg(FYI, "Query Info\n"); @@ -3912,8 +3928,12 @@ replay_again: trace_smb3_query_info_enter(xid, persistent_fid, tcon->tid, ses->Suid, info_class, (__u32)info_type); - if (retries) + if (retries) { + /* Back-off before retry */ + if (cur_sleep) + msleep(cur_sleep); smb2_set_replay(server, &rqst); + } rc = cifs_send_recv(xid, ses, server, &rqst, &resp_buftype, flags, &rsp_iov); @@ -4069,7 +4089,7 @@ SMB2_change_notify(const unsigned int xid, struct cifs_tcon *tcon, int resp_buftype = CIFS_NO_BUFFER; int flags = 0; int rc = 0; - int retries = 0, cur_sleep = 1; + int retries = 0, cur_sleep = 0; replay_again: /* reinitialize for possible replay */ @@ -4100,8 +4120,12 @@ replay_again: trace_smb3_notify_enter(xid, persistent_fid, tcon->tid, ses->Suid, (u8)watch_tree, completion_filter); - if (retries) + if (retries) { + /* Back-off before retry */ + if (cur_sleep) + msleep(cur_sleep); smb2_set_replay(server, &rqst); + } rc = cifs_send_recv(xid, ses, server, &rqst, &resp_buftype, flags, &rsp_iov); @@ -4239,7 +4263,9 @@ void smb2_reconnect_server(struct work_struct *work) list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { if (tcon->need_reconnect || tcon->need_reopen_files) { + spin_lock(&tcon->tc_lock); tcon->tc_count++; + spin_unlock(&tcon->tc_lock); trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count, netfs_trace_tcon_ref_get_reconnect_server); list_add_tail(&tcon->rlist, &tmp_list); @@ -4405,7 +4431,7 @@ SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, int resp_buftype = CIFS_NO_BUFFER; int flags = 0; int rc = 0; - int retries = 0, cur_sleep = 1; + int retries = 0, cur_sleep = 0; replay_again: /* reinitialize for possible replay */ @@ -4431,8 +4457,12 @@ replay_again: trace_smb3_flush_enter(xid, persistent_fid, tcon->tid, ses->Suid); - if (retries) + if (retries) { + /* Back-off before retry */ + if (cur_sleep) + msleep(cur_sleep); smb2_set_replay(server, &rqst); + } rc = cifs_send_recv(xid, ses, server, &rqst, &resp_buftype, flags, &rsp_iov); @@ -4622,9 +4652,19 @@ smb2_readv_callback(struct TCP_Server_Info *server, struct mid_q_entry *mid) iov_iter_truncate(&rqst.rq_iter, rdata->got_bytes); rc = smb2_verify_signature(&rqst, server); - if (rc) + if (rc) { cifs_tcon_dbg(VFS, "SMB signature verification returned error = %d\n", - rc); + rc); + rdata->subreq.error = rc; + rdata->result = rc; + + if (is_replayable_error(rc)) { + trace_netfs_sreq(&rdata->subreq, netfs_sreq_trace_io_retry_needed); + __set_bit(NETFS_SREQ_NEED_RETRY, &rdata->subreq.flags); + } else + trace_netfs_sreq(&rdata->subreq, netfs_sreq_trace_io_bad); + } else + trace_netfs_sreq(&rdata->subreq, netfs_sreq_trace_io_progress); } /* FIXME: should this be counted toward the initiating task? */ task_io_account_read(rdata->got_bytes); @@ -4700,6 +4740,14 @@ do_retry: if (rdata->got_bytes) __set_bit(NETFS_SREQ_MADE_PROGRESS, &rdata->subreq.flags); } + + /* see if we need to retry */ + if (is_replayable_error(rdata->result) && + smb2_should_replay(tcon, + &rdata->retries, + &rdata->cur_sleep)) + rdata->replay = true; + trace_smb3_rw_credits(rreq_debug_id, subreq_debug_index, rdata->credits.value, server->credits, server->in_flight, 0, cifs_trace_rw_credits_read_response_clear); @@ -4748,7 +4796,7 @@ smb2_async_readv(struct cifs_io_subrequest *rdata) rc = smb2_new_read_req( (void **) &buf, &total_len, &io_parms, rdata, 0, 0); if (rc) - return rc; + goto out; if (smb3_encryption_required(io_parms.tcon)) flags |= CIFS_TRANSFORM_REQ; @@ -4760,6 +4808,13 @@ smb2_async_readv(struct cifs_io_subrequest *rdata) shdr = (struct smb2_hdr *)buf; + if (rdata->replay) { + /* Back-off before retry */ + if (rdata->cur_sleep) + msleep(rdata->cur_sleep); + smb2_set_replay(server, &rqst); + } + if (rdata->credits.value > 0) { shdr->CreditCharge = cpu_to_le16(DIV_ROUND_UP(io_parms.length, SMB2_MAX_BUFFER_SIZE)); @@ -4795,6 +4850,17 @@ smb2_async_readv(struct cifs_io_subrequest *rdata) async_readv_out: cifs_small_buf_release(buf); + +out: + /* if the send error is retryable, let netfs know about it */ + if (is_replayable_error(rc) && + smb2_should_replay(tcon, + &rdata->retries, + &rdata->cur_sleep)) { + trace_netfs_sreq(&rdata->subreq, netfs_sreq_trace_io_retry_needed); + __set_bit(NETFS_SREQ_NEED_RETRY, &rdata->subreq.flags); + } + return rc; } @@ -4908,14 +4974,20 @@ smb2_writev_callback(struct TCP_Server_Info *server, struct mid_q_entry *mid) switch (mid->mid_state) { case MID_RESPONSE_RECEIVED: - trace_netfs_sreq(&wdata->subreq, netfs_sreq_trace_io_progress); credits.value = le16_to_cpu(rsp->hdr.CreditRequest); credits.instance = server->reconnect_instance; result = smb2_check_receive(mid, server, 0); if (result != 0) { - trace_netfs_sreq(&wdata->subreq, netfs_sreq_trace_io_bad); + if (is_replayable_error(result)) { + trace_netfs_sreq(&wdata->subreq, netfs_sreq_trace_io_retry_needed); + __set_bit(NETFS_SREQ_NEED_RETRY, &wdata->subreq.flags); + } else { + wdata->subreq.error = result; + trace_netfs_sreq(&wdata->subreq, netfs_sreq_trace_io_bad); + } break; } + trace_netfs_sreq(&wdata->subreq, netfs_sreq_trace_io_progress); written = le32_to_cpu(rsp->DataLength); /* @@ -4930,7 +5002,7 @@ smb2_writev_callback(struct TCP_Server_Info *server, struct mid_q_entry *mid) cifs_stats_bytes_written(tcon, written); if (written < wdata->subreq.len) { - wdata->result = -ENOSPC; + result = -ENOSPC; } else if (written > 0) { wdata->subreq.len = written; __set_bit(NETFS_SREQ_MADE_PROGRESS, &wdata->subreq.flags); @@ -4972,6 +5044,7 @@ smb2_writev_callback(struct TCP_Server_Info *server, struct mid_q_entry *mid) } #endif if (result) { + wdata->result = result; cifs_stats_fail_inc(tcon, SMB2_WRITE_HE); trace_smb3_write_err(wdata->rreq->debug_id, wdata->subreq.debug_index, @@ -4994,6 +5067,14 @@ smb2_writev_callback(struct TCP_Server_Info *server, struct mid_q_entry *mid) server->credits, server->in_flight, 0, cifs_trace_rw_credits_write_response_clear); wdata->credits.value = 0; + + /* see if we need to retry */ + if (is_replayable_error(wdata->result) && + smb2_should_replay(tcon, + &wdata->retries, + &wdata->cur_sleep)) + wdata->replay = true; + cifs_write_subrequest_terminated(wdata, result ?: written); release_mid(server, mid); trace_smb3_rw_credits(rreq_debug_id, subreq_debug_index, 0, @@ -5112,8 +5193,12 @@ smb2_async_writev(struct cifs_io_subrequest *wdata) } #endif - if (wdata->subreq.retry_count > 0) + if (wdata->replay) { + /* Back-off before retry */ + if (wdata->cur_sleep) + msleep(wdata->cur_sleep); smb2_set_replay(server, &rqst); + } cifs_dbg(FYI, "async write at %llu %u bytes iter=%zx\n", io_parms->offset, io_parms->length, iov_iter_count(&wdata->subreq.io_iter)); @@ -5159,6 +5244,16 @@ smb2_async_writev(struct cifs_io_subrequest *wdata) async_writev_out: cifs_small_buf_release(req); out: + /* if the send error is retryable, let netfs know about it */ + if (is_replayable_error(rc) && + smb2_should_replay(tcon, + &wdata->retries, + &wdata->cur_sleep)) { + wdata->replay = true; + trace_netfs_sreq(&wdata->subreq, netfs_sreq_trace_io_retry_needed); + __set_bit(NETFS_SREQ_NEED_RETRY, &wdata->subreq.flags); + } + if (rc) { trace_smb3_rw_credits(wdata->rreq->debug_id, wdata->subreq.debug_index, @@ -5190,7 +5285,7 @@ SMB2_write(const unsigned int xid, struct cifs_io_parms *io_parms, int flags = 0; unsigned int total_len; struct TCP_Server_Info *server; - int retries = 0, cur_sleep = 1; + int retries = 0, cur_sleep = 0; replay_again: /* reinitialize for possible replay */ @@ -5238,8 +5333,12 @@ replay_again: rqst.rq_iov = iov; rqst.rq_nvec = n_vec + 1; - if (retries) + if (retries) { + /* Back-off before retry */ + if (cur_sleep) + msleep(cur_sleep); smb2_set_replay(server, &rqst); + } rc = cifs_send_recv(xid, io_parms->tcon->ses, server, &rqst, @@ -5590,7 +5689,7 @@ SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon, struct cifs_ses *ses = tcon->ses; struct TCP_Server_Info *server; int flags = 0; - int retries = 0, cur_sleep = 1; + int retries = 0, cur_sleep = 0; replay_again: /* reinitialize for possible replay */ @@ -5615,8 +5714,12 @@ replay_again: if (rc) goto qdir_exit; - if (retries) + if (retries) { + /* Back-off before retry */ + if (cur_sleep) + msleep(cur_sleep); smb2_set_replay(server, &rqst); + } rc = cifs_send_recv(xid, ses, server, &rqst, &resp_buftype, flags, &rsp_iov); @@ -5725,7 +5828,7 @@ send_set_info(const unsigned int xid, struct cifs_tcon *tcon, struct cifs_ses *ses = tcon->ses; struct TCP_Server_Info *server; int flags = 0; - int retries = 0, cur_sleep = 1; + int retries = 0, cur_sleep = 0; replay_again: /* reinitialize for possible replay */ @@ -5758,8 +5861,12 @@ replay_again: return rc; } - if (retries) + if (retries) { + /* Back-off before retry */ + if (cur_sleep) + msleep(cur_sleep); smb2_set_replay(server, &rqst); + } rc = cifs_send_recv(xid, ses, server, &rqst, &resp_buftype, flags, @@ -5838,7 +5945,7 @@ SMB2_oplock_break(const unsigned int xid, struct cifs_tcon *tcon, struct kvec iov[1]; struct kvec rsp_iov; int resp_buf_type; - int retries = 0, cur_sleep = 1; + int retries = 0, cur_sleep = 0; replay_again: /* reinitialize for possible replay */ @@ -5868,8 +5975,12 @@ replay_again: rqst.rq_iov = iov; rqst.rq_nvec = 1; - if (retries) + if (retries) { + /* Back-off before retry */ + if (cur_sleep) + msleep(cur_sleep); smb2_set_replay(server, &rqst); + } rc = cifs_send_recv(xid, ses, server, &rqst, &resp_buf_type, flags, &rsp_iov); @@ -5971,7 +6082,7 @@ SMB311_posix_qfs_info(const unsigned int xid, struct cifs_tcon *tcon, struct TCP_Server_Info *server; FILE_SYSTEM_POSIX_INFO *info = NULL; int flags = 0; - int retries = 0, cur_sleep = 1; + int retries = 0, cur_sleep = 0; replay_again: /* reinitialize for possible replay */ @@ -5992,8 +6103,12 @@ replay_again: rqst.rq_iov = &iov; rqst.rq_nvec = 1; - if (retries) + if (retries) { + /* Back-off before retry */ + if (cur_sleep) + msleep(cur_sleep); smb2_set_replay(server, &rqst); + } rc = cifs_send_recv(xid, ses, server, &rqst, &resp_buftype, flags, &rsp_iov); @@ -6036,7 +6151,7 @@ SMB2_QFS_attr(const unsigned int xid, struct cifs_tcon *tcon, struct TCP_Server_Info *server; unsigned int rsp_len, offset; int flags = 0; - int retries = 0, cur_sleep = 1; + int retries = 0, cur_sleep = 0; replay_again: /* reinitialize for possible replay */ @@ -6073,8 +6188,12 @@ replay_again: rqst.rq_iov = &iov; rqst.rq_nvec = 1; - if (retries) + if (retries) { + /* Back-off before retry */ + if (cur_sleep) + msleep(cur_sleep); smb2_set_replay(server, &rqst); + } rc = cifs_send_recv(xid, ses, server, &rqst, &resp_buftype, flags, &rsp_iov); @@ -6136,7 +6255,7 @@ smb2_lockv(const unsigned int xid, struct cifs_tcon *tcon, int flags = CIFS_NO_RSP_BUF; unsigned int total_len; struct TCP_Server_Info *server; - int retries = 0, cur_sleep = 1; + int retries = 0, cur_sleep = 0; replay_again: /* reinitialize for possible replay */ @@ -6172,8 +6291,12 @@ replay_again: rqst.rq_iov = iov; rqst.rq_nvec = 2; - if (retries) + if (retries) { + /* Back-off before retry */ + if (cur_sleep) + msleep(cur_sleep); smb2_set_replay(server, &rqst); + } rc = cifs_send_recv(xid, tcon->ses, server, &rqst, &resp_buf_type, flags, diff --git a/fs/smb/client/smb2proto.h b/fs/smb/client/smb2proto.h index 063c9f83bbcd..c7759e37d975 100644 --- a/fs/smb/client/smb2proto.h +++ b/fs/smb/client/smb2proto.h @@ -22,287 +22,252 @@ struct smb_rqst; * All Prototypes ***************************************************************** */ -extern int map_smb2_to_linux_error(char *buf, bool log_err); -extern int smb2_check_message(char *buf, unsigned int pdu_len, unsigned int length, - struct TCP_Server_Info *server); -extern unsigned int smb2_calc_size(void *buf); -extern char *smb2_get_data_area_len(int *off, int *len, - struct smb2_hdr *shdr); -extern __le16 *cifs_convert_path_to_utf16(const char *from, - struct cifs_sb_info *cifs_sb); +int map_smb2_to_linux_error(char *buf, bool log_err); +int smb2_init_maperror(void); +int smb2_check_message(char *buf, unsigned int pdu_len, unsigned int len, + struct TCP_Server_Info *server); +unsigned int smb2_calc_size(void *buf); +char *smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *shdr); +__le16 *cifs_convert_path_to_utf16(const char *from, + struct cifs_sb_info *cifs_sb); -extern int smb2_verify_signature(struct smb_rqst *, struct TCP_Server_Info *); -extern int smb2_check_receive(struct mid_q_entry *mid, - struct TCP_Server_Info *server, bool log_error); -extern struct mid_q_entry *smb2_setup_request(struct cifs_ses *ses, - struct TCP_Server_Info *, - struct smb_rqst *rqst); -extern struct mid_q_entry *smb2_setup_async_request( - struct TCP_Server_Info *server, struct smb_rqst *rqst); -extern struct cifs_tcon *smb2_find_smb_tcon(struct TCP_Server_Info *server, - __u64 ses_id, __u32 tid); -extern __le32 smb2_get_lease_state(struct cifsInodeInfo *cinode); -extern bool smb2_is_valid_oplock_break(char *buffer, - struct TCP_Server_Info *srv); -extern int smb3_handle_read_data(struct TCP_Server_Info *server, - struct mid_q_entry *mid); +int smb2_verify_signature(struct smb_rqst *rqst, + struct TCP_Server_Info *server); +int smb2_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server, + bool log_error); +struct mid_q_entry *smb2_setup_request(struct cifs_ses *ses, + struct TCP_Server_Info *server, + struct smb_rqst *rqst); +struct mid_q_entry *smb2_setup_async_request(struct TCP_Server_Info *server, + struct smb_rqst *rqst); +struct cifs_tcon *smb2_find_smb_tcon(struct TCP_Server_Info *server, + __u64 ses_id, __u32 tid); +__le32 smb2_get_lease_state(struct cifsInodeInfo *cinode); +bool smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server); +int smb3_handle_read_data(struct TCP_Server_Info *server, + struct mid_q_entry *mid); struct inode *smb2_create_reparse_inode(struct cifs_open_info_data *data, - struct super_block *sb, - const unsigned int xid, - struct cifs_tcon *tcon, - const char *full_path, - bool directory, - struct kvec *reparse_iov, - struct kvec *xattr_iov); -int smb2_query_reparse_point(const unsigned int xid, - struct cifs_tcon *tcon, + struct super_block *sb, + const unsigned int xid, + struct cifs_tcon *tcon, + const char *full_path, bool directory, + struct kvec *reparse_iov, + struct kvec *xattr_iov); +int smb2_query_reparse_point(const unsigned int xid, struct cifs_tcon *tcon, struct cifs_sb_info *cifs_sb, - const char *full_path, - u32 *tag, struct kvec *rsp, + const char *full_path, u32 *tag, struct kvec *rsp, int *rsp_buftype); -int smb2_query_path_info(const unsigned int xid, - struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, - const char *full_path, +int smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, const char *full_path, struct cifs_open_info_data *data); -extern int smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon, - const char *full_path, __u64 size, - struct cifs_sb_info *cifs_sb, bool set_alloc, - struct dentry *dentry); -extern int smb2_set_file_info(struct inode *inode, const char *full_path, - FILE_BASIC_INFO *buf, const unsigned int xid); -extern int smb311_posix_mkdir(const unsigned int xid, struct inode *inode, - umode_t mode, struct cifs_tcon *tcon, - const char *full_path, - struct cifs_sb_info *cifs_sb); -extern int smb2_mkdir(const unsigned int xid, struct inode *inode, - umode_t mode, struct cifs_tcon *tcon, - const char *name, struct cifs_sb_info *cifs_sb); -extern void smb2_mkdir_setinfo(struct inode *inode, const char *full_path, - struct cifs_sb_info *cifs_sb, - struct cifs_tcon *tcon, const unsigned int xid); -extern int smb2_rmdir(const unsigned int xid, struct cifs_tcon *tcon, - const char *name, struct cifs_sb_info *cifs_sb); -extern int smb2_unlink(const unsigned int xid, struct cifs_tcon *tcon, - const char *name, struct cifs_sb_info *cifs_sb, - struct dentry *dentry); -int smb2_rename_path(const unsigned int xid, - struct cifs_tcon *tcon, - struct dentry *source_dentry, - const char *from_name, const char *to_name, - struct cifs_sb_info *cifs_sb); -int smb2_create_hardlink(const unsigned int xid, - struct cifs_tcon *tcon, - struct dentry *source_dentry, - const char *from_name, const char *to_name, - struct cifs_sb_info *cifs_sb); -extern int smb3_create_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, const unsigned char *path, - char *pbuf, unsigned int *pbytes_written); -extern int smb3_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, +int smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon, + const char *full_path, __u64 size, + struct cifs_sb_info *cifs_sb, bool set_alloc, + struct dentry *dentry); +int smb2_set_file_info(struct inode *inode, const char *full_path, + FILE_BASIC_INFO *buf, const unsigned int xid); +int smb311_posix_mkdir(const unsigned int xid, struct inode *inode, + umode_t mode, struct cifs_tcon *tcon, + const char *full_path, struct cifs_sb_info *cifs_sb); +int smb2_mkdir(const unsigned int xid, struct inode *parent_inode, + umode_t mode, struct cifs_tcon *tcon, const char *name, + struct cifs_sb_info *cifs_sb); +void smb2_mkdir_setinfo(struct inode *inode, const char *name, + struct cifs_sb_info *cifs_sb, struct cifs_tcon *tcon, + const unsigned int xid); +int smb2_rmdir(const unsigned int xid, struct cifs_tcon *tcon, + const char *name, struct cifs_sb_info *cifs_sb); +int smb2_unlink(const unsigned int xid, struct cifs_tcon *tcon, + const char *name, struct cifs_sb_info *cifs_sb, + struct dentry *dentry); +int smb2_rename_path(const unsigned int xid, struct cifs_tcon *tcon, + struct dentry *source_dentry, const char *from_name, + const char *to_name, struct cifs_sb_info *cifs_sb); +int smb2_create_hardlink(const unsigned int xid, struct cifs_tcon *tcon, + struct dentry *source_dentry, const char *from_name, + const char *to_name, struct cifs_sb_info *cifs_sb); +int smb3_create_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, + const unsigned char *path, char *pbuf, + unsigned int *pbytes_written); +int smb3_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, struct cifs_sb_info *cifs_sb, const unsigned char *path, char *pbuf, unsigned int *pbytes_read); -int smb2_fix_symlink_target_type(char **target, bool directory, struct cifs_sb_info *cifs_sb); +int smb2_fix_symlink_target_type(char **target, bool directory, + struct cifs_sb_info *cifs_sb); int smb2_parse_native_symlink(char **target, const char *buf, unsigned int len, - bool relative, - const char *full_path, + bool relative, const char *full_path, struct cifs_sb_info *cifs_sb); int smb2_parse_symlink_response(struct cifs_sb_info *cifs_sb, - const struct kvec *iov, - const char *full_path, + const struct kvec *iov, const char *full_path, char **path); -int smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms, __u32 *oplock, - void *buf); -extern int smb2_unlock_range(struct cifsFileInfo *cfile, - struct file_lock *flock, const unsigned int xid); -extern int smb2_push_mandatory_locks(struct cifsFileInfo *cfile); -extern void smb2_reconnect_server(struct work_struct *work); -extern int smb3_crypto_aead_allocate(struct TCP_Server_Info *server); -extern unsigned long smb_rqst_len(struct TCP_Server_Info *server, - struct smb_rqst *rqst); -extern void smb2_set_next_command(struct cifs_tcon *tcon, - struct smb_rqst *rqst); -extern void smb2_set_related(struct smb_rqst *rqst); -extern void smb2_set_replay(struct TCP_Server_Info *server, - struct smb_rqst *rqst); -extern bool smb2_should_replay(struct cifs_tcon *tcon, - int *pretries, - int *pcur_sleep); +int smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms, + __u32 *oplock, void *buf); +int smb2_unlock_range(struct cifsFileInfo *cfile, struct file_lock *flock, + const unsigned int xid); +int smb2_push_mandatory_locks(struct cifsFileInfo *cfile); +void smb2_reconnect_server(struct work_struct *work); +int smb3_crypto_aead_allocate(struct TCP_Server_Info *server); +unsigned long smb_rqst_len(struct TCP_Server_Info *server, + struct smb_rqst *rqst); +void smb2_set_next_command(struct cifs_tcon *tcon, struct smb_rqst *rqst); +void smb2_set_related(struct smb_rqst *rqst); +void smb2_set_replay(struct TCP_Server_Info *server, struct smb_rqst *rqst); +bool smb2_should_replay(struct cifs_tcon *tcon, int *pretries, + int *pcur_sleep); /* * SMB2 Worker functions - most of protocol specific implementation details * are contained within these calls. */ -extern int SMB2_negotiate(const unsigned int xid, - struct cifs_ses *ses, - struct TCP_Server_Info *server); -extern int SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses, - struct TCP_Server_Info *server, - const struct nls_table *nls_cp); -extern int SMB2_logoff(const unsigned int xid, struct cifs_ses *ses); -extern int SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, - const char *tree, struct cifs_tcon *tcon, - const struct nls_table *); -extern int SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon); -extern int SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, - __le16 *path, __u8 *oplock, - struct smb2_file_all_info *buf, - struct create_posix_rsp *posix, - struct kvec *err_iov, int *resp_buftype); -extern int SMB2_open_init(struct cifs_tcon *tcon, - struct TCP_Server_Info *server, - struct smb_rqst *rqst, - __u8 *oplock, struct cifs_open_parms *oparms, - __le16 *path); -extern void SMB2_open_free(struct smb_rqst *rqst); -extern int SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid, u32 opcode, - char *in_data, u32 indatalen, u32 maxoutlen, - char **out_data, u32 *plen /* returned data len */); -extern int SMB2_ioctl_init(struct cifs_tcon *tcon, - struct TCP_Server_Info *server, - struct smb_rqst *rqst, - u64 persistent_fid, u64 volatile_fid, u32 opcode, - char *in_data, u32 indatalen, - __u32 max_response_size); -extern void SMB2_ioctl_free(struct smb_rqst *rqst); -extern int SMB2_change_notify(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid, bool watch_tree, - u32 completion_filter, u32 max_out_data_len, - char **out_data, u32 *plen /* returned data len */); +int SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses, + struct TCP_Server_Info *server); +int SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses, + struct TCP_Server_Info *server, + const struct nls_table *nls_cp); +int SMB2_logoff(const unsigned int xid, struct cifs_ses *ses); +int SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree, + struct cifs_tcon *tcon, const struct nls_table *cp); +int SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon); +int SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, + __le16 *path, __u8 *oplock, struct smb2_file_all_info *buf, + struct create_posix_rsp *posix, struct kvec *err_iov, + int *buftype); +int SMB2_open_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server, + struct smb_rqst *rqst, __u8 *oplock, + struct cifs_open_parms *oparms, __le16 *path); +void SMB2_open_free(struct smb_rqst *rqst); +int SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, u32 opcode, char *in_data, + u32 indatalen, u32 max_out_data_len, char **out_data, + u32 *plen /* returned data len */); +int SMB2_ioctl_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server, + struct smb_rqst *rqst, u64 persistent_fid, + u64 volatile_fid, u32 opcode, char *in_data, u32 indatalen, + __u32 max_response_size); +void SMB2_ioctl_free(struct smb_rqst *rqst); +int SMB2_change_notify(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, bool watch_tree, + u32 completion_filter, u32 max_out_data_len, + char **out_data, u32 *plen /* returned data len */); -extern int __SMB2_close(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid, - struct smb2_file_network_open_info *pbuf); -extern int SMB2_close(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_file_id, u64 volatile_file_id); -extern int SMB2_close_init(struct cifs_tcon *tcon, - struct TCP_Server_Info *server, - struct smb_rqst *rqst, - u64 persistent_fid, u64 volatile_fid, - bool query_attrs); -extern void SMB2_close_free(struct smb_rqst *rqst); -extern int SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_file_id, u64 volatile_file_id); -extern int SMB2_flush_init(const unsigned int xid, struct smb_rqst *rqst, - struct cifs_tcon *tcon, - struct TCP_Server_Info *server, - u64 persistent_file_id, u64 volatile_file_id); -extern void SMB2_flush_free(struct smb_rqst *rqst); -extern int SMB311_posix_query_info(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid, struct smb311_posix_qinfo *data, u32 *plen); -extern int SMB2_query_info(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_file_id, u64 volatile_file_id, - struct smb2_file_all_info *data); -extern int SMB2_query_info_init(struct cifs_tcon *tcon, - struct TCP_Server_Info *server, - struct smb_rqst *rqst, - u64 persistent_fid, u64 volatile_fid, - u8 info_class, u8 info_type, - u32 additional_info, size_t output_len, - size_t input_len, void *input); -extern void SMB2_query_info_free(struct smb_rqst *rqst); -extern int SMB2_query_acl(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_file_id, u64 volatile_file_id, - void **data, unsigned int *plen, u32 info); -extern int SMB2_get_srv_num(const unsigned int xid, struct cifs_tcon *tcon, +int __SMB2_close(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, + struct smb2_file_network_open_info *pbuf); +int SMB2_close(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid); +int SMB2_close_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server, + struct smb_rqst *rqst, u64 persistent_fid, + u64 volatile_fid, bool query_attrs); +void SMB2_close_free(struct smb_rqst *rqst); +int SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid); +int SMB2_flush_init(const unsigned int xid, struct smb_rqst *rqst, + struct cifs_tcon *tcon, struct TCP_Server_Info *server, + u64 persistent_fid, u64 volatile_fid); +void SMB2_flush_free(struct smb_rqst *rqst); +int SMB311_posix_query_info(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, u64 volatile_fid, - __le64 *uniqueid); -extern int smb2_async_readv(struct cifs_io_subrequest *rdata); -extern int SMB2_read(const unsigned int xid, struct cifs_io_parms *io_parms, - unsigned int *nbytes, char **buf, int *buf_type); -extern void smb2_async_writev(struct cifs_io_subrequest *wdata); -extern int SMB2_write(const unsigned int xid, struct cifs_io_parms *io_parms, - unsigned int *nbytes, struct kvec *iov, int n_vec); -extern int SMB2_echo(struct TCP_Server_Info *server); -extern int SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid, int index, - struct cifs_search_info *srch_inf); -extern int SMB2_query_directory_init(unsigned int xid, struct cifs_tcon *tcon, - struct TCP_Server_Info *server, - struct smb_rqst *rqst, - u64 persistent_fid, u64 volatile_fid, - int index, int info_level); -extern void SMB2_query_directory_free(struct smb_rqst *rqst); -extern int SMB2_set_eof(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid, u32 pid, - loff_t new_eof); -extern int SMB2_set_info_init(struct cifs_tcon *tcon, + struct smb311_posix_qinfo *data, u32 *plen); +int SMB2_query_info(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, + struct smb2_file_all_info *data); +int SMB2_query_info_init(struct cifs_tcon *tcon, + struct TCP_Server_Info *server, struct smb_rqst *rqst, + u64 persistent_fid, u64 volatile_fid, u8 info_class, + u8 info_type, u32 additional_info, size_t output_len, + size_t input_len, void *input); +void SMB2_query_info_free(struct smb_rqst *rqst); +int SMB2_query_acl(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, void **data, + u32 *plen, u32 extra_info); +int SMB2_get_srv_num(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, __le64 *uniqueid); +int smb2_async_readv(struct cifs_io_subrequest *rdata); +int SMB2_read(const unsigned int xid, struct cifs_io_parms *io_parms, + unsigned int *nbytes, char **buf, int *buf_type); +void smb2_async_writev(struct cifs_io_subrequest *wdata); +int SMB2_write(const unsigned int xid, struct cifs_io_parms *io_parms, + unsigned int *nbytes, struct kvec *iov, int n_vec); +int SMB2_echo(struct TCP_Server_Info *server); +int SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, int index, + struct cifs_search_info *srch_inf); +int SMB2_query_directory_init(const unsigned int xid, struct cifs_tcon *tcon, struct TCP_Server_Info *server, - struct smb_rqst *rqst, - u64 persistent_fid, u64 volatile_fid, u32 pid, - u8 info_class, u8 info_type, u32 additional_info, - void **data, unsigned int *size); -extern void SMB2_set_info_free(struct smb_rqst *rqst); -extern int SMB2_set_acl(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid, - struct smb_ntsd *pnntsd, int pacllen, int aclflag); -extern int SMB2_set_ea(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid, - struct smb2_file_full_ea_info *buf, int len); -extern int SMB2_set_compression(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid); -extern int SMB2_oplock_break(const unsigned int xid, struct cifs_tcon *tcon, - const u64 persistent_fid, const u64 volatile_fid, - const __u8 oplock_level); -extern int smb2_handle_cancelled_close(struct cifs_tcon *tcon, - __u64 persistent_fid, - __u64 volatile_fid); -extern int smb2_handle_cancelled_mid(struct mid_q_entry *mid, struct TCP_Server_Info *server); + struct smb_rqst *rqst, u64 persistent_fid, + u64 volatile_fid, int index, int info_level); +void SMB2_query_directory_free(struct smb_rqst *rqst); +int SMB2_set_eof(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, u32 pid, + loff_t new_eof); +int SMB2_set_info_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server, + struct smb_rqst *rqst, u64 persistent_fid, + u64 volatile_fid, u32 pid, u8 info_class, u8 info_type, + u32 additional_info, void **data, unsigned int *size); +void SMB2_set_info_free(struct smb_rqst *rqst); +int SMB2_set_acl(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, struct smb_ntsd *pnntsd, + int pacllen, int aclflag); +int SMB2_set_ea(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, + struct smb2_file_full_ea_info *buf, int len); +int SMB2_set_compression(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid); +int SMB2_oplock_break(const unsigned int xid, struct cifs_tcon *tcon, + const u64 persistent_fid, const u64 volatile_fid, + __u8 oplock_level); +int smb2_handle_cancelled_close(struct cifs_tcon *tcon, __u64 persistent_fid, + __u64 volatile_fid); +int smb2_handle_cancelled_mid(struct mid_q_entry *mid, + struct TCP_Server_Info *server); void smb2_cancelled_close_fid(struct work_struct *work); -extern int SMB311_posix_qfs_info(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_file_id, u64 volatile_file_id, - struct kstatfs *FSData); -extern int SMB2_QFS_attr(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_file_id, u64 volatile_file_id, int lvl); -extern int SMB2_lock(const unsigned int xid, struct cifs_tcon *tcon, - const __u64 persist_fid, const __u64 volatile_fid, - const __u32 pid, const __u64 length, const __u64 offset, - const __u32 lockFlags, const bool wait); -extern int smb2_lockv(const unsigned int xid, struct cifs_tcon *tcon, - const __u64 persist_fid, const __u64 volatile_fid, - const __u32 pid, const __u32 num_lock, - struct smb2_lock_element *buf); -extern int SMB2_lease_break(const unsigned int xid, struct cifs_tcon *tcon, - __u8 *lease_key, const __le32 lease_state); -extern int smb3_validate_negotiate(const unsigned int, struct cifs_tcon *); +int SMB311_posix_qfs_info(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, + struct kstatfs *fsdata); +int SMB2_QFS_attr(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, int level); +int SMB2_lock(const unsigned int xid, struct cifs_tcon *tcon, + const __u64 persist_fid, const __u64 volatile_fid, + const __u32 pid, const __u64 length, const __u64 offset, + const __u32 lock_flags, const bool wait); +int smb2_lockv(const unsigned int xid, struct cifs_tcon *tcon, + const __u64 persist_fid, const __u64 volatile_fid, + const __u32 pid, const __u32 num_lock, + struct smb2_lock_element *buf); +int SMB2_lease_break(const unsigned int xid, struct cifs_tcon *tcon, + __u8 *lease_key, const __le32 lease_state); +int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon); -extern enum securityEnum smb2_select_sectype(struct TCP_Server_Info *, - enum securityEnum); -int smb2_parse_contexts(struct TCP_Server_Info *server, - struct kvec *rsp_iov, - __u16 *epoch, - char *lease_key, __u8 *oplock, +enum securityEnum smb2_select_sectype(struct TCP_Server_Info *server, + enum securityEnum requested); +int smb2_parse_contexts(struct TCP_Server_Info *server, struct kvec *rsp_iov, + __u16 *epoch, char *lease_key, __u8 *oplock, struct smb2_file_all_info *buf, struct create_posix_rsp *posix); -extern int smb3_encryption_required(const struct cifs_tcon *tcon); -extern int smb2_validate_iov(unsigned int offset, unsigned int buffer_length, - struct kvec *iov, unsigned int min_buf_size); -extern int smb2_validate_and_copy_iov(unsigned int offset, - unsigned int buffer_length, - struct kvec *iov, - unsigned int minbufsize, char *data); -extern void smb2_copy_fs_info_to_kstatfs( - struct smb2_fs_full_size_info *pfs_inf, - struct kstatfs *kst); -extern int smb3_crypto_shash_allocate(struct TCP_Server_Info *server); -extern void smb311_update_preauth_hash(struct cifs_ses *ses, - struct TCP_Server_Info *server, - struct kvec *iov, int nvec); -extern int smb2_query_info_compound(const unsigned int xid, - struct cifs_tcon *tcon, - const char *path, u32 desired_access, - u32 class, u32 type, u32 output_len, - struct kvec *rsp, int *buftype, - struct cifs_sb_info *cifs_sb); +int smb3_encryption_required(const struct cifs_tcon *tcon); +int smb2_validate_iov(unsigned int offset, unsigned int buffer_length, + struct kvec *iov, unsigned int min_buf_size); +int smb2_validate_and_copy_iov(unsigned int offset, unsigned int buffer_length, + struct kvec *iov, unsigned int minbufsize, + char *data); +void smb2_copy_fs_info_to_kstatfs(struct smb2_fs_full_size_info *pfs_inf, + struct kstatfs *kst); +int smb3_crypto_shash_allocate(struct TCP_Server_Info *server); +void smb311_update_preauth_hash(struct cifs_ses *ses, + struct TCP_Server_Info *server, + struct kvec *iov, int nvec); +int smb2_query_info_compound(const unsigned int xid, struct cifs_tcon *tcon, + const char *path, u32 desired_access, u32 class, + u32 type, u32 output_len, struct kvec *rsp, + int *buftype, struct cifs_sb_info *cifs_sb); /* query path info from the server using SMB311 POSIX extensions*/ int posix_info_parse(const void *beg, const void *end, struct smb2_posix_info_parsed *out); int posix_info_sid_size(const void *beg, const void *end); -int smb2_rename_pending_delete(const char *full_path, - struct dentry *dentry, +int smb2_rename_pending_delete(const char *full_path, struct dentry *dentry, const unsigned int xid); #endif /* _SMB2PROTO_H */ diff --git a/fs/smb/client/smbdirect.c b/fs/smb/client/smbdirect.c index 788a0670c4a8..d44847c9d8fc 100644 --- a/fs/smb/client/smbdirect.c +++ b/fs/smb/client/smbdirect.c @@ -35,6 +35,10 @@ static void enqueue_reassembly( static struct smbdirect_recv_io *_get_first_reassembly( struct smbdirect_socket *sc); +static int smbd_post_send(struct smbdirect_socket *sc, + struct smbdirect_send_batch *batch, + struct smbdirect_send_io *request); + static int smbd_post_recv( struct smbdirect_socket *sc, struct smbdirect_recv_io *response); @@ -97,8 +101,23 @@ int smbd_send_credit_target = 255; /* The maximum single message size can be sent to remote peer */ int smbd_max_send_size = 1364; -/* The maximum fragmented upper-layer payload receive size supported */ -int smbd_max_fragmented_recv_size = 1024 * 1024; +/* + * The maximum fragmented upper-layer payload receive size supported + * + * Assume max_payload_per_credit is + * smbd_max_receive_size - 24 = 1340 + * + * The maximum number would be + * smbd_receive_credit_max * max_payload_per_credit + * + * 1340 * 255 = 341700 (0x536C4) + * + * The minimum value from the spec is 131072 (0x20000) + * + * For now we use the logic we used in ksmbd before: + * (1364 * 255) / 2 = 173910 (0x2A756) + */ +int smbd_max_fragmented_recv_size = (1364 * 255) / 2; /* The maximum single-message size which can be received */ int smbd_max_receive_size = 1364; @@ -493,27 +512,103 @@ static inline void *smbdirect_recv_io_payload(struct smbdirect_recv_io *response return (void *)response->packet; } +static struct smbdirect_send_io *smbd_alloc_send_io(struct smbdirect_socket *sc) +{ + struct smbdirect_send_io *msg; + + msg = mempool_alloc(sc->send_io.mem.pool, GFP_KERNEL); + if (!msg) + return ERR_PTR(-ENOMEM); + msg->socket = sc; + INIT_LIST_HEAD(&msg->sibling_list); + msg->num_sge = 0; + + return msg; +} + +static void smbd_free_send_io(struct smbdirect_send_io *msg) +{ + struct smbdirect_socket *sc = msg->socket; + size_t i; + + /* + * The list needs to be empty! + * The caller should take care of it. + */ + WARN_ON_ONCE(!list_empty(&msg->sibling_list)); + + /* + * Note we call ib_dma_unmap_page(), even if some sges are mapped using + * ib_dma_map_single(). + * + * The difference between _single() and _page() only matters for the + * ib_dma_map_*() case. + * + * For the ib_dma_unmap_*() case it does not matter as both take the + * dma_addr_t and dma_unmap_single_attrs() is just an alias to + * dma_unmap_page_attrs(). + */ + for (i = 0; i < msg->num_sge; i++) + ib_dma_unmap_page(sc->ib.dev, + msg->sge[i].addr, + msg->sge[i].length, + DMA_TO_DEVICE); + + mempool_free(msg, sc->send_io.mem.pool); +} + /* Called when a RDMA send is done */ static void send_done(struct ib_cq *cq, struct ib_wc *wc) { - int i; struct smbdirect_send_io *request = container_of(wc->wr_cqe, struct smbdirect_send_io, cqe); struct smbdirect_socket *sc = request->socket; + struct smbdirect_send_io *sibling, *next; int lcredits = 0; log_rdma_send(INFO, "smbdirect_send_io 0x%p completed wc->status=%s\n", request, ib_wc_status_msg(wc->status)); - for (i = 0; i < request->num_sge; i++) - ib_dma_unmap_single(sc->ib.dev, - request->sge[i].addr, - request->sge[i].length, - DMA_TO_DEVICE); - mempool_free(request, sc->send_io.mem.pool); + if (unlikely(!(request->wr.send_flags & IB_SEND_SIGNALED))) { + /* + * This happens when smbdirect_send_io is a sibling + * before the final message, it is signaled on + * error anyway, so we need to skip + * smbdirect_connection_free_send_io here, + * otherwise is will destroy the memory + * of the siblings too, which will cause + * use after free problems for the others + * triggered from ib_drain_qp(). + */ + if (wc->status != IB_WC_SUCCESS) + goto skip_free; + + /* + * This should not happen! + * But we better just close the + * connection... + */ + log_rdma_send(ERR, + "unexpected send completion wc->status=%s (%d) wc->opcode=%d\n", + ib_wc_status_msg(wc->status), wc->status, wc->opcode); + smbd_disconnect_rdma_connection(sc); + return; + } + + /* + * Free possible siblings and then the main send_io + */ + list_for_each_entry_safe(sibling, next, &request->sibling_list, sibling_list) { + list_del_init(&sibling->sibling_list); + smbd_free_send_io(sibling); + lcredits += 1; + } + /* Note this frees wc->wr_cqe, but not wc */ + smbd_free_send_io(request); lcredits += 1; if (wc->status != IB_WC_SUCCESS || wc->opcode != IB_WC_SEND) { +skip_free: if (wc->status != IB_WC_WR_FLUSH_ERR) log_rdma_send(ERR, "wc->status=%s wc->opcode=%d\n", ib_wc_status_msg(wc->status), wc->opcode); @@ -608,6 +703,7 @@ static bool process_negotiation_response( sp->max_frmr_depth * PAGE_SIZE); sp->max_frmr_depth = sp->max_read_write_size / PAGE_SIZE; + atomic_set(&sc->send_io.bcredits.count, 1); sc->recv_io.expected = SMBDIRECT_EXPECT_DATA_TRANSFER; return true; } @@ -618,6 +714,7 @@ static void smbd_post_send_credits(struct work_struct *work) struct smbdirect_recv_io *response; struct smbdirect_socket *sc = container_of(work, struct smbdirect_socket, recv_io.posted.refill_work); + int posted = 0; if (sc->status != SMBDIRECT_SOCKET_CONNECTED) { return; @@ -640,9 +737,21 @@ static void smbd_post_send_credits(struct work_struct *work) } atomic_inc(&sc->recv_io.posted.count); + posted += 1; } } + atomic_add(posted, &sc->recv_io.credits.available); + + /* + * If the last send credit is waiting for credits + * it can grant we need to wake it up + */ + if (posted && + atomic_read(&sc->send_io.bcredits.count) == 0 && + atomic_read(&sc->send_io.credits.count) == 0) + wake_up(&sc->send_io.credits.wait_queue); + /* Promptly send an immediate packet as defined in [MS-SMBD] 3.1.1.1 */ if (atomic_read(&sc->recv_io.credits.count) < sc->recv_io.credits.target - 1) { @@ -659,6 +768,7 @@ static void recv_done(struct ib_cq *cq, struct ib_wc *wc) container_of(wc->wr_cqe, struct smbdirect_recv_io, cqe); struct smbdirect_socket *sc = response->socket; struct smbdirect_socket_parameters *sp = &sc->parameters; + int current_recv_credits; u16 old_recv_credit_target; u32 data_offset = 0; u32 data_length = 0; @@ -743,7 +853,8 @@ static void recv_done(struct ib_cq *cq, struct ib_wc *wc) } atomic_dec(&sc->recv_io.posted.count); - atomic_dec(&sc->recv_io.credits.count); + current_recv_credits = atomic_dec_return(&sc->recv_io.credits.count); + old_recv_credit_target = sc->recv_io.credits.target; sc->recv_io.credits.target = le16_to_cpu(data_transfer->credits_requested); @@ -779,7 +890,8 @@ static void recv_done(struct ib_cq *cq, struct ib_wc *wc) * reassembly queue and wake up the reading thread */ if (data_length) { - if (sc->recv_io.credits.target > old_recv_credit_target) + if (current_recv_credits <= (sc->recv_io.credits.target / 4) || + sc->recv_io.credits.target > old_recv_credit_target) queue_work(sc->workqueue, &sc->recv_io.posted.refill_work); enqueue_reassembly(sc, response, data_length); @@ -810,6 +922,7 @@ static struct rdma_cm_id *smbd_create_id( { struct smbdirect_socket_parameters *sp = &sc->parameters; struct rdma_cm_id *id; + u8 node_type = RDMA_NODE_UNSPECIFIED; int rc; __be16 *sport; @@ -821,6 +934,31 @@ static struct rdma_cm_id *smbd_create_id( return id; } + switch (port) { + case SMBD_PORT: + /* + * only allow iWarp devices + * for port 5445. + */ + node_type = RDMA_NODE_RNIC; + break; + case SMB_PORT: + /* + * only allow InfiniBand, RoCEv1 or RoCEv2 + * devices for port 445. + * + * (Basically don't allow iWarp devices) + */ + node_type = RDMA_NODE_IB_CA; + break; + } + rc = rdma_restrict_node_type(id, node_type); + if (rc) { + log_rdma_event(ERR, "rdma_restrict_node_type(%u) failed %i\n", + node_type, rc); + goto out; + } + if (dstaddr->sa_family == AF_INET6) sport = &((struct sockaddr_in6 *)dstaddr)->sin6_port; else @@ -955,16 +1093,13 @@ out1: static int smbd_post_send_negotiate_req(struct smbdirect_socket *sc) { struct smbdirect_socket_parameters *sp = &sc->parameters; - struct ib_send_wr send_wr; - int rc = -ENOMEM; + int rc; struct smbdirect_send_io *request; struct smbdirect_negotiate_req *packet; - request = mempool_alloc(sc->send_io.mem.pool, GFP_KERNEL); - if (!request) - return rc; - - request->socket = sc; + request = smbd_alloc_send_io(sc); + if (IS_ERR(request)) + return PTR_ERR(request); packet = smbdirect_send_io_payload(request); packet->min_version = cpu_to_le16(SMBDIRECT_V1); @@ -976,7 +1111,6 @@ static int smbd_post_send_negotiate_req(struct smbdirect_socket *sc) packet->max_fragmented_size = cpu_to_le32(sp->max_fragmented_recv_size); - request->num_sge = 1; request->sge[0].addr = ib_dma_map_single( sc->ib.dev, (void *)packet, sizeof(*packet), DMA_TO_DEVICE); @@ -984,42 +1118,20 @@ static int smbd_post_send_negotiate_req(struct smbdirect_socket *sc) rc = -EIO; goto dma_mapping_failed; } + request->num_sge = 1; request->sge[0].length = sizeof(*packet); request->sge[0].lkey = sc->ib.pd->local_dma_lkey; - ib_dma_sync_single_for_device( - sc->ib.dev, request->sge[0].addr, - request->sge[0].length, DMA_TO_DEVICE); - - request->cqe.done = send_done; - - send_wr.next = NULL; - send_wr.wr_cqe = &request->cqe; - send_wr.sg_list = request->sge; - send_wr.num_sge = request->num_sge; - send_wr.opcode = IB_WR_SEND; - send_wr.send_flags = IB_SEND_SIGNALED; - - log_rdma_send(INFO, "sge addr=0x%llx length=%u lkey=0x%x\n", - request->sge[0].addr, - request->sge[0].length, request->sge[0].lkey); - - atomic_inc(&sc->send_io.pending.count); - rc = ib_post_send(sc->ib.qp, &send_wr, NULL); + rc = smbd_post_send(sc, NULL, request); if (!rc) return 0; - /* if we reach here, post send failed */ - log_rdma_send(ERR, "ib_post_send failed rc=%d\n", rc); - atomic_dec(&sc->send_io.pending.count); - ib_dma_unmap_single(sc->ib.dev, request->sge[0].addr, - request->sge[0].length, DMA_TO_DEVICE); - - smbd_disconnect_rdma_connection(sc); + if (rc == -EAGAIN) + rc = -EIO; dma_mapping_failed: - mempool_free(request, sc->send_io.mem.pool); + smbd_free_send_io(request); return rc; } @@ -1033,19 +1145,38 @@ dma_mapping_failed: */ static int manage_credits_prior_sending(struct smbdirect_socket *sc) { + int missing; + int available; int new_credits; if (atomic_read(&sc->recv_io.credits.count) >= sc->recv_io.credits.target) return 0; - new_credits = atomic_read(&sc->recv_io.posted.count); - if (new_credits == 0) + missing = (int)sc->recv_io.credits.target - atomic_read(&sc->recv_io.credits.count); + available = atomic_xchg(&sc->recv_io.credits.available, 0); + new_credits = (u16)min3(U16_MAX, missing, available); + if (new_credits <= 0) { + /* + * If credits are available, but not granted + * we need to re-add them again. + */ + if (available) + atomic_add(available, &sc->recv_io.credits.available); return 0; + } - new_credits -= atomic_read(&sc->recv_io.credits.count); - if (new_credits <= 0) - return 0; + if (new_credits < available) { + /* + * Readd the remaining available again. + */ + available -= new_credits; + atomic_add(available, &sc->recv_io.credits.available); + } + /* + * Remember we granted the credits + */ + atomic_add(new_credits, &sc->recv_io.credits.count); return new_credits; } @@ -1075,12 +1206,27 @@ static int manage_keep_alive_before_sending(struct smbdirect_socket *sc) return 0; } +static int smbd_ib_post_send(struct smbdirect_socket *sc, + struct ib_send_wr *wr) +{ + int ret; + + atomic_inc(&sc->send_io.pending.count); + ret = ib_post_send(sc->ib.qp, wr, NULL); + if (ret) { + pr_err("failed to post send: %d\n", ret); + smbd_disconnect_rdma_connection(sc); + ret = -EAGAIN; + } + return ret; +} + /* Post the send request */ static int smbd_post_send(struct smbdirect_socket *sc, - struct smbdirect_send_io *request) + struct smbdirect_send_batch *batch, + struct smbdirect_send_io *request) { - struct ib_send_wr send_wr; - int rc, i; + int i; for (i = 0; i < request->num_sge; i++) { log_rdma_send(INFO, @@ -1094,79 +1240,245 @@ static int smbd_post_send(struct smbdirect_socket *sc, } request->cqe.done = send_done; + request->wr.next = NULL; + request->wr.sg_list = request->sge; + request->wr.num_sge = request->num_sge; + request->wr.opcode = IB_WR_SEND; + + if (batch) { + request->wr.wr_cqe = NULL; + request->wr.send_flags = 0; + if (!list_empty(&batch->msg_list)) { + struct smbdirect_send_io *last; + + last = list_last_entry(&batch->msg_list, + struct smbdirect_send_io, + sibling_list); + last->wr.next = &request->wr; + } + list_add_tail(&request->sibling_list, &batch->msg_list); + batch->wr_cnt++; + return 0; + } - send_wr.next = NULL; - send_wr.wr_cqe = &request->cqe; - send_wr.sg_list = request->sge; - send_wr.num_sge = request->num_sge; - send_wr.opcode = IB_WR_SEND; - send_wr.send_flags = IB_SEND_SIGNALED; + request->wr.wr_cqe = &request->cqe; + request->wr.send_flags = IB_SEND_SIGNALED; + return smbd_ib_post_send(sc, &request->wr); +} - rc = ib_post_send(sc->ib.qp, &send_wr, NULL); - if (rc) { - log_rdma_send(ERR, "ib_post_send failed rc=%d\n", rc); - smbd_disconnect_rdma_connection(sc); - rc = -EAGAIN; +static void smbd_send_batch_init(struct smbdirect_send_batch *batch, + bool need_invalidate_rkey, + unsigned int remote_key) +{ + INIT_LIST_HEAD(&batch->msg_list); + batch->wr_cnt = 0; + batch->need_invalidate_rkey = need_invalidate_rkey; + batch->remote_key = remote_key; + batch->credit = 0; +} + +static int smbd_send_batch_flush(struct smbdirect_socket *sc, + struct smbdirect_send_batch *batch, + bool is_last) +{ + struct smbdirect_send_io *first, *last; + int ret = 0; + + if (list_empty(&batch->msg_list)) + goto release_credit; + + first = list_first_entry(&batch->msg_list, + struct smbdirect_send_io, + sibling_list); + last = list_last_entry(&batch->msg_list, + struct smbdirect_send_io, + sibling_list); + + if (batch->need_invalidate_rkey) { + first->wr.opcode = IB_WR_SEND_WITH_INV; + first->wr.ex.invalidate_rkey = batch->remote_key; + batch->need_invalidate_rkey = false; + batch->remote_key = 0; } - return rc; + last->wr.send_flags = IB_SEND_SIGNALED; + last->wr.wr_cqe = &last->cqe; + + /* + * Remove last from batch->msg_list + * and splice the rest of batch->msg_list + * to last->sibling_list. + * + * batch->msg_list is a valid empty list + * at the end. + */ + list_del_init(&last->sibling_list); + list_splice_tail_init(&batch->msg_list, &last->sibling_list); + batch->wr_cnt = 0; + + ret = smbd_ib_post_send(sc, &first->wr); + if (ret) { + struct smbdirect_send_io *sibling, *next; + + list_for_each_entry_safe(sibling, next, &last->sibling_list, sibling_list) { + list_del_init(&sibling->sibling_list); + smbd_free_send_io(sibling); + } + smbd_free_send_io(last); + } + +release_credit: + if (is_last && !ret && batch->credit) { + atomic_add(batch->credit, &sc->send_io.bcredits.count); + batch->credit = 0; + wake_up(&sc->send_io.bcredits.wait_queue); + } + + return ret; +} + +static int wait_for_credits(struct smbdirect_socket *sc, + wait_queue_head_t *waitq, atomic_t *total_credits, + int needed) +{ + int ret; + + do { + if (atomic_sub_return(needed, total_credits) >= 0) + return 0; + + atomic_add(needed, total_credits); + ret = wait_event_interruptible(*waitq, + atomic_read(total_credits) >= needed || + sc->status != SMBDIRECT_SOCKET_CONNECTED); + + if (sc->status != SMBDIRECT_SOCKET_CONNECTED) + return -ENOTCONN; + else if (ret < 0) + return ret; + } while (true); +} + +static int wait_for_send_bcredit(struct smbdirect_socket *sc, + struct smbdirect_send_batch *batch) +{ + int ret; + + if (batch->credit) + return 0; + + ret = wait_for_credits(sc, + &sc->send_io.bcredits.wait_queue, + &sc->send_io.bcredits.count, + 1); + if (ret) + return ret; + + batch->credit = 1; + return 0; +} + +static int wait_for_send_lcredit(struct smbdirect_socket *sc, + struct smbdirect_send_batch *batch) +{ + if (batch && (atomic_read(&sc->send_io.lcredits.count) <= 1)) { + int ret; + + ret = smbd_send_batch_flush(sc, batch, false); + if (ret) + return ret; + } + + return wait_for_credits(sc, + &sc->send_io.lcredits.wait_queue, + &sc->send_io.lcredits.count, + 1); +} + +static int wait_for_send_credits(struct smbdirect_socket *sc, + struct smbdirect_send_batch *batch) +{ + if (batch && + (batch->wr_cnt >= 16 || atomic_read(&sc->send_io.credits.count) <= 1)) { + int ret; + + ret = smbd_send_batch_flush(sc, batch, false); + if (ret) + return ret; + } + + return wait_for_credits(sc, + &sc->send_io.credits.wait_queue, + &sc->send_io.credits.count, + 1); } static int smbd_post_send_iter(struct smbdirect_socket *sc, + struct smbdirect_send_batch *batch, struct iov_iter *iter, int *_remaining_data_length) { struct smbdirect_socket_parameters *sp = &sc->parameters; - int i, rc; + int rc; int header_length; int data_length; struct smbdirect_send_io *request; struct smbdirect_data_transfer *packet; int new_credits = 0; + struct smbdirect_send_batch _batch; -wait_lcredit: - /* Wait for local send credits */ - rc = wait_event_interruptible(sc->send_io.lcredits.wait_queue, - atomic_read(&sc->send_io.lcredits.count) > 0 || - sc->status != SMBDIRECT_SOCKET_CONNECTED); - if (rc) - goto err_wait_lcredit; + if (!batch) { + smbd_send_batch_init(&_batch, false, 0); + batch = &_batch; + } - if (sc->status != SMBDIRECT_SOCKET_CONNECTED) { - log_outgoing(ERR, "disconnected not sending on wait_credit\n"); + rc = wait_for_send_bcredit(sc, batch); + if (rc) { + log_outgoing(ERR, "disconnected not sending on wait_bcredit\n"); rc = -EAGAIN; - goto err_wait_lcredit; - } - if (unlikely(atomic_dec_return(&sc->send_io.lcredits.count) < 0)) { - atomic_inc(&sc->send_io.lcredits.count); - goto wait_lcredit; + goto err_wait_bcredit; } -wait_credit: - /* Wait for send credits. A SMBD packet needs one credit */ - rc = wait_event_interruptible(sc->send_io.credits.wait_queue, - atomic_read(&sc->send_io.credits.count) > 0 || - sc->status != SMBDIRECT_SOCKET_CONNECTED); - if (rc) - goto err_wait_credit; + rc = wait_for_send_lcredit(sc, batch); + if (rc) { + log_outgoing(ERR, "disconnected not sending on wait_lcredit\n"); + rc = -EAGAIN; + goto err_wait_lcredit; + } - if (sc->status != SMBDIRECT_SOCKET_CONNECTED) { + rc = wait_for_send_credits(sc, batch); + if (rc) { log_outgoing(ERR, "disconnected not sending on wait_credit\n"); rc = -EAGAIN; goto err_wait_credit; } - if (unlikely(atomic_dec_return(&sc->send_io.credits.count) < 0)) { - atomic_inc(&sc->send_io.credits.count); - goto wait_credit; + + new_credits = manage_credits_prior_sending(sc); + if (new_credits == 0 && + atomic_read(&sc->send_io.credits.count) == 0 && + atomic_read(&sc->recv_io.credits.count) == 0) { + queue_work(sc->workqueue, &sc->recv_io.posted.refill_work); + rc = wait_event_interruptible(sc->send_io.credits.wait_queue, + atomic_read(&sc->send_io.credits.count) >= 1 || + atomic_read(&sc->recv_io.credits.available) >= 1 || + sc->status != SMBDIRECT_SOCKET_CONNECTED); + if (sc->status != SMBDIRECT_SOCKET_CONNECTED) + rc = -ENOTCONN; + if (rc < 0) { + log_outgoing(ERR, "disconnected not sending on last credit\n"); + rc = -EAGAIN; + goto err_wait_credit; + } + + new_credits = manage_credits_prior_sending(sc); } - request = mempool_alloc(sc->send_io.mem.pool, GFP_KERNEL); - if (!request) { - rc = -ENOMEM; + request = smbd_alloc_send_io(sc); + if (IS_ERR(request)) { + rc = PTR_ERR(request); goto err_alloc; } - request->socket = sc; memset(request->sge, 0, sizeof(request->sge)); /* Map the packet to DMA */ @@ -1215,9 +1527,6 @@ wait_credit: /* Fill in the packet header */ packet->credits_requested = cpu_to_le16(sp->send_credit_target); - - new_credits = manage_credits_prior_sending(sc); - atomic_add(new_credits, &sc->recv_io.credits.count); packet->credits_granted = cpu_to_le16(new_credits); packet->flags = 0; @@ -1240,32 +1549,18 @@ wait_credit: le32_to_cpu(packet->data_length), le32_to_cpu(packet->remaining_data_length)); - /* - * Now that we got a local and a remote credit - * we add us as pending - */ - atomic_inc(&sc->send_io.pending.count); - - rc = smbd_post_send(sc, request); - if (!rc) - return 0; - - if (atomic_dec_and_test(&sc->send_io.pending.count)) - wake_up(&sc->send_io.pending.zero_wait_queue); + rc = smbd_post_send(sc, batch, request); + if (!rc) { + if (batch != &_batch) + return 0; - wake_up(&sc->send_io.pending.dec_wait_queue); + rc = smbd_send_batch_flush(sc, batch, true); + if (!rc) + return 0; + } err_dma: - for (i = 0; i < request->num_sge; i++) - if (request->sge[i].addr) - ib_dma_unmap_single(sc->ib.dev, - request->sge[i].addr, - request->sge[i].length, - DMA_TO_DEVICE); - mempool_free(request, sc->send_io.mem.pool); - - /* roll back the granted receive credits */ - atomic_sub(new_credits, &sc->recv_io.credits.count); + smbd_free_send_io(request); err_alloc: atomic_inc(&sc->send_io.credits.count); @@ -1276,6 +1571,11 @@ err_wait_credit: wake_up(&sc->send_io.lcredits.wait_queue); err_wait_lcredit: + atomic_add(batch->credit, &sc->send_io.bcredits.count); + batch->credit = 0; + wake_up(&sc->send_io.bcredits.wait_queue); + +err_wait_bcredit: return rc; } @@ -1289,10 +1589,11 @@ static int smbd_post_send_empty(struct smbdirect_socket *sc) int remaining_data_length = 0; sc->statistics.send_empty++; - return smbd_post_send_iter(sc, NULL, &remaining_data_length); + return smbd_post_send_iter(sc, NULL, NULL, &remaining_data_length); } static int smbd_post_send_full_iter(struct smbdirect_socket *sc, + struct smbdirect_send_batch *batch, struct iov_iter *iter, int *_remaining_data_length) { @@ -1305,7 +1606,7 @@ static int smbd_post_send_full_iter(struct smbdirect_socket *sc, */ while (iov_iter_count(iter) > 0) { - rc = smbd_post_send_iter(sc, iter, _remaining_data_length); + rc = smbd_post_send_iter(sc, batch, iter, _remaining_data_length); if (rc < 0) break; } @@ -2227,8 +2528,10 @@ int smbd_send(struct TCP_Server_Info *server, struct smbdirect_socket_parameters *sp = &sc->parameters; struct smb_rqst *rqst; struct iov_iter iter; + struct smbdirect_send_batch batch; unsigned int remaining_data_length, klen; int rc, i, rqst_idx; + int error = 0; if (sc->status != SMBDIRECT_SOCKET_CONNECTED) return -EAGAIN; @@ -2253,6 +2556,7 @@ int smbd_send(struct TCP_Server_Info *server, num_rqst, remaining_data_length); rqst_idx = 0; + smbd_send_batch_init(&batch, false, 0); do { rqst = &rqst_array[rqst_idx]; @@ -2271,20 +2575,28 @@ int smbd_send(struct TCP_Server_Info *server, klen += rqst->rq_iov[i].iov_len; iov_iter_kvec(&iter, ITER_SOURCE, rqst->rq_iov, rqst->rq_nvec, klen); - rc = smbd_post_send_full_iter(sc, &iter, &remaining_data_length); - if (rc < 0) + rc = smbd_post_send_full_iter(sc, &batch, &iter, &remaining_data_length); + if (rc < 0) { + error = rc; break; + } if (iov_iter_count(&rqst->rq_iter) > 0) { /* And then the data pages if there are any */ - rc = smbd_post_send_full_iter(sc, &rqst->rq_iter, + rc = smbd_post_send_full_iter(sc, &batch, &rqst->rq_iter, &remaining_data_length); - if (rc < 0) + if (rc < 0) { + error = rc; break; + } } } while (++rqst_idx < num_rqst); + rc = smbd_send_batch_flush(sc, &batch, true); + if (unlikely(!rc && error)) + rc = error; + /* * As an optimization, we don't wait for individual I/O to finish * before sending the next one. diff --git a/fs/smb/client/smbencrypt.c b/fs/smb/client/smbencrypt.c index 1d1ee9f18f37..094b8296d9b4 100644 --- a/fs/smb/client/smbencrypt.c +++ b/fs/smb/client/smbencrypt.c @@ -20,7 +20,6 @@ #include <linux/random.h> #include "cifs_fs_sb.h" #include "cifs_unicode.h" -#include "cifspdu.h" #include "cifsglob.h" #include "cifs_debug.h" #include "cifsproto.h" diff --git a/fs/smb/client/smberr.h b/fs/smb/client/smberr.h index aeffdad829e2..6fb63f9e9a95 100644 --- a/fs/smb/client/smberr.h +++ b/fs/smb/client/smberr.h @@ -80,7 +80,7 @@ #define ERRinvparm 87 #define ERRdiskfull 112 #define ERRinvname 123 -#define ERRinvlevel 124 +#define ERRunknownlevel 124 #define ERRdirnotempty 145 #define ERRnotlocked 158 #define ERRcancelviolation 173 diff --git a/fs/smb/client/trace.h b/fs/smb/client/trace.h index a584a7743113..191f02344dcd 100644 --- a/fs/smb/client/trace.h +++ b/fs/smb/client/trace.h @@ -189,6 +189,7 @@ EM(netfs_trace_tcon_ref_put_cancelled_close_fid, "PUT Cn-Fid") \ EM(netfs_trace_tcon_ref_put_cancelled_mid, "PUT Cn-Mid") \ EM(netfs_trace_tcon_ref_put_mnt_ctx, "PUT MntCtx") \ + EM(netfs_trace_tcon_ref_put_dfs_refer, "PUT DfsRfr") \ EM(netfs_trace_tcon_ref_put_reconnect_server, "PUT Reconn") \ EM(netfs_trace_tcon_ref_put_tlink, "PUT Tlink ") \ EM(netfs_trace_tcon_ref_see_cancelled_close, "SEE Cn-Cls") \ diff --git a/fs/smb/client/transport.c b/fs/smb/client/transport.c index 3b34c3f4da2d..75697f6d2566 100644 --- a/fs/smb/client/transport.c +++ b/fs/smb/client/transport.c @@ -23,7 +23,6 @@ #include <linux/sched/signal.h> #include <linux/task_io_accounting_ops.h> #include <linux/task_work.h> -#include "cifspdu.h" #include "cifsglob.h" #include "cifsproto.h" #include "cifs_debug.h" diff --git a/fs/smb/client/xattr.c b/fs/smb/client/xattr.c index 6bc89c59164a..e1a7d9a10a53 100644 --- a/fs/smb/client/xattr.c +++ b/fs/smb/client/xattr.c @@ -11,7 +11,6 @@ #include <linux/slab.h> #include <linux/xattr.h> #include "cifsfs.h" -#include "cifspdu.h" #include "cifsglob.h" #include "cifsproto.h" #include "cifs_debug.h" diff --git a/fs/smb/common/smb2pdu.h b/fs/smb/common/smb2pdu.h index f5ebbe31384a..e482c86ceb00 100644 --- a/fs/smb/common/smb2pdu.h +++ b/fs/smb/common/smb2pdu.h @@ -2,6 +2,9 @@ #ifndef _COMMON_SMB2PDU_H #define _COMMON_SMB2PDU_H +#include <linux/types.h> +#include <linux/build_bug.h> + /* * Note that, due to trying to use names similar to the protocol specifications, * there are many mixed case field names in the structures below. Although diff --git a/fs/smb/common/smb2status.h b/fs/smb/common/smb2status.h index 7d6b8ed304fc..b6421bc5113c 100644 --- a/fs/smb/common/smb2status.h +++ b/fs/smb/common/smb2status.h @@ -9,6 +9,9 @@ * */ +#ifndef FS_SMB_COMMON_SMB2STATUS_H +#define FS_SMB_COMMON_SMB2STATUS_H + /* * 0 1 2 3 4 5 6 7 8 9 0 A B C D E F 0 1 2 3 4 5 6 7 8 9 A B C D E F * SEV C N <-------Facility--------> <------Error Status Code------> @@ -27,1752 +30,1760 @@ struct ntstatus { __le32 Code; }; -#define STATUS_SUCCESS cpu_to_le32(0x00000000) -#define STATUS_WAIT_0 cpu_to_le32(0x00000000) -#define STATUS_WAIT_1 cpu_to_le32(0x00000001) -#define STATUS_WAIT_2 cpu_to_le32(0x00000002) -#define STATUS_WAIT_3 cpu_to_le32(0x00000003) -#define STATUS_WAIT_63 cpu_to_le32(0x0000003F) -#define STATUS_ABANDONED cpu_to_le32(0x00000080) -#define STATUS_ABANDONED_WAIT_0 cpu_to_le32(0x00000080) -#define STATUS_ABANDONED_WAIT_63 cpu_to_le32(0x000000BF) -#define STATUS_USER_APC cpu_to_le32(0x000000C0) -#define STATUS_KERNEL_APC cpu_to_le32(0x00000100) -#define STATUS_ALERTED cpu_to_le32(0x00000101) -#define STATUS_TIMEOUT cpu_to_le32(0x00000102) -#define STATUS_PENDING cpu_to_le32(0x00000103) -#define STATUS_REPARSE cpu_to_le32(0x00000104) -#define STATUS_MORE_ENTRIES cpu_to_le32(0x00000105) -#define STATUS_NOT_ALL_ASSIGNED cpu_to_le32(0x00000106) -#define STATUS_SOME_NOT_MAPPED cpu_to_le32(0x00000107) -#define STATUS_OPLOCK_BREAK_IN_PROGRESS cpu_to_le32(0x00000108) -#define STATUS_VOLUME_MOUNTED cpu_to_le32(0x00000109) -#define STATUS_RXACT_COMMITTED cpu_to_le32(0x0000010A) -#define STATUS_NOTIFY_CLEANUP cpu_to_le32(0x0000010B) -#define STATUS_NOTIFY_ENUM_DIR cpu_to_le32(0x0000010C) -#define STATUS_NO_QUOTAS_FOR_ACCOUNT cpu_to_le32(0x0000010D) -#define STATUS_PRIMARY_TRANSPORT_CONNECT_FAILED cpu_to_le32(0x0000010E) -#define STATUS_PAGE_FAULT_TRANSITION cpu_to_le32(0x00000110) -#define STATUS_PAGE_FAULT_DEMAND_ZERO cpu_to_le32(0x00000111) -#define STATUS_PAGE_FAULT_COPY_ON_WRITE cpu_to_le32(0x00000112) -#define STATUS_PAGE_FAULT_GUARD_PAGE cpu_to_le32(0x00000113) -#define STATUS_PAGE_FAULT_PAGING_FILE cpu_to_le32(0x00000114) -#define STATUS_CACHE_PAGE_LOCKED cpu_to_le32(0x00000115) -#define STATUS_CRASH_DUMP cpu_to_le32(0x00000116) -#define STATUS_BUFFER_ALL_ZEROS cpu_to_le32(0x00000117) -#define STATUS_REPARSE_OBJECT cpu_to_le32(0x00000118) -#define STATUS_RESOURCE_REQUIREMENTS_CHANGED cpu_to_le32(0x00000119) -#define STATUS_TRANSLATION_COMPLETE cpu_to_le32(0x00000120) -#define STATUS_DS_MEMBERSHIP_EVALUATED_LOCALLY cpu_to_le32(0x00000121) -#define STATUS_NOTHING_TO_TERMINATE cpu_to_le32(0x00000122) -#define STATUS_PROCESS_NOT_IN_JOB cpu_to_le32(0x00000123) -#define STATUS_PROCESS_IN_JOB cpu_to_le32(0x00000124) -#define STATUS_VOLSNAP_HIBERNATE_READY cpu_to_le32(0x00000125) -#define STATUS_FSFILTER_OP_COMPLETED_SUCCESSFULLY cpu_to_le32(0x00000126) -#define STATUS_INTERRUPT_VECTOR_ALREADY_CONNECTED cpu_to_le32(0x00000127) -#define STATUS_INTERRUPT_STILL_CONNECTED cpu_to_le32(0x00000128) -#define STATUS_PROCESS_CLONED cpu_to_le32(0x00000129) -#define STATUS_FILE_LOCKED_WITH_ONLY_READERS cpu_to_le32(0x0000012A) -#define STATUS_FILE_LOCKED_WITH_WRITERS cpu_to_le32(0x0000012B) -#define STATUS_RESOURCEMANAGER_READ_ONLY cpu_to_le32(0x00000202) -#define STATUS_WAIT_FOR_OPLOCK cpu_to_le32(0x00000367) -#define DBG_EXCEPTION_HANDLED cpu_to_le32(0x00010001) -#define DBG_CONTINUE cpu_to_le32(0x00010002) -#define STATUS_FLT_IO_COMPLETE cpu_to_le32(0x001C0001) -#define STATUS_OBJECT_NAME_EXISTS cpu_to_le32(0x40000000) -#define STATUS_THREAD_WAS_SUSPENDED cpu_to_le32(0x40000001) -#define STATUS_WORKING_SET_LIMIT_RANGE cpu_to_le32(0x40000002) -#define STATUS_IMAGE_NOT_AT_BASE cpu_to_le32(0x40000003) -#define STATUS_RXACT_STATE_CREATED cpu_to_le32(0x40000004) -#define STATUS_SEGMENT_NOTIFICATION cpu_to_le32(0x40000005) -#define STATUS_LOCAL_USER_SESSION_KEY cpu_to_le32(0x40000006) -#define STATUS_BAD_CURRENT_DIRECTORY cpu_to_le32(0x40000007) -#define STATUS_SERIAL_MORE_WRITES cpu_to_le32(0x40000008) -#define STATUS_REGISTRY_RECOVERED cpu_to_le32(0x40000009) -#define STATUS_FT_READ_RECOVERY_FROM_BACKUP cpu_to_le32(0x4000000A) -#define STATUS_FT_WRITE_RECOVERY cpu_to_le32(0x4000000B) -#define STATUS_SERIAL_COUNTER_TIMEOUT cpu_to_le32(0x4000000C) -#define STATUS_NULL_LM_PASSWORD cpu_to_le32(0x4000000D) -#define STATUS_IMAGE_MACHINE_TYPE_MISMATCH cpu_to_le32(0x4000000E) -#define STATUS_RECEIVE_PARTIAL cpu_to_le32(0x4000000F) -#define STATUS_RECEIVE_EXPEDITED cpu_to_le32(0x40000010) -#define STATUS_RECEIVE_PARTIAL_EXPEDITED cpu_to_le32(0x40000011) -#define STATUS_EVENT_DONE cpu_to_le32(0x40000012) -#define STATUS_EVENT_PENDING cpu_to_le32(0x40000013) -#define STATUS_CHECKING_FILE_SYSTEM cpu_to_le32(0x40000014) -#define STATUS_FATAL_APP_EXIT cpu_to_le32(0x40000015) -#define STATUS_PREDEFINED_HANDLE cpu_to_le32(0x40000016) -#define STATUS_WAS_UNLOCKED cpu_to_le32(0x40000017) -#define STATUS_SERVICE_NOTIFICATION cpu_to_le32(0x40000018) -#define STATUS_WAS_LOCKED cpu_to_le32(0x40000019) -#define STATUS_LOG_HARD_ERROR cpu_to_le32(0x4000001A) -#define STATUS_ALREADY_WIN32 cpu_to_le32(0x4000001B) -#define STATUS_WX86_UNSIMULATE cpu_to_le32(0x4000001C) -#define STATUS_WX86_CONTINUE cpu_to_le32(0x4000001D) -#define STATUS_WX86_SINGLE_STEP cpu_to_le32(0x4000001E) -#define STATUS_WX86_BREAKPOINT cpu_to_le32(0x4000001F) -#define STATUS_WX86_EXCEPTION_CONTINUE cpu_to_le32(0x40000020) -#define STATUS_WX86_EXCEPTION_LASTCHANCE cpu_to_le32(0x40000021) -#define STATUS_WX86_EXCEPTION_CHAIN cpu_to_le32(0x40000022) -#define STATUS_IMAGE_MACHINE_TYPE_MISMATCH_EXE cpu_to_le32(0x40000023) -#define STATUS_NO_YIELD_PERFORMED cpu_to_le32(0x40000024) -#define STATUS_TIMER_RESUME_IGNORED cpu_to_le32(0x40000025) -#define STATUS_ARBITRATION_UNHANDLED cpu_to_le32(0x40000026) -#define STATUS_CARDBUS_NOT_SUPPORTED cpu_to_le32(0x40000027) -#define STATUS_WX86_CREATEWX86TIB cpu_to_le32(0x40000028) -#define STATUS_MP_PROCESSOR_MISMATCH cpu_to_le32(0x40000029) -#define STATUS_HIBERNATED cpu_to_le32(0x4000002A) -#define STATUS_RESUME_HIBERNATION cpu_to_le32(0x4000002B) -#define STATUS_FIRMWARE_UPDATED cpu_to_le32(0x4000002C) -#define STATUS_DRIVERS_LEAKING_LOCKED_PAGES cpu_to_le32(0x4000002D) -#define STATUS_MESSAGE_RETRIEVED cpu_to_le32(0x4000002E) -#define STATUS_SYSTEM_POWERSTATE_TRANSITION cpu_to_le32(0x4000002F) -#define STATUS_ALPC_CHECK_COMPLETION_LIST cpu_to_le32(0x40000030) -#define STATUS_SYSTEM_POWERSTATE_COMPLEX_TRANSITION cpu_to_le32(0x40000031) -#define STATUS_ACCESS_AUDIT_BY_POLICY cpu_to_le32(0x40000032) -#define STATUS_ABANDON_HIBERFILE cpu_to_le32(0x40000033) -#define STATUS_BIZRULES_NOT_ENABLED cpu_to_le32(0x40000034) -#define STATUS_WAKE_SYSTEM cpu_to_le32(0x40000294) -#define STATUS_DS_SHUTTING_DOWN cpu_to_le32(0x40000370) -#define DBG_REPLY_LATER cpu_to_le32(0x40010001) -#define DBG_UNABLE_TO_PROVIDE_HANDLE cpu_to_le32(0x40010002) -#define DBG_TERMINATE_THREAD cpu_to_le32(0x40010003) -#define DBG_TERMINATE_PROCESS cpu_to_le32(0x40010004) -#define DBG_CONTROL_C cpu_to_le32(0x40010005) -#define DBG_PRINTEXCEPTION_C cpu_to_le32(0x40010006) -#define DBG_RIPEXCEPTION cpu_to_le32(0x40010007) -#define DBG_CONTROL_BREAK cpu_to_le32(0x40010008) -#define DBG_COMMAND_EXCEPTION cpu_to_le32(0x40010009) -#define RPC_NT_UUID_LOCAL_ONLY cpu_to_le32(0x40020056) -#define RPC_NT_SEND_INCOMPLETE cpu_to_le32(0x400200AF) -#define STATUS_CTX_CDM_CONNECT cpu_to_le32(0x400A0004) -#define STATUS_CTX_CDM_DISCONNECT cpu_to_le32(0x400A0005) -#define STATUS_SXS_RELEASE_ACTIVATION_CONTEXT cpu_to_le32(0x4015000D) -#define STATUS_RECOVERY_NOT_NEEDED cpu_to_le32(0x40190034) -#define STATUS_RM_ALREADY_STARTED cpu_to_le32(0x40190035) -#define STATUS_LOG_NO_RESTART cpu_to_le32(0x401A000C) -#define STATUS_VIDEO_DRIVER_DEBUG_REPORT_REQUEST cpu_to_le32(0x401B00EC) -#define STATUS_GRAPHICS_PARTIAL_DATA_POPULATED cpu_to_le32(0x401E000A) -#define STATUS_GRAPHICS_DRIVER_MISMATCH cpu_to_le32(0x401E0117) -#define STATUS_GRAPHICS_MODE_NOT_PINNED cpu_to_le32(0x401E0307) -#define STATUS_GRAPHICS_NO_PREFERRED_MODE cpu_to_le32(0x401E031E) -#define STATUS_GRAPHICS_DATASET_IS_EMPTY cpu_to_le32(0x401E034B) -#define STATUS_GRAPHICS_NO_MORE_ELEMENTS_IN_DATASET cpu_to_le32(0x401E034C) -#define STATUS_GRAPHICS_PATH_CONTENT_GEOMETRY_TRANSFORMATION_NOT_PINNED cpu_to_le32(0x401E0351) -#define STATUS_GRAPHICS_UNKNOWN_CHILD_STATUS cpu_to_le32(0x401E042F) -#define STATUS_GRAPHICS_LEADLINK_START_DEFERRED cpu_to_le32(0x401E0437) -#define STATUS_GRAPHICS_POLLING_TOO_FREQUENTLY cpu_to_le32(0x401E0439) -#define STATUS_GRAPHICS_START_DEFERRED cpu_to_le32(0x401E043A) -#define STATUS_NDIS_INDICATION_REQUIRED cpu_to_le32(0x40230001) -#define STATUS_GUARD_PAGE_VIOLATION cpu_to_le32(0x80000001) -#define STATUS_DATATYPE_MISALIGNMENT cpu_to_le32(0x80000002) -#define STATUS_BREAKPOINT cpu_to_le32(0x80000003) -#define STATUS_SINGLE_STEP cpu_to_le32(0x80000004) -#define STATUS_BUFFER_OVERFLOW cpu_to_le32(0x80000005) -#define STATUS_NO_MORE_FILES cpu_to_le32(0x80000006) -#define STATUS_WAKE_SYSTEM_DEBUGGER cpu_to_le32(0x80000007) -#define STATUS_HANDLES_CLOSED cpu_to_le32(0x8000000A) -#define STATUS_NO_INHERITANCE cpu_to_le32(0x8000000B) -#define STATUS_GUID_SUBSTITUTION_MADE cpu_to_le32(0x8000000C) -#define STATUS_PARTIAL_COPY cpu_to_le32(0x8000000D) -#define STATUS_DEVICE_PAPER_EMPTY cpu_to_le32(0x8000000E) -#define STATUS_DEVICE_POWERED_OFF cpu_to_le32(0x8000000F) -#define STATUS_DEVICE_OFF_LINE cpu_to_le32(0x80000010) -#define STATUS_DEVICE_BUSY cpu_to_le32(0x80000011) -#define STATUS_NO_MORE_EAS cpu_to_le32(0x80000012) -#define STATUS_INVALID_EA_NAME cpu_to_le32(0x80000013) -#define STATUS_EA_LIST_INCONSISTENT cpu_to_le32(0x80000014) -#define STATUS_INVALID_EA_FLAG cpu_to_le32(0x80000015) -#define STATUS_VERIFY_REQUIRED cpu_to_le32(0x80000016) -#define STATUS_EXTRANEOUS_INFORMATION cpu_to_le32(0x80000017) -#define STATUS_RXACT_COMMIT_NECESSARY cpu_to_le32(0x80000018) -#define STATUS_NO_MORE_ENTRIES cpu_to_le32(0x8000001A) -#define STATUS_FILEMARK_DETECTED cpu_to_le32(0x8000001B) -#define STATUS_MEDIA_CHANGED cpu_to_le32(0x8000001C) -#define STATUS_BUS_RESET cpu_to_le32(0x8000001D) -#define STATUS_END_OF_MEDIA cpu_to_le32(0x8000001E) -#define STATUS_BEGINNING_OF_MEDIA cpu_to_le32(0x8000001F) -#define STATUS_MEDIA_CHECK cpu_to_le32(0x80000020) -#define STATUS_SETMARK_DETECTED cpu_to_le32(0x80000021) -#define STATUS_NO_DATA_DETECTED cpu_to_le32(0x80000022) -#define STATUS_REDIRECTOR_HAS_OPEN_HANDLES cpu_to_le32(0x80000023) -#define STATUS_SERVER_HAS_OPEN_HANDLES cpu_to_le32(0x80000024) -#define STATUS_ALREADY_DISCONNECTED cpu_to_le32(0x80000025) -#define STATUS_LONGJUMP cpu_to_le32(0x80000026) -#define STATUS_CLEANER_CARTRIDGE_INSTALLED cpu_to_le32(0x80000027) -#define STATUS_PLUGPLAY_QUERY_VETOED cpu_to_le32(0x80000028) -#define STATUS_UNWIND_CONSOLIDATE cpu_to_le32(0x80000029) -#define STATUS_REGISTRY_HIVE_RECOVERED cpu_to_le32(0x8000002A) -#define STATUS_DLL_MIGHT_BE_INSECURE cpu_to_le32(0x8000002B) -#define STATUS_DLL_MIGHT_BE_INCOMPATIBLE cpu_to_le32(0x8000002C) -#define STATUS_STOPPED_ON_SYMLINK cpu_to_le32(0x8000002D) -#define STATUS_DEVICE_REQUIRES_CLEANING cpu_to_le32(0x80000288) -#define STATUS_DEVICE_DOOR_OPEN cpu_to_le32(0x80000289) -#define STATUS_DATA_LOST_REPAIR cpu_to_le32(0x80000803) -#define DBG_EXCEPTION_NOT_HANDLED cpu_to_le32(0x80010001) -#define STATUS_CLUSTER_NODE_ALREADY_UP cpu_to_le32(0x80130001) -#define STATUS_CLUSTER_NODE_ALREADY_DOWN cpu_to_le32(0x80130002) -#define STATUS_CLUSTER_NETWORK_ALREADY_ONLINE cpu_to_le32(0x80130003) -#define STATUS_CLUSTER_NETWORK_ALREADY_OFFLINE cpu_to_le32(0x80130004) -#define STATUS_CLUSTER_NODE_ALREADY_MEMBER cpu_to_le32(0x80130005) -#define STATUS_COULD_NOT_RESIZE_LOG cpu_to_le32(0x80190009) -#define STATUS_NO_TXF_METADATA cpu_to_le32(0x80190029) -#define STATUS_CANT_RECOVER_WITH_HANDLE_OPEN cpu_to_le32(0x80190031) -#define STATUS_TXF_METADATA_ALREADY_PRESENT cpu_to_le32(0x80190041) -#define STATUS_TRANSACTION_SCOPE_CALLBACKS_NOT_SET cpu_to_le32(0x80190042) -#define STATUS_VIDEO_HUNG_DISPLAY_DRIVER_THREAD_RECOVERED cpu_to_le32(0x801B00EB) -#define STATUS_FLT_BUFFER_TOO_SMALL cpu_to_le32(0x801C0001) -#define STATUS_FVE_PARTIAL_METADATA cpu_to_le32(0x80210001) -#define STATUS_UNSUCCESSFUL cpu_to_le32(0xC0000001) -#define STATUS_NOT_IMPLEMENTED cpu_to_le32(0xC0000002) -#define STATUS_INVALID_INFO_CLASS cpu_to_le32(0xC0000003) -#define STATUS_INFO_LENGTH_MISMATCH cpu_to_le32(0xC0000004) -#define STATUS_ACCESS_VIOLATION cpu_to_le32(0xC0000005) -#define STATUS_IN_PAGE_ERROR cpu_to_le32(0xC0000006) -#define STATUS_PAGEFILE_QUOTA cpu_to_le32(0xC0000007) -#define STATUS_INVALID_HANDLE cpu_to_le32(0xC0000008) -#define STATUS_BAD_INITIAL_STACK cpu_to_le32(0xC0000009) -#define STATUS_BAD_INITIAL_PC cpu_to_le32(0xC000000A) -#define STATUS_INVALID_CID cpu_to_le32(0xC000000B) -#define STATUS_TIMER_NOT_CANCELED cpu_to_le32(0xC000000C) -#define STATUS_INVALID_PARAMETER cpu_to_le32(0xC000000D) -#define STATUS_NO_SUCH_DEVICE cpu_to_le32(0xC000000E) -#define STATUS_NO_SUCH_FILE cpu_to_le32(0xC000000F) -#define STATUS_INVALID_DEVICE_REQUEST cpu_to_le32(0xC0000010) -#define STATUS_END_OF_FILE cpu_to_le32(0xC0000011) -#define STATUS_WRONG_VOLUME cpu_to_le32(0xC0000012) -#define STATUS_NO_MEDIA_IN_DEVICE cpu_to_le32(0xC0000013) -#define STATUS_UNRECOGNIZED_MEDIA cpu_to_le32(0xC0000014) -#define STATUS_NONEXISTENT_SECTOR cpu_to_le32(0xC0000015) -#define STATUS_MORE_PROCESSING_REQUIRED cpu_to_le32(0xC0000016) -#define STATUS_NO_MEMORY cpu_to_le32(0xC0000017) -#define STATUS_CONFLICTING_ADDRESSES cpu_to_le32(0xC0000018) -#define STATUS_NOT_MAPPED_VIEW cpu_to_le32(0xC0000019) -#define STATUS_UNABLE_TO_FREE_VM cpu_to_le32(0xC000001A) -#define STATUS_UNABLE_TO_DELETE_SECTION cpu_to_le32(0xC000001B) -#define STATUS_INVALID_SYSTEM_SERVICE cpu_to_le32(0xC000001C) -#define STATUS_ILLEGAL_INSTRUCTION cpu_to_le32(0xC000001D) -#define STATUS_INVALID_LOCK_SEQUENCE cpu_to_le32(0xC000001E) -#define STATUS_INVALID_VIEW_SIZE cpu_to_le32(0xC000001F) -#define STATUS_INVALID_FILE_FOR_SECTION cpu_to_le32(0xC0000020) -#define STATUS_ALREADY_COMMITTED cpu_to_le32(0xC0000021) -#define STATUS_ACCESS_DENIED cpu_to_le32(0xC0000022) -#define STATUS_BUFFER_TOO_SMALL cpu_to_le32(0xC0000023) -#define STATUS_OBJECT_TYPE_MISMATCH cpu_to_le32(0xC0000024) -#define STATUS_NONCONTINUABLE_EXCEPTION cpu_to_le32(0xC0000025) -#define STATUS_INVALID_DISPOSITION cpu_to_le32(0xC0000026) -#define STATUS_UNWIND cpu_to_le32(0xC0000027) -#define STATUS_BAD_STACK cpu_to_le32(0xC0000028) -#define STATUS_INVALID_UNWIND_TARGET cpu_to_le32(0xC0000029) -#define STATUS_NOT_LOCKED cpu_to_le32(0xC000002A) -#define STATUS_PARITY_ERROR cpu_to_le32(0xC000002B) -#define STATUS_UNABLE_TO_DECOMMIT_VM cpu_to_le32(0xC000002C) -#define STATUS_NOT_COMMITTED cpu_to_le32(0xC000002D) -#define STATUS_INVALID_PORT_ATTRIBUTES cpu_to_le32(0xC000002E) -#define STATUS_PORT_MESSAGE_TOO_LONG cpu_to_le32(0xC000002F) -#define STATUS_INVALID_PARAMETER_MIX cpu_to_le32(0xC0000030) -#define STATUS_INVALID_QUOTA_LOWER cpu_to_le32(0xC0000031) -#define STATUS_DISK_CORRUPT_ERROR cpu_to_le32(0xC0000032) -#define STATUS_OBJECT_NAME_INVALID cpu_to_le32(0xC0000033) -#define STATUS_OBJECT_NAME_NOT_FOUND cpu_to_le32(0xC0000034) -#define STATUS_OBJECT_NAME_COLLISION cpu_to_le32(0xC0000035) -#define STATUS_PORT_DISCONNECTED cpu_to_le32(0xC0000037) -#define STATUS_DEVICE_ALREADY_ATTACHED cpu_to_le32(0xC0000038) -#define STATUS_OBJECT_PATH_INVALID cpu_to_le32(0xC0000039) -#define STATUS_OBJECT_PATH_NOT_FOUND cpu_to_le32(0xC000003A) -#define STATUS_OBJECT_PATH_SYNTAX_BAD cpu_to_le32(0xC000003B) -#define STATUS_DATA_OVERRUN cpu_to_le32(0xC000003C) -#define STATUS_DATA_LATE_ERROR cpu_to_le32(0xC000003D) -#define STATUS_DATA_ERROR cpu_to_le32(0xC000003E) -#define STATUS_CRC_ERROR cpu_to_le32(0xC000003F) -#define STATUS_SECTION_TOO_BIG cpu_to_le32(0xC0000040) -#define STATUS_PORT_CONNECTION_REFUSED cpu_to_le32(0xC0000041) -#define STATUS_INVALID_PORT_HANDLE cpu_to_le32(0xC0000042) -#define STATUS_SHARING_VIOLATION cpu_to_le32(0xC0000043) -#define STATUS_QUOTA_EXCEEDED cpu_to_le32(0xC0000044) -#define STATUS_INVALID_PAGE_PROTECTION cpu_to_le32(0xC0000045) -#define STATUS_MUTANT_NOT_OWNED cpu_to_le32(0xC0000046) -#define STATUS_SEMAPHORE_LIMIT_EXCEEDED cpu_to_le32(0xC0000047) -#define STATUS_PORT_ALREADY_SET cpu_to_le32(0xC0000048) -#define STATUS_SECTION_NOT_IMAGE cpu_to_le32(0xC0000049) -#define STATUS_SUSPEND_COUNT_EXCEEDED cpu_to_le32(0xC000004A) -#define STATUS_THREAD_IS_TERMINATING cpu_to_le32(0xC000004B) -#define STATUS_BAD_WORKING_SET_LIMIT cpu_to_le32(0xC000004C) -#define STATUS_INCOMPATIBLE_FILE_MAP cpu_to_le32(0xC000004D) -#define STATUS_SECTION_PROTECTION cpu_to_le32(0xC000004E) -#define STATUS_EAS_NOT_SUPPORTED cpu_to_le32(0xC000004F) -#define STATUS_EA_TOO_LARGE cpu_to_le32(0xC0000050) -#define STATUS_NONEXISTENT_EA_ENTRY cpu_to_le32(0xC0000051) -#define STATUS_NO_EAS_ON_FILE cpu_to_le32(0xC0000052) -#define STATUS_EA_CORRUPT_ERROR cpu_to_le32(0xC0000053) -#define STATUS_FILE_LOCK_CONFLICT cpu_to_le32(0xC0000054) -#define STATUS_LOCK_NOT_GRANTED cpu_to_le32(0xC0000055) -#define STATUS_DELETE_PENDING cpu_to_le32(0xC0000056) -#define STATUS_CTL_FILE_NOT_SUPPORTED cpu_to_le32(0xC0000057) -#define STATUS_UNKNOWN_REVISION cpu_to_le32(0xC0000058) -#define STATUS_REVISION_MISMATCH cpu_to_le32(0xC0000059) -#define STATUS_INVALID_OWNER cpu_to_le32(0xC000005A) -#define STATUS_INVALID_PRIMARY_GROUP cpu_to_le32(0xC000005B) -#define STATUS_NO_IMPERSONATION_TOKEN cpu_to_le32(0xC000005C) -#define STATUS_CANT_DISABLE_MANDATORY cpu_to_le32(0xC000005D) -#define STATUS_NO_LOGON_SERVERS cpu_to_le32(0xC000005E) -#define STATUS_NO_SUCH_LOGON_SESSION cpu_to_le32(0xC000005F) -#define STATUS_NO_SUCH_PRIVILEGE cpu_to_le32(0xC0000060) -#define STATUS_PRIVILEGE_NOT_HELD cpu_to_le32(0xC0000061) -#define STATUS_INVALID_ACCOUNT_NAME cpu_to_le32(0xC0000062) -#define STATUS_USER_EXISTS cpu_to_le32(0xC0000063) -#define STATUS_NO_SUCH_USER cpu_to_le32(0xC0000064) -#define STATUS_GROUP_EXISTS cpu_to_le32(0xC0000065) -#define STATUS_NO_SUCH_GROUP cpu_to_le32(0xC0000066) -#define STATUS_MEMBER_IN_GROUP cpu_to_le32(0xC0000067) -#define STATUS_MEMBER_NOT_IN_GROUP cpu_to_le32(0xC0000068) -#define STATUS_LAST_ADMIN cpu_to_le32(0xC0000069) -#define STATUS_WRONG_PASSWORD cpu_to_le32(0xC000006A) -#define STATUS_ILL_FORMED_PASSWORD cpu_to_le32(0xC000006B) -#define STATUS_PASSWORD_RESTRICTION cpu_to_le32(0xC000006C) -#define STATUS_LOGON_FAILURE cpu_to_le32(0xC000006D) -#define STATUS_ACCOUNT_RESTRICTION cpu_to_le32(0xC000006E) -#define STATUS_INVALID_LOGON_HOURS cpu_to_le32(0xC000006F) -#define STATUS_INVALID_WORKSTATION cpu_to_le32(0xC0000070) -#define STATUS_PASSWORD_EXPIRED cpu_to_le32(0xC0000071) -#define STATUS_ACCOUNT_DISABLED cpu_to_le32(0xC0000072) -#define STATUS_NONE_MAPPED cpu_to_le32(0xC0000073) -#define STATUS_TOO_MANY_LUIDS_REQUESTED cpu_to_le32(0xC0000074) -#define STATUS_LUIDS_EXHAUSTED cpu_to_le32(0xC0000075) -#define STATUS_INVALID_SUB_AUTHORITY cpu_to_le32(0xC0000076) -#define STATUS_INVALID_ACL cpu_to_le32(0xC0000077) -#define STATUS_INVALID_SID cpu_to_le32(0xC0000078) -#define STATUS_INVALID_SECURITY_DESCR cpu_to_le32(0xC0000079) -#define STATUS_PROCEDURE_NOT_FOUND cpu_to_le32(0xC000007A) -#define STATUS_INVALID_IMAGE_FORMAT cpu_to_le32(0xC000007B) -#define STATUS_NO_TOKEN cpu_to_le32(0xC000007C) -#define STATUS_BAD_INHERITANCE_ACL cpu_to_le32(0xC000007D) -#define STATUS_RANGE_NOT_LOCKED cpu_to_le32(0xC000007E) -#define STATUS_DISK_FULL cpu_to_le32(0xC000007F) -#define STATUS_SERVER_DISABLED cpu_to_le32(0xC0000080) -#define STATUS_SERVER_NOT_DISABLED cpu_to_le32(0xC0000081) -#define STATUS_TOO_MANY_GUIDS_REQUESTED cpu_to_le32(0xC0000082) -#define STATUS_GUIDS_EXHAUSTED cpu_to_le32(0xC0000083) -#define STATUS_INVALID_ID_AUTHORITY cpu_to_le32(0xC0000084) -#define STATUS_AGENTS_EXHAUSTED cpu_to_le32(0xC0000085) -#define STATUS_INVALID_VOLUME_LABEL cpu_to_le32(0xC0000086) -#define STATUS_SECTION_NOT_EXTENDED cpu_to_le32(0xC0000087) -#define STATUS_NOT_MAPPED_DATA cpu_to_le32(0xC0000088) -#define STATUS_RESOURCE_DATA_NOT_FOUND cpu_to_le32(0xC0000089) -#define STATUS_RESOURCE_TYPE_NOT_FOUND cpu_to_le32(0xC000008A) -#define STATUS_RESOURCE_NAME_NOT_FOUND cpu_to_le32(0xC000008B) -#define STATUS_ARRAY_BOUNDS_EXCEEDED cpu_to_le32(0xC000008C) -#define STATUS_FLOAT_DENORMAL_OPERAND cpu_to_le32(0xC000008D) -#define STATUS_FLOAT_DIVIDE_BY_ZERO cpu_to_le32(0xC000008E) -#define STATUS_FLOAT_INEXACT_RESULT cpu_to_le32(0xC000008F) -#define STATUS_FLOAT_INVALID_OPERATION cpu_to_le32(0xC0000090) -#define STATUS_FLOAT_OVERFLOW cpu_to_le32(0xC0000091) -#define STATUS_FLOAT_STACK_CHECK cpu_to_le32(0xC0000092) -#define STATUS_FLOAT_UNDERFLOW cpu_to_le32(0xC0000093) -#define STATUS_INTEGER_DIVIDE_BY_ZERO cpu_to_le32(0xC0000094) -#define STATUS_INTEGER_OVERFLOW cpu_to_le32(0xC0000095) -#define STATUS_PRIVILEGED_INSTRUCTION cpu_to_le32(0xC0000096) -#define STATUS_TOO_MANY_PAGING_FILES cpu_to_le32(0xC0000097) -#define STATUS_FILE_INVALID cpu_to_le32(0xC0000098) -#define STATUS_ALLOTTED_SPACE_EXCEEDED cpu_to_le32(0xC0000099) -#define STATUS_INSUFFICIENT_RESOURCES cpu_to_le32(0xC000009A) -#define STATUS_DFS_EXIT_PATH_FOUND cpu_to_le32(0xC000009B) -#define STATUS_DEVICE_DATA_ERROR cpu_to_le32(0xC000009C) -#define STATUS_DEVICE_NOT_CONNECTED cpu_to_le32(0xC000009D) -#define STATUS_DEVICE_POWER_FAILURE cpu_to_le32(0xC000009E) -#define STATUS_FREE_VM_NOT_AT_BASE cpu_to_le32(0xC000009F) -#define STATUS_MEMORY_NOT_ALLOCATED cpu_to_le32(0xC00000A0) -#define STATUS_WORKING_SET_QUOTA cpu_to_le32(0xC00000A1) -#define STATUS_MEDIA_WRITE_PROTECTED cpu_to_le32(0xC00000A2) -#define STATUS_DEVICE_NOT_READY cpu_to_le32(0xC00000A3) -#define STATUS_INVALID_GROUP_ATTRIBUTES cpu_to_le32(0xC00000A4) -#define STATUS_BAD_IMPERSONATION_LEVEL cpu_to_le32(0xC00000A5) -#define STATUS_CANT_OPEN_ANONYMOUS cpu_to_le32(0xC00000A6) -#define STATUS_BAD_VALIDATION_CLASS cpu_to_le32(0xC00000A7) -#define STATUS_BAD_TOKEN_TYPE cpu_to_le32(0xC00000A8) -#define STATUS_BAD_MASTER_BOOT_RECORD cpu_to_le32(0xC00000A9) -#define STATUS_INSTRUCTION_MISALIGNMENT cpu_to_le32(0xC00000AA) -#define STATUS_INSTANCE_NOT_AVAILABLE cpu_to_le32(0xC00000AB) -#define STATUS_PIPE_NOT_AVAILABLE cpu_to_le32(0xC00000AC) -#define STATUS_INVALID_PIPE_STATE cpu_to_le32(0xC00000AD) -#define STATUS_PIPE_BUSY cpu_to_le32(0xC00000AE) -#define STATUS_ILLEGAL_FUNCTION cpu_to_le32(0xC00000AF) -#define STATUS_PIPE_DISCONNECTED cpu_to_le32(0xC00000B0) -#define STATUS_PIPE_CLOSING cpu_to_le32(0xC00000B1) -#define STATUS_PIPE_CONNECTED cpu_to_le32(0xC00000B2) -#define STATUS_PIPE_LISTENING cpu_to_le32(0xC00000B3) -#define STATUS_INVALID_READ_MODE cpu_to_le32(0xC00000B4) -#define STATUS_IO_TIMEOUT cpu_to_le32(0xC00000B5) -#define STATUS_FILE_FORCED_CLOSED cpu_to_le32(0xC00000B6) -#define STATUS_PROFILING_NOT_STARTED cpu_to_le32(0xC00000B7) -#define STATUS_PROFILING_NOT_STOPPED cpu_to_le32(0xC00000B8) -#define STATUS_COULD_NOT_INTERPRET cpu_to_le32(0xC00000B9) -#define STATUS_FILE_IS_A_DIRECTORY cpu_to_le32(0xC00000BA) -#define STATUS_NOT_SUPPORTED cpu_to_le32(0xC00000BB) -#define STATUS_REMOTE_NOT_LISTENING cpu_to_le32(0xC00000BC) -#define STATUS_DUPLICATE_NAME cpu_to_le32(0xC00000BD) -#define STATUS_BAD_NETWORK_PATH cpu_to_le32(0xC00000BE) -#define STATUS_NETWORK_BUSY cpu_to_le32(0xC00000BF) -#define STATUS_DEVICE_DOES_NOT_EXIST cpu_to_le32(0xC00000C0) -#define STATUS_TOO_MANY_COMMANDS cpu_to_le32(0xC00000C1) -#define STATUS_ADAPTER_HARDWARE_ERROR cpu_to_le32(0xC00000C2) -#define STATUS_INVALID_NETWORK_RESPONSE cpu_to_le32(0xC00000C3) -#define STATUS_UNEXPECTED_NETWORK_ERROR cpu_to_le32(0xC00000C4) -#define STATUS_BAD_REMOTE_ADAPTER cpu_to_le32(0xC00000C5) -#define STATUS_PRINT_QUEUE_FULL cpu_to_le32(0xC00000C6) -#define STATUS_NO_SPOOL_SPACE cpu_to_le32(0xC00000C7) -#define STATUS_PRINT_CANCELLED cpu_to_le32(0xC00000C8) -#define STATUS_NETWORK_NAME_DELETED cpu_to_le32(0xC00000C9) -#define STATUS_NETWORK_ACCESS_DENIED cpu_to_le32(0xC00000CA) -#define STATUS_BAD_DEVICE_TYPE cpu_to_le32(0xC00000CB) -#define STATUS_BAD_NETWORK_NAME cpu_to_le32(0xC00000CC) -#define STATUS_TOO_MANY_NAMES cpu_to_le32(0xC00000CD) -#define STATUS_TOO_MANY_SESSIONS cpu_to_le32(0xC00000CE) -#define STATUS_SHARING_PAUSED cpu_to_le32(0xC00000CF) -#define STATUS_REQUEST_NOT_ACCEPTED cpu_to_le32(0xC00000D0) -#define STATUS_REDIRECTOR_PAUSED cpu_to_le32(0xC00000D1) -#define STATUS_NET_WRITE_FAULT cpu_to_le32(0xC00000D2) -#define STATUS_PROFILING_AT_LIMIT cpu_to_le32(0xC00000D3) -#define STATUS_NOT_SAME_DEVICE cpu_to_le32(0xC00000D4) -#define STATUS_FILE_RENAMED cpu_to_le32(0xC00000D5) -#define STATUS_VIRTUAL_CIRCUIT_CLOSED cpu_to_le32(0xC00000D6) -#define STATUS_NO_SECURITY_ON_OBJECT cpu_to_le32(0xC00000D7) -#define STATUS_CANT_WAIT cpu_to_le32(0xC00000D8) -#define STATUS_PIPE_EMPTY cpu_to_le32(0xC00000D9) -#define STATUS_CANT_ACCESS_DOMAIN_INFO cpu_to_le32(0xC00000DA) -#define STATUS_CANT_TERMINATE_SELF cpu_to_le32(0xC00000DB) -#define STATUS_INVALID_SERVER_STATE cpu_to_le32(0xC00000DC) -#define STATUS_INVALID_DOMAIN_STATE cpu_to_le32(0xC00000DD) -#define STATUS_INVALID_DOMAIN_ROLE cpu_to_le32(0xC00000DE) -#define STATUS_NO_SUCH_DOMAIN cpu_to_le32(0xC00000DF) -#define STATUS_DOMAIN_EXISTS cpu_to_le32(0xC00000E0) -#define STATUS_DOMAIN_LIMIT_EXCEEDED cpu_to_le32(0xC00000E1) -#define STATUS_OPLOCK_NOT_GRANTED cpu_to_le32(0xC00000E2) -#define STATUS_INVALID_OPLOCK_PROTOCOL cpu_to_le32(0xC00000E3) -#define STATUS_INTERNAL_DB_CORRUPTION cpu_to_le32(0xC00000E4) -#define STATUS_INTERNAL_ERROR cpu_to_le32(0xC00000E5) -#define STATUS_GENERIC_NOT_MAPPED cpu_to_le32(0xC00000E6) -#define STATUS_BAD_DESCRIPTOR_FORMAT cpu_to_le32(0xC00000E7) -#define STATUS_INVALID_USER_BUFFER cpu_to_le32(0xC00000E8) -#define STATUS_UNEXPECTED_IO_ERROR cpu_to_le32(0xC00000E9) -#define STATUS_UNEXPECTED_MM_CREATE_ERR cpu_to_le32(0xC00000EA) -#define STATUS_UNEXPECTED_MM_MAP_ERROR cpu_to_le32(0xC00000EB) -#define STATUS_UNEXPECTED_MM_EXTEND_ERR cpu_to_le32(0xC00000EC) -#define STATUS_NOT_LOGON_PROCESS cpu_to_le32(0xC00000ED) -#define STATUS_LOGON_SESSION_EXISTS cpu_to_le32(0xC00000EE) -#define STATUS_INVALID_PARAMETER_1 cpu_to_le32(0xC00000EF) -#define STATUS_INVALID_PARAMETER_2 cpu_to_le32(0xC00000F0) -#define STATUS_INVALID_PARAMETER_3 cpu_to_le32(0xC00000F1) -#define STATUS_INVALID_PARAMETER_4 cpu_to_le32(0xC00000F2) -#define STATUS_INVALID_PARAMETER_5 cpu_to_le32(0xC00000F3) -#define STATUS_INVALID_PARAMETER_6 cpu_to_le32(0xC00000F4) -#define STATUS_INVALID_PARAMETER_7 cpu_to_le32(0xC00000F5) -#define STATUS_INVALID_PARAMETER_8 cpu_to_le32(0xC00000F6) -#define STATUS_INVALID_PARAMETER_9 cpu_to_le32(0xC00000F7) -#define STATUS_INVALID_PARAMETER_10 cpu_to_le32(0xC00000F8) -#define STATUS_INVALID_PARAMETER_11 cpu_to_le32(0xC00000F9) -#define STATUS_INVALID_PARAMETER_12 cpu_to_le32(0xC00000FA) -#define STATUS_REDIRECTOR_NOT_STARTED cpu_to_le32(0xC00000FB) -#define STATUS_REDIRECTOR_STARTED cpu_to_le32(0xC00000FC) -#define STATUS_STACK_OVERFLOW cpu_to_le32(0xC00000FD) -#define STATUS_NO_SUCH_PACKAGE cpu_to_le32(0xC00000FE) -#define STATUS_BAD_FUNCTION_TABLE cpu_to_le32(0xC00000FF) -#define STATUS_VARIABLE_NOT_FOUND cpu_to_le32(0xC0000100) -#define STATUS_DIRECTORY_NOT_EMPTY cpu_to_le32(0xC0000101) -#define STATUS_FILE_CORRUPT_ERROR cpu_to_le32(0xC0000102) -#define STATUS_NOT_A_DIRECTORY cpu_to_le32(0xC0000103) -#define STATUS_BAD_LOGON_SESSION_STATE cpu_to_le32(0xC0000104) -#define STATUS_LOGON_SESSION_COLLISION cpu_to_le32(0xC0000105) -#define STATUS_NAME_TOO_LONG cpu_to_le32(0xC0000106) -#define STATUS_FILES_OPEN cpu_to_le32(0xC0000107) -#define STATUS_CONNECTION_IN_USE cpu_to_le32(0xC0000108) -#define STATUS_MESSAGE_NOT_FOUND cpu_to_le32(0xC0000109) -#define STATUS_PROCESS_IS_TERMINATING cpu_to_le32(0xC000010A) -#define STATUS_INVALID_LOGON_TYPE cpu_to_le32(0xC000010B) -#define STATUS_NO_GUID_TRANSLATION cpu_to_le32(0xC000010C) -#define STATUS_CANNOT_IMPERSONATE cpu_to_le32(0xC000010D) -#define STATUS_IMAGE_ALREADY_LOADED cpu_to_le32(0xC000010E) -#define STATUS_ABIOS_NOT_PRESENT cpu_to_le32(0xC000010F) -#define STATUS_ABIOS_LID_NOT_EXIST cpu_to_le32(0xC0000110) -#define STATUS_ABIOS_LID_ALREADY_OWNED cpu_to_le32(0xC0000111) -#define STATUS_ABIOS_NOT_LID_OWNER cpu_to_le32(0xC0000112) -#define STATUS_ABIOS_INVALID_COMMAND cpu_to_le32(0xC0000113) -#define STATUS_ABIOS_INVALID_LID cpu_to_le32(0xC0000114) -#define STATUS_ABIOS_SELECTOR_NOT_AVAILABLE cpu_to_le32(0xC0000115) -#define STATUS_ABIOS_INVALID_SELECTOR cpu_to_le32(0xC0000116) -#define STATUS_NO_LDT cpu_to_le32(0xC0000117) -#define STATUS_INVALID_LDT_SIZE cpu_to_le32(0xC0000118) -#define STATUS_INVALID_LDT_OFFSET cpu_to_le32(0xC0000119) -#define STATUS_INVALID_LDT_DESCRIPTOR cpu_to_le32(0xC000011A) -#define STATUS_INVALID_IMAGE_NE_FORMAT cpu_to_le32(0xC000011B) -#define STATUS_RXACT_INVALID_STATE cpu_to_le32(0xC000011C) -#define STATUS_RXACT_COMMIT_FAILURE cpu_to_le32(0xC000011D) -#define STATUS_MAPPED_FILE_SIZE_ZERO cpu_to_le32(0xC000011E) -#define STATUS_TOO_MANY_OPENED_FILES cpu_to_le32(0xC000011F) -#define STATUS_CANCELLED cpu_to_le32(0xC0000120) -#define STATUS_CANNOT_DELETE cpu_to_le32(0xC0000121) -#define STATUS_INVALID_COMPUTER_NAME cpu_to_le32(0xC0000122) -#define STATUS_FILE_DELETED cpu_to_le32(0xC0000123) -#define STATUS_SPECIAL_ACCOUNT cpu_to_le32(0xC0000124) -#define STATUS_SPECIAL_GROUP cpu_to_le32(0xC0000125) -#define STATUS_SPECIAL_USER cpu_to_le32(0xC0000126) -#define STATUS_MEMBERS_PRIMARY_GROUP cpu_to_le32(0xC0000127) -#define STATUS_FILE_CLOSED cpu_to_le32(0xC0000128) -#define STATUS_TOO_MANY_THREADS cpu_to_le32(0xC0000129) -#define STATUS_THREAD_NOT_IN_PROCESS cpu_to_le32(0xC000012A) -#define STATUS_TOKEN_ALREADY_IN_USE cpu_to_le32(0xC000012B) -#define STATUS_PAGEFILE_QUOTA_EXCEEDED cpu_to_le32(0xC000012C) -#define STATUS_COMMITMENT_LIMIT cpu_to_le32(0xC000012D) -#define STATUS_INVALID_IMAGE_LE_FORMAT cpu_to_le32(0xC000012E) -#define STATUS_INVALID_IMAGE_NOT_MZ cpu_to_le32(0xC000012F) -#define STATUS_INVALID_IMAGE_PROTECT cpu_to_le32(0xC0000130) -#define STATUS_INVALID_IMAGE_WIN_16 cpu_to_le32(0xC0000131) -#define STATUS_LOGON_SERVER_CONFLICT cpu_to_le32(0xC0000132) -#define STATUS_TIME_DIFFERENCE_AT_DC cpu_to_le32(0xC0000133) -#define STATUS_SYNCHRONIZATION_REQUIRED cpu_to_le32(0xC0000134) -#define STATUS_DLL_NOT_FOUND cpu_to_le32(0xC0000135) -#define STATUS_OPEN_FAILED cpu_to_le32(0xC0000136) -#define STATUS_IO_PRIVILEGE_FAILED cpu_to_le32(0xC0000137) -#define STATUS_ORDINAL_NOT_FOUND cpu_to_le32(0xC0000138) -#define STATUS_ENTRYPOINT_NOT_FOUND cpu_to_le32(0xC0000139) -#define STATUS_CONTROL_C_EXIT cpu_to_le32(0xC000013A) -#define STATUS_LOCAL_DISCONNECT cpu_to_le32(0xC000013B) -#define STATUS_REMOTE_DISCONNECT cpu_to_le32(0xC000013C) -#define STATUS_REMOTE_RESOURCES cpu_to_le32(0xC000013D) -#define STATUS_LINK_FAILED cpu_to_le32(0xC000013E) -#define STATUS_LINK_TIMEOUT cpu_to_le32(0xC000013F) -#define STATUS_INVALID_CONNECTION cpu_to_le32(0xC0000140) -#define STATUS_INVALID_ADDRESS cpu_to_le32(0xC0000141) -#define STATUS_DLL_INIT_FAILED cpu_to_le32(0xC0000142) -#define STATUS_MISSING_SYSTEMFILE cpu_to_le32(0xC0000143) -#define STATUS_UNHANDLED_EXCEPTION cpu_to_le32(0xC0000144) -#define STATUS_APP_INIT_FAILURE cpu_to_le32(0xC0000145) -#define STATUS_PAGEFILE_CREATE_FAILED cpu_to_le32(0xC0000146) -#define STATUS_NO_PAGEFILE cpu_to_le32(0xC0000147) -#define STATUS_INVALID_LEVEL cpu_to_le32(0xC0000148) -#define STATUS_WRONG_PASSWORD_CORE cpu_to_le32(0xC0000149) -#define STATUS_ILLEGAL_FLOAT_CONTEXT cpu_to_le32(0xC000014A) -#define STATUS_PIPE_BROKEN cpu_to_le32(0xC000014B) -#define STATUS_REGISTRY_CORRUPT cpu_to_le32(0xC000014C) -#define STATUS_REGISTRY_IO_FAILED cpu_to_le32(0xC000014D) -#define STATUS_NO_EVENT_PAIR cpu_to_le32(0xC000014E) -#define STATUS_UNRECOGNIZED_VOLUME cpu_to_le32(0xC000014F) -#define STATUS_SERIAL_NO_DEVICE_INITED cpu_to_le32(0xC0000150) -#define STATUS_NO_SUCH_ALIAS cpu_to_le32(0xC0000151) -#define STATUS_MEMBER_NOT_IN_ALIAS cpu_to_le32(0xC0000152) -#define STATUS_MEMBER_IN_ALIAS cpu_to_le32(0xC0000153) -#define STATUS_ALIAS_EXISTS cpu_to_le32(0xC0000154) -#define STATUS_LOGON_NOT_GRANTED cpu_to_le32(0xC0000155) -#define STATUS_TOO_MANY_SECRETS cpu_to_le32(0xC0000156) -#define STATUS_SECRET_TOO_LONG cpu_to_le32(0xC0000157) -#define STATUS_INTERNAL_DB_ERROR cpu_to_le32(0xC0000158) -#define STATUS_FULLSCREEN_MODE cpu_to_le32(0xC0000159) -#define STATUS_TOO_MANY_CONTEXT_IDS cpu_to_le32(0xC000015A) -#define STATUS_LOGON_TYPE_NOT_GRANTED cpu_to_le32(0xC000015B) -#define STATUS_NOT_REGISTRY_FILE cpu_to_le32(0xC000015C) -#define STATUS_NT_CROSS_ENCRYPTION_REQUIRED cpu_to_le32(0xC000015D) -#define STATUS_DOMAIN_CTRLR_CONFIG_ERROR cpu_to_le32(0xC000015E) -#define STATUS_FT_MISSING_MEMBER cpu_to_le32(0xC000015F) -#define STATUS_ILL_FORMED_SERVICE_ENTRY cpu_to_le32(0xC0000160) -#define STATUS_ILLEGAL_CHARACTER cpu_to_le32(0xC0000161) -#define STATUS_UNMAPPABLE_CHARACTER cpu_to_le32(0xC0000162) -#define STATUS_UNDEFINED_CHARACTER cpu_to_le32(0xC0000163) -#define STATUS_FLOPPY_VOLUME cpu_to_le32(0xC0000164) -#define STATUS_FLOPPY_ID_MARK_NOT_FOUND cpu_to_le32(0xC0000165) -#define STATUS_FLOPPY_WRONG_CYLINDER cpu_to_le32(0xC0000166) -#define STATUS_FLOPPY_UNKNOWN_ERROR cpu_to_le32(0xC0000167) -#define STATUS_FLOPPY_BAD_REGISTERS cpu_to_le32(0xC0000168) -#define STATUS_DISK_RECALIBRATE_FAILED cpu_to_le32(0xC0000169) -#define STATUS_DISK_OPERATION_FAILED cpu_to_le32(0xC000016A) -#define STATUS_DISK_RESET_FAILED cpu_to_le32(0xC000016B) -#define STATUS_SHARED_IRQ_BUSY cpu_to_le32(0xC000016C) -#define STATUS_FT_ORPHANING cpu_to_le32(0xC000016D) -#define STATUS_BIOS_FAILED_TO_CONNECT_INTERRUPT cpu_to_le32(0xC000016E) -#define STATUS_PARTITION_FAILURE cpu_to_le32(0xC0000172) -#define STATUS_INVALID_BLOCK_LENGTH cpu_to_le32(0xC0000173) -#define STATUS_DEVICE_NOT_PARTITIONED cpu_to_le32(0xC0000174) -#define STATUS_UNABLE_TO_LOCK_MEDIA cpu_to_le32(0xC0000175) -#define STATUS_UNABLE_TO_UNLOAD_MEDIA cpu_to_le32(0xC0000176) -#define STATUS_EOM_OVERFLOW cpu_to_le32(0xC0000177) -#define STATUS_NO_MEDIA cpu_to_le32(0xC0000178) -#define STATUS_NO_SUCH_MEMBER cpu_to_le32(0xC000017A) -#define STATUS_INVALID_MEMBER cpu_to_le32(0xC000017B) -#define STATUS_KEY_DELETED cpu_to_le32(0xC000017C) -#define STATUS_NO_LOG_SPACE cpu_to_le32(0xC000017D) -#define STATUS_TOO_MANY_SIDS cpu_to_le32(0xC000017E) -#define STATUS_LM_CROSS_ENCRYPTION_REQUIRED cpu_to_le32(0xC000017F) -#define STATUS_KEY_HAS_CHILDREN cpu_to_le32(0xC0000180) -#define STATUS_CHILD_MUST_BE_VOLATILE cpu_to_le32(0xC0000181) -#define STATUS_DEVICE_CONFIGURATION_ERROR cpu_to_le32(0xC0000182) -#define STATUS_DRIVER_INTERNAL_ERROR cpu_to_le32(0xC0000183) -#define STATUS_INVALID_DEVICE_STATE cpu_to_le32(0xC0000184) -#define STATUS_IO_DEVICE_ERROR cpu_to_le32(0xC0000185) -#define STATUS_DEVICE_PROTOCOL_ERROR cpu_to_le32(0xC0000186) -#define STATUS_BACKUP_CONTROLLER cpu_to_le32(0xC0000187) -#define STATUS_LOG_FILE_FULL cpu_to_le32(0xC0000188) -#define STATUS_TOO_LATE cpu_to_le32(0xC0000189) -#define STATUS_NO_TRUST_LSA_SECRET cpu_to_le32(0xC000018A) -#define STATUS_NO_TRUST_SAM_ACCOUNT cpu_to_le32(0xC000018B) -#define STATUS_TRUSTED_DOMAIN_FAILURE cpu_to_le32(0xC000018C) -#define STATUS_TRUSTED_RELATIONSHIP_FAILURE cpu_to_le32(0xC000018D) -#define STATUS_EVENTLOG_FILE_CORRUPT cpu_to_le32(0xC000018E) -#define STATUS_EVENTLOG_CANT_START cpu_to_le32(0xC000018F) -#define STATUS_TRUST_FAILURE cpu_to_le32(0xC0000190) -#define STATUS_MUTANT_LIMIT_EXCEEDED cpu_to_le32(0xC0000191) -#define STATUS_NETLOGON_NOT_STARTED cpu_to_le32(0xC0000192) -#define STATUS_ACCOUNT_EXPIRED cpu_to_le32(0xC0000193) -#define STATUS_POSSIBLE_DEADLOCK cpu_to_le32(0xC0000194) -#define STATUS_NETWORK_CREDENTIAL_CONFLICT cpu_to_le32(0xC0000195) -#define STATUS_REMOTE_SESSION_LIMIT cpu_to_le32(0xC0000196) -#define STATUS_EVENTLOG_FILE_CHANGED cpu_to_le32(0xC0000197) -#define STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT cpu_to_le32(0xC0000198) -#define STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT cpu_to_le32(0xC0000199) -#define STATUS_NOLOGON_SERVER_TRUST_ACCOUNT cpu_to_le32(0xC000019A) -#define STATUS_DOMAIN_TRUST_INCONSISTENT cpu_to_le32(0xC000019B) -#define STATUS_FS_DRIVER_REQUIRED cpu_to_le32(0xC000019C) -#define STATUS_IMAGE_ALREADY_LOADED_AS_DLL cpu_to_le32(0xC000019D) -#define STATUS_INVALID_LOCK_RANGE cpu_to_le32(0xC00001A1) -#define STATUS_NETWORK_OPEN_RESTRICTION cpu_to_le32(0xC0000201) -#define STATUS_NO_USER_SESSION_KEY cpu_to_le32(0xC0000202) -#define STATUS_USER_SESSION_DELETED cpu_to_le32(0xC0000203) -#define STATUS_RESOURCE_LANG_NOT_FOUND cpu_to_le32(0xC0000204) -#define STATUS_INSUFF_SERVER_RESOURCES cpu_to_le32(0xC0000205) -#define STATUS_INVALID_BUFFER_SIZE cpu_to_le32(0xC0000206) -#define STATUS_INVALID_ADDRESS_COMPONENT cpu_to_le32(0xC0000207) -#define STATUS_INVALID_ADDRESS_WILDCARD cpu_to_le32(0xC0000208) -#define STATUS_TOO_MANY_ADDRESSES cpu_to_le32(0xC0000209) -#define STATUS_ADDRESS_ALREADY_EXISTS cpu_to_le32(0xC000020A) -#define STATUS_ADDRESS_CLOSED cpu_to_le32(0xC000020B) -#define STATUS_CONNECTION_DISCONNECTED cpu_to_le32(0xC000020C) -#define STATUS_CONNECTION_RESET cpu_to_le32(0xC000020D) -#define STATUS_TOO_MANY_NODES cpu_to_le32(0xC000020E) -#define STATUS_TRANSACTION_ABORTED cpu_to_le32(0xC000020F) -#define STATUS_TRANSACTION_TIMED_OUT cpu_to_le32(0xC0000210) -#define STATUS_TRANSACTION_NO_RELEASE cpu_to_le32(0xC0000211) -#define STATUS_TRANSACTION_NO_MATCH cpu_to_le32(0xC0000212) -#define STATUS_TRANSACTION_RESPONDED cpu_to_le32(0xC0000213) -#define STATUS_TRANSACTION_INVALID_ID cpu_to_le32(0xC0000214) -#define STATUS_TRANSACTION_INVALID_TYPE cpu_to_le32(0xC0000215) -#define STATUS_NOT_SERVER_SESSION cpu_to_le32(0xC0000216) -#define STATUS_NOT_CLIENT_SESSION cpu_to_le32(0xC0000217) -#define STATUS_CANNOT_LOAD_REGISTRY_FILE cpu_to_le32(0xC0000218) -#define STATUS_DEBUG_ATTACH_FAILED cpu_to_le32(0xC0000219) -#define STATUS_SYSTEM_PROCESS_TERMINATED cpu_to_le32(0xC000021A) -#define STATUS_DATA_NOT_ACCEPTED cpu_to_le32(0xC000021B) -#define STATUS_NO_BROWSER_SERVERS_FOUND cpu_to_le32(0xC000021C) -#define STATUS_VDM_HARD_ERROR cpu_to_le32(0xC000021D) -#define STATUS_DRIVER_CANCEL_TIMEOUT cpu_to_le32(0xC000021E) -#define STATUS_REPLY_MESSAGE_MISMATCH cpu_to_le32(0xC000021F) -#define STATUS_MAPPED_ALIGNMENT cpu_to_le32(0xC0000220) -#define STATUS_IMAGE_CHECKSUM_MISMATCH cpu_to_le32(0xC0000221) -#define STATUS_LOST_WRITEBEHIND_DATA cpu_to_le32(0xC0000222) -#define STATUS_CLIENT_SERVER_PARAMETERS_INVALID cpu_to_le32(0xC0000223) -#define STATUS_PASSWORD_MUST_CHANGE cpu_to_le32(0xC0000224) -#define STATUS_NOT_FOUND cpu_to_le32(0xC0000225) -#define STATUS_NOT_TINY_STREAM cpu_to_le32(0xC0000226) -#define STATUS_RECOVERY_FAILURE cpu_to_le32(0xC0000227) -#define STATUS_STACK_OVERFLOW_READ cpu_to_le32(0xC0000228) -#define STATUS_FAIL_CHECK cpu_to_le32(0xC0000229) -#define STATUS_DUPLICATE_OBJECTID cpu_to_le32(0xC000022A) -#define STATUS_OBJECTID_EXISTS cpu_to_le32(0xC000022B) -#define STATUS_CONVERT_TO_LARGE cpu_to_le32(0xC000022C) -#define STATUS_RETRY cpu_to_le32(0xC000022D) -#define STATUS_FOUND_OUT_OF_SCOPE cpu_to_le32(0xC000022E) -#define STATUS_ALLOCATE_BUCKET cpu_to_le32(0xC000022F) -#define STATUS_PROPSET_NOT_FOUND cpu_to_le32(0xC0000230) -#define STATUS_MARSHALL_OVERFLOW cpu_to_le32(0xC0000231) -#define STATUS_INVALID_VARIANT cpu_to_le32(0xC0000232) -#define STATUS_DOMAIN_CONTROLLER_NOT_FOUND cpu_to_le32(0xC0000233) -#define STATUS_ACCOUNT_LOCKED_OUT cpu_to_le32(0xC0000234) -#define STATUS_HANDLE_NOT_CLOSABLE cpu_to_le32(0xC0000235) -#define STATUS_CONNECTION_REFUSED cpu_to_le32(0xC0000236) -#define STATUS_GRACEFUL_DISCONNECT cpu_to_le32(0xC0000237) -#define STATUS_ADDRESS_ALREADY_ASSOCIATED cpu_to_le32(0xC0000238) -#define STATUS_ADDRESS_NOT_ASSOCIATED cpu_to_le32(0xC0000239) -#define STATUS_CONNECTION_INVALID cpu_to_le32(0xC000023A) -#define STATUS_CONNECTION_ACTIVE cpu_to_le32(0xC000023B) -#define STATUS_NETWORK_UNREACHABLE cpu_to_le32(0xC000023C) -#define STATUS_HOST_UNREACHABLE cpu_to_le32(0xC000023D) -#define STATUS_PROTOCOL_UNREACHABLE cpu_to_le32(0xC000023E) -#define STATUS_PORT_UNREACHABLE cpu_to_le32(0xC000023F) -#define STATUS_REQUEST_ABORTED cpu_to_le32(0xC0000240) -#define STATUS_CONNECTION_ABORTED cpu_to_le32(0xC0000241) -#define STATUS_BAD_COMPRESSION_BUFFER cpu_to_le32(0xC0000242) -#define STATUS_USER_MAPPED_FILE cpu_to_le32(0xC0000243) -#define STATUS_AUDIT_FAILED cpu_to_le32(0xC0000244) -#define STATUS_TIMER_RESOLUTION_NOT_SET cpu_to_le32(0xC0000245) -#define STATUS_CONNECTION_COUNT_LIMIT cpu_to_le32(0xC0000246) -#define STATUS_LOGIN_TIME_RESTRICTION cpu_to_le32(0xC0000247) -#define STATUS_LOGIN_WKSTA_RESTRICTION cpu_to_le32(0xC0000248) -#define STATUS_IMAGE_MP_UP_MISMATCH cpu_to_le32(0xC0000249) -#define STATUS_INSUFFICIENT_LOGON_INFO cpu_to_le32(0xC0000250) -#define STATUS_BAD_DLL_ENTRYPOINT cpu_to_le32(0xC0000251) -#define STATUS_BAD_SERVICE_ENTRYPOINT cpu_to_le32(0xC0000252) -#define STATUS_LPC_REPLY_LOST cpu_to_le32(0xC0000253) -#define STATUS_IP_ADDRESS_CONFLICT1 cpu_to_le32(0xC0000254) -#define STATUS_IP_ADDRESS_CONFLICT2 cpu_to_le32(0xC0000255) -#define STATUS_REGISTRY_QUOTA_LIMIT cpu_to_le32(0xC0000256) -#define STATUS_PATH_NOT_COVERED cpu_to_le32(0xC0000257) -#define STATUS_NO_CALLBACK_ACTIVE cpu_to_le32(0xC0000258) -#define STATUS_LICENSE_QUOTA_EXCEEDED cpu_to_le32(0xC0000259) -#define STATUS_PWD_TOO_SHORT cpu_to_le32(0xC000025A) -#define STATUS_PWD_TOO_RECENT cpu_to_le32(0xC000025B) -#define STATUS_PWD_HISTORY_CONFLICT cpu_to_le32(0xC000025C) -#define STATUS_PLUGPLAY_NO_DEVICE cpu_to_le32(0xC000025E) -#define STATUS_UNSUPPORTED_COMPRESSION cpu_to_le32(0xC000025F) -#define STATUS_INVALID_HW_PROFILE cpu_to_le32(0xC0000260) -#define STATUS_INVALID_PLUGPLAY_DEVICE_PATH cpu_to_le32(0xC0000261) -#define STATUS_DRIVER_ORDINAL_NOT_FOUND cpu_to_le32(0xC0000262) -#define STATUS_DRIVER_ENTRYPOINT_NOT_FOUND cpu_to_le32(0xC0000263) -#define STATUS_RESOURCE_NOT_OWNED cpu_to_le32(0xC0000264) -#define STATUS_TOO_MANY_LINKS cpu_to_le32(0xC0000265) -#define STATUS_QUOTA_LIST_INCONSISTENT cpu_to_le32(0xC0000266) -#define STATUS_FILE_IS_OFFLINE cpu_to_le32(0xC0000267) -#define STATUS_EVALUATION_EXPIRATION cpu_to_le32(0xC0000268) -#define STATUS_ILLEGAL_DLL_RELOCATION cpu_to_le32(0xC0000269) -#define STATUS_LICENSE_VIOLATION cpu_to_le32(0xC000026A) -#define STATUS_DLL_INIT_FAILED_LOGOFF cpu_to_le32(0xC000026B) -#define STATUS_DRIVER_UNABLE_TO_LOAD cpu_to_le32(0xC000026C) -#define STATUS_DFS_UNAVAILABLE cpu_to_le32(0xC000026D) -#define STATUS_VOLUME_DISMOUNTED cpu_to_le32(0xC000026E) -#define STATUS_WX86_INTERNAL_ERROR cpu_to_le32(0xC000026F) -#define STATUS_WX86_FLOAT_STACK_CHECK cpu_to_le32(0xC0000270) -#define STATUS_VALIDATE_CONTINUE cpu_to_le32(0xC0000271) -#define STATUS_NO_MATCH cpu_to_le32(0xC0000272) -#define STATUS_NO_MORE_MATCHES cpu_to_le32(0xC0000273) -#define STATUS_NOT_A_REPARSE_POINT cpu_to_le32(0xC0000275) -#define STATUS_IO_REPARSE_TAG_INVALID cpu_to_le32(0xC0000276) -#define STATUS_IO_REPARSE_TAG_MISMATCH cpu_to_le32(0xC0000277) -#define STATUS_IO_REPARSE_DATA_INVALID cpu_to_le32(0xC0000278) -#define STATUS_IO_REPARSE_TAG_NOT_HANDLED cpu_to_le32(0xC0000279) -#define STATUS_REPARSE_POINT_NOT_RESOLVED cpu_to_le32(0xC0000280) -#define STATUS_DIRECTORY_IS_A_REPARSE_POINT cpu_to_le32(0xC0000281) -#define STATUS_RANGE_LIST_CONFLICT cpu_to_le32(0xC0000282) -#define STATUS_SOURCE_ELEMENT_EMPTY cpu_to_le32(0xC0000283) -#define STATUS_DESTINATION_ELEMENT_FULL cpu_to_le32(0xC0000284) -#define STATUS_ILLEGAL_ELEMENT_ADDRESS cpu_to_le32(0xC0000285) -#define STATUS_MAGAZINE_NOT_PRESENT cpu_to_le32(0xC0000286) -#define STATUS_REINITIALIZATION_NEEDED cpu_to_le32(0xC0000287) -#define STATUS_ENCRYPTION_FAILED cpu_to_le32(0xC000028A) -#define STATUS_DECRYPTION_FAILED cpu_to_le32(0xC000028B) -#define STATUS_RANGE_NOT_FOUND cpu_to_le32(0xC000028C) -#define STATUS_NO_RECOVERY_POLICY cpu_to_le32(0xC000028D) -#define STATUS_NO_EFS cpu_to_le32(0xC000028E) -#define STATUS_WRONG_EFS cpu_to_le32(0xC000028F) -#define STATUS_NO_USER_KEYS cpu_to_le32(0xC0000290) -#define STATUS_FILE_NOT_ENCRYPTED cpu_to_le32(0xC0000291) -#define STATUS_NOT_EXPORT_FORMAT cpu_to_le32(0xC0000292) -#define STATUS_FILE_ENCRYPTED cpu_to_le32(0xC0000293) -#define STATUS_WMI_GUID_NOT_FOUND cpu_to_le32(0xC0000295) -#define STATUS_WMI_INSTANCE_NOT_FOUND cpu_to_le32(0xC0000296) -#define STATUS_WMI_ITEMID_NOT_FOUND cpu_to_le32(0xC0000297) -#define STATUS_WMI_TRY_AGAIN cpu_to_le32(0xC0000298) -#define STATUS_SHARED_POLICY cpu_to_le32(0xC0000299) -#define STATUS_POLICY_OBJECT_NOT_FOUND cpu_to_le32(0xC000029A) -#define STATUS_POLICY_ONLY_IN_DS cpu_to_le32(0xC000029B) -#define STATUS_VOLUME_NOT_UPGRADED cpu_to_le32(0xC000029C) -#define STATUS_REMOTE_STORAGE_NOT_ACTIVE cpu_to_le32(0xC000029D) -#define STATUS_REMOTE_STORAGE_MEDIA_ERROR cpu_to_le32(0xC000029E) -#define STATUS_NO_TRACKING_SERVICE cpu_to_le32(0xC000029F) -#define STATUS_SERVER_SID_MISMATCH cpu_to_le32(0xC00002A0) -#define STATUS_DS_NO_ATTRIBUTE_OR_VALUE cpu_to_le32(0xC00002A1) -#define STATUS_DS_INVALID_ATTRIBUTE_SYNTAX cpu_to_le32(0xC00002A2) -#define STATUS_DS_ATTRIBUTE_TYPE_UNDEFINED cpu_to_le32(0xC00002A3) -#define STATUS_DS_ATTRIBUTE_OR_VALUE_EXISTS cpu_to_le32(0xC00002A4) -#define STATUS_DS_BUSY cpu_to_le32(0xC00002A5) -#define STATUS_DS_UNAVAILABLE cpu_to_le32(0xC00002A6) -#define STATUS_DS_NO_RIDS_ALLOCATED cpu_to_le32(0xC00002A7) -#define STATUS_DS_NO_MORE_RIDS cpu_to_le32(0xC00002A8) -#define STATUS_DS_INCORRECT_ROLE_OWNER cpu_to_le32(0xC00002A9) -#define STATUS_DS_RIDMGR_INIT_ERROR cpu_to_le32(0xC00002AA) -#define STATUS_DS_OBJ_CLASS_VIOLATION cpu_to_le32(0xC00002AB) -#define STATUS_DS_CANT_ON_NON_LEAF cpu_to_le32(0xC00002AC) -#define STATUS_DS_CANT_ON_RDN cpu_to_le32(0xC00002AD) -#define STATUS_DS_CANT_MOD_OBJ_CLASS cpu_to_le32(0xC00002AE) -#define STATUS_DS_CROSS_DOM_MOVE_FAILED cpu_to_le32(0xC00002AF) -#define STATUS_DS_GC_NOT_AVAILABLE cpu_to_le32(0xC00002B0) -#define STATUS_DIRECTORY_SERVICE_REQUIRED cpu_to_le32(0xC00002B1) -#define STATUS_REPARSE_ATTRIBUTE_CONFLICT cpu_to_le32(0xC00002B2) -#define STATUS_CANT_ENABLE_DENY_ONLY cpu_to_le32(0xC00002B3) -#define STATUS_FLOAT_MULTIPLE_FAULTS cpu_to_le32(0xC00002B4) -#define STATUS_FLOAT_MULTIPLE_TRAPS cpu_to_le32(0xC00002B5) -#define STATUS_DEVICE_REMOVED cpu_to_le32(0xC00002B6) -#define STATUS_JOURNAL_DELETE_IN_PROGRESS cpu_to_le32(0xC00002B7) -#define STATUS_JOURNAL_NOT_ACTIVE cpu_to_le32(0xC00002B8) -#define STATUS_NOINTERFACE cpu_to_le32(0xC00002B9) -#define STATUS_DS_ADMIN_LIMIT_EXCEEDED cpu_to_le32(0xC00002C1) -#define STATUS_DRIVER_FAILED_SLEEP cpu_to_le32(0xC00002C2) -#define STATUS_MUTUAL_AUTHENTICATION_FAILED cpu_to_le32(0xC00002C3) -#define STATUS_CORRUPT_SYSTEM_FILE cpu_to_le32(0xC00002C4) -#define STATUS_DATATYPE_MISALIGNMENT_ERROR cpu_to_le32(0xC00002C5) -#define STATUS_WMI_READ_ONLY cpu_to_le32(0xC00002C6) -#define STATUS_WMI_SET_FAILURE cpu_to_le32(0xC00002C7) -#define STATUS_COMMITMENT_MINIMUM cpu_to_le32(0xC00002C8) -#define STATUS_REG_NAT_CONSUMPTION cpu_to_le32(0xC00002C9) -#define STATUS_TRANSPORT_FULL cpu_to_le32(0xC00002CA) -#define STATUS_DS_SAM_INIT_FAILURE cpu_to_le32(0xC00002CB) -#define STATUS_ONLY_IF_CONNECTED cpu_to_le32(0xC00002CC) -#define STATUS_DS_SENSITIVE_GROUP_VIOLATION cpu_to_le32(0xC00002CD) -#define STATUS_PNP_RESTART_ENUMERATION cpu_to_le32(0xC00002CE) -#define STATUS_JOURNAL_ENTRY_DELETED cpu_to_le32(0xC00002CF) -#define STATUS_DS_CANT_MOD_PRIMARYGROUPID cpu_to_le32(0xC00002D0) -#define STATUS_SYSTEM_IMAGE_BAD_SIGNATURE cpu_to_le32(0xC00002D1) -#define STATUS_PNP_REBOOT_REQUIRED cpu_to_le32(0xC00002D2) -#define STATUS_POWER_STATE_INVALID cpu_to_le32(0xC00002D3) -#define STATUS_DS_INVALID_GROUP_TYPE cpu_to_le32(0xC00002D4) -#define STATUS_DS_NO_NEST_GLOBALGROUP_IN_MIXEDDOMAIN cpu_to_le32(0xC00002D5) -#define STATUS_DS_NO_NEST_LOCALGROUP_IN_MIXEDDOMAIN cpu_to_le32(0xC00002D6) -#define STATUS_DS_GLOBAL_CANT_HAVE_LOCAL_MEMBER cpu_to_le32(0xC00002D7) -#define STATUS_DS_GLOBAL_CANT_HAVE_UNIVERSAL_MEMBER cpu_to_le32(0xC00002D8) -#define STATUS_DS_UNIVERSAL_CANT_HAVE_LOCAL_MEMBER cpu_to_le32(0xC00002D9) -#define STATUS_DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER cpu_to_le32(0xC00002DA) -#define STATUS_DS_LOCAL_CANT_HAVE_CROSSDOMAIN_LOCAL_MEMBER cpu_to_le32(0xC00002DB) -#define STATUS_DS_HAVE_PRIMARY_MEMBERS cpu_to_le32(0xC00002DC) -#define STATUS_WMI_NOT_SUPPORTED cpu_to_le32(0xC00002DD) -#define STATUS_INSUFFICIENT_POWER cpu_to_le32(0xC00002DE) -#define STATUS_SAM_NEED_BOOTKEY_PASSWORD cpu_to_le32(0xC00002DF) -#define STATUS_SAM_NEED_BOOTKEY_FLOPPY cpu_to_le32(0xC00002E0) -#define STATUS_DS_CANT_START cpu_to_le32(0xC00002E1) -#define STATUS_DS_INIT_FAILURE cpu_to_le32(0xC00002E2) -#define STATUS_SAM_INIT_FAILURE cpu_to_le32(0xC00002E3) -#define STATUS_DS_GC_REQUIRED cpu_to_le32(0xC00002E4) -#define STATUS_DS_LOCAL_MEMBER_OF_LOCAL_ONLY cpu_to_le32(0xC00002E5) -#define STATUS_DS_NO_FPO_IN_UNIVERSAL_GROUPS cpu_to_le32(0xC00002E6) -#define STATUS_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED cpu_to_le32(0xC00002E7) -#define STATUS_MULTIPLE_FAULT_VIOLATION cpu_to_le32(0xC00002E8) -#define STATUS_CURRENT_DOMAIN_NOT_ALLOWED cpu_to_le32(0xC00002E9) -#define STATUS_CANNOT_MAKE cpu_to_le32(0xC00002EA) -#define STATUS_SYSTEM_SHUTDOWN cpu_to_le32(0xC00002EB) -#define STATUS_DS_INIT_FAILURE_CONSOLE cpu_to_le32(0xC00002EC) -#define STATUS_DS_SAM_INIT_FAILURE_CONSOLE cpu_to_le32(0xC00002ED) -#define STATUS_UNFINISHED_CONTEXT_DELETED cpu_to_le32(0xC00002EE) -#define STATUS_NO_TGT_REPLY cpu_to_le32(0xC00002EF) -#define STATUS_OBJECTID_NOT_FOUND cpu_to_le32(0xC00002F0) -#define STATUS_NO_IP_ADDRESSES cpu_to_le32(0xC00002F1) -#define STATUS_WRONG_CREDENTIAL_HANDLE cpu_to_le32(0xC00002F2) -#define STATUS_CRYPTO_SYSTEM_INVALID cpu_to_le32(0xC00002F3) -#define STATUS_MAX_REFERRALS_EXCEEDED cpu_to_le32(0xC00002F4) -#define STATUS_MUST_BE_KDC cpu_to_le32(0xC00002F5) -#define STATUS_STRONG_CRYPTO_NOT_SUPPORTED cpu_to_le32(0xC00002F6) -#define STATUS_TOO_MANY_PRINCIPALS cpu_to_le32(0xC00002F7) -#define STATUS_NO_PA_DATA cpu_to_le32(0xC00002F8) -#define STATUS_PKINIT_NAME_MISMATCH cpu_to_le32(0xC00002F9) -#define STATUS_SMARTCARD_LOGON_REQUIRED cpu_to_le32(0xC00002FA) -#define STATUS_KDC_INVALID_REQUEST cpu_to_le32(0xC00002FB) -#define STATUS_KDC_UNABLE_TO_REFER cpu_to_le32(0xC00002FC) -#define STATUS_KDC_UNKNOWN_ETYPE cpu_to_le32(0xC00002FD) -#define STATUS_SHUTDOWN_IN_PROGRESS cpu_to_le32(0xC00002FE) -#define STATUS_SERVER_SHUTDOWN_IN_PROGRESS cpu_to_le32(0xC00002FF) -#define STATUS_NOT_SUPPORTED_ON_SBS cpu_to_le32(0xC0000300) -#define STATUS_WMI_GUID_DISCONNECTED cpu_to_le32(0xC0000301) -#define STATUS_WMI_ALREADY_DISABLED cpu_to_le32(0xC0000302) -#define STATUS_WMI_ALREADY_ENABLED cpu_to_le32(0xC0000303) -#define STATUS_MFT_TOO_FRAGMENTED cpu_to_le32(0xC0000304) -#define STATUS_COPY_PROTECTION_FAILURE cpu_to_le32(0xC0000305) -#define STATUS_CSS_AUTHENTICATION_FAILURE cpu_to_le32(0xC0000306) -#define STATUS_CSS_KEY_NOT_PRESENT cpu_to_le32(0xC0000307) -#define STATUS_CSS_KEY_NOT_ESTABLISHED cpu_to_le32(0xC0000308) -#define STATUS_CSS_SCRAMBLED_SECTOR cpu_to_le32(0xC0000309) -#define STATUS_CSS_REGION_MISMATCH cpu_to_le32(0xC000030A) -#define STATUS_CSS_RESETS_EXHAUSTED cpu_to_le32(0xC000030B) -#define STATUS_PKINIT_FAILURE cpu_to_le32(0xC0000320) -#define STATUS_SMARTCARD_SUBSYSTEM_FAILURE cpu_to_le32(0xC0000321) -#define STATUS_NO_KERB_KEY cpu_to_le32(0xC0000322) -#define STATUS_HOST_DOWN cpu_to_le32(0xC0000350) -#define STATUS_UNSUPPORTED_PREAUTH cpu_to_le32(0xC0000351) -#define STATUS_EFS_ALG_BLOB_TOO_BIG cpu_to_le32(0xC0000352) -#define STATUS_PORT_NOT_SET cpu_to_le32(0xC0000353) -#define STATUS_DEBUGGER_INACTIVE cpu_to_le32(0xC0000354) -#define STATUS_DS_VERSION_CHECK_FAILURE cpu_to_le32(0xC0000355) -#define STATUS_AUDITING_DISABLED cpu_to_le32(0xC0000356) -#define STATUS_PRENT4_MACHINE_ACCOUNT cpu_to_le32(0xC0000357) -#define STATUS_DS_AG_CANT_HAVE_UNIVERSAL_MEMBER cpu_to_le32(0xC0000358) -#define STATUS_INVALID_IMAGE_WIN_32 cpu_to_le32(0xC0000359) -#define STATUS_INVALID_IMAGE_WIN_64 cpu_to_le32(0xC000035A) -#define STATUS_BAD_BINDINGS cpu_to_le32(0xC000035B) -#define STATUS_NETWORK_SESSION_EXPIRED cpu_to_le32(0xC000035C) -#define STATUS_APPHELP_BLOCK cpu_to_le32(0xC000035D) -#define STATUS_ALL_SIDS_FILTERED cpu_to_le32(0xC000035E) -#define STATUS_NOT_SAFE_MODE_DRIVER cpu_to_le32(0xC000035F) -#define STATUS_ACCESS_DISABLED_BY_POLICY_DEFAULT cpu_to_le32(0xC0000361) -#define STATUS_ACCESS_DISABLED_BY_POLICY_PATH cpu_to_le32(0xC0000362) -#define STATUS_ACCESS_DISABLED_BY_POLICY_PUBLISHER cpu_to_le32(0xC0000363) -#define STATUS_ACCESS_DISABLED_BY_POLICY_OTHER cpu_to_le32(0xC0000364) -#define STATUS_FAILED_DRIVER_ENTRY cpu_to_le32(0xC0000365) -#define STATUS_DEVICE_ENUMERATION_ERROR cpu_to_le32(0xC0000366) -#define STATUS_MOUNT_POINT_NOT_RESOLVED cpu_to_le32(0xC0000368) -#define STATUS_INVALID_DEVICE_OBJECT_PARAMETER cpu_to_le32(0xC0000369) +/* + * The comment at the end of each definition indicates `posix_error` + * field of `struct status_to_posix_error`, it is used to generate the + * `smb2_error_map_table` array. + */ + +#define STATUS_SUCCESS cpu_to_le32(0x00000000) // 0 +#define STATUS_WAIT_0 cpu_to_le32(0x00000000) // 0 +#define STATUS_WAIT_1 cpu_to_le32(0x00000001) // -EIO +#define STATUS_WAIT_2 cpu_to_le32(0x00000002) // -EIO +#define STATUS_WAIT_3 cpu_to_le32(0x00000003) // -EIO +#define STATUS_WAIT_63 cpu_to_le32(0x0000003F) // -EIO +#define STATUS_ABANDONED cpu_to_le32(0x00000080) // -EIO +#define STATUS_ABANDONED_WAIT_0 cpu_to_le32(0x00000080) // -EIO +#define STATUS_ABANDONED_WAIT_63 cpu_to_le32(0x000000BF) // -EIO +#define STATUS_USER_APC cpu_to_le32(0x000000C0) // -EIO +#define STATUS_KERNEL_APC cpu_to_le32(0x00000100) // -EIO +#define STATUS_ALERTED cpu_to_le32(0x00000101) // -EIO +#define STATUS_TIMEOUT cpu_to_le32(0x00000102) // -ETIMEDOUT +#define STATUS_PENDING cpu_to_le32(0x00000103) // -EIO +#define STATUS_REPARSE cpu_to_le32(0x00000104) // -EIO +#define STATUS_MORE_ENTRIES cpu_to_le32(0x00000105) // -EIO +#define STATUS_NOT_ALL_ASSIGNED cpu_to_le32(0x00000106) // -EIO +#define STATUS_SOME_NOT_MAPPED cpu_to_le32(0x00000107) // -EIO +#define STATUS_OPLOCK_BREAK_IN_PROGRESS cpu_to_le32(0x00000108) // -EIO +#define STATUS_VOLUME_MOUNTED cpu_to_le32(0x00000109) // -EIO +#define STATUS_RXACT_COMMITTED cpu_to_le32(0x0000010A) // -EIO +#define STATUS_NOTIFY_CLEANUP cpu_to_le32(0x0000010B) // -EIO +#define STATUS_NOTIFY_ENUM_DIR cpu_to_le32(0x0000010C) // -EIO +#define STATUS_NO_QUOTAS_FOR_ACCOUNT cpu_to_le32(0x0000010D) // -EIO +#define STATUS_PRIMARY_TRANSPORT_CONNECT_FAILED cpu_to_le32(0x0000010E) // -EIO +#define STATUS_PAGE_FAULT_TRANSITION cpu_to_le32(0x00000110) // -EIO +#define STATUS_PAGE_FAULT_DEMAND_ZERO cpu_to_le32(0x00000111) // -EIO +#define STATUS_PAGE_FAULT_COPY_ON_WRITE cpu_to_le32(0x00000112) // -EIO +#define STATUS_PAGE_FAULT_GUARD_PAGE cpu_to_le32(0x00000113) // -EIO +#define STATUS_PAGE_FAULT_PAGING_FILE cpu_to_le32(0x00000114) // -EIO +#define STATUS_CACHE_PAGE_LOCKED cpu_to_le32(0x00000115) // -EIO +#define STATUS_CRASH_DUMP cpu_to_le32(0x00000116) // -EIO +#define STATUS_BUFFER_ALL_ZEROS cpu_to_le32(0x00000117) // -EIO +#define STATUS_REPARSE_OBJECT cpu_to_le32(0x00000118) // -EIO +#define STATUS_RESOURCE_REQUIREMENTS_CHANGED cpu_to_le32(0x00000119) // -EIO +#define STATUS_TRANSLATION_COMPLETE cpu_to_le32(0x00000120) // -EIO +#define STATUS_DS_MEMBERSHIP_EVALUATED_LOCALLY cpu_to_le32(0x00000121) // -EIO +#define STATUS_NOTHING_TO_TERMINATE cpu_to_le32(0x00000122) // -EIO +#define STATUS_PROCESS_NOT_IN_JOB cpu_to_le32(0x00000123) // -EIO +#define STATUS_PROCESS_IN_JOB cpu_to_le32(0x00000124) // -EIO +#define STATUS_VOLSNAP_HIBERNATE_READY cpu_to_le32(0x00000125) // -EIO +#define STATUS_FSFILTER_OP_COMPLETED_SUCCESSFULLY cpu_to_le32(0x00000126) // -EIO +#define STATUS_INTERRUPT_VECTOR_ALREADY_CONNECTED cpu_to_le32(0x00000127) // -EIO +#define STATUS_INTERRUPT_STILL_CONNECTED cpu_to_le32(0x00000128) // -EIO +#define STATUS_PROCESS_CLONED cpu_to_le32(0x00000129) // -EIO +#define STATUS_FILE_LOCKED_WITH_ONLY_READERS cpu_to_le32(0x0000012A) // -EIO +#define STATUS_FILE_LOCKED_WITH_WRITERS cpu_to_le32(0x0000012B) // -EIO +#define STATUS_RESOURCEMANAGER_READ_ONLY cpu_to_le32(0x00000202) // -EROFS +#define STATUS_WAIT_FOR_OPLOCK cpu_to_le32(0x00000367) // -EIO +#define DBG_EXCEPTION_HANDLED cpu_to_le32(0x00010001) // -EIO +#define DBG_CONTINUE cpu_to_le32(0x00010002) // -EIO +#define STATUS_FLT_IO_COMPLETE cpu_to_le32(0x001C0001) // -EIO +#define STATUS_OBJECT_NAME_EXISTS cpu_to_le32(0x40000000) // -EIO +#define STATUS_THREAD_WAS_SUSPENDED cpu_to_le32(0x40000001) // -EIO +#define STATUS_WORKING_SET_LIMIT_RANGE cpu_to_le32(0x40000002) // -EIO +#define STATUS_IMAGE_NOT_AT_BASE cpu_to_le32(0x40000003) // -EIO +#define STATUS_RXACT_STATE_CREATED cpu_to_le32(0x40000004) // -EIO +#define STATUS_SEGMENT_NOTIFICATION cpu_to_le32(0x40000005) // -EIO +#define STATUS_LOCAL_USER_SESSION_KEY cpu_to_le32(0x40000006) // -EIO +#define STATUS_BAD_CURRENT_DIRECTORY cpu_to_le32(0x40000007) // -EIO +#define STATUS_SERIAL_MORE_WRITES cpu_to_le32(0x40000008) // -EIO +#define STATUS_REGISTRY_RECOVERED cpu_to_le32(0x40000009) // -EIO +#define STATUS_FT_READ_RECOVERY_FROM_BACKUP cpu_to_le32(0x4000000A) // -EIO +#define STATUS_FT_WRITE_RECOVERY cpu_to_le32(0x4000000B) // -EIO +#define STATUS_SERIAL_COUNTER_TIMEOUT cpu_to_le32(0x4000000C) // -ETIMEDOUT +#define STATUS_NULL_LM_PASSWORD cpu_to_le32(0x4000000D) // -EIO +#define STATUS_IMAGE_MACHINE_TYPE_MISMATCH cpu_to_le32(0x4000000E) // -EIO +#define STATUS_RECEIVE_PARTIAL cpu_to_le32(0x4000000F) // -EIO +#define STATUS_RECEIVE_EXPEDITED cpu_to_le32(0x40000010) // -EIO +#define STATUS_RECEIVE_PARTIAL_EXPEDITED cpu_to_le32(0x40000011) // -EIO +#define STATUS_EVENT_DONE cpu_to_le32(0x40000012) // -EIO +#define STATUS_EVENT_PENDING cpu_to_le32(0x40000013) // -EIO +#define STATUS_CHECKING_FILE_SYSTEM cpu_to_le32(0x40000014) // -EIO +#define STATUS_FATAL_APP_EXIT cpu_to_le32(0x40000015) // -EIO +#define STATUS_PREDEFINED_HANDLE cpu_to_le32(0x40000016) // -EIO +#define STATUS_WAS_UNLOCKED cpu_to_le32(0x40000017) // -EIO +#define STATUS_SERVICE_NOTIFICATION cpu_to_le32(0x40000018) // -EIO +#define STATUS_WAS_LOCKED cpu_to_le32(0x40000019) // -EIO +#define STATUS_LOG_HARD_ERROR cpu_to_le32(0x4000001A) // -EIO +#define STATUS_ALREADY_WIN32 cpu_to_le32(0x4000001B) // -EIO +#define STATUS_WX86_UNSIMULATE cpu_to_le32(0x4000001C) // -EIO +#define STATUS_WX86_CONTINUE cpu_to_le32(0x4000001D) // -EIO +#define STATUS_WX86_SINGLE_STEP cpu_to_le32(0x4000001E) // -EIO +#define STATUS_WX86_BREAKPOINT cpu_to_le32(0x4000001F) // -EIO +#define STATUS_WX86_EXCEPTION_CONTINUE cpu_to_le32(0x40000020) // -EIO +#define STATUS_WX86_EXCEPTION_LASTCHANCE cpu_to_le32(0x40000021) // -EIO +#define STATUS_WX86_EXCEPTION_CHAIN cpu_to_le32(0x40000022) // -EIO +#define STATUS_IMAGE_MACHINE_TYPE_MISMATCH_EXE cpu_to_le32(0x40000023) // -EIO +#define STATUS_NO_YIELD_PERFORMED cpu_to_le32(0x40000024) // -EIO +#define STATUS_TIMER_RESUME_IGNORED cpu_to_le32(0x40000025) // -EIO +#define STATUS_ARBITRATION_UNHANDLED cpu_to_le32(0x40000026) // -EIO +#define STATUS_CARDBUS_NOT_SUPPORTED cpu_to_le32(0x40000027) // -ENOSYS +#define STATUS_WX86_CREATEWX86TIB cpu_to_le32(0x40000028) // -EIO +#define STATUS_MP_PROCESSOR_MISMATCH cpu_to_le32(0x40000029) // -EIO +#define STATUS_HIBERNATED cpu_to_le32(0x4000002A) // -EIO +#define STATUS_RESUME_HIBERNATION cpu_to_le32(0x4000002B) // -EIO +#define STATUS_FIRMWARE_UPDATED cpu_to_le32(0x4000002C) // -EIO +#define STATUS_DRIVERS_LEAKING_LOCKED_PAGES cpu_to_le32(0x4000002D) // -EIO +#define STATUS_MESSAGE_RETRIEVED cpu_to_le32(0x4000002E) // -EIO +#define STATUS_SYSTEM_POWERSTATE_TRANSITION cpu_to_le32(0x4000002F) // -EIO +#define STATUS_ALPC_CHECK_COMPLETION_LIST cpu_to_le32(0x40000030) // -EIO +#define STATUS_SYSTEM_POWERSTATE_COMPLEX_TRANSITION cpu_to_le32(0x40000031) // -EIO +#define STATUS_ACCESS_AUDIT_BY_POLICY cpu_to_le32(0x40000032) // -EIO +#define STATUS_ABANDON_HIBERFILE cpu_to_le32(0x40000033) // -EIO +#define STATUS_BIZRULES_NOT_ENABLED cpu_to_le32(0x40000034) // -EIO +#define STATUS_WAKE_SYSTEM cpu_to_le32(0x40000294) // -EIO +#define STATUS_DS_SHUTTING_DOWN cpu_to_le32(0x40000370) // -EIO +#define DBG_REPLY_LATER cpu_to_le32(0x40010001) // -EIO +#define DBG_UNABLE_TO_PROVIDE_HANDLE cpu_to_le32(0x40010002) // -EIO +#define DBG_TERMINATE_THREAD cpu_to_le32(0x40010003) // -EIO +#define DBG_TERMINATE_PROCESS cpu_to_le32(0x40010004) // -EIO +#define DBG_CONTROL_C cpu_to_le32(0x40010005) // -EIO +#define DBG_PRINTEXCEPTION_C cpu_to_le32(0x40010006) // -EIO +#define DBG_RIPEXCEPTION cpu_to_le32(0x40010007) // -EIO +#define DBG_CONTROL_BREAK cpu_to_le32(0x40010008) // -EIO +#define DBG_COMMAND_EXCEPTION cpu_to_le32(0x40010009) // -EIO +#define RPC_NT_UUID_LOCAL_ONLY cpu_to_le32(0x40020056) // -EIO +#define RPC_NT_SEND_INCOMPLETE cpu_to_le32(0x400200AF) // -EIO +#define STATUS_CTX_CDM_CONNECT cpu_to_le32(0x400A0004) // -EIO +#define STATUS_CTX_CDM_DISCONNECT cpu_to_le32(0x400A0005) // -EIO +#define STATUS_SXS_RELEASE_ACTIVATION_CONTEXT cpu_to_le32(0x4015000D) // -EIO +#define STATUS_RECOVERY_NOT_NEEDED cpu_to_le32(0x40190034) // -EIO +#define STATUS_RM_ALREADY_STARTED cpu_to_le32(0x40190035) // -EIO +#define STATUS_LOG_NO_RESTART cpu_to_le32(0x401A000C) // -EIO +#define STATUS_VIDEO_DRIVER_DEBUG_REPORT_REQUEST cpu_to_le32(0x401B00EC) // -EIO +#define STATUS_GRAPHICS_PARTIAL_DATA_POPULATED cpu_to_le32(0x401E000A) // -EIO +#define STATUS_GRAPHICS_DRIVER_MISMATCH cpu_to_le32(0x401E0117) // -EIO +#define STATUS_GRAPHICS_MODE_NOT_PINNED cpu_to_le32(0x401E0307) // -EIO +#define STATUS_GRAPHICS_NO_PREFERRED_MODE cpu_to_le32(0x401E031E) // -EIO +#define STATUS_GRAPHICS_DATASET_IS_EMPTY cpu_to_le32(0x401E034B) // -EIO +#define STATUS_GRAPHICS_NO_MORE_ELEMENTS_IN_DATASET cpu_to_le32(0x401E034C) // -EIO +#define STATUS_GRAPHICS_PATH_CONTENT_GEOMETRY_TRANSFORMATION_NOT_PINNED cpu_to_le32(0x401E0351) // -EIO +#define STATUS_GRAPHICS_UNKNOWN_CHILD_STATUS cpu_to_le32(0x401E042F) // -EIO +#define STATUS_GRAPHICS_LEADLINK_START_DEFERRED cpu_to_le32(0x401E0437) // -EIO +#define STATUS_GRAPHICS_POLLING_TOO_FREQUENTLY cpu_to_le32(0x401E0439) // -EIO +#define STATUS_GRAPHICS_START_DEFERRED cpu_to_le32(0x401E043A) // -EIO +#define STATUS_NDIS_INDICATION_REQUIRED cpu_to_le32(0x40230001) // -EIO +#define STATUS_GUARD_PAGE_VIOLATION cpu_to_le32(0x80000001) // -EIO +#define STATUS_DATATYPE_MISALIGNMENT cpu_to_le32(0x80000002) // -EIO +#define STATUS_BREAKPOINT cpu_to_le32(0x80000003) // -EIO +#define STATUS_SINGLE_STEP cpu_to_le32(0x80000004) // -EIO +#define STATUS_BUFFER_OVERFLOW cpu_to_le32(0x80000005) // -E2BIG +#define STATUS_NO_MORE_FILES cpu_to_le32(0x80000006) // -ENODATA +#define STATUS_WAKE_SYSTEM_DEBUGGER cpu_to_le32(0x80000007) // -EIO +#define STATUS_HANDLES_CLOSED cpu_to_le32(0x8000000A) // -EIO +#define STATUS_NO_INHERITANCE cpu_to_le32(0x8000000B) // -EIO +#define STATUS_GUID_SUBSTITUTION_MADE cpu_to_le32(0x8000000C) // -EIO +#define STATUS_PARTIAL_COPY cpu_to_le32(0x8000000D) // -EIO +#define STATUS_DEVICE_PAPER_EMPTY cpu_to_le32(0x8000000E) // -EIO +#define STATUS_DEVICE_POWERED_OFF cpu_to_le32(0x8000000F) // -EIO +#define STATUS_DEVICE_OFF_LINE cpu_to_le32(0x80000010) // -EIO +#define STATUS_DEVICE_BUSY cpu_to_le32(0x80000011) // -EBUSY +#define STATUS_NO_MORE_EAS cpu_to_le32(0x80000012) // -EIO +#define STATUS_INVALID_EA_NAME cpu_to_le32(0x80000013) // -EINVAL +#define STATUS_EA_LIST_INCONSISTENT cpu_to_le32(0x80000014) // -EIO +#define STATUS_INVALID_EA_FLAG cpu_to_le32(0x80000015) // -EINVAL +#define STATUS_VERIFY_REQUIRED cpu_to_le32(0x80000016) // -EIO +#define STATUS_EXTRANEOUS_INFORMATION cpu_to_le32(0x80000017) // -EIO +#define STATUS_RXACT_COMMIT_NECESSARY cpu_to_le32(0x80000018) // -EIO +#define STATUS_NO_MORE_ENTRIES cpu_to_le32(0x8000001A) // -EIO +#define STATUS_FILEMARK_DETECTED cpu_to_le32(0x8000001B) // -EIO +#define STATUS_MEDIA_CHANGED cpu_to_le32(0x8000001C) // -EIO +#define STATUS_BUS_RESET cpu_to_le32(0x8000001D) // -EIO +#define STATUS_END_OF_MEDIA cpu_to_le32(0x8000001E) // -EIO +#define STATUS_BEGINNING_OF_MEDIA cpu_to_le32(0x8000001F) // -EIO +#define STATUS_MEDIA_CHECK cpu_to_le32(0x80000020) // -EIO +#define STATUS_SETMARK_DETECTED cpu_to_le32(0x80000021) // -EIO +#define STATUS_NO_DATA_DETECTED cpu_to_le32(0x80000022) // -EIO +#define STATUS_REDIRECTOR_HAS_OPEN_HANDLES cpu_to_le32(0x80000023) // -EIO +#define STATUS_SERVER_HAS_OPEN_HANDLES cpu_to_le32(0x80000024) // -EIO +#define STATUS_ALREADY_DISCONNECTED cpu_to_le32(0x80000025) // -EIO +#define STATUS_LONGJUMP cpu_to_le32(0x80000026) // -EIO +#define STATUS_CLEANER_CARTRIDGE_INSTALLED cpu_to_le32(0x80000027) // -EIO +#define STATUS_PLUGPLAY_QUERY_VETOED cpu_to_le32(0x80000028) // -EIO +#define STATUS_UNWIND_CONSOLIDATE cpu_to_le32(0x80000029) // -EIO +#define STATUS_REGISTRY_HIVE_RECOVERED cpu_to_le32(0x8000002A) // -EIO +#define STATUS_DLL_MIGHT_BE_INSECURE cpu_to_le32(0x8000002B) // -EIO +#define STATUS_DLL_MIGHT_BE_INCOMPATIBLE cpu_to_le32(0x8000002C) // -EIO +#define STATUS_STOPPED_ON_SYMLINK cpu_to_le32(0x8000002D) // -EOPNOTSUPP +#define STATUS_DEVICE_REQUIRES_CLEANING cpu_to_le32(0x80000288) // -EIO +#define STATUS_DEVICE_DOOR_OPEN cpu_to_le32(0x80000289) // -EIO +#define STATUS_DATA_LOST_REPAIR cpu_to_le32(0x80000803) // -EIO +#define DBG_EXCEPTION_NOT_HANDLED cpu_to_le32(0x80010001) // -EIO +#define STATUS_CLUSTER_NODE_ALREADY_UP cpu_to_le32(0x80130001) // -EIO +#define STATUS_CLUSTER_NODE_ALREADY_DOWN cpu_to_le32(0x80130002) // -EIO +#define STATUS_CLUSTER_NETWORK_ALREADY_ONLINE cpu_to_le32(0x80130003) // -EIO +#define STATUS_CLUSTER_NETWORK_ALREADY_OFFLINE cpu_to_le32(0x80130004) // -EIO +#define STATUS_CLUSTER_NODE_ALREADY_MEMBER cpu_to_le32(0x80130005) // -EIO +#define STATUS_COULD_NOT_RESIZE_LOG cpu_to_le32(0x80190009) // -EIO +#define STATUS_NO_TXF_METADATA cpu_to_le32(0x80190029) // -EIO +#define STATUS_CANT_RECOVER_WITH_HANDLE_OPEN cpu_to_le32(0x80190031) // -EIO +#define STATUS_TXF_METADATA_ALREADY_PRESENT cpu_to_le32(0x80190041) // -EIO +#define STATUS_TRANSACTION_SCOPE_CALLBACKS_NOT_SET cpu_to_le32(0x80190042) // -EIO +#define STATUS_VIDEO_HUNG_DISPLAY_DRIVER_THREAD_RECOVERED cpu_to_le32(0x801B00EB) // -EIO +#define STATUS_FLT_BUFFER_TOO_SMALL cpu_to_le32(0x801C0001) // -ENOBUFS +#define STATUS_FVE_PARTIAL_METADATA cpu_to_le32(0x80210001) // -EIO +#define STATUS_UNSUCCESSFUL cpu_to_le32(0xC0000001) // -EIO +#define STATUS_NOT_IMPLEMENTED cpu_to_le32(0xC0000002) // -EOPNOTSUPP +#define STATUS_INVALID_INFO_CLASS cpu_to_le32(0xC0000003) // -EIO +#define STATUS_INFO_LENGTH_MISMATCH cpu_to_le32(0xC0000004) // -EIO +#define STATUS_ACCESS_VIOLATION cpu_to_le32(0xC0000005) // -EACCES +#define STATUS_IN_PAGE_ERROR cpu_to_le32(0xC0000006) // -EFAULT +#define STATUS_PAGEFILE_QUOTA cpu_to_le32(0xC0000007) // -EDQUOT +#define STATUS_INVALID_HANDLE cpu_to_le32(0xC0000008) // -EBADF +#define STATUS_BAD_INITIAL_STACK cpu_to_le32(0xC0000009) // -EIO +#define STATUS_BAD_INITIAL_PC cpu_to_le32(0xC000000A) // -EIO +#define STATUS_INVALID_CID cpu_to_le32(0xC000000B) // -EIO +#define STATUS_TIMER_NOT_CANCELED cpu_to_le32(0xC000000C) // -EIO +#define STATUS_INVALID_PARAMETER cpu_to_le32(0xC000000D) // -EINVAL +#define STATUS_NO_SUCH_DEVICE cpu_to_le32(0xC000000E) // -ENODEV +#define STATUS_NO_SUCH_FILE cpu_to_le32(0xC000000F) // -ENOENT +#define STATUS_INVALID_DEVICE_REQUEST cpu_to_le32(0xC0000010) // -EOPNOTSUPP +#define STATUS_END_OF_FILE cpu_to_le32(0xC0000011) // -ENODATA +#define STATUS_WRONG_VOLUME cpu_to_le32(0xC0000012) // -EIO +#define STATUS_NO_MEDIA_IN_DEVICE cpu_to_le32(0xC0000013) // -EIO +#define STATUS_UNRECOGNIZED_MEDIA cpu_to_le32(0xC0000014) // -EIO +#define STATUS_NONEXISTENT_SECTOR cpu_to_le32(0xC0000015) // -EIO +#define STATUS_MORE_PROCESSING_REQUIRED cpu_to_le32(0xC0000016) // -EIO +#define STATUS_NO_MEMORY cpu_to_le32(0xC0000017) // -EREMOTEIO +#define STATUS_CONFLICTING_ADDRESSES cpu_to_le32(0xC0000018) // -EADDRINUSE +#define STATUS_NOT_MAPPED_VIEW cpu_to_le32(0xC0000019) // -EIO +#define STATUS_UNABLE_TO_FREE_VM cpu_to_le32(0xC000001A) // -EIO +#define STATUS_UNABLE_TO_DELETE_SECTION cpu_to_le32(0xC000001B) // -EIO +#define STATUS_INVALID_SYSTEM_SERVICE cpu_to_le32(0xC000001C) // -EIO +#define STATUS_ILLEGAL_INSTRUCTION cpu_to_le32(0xC000001D) // -EIO +#define STATUS_INVALID_LOCK_SEQUENCE cpu_to_le32(0xC000001E) // -EIO +#define STATUS_INVALID_VIEW_SIZE cpu_to_le32(0xC000001F) // -EIO +#define STATUS_INVALID_FILE_FOR_SECTION cpu_to_le32(0xC0000020) // -EIO +#define STATUS_ALREADY_COMMITTED cpu_to_le32(0xC0000021) // -EIO +#define STATUS_ACCESS_DENIED cpu_to_le32(0xC0000022) // -EACCES +#define STATUS_BUFFER_TOO_SMALL cpu_to_le32(0xC0000023) // -EIO +#define STATUS_OBJECT_TYPE_MISMATCH cpu_to_le32(0xC0000024) // -EIO +#define STATUS_NONCONTINUABLE_EXCEPTION cpu_to_le32(0xC0000025) // -EIO +#define STATUS_INVALID_DISPOSITION cpu_to_le32(0xC0000026) // -EIO +#define STATUS_UNWIND cpu_to_le32(0xC0000027) // -EIO +#define STATUS_BAD_STACK cpu_to_le32(0xC0000028) // -EIO +#define STATUS_INVALID_UNWIND_TARGET cpu_to_le32(0xC0000029) // -EIO +#define STATUS_NOT_LOCKED cpu_to_le32(0xC000002A) // -EIO +#define STATUS_PARITY_ERROR cpu_to_le32(0xC000002B) // -EIO +#define STATUS_UNABLE_TO_DECOMMIT_VM cpu_to_le32(0xC000002C) // -EIO +#define STATUS_NOT_COMMITTED cpu_to_le32(0xC000002D) // -EIO +#define STATUS_INVALID_PORT_ATTRIBUTES cpu_to_le32(0xC000002E) // -EIO +#define STATUS_PORT_MESSAGE_TOO_LONG cpu_to_le32(0xC000002F) // -EIO +#define STATUS_INVALID_PARAMETER_MIX cpu_to_le32(0xC0000030) // -EINVAL +#define STATUS_INVALID_QUOTA_LOWER cpu_to_le32(0xC0000031) // -EIO +#define STATUS_DISK_CORRUPT_ERROR cpu_to_le32(0xC0000032) // -EIO +#define STATUS_OBJECT_NAME_INVALID cpu_to_le32(0xC0000033) // -ENOENT +#define STATUS_OBJECT_NAME_NOT_FOUND cpu_to_le32(0xC0000034) // -ENOENT +#define STATUS_OBJECT_NAME_COLLISION cpu_to_le32(0xC0000035) // -EEXIST +#define STATUS_PORT_DISCONNECTED cpu_to_le32(0xC0000037) // -EIO +#define STATUS_DEVICE_ALREADY_ATTACHED cpu_to_le32(0xC0000038) // -EIO +#define STATUS_OBJECT_PATH_INVALID cpu_to_le32(0xC0000039) // -ENOTDIR +#define STATUS_OBJECT_PATH_NOT_FOUND cpu_to_le32(0xC000003A) // -ENOENT +#define STATUS_OBJECT_PATH_SYNTAX_BAD cpu_to_le32(0xC000003B) // -EIO +#define STATUS_DATA_OVERRUN cpu_to_le32(0xC000003C) // -EIO +#define STATUS_DATA_LATE_ERROR cpu_to_le32(0xC000003D) // -EIO +#define STATUS_DATA_ERROR cpu_to_le32(0xC000003E) // -EIO +#define STATUS_CRC_ERROR cpu_to_le32(0xC000003F) // -EIO +#define STATUS_SECTION_TOO_BIG cpu_to_le32(0xC0000040) // -EIO +#define STATUS_PORT_CONNECTION_REFUSED cpu_to_le32(0xC0000041) // -ECONNREFUSED +#define STATUS_INVALID_PORT_HANDLE cpu_to_le32(0xC0000042) // -EIO +#define STATUS_SHARING_VIOLATION cpu_to_le32(0xC0000043) // -EBUSY +#define STATUS_QUOTA_EXCEEDED cpu_to_le32(0xC0000044) // -EDQUOT +#define STATUS_INVALID_PAGE_PROTECTION cpu_to_le32(0xC0000045) // -EIO +#define STATUS_MUTANT_NOT_OWNED cpu_to_le32(0xC0000046) // -EIO +#define STATUS_SEMAPHORE_LIMIT_EXCEEDED cpu_to_le32(0xC0000047) // -EIO +#define STATUS_PORT_ALREADY_SET cpu_to_le32(0xC0000048) // -EIO +#define STATUS_SECTION_NOT_IMAGE cpu_to_le32(0xC0000049) // -EIO +#define STATUS_SUSPEND_COUNT_EXCEEDED cpu_to_le32(0xC000004A) // -EIO +#define STATUS_THREAD_IS_TERMINATING cpu_to_le32(0xC000004B) // -EIO +#define STATUS_BAD_WORKING_SET_LIMIT cpu_to_le32(0xC000004C) // -EIO +#define STATUS_INCOMPATIBLE_FILE_MAP cpu_to_le32(0xC000004D) // -EIO +#define STATUS_SECTION_PROTECTION cpu_to_le32(0xC000004E) // -EIO +#define STATUS_EAS_NOT_SUPPORTED cpu_to_le32(0xC000004F) // -EOPNOTSUPP +#define STATUS_EA_TOO_LARGE cpu_to_le32(0xC0000050) // -EIO +#define STATUS_NONEXISTENT_EA_ENTRY cpu_to_le32(0xC0000051) // -EIO +#define STATUS_NO_EAS_ON_FILE cpu_to_le32(0xC0000052) // -ENODATA +#define STATUS_EA_CORRUPT_ERROR cpu_to_le32(0xC0000053) // -EIO +#define STATUS_FILE_LOCK_CONFLICT cpu_to_le32(0xC0000054) // -EACCES +#define STATUS_LOCK_NOT_GRANTED cpu_to_le32(0xC0000055) // -EACCES +#define STATUS_DELETE_PENDING cpu_to_le32(0xC0000056) // -ENOENT +#define STATUS_CTL_FILE_NOT_SUPPORTED cpu_to_le32(0xC0000057) // -ENOSYS +#define STATUS_UNKNOWN_REVISION cpu_to_le32(0xC0000058) // -EIO +#define STATUS_REVISION_MISMATCH cpu_to_le32(0xC0000059) // -EIO +#define STATUS_INVALID_OWNER cpu_to_le32(0xC000005A) // -EIO +#define STATUS_INVALID_PRIMARY_GROUP cpu_to_le32(0xC000005B) // -EIO +#define STATUS_NO_IMPERSONATION_TOKEN cpu_to_le32(0xC000005C) // -EIO +#define STATUS_CANT_DISABLE_MANDATORY cpu_to_le32(0xC000005D) // -EIO +#define STATUS_NO_LOGON_SERVERS cpu_to_le32(0xC000005E) // -EIO +#define STATUS_NO_SUCH_LOGON_SESSION cpu_to_le32(0xC000005F) // -EIO +#define STATUS_NO_SUCH_PRIVILEGE cpu_to_le32(0xC0000060) // -EIO +#define STATUS_PRIVILEGE_NOT_HELD cpu_to_le32(0xC0000061) // -EPERM +#define STATUS_INVALID_ACCOUNT_NAME cpu_to_le32(0xC0000062) // -EIO +#define STATUS_USER_EXISTS cpu_to_le32(0xC0000063) // -EIO +#define STATUS_NO_SUCH_USER cpu_to_le32(0xC0000064) // -EIO +#define STATUS_GROUP_EXISTS cpu_to_le32(0xC0000065) // -EIO +#define STATUS_NO_SUCH_GROUP cpu_to_le32(0xC0000066) // -EIO +#define STATUS_MEMBER_IN_GROUP cpu_to_le32(0xC0000067) // -EIO +#define STATUS_MEMBER_NOT_IN_GROUP cpu_to_le32(0xC0000068) // -EIO +#define STATUS_LAST_ADMIN cpu_to_le32(0xC0000069) // -EIO +#define STATUS_WRONG_PASSWORD cpu_to_le32(0xC000006A) // -EACCES +#define STATUS_ILL_FORMED_PASSWORD cpu_to_le32(0xC000006B) // -EINVAL +#define STATUS_PASSWORD_RESTRICTION cpu_to_le32(0xC000006C) // -EACCES +#define STATUS_LOGON_FAILURE cpu_to_le32(0xC000006D) // -EACCES +#define STATUS_ACCOUNT_RESTRICTION cpu_to_le32(0xC000006E) // -EACCES +#define STATUS_INVALID_LOGON_HOURS cpu_to_le32(0xC000006F) // -EACCES +#define STATUS_INVALID_WORKSTATION cpu_to_le32(0xC0000070) // -EACCES +#define STATUS_PASSWORD_EXPIRED cpu_to_le32(0xC0000071) // -EKEYEXPIRED +#define STATUS_ACCOUNT_DISABLED cpu_to_le32(0xC0000072) // -EKEYREVOKED +#define STATUS_NONE_MAPPED cpu_to_le32(0xC0000073) // -EIO +#define STATUS_TOO_MANY_LUIDS_REQUESTED cpu_to_le32(0xC0000074) // -EIO +#define STATUS_LUIDS_EXHAUSTED cpu_to_le32(0xC0000075) // -EIO +#define STATUS_INVALID_SUB_AUTHORITY cpu_to_le32(0xC0000076) // -EIO +#define STATUS_INVALID_ACL cpu_to_le32(0xC0000077) // -EIO +#define STATUS_INVALID_SID cpu_to_le32(0xC0000078) // -EIO +#define STATUS_INVALID_SECURITY_DESCR cpu_to_le32(0xC0000079) // -EIO +#define STATUS_PROCEDURE_NOT_FOUND cpu_to_le32(0xC000007A) // -EIO +#define STATUS_INVALID_IMAGE_FORMAT cpu_to_le32(0xC000007B) // -EIO +#define STATUS_NO_TOKEN cpu_to_le32(0xC000007C) // -EIO +#define STATUS_BAD_INHERITANCE_ACL cpu_to_le32(0xC000007D) // -EIO +#define STATUS_RANGE_NOT_LOCKED cpu_to_le32(0xC000007E) // -EIO +#define STATUS_DISK_FULL cpu_to_le32(0xC000007F) // -ENOSPC +#define STATUS_SERVER_DISABLED cpu_to_le32(0xC0000080) // -EIO +#define STATUS_SERVER_NOT_DISABLED cpu_to_le32(0xC0000081) // -EIO +#define STATUS_TOO_MANY_GUIDS_REQUESTED cpu_to_le32(0xC0000082) // -EIO +#define STATUS_GUIDS_EXHAUSTED cpu_to_le32(0xC0000083) // -EIO +#define STATUS_INVALID_ID_AUTHORITY cpu_to_le32(0xC0000084) // -EIO +#define STATUS_AGENTS_EXHAUSTED cpu_to_le32(0xC0000085) // -EIO +#define STATUS_INVALID_VOLUME_LABEL cpu_to_le32(0xC0000086) // -EIO +#define STATUS_SECTION_NOT_EXTENDED cpu_to_le32(0xC0000087) // -EIO +#define STATUS_NOT_MAPPED_DATA cpu_to_le32(0xC0000088) // -EIO +#define STATUS_RESOURCE_DATA_NOT_FOUND cpu_to_le32(0xC0000089) // -EIO +#define STATUS_RESOURCE_TYPE_NOT_FOUND cpu_to_le32(0xC000008A) // -EIO +#define STATUS_RESOURCE_NAME_NOT_FOUND cpu_to_le32(0xC000008B) // -EIO +#define STATUS_ARRAY_BOUNDS_EXCEEDED cpu_to_le32(0xC000008C) // -EIO +#define STATUS_FLOAT_DENORMAL_OPERAND cpu_to_le32(0xC000008D) // -EIO +#define STATUS_FLOAT_DIVIDE_BY_ZERO cpu_to_le32(0xC000008E) // -EIO +#define STATUS_FLOAT_INEXACT_RESULT cpu_to_le32(0xC000008F) // -EIO +#define STATUS_FLOAT_INVALID_OPERATION cpu_to_le32(0xC0000090) // -EIO +#define STATUS_FLOAT_OVERFLOW cpu_to_le32(0xC0000091) // -EIO +#define STATUS_FLOAT_STACK_CHECK cpu_to_le32(0xC0000092) // -EIO +#define STATUS_FLOAT_UNDERFLOW cpu_to_le32(0xC0000093) // -EIO +#define STATUS_INTEGER_DIVIDE_BY_ZERO cpu_to_le32(0xC0000094) // -EIO +#define STATUS_INTEGER_OVERFLOW cpu_to_le32(0xC0000095) // -EIO +#define STATUS_PRIVILEGED_INSTRUCTION cpu_to_le32(0xC0000096) // -EIO +#define STATUS_TOO_MANY_PAGING_FILES cpu_to_le32(0xC0000097) // -EIO +#define STATUS_FILE_INVALID cpu_to_le32(0xC0000098) // -EIO +#define STATUS_ALLOTTED_SPACE_EXCEEDED cpu_to_le32(0xC0000099) // -EIO +#define STATUS_INSUFFICIENT_RESOURCES cpu_to_le32(0xC000009A) // -EAGAIN +#define STATUS_DFS_EXIT_PATH_FOUND cpu_to_le32(0xC000009B) // -EIO +#define STATUS_DEVICE_DATA_ERROR cpu_to_le32(0xC000009C) // -EIO +#define STATUS_DEVICE_NOT_CONNECTED cpu_to_le32(0xC000009D) // -EIO +#define STATUS_DEVICE_POWER_FAILURE cpu_to_le32(0xC000009E) // -EIO +#define STATUS_FREE_VM_NOT_AT_BASE cpu_to_le32(0xC000009F) // -EIO +#define STATUS_MEMORY_NOT_ALLOCATED cpu_to_le32(0xC00000A0) // -EFAULT +#define STATUS_WORKING_SET_QUOTA cpu_to_le32(0xC00000A1) // -EIO +#define STATUS_MEDIA_WRITE_PROTECTED cpu_to_le32(0xC00000A2) // -EROFS +#define STATUS_DEVICE_NOT_READY cpu_to_le32(0xC00000A3) // -EIO +#define STATUS_INVALID_GROUP_ATTRIBUTES cpu_to_le32(0xC00000A4) // -EIO +#define STATUS_BAD_IMPERSONATION_LEVEL cpu_to_le32(0xC00000A5) // -EIO +#define STATUS_CANT_OPEN_ANONYMOUS cpu_to_le32(0xC00000A6) // -EIO +#define STATUS_BAD_VALIDATION_CLASS cpu_to_le32(0xC00000A7) // -EIO +#define STATUS_BAD_TOKEN_TYPE cpu_to_le32(0xC00000A8) // -EIO +#define STATUS_BAD_MASTER_BOOT_RECORD cpu_to_le32(0xC00000A9) // -EIO +#define STATUS_INSTRUCTION_MISALIGNMENT cpu_to_le32(0xC00000AA) // -EIO +#define STATUS_INSTANCE_NOT_AVAILABLE cpu_to_le32(0xC00000AB) // -EIO +#define STATUS_PIPE_NOT_AVAILABLE cpu_to_le32(0xC00000AC) // -EIO +#define STATUS_INVALID_PIPE_STATE cpu_to_le32(0xC00000AD) // -EIO +#define STATUS_PIPE_BUSY cpu_to_le32(0xC00000AE) // -EBUSY +#define STATUS_ILLEGAL_FUNCTION cpu_to_le32(0xC00000AF) // -EIO +#define STATUS_PIPE_DISCONNECTED cpu_to_le32(0xC00000B0) // -EPIPE +#define STATUS_PIPE_CLOSING cpu_to_le32(0xC00000B1) // -EIO +#define STATUS_PIPE_CONNECTED cpu_to_le32(0xC00000B2) // -EIO +#define STATUS_PIPE_LISTENING cpu_to_le32(0xC00000B3) // -EIO +#define STATUS_INVALID_READ_MODE cpu_to_le32(0xC00000B4) // -EIO +#define STATUS_IO_TIMEOUT cpu_to_le32(0xC00000B5) // -EAGAIN +#define STATUS_FILE_FORCED_CLOSED cpu_to_le32(0xC00000B6) // -EIO +#define STATUS_PROFILING_NOT_STARTED cpu_to_le32(0xC00000B7) // -EIO +#define STATUS_PROFILING_NOT_STOPPED cpu_to_le32(0xC00000B8) // -EIO +#define STATUS_COULD_NOT_INTERPRET cpu_to_le32(0xC00000B9) // -EIO +#define STATUS_FILE_IS_A_DIRECTORY cpu_to_le32(0xC00000BA) // -EISDIR +#define STATUS_NOT_SUPPORTED cpu_to_le32(0xC00000BB) // -EOPNOTSUPP +#define STATUS_REMOTE_NOT_LISTENING cpu_to_le32(0xC00000BC) // -EHOSTDOWN +#define STATUS_DUPLICATE_NAME cpu_to_le32(0xC00000BD) // -ENOTUNIQ +#define STATUS_BAD_NETWORK_PATH cpu_to_le32(0xC00000BE) // -EINVAL +#define STATUS_NETWORK_BUSY cpu_to_le32(0xC00000BF) // -EBUSY +#define STATUS_DEVICE_DOES_NOT_EXIST cpu_to_le32(0xC00000C0) // -ENODEV +#define STATUS_TOO_MANY_COMMANDS cpu_to_le32(0xC00000C1) // -EIO +#define STATUS_ADAPTER_HARDWARE_ERROR cpu_to_le32(0xC00000C2) // -EIO +#define STATUS_INVALID_NETWORK_RESPONSE cpu_to_le32(0xC00000C3) // -EIO +#define STATUS_UNEXPECTED_NETWORK_ERROR cpu_to_le32(0xC00000C4) // -EIO +#define STATUS_BAD_REMOTE_ADAPTER cpu_to_le32(0xC00000C5) // -EIO +#define STATUS_PRINT_QUEUE_FULL cpu_to_le32(0xC00000C6) // -EIO +#define STATUS_NO_SPOOL_SPACE cpu_to_le32(0xC00000C7) // -EIO +#define STATUS_PRINT_CANCELLED cpu_to_le32(0xC00000C8) // -EIO +#define STATUS_NETWORK_NAME_DELETED cpu_to_le32(0xC00000C9) // -EREMCHG +#define STATUS_NETWORK_ACCESS_DENIED cpu_to_le32(0xC00000CA) // -EACCES +#define STATUS_BAD_DEVICE_TYPE cpu_to_le32(0xC00000CB) // -EIO +#define STATUS_BAD_NETWORK_NAME cpu_to_le32(0xC00000CC) // -ENOENT +#define STATUS_TOO_MANY_NAMES cpu_to_le32(0xC00000CD) // -EIO +#define STATUS_TOO_MANY_SESSIONS cpu_to_le32(0xC00000CE) // -EIO +#define STATUS_SHARING_PAUSED cpu_to_le32(0xC00000CF) // -EIO +#define STATUS_REQUEST_NOT_ACCEPTED cpu_to_le32(0xC00000D0) // -EIO +#define STATUS_REDIRECTOR_PAUSED cpu_to_le32(0xC00000D1) // -EIO +#define STATUS_NET_WRITE_FAULT cpu_to_le32(0xC00000D2) // -EIO +#define STATUS_PROFILING_AT_LIMIT cpu_to_le32(0xC00000D3) // -EIO +#define STATUS_NOT_SAME_DEVICE cpu_to_le32(0xC00000D4) // -EXDEV +#define STATUS_FILE_RENAMED cpu_to_le32(0xC00000D5) // -EIO +#define STATUS_VIRTUAL_CIRCUIT_CLOSED cpu_to_le32(0xC00000D6) // -EIO +#define STATUS_NO_SECURITY_ON_OBJECT cpu_to_le32(0xC00000D7) // -EIO +#define STATUS_CANT_WAIT cpu_to_le32(0xC00000D8) // -EIO +#define STATUS_PIPE_EMPTY cpu_to_le32(0xC00000D9) // -EIO +#define STATUS_CANT_ACCESS_DOMAIN_INFO cpu_to_le32(0xC00000DA) // -EIO +#define STATUS_CANT_TERMINATE_SELF cpu_to_le32(0xC00000DB) // -EIO +#define STATUS_INVALID_SERVER_STATE cpu_to_le32(0xC00000DC) // -EIO +#define STATUS_INVALID_DOMAIN_STATE cpu_to_le32(0xC00000DD) // -EIO +#define STATUS_INVALID_DOMAIN_ROLE cpu_to_le32(0xC00000DE) // -EIO +#define STATUS_NO_SUCH_DOMAIN cpu_to_le32(0xC00000DF) // -EIO +#define STATUS_DOMAIN_EXISTS cpu_to_le32(0xC00000E0) // -EIO +#define STATUS_DOMAIN_LIMIT_EXCEEDED cpu_to_le32(0xC00000E1) // -EIO +#define STATUS_OPLOCK_NOT_GRANTED cpu_to_le32(0xC00000E2) // -EIO +#define STATUS_INVALID_OPLOCK_PROTOCOL cpu_to_le32(0xC00000E3) // -EIO +#define STATUS_INTERNAL_DB_CORRUPTION cpu_to_le32(0xC00000E4) // -EIO +#define STATUS_INTERNAL_ERROR cpu_to_le32(0xC00000E5) // -EIO +#define STATUS_GENERIC_NOT_MAPPED cpu_to_le32(0xC00000E6) // -EIO +#define STATUS_BAD_DESCRIPTOR_FORMAT cpu_to_le32(0xC00000E7) // -EIO +#define STATUS_INVALID_USER_BUFFER cpu_to_le32(0xC00000E8) // -EIO +#define STATUS_UNEXPECTED_IO_ERROR cpu_to_le32(0xC00000E9) // -EIO +#define STATUS_UNEXPECTED_MM_CREATE_ERR cpu_to_le32(0xC00000EA) // -EIO +#define STATUS_UNEXPECTED_MM_MAP_ERROR cpu_to_le32(0xC00000EB) // -EIO +#define STATUS_UNEXPECTED_MM_EXTEND_ERR cpu_to_le32(0xC00000EC) // -EIO +#define STATUS_NOT_LOGON_PROCESS cpu_to_le32(0xC00000ED) // -EIO +#define STATUS_LOGON_SESSION_EXISTS cpu_to_le32(0xC00000EE) // -EIO +#define STATUS_INVALID_PARAMETER_1 cpu_to_le32(0xC00000EF) // -EINVAL +#define STATUS_INVALID_PARAMETER_2 cpu_to_le32(0xC00000F0) // -EINVAL +#define STATUS_INVALID_PARAMETER_3 cpu_to_le32(0xC00000F1) // -EINVAL +#define STATUS_INVALID_PARAMETER_4 cpu_to_le32(0xC00000F2) // -EINVAL +#define STATUS_INVALID_PARAMETER_5 cpu_to_le32(0xC00000F3) // -EINVAL +#define STATUS_INVALID_PARAMETER_6 cpu_to_le32(0xC00000F4) // -EINVAL +#define STATUS_INVALID_PARAMETER_7 cpu_to_le32(0xC00000F5) // -EINVAL +#define STATUS_INVALID_PARAMETER_8 cpu_to_le32(0xC00000F6) // -EINVAL +#define STATUS_INVALID_PARAMETER_9 cpu_to_le32(0xC00000F7) // -EINVAL +#define STATUS_INVALID_PARAMETER_10 cpu_to_le32(0xC00000F8) // -EINVAL +#define STATUS_INVALID_PARAMETER_11 cpu_to_le32(0xC00000F9) // -EINVAL +#define STATUS_INVALID_PARAMETER_12 cpu_to_le32(0xC00000FA) // -EINVAL +#define STATUS_REDIRECTOR_NOT_STARTED cpu_to_le32(0xC00000FB) // -EIO +#define STATUS_REDIRECTOR_STARTED cpu_to_le32(0xC00000FC) // -EIO +#define STATUS_STACK_OVERFLOW cpu_to_le32(0xC00000FD) // -EIO +#define STATUS_NO_SUCH_PACKAGE cpu_to_le32(0xC00000FE) // -EIO +#define STATUS_BAD_FUNCTION_TABLE cpu_to_le32(0xC00000FF) // -EIO +#define STATUS_VARIABLE_NOT_FOUND cpu_to_le32(0xC0000100) // -EIO +#define STATUS_DIRECTORY_NOT_EMPTY cpu_to_le32(0xC0000101) // -ENOTEMPTY +#define STATUS_FILE_CORRUPT_ERROR cpu_to_le32(0xC0000102) // -EIO +#define STATUS_NOT_A_DIRECTORY cpu_to_le32(0xC0000103) // -ENOTDIR +#define STATUS_BAD_LOGON_SESSION_STATE cpu_to_le32(0xC0000104) // -EIO +#define STATUS_LOGON_SESSION_COLLISION cpu_to_le32(0xC0000105) // -EIO +#define STATUS_NAME_TOO_LONG cpu_to_le32(0xC0000106) // -ENAMETOOLONG +#define STATUS_FILES_OPEN cpu_to_le32(0xC0000107) // -EIO +#define STATUS_CONNECTION_IN_USE cpu_to_le32(0xC0000108) // -EIO +#define STATUS_MESSAGE_NOT_FOUND cpu_to_le32(0xC0000109) // -EIO +#define STATUS_PROCESS_IS_TERMINATING cpu_to_le32(0xC000010A) // -EIO +#define STATUS_INVALID_LOGON_TYPE cpu_to_le32(0xC000010B) // -EIO +#define STATUS_NO_GUID_TRANSLATION cpu_to_le32(0xC000010C) // -EIO +#define STATUS_CANNOT_IMPERSONATE cpu_to_le32(0xC000010D) // -EIO +#define STATUS_IMAGE_ALREADY_LOADED cpu_to_le32(0xC000010E) // -EIO +#define STATUS_ABIOS_NOT_PRESENT cpu_to_le32(0xC000010F) // -EIO +#define STATUS_ABIOS_LID_NOT_EXIST cpu_to_le32(0xC0000110) // -EIO +#define STATUS_ABIOS_LID_ALREADY_OWNED cpu_to_le32(0xC0000111) // -EIO +#define STATUS_ABIOS_NOT_LID_OWNER cpu_to_le32(0xC0000112) // -EIO +#define STATUS_ABIOS_INVALID_COMMAND cpu_to_le32(0xC0000113) // -EIO +#define STATUS_ABIOS_INVALID_LID cpu_to_le32(0xC0000114) // -EIO +#define STATUS_ABIOS_SELECTOR_NOT_AVAILABLE cpu_to_le32(0xC0000115) // -EIO +#define STATUS_ABIOS_INVALID_SELECTOR cpu_to_le32(0xC0000116) // -EIO +#define STATUS_NO_LDT cpu_to_le32(0xC0000117) // -EIO +#define STATUS_INVALID_LDT_SIZE cpu_to_le32(0xC0000118) // -EIO +#define STATUS_INVALID_LDT_OFFSET cpu_to_le32(0xC0000119) // -EIO +#define STATUS_INVALID_LDT_DESCRIPTOR cpu_to_le32(0xC000011A) // -EIO +#define STATUS_INVALID_IMAGE_NE_FORMAT cpu_to_le32(0xC000011B) // -EIO +#define STATUS_RXACT_INVALID_STATE cpu_to_le32(0xC000011C) // -EIO +#define STATUS_RXACT_COMMIT_FAILURE cpu_to_le32(0xC000011D) // -EIO +#define STATUS_MAPPED_FILE_SIZE_ZERO cpu_to_le32(0xC000011E) // -EIO +#define STATUS_TOO_MANY_OPENED_FILES cpu_to_le32(0xC000011F) // -EMFILE +#define STATUS_CANCELLED cpu_to_le32(0xC0000120) // -EIO +#define STATUS_CANNOT_DELETE cpu_to_le32(0xC0000121) // -EACCES +#define STATUS_INVALID_COMPUTER_NAME cpu_to_le32(0xC0000122) // -EIO +#define STATUS_FILE_DELETED cpu_to_le32(0xC0000123) // -EIO +#define STATUS_SPECIAL_ACCOUNT cpu_to_le32(0xC0000124) // -EIO +#define STATUS_SPECIAL_GROUP cpu_to_le32(0xC0000125) // -EIO +#define STATUS_SPECIAL_USER cpu_to_le32(0xC0000126) // -EIO +#define STATUS_MEMBERS_PRIMARY_GROUP cpu_to_le32(0xC0000127) // -EIO +#define STATUS_FILE_CLOSED cpu_to_le32(0xC0000128) // -EBADF +#define STATUS_TOO_MANY_THREADS cpu_to_le32(0xC0000129) // -EIO +#define STATUS_THREAD_NOT_IN_PROCESS cpu_to_le32(0xC000012A) // -EIO +#define STATUS_TOKEN_ALREADY_IN_USE cpu_to_le32(0xC000012B) // -EIO +#define STATUS_PAGEFILE_QUOTA_EXCEEDED cpu_to_le32(0xC000012C) // -EDQUOT +#define STATUS_COMMITMENT_LIMIT cpu_to_le32(0xC000012D) // -EIO +#define STATUS_INVALID_IMAGE_LE_FORMAT cpu_to_le32(0xC000012E) // -EIO +#define STATUS_INVALID_IMAGE_NOT_MZ cpu_to_le32(0xC000012F) // -EIO +#define STATUS_INVALID_IMAGE_PROTECT cpu_to_le32(0xC0000130) // -EIO +#define STATUS_INVALID_IMAGE_WIN_16 cpu_to_le32(0xC0000131) // -EIO +#define STATUS_LOGON_SERVER_CONFLICT cpu_to_le32(0xC0000132) // -EIO +#define STATUS_TIME_DIFFERENCE_AT_DC cpu_to_le32(0xC0000133) // -EIO +#define STATUS_SYNCHRONIZATION_REQUIRED cpu_to_le32(0xC0000134) // -EIO +#define STATUS_DLL_NOT_FOUND cpu_to_le32(0xC0000135) // -ENOENT +#define STATUS_OPEN_FAILED cpu_to_le32(0xC0000136) // -EIO +#define STATUS_IO_PRIVILEGE_FAILED cpu_to_le32(0xC0000137) // -EIO +#define STATUS_ORDINAL_NOT_FOUND cpu_to_le32(0xC0000138) // -EIO +#define STATUS_ENTRYPOINT_NOT_FOUND cpu_to_le32(0xC0000139) // -EIO +#define STATUS_CONTROL_C_EXIT cpu_to_le32(0xC000013A) // -EIO +#define STATUS_LOCAL_DISCONNECT cpu_to_le32(0xC000013B) // -EIO +#define STATUS_REMOTE_DISCONNECT cpu_to_le32(0xC000013C) // -ESHUTDOWN +#define STATUS_REMOTE_RESOURCES cpu_to_le32(0xC000013D) // -EIO +#define STATUS_LINK_FAILED cpu_to_le32(0xC000013E) // -EXDEV +#define STATUS_LINK_TIMEOUT cpu_to_le32(0xC000013F) // -ETIMEDOUT +#define STATUS_INVALID_CONNECTION cpu_to_le32(0xC0000140) // -EIO +#define STATUS_INVALID_ADDRESS cpu_to_le32(0xC0000141) // -EIO +#define STATUS_DLL_INIT_FAILED cpu_to_le32(0xC0000142) // -EIO +#define STATUS_MISSING_SYSTEMFILE cpu_to_le32(0xC0000143) // -EIO +#define STATUS_UNHANDLED_EXCEPTION cpu_to_le32(0xC0000144) // -EIO +#define STATUS_APP_INIT_FAILURE cpu_to_le32(0xC0000145) // -EIO +#define STATUS_PAGEFILE_CREATE_FAILED cpu_to_le32(0xC0000146) // -EIO +#define STATUS_NO_PAGEFILE cpu_to_le32(0xC0000147) // -EIO +#define STATUS_INVALID_LEVEL cpu_to_le32(0xC0000148) // -EIO +#define STATUS_WRONG_PASSWORD_CORE cpu_to_le32(0xC0000149) // -EIO +#define STATUS_ILLEGAL_FLOAT_CONTEXT cpu_to_le32(0xC000014A) // -EIO +#define STATUS_PIPE_BROKEN cpu_to_le32(0xC000014B) // -EPIPE +#define STATUS_REGISTRY_CORRUPT cpu_to_le32(0xC000014C) // -EIO +#define STATUS_REGISTRY_IO_FAILED cpu_to_le32(0xC000014D) // -EIO +#define STATUS_NO_EVENT_PAIR cpu_to_le32(0xC000014E) // -EIO +#define STATUS_UNRECOGNIZED_VOLUME cpu_to_le32(0xC000014F) // -EIO +#define STATUS_SERIAL_NO_DEVICE_INITED cpu_to_le32(0xC0000150) // -EIO +#define STATUS_NO_SUCH_ALIAS cpu_to_le32(0xC0000151) // -EIO +#define STATUS_MEMBER_NOT_IN_ALIAS cpu_to_le32(0xC0000152) // -EIO +#define STATUS_MEMBER_IN_ALIAS cpu_to_le32(0xC0000153) // -EIO +#define STATUS_ALIAS_EXISTS cpu_to_le32(0xC0000154) // -EIO +#define STATUS_LOGON_NOT_GRANTED cpu_to_le32(0xC0000155) // -EIO +#define STATUS_TOO_MANY_SECRETS cpu_to_le32(0xC0000156) // -EIO +#define STATUS_SECRET_TOO_LONG cpu_to_le32(0xC0000157) // -EIO +#define STATUS_INTERNAL_DB_ERROR cpu_to_le32(0xC0000158) // -EIO +#define STATUS_FULLSCREEN_MODE cpu_to_le32(0xC0000159) // -EIO +#define STATUS_TOO_MANY_CONTEXT_IDS cpu_to_le32(0xC000015A) // -EIO +#define STATUS_LOGON_TYPE_NOT_GRANTED cpu_to_le32(0xC000015B) // -EIO +#define STATUS_NOT_REGISTRY_FILE cpu_to_le32(0xC000015C) // -EIO +#define STATUS_NT_CROSS_ENCRYPTION_REQUIRED cpu_to_le32(0xC000015D) // -EIO +#define STATUS_DOMAIN_CTRLR_CONFIG_ERROR cpu_to_le32(0xC000015E) // -EIO +#define STATUS_FT_MISSING_MEMBER cpu_to_le32(0xC000015F) // -EIO +#define STATUS_ILL_FORMED_SERVICE_ENTRY cpu_to_le32(0xC0000160) // -EIO +#define STATUS_ILLEGAL_CHARACTER cpu_to_le32(0xC0000161) // -EIO +#define STATUS_UNMAPPABLE_CHARACTER cpu_to_le32(0xC0000162) // -EIO +#define STATUS_UNDEFINED_CHARACTER cpu_to_le32(0xC0000163) // -EIO +#define STATUS_FLOPPY_VOLUME cpu_to_le32(0xC0000164) // -EIO +#define STATUS_FLOPPY_ID_MARK_NOT_FOUND cpu_to_le32(0xC0000165) // -EIO +#define STATUS_FLOPPY_WRONG_CYLINDER cpu_to_le32(0xC0000166) // -EIO +#define STATUS_FLOPPY_UNKNOWN_ERROR cpu_to_le32(0xC0000167) // -EIO +#define STATUS_FLOPPY_BAD_REGISTERS cpu_to_le32(0xC0000168) // -EIO +#define STATUS_DISK_RECALIBRATE_FAILED cpu_to_le32(0xC0000169) // -EIO +#define STATUS_DISK_OPERATION_FAILED cpu_to_le32(0xC000016A) // -EIO +#define STATUS_DISK_RESET_FAILED cpu_to_le32(0xC000016B) // -EIO +#define STATUS_SHARED_IRQ_BUSY cpu_to_le32(0xC000016C) // -EBUSY +#define STATUS_FT_ORPHANING cpu_to_le32(0xC000016D) // -EIO +#define STATUS_BIOS_FAILED_TO_CONNECT_INTERRUPT cpu_to_le32(0xC000016E) // -EIO +#define STATUS_PARTITION_FAILURE cpu_to_le32(0xC0000172) // -EIO +#define STATUS_INVALID_BLOCK_LENGTH cpu_to_le32(0xC0000173) // -EIO +#define STATUS_DEVICE_NOT_PARTITIONED cpu_to_le32(0xC0000174) // -EIO +#define STATUS_UNABLE_TO_LOCK_MEDIA cpu_to_le32(0xC0000175) // -EIO +#define STATUS_UNABLE_TO_UNLOAD_MEDIA cpu_to_le32(0xC0000176) // -EIO +#define STATUS_EOM_OVERFLOW cpu_to_le32(0xC0000177) // -EIO +#define STATUS_NO_MEDIA cpu_to_le32(0xC0000178) // -EIO +#define STATUS_NO_SUCH_MEMBER cpu_to_le32(0xC000017A) // -EIO +#define STATUS_INVALID_MEMBER cpu_to_le32(0xC000017B) // -EIO +#define STATUS_KEY_DELETED cpu_to_le32(0xC000017C) // -EIO +#define STATUS_NO_LOG_SPACE cpu_to_le32(0xC000017D) // -EIO +#define STATUS_TOO_MANY_SIDS cpu_to_le32(0xC000017E) // -EIO +#define STATUS_LM_CROSS_ENCRYPTION_REQUIRED cpu_to_le32(0xC000017F) // -EIO +#define STATUS_KEY_HAS_CHILDREN cpu_to_le32(0xC0000180) // -EIO +#define STATUS_CHILD_MUST_BE_VOLATILE cpu_to_le32(0xC0000181) // -EIO +#define STATUS_DEVICE_CONFIGURATION_ERROR cpu_to_le32(0xC0000182) // -EIO +#define STATUS_DRIVER_INTERNAL_ERROR cpu_to_le32(0xC0000183) // -EIO +#define STATUS_INVALID_DEVICE_STATE cpu_to_le32(0xC0000184) // -EIO +#define STATUS_IO_DEVICE_ERROR cpu_to_le32(0xC0000185) // -EIO +#define STATUS_DEVICE_PROTOCOL_ERROR cpu_to_le32(0xC0000186) // -EIO +#define STATUS_BACKUP_CONTROLLER cpu_to_le32(0xC0000187) // -EIO +#define STATUS_LOG_FILE_FULL cpu_to_le32(0xC0000188) // -EIO +#define STATUS_TOO_LATE cpu_to_le32(0xC0000189) // -EIO +#define STATUS_NO_TRUST_LSA_SECRET cpu_to_le32(0xC000018A) // -EIO +#define STATUS_NO_TRUST_SAM_ACCOUNT cpu_to_le32(0xC000018B) // -EIO +#define STATUS_TRUSTED_DOMAIN_FAILURE cpu_to_le32(0xC000018C) // -EIO +#define STATUS_TRUSTED_RELATIONSHIP_FAILURE cpu_to_le32(0xC000018D) // -EIO +#define STATUS_EVENTLOG_FILE_CORRUPT cpu_to_le32(0xC000018E) // -EIO +#define STATUS_EVENTLOG_CANT_START cpu_to_le32(0xC000018F) // -EIO +#define STATUS_TRUST_FAILURE cpu_to_le32(0xC0000190) // -EIO +#define STATUS_MUTANT_LIMIT_EXCEEDED cpu_to_le32(0xC0000191) // -EIO +#define STATUS_NETLOGON_NOT_STARTED cpu_to_le32(0xC0000192) // -EIO +#define STATUS_ACCOUNT_EXPIRED cpu_to_le32(0xC0000193) // -EKEYEXPIRED +#define STATUS_POSSIBLE_DEADLOCK cpu_to_le32(0xC0000194) // -EIO +#define STATUS_NETWORK_CREDENTIAL_CONFLICT cpu_to_le32(0xC0000195) // -EIO +#define STATUS_REMOTE_SESSION_LIMIT cpu_to_le32(0xC0000196) // -EIO +#define STATUS_EVENTLOG_FILE_CHANGED cpu_to_le32(0xC0000197) // -EIO +#define STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT cpu_to_le32(0xC0000198) // -EIO +#define STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT cpu_to_le32(0xC0000199) // -EIO +#define STATUS_NOLOGON_SERVER_TRUST_ACCOUNT cpu_to_le32(0xC000019A) // -EIO +#define STATUS_DOMAIN_TRUST_INCONSISTENT cpu_to_le32(0xC000019B) // -EIO +#define STATUS_FS_DRIVER_REQUIRED cpu_to_le32(0xC000019C) // -EOPNOTSUPP +#define STATUS_IMAGE_ALREADY_LOADED_AS_DLL cpu_to_le32(0xC000019D) // -EIO +#define STATUS_INVALID_LOCK_RANGE cpu_to_le32(0xC00001A1) // -EIO +#define STATUS_NETWORK_OPEN_RESTRICTION cpu_to_le32(0xC0000201) // -EIO +#define STATUS_NO_USER_SESSION_KEY cpu_to_le32(0xC0000202) // -EIO +#define STATUS_USER_SESSION_DELETED cpu_to_le32(0xC0000203) // -EIO +#define STATUS_RESOURCE_LANG_NOT_FOUND cpu_to_le32(0xC0000204) // -EIO +#define STATUS_INSUFF_SERVER_RESOURCES cpu_to_le32(0xC0000205) // -EIO +#define STATUS_INVALID_BUFFER_SIZE cpu_to_le32(0xC0000206) // -EIO +#define STATUS_INVALID_ADDRESS_COMPONENT cpu_to_le32(0xC0000207) // -EIO +#define STATUS_INVALID_ADDRESS_WILDCARD cpu_to_le32(0xC0000208) // -EIO +#define STATUS_TOO_MANY_ADDRESSES cpu_to_le32(0xC0000209) // -EIO +#define STATUS_ADDRESS_ALREADY_EXISTS cpu_to_le32(0xC000020A) // -EADDRINUSE +#define STATUS_ADDRESS_CLOSED cpu_to_le32(0xC000020B) // -EIO +#define STATUS_CONNECTION_DISCONNECTED cpu_to_le32(0xC000020C) // -ECONNABORTED +#define STATUS_CONNECTION_RESET cpu_to_le32(0xC000020D) // -ENETRESET +#define STATUS_TOO_MANY_NODES cpu_to_le32(0xC000020E) // -EIO +#define STATUS_TRANSACTION_ABORTED cpu_to_le32(0xC000020F) // -EIO +#define STATUS_TRANSACTION_TIMED_OUT cpu_to_le32(0xC0000210) // -EIO +#define STATUS_TRANSACTION_NO_RELEASE cpu_to_le32(0xC0000211) // -EIO +#define STATUS_TRANSACTION_NO_MATCH cpu_to_le32(0xC0000212) // -EIO +#define STATUS_TRANSACTION_RESPONDED cpu_to_le32(0xC0000213) // -EIO +#define STATUS_TRANSACTION_INVALID_ID cpu_to_le32(0xC0000214) // -EIO +#define STATUS_TRANSACTION_INVALID_TYPE cpu_to_le32(0xC0000215) // -EIO +#define STATUS_NOT_SERVER_SESSION cpu_to_le32(0xC0000216) // -EIO +#define STATUS_NOT_CLIENT_SESSION cpu_to_le32(0xC0000217) // -EIO +#define STATUS_CANNOT_LOAD_REGISTRY_FILE cpu_to_le32(0xC0000218) // -EIO +#define STATUS_DEBUG_ATTACH_FAILED cpu_to_le32(0xC0000219) // -EIO +#define STATUS_SYSTEM_PROCESS_TERMINATED cpu_to_le32(0xC000021A) // -EIO +#define STATUS_DATA_NOT_ACCEPTED cpu_to_le32(0xC000021B) // -EIO +#define STATUS_NO_BROWSER_SERVERS_FOUND cpu_to_le32(0xC000021C) // -EIO +#define STATUS_VDM_HARD_ERROR cpu_to_le32(0xC000021D) // -EIO +#define STATUS_DRIVER_CANCEL_TIMEOUT cpu_to_le32(0xC000021E) // -EIO +#define STATUS_REPLY_MESSAGE_MISMATCH cpu_to_le32(0xC000021F) // -EIO +#define STATUS_MAPPED_ALIGNMENT cpu_to_le32(0xC0000220) // -EIO +#define STATUS_IMAGE_CHECKSUM_MISMATCH cpu_to_le32(0xC0000221) // -EIO +#define STATUS_LOST_WRITEBEHIND_DATA cpu_to_le32(0xC0000222) // -EIO +#define STATUS_CLIENT_SERVER_PARAMETERS_INVALID cpu_to_le32(0xC0000223) // -EIO +#define STATUS_PASSWORD_MUST_CHANGE cpu_to_le32(0xC0000224) // -EIO +#define STATUS_NOT_FOUND cpu_to_le32(0xC0000225) // -ENOENT +#define STATUS_NOT_TINY_STREAM cpu_to_le32(0xC0000226) // -EIO +#define STATUS_RECOVERY_FAILURE cpu_to_le32(0xC0000227) // -EIO +#define STATUS_STACK_OVERFLOW_READ cpu_to_le32(0xC0000228) // -EIO +#define STATUS_FAIL_CHECK cpu_to_le32(0xC0000229) // -EIO +#define STATUS_DUPLICATE_OBJECTID cpu_to_le32(0xC000022A) // -EIO +#define STATUS_OBJECTID_EXISTS cpu_to_le32(0xC000022B) // -EIO +#define STATUS_CONVERT_TO_LARGE cpu_to_le32(0xC000022C) // -EIO +#define STATUS_RETRY cpu_to_le32(0xC000022D) // -EAGAIN +#define STATUS_FOUND_OUT_OF_SCOPE cpu_to_le32(0xC000022E) // -EIO +#define STATUS_ALLOCATE_BUCKET cpu_to_le32(0xC000022F) // -EIO +#define STATUS_PROPSET_NOT_FOUND cpu_to_le32(0xC0000230) // -EIO +#define STATUS_MARSHALL_OVERFLOW cpu_to_le32(0xC0000231) // -EIO +#define STATUS_INVALID_VARIANT cpu_to_le32(0xC0000232) // -EIO +#define STATUS_DOMAIN_CONTROLLER_NOT_FOUND cpu_to_le32(0xC0000233) // -EIO +#define STATUS_ACCOUNT_LOCKED_OUT cpu_to_le32(0xC0000234) // -EACCES +#define STATUS_HANDLE_NOT_CLOSABLE cpu_to_le32(0xC0000235) // -EIO +#define STATUS_CONNECTION_REFUSED cpu_to_le32(0xC0000236) // -EIO +#define STATUS_GRACEFUL_DISCONNECT cpu_to_le32(0xC0000237) // -EIO +#define STATUS_ADDRESS_ALREADY_ASSOCIATED cpu_to_le32(0xC0000238) // -EIO +#define STATUS_ADDRESS_NOT_ASSOCIATED cpu_to_le32(0xC0000239) // -EIO +#define STATUS_CONNECTION_INVALID cpu_to_le32(0xC000023A) // -EIO +#define STATUS_CONNECTION_ACTIVE cpu_to_le32(0xC000023B) // -EIO +#define STATUS_NETWORK_UNREACHABLE cpu_to_le32(0xC000023C) // -ENETUNREACH +#define STATUS_HOST_UNREACHABLE cpu_to_le32(0xC000023D) // -EHOSTDOWN +#define STATUS_PROTOCOL_UNREACHABLE cpu_to_le32(0xC000023E) // -ENETUNREACH +#define STATUS_PORT_UNREACHABLE cpu_to_le32(0xC000023F) // -ENETUNREACH +#define STATUS_REQUEST_ABORTED cpu_to_le32(0xC0000240) // -EIO +#define STATUS_CONNECTION_ABORTED cpu_to_le32(0xC0000241) // -ECONNABORTED +#define STATUS_BAD_COMPRESSION_BUFFER cpu_to_le32(0xC0000242) // -EIO +#define STATUS_USER_MAPPED_FILE cpu_to_le32(0xC0000243) // -EIO +#define STATUS_AUDIT_FAILED cpu_to_le32(0xC0000244) // -EIO +#define STATUS_TIMER_RESOLUTION_NOT_SET cpu_to_le32(0xC0000245) // -EIO +#define STATUS_CONNECTION_COUNT_LIMIT cpu_to_le32(0xC0000246) // -EIO +#define STATUS_LOGIN_TIME_RESTRICTION cpu_to_le32(0xC0000247) // -EACCES +#define STATUS_LOGIN_WKSTA_RESTRICTION cpu_to_le32(0xC0000248) // -EACCES +#define STATUS_IMAGE_MP_UP_MISMATCH cpu_to_le32(0xC0000249) // -EIO +#define STATUS_INSUFFICIENT_LOGON_INFO cpu_to_le32(0xC0000250) // -EIO +#define STATUS_BAD_DLL_ENTRYPOINT cpu_to_le32(0xC0000251) // -EIO +#define STATUS_BAD_SERVICE_ENTRYPOINT cpu_to_le32(0xC0000252) // -EIO +#define STATUS_LPC_REPLY_LOST cpu_to_le32(0xC0000253) // -EIO +#define STATUS_IP_ADDRESS_CONFLICT1 cpu_to_le32(0xC0000254) // -EIO +#define STATUS_IP_ADDRESS_CONFLICT2 cpu_to_le32(0xC0000255) // -EIO +#define STATUS_REGISTRY_QUOTA_LIMIT cpu_to_le32(0xC0000256) // -EDQUOT +#define STATUS_PATH_NOT_COVERED cpu_to_le32(0xC0000257) // -EREMOTE +#define STATUS_NO_CALLBACK_ACTIVE cpu_to_le32(0xC0000258) // -EIO +#define STATUS_LICENSE_QUOTA_EXCEEDED cpu_to_le32(0xC0000259) // -EACCES +#define STATUS_PWD_TOO_SHORT cpu_to_le32(0xC000025A) // -EIO +#define STATUS_PWD_TOO_RECENT cpu_to_le32(0xC000025B) // -EIO +#define STATUS_PWD_HISTORY_CONFLICT cpu_to_le32(0xC000025C) // -EIO +#define STATUS_PLUGPLAY_NO_DEVICE cpu_to_le32(0xC000025E) // -EIO +#define STATUS_UNSUPPORTED_COMPRESSION cpu_to_le32(0xC000025F) // -EIO +#define STATUS_INVALID_HW_PROFILE cpu_to_le32(0xC0000260) // -EIO +#define STATUS_INVALID_PLUGPLAY_DEVICE_PATH cpu_to_le32(0xC0000261) // -EIO +#define STATUS_DRIVER_ORDINAL_NOT_FOUND cpu_to_le32(0xC0000262) // -EIO +#define STATUS_DRIVER_ENTRYPOINT_NOT_FOUND cpu_to_le32(0xC0000263) // -EIO +#define STATUS_RESOURCE_NOT_OWNED cpu_to_le32(0xC0000264) // -EIO +#define STATUS_TOO_MANY_LINKS cpu_to_le32(0xC0000265) // -EMLINK +#define STATUS_QUOTA_LIST_INCONSISTENT cpu_to_le32(0xC0000266) // -EIO +#define STATUS_FILE_IS_OFFLINE cpu_to_le32(0xC0000267) // -EIO +#define STATUS_EVALUATION_EXPIRATION cpu_to_le32(0xC0000268) // -EIO +#define STATUS_ILLEGAL_DLL_RELOCATION cpu_to_le32(0xC0000269) // -EIO +#define STATUS_LICENSE_VIOLATION cpu_to_le32(0xC000026A) // -EIO +#define STATUS_DLL_INIT_FAILED_LOGOFF cpu_to_le32(0xC000026B) // -EIO +#define STATUS_DRIVER_UNABLE_TO_LOAD cpu_to_le32(0xC000026C) // -EIO +#define STATUS_DFS_UNAVAILABLE cpu_to_le32(0xC000026D) // -EIO +#define STATUS_VOLUME_DISMOUNTED cpu_to_le32(0xC000026E) // -EIO +#define STATUS_WX86_INTERNAL_ERROR cpu_to_le32(0xC000026F) // -EIO +#define STATUS_WX86_FLOAT_STACK_CHECK cpu_to_le32(0xC0000270) // -EIO +#define STATUS_VALIDATE_CONTINUE cpu_to_le32(0xC0000271) // -EIO +#define STATUS_NO_MATCH cpu_to_le32(0xC0000272) // -EIO +#define STATUS_NO_MORE_MATCHES cpu_to_le32(0xC0000273) // -EIO +#define STATUS_NOT_A_REPARSE_POINT cpu_to_le32(0xC0000275) // -ENODATA +#define STATUS_IO_REPARSE_TAG_INVALID cpu_to_le32(0xC0000276) // -EIO +#define STATUS_IO_REPARSE_TAG_MISMATCH cpu_to_le32(0xC0000277) // -EIO +#define STATUS_IO_REPARSE_DATA_INVALID cpu_to_le32(0xC0000278) // -EIO +#define STATUS_IO_REPARSE_TAG_NOT_HANDLED cpu_to_le32(0xC0000279) // -EOPNOTSUPP +#define STATUS_REPARSE_POINT_NOT_RESOLVED cpu_to_le32(0xC0000280) // -EIO +#define STATUS_DIRECTORY_IS_A_REPARSE_POINT cpu_to_le32(0xC0000281) // -EIO +#define STATUS_RANGE_LIST_CONFLICT cpu_to_le32(0xC0000282) // -EIO +#define STATUS_SOURCE_ELEMENT_EMPTY cpu_to_le32(0xC0000283) // -EIO +#define STATUS_DESTINATION_ELEMENT_FULL cpu_to_le32(0xC0000284) // -EIO +#define STATUS_ILLEGAL_ELEMENT_ADDRESS cpu_to_le32(0xC0000285) // -EIO +#define STATUS_MAGAZINE_NOT_PRESENT cpu_to_le32(0xC0000286) // -EIO +#define STATUS_REINITIALIZATION_NEEDED cpu_to_le32(0xC0000287) // -EIO +#define STATUS_ENCRYPTION_FAILED cpu_to_le32(0xC000028A) // -EIO +#define STATUS_DECRYPTION_FAILED cpu_to_le32(0xC000028B) // -EIO +#define STATUS_RANGE_NOT_FOUND cpu_to_le32(0xC000028C) // -EIO +#define STATUS_NO_RECOVERY_POLICY cpu_to_le32(0xC000028D) // -EIO +#define STATUS_NO_EFS cpu_to_le32(0xC000028E) // -EIO +#define STATUS_WRONG_EFS cpu_to_le32(0xC000028F) // -EIO +#define STATUS_NO_USER_KEYS cpu_to_le32(0xC0000290) // -EIO +#define STATUS_FILE_NOT_ENCRYPTED cpu_to_le32(0xC0000291) // -EIO +#define STATUS_NOT_EXPORT_FORMAT cpu_to_le32(0xC0000292) // -EIO +#define STATUS_FILE_ENCRYPTED cpu_to_le32(0xC0000293) // -EIO +#define STATUS_WMI_GUID_NOT_FOUND cpu_to_le32(0xC0000295) // -EIO +#define STATUS_WMI_INSTANCE_NOT_FOUND cpu_to_le32(0xC0000296) // -EIO +#define STATUS_WMI_ITEMID_NOT_FOUND cpu_to_le32(0xC0000297) // -EIO +#define STATUS_WMI_TRY_AGAIN cpu_to_le32(0xC0000298) // -EIO +#define STATUS_SHARED_POLICY cpu_to_le32(0xC0000299) // -EIO +#define STATUS_POLICY_OBJECT_NOT_FOUND cpu_to_le32(0xC000029A) // -EIO +#define STATUS_POLICY_ONLY_IN_DS cpu_to_le32(0xC000029B) // -EIO +#define STATUS_VOLUME_NOT_UPGRADED cpu_to_le32(0xC000029C) // -EIO +#define STATUS_REMOTE_STORAGE_NOT_ACTIVE cpu_to_le32(0xC000029D) // -EIO +#define STATUS_REMOTE_STORAGE_MEDIA_ERROR cpu_to_le32(0xC000029E) // -EIO +#define STATUS_NO_TRACKING_SERVICE cpu_to_le32(0xC000029F) // -EIO +#define STATUS_SERVER_SID_MISMATCH cpu_to_le32(0xC00002A0) // -EIO +#define STATUS_DS_NO_ATTRIBUTE_OR_VALUE cpu_to_le32(0xC00002A1) // -EIO +#define STATUS_DS_INVALID_ATTRIBUTE_SYNTAX cpu_to_le32(0xC00002A2) // -EIO +#define STATUS_DS_ATTRIBUTE_TYPE_UNDEFINED cpu_to_le32(0xC00002A3) // -EIO +#define STATUS_DS_ATTRIBUTE_OR_VALUE_EXISTS cpu_to_le32(0xC00002A4) // -EIO +#define STATUS_DS_BUSY cpu_to_le32(0xC00002A5) // -EBUSY +#define STATUS_DS_UNAVAILABLE cpu_to_le32(0xC00002A6) // -EIO +#define STATUS_DS_NO_RIDS_ALLOCATED cpu_to_le32(0xC00002A7) // -EIO +#define STATUS_DS_NO_MORE_RIDS cpu_to_le32(0xC00002A8) // -EIO +#define STATUS_DS_INCORRECT_ROLE_OWNER cpu_to_le32(0xC00002A9) // -EIO +#define STATUS_DS_RIDMGR_INIT_ERROR cpu_to_le32(0xC00002AA) // -EIO +#define STATUS_DS_OBJ_CLASS_VIOLATION cpu_to_le32(0xC00002AB) // -EIO +#define STATUS_DS_CANT_ON_NON_LEAF cpu_to_le32(0xC00002AC) // -EIO +#define STATUS_DS_CANT_ON_RDN cpu_to_le32(0xC00002AD) // -EIO +#define STATUS_DS_CANT_MOD_OBJ_CLASS cpu_to_le32(0xC00002AE) // -EIO +#define STATUS_DS_CROSS_DOM_MOVE_FAILED cpu_to_le32(0xC00002AF) // -EIO +#define STATUS_DS_GC_NOT_AVAILABLE cpu_to_le32(0xC00002B0) // -EIO +#define STATUS_DIRECTORY_SERVICE_REQUIRED cpu_to_le32(0xC00002B1) // -EIO +#define STATUS_REPARSE_ATTRIBUTE_CONFLICT cpu_to_le32(0xC00002B2) // -EIO +#define STATUS_CANT_ENABLE_DENY_ONLY cpu_to_le32(0xC00002B3) // -EIO +#define STATUS_FLOAT_MULTIPLE_FAULTS cpu_to_le32(0xC00002B4) // -EIO +#define STATUS_FLOAT_MULTIPLE_TRAPS cpu_to_le32(0xC00002B5) // -EIO +#define STATUS_DEVICE_REMOVED cpu_to_le32(0xC00002B6) // -EIO +#define STATUS_JOURNAL_DELETE_IN_PROGRESS cpu_to_le32(0xC00002B7) // -EIO +#define STATUS_JOURNAL_NOT_ACTIVE cpu_to_le32(0xC00002B8) // -EIO +#define STATUS_NOINTERFACE cpu_to_le32(0xC00002B9) // -EIO +#define STATUS_DS_ADMIN_LIMIT_EXCEEDED cpu_to_le32(0xC00002C1) // -EIO +#define STATUS_DRIVER_FAILED_SLEEP cpu_to_le32(0xC00002C2) // -EIO +#define STATUS_MUTUAL_AUTHENTICATION_FAILED cpu_to_le32(0xC00002C3) // -EIO +#define STATUS_CORRUPT_SYSTEM_FILE cpu_to_le32(0xC00002C4) // -EIO +#define STATUS_DATATYPE_MISALIGNMENT_ERROR cpu_to_le32(0xC00002C5) // -EIO +#define STATUS_WMI_READ_ONLY cpu_to_le32(0xC00002C6) // -EROFS +#define STATUS_WMI_SET_FAILURE cpu_to_le32(0xC00002C7) // -EIO +#define STATUS_COMMITMENT_MINIMUM cpu_to_le32(0xC00002C8) // -EIO +#define STATUS_REG_NAT_CONSUMPTION cpu_to_le32(0xC00002C9) // -EIO +#define STATUS_TRANSPORT_FULL cpu_to_le32(0xC00002CA) // -EIO +#define STATUS_DS_SAM_INIT_FAILURE cpu_to_le32(0xC00002CB) // -EIO +#define STATUS_ONLY_IF_CONNECTED cpu_to_le32(0xC00002CC) // -EIO +#define STATUS_DS_SENSITIVE_GROUP_VIOLATION cpu_to_le32(0xC00002CD) // -EIO +#define STATUS_PNP_RESTART_ENUMERATION cpu_to_le32(0xC00002CE) // -EIO +#define STATUS_JOURNAL_ENTRY_DELETED cpu_to_le32(0xC00002CF) // -EIO +#define STATUS_DS_CANT_MOD_PRIMARYGROUPID cpu_to_le32(0xC00002D0) // -EIO +#define STATUS_SYSTEM_IMAGE_BAD_SIGNATURE cpu_to_le32(0xC00002D1) // -EIO +#define STATUS_PNP_REBOOT_REQUIRED cpu_to_le32(0xC00002D2) // -EIO +#define STATUS_POWER_STATE_INVALID cpu_to_le32(0xC00002D3) // -EIO +#define STATUS_DS_INVALID_GROUP_TYPE cpu_to_le32(0xC00002D4) // -EIO +#define STATUS_DS_NO_NEST_GLOBALGROUP_IN_MIXEDDOMAIN cpu_to_le32(0xC00002D5) // -EIO +#define STATUS_DS_NO_NEST_LOCALGROUP_IN_MIXEDDOMAIN cpu_to_le32(0xC00002D6) // -EIO +#define STATUS_DS_GLOBAL_CANT_HAVE_LOCAL_MEMBER cpu_to_le32(0xC00002D7) // -EIO +#define STATUS_DS_GLOBAL_CANT_HAVE_UNIVERSAL_MEMBER cpu_to_le32(0xC00002D8) // -EIO +#define STATUS_DS_UNIVERSAL_CANT_HAVE_LOCAL_MEMBER cpu_to_le32(0xC00002D9) // -EIO +#define STATUS_DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER cpu_to_le32(0xC00002DA) // -EIO +#define STATUS_DS_LOCAL_CANT_HAVE_CROSSDOMAIN_LOCAL_MEMBER cpu_to_le32(0xC00002DB) // -EIO +#define STATUS_DS_HAVE_PRIMARY_MEMBERS cpu_to_le32(0xC00002DC) // -EIO +#define STATUS_WMI_NOT_SUPPORTED cpu_to_le32(0xC00002DD) // -EOPNOTSUPP +#define STATUS_INSUFFICIENT_POWER cpu_to_le32(0xC00002DE) // -EIO +#define STATUS_SAM_NEED_BOOTKEY_PASSWORD cpu_to_le32(0xC00002DF) // -EIO +#define STATUS_SAM_NEED_BOOTKEY_FLOPPY cpu_to_le32(0xC00002E0) // -EIO +#define STATUS_DS_CANT_START cpu_to_le32(0xC00002E1) // -EIO +#define STATUS_DS_INIT_FAILURE cpu_to_le32(0xC00002E2) // -EIO +#define STATUS_SAM_INIT_FAILURE cpu_to_le32(0xC00002E3) // -EIO +#define STATUS_DS_GC_REQUIRED cpu_to_le32(0xC00002E4) // -EIO +#define STATUS_DS_LOCAL_MEMBER_OF_LOCAL_ONLY cpu_to_le32(0xC00002E5) // -EIO +#define STATUS_DS_NO_FPO_IN_UNIVERSAL_GROUPS cpu_to_le32(0xC00002E6) // -EIO +#define STATUS_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED cpu_to_le32(0xC00002E7) // -EDQUOT +#define STATUS_MULTIPLE_FAULT_VIOLATION cpu_to_le32(0xC00002E8) // -EIO +#define STATUS_CURRENT_DOMAIN_NOT_ALLOWED cpu_to_le32(0xC00002E9) // -EIO +#define STATUS_CANNOT_MAKE cpu_to_le32(0xC00002EA) // -EIO +#define STATUS_SYSTEM_SHUTDOWN cpu_to_le32(0xC00002EB) // -EIO +#define STATUS_DS_INIT_FAILURE_CONSOLE cpu_to_le32(0xC00002EC) // -EIO +#define STATUS_DS_SAM_INIT_FAILURE_CONSOLE cpu_to_le32(0xC00002ED) // -EIO +#define STATUS_UNFINISHED_CONTEXT_DELETED cpu_to_le32(0xC00002EE) // -EIO +#define STATUS_NO_TGT_REPLY cpu_to_le32(0xC00002EF) // -EIO +#define STATUS_OBJECTID_NOT_FOUND cpu_to_le32(0xC00002F0) // -ENODATA +#define STATUS_NO_IP_ADDRESSES cpu_to_le32(0xC00002F1) // -EIO +#define STATUS_WRONG_CREDENTIAL_HANDLE cpu_to_le32(0xC00002F2) // -EIO +#define STATUS_CRYPTO_SYSTEM_INVALID cpu_to_le32(0xC00002F3) // -EIO +#define STATUS_MAX_REFERRALS_EXCEEDED cpu_to_le32(0xC00002F4) // -EIO +#define STATUS_MUST_BE_KDC cpu_to_le32(0xC00002F5) // -EIO +#define STATUS_STRONG_CRYPTO_NOT_SUPPORTED cpu_to_le32(0xC00002F6) // -EIO +#define STATUS_TOO_MANY_PRINCIPALS cpu_to_le32(0xC00002F7) // -EIO +#define STATUS_NO_PA_DATA cpu_to_le32(0xC00002F8) // -EIO +#define STATUS_PKINIT_NAME_MISMATCH cpu_to_le32(0xC00002F9) // -EIO +#define STATUS_SMARTCARD_LOGON_REQUIRED cpu_to_le32(0xC00002FA) // -EIO +#define STATUS_KDC_INVALID_REQUEST cpu_to_le32(0xC00002FB) // -EIO +#define STATUS_KDC_UNABLE_TO_REFER cpu_to_le32(0xC00002FC) // -EIO +#define STATUS_KDC_UNKNOWN_ETYPE cpu_to_le32(0xC00002FD) // -EIO +#define STATUS_SHUTDOWN_IN_PROGRESS cpu_to_le32(0xC00002FE) // -EIO +#define STATUS_SERVER_SHUTDOWN_IN_PROGRESS cpu_to_le32(0xC00002FF) // -EIO +#define STATUS_NOT_SUPPORTED_ON_SBS cpu_to_le32(0xC0000300) // -EOPNOTSUPP +#define STATUS_WMI_GUID_DISCONNECTED cpu_to_le32(0xC0000301) // -EIO +#define STATUS_WMI_ALREADY_DISABLED cpu_to_le32(0xC0000302) // -EIO +#define STATUS_WMI_ALREADY_ENABLED cpu_to_le32(0xC0000303) // -EIO +#define STATUS_MFT_TOO_FRAGMENTED cpu_to_le32(0xC0000304) // -EIO +#define STATUS_COPY_PROTECTION_FAILURE cpu_to_le32(0xC0000305) // -EIO +#define STATUS_CSS_AUTHENTICATION_FAILURE cpu_to_le32(0xC0000306) // -EIO +#define STATUS_CSS_KEY_NOT_PRESENT cpu_to_le32(0xC0000307) // -EIO +#define STATUS_CSS_KEY_NOT_ESTABLISHED cpu_to_le32(0xC0000308) // -EIO +#define STATUS_CSS_SCRAMBLED_SECTOR cpu_to_le32(0xC0000309) // -EIO +#define STATUS_CSS_REGION_MISMATCH cpu_to_le32(0xC000030A) // -EIO +#define STATUS_CSS_RESETS_EXHAUSTED cpu_to_le32(0xC000030B) // -EIO +#define STATUS_PKINIT_FAILURE cpu_to_le32(0xC0000320) // -EIO +#define STATUS_SMARTCARD_SUBSYSTEM_FAILURE cpu_to_le32(0xC0000321) // -EIO +#define STATUS_NO_KERB_KEY cpu_to_le32(0xC0000322) // -EIO +#define STATUS_HOST_DOWN cpu_to_le32(0xC0000350) // -EIO +#define STATUS_UNSUPPORTED_PREAUTH cpu_to_le32(0xC0000351) // -EIO +#define STATUS_EFS_ALG_BLOB_TOO_BIG cpu_to_le32(0xC0000352) // -EIO +#define STATUS_PORT_NOT_SET cpu_to_le32(0xC0000353) // -EIO +#define STATUS_DEBUGGER_INACTIVE cpu_to_le32(0xC0000354) // -EIO +#define STATUS_DS_VERSION_CHECK_FAILURE cpu_to_le32(0xC0000355) // -EIO +#define STATUS_AUDITING_DISABLED cpu_to_le32(0xC0000356) // -EIO +#define STATUS_PRENT4_MACHINE_ACCOUNT cpu_to_le32(0xC0000357) // -EIO +#define STATUS_DS_AG_CANT_HAVE_UNIVERSAL_MEMBER cpu_to_le32(0xC0000358) // -EIO +#define STATUS_INVALID_IMAGE_WIN_32 cpu_to_le32(0xC0000359) // -EIO +#define STATUS_INVALID_IMAGE_WIN_64 cpu_to_le32(0xC000035A) // -EIO +#define STATUS_BAD_BINDINGS cpu_to_le32(0xC000035B) // -EIO +#define STATUS_NETWORK_SESSION_EXPIRED cpu_to_le32(0xC000035C) // -EIO +#define STATUS_APPHELP_BLOCK cpu_to_le32(0xC000035D) // -EIO +#define STATUS_ALL_SIDS_FILTERED cpu_to_le32(0xC000035E) // -EIO +#define STATUS_NOT_SAFE_MODE_DRIVER cpu_to_le32(0xC000035F) // -EIO +#define STATUS_ACCESS_DISABLED_BY_POLICY_DEFAULT cpu_to_le32(0xC0000361) // -EACCES +#define STATUS_ACCESS_DISABLED_BY_POLICY_PATH cpu_to_le32(0xC0000362) // -EACCES +#define STATUS_ACCESS_DISABLED_BY_POLICY_PUBLISHER cpu_to_le32(0xC0000363) // -EACCES +#define STATUS_ACCESS_DISABLED_BY_POLICY_OTHER cpu_to_le32(0xC0000364) // -EACCES +#define STATUS_FAILED_DRIVER_ENTRY cpu_to_le32(0xC0000365) // -EIO +#define STATUS_DEVICE_ENUMERATION_ERROR cpu_to_le32(0xC0000366) // -EIO +#define STATUS_MOUNT_POINT_NOT_RESOLVED cpu_to_le32(0xC0000368) // -EIO +#define STATUS_INVALID_DEVICE_OBJECT_PARAMETER cpu_to_le32(0xC0000369) // -EIO /* * 'OCCURED' is typo in MS-ERREF, it should be 'OCCURRED', * but we'll keep it consistent with MS-ERREF. */ -#define STATUS_MCA_OCCURED cpu_to_le32(0xC000036A) -#define STATUS_DRIVER_BLOCKED_CRITICAL cpu_to_le32(0xC000036B) -#define STATUS_DRIVER_BLOCKED cpu_to_le32(0xC000036C) -#define STATUS_DRIVER_DATABASE_ERROR cpu_to_le32(0xC000036D) -#define STATUS_SYSTEM_HIVE_TOO_LARGE cpu_to_le32(0xC000036E) -#define STATUS_INVALID_IMPORT_OF_NON_DLL cpu_to_le32(0xC000036F) -#define STATUS_NO_SECRETS cpu_to_le32(0xC0000371) -#define STATUS_ACCESS_DISABLED_NO_SAFER_UI_BY_POLICY cpu_to_le32(0xC0000372) -#define STATUS_FAILED_STACK_SWITCH cpu_to_le32(0xC0000373) -#define STATUS_HEAP_CORRUPTION cpu_to_le32(0xC0000374) -#define STATUS_SMARTCARD_WRONG_PIN cpu_to_le32(0xC0000380) -#define STATUS_SMARTCARD_CARD_BLOCKED cpu_to_le32(0xC0000381) -#define STATUS_SMARTCARD_CARD_NOT_AUTHENTICATED cpu_to_le32(0xC0000382) -#define STATUS_SMARTCARD_NO_CARD cpu_to_le32(0xC0000383) -#define STATUS_SMARTCARD_NO_KEY_CONTAINER cpu_to_le32(0xC0000384) -#define STATUS_SMARTCARD_NO_CERTIFICATE cpu_to_le32(0xC0000385) -#define STATUS_SMARTCARD_NO_KEYSET cpu_to_le32(0xC0000386) -#define STATUS_SMARTCARD_IO_ERROR cpu_to_le32(0xC0000387) -#define STATUS_DOWNGRADE_DETECTED cpu_to_le32(0xC0000388) -#define STATUS_SMARTCARD_CERT_REVOKED cpu_to_le32(0xC0000389) -#define STATUS_ISSUING_CA_UNTRUSTED cpu_to_le32(0xC000038A) -#define STATUS_REVOCATION_OFFLINE_C cpu_to_le32(0xC000038B) -#define STATUS_PKINIT_CLIENT_FAILURE cpu_to_le32(0xC000038C) -#define STATUS_SMARTCARD_CERT_EXPIRED cpu_to_le32(0xC000038D) -#define STATUS_DRIVER_FAILED_PRIOR_UNLOAD cpu_to_le32(0xC000038E) -#define STATUS_SMARTCARD_SILENT_CONTEXT cpu_to_le32(0xC000038F) -#define STATUS_PER_USER_TRUST_QUOTA_EXCEEDED cpu_to_le32(0xC0000401) -#define STATUS_ALL_USER_TRUST_QUOTA_EXCEEDED cpu_to_le32(0xC0000402) -#define STATUS_USER_DELETE_TRUST_QUOTA_EXCEEDED cpu_to_le32(0xC0000403) -#define STATUS_DS_NAME_NOT_UNIQUE cpu_to_le32(0xC0000404) -#define STATUS_DS_DUPLICATE_ID_FOUND cpu_to_le32(0xC0000405) -#define STATUS_DS_GROUP_CONVERSION_ERROR cpu_to_le32(0xC0000406) -#define STATUS_VOLSNAP_PREPARE_HIBERNATE cpu_to_le32(0xC0000407) -#define STATUS_USER2USER_REQUIRED cpu_to_le32(0xC0000408) -#define STATUS_STACK_BUFFER_OVERRUN cpu_to_le32(0xC0000409) -#define STATUS_NO_S4U_PROT_SUPPORT cpu_to_le32(0xC000040A) -#define STATUS_CROSSREALM_DELEGATION_FAILURE cpu_to_le32(0xC000040B) -#define STATUS_REVOCATION_OFFLINE_KDC cpu_to_le32(0xC000040C) -#define STATUS_ISSUING_CA_UNTRUSTED_KDC cpu_to_le32(0xC000040D) -#define STATUS_KDC_CERT_EXPIRED cpu_to_le32(0xC000040E) -#define STATUS_KDC_CERT_REVOKED cpu_to_le32(0xC000040F) -#define STATUS_PARAMETER_QUOTA_EXCEEDED cpu_to_le32(0xC0000410) -#define STATUS_HIBERNATION_FAILURE cpu_to_le32(0xC0000411) -#define STATUS_DELAY_LOAD_FAILED cpu_to_le32(0xC0000412) -#define STATUS_AUTHENTICATION_FIREWALL_FAILED cpu_to_le32(0xC0000413) -#define STATUS_VDM_DISALLOWED cpu_to_le32(0xC0000414) -#define STATUS_HUNG_DISPLAY_DRIVER_THREAD cpu_to_le32(0xC0000415) -#define STATUS_INSUFFICIENT_RESOURCE_FOR_SPECIFIED_SHARED_SECTION_SIZE cpu_to_le32(0xC0000416) -#define STATUS_INVALID_CRUNTIME_PARAMETER cpu_to_le32(0xC0000417) -#define STATUS_NTLM_BLOCKED cpu_to_le32(0xC0000418) -#define STATUS_ASSERTION_FAILURE cpu_to_le32(0xC0000420) -#define STATUS_VERIFIER_STOP cpu_to_le32(0xC0000421) -#define STATUS_CALLBACK_POP_STACK cpu_to_le32(0xC0000423) -#define STATUS_INCOMPATIBLE_DRIVER_BLOCKED cpu_to_le32(0xC0000424) -#define STATUS_HIVE_UNLOADED cpu_to_le32(0xC0000425) -#define STATUS_COMPRESSION_DISABLED cpu_to_le32(0xC0000426) -#define STATUS_FILE_SYSTEM_LIMITATION cpu_to_le32(0xC0000427) -#define STATUS_INVALID_IMAGE_HASH cpu_to_le32(0xC0000428) -#define STATUS_NOT_CAPABLE cpu_to_le32(0xC0000429) -#define STATUS_REQUEST_OUT_OF_SEQUENCE cpu_to_le32(0xC000042A) -#define STATUS_IMPLEMENTATION_LIMIT cpu_to_le32(0xC000042B) -#define STATUS_ELEVATION_REQUIRED cpu_to_le32(0xC000042C) -#define STATUS_BEYOND_VDL cpu_to_le32(0xC0000432) -#define STATUS_ENCOUNTERED_WRITE_IN_PROGRESS cpu_to_le32(0xC0000433) -#define STATUS_PTE_CHANGED cpu_to_le32(0xC0000434) -#define STATUS_PURGE_FAILED cpu_to_le32(0xC0000435) -#define STATUS_CRED_REQUIRES_CONFIRMATION cpu_to_le32(0xC0000440) -#define STATUS_CS_ENCRYPTION_INVALID_SERVER_RESPONSE cpu_to_le32(0xC0000441) -#define STATUS_CS_ENCRYPTION_UNSUPPORTED_SERVER cpu_to_le32(0xC0000442) -#define STATUS_CS_ENCRYPTION_EXISTING_ENCRYPTED_FILE cpu_to_le32(0xC0000443) -#define STATUS_CS_ENCRYPTION_NEW_ENCRYPTED_FILE cpu_to_le32(0xC0000444) -#define STATUS_CS_ENCRYPTION_FILE_NOT_CSE cpu_to_le32(0xC0000445) -#define STATUS_INVALID_LABEL cpu_to_le32(0xC0000446) -#define STATUS_DRIVER_PROCESS_TERMINATED cpu_to_le32(0xC0000450) -#define STATUS_AMBIGUOUS_SYSTEM_DEVICE cpu_to_le32(0xC0000451) -#define STATUS_SYSTEM_DEVICE_NOT_FOUND cpu_to_le32(0xC0000452) -#define STATUS_RESTART_BOOT_APPLICATION cpu_to_le32(0xC0000453) -#define STATUS_INVALID_TASK_NAME cpu_to_le32(0xC0000500) -#define STATUS_INVALID_TASK_INDEX cpu_to_le32(0xC0000501) -#define STATUS_THREAD_ALREADY_IN_TASK cpu_to_le32(0xC0000502) -#define STATUS_CALLBACK_BYPASS cpu_to_le32(0xC0000503) -#define STATUS_SERVER_UNAVAILABLE cpu_to_le32(0xC0000466) -#define STATUS_FILE_NOT_AVAILABLE cpu_to_le32(0xC0000467) -#define STATUS_PORT_CLOSED cpu_to_le32(0xC0000700) -#define STATUS_MESSAGE_LOST cpu_to_le32(0xC0000701) -#define STATUS_INVALID_MESSAGE cpu_to_le32(0xC0000702) -#define STATUS_REQUEST_CANCELED cpu_to_le32(0xC0000703) -#define STATUS_RECURSIVE_DISPATCH cpu_to_le32(0xC0000704) -#define STATUS_LPC_RECEIVE_BUFFER_EXPECTED cpu_to_le32(0xC0000705) -#define STATUS_LPC_INVALID_CONNECTION_USAGE cpu_to_le32(0xC0000706) -#define STATUS_LPC_REQUESTS_NOT_ALLOWED cpu_to_le32(0xC0000707) -#define STATUS_RESOURCE_IN_USE cpu_to_le32(0xC0000708) -#define STATUS_HARDWARE_MEMORY_ERROR cpu_to_le32(0xC0000709) -#define STATUS_THREADPOOL_HANDLE_EXCEPTION cpu_to_le32(0xC000070A) -#define STATUS_THREADPOOL_SET_EVENT_ON_COMPLETION_FAILED cpu_to_le32(0xC000070B) -#define STATUS_THREADPOOL_RELEASE_SEMAPHORE_ON_COMPLETION_FAILED cpu_to_le32(0xC000070C) -#define STATUS_THREADPOOL_RELEASE_MUTEX_ON_COMPLETION_FAILED cpu_to_le32(0xC000070D) -#define STATUS_THREADPOOL_FREE_LIBRARY_ON_COMPLETION_FAILED cpu_to_le32(0xC000070E) -#define STATUS_THREADPOOL_RELEASED_DURING_OPERATION cpu_to_le32(0xC000070F) -#define STATUS_CALLBACK_RETURNED_WHILE_IMPERSONATING cpu_to_le32(0xC0000710) -#define STATUS_APC_RETURNED_WHILE_IMPERSONATING cpu_to_le32(0xC0000711) -#define STATUS_PROCESS_IS_PROTECTED cpu_to_le32(0xC0000712) -#define STATUS_MCA_EXCEPTION cpu_to_le32(0xC0000713) -#define STATUS_CERTIFICATE_MAPPING_NOT_UNIQUE cpu_to_le32(0xC0000714) -#define STATUS_SYMLINK_CLASS_DISABLED cpu_to_le32(0xC0000715) -#define STATUS_INVALID_IDN_NORMALIZATION cpu_to_le32(0xC0000716) -#define STATUS_NO_UNICODE_TRANSLATION cpu_to_le32(0xC0000717) -#define STATUS_ALREADY_REGISTERED cpu_to_le32(0xC0000718) -#define STATUS_CONTEXT_MISMATCH cpu_to_le32(0xC0000719) -#define STATUS_PORT_ALREADY_HAS_COMPLETION_LIST cpu_to_le32(0xC000071A) -#define STATUS_CALLBACK_RETURNED_THREAD_PRIORITY cpu_to_le32(0xC000071B) -#define STATUS_INVALID_THREAD cpu_to_le32(0xC000071C) -#define STATUS_CALLBACK_RETURNED_TRANSACTION cpu_to_le32(0xC000071D) -#define STATUS_CALLBACK_RETURNED_LDR_LOCK cpu_to_le32(0xC000071E) -#define STATUS_CALLBACK_RETURNED_LANG cpu_to_le32(0xC000071F) -#define STATUS_CALLBACK_RETURNED_PRI_BACK cpu_to_le32(0xC0000720) -#define STATUS_CALLBACK_RETURNED_THREAD_AFFINITY cpu_to_le32(0xC0000721) -#define STATUS_DISK_REPAIR_DISABLED cpu_to_le32(0xC0000800) -#define STATUS_DS_DOMAIN_RENAME_IN_PROGRESS cpu_to_le32(0xC0000801) -#define STATUS_DISK_QUOTA_EXCEEDED cpu_to_le32(0xC0000802) -#define STATUS_CONTENT_BLOCKED cpu_to_le32(0xC0000804) -#define STATUS_BAD_CLUSTERS cpu_to_le32(0xC0000805) -#define STATUS_VOLUME_DIRTY cpu_to_le32(0xC0000806) -#define STATUS_FILE_CHECKED_OUT cpu_to_le32(0xC0000901) -#define STATUS_CHECKOUT_REQUIRED cpu_to_le32(0xC0000902) -#define STATUS_BAD_FILE_TYPE cpu_to_le32(0xC0000903) -#define STATUS_FILE_TOO_LARGE cpu_to_le32(0xC0000904) -#define STATUS_FORMS_AUTH_REQUIRED cpu_to_le32(0xC0000905) -#define STATUS_VIRUS_INFECTED cpu_to_le32(0xC0000906) -#define STATUS_VIRUS_DELETED cpu_to_le32(0xC0000907) -#define STATUS_BAD_MCFG_TABLE cpu_to_le32(0xC0000908) -#define STATUS_WOW_ASSERTION cpu_to_le32(0xC0009898) -#define STATUS_INVALID_SIGNATURE cpu_to_le32(0xC000A000) -#define STATUS_HMAC_NOT_SUPPORTED cpu_to_le32(0xC000A001) -#define STATUS_IPSEC_QUEUE_OVERFLOW cpu_to_le32(0xC000A010) -#define STATUS_ND_QUEUE_OVERFLOW cpu_to_le32(0xC000A011) -#define STATUS_HOPLIMIT_EXCEEDED cpu_to_le32(0xC000A012) -#define STATUS_PROTOCOL_NOT_SUPPORTED cpu_to_le32(0xC000A013) -#define STATUS_LOST_WRITEBEHIND_DATA_NETWORK_DISCONNECTED cpu_to_le32(0xC000A080) -#define STATUS_LOST_WRITEBEHIND_DATA_NETWORK_SERVER_ERROR cpu_to_le32(0xC000A081) -#define STATUS_LOST_WRITEBEHIND_DATA_LOCAL_DISK_ERROR cpu_to_le32(0xC000A082) -#define STATUS_XML_PARSE_ERROR cpu_to_le32(0xC000A083) -#define STATUS_XMLDSIG_ERROR cpu_to_le32(0xC000A084) -#define STATUS_WRONG_COMPARTMENT cpu_to_le32(0xC000A085) -#define STATUS_AUTHIP_FAILURE cpu_to_le32(0xC000A086) -#define DBG_NO_STATE_CHANGE cpu_to_le32(0xC0010001) -#define DBG_APP_NOT_IDLE cpu_to_le32(0xC0010002) -#define RPC_NT_INVALID_STRING_BINDING cpu_to_le32(0xC0020001) -#define RPC_NT_WRONG_KIND_OF_BINDING cpu_to_le32(0xC0020002) -#define RPC_NT_INVALID_BINDING cpu_to_le32(0xC0020003) -#define RPC_NT_PROTSEQ_NOT_SUPPORTED cpu_to_le32(0xC0020004) -#define RPC_NT_INVALID_RPC_PROTSEQ cpu_to_le32(0xC0020005) -#define RPC_NT_INVALID_STRING_UUID cpu_to_le32(0xC0020006) -#define RPC_NT_INVALID_ENDPOINT_FORMAT cpu_to_le32(0xC0020007) -#define RPC_NT_INVALID_NET_ADDR cpu_to_le32(0xC0020008) -#define RPC_NT_NO_ENDPOINT_FOUND cpu_to_le32(0xC0020009) -#define RPC_NT_INVALID_TIMEOUT cpu_to_le32(0xC002000A) -#define RPC_NT_OBJECT_NOT_FOUND cpu_to_le32(0xC002000B) -#define RPC_NT_ALREADY_REGISTERED cpu_to_le32(0xC002000C) -#define RPC_NT_TYPE_ALREADY_REGISTERED cpu_to_le32(0xC002000D) -#define RPC_NT_ALREADY_LISTENING cpu_to_le32(0xC002000E) -#define RPC_NT_NO_PROTSEQS_REGISTERED cpu_to_le32(0xC002000F) -#define RPC_NT_NOT_LISTENING cpu_to_le32(0xC0020010) -#define RPC_NT_UNKNOWN_MGR_TYPE cpu_to_le32(0xC0020011) -#define RPC_NT_UNKNOWN_IF cpu_to_le32(0xC0020012) -#define RPC_NT_NO_BINDINGS cpu_to_le32(0xC0020013) -#define RPC_NT_NO_PROTSEQS cpu_to_le32(0xC0020014) -#define RPC_NT_CANT_CREATE_ENDPOINT cpu_to_le32(0xC0020015) -#define RPC_NT_OUT_OF_RESOURCES cpu_to_le32(0xC0020016) -#define RPC_NT_SERVER_UNAVAILABLE cpu_to_le32(0xC0020017) -#define RPC_NT_SERVER_TOO_BUSY cpu_to_le32(0xC0020018) -#define RPC_NT_INVALID_NETWORK_OPTIONS cpu_to_le32(0xC0020019) -#define RPC_NT_NO_CALL_ACTIVE cpu_to_le32(0xC002001A) -#define RPC_NT_CALL_FAILED cpu_to_le32(0xC002001B) -#define RPC_NT_CALL_FAILED_DNE cpu_to_le32(0xC002001C) -#define RPC_NT_PROTOCOL_ERROR cpu_to_le32(0xC002001D) -#define RPC_NT_UNSUPPORTED_TRANS_SYN cpu_to_le32(0xC002001F) -#define RPC_NT_UNSUPPORTED_TYPE cpu_to_le32(0xC0020021) -#define RPC_NT_INVALID_TAG cpu_to_le32(0xC0020022) -#define RPC_NT_INVALID_BOUND cpu_to_le32(0xC0020023) -#define RPC_NT_NO_ENTRY_NAME cpu_to_le32(0xC0020024) -#define RPC_NT_INVALID_NAME_SYNTAX cpu_to_le32(0xC0020025) -#define RPC_NT_UNSUPPORTED_NAME_SYNTAX cpu_to_le32(0xC0020026) -#define RPC_NT_UUID_NO_ADDRESS cpu_to_le32(0xC0020028) -#define RPC_NT_DUPLICATE_ENDPOINT cpu_to_le32(0xC0020029) -#define RPC_NT_UNKNOWN_AUTHN_TYPE cpu_to_le32(0xC002002A) -#define RPC_NT_MAX_CALLS_TOO_SMALL cpu_to_le32(0xC002002B) -#define RPC_NT_STRING_TOO_LONG cpu_to_le32(0xC002002C) -#define RPC_NT_PROTSEQ_NOT_FOUND cpu_to_le32(0xC002002D) -#define RPC_NT_PROCNUM_OUT_OF_RANGE cpu_to_le32(0xC002002E) -#define RPC_NT_BINDING_HAS_NO_AUTH cpu_to_le32(0xC002002F) -#define RPC_NT_UNKNOWN_AUTHN_SERVICE cpu_to_le32(0xC0020030) -#define RPC_NT_UNKNOWN_AUTHN_LEVEL cpu_to_le32(0xC0020031) -#define RPC_NT_INVALID_AUTH_IDENTITY cpu_to_le32(0xC0020032) -#define RPC_NT_UNKNOWN_AUTHZ_SERVICE cpu_to_le32(0xC0020033) -#define EPT_NT_INVALID_ENTRY cpu_to_le32(0xC0020034) -#define EPT_NT_CANT_PERFORM_OP cpu_to_le32(0xC0020035) -#define EPT_NT_NOT_REGISTERED cpu_to_le32(0xC0020036) -#define RPC_NT_NOTHING_TO_EXPORT cpu_to_le32(0xC0020037) -#define RPC_NT_INCOMPLETE_NAME cpu_to_le32(0xC0020038) -#define RPC_NT_INVALID_VERS_OPTION cpu_to_le32(0xC0020039) -#define RPC_NT_NO_MORE_MEMBERS cpu_to_le32(0xC002003A) -#define RPC_NT_NOT_ALL_OBJS_UNEXPORTED cpu_to_le32(0xC002003B) -#define RPC_NT_INTERFACE_NOT_FOUND cpu_to_le32(0xC002003C) -#define RPC_NT_ENTRY_ALREADY_EXISTS cpu_to_le32(0xC002003D) -#define RPC_NT_ENTRY_NOT_FOUND cpu_to_le32(0xC002003E) -#define RPC_NT_NAME_SERVICE_UNAVAILABLE cpu_to_le32(0xC002003F) -#define RPC_NT_INVALID_NAF_ID cpu_to_le32(0xC0020040) -#define RPC_NT_CANNOT_SUPPORT cpu_to_le32(0xC0020041) -#define RPC_NT_NO_CONTEXT_AVAILABLE cpu_to_le32(0xC0020042) -#define RPC_NT_INTERNAL_ERROR cpu_to_le32(0xC0020043) -#define RPC_NT_ZERO_DIVIDE cpu_to_le32(0xC0020044) -#define RPC_NT_ADDRESS_ERROR cpu_to_le32(0xC0020045) -#define RPC_NT_FP_DIV_ZERO cpu_to_le32(0xC0020046) -#define RPC_NT_FP_UNDERFLOW cpu_to_le32(0xC0020047) -#define RPC_NT_FP_OVERFLOW cpu_to_le32(0xC0020048) -#define RPC_NT_CALL_IN_PROGRESS cpu_to_le32(0xC0020049) -#define RPC_NT_NO_MORE_BINDINGS cpu_to_le32(0xC002004A) -#define RPC_NT_GROUP_MEMBER_NOT_FOUND cpu_to_le32(0xC002004B) -#define EPT_NT_CANT_CREATE cpu_to_le32(0xC002004C) -#define RPC_NT_INVALID_OBJECT cpu_to_le32(0xC002004D) -#define RPC_NT_NO_INTERFACES cpu_to_le32(0xC002004F) -#define RPC_NT_CALL_CANCELLED cpu_to_le32(0xC0020050) -#define RPC_NT_BINDING_INCOMPLETE cpu_to_le32(0xC0020051) -#define RPC_NT_COMM_FAILURE cpu_to_le32(0xC0020052) -#define RPC_NT_UNSUPPORTED_AUTHN_LEVEL cpu_to_le32(0xC0020053) -#define RPC_NT_NO_PRINC_NAME cpu_to_le32(0xC0020054) -#define RPC_NT_NOT_RPC_ERROR cpu_to_le32(0xC0020055) -#define RPC_NT_SEC_PKG_ERROR cpu_to_le32(0xC0020057) -#define RPC_NT_NOT_CANCELLED cpu_to_le32(0xC0020058) -#define RPC_NT_INVALID_ASYNC_HANDLE cpu_to_le32(0xC0020062) -#define RPC_NT_INVALID_ASYNC_CALL cpu_to_le32(0xC0020063) -#define RPC_NT_PROXY_ACCESS_DENIED cpu_to_le32(0xC0020064) -#define RPC_NT_NO_MORE_ENTRIES cpu_to_le32(0xC0030001) -#define RPC_NT_SS_CHAR_TRANS_OPEN_FAIL cpu_to_le32(0xC0030002) -#define RPC_NT_SS_CHAR_TRANS_SHORT_FILE cpu_to_le32(0xC0030003) -#define RPC_NT_SS_IN_NULL_CONTEXT cpu_to_le32(0xC0030004) -#define RPC_NT_SS_CONTEXT_MISMATCH cpu_to_le32(0xC0030005) -#define RPC_NT_SS_CONTEXT_DAMAGED cpu_to_le32(0xC0030006) -#define RPC_NT_SS_HANDLES_MISMATCH cpu_to_le32(0xC0030007) -#define RPC_NT_SS_CANNOT_GET_CALL_HANDLE cpu_to_le32(0xC0030008) -#define RPC_NT_NULL_REF_POINTER cpu_to_le32(0xC0030009) -#define RPC_NT_ENUM_VALUE_OUT_OF_RANGE cpu_to_le32(0xC003000A) -#define RPC_NT_BYTE_COUNT_TOO_SMALL cpu_to_le32(0xC003000B) -#define RPC_NT_BAD_STUB_DATA cpu_to_le32(0xC003000C) -#define RPC_NT_INVALID_ES_ACTION cpu_to_le32(0xC0030059) -#define RPC_NT_WRONG_ES_VERSION cpu_to_le32(0xC003005A) -#define RPC_NT_WRONG_STUB_VERSION cpu_to_le32(0xC003005B) -#define RPC_NT_INVALID_PIPE_OBJECT cpu_to_le32(0xC003005C) -#define RPC_NT_INVALID_PIPE_OPERATION cpu_to_le32(0xC003005D) -#define RPC_NT_WRONG_PIPE_VERSION cpu_to_le32(0xC003005E) -#define RPC_NT_PIPE_CLOSED cpu_to_le32(0xC003005F) -#define RPC_NT_PIPE_DISCIPLINE_ERROR cpu_to_le32(0xC0030060) -#define RPC_NT_PIPE_EMPTY cpu_to_le32(0xC0030061) -#define STATUS_PNP_BAD_MPS_TABLE cpu_to_le32(0xC0040035) -#define STATUS_PNP_TRANSLATION_FAILED cpu_to_le32(0xC0040036) -#define STATUS_PNP_IRQ_TRANSLATION_FAILED cpu_to_le32(0xC0040037) -#define STATUS_PNP_INVALID_ID cpu_to_le32(0xC0040038) -#define STATUS_IO_REISSUE_AS_CACHED cpu_to_le32(0xC0040039) -#define STATUS_CTX_WINSTATION_NAME_INVALID cpu_to_le32(0xC00A0001) -#define STATUS_CTX_INVALID_PD cpu_to_le32(0xC00A0002) -#define STATUS_CTX_PD_NOT_FOUND cpu_to_le32(0xC00A0003) -#define STATUS_CTX_CLOSE_PENDING cpu_to_le32(0xC00A0006) -#define STATUS_CTX_NO_OUTBUF cpu_to_le32(0xC00A0007) -#define STATUS_CTX_MODEM_INF_NOT_FOUND cpu_to_le32(0xC00A0008) -#define STATUS_CTX_INVALID_MODEMNAME cpu_to_le32(0xC00A0009) -#define STATUS_CTX_RESPONSE_ERROR cpu_to_le32(0xC00A000A) -#define STATUS_CTX_MODEM_RESPONSE_TIMEOUT cpu_to_le32(0xC00A000B) -#define STATUS_CTX_MODEM_RESPONSE_NO_CARRIER cpu_to_le32(0xC00A000C) -#define STATUS_CTX_MODEM_RESPONSE_NO_DIALTONE cpu_to_le32(0xC00A000D) -#define STATUS_CTX_MODEM_RESPONSE_BUSY cpu_to_le32(0xC00A000E) -#define STATUS_CTX_MODEM_RESPONSE_VOICE cpu_to_le32(0xC00A000F) -#define STATUS_CTX_TD_ERROR cpu_to_le32(0xC00A0010) -#define STATUS_CTX_LICENSE_CLIENT_INVALID cpu_to_le32(0xC00A0012) -#define STATUS_CTX_LICENSE_NOT_AVAILABLE cpu_to_le32(0xC00A0013) -#define STATUS_CTX_LICENSE_EXPIRED cpu_to_le32(0xC00A0014) -#define STATUS_CTX_WINSTATION_NOT_FOUND cpu_to_le32(0xC00A0015) -#define STATUS_CTX_WINSTATION_NAME_COLLISION cpu_to_le32(0xC00A0016) -#define STATUS_CTX_WINSTATION_BUSY cpu_to_le32(0xC00A0017) -#define STATUS_CTX_BAD_VIDEO_MODE cpu_to_le32(0xC00A0018) -#define STATUS_CTX_GRAPHICS_INVALID cpu_to_le32(0xC00A0022) -#define STATUS_CTX_NOT_CONSOLE cpu_to_le32(0xC00A0024) -#define STATUS_CTX_CLIENT_QUERY_TIMEOUT cpu_to_le32(0xC00A0026) -#define STATUS_CTX_CONSOLE_DISCONNECT cpu_to_le32(0xC00A0027) -#define STATUS_CTX_CONSOLE_CONNECT cpu_to_le32(0xC00A0028) -#define STATUS_CTX_SHADOW_DENIED cpu_to_le32(0xC00A002A) -#define STATUS_CTX_WINSTATION_ACCESS_DENIED cpu_to_le32(0xC00A002B) -#define STATUS_CTX_INVALID_WD cpu_to_le32(0xC00A002E) -#define STATUS_CTX_WD_NOT_FOUND cpu_to_le32(0xC00A002F) -#define STATUS_CTX_SHADOW_INVALID cpu_to_le32(0xC00A0030) -#define STATUS_CTX_SHADOW_DISABLED cpu_to_le32(0xC00A0031) -#define STATUS_RDP_PROTOCOL_ERROR cpu_to_le32(0xC00A0032) -#define STATUS_CTX_CLIENT_LICENSE_NOT_SET cpu_to_le32(0xC00A0033) -#define STATUS_CTX_CLIENT_LICENSE_IN_USE cpu_to_le32(0xC00A0034) -#define STATUS_CTX_SHADOW_ENDED_BY_MODE_CHANGE cpu_to_le32(0xC00A0035) -#define STATUS_CTX_SHADOW_NOT_RUNNING cpu_to_le32(0xC00A0036) -#define STATUS_CTX_LOGON_DISABLED cpu_to_le32(0xC00A0037) -#define STATUS_CTX_SECURITY_LAYER_ERROR cpu_to_le32(0xC00A0038) -#define STATUS_TS_INCOMPATIBLE_SESSIONS cpu_to_le32(0xC00A0039) -#define STATUS_MUI_FILE_NOT_FOUND cpu_to_le32(0xC00B0001) -#define STATUS_MUI_INVALID_FILE cpu_to_le32(0xC00B0002) -#define STATUS_MUI_INVALID_RC_CONFIG cpu_to_le32(0xC00B0003) -#define STATUS_MUI_INVALID_LOCALE_NAME cpu_to_le32(0xC00B0004) -#define STATUS_MUI_INVALID_ULTIMATEFALLBACK_NAME cpu_to_le32(0xC00B0005) -#define STATUS_MUI_FILE_NOT_LOADED cpu_to_le32(0xC00B0006) -#define STATUS_RESOURCE_ENUM_USER_STOP cpu_to_le32(0xC00B0007) -#define STATUS_CLUSTER_INVALID_NODE cpu_to_le32(0xC0130001) -#define STATUS_CLUSTER_NODE_EXISTS cpu_to_le32(0xC0130002) -#define STATUS_CLUSTER_JOIN_IN_PROGRESS cpu_to_le32(0xC0130003) -#define STATUS_CLUSTER_NODE_NOT_FOUND cpu_to_le32(0xC0130004) -#define STATUS_CLUSTER_LOCAL_NODE_NOT_FOUND cpu_to_le32(0xC0130005) -#define STATUS_CLUSTER_NETWORK_EXISTS cpu_to_le32(0xC0130006) -#define STATUS_CLUSTER_NETWORK_NOT_FOUND cpu_to_le32(0xC0130007) -#define STATUS_CLUSTER_NETINTERFACE_EXISTS cpu_to_le32(0xC0130008) -#define STATUS_CLUSTER_NETINTERFACE_NOT_FOUND cpu_to_le32(0xC0130009) -#define STATUS_CLUSTER_INVALID_REQUEST cpu_to_le32(0xC013000A) -#define STATUS_CLUSTER_INVALID_NETWORK_PROVIDER cpu_to_le32(0xC013000B) -#define STATUS_CLUSTER_NODE_DOWN cpu_to_le32(0xC013000C) -#define STATUS_CLUSTER_NODE_UNREACHABLE cpu_to_le32(0xC013000D) -#define STATUS_CLUSTER_NODE_NOT_MEMBER cpu_to_le32(0xC013000E) -#define STATUS_CLUSTER_JOIN_NOT_IN_PROGRESS cpu_to_le32(0xC013000F) -#define STATUS_CLUSTER_INVALID_NETWORK cpu_to_le32(0xC0130010) -#define STATUS_CLUSTER_NO_NET_ADAPTERS cpu_to_le32(0xC0130011) -#define STATUS_CLUSTER_NODE_UP cpu_to_le32(0xC0130012) -#define STATUS_CLUSTER_NODE_PAUSED cpu_to_le32(0xC0130013) -#define STATUS_CLUSTER_NODE_NOT_PAUSED cpu_to_le32(0xC0130014) -#define STATUS_CLUSTER_NO_SECURITY_CONTEXT cpu_to_le32(0xC0130015) -#define STATUS_CLUSTER_NETWORK_NOT_INTERNAL cpu_to_le32(0xC0130016) -#define STATUS_CLUSTER_POISONED cpu_to_le32(0xC0130017) -#define STATUS_ACPI_INVALID_OPCODE cpu_to_le32(0xC0140001) -#define STATUS_ACPI_STACK_OVERFLOW cpu_to_le32(0xC0140002) -#define STATUS_ACPI_ASSERT_FAILED cpu_to_le32(0xC0140003) -#define STATUS_ACPI_INVALID_INDEX cpu_to_le32(0xC0140004) -#define STATUS_ACPI_INVALID_ARGUMENT cpu_to_le32(0xC0140005) -#define STATUS_ACPI_FATAL cpu_to_le32(0xC0140006) -#define STATUS_ACPI_INVALID_SUPERNAME cpu_to_le32(0xC0140007) -#define STATUS_ACPI_INVALID_ARGTYPE cpu_to_le32(0xC0140008) -#define STATUS_ACPI_INVALID_OBJTYPE cpu_to_le32(0xC0140009) -#define STATUS_ACPI_INVALID_TARGETTYPE cpu_to_le32(0xC014000A) -#define STATUS_ACPI_INCORRECT_ARGUMENT_COUNT cpu_to_le32(0xC014000B) -#define STATUS_ACPI_ADDRESS_NOT_MAPPED cpu_to_le32(0xC014000C) -#define STATUS_ACPI_INVALID_EVENTTYPE cpu_to_le32(0xC014000D) -#define STATUS_ACPI_HANDLER_COLLISION cpu_to_le32(0xC014000E) -#define STATUS_ACPI_INVALID_DATA cpu_to_le32(0xC014000F) -#define STATUS_ACPI_INVALID_REGION cpu_to_le32(0xC0140010) -#define STATUS_ACPI_INVALID_ACCESS_SIZE cpu_to_le32(0xC0140011) -#define STATUS_ACPI_ACQUIRE_GLOBAL_LOCK cpu_to_le32(0xC0140012) -#define STATUS_ACPI_ALREADY_INITIALIZED cpu_to_le32(0xC0140013) -#define STATUS_ACPI_NOT_INITIALIZED cpu_to_le32(0xC0140014) -#define STATUS_ACPI_INVALID_MUTEX_LEVEL cpu_to_le32(0xC0140015) -#define STATUS_ACPI_MUTEX_NOT_OWNED cpu_to_le32(0xC0140016) -#define STATUS_ACPI_MUTEX_NOT_OWNER cpu_to_le32(0xC0140017) -#define STATUS_ACPI_RS_ACCESS cpu_to_le32(0xC0140018) -#define STATUS_ACPI_INVALID_TABLE cpu_to_le32(0xC0140019) -#define STATUS_ACPI_REG_HANDLER_FAILED cpu_to_le32(0xC0140020) -#define STATUS_ACPI_POWER_REQUEST_FAILED cpu_to_le32(0xC0140021) -#define STATUS_SXS_SECTION_NOT_FOUND cpu_to_le32(0xC0150001) -#define STATUS_SXS_CANT_GEN_ACTCTX cpu_to_le32(0xC0150002) -#define STATUS_SXS_INVALID_ACTCTXDATA_FORMAT cpu_to_le32(0xC0150003) -#define STATUS_SXS_ASSEMBLY_NOT_FOUND cpu_to_le32(0xC0150004) -#define STATUS_SXS_MANIFEST_FORMAT_ERROR cpu_to_le32(0xC0150005) -#define STATUS_SXS_MANIFEST_PARSE_ERROR cpu_to_le32(0xC0150006) -#define STATUS_SXS_ACTIVATION_CONTEXT_DISABLED cpu_to_le32(0xC0150007) -#define STATUS_SXS_KEY_NOT_FOUND cpu_to_le32(0xC0150008) -#define STATUS_SXS_VERSION_CONFLICT cpu_to_le32(0xC0150009) -#define STATUS_SXS_WRONG_SECTION_TYPE cpu_to_le32(0xC015000A) -#define STATUS_SXS_THREAD_QUERIES_DISABLED cpu_to_le32(0xC015000B) -#define STATUS_SXS_ASSEMBLY_MISSING cpu_to_le32(0xC015000C) -#define STATUS_SXS_PROCESS_DEFAULT_ALREADY_SET cpu_to_le32(0xC015000E) -#define STATUS_SXS_EARLY_DEACTIVATION cpu_to_le32(0xC015000F) -#define STATUS_SXS_INVALID_DEACTIVATION cpu_to_le32(0xC0150010) -#define STATUS_SXS_MULTIPLE_DEACTIVATION cpu_to_le32(0xC0150011) -#define STATUS_SXS_SYSTEM_DEFAULT_ACTIVATION_CONTEXT_EMPTY cpu_to_le32(0xC0150012) -#define STATUS_SXS_PROCESS_TERMINATION_REQUESTED cpu_to_le32(0xC0150013) -#define STATUS_SXS_CORRUPT_ACTIVATION_STACK cpu_to_le32(0xC0150014) -#define STATUS_SXS_CORRUPTION cpu_to_le32(0xC0150015) -#define STATUS_SXS_INVALID_IDENTITY_ATTRIBUTE_VALUE cpu_to_le32(0xC0150016) -#define STATUS_SXS_INVALID_IDENTITY_ATTRIBUTE_NAME cpu_to_le32(0xC0150017) -#define STATUS_SXS_IDENTITY_DUPLICATE_ATTRIBUTE cpu_to_le32(0xC0150018) -#define STATUS_SXS_IDENTITY_PARSE_ERROR cpu_to_le32(0xC0150019) -#define STATUS_SXS_COMPONENT_STORE_CORRUPT cpu_to_le32(0xC015001A) -#define STATUS_SXS_FILE_HASH_MISMATCH cpu_to_le32(0xC015001B) -#define STATUS_SXS_MANIFEST_IDENTITY_SAME_BUT_CONTENTS_DIFFERENT cpu_to_le32(0xC015001C) -#define STATUS_SXS_IDENTITIES_DIFFERENT cpu_to_le32(0xC015001D) -#define STATUS_SXS_ASSEMBLY_IS_NOT_A_DEPLOYMENT cpu_to_le32(0xC015001E) -#define STATUS_SXS_FILE_NOT_PART_OF_ASSEMBLY cpu_to_le32(0xC015001F) -#define STATUS_ADVANCED_INSTALLER_FAILED cpu_to_le32(0xC0150020) -#define STATUS_XML_ENCODING_MISMATCH cpu_to_le32(0xC0150021) -#define STATUS_SXS_MANIFEST_TOO_BIG cpu_to_le32(0xC0150022) -#define STATUS_SXS_SETTING_NOT_REGISTERED cpu_to_le32(0xC0150023) -#define STATUS_SXS_TRANSACTION_CLOSURE_INCOMPLETE cpu_to_le32(0xC0150024) -#define STATUS_SMI_PRIMITIVE_INSTALLER_FAILED cpu_to_le32(0xC0150025) -#define STATUS_GENERIC_COMMAND_FAILED cpu_to_le32(0xC0150026) -#define STATUS_SXS_FILE_HASH_MISSING cpu_to_le32(0xC0150027) -#define STATUS_TRANSACTIONAL_CONFLICT cpu_to_le32(0xC0190001) -#define STATUS_INVALID_TRANSACTION cpu_to_le32(0xC0190002) -#define STATUS_TRANSACTION_NOT_ACTIVE cpu_to_le32(0xC0190003) -#define STATUS_TM_INITIALIZATION_FAILED cpu_to_le32(0xC0190004) -#define STATUS_RM_NOT_ACTIVE cpu_to_le32(0xC0190005) -#define STATUS_RM_METADATA_CORRUPT cpu_to_le32(0xC0190006) -#define STATUS_TRANSACTION_NOT_JOINED cpu_to_le32(0xC0190007) -#define STATUS_DIRECTORY_NOT_RM cpu_to_le32(0xC0190008) -#define STATUS_TRANSACTIONS_UNSUPPORTED_REMOTE cpu_to_le32(0xC019000A) -#define STATUS_LOG_RESIZE_INVALID_SIZE cpu_to_le32(0xC019000B) -#define STATUS_REMOTE_FILE_VERSION_MISMATCH cpu_to_le32(0xC019000C) -#define STATUS_CRM_PROTOCOL_ALREADY_EXISTS cpu_to_le32(0xC019000F) -#define STATUS_TRANSACTION_PROPAGATION_FAILED cpu_to_le32(0xC0190010) -#define STATUS_CRM_PROTOCOL_NOT_FOUND cpu_to_le32(0xC0190011) -#define STATUS_TRANSACTION_SUPERIOR_EXISTS cpu_to_le32(0xC0190012) -#define STATUS_TRANSACTION_REQUEST_NOT_VALID cpu_to_le32(0xC0190013) -#define STATUS_TRANSACTION_NOT_REQUESTED cpu_to_le32(0xC0190014) -#define STATUS_TRANSACTION_ALREADY_ABORTED cpu_to_le32(0xC0190015) -#define STATUS_TRANSACTION_ALREADY_COMMITTED cpu_to_le32(0xC0190016) -#define STATUS_TRANSACTION_INVALID_MARSHALL_BUFFER cpu_to_le32(0xC0190017) -#define STATUS_CURRENT_TRANSACTION_NOT_VALID cpu_to_le32(0xC0190018) -#define STATUS_LOG_GROWTH_FAILED cpu_to_le32(0xC0190019) -#define STATUS_OBJECT_NO_LONGER_EXISTS cpu_to_le32(0xC0190021) -#define STATUS_STREAM_MINIVERSION_NOT_FOUND cpu_to_le32(0xC0190022) -#define STATUS_STREAM_MINIVERSION_NOT_VALID cpu_to_le32(0xC0190023) -#define STATUS_MINIVERSION_INACCESSIBLE_FROM_SPECIFIED_TRANSACTION cpu_to_le32(0xC0190024) -#define STATUS_CANT_OPEN_MINIVERSION_WITH_MODIFY_INTENT cpu_to_le32(0xC0190025) -#define STATUS_CANT_CREATE_MORE_STREAM_MINIVERSIONS cpu_to_le32(0xC0190026) -#define STATUS_HANDLE_NO_LONGER_VALID cpu_to_le32(0xC0190028) -#define STATUS_LOG_CORRUPTION_DETECTED cpu_to_le32(0xC0190030) -#define STATUS_RM_DISCONNECTED cpu_to_le32(0xC0190032) -#define STATUS_ENLISTMENT_NOT_SUPERIOR cpu_to_le32(0xC0190033) -#define STATUS_FILE_IDENTITY_NOT_PERSISTENT cpu_to_le32(0xC0190036) -#define STATUS_CANT_BREAK_TRANSACTIONAL_DEPENDENCY cpu_to_le32(0xC0190037) -#define STATUS_CANT_CROSS_RM_BOUNDARY cpu_to_le32(0xC0190038) -#define STATUS_TXF_DIR_NOT_EMPTY cpu_to_le32(0xC0190039) -#define STATUS_INDOUBT_TRANSACTIONS_EXIST cpu_to_le32(0xC019003A) -#define STATUS_TM_VOLATILE cpu_to_le32(0xC019003B) -#define STATUS_ROLLBACK_TIMER_EXPIRED cpu_to_le32(0xC019003C) -#define STATUS_TXF_ATTRIBUTE_CORRUPT cpu_to_le32(0xC019003D) -#define STATUS_EFS_NOT_ALLOWED_IN_TRANSACTION cpu_to_le32(0xC019003E) -#define STATUS_TRANSACTIONAL_OPEN_NOT_ALLOWED cpu_to_le32(0xC019003F) -#define STATUS_TRANSACTED_MAPPING_UNSUPPORTED_REMOTE cpu_to_le32(0xC0190040) -#define STATUS_TRANSACTION_REQUIRED_PROMOTION cpu_to_le32(0xC0190043) -#define STATUS_CANNOT_EXECUTE_FILE_IN_TRANSACTION cpu_to_le32(0xC0190044) -#define STATUS_TRANSACTIONS_NOT_FROZEN cpu_to_le32(0xC0190045) -#define STATUS_TRANSACTION_FREEZE_IN_PROGRESS cpu_to_le32(0xC0190046) -#define STATUS_NOT_SNAPSHOT_VOLUME cpu_to_le32(0xC0190047) -#define STATUS_NO_SAVEPOINT_WITH_OPEN_FILES cpu_to_le32(0xC0190048) -#define STATUS_SPARSE_NOT_ALLOWED_IN_TRANSACTION cpu_to_le32(0xC0190049) -#define STATUS_TM_IDENTITY_MISMATCH cpu_to_le32(0xC019004A) -#define STATUS_FLOATED_SECTION cpu_to_le32(0xC019004B) -#define STATUS_CANNOT_ACCEPT_TRANSACTED_WORK cpu_to_le32(0xC019004C) -#define STATUS_CANNOT_ABORT_TRANSACTIONS cpu_to_le32(0xC019004D) -#define STATUS_TRANSACTION_NOT_FOUND cpu_to_le32(0xC019004E) -#define STATUS_RESOURCEMANAGER_NOT_FOUND cpu_to_le32(0xC019004F) -#define STATUS_ENLISTMENT_NOT_FOUND cpu_to_le32(0xC0190050) -#define STATUS_TRANSACTIONMANAGER_NOT_FOUND cpu_to_le32(0xC0190051) -#define STATUS_TRANSACTIONMANAGER_NOT_ONLINE cpu_to_le32(0xC0190052) -#define STATUS_TRANSACTIONMANAGER_RECOVERY_NAME_COLLISION cpu_to_le32(0xC0190053) -#define STATUS_TRANSACTION_NOT_ROOT cpu_to_le32(0xC0190054) -#define STATUS_TRANSACTION_OBJECT_EXPIRED cpu_to_le32(0xC0190055) -#define STATUS_COMPRESSION_NOT_ALLOWED_IN_TRANSACTION cpu_to_le32(0xC0190056) -#define STATUS_TRANSACTION_RESPONSE_NOT_ENLISTED cpu_to_le32(0xC0190057) -#define STATUS_TRANSACTION_RECORD_TOO_LONG cpu_to_le32(0xC0190058) -#define STATUS_NO_LINK_TRACKING_IN_TRANSACTION cpu_to_le32(0xC0190059) -#define STATUS_OPERATION_NOT_SUPPORTED_IN_TRANSACTION cpu_to_le32(0xC019005A) -#define STATUS_TRANSACTION_INTEGRITY_VIOLATED cpu_to_le32(0xC019005B) -#define STATUS_LOG_SECTOR_INVALID cpu_to_le32(0xC01A0001) -#define STATUS_LOG_SECTOR_PARITY_INVALID cpu_to_le32(0xC01A0002) -#define STATUS_LOG_SECTOR_REMAPPED cpu_to_le32(0xC01A0003) -#define STATUS_LOG_BLOCK_INCOMPLETE cpu_to_le32(0xC01A0004) -#define STATUS_LOG_INVALID_RANGE cpu_to_le32(0xC01A0005) -#define STATUS_LOG_BLOCKS_EXHAUSTED cpu_to_le32(0xC01A0006) -#define STATUS_LOG_READ_CONTEXT_INVALID cpu_to_le32(0xC01A0007) -#define STATUS_LOG_RESTART_INVALID cpu_to_le32(0xC01A0008) -#define STATUS_LOG_BLOCK_VERSION cpu_to_le32(0xC01A0009) -#define STATUS_LOG_BLOCK_INVALID cpu_to_le32(0xC01A000A) -#define STATUS_LOG_READ_MODE_INVALID cpu_to_le32(0xC01A000B) -#define STATUS_LOG_METADATA_CORRUPT cpu_to_le32(0xC01A000D) -#define STATUS_LOG_METADATA_INVALID cpu_to_le32(0xC01A000E) -#define STATUS_LOG_METADATA_INCONSISTENT cpu_to_le32(0xC01A000F) -#define STATUS_LOG_RESERVATION_INVALID cpu_to_le32(0xC01A0010) -#define STATUS_LOG_CANT_DELETE cpu_to_le32(0xC01A0011) -#define STATUS_LOG_CONTAINER_LIMIT_EXCEEDED cpu_to_le32(0xC01A0012) -#define STATUS_LOG_START_OF_LOG cpu_to_le32(0xC01A0013) -#define STATUS_LOG_POLICY_ALREADY_INSTALLED cpu_to_le32(0xC01A0014) -#define STATUS_LOG_POLICY_NOT_INSTALLED cpu_to_le32(0xC01A0015) -#define STATUS_LOG_POLICY_INVALID cpu_to_le32(0xC01A0016) -#define STATUS_LOG_POLICY_CONFLICT cpu_to_le32(0xC01A0017) -#define STATUS_LOG_PINNED_ARCHIVE_TAIL cpu_to_le32(0xC01A0018) -#define STATUS_LOG_RECORD_NONEXISTENT cpu_to_le32(0xC01A0019) -#define STATUS_LOG_RECORDS_RESERVED_INVALID cpu_to_le32(0xC01A001A) -#define STATUS_LOG_SPACE_RESERVED_INVALID cpu_to_le32(0xC01A001B) -#define STATUS_LOG_TAIL_INVALID cpu_to_le32(0xC01A001C) -#define STATUS_LOG_FULL cpu_to_le32(0xC01A001D) -#define STATUS_LOG_MULTIPLEXED cpu_to_le32(0xC01A001E) -#define STATUS_LOG_DEDICATED cpu_to_le32(0xC01A001F) -#define STATUS_LOG_ARCHIVE_NOT_IN_PROGRESS cpu_to_le32(0xC01A0020) -#define STATUS_LOG_ARCHIVE_IN_PROGRESS cpu_to_le32(0xC01A0021) -#define STATUS_LOG_EPHEMERAL cpu_to_le32(0xC01A0022) -#define STATUS_LOG_NOT_ENOUGH_CONTAINERS cpu_to_le32(0xC01A0023) -#define STATUS_LOG_CLIENT_ALREADY_REGISTERED cpu_to_le32(0xC01A0024) -#define STATUS_LOG_CLIENT_NOT_REGISTERED cpu_to_le32(0xC01A0025) -#define STATUS_LOG_FULL_HANDLER_IN_PROGRESS cpu_to_le32(0xC01A0026) -#define STATUS_LOG_CONTAINER_READ_FAILED cpu_to_le32(0xC01A0027) -#define STATUS_LOG_CONTAINER_WRITE_FAILED cpu_to_le32(0xC01A0028) -#define STATUS_LOG_CONTAINER_OPEN_FAILED cpu_to_le32(0xC01A0029) -#define STATUS_LOG_CONTAINER_STATE_INVALID cpu_to_le32(0xC01A002A) -#define STATUS_LOG_STATE_INVALID cpu_to_le32(0xC01A002B) -#define STATUS_LOG_PINNED cpu_to_le32(0xC01A002C) -#define STATUS_LOG_METADATA_FLUSH_FAILED cpu_to_le32(0xC01A002D) -#define STATUS_LOG_INCONSISTENT_SECURITY cpu_to_le32(0xC01A002E) -#define STATUS_LOG_APPENDED_FLUSH_FAILED cpu_to_le32(0xC01A002F) -#define STATUS_LOG_PINNED_RESERVATION cpu_to_le32(0xC01A0030) -#define STATUS_VIDEO_HUNG_DISPLAY_DRIVER_THREAD cpu_to_le32(0xC01B00EA) -#define STATUS_FLT_NO_HANDLER_DEFINED cpu_to_le32(0xC01C0001) -#define STATUS_FLT_CONTEXT_ALREADY_DEFINED cpu_to_le32(0xC01C0002) -#define STATUS_FLT_INVALID_ASYNCHRONOUS_REQUEST cpu_to_le32(0xC01C0003) -#define STATUS_FLT_DISALLOW_FAST_IO cpu_to_le32(0xC01C0004) -#define STATUS_FLT_INVALID_NAME_REQUEST cpu_to_le32(0xC01C0005) -#define STATUS_FLT_NOT_SAFE_TO_POST_OPERATION cpu_to_le32(0xC01C0006) -#define STATUS_FLT_NOT_INITIALIZED cpu_to_le32(0xC01C0007) -#define STATUS_FLT_FILTER_NOT_READY cpu_to_le32(0xC01C0008) -#define STATUS_FLT_POST_OPERATION_CLEANUP cpu_to_le32(0xC01C0009) -#define STATUS_FLT_INTERNAL_ERROR cpu_to_le32(0xC01C000A) -#define STATUS_FLT_DELETING_OBJECT cpu_to_le32(0xC01C000B) -#define STATUS_FLT_MUST_BE_NONPAGED_POOL cpu_to_le32(0xC01C000C) -#define STATUS_FLT_DUPLICATE_ENTRY cpu_to_le32(0xC01C000D) -#define STATUS_FLT_CBDQ_DISABLED cpu_to_le32(0xC01C000E) -#define STATUS_FLT_DO_NOT_ATTACH cpu_to_le32(0xC01C000F) -#define STATUS_FLT_DO_NOT_DETACH cpu_to_le32(0xC01C0010) -#define STATUS_FLT_INSTANCE_ALTITUDE_COLLISION cpu_to_le32(0xC01C0011) -#define STATUS_FLT_INSTANCE_NAME_COLLISION cpu_to_le32(0xC01C0012) -#define STATUS_FLT_FILTER_NOT_FOUND cpu_to_le32(0xC01C0013) -#define STATUS_FLT_VOLUME_NOT_FOUND cpu_to_le32(0xC01C0014) -#define STATUS_FLT_INSTANCE_NOT_FOUND cpu_to_le32(0xC01C0015) -#define STATUS_FLT_CONTEXT_ALLOCATION_NOT_FOUND cpu_to_le32(0xC01C0016) -#define STATUS_FLT_INVALID_CONTEXT_REGISTRATION cpu_to_le32(0xC01C0017) -#define STATUS_FLT_NAME_CACHE_MISS cpu_to_le32(0xC01C0018) -#define STATUS_FLT_NO_DEVICE_OBJECT cpu_to_le32(0xC01C0019) -#define STATUS_FLT_VOLUME_ALREADY_MOUNTED cpu_to_le32(0xC01C001A) -#define STATUS_FLT_ALREADY_ENLISTED cpu_to_le32(0xC01C001B) -#define STATUS_FLT_CONTEXT_ALREADY_LINKED cpu_to_le32(0xC01C001C) -#define STATUS_FLT_NO_WAITER_FOR_REPLY cpu_to_le32(0xC01C0020) -#define STATUS_MONITOR_NO_DESCRIPTOR cpu_to_le32(0xC01D0001) -#define STATUS_MONITOR_UNKNOWN_DESCRIPTOR_FORMAT cpu_to_le32(0xC01D0002) -#define STATUS_MONITOR_INVALID_DESCRIPTOR_CHECKSUM cpu_to_le32(0xC01D0003) -#define STATUS_MONITOR_INVALID_STANDARD_TIMING_BLOCK cpu_to_le32(0xC01D0004) -#define STATUS_MONITOR_WMI_DATABLOCK_REGISTRATION_FAILED cpu_to_le32(0xC01D0005) -#define STATUS_MONITOR_INVALID_SERIAL_NUMBER_MONDSC_BLOCK cpu_to_le32(0xC01D0006) -#define STATUS_MONITOR_INVALID_USER_FRIENDLY_MONDSC_BLOCK cpu_to_le32(0xC01D0007) -#define STATUS_MONITOR_NO_MORE_DESCRIPTOR_DATA cpu_to_le32(0xC01D0008) -#define STATUS_MONITOR_INVALID_DETAILED_TIMING_BLOCK cpu_to_le32(0xC01D0009) -#define STATUS_GRAPHICS_NOT_EXCLUSIVE_MODE_OWNER cpu_to_le32(0xC01E0000) -#define STATUS_GRAPHICS_INSUFFICIENT_DMA_BUFFER cpu_to_le32(0xC01E0001) -#define STATUS_GRAPHICS_INVALID_DISPLAY_ADAPTER cpu_to_le32(0xC01E0002) -#define STATUS_GRAPHICS_ADAPTER_WAS_RESET cpu_to_le32(0xC01E0003) -#define STATUS_GRAPHICS_INVALID_DRIVER_MODEL cpu_to_le32(0xC01E0004) -#define STATUS_GRAPHICS_PRESENT_MODE_CHANGED cpu_to_le32(0xC01E0005) -#define STATUS_GRAPHICS_PRESENT_OCCLUDED cpu_to_le32(0xC01E0006) -#define STATUS_GRAPHICS_PRESENT_DENIED cpu_to_le32(0xC01E0007) -#define STATUS_GRAPHICS_CANNOTCOLORCONVERT cpu_to_le32(0xC01E0008) -#define STATUS_GRAPHICS_NO_VIDEO_MEMORY cpu_to_le32(0xC01E0100) -#define STATUS_GRAPHICS_CANT_LOCK_MEMORY cpu_to_le32(0xC01E0101) -#define STATUS_GRAPHICS_ALLOCATION_BUSY cpu_to_le32(0xC01E0102) -#define STATUS_GRAPHICS_TOO_MANY_REFERENCES cpu_to_le32(0xC01E0103) -#define STATUS_GRAPHICS_TRY_AGAIN_LATER cpu_to_le32(0xC01E0104) -#define STATUS_GRAPHICS_TRY_AGAIN_NOW cpu_to_le32(0xC01E0105) -#define STATUS_GRAPHICS_ALLOCATION_INVALID cpu_to_le32(0xC01E0106) -#define STATUS_GRAPHICS_UNSWIZZLING_APERTURE_UNAVAILABLE cpu_to_le32(0xC01E0107) -#define STATUS_GRAPHICS_UNSWIZZLING_APERTURE_UNSUPPORTED cpu_to_le32(0xC01E0108) -#define STATUS_GRAPHICS_CANT_EVICT_PINNED_ALLOCATION cpu_to_le32(0xC01E0109) -#define STATUS_GRAPHICS_INVALID_ALLOCATION_USAGE cpu_to_le32(0xC01E0110) -#define STATUS_GRAPHICS_CANT_RENDER_LOCKED_ALLOCATION cpu_to_le32(0xC01E0111) -#define STATUS_GRAPHICS_ALLOCATION_CLOSED cpu_to_le32(0xC01E0112) -#define STATUS_GRAPHICS_INVALID_ALLOCATION_INSTANCE cpu_to_le32(0xC01E0113) -#define STATUS_GRAPHICS_INVALID_ALLOCATION_HANDLE cpu_to_le32(0xC01E0114) -#define STATUS_GRAPHICS_WRONG_ALLOCATION_DEVICE cpu_to_le32(0xC01E0115) -#define STATUS_GRAPHICS_ALLOCATION_CONTENT_LOST cpu_to_le32(0xC01E0116) -#define STATUS_GRAPHICS_GPU_EXCEPTION_ON_DEVICE cpu_to_le32(0xC01E0200) -#define STATUS_GRAPHICS_INVALID_VIDPN_TOPOLOGY cpu_to_le32(0xC01E0300) -#define STATUS_GRAPHICS_VIDPN_TOPOLOGY_NOT_SUPPORTED cpu_to_le32(0xC01E0301) -#define STATUS_GRAPHICS_VIDPN_TOPOLOGY_CURRENTLY_NOT_SUPPORTED cpu_to_le32(0xC01E0302) -#define STATUS_GRAPHICS_INVALID_VIDPN cpu_to_le32(0xC01E0303) -#define STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE cpu_to_le32(0xC01E0304) -#define STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_TARGET cpu_to_le32(0xC01E0305) -#define STATUS_GRAPHICS_VIDPN_MODALITY_NOT_SUPPORTED cpu_to_le32(0xC01E0306) -#define STATUS_GRAPHICS_INVALID_VIDPN_SOURCEMODESET cpu_to_le32(0xC01E0308) -#define STATUS_GRAPHICS_INVALID_VIDPN_TARGETMODESET cpu_to_le32(0xC01E0309) -#define STATUS_GRAPHICS_INVALID_FREQUENCY cpu_to_le32(0xC01E030A) -#define STATUS_GRAPHICS_INVALID_ACTIVE_REGION cpu_to_le32(0xC01E030B) -#define STATUS_GRAPHICS_INVALID_TOTAL_REGION cpu_to_le32(0xC01E030C) -#define STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE_MODE cpu_to_le32(0xC01E0310) -#define STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_TARGET_MODE cpu_to_le32(0xC01E0311) -#define STATUS_GRAPHICS_PINNED_MODE_MUST_REMAIN_IN_SET cpu_to_le32(0xC01E0312) -#define STATUS_GRAPHICS_PATH_ALREADY_IN_TOPOLOGY cpu_to_le32(0xC01E0313) -#define STATUS_GRAPHICS_MODE_ALREADY_IN_MODESET cpu_to_le32(0xC01E0314) -#define STATUS_GRAPHICS_INVALID_VIDEOPRESENTSOURCESET cpu_to_le32(0xC01E0315) -#define STATUS_GRAPHICS_INVALID_VIDEOPRESENTTARGETSET cpu_to_le32(0xC01E0316) -#define STATUS_GRAPHICS_SOURCE_ALREADY_IN_SET cpu_to_le32(0xC01E0317) -#define STATUS_GRAPHICS_TARGET_ALREADY_IN_SET cpu_to_le32(0xC01E0318) -#define STATUS_GRAPHICS_INVALID_VIDPN_PRESENT_PATH cpu_to_le32(0xC01E0319) -#define STATUS_GRAPHICS_NO_RECOMMENDED_VIDPN_TOPOLOGY cpu_to_le32(0xC01E031A) -#define STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGESET cpu_to_le32(0xC01E031B) -#define STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGE cpu_to_le32(0xC01E031C) -#define STATUS_GRAPHICS_FREQUENCYRANGE_NOT_IN_SET cpu_to_le32(0xC01E031D) -#define STATUS_GRAPHICS_FREQUENCYRANGE_ALREADY_IN_SET cpu_to_le32(0xC01E031F) -#define STATUS_GRAPHICS_STALE_MODESET cpu_to_le32(0xC01E0320) -#define STATUS_GRAPHICS_INVALID_MONITOR_SOURCEMODESET cpu_to_le32(0xC01E0321) -#define STATUS_GRAPHICS_INVALID_MONITOR_SOURCE_MODE cpu_to_le32(0xC01E0322) -#define STATUS_GRAPHICS_NO_RECOMMENDED_FUNCTIONAL_VIDPN cpu_to_le32(0xC01E0323) -#define STATUS_GRAPHICS_MODE_ID_MUST_BE_UNIQUE cpu_to_le32(0xC01E0324) -#define STATUS_GRAPHICS_EMPTY_ADAPTER_MONITOR_MODE_SUPPORT_INTERSECTION cpu_to_le32(0xC01E0325) -#define STATUS_GRAPHICS_VIDEO_PRESENT_TARGETS_LESS_THAN_SOURCES cpu_to_le32(0xC01E0326) -#define STATUS_GRAPHICS_PATH_NOT_IN_TOPOLOGY cpu_to_le32(0xC01E0327) -#define STATUS_GRAPHICS_ADAPTER_MUST_HAVE_AT_LEAST_ONE_SOURCE cpu_to_le32(0xC01E0328) -#define STATUS_GRAPHICS_ADAPTER_MUST_HAVE_AT_LEAST_ONE_TARGET cpu_to_le32(0xC01E0329) -#define STATUS_GRAPHICS_INVALID_MONITORDESCRIPTORSET cpu_to_le32(0xC01E032A) -#define STATUS_GRAPHICS_INVALID_MONITORDESCRIPTOR cpu_to_le32(0xC01E032B) -#define STATUS_GRAPHICS_MONITORDESCRIPTOR_NOT_IN_SET cpu_to_le32(0xC01E032C) -#define STATUS_GRAPHICS_MONITORDESCRIPTOR_ALREADY_IN_SET cpu_to_le32(0xC01E032D) -#define STATUS_GRAPHICS_MONITORDESCRIPTOR_ID_MUST_BE_UNIQUE cpu_to_le32(0xC01E032E) -#define STATUS_GRAPHICS_INVALID_VIDPN_TARGET_SUBSET_TYPE cpu_to_le32(0xC01E032F) -#define STATUS_GRAPHICS_RESOURCES_NOT_RELATED cpu_to_le32(0xC01E0330) -#define STATUS_GRAPHICS_SOURCE_ID_MUST_BE_UNIQUE cpu_to_le32(0xC01E0331) -#define STATUS_GRAPHICS_TARGET_ID_MUST_BE_UNIQUE cpu_to_le32(0xC01E0332) -#define STATUS_GRAPHICS_NO_AVAILABLE_VIDPN_TARGET cpu_to_le32(0xC01E0333) -#define STATUS_GRAPHICS_MONITOR_COULD_NOT_BE_ASSOCIATED_WITH_ADAPTER cpu_to_le32(0xC01E0334) -#define STATUS_GRAPHICS_NO_VIDPNMGR cpu_to_le32(0xC01E0335) -#define STATUS_GRAPHICS_NO_ACTIVE_VIDPN cpu_to_le32(0xC01E0336) -#define STATUS_GRAPHICS_STALE_VIDPN_TOPOLOGY cpu_to_le32(0xC01E0337) -#define STATUS_GRAPHICS_MONITOR_NOT_CONNECTED cpu_to_le32(0xC01E0338) -#define STATUS_GRAPHICS_SOURCE_NOT_IN_TOPOLOGY cpu_to_le32(0xC01E0339) -#define STATUS_GRAPHICS_INVALID_PRIMARYSURFACE_SIZE cpu_to_le32(0xC01E033A) -#define STATUS_GRAPHICS_INVALID_VISIBLEREGION_SIZE cpu_to_le32(0xC01E033B) -#define STATUS_GRAPHICS_INVALID_STRIDE cpu_to_le32(0xC01E033C) -#define STATUS_GRAPHICS_INVALID_PIXELFORMAT cpu_to_le32(0xC01E033D) -#define STATUS_GRAPHICS_INVALID_COLORBASIS cpu_to_le32(0xC01E033E) -#define STATUS_GRAPHICS_INVALID_PIXELVALUEACCESSMODE cpu_to_le32(0xC01E033F) -#define STATUS_GRAPHICS_TARGET_NOT_IN_TOPOLOGY cpu_to_le32(0xC01E0340) -#define STATUS_GRAPHICS_NO_DISPLAY_MODE_MANAGEMENT_SUPPORT cpu_to_le32(0xC01E0341) -#define STATUS_GRAPHICS_VIDPN_SOURCE_IN_USE cpu_to_le32(0xC01E0342) -#define STATUS_GRAPHICS_CANT_ACCESS_ACTIVE_VIDPN cpu_to_le32(0xC01E0343) -#define STATUS_GRAPHICS_INVALID_PATH_IMPORTANCE_ORDINAL cpu_to_le32(0xC01E0344) -#define STATUS_GRAPHICS_INVALID_PATH_CONTENT_GEOMETRY_TRANSFORMATION cpu_to_le32(0xC01E0345) -#define STATUS_GRAPHICS_PATH_CONTENT_GEOMETRY_TRANSFORMATION_NOT_SUPPORTED cpu_to_le32(0xC01E0346) -#define STATUS_GRAPHICS_INVALID_GAMMA_RAMP cpu_to_le32(0xC01E0347) -#define STATUS_GRAPHICS_GAMMA_RAMP_NOT_SUPPORTED cpu_to_le32(0xC01E0348) -#define STATUS_GRAPHICS_MULTISAMPLING_NOT_SUPPORTED cpu_to_le32(0xC01E0349) -#define STATUS_GRAPHICS_MODE_NOT_IN_MODESET cpu_to_le32(0xC01E034A) -#define STATUS_GRAPHICS_INVALID_VIDPN_TOPOLOGY_RECOMMENDATION_REASON cpu_to_le32(0xC01E034D) -#define STATUS_GRAPHICS_INVALID_PATH_CONTENT_TYPE cpu_to_le32(0xC01E034E) -#define STATUS_GRAPHICS_INVALID_COPYPROTECTION_TYPE cpu_to_le32(0xC01E034F) -#define STATUS_GRAPHICS_UNASSIGNED_MODESET_ALREADY_EXISTS cpu_to_le32(0xC01E0350) -#define STATUS_GRAPHICS_INVALID_SCANLINE_ORDERING cpu_to_le32(0xC01E0352) -#define STATUS_GRAPHICS_TOPOLOGY_CHANGES_NOT_ALLOWED cpu_to_le32(0xC01E0353) -#define STATUS_GRAPHICS_NO_AVAILABLE_IMPORTANCE_ORDINALS cpu_to_le32(0xC01E0354) -#define STATUS_GRAPHICS_INCOMPATIBLE_PRIVATE_FORMAT cpu_to_le32(0xC01E0355) -#define STATUS_GRAPHICS_INVALID_MODE_PRUNING_ALGORITHM cpu_to_le32(0xC01E0356) -#define STATUS_GRAPHICS_INVALID_MONITOR_CAPABILITY_ORIGIN cpu_to_le32(0xC01E0357) -#define STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGE_CONSTRAINT cpu_to_le32(0xC01E0358) -#define STATUS_GRAPHICS_MAX_NUM_PATHS_REACHED cpu_to_le32(0xC01E0359) -#define STATUS_GRAPHICS_CANCEL_VIDPN_TOPOLOGY_AUGMENTATION cpu_to_le32(0xC01E035A) -#define STATUS_GRAPHICS_INVALID_CLIENT_TYPE cpu_to_le32(0xC01E035B) -#define STATUS_GRAPHICS_CLIENTVIDPN_NOT_SET cpu_to_le32(0xC01E035C) -#define STATUS_GRAPHICS_SPECIFIED_CHILD_ALREADY_CONNECTED cpu_to_le32(0xC01E0400) -#define STATUS_GRAPHICS_CHILD_DESCRIPTOR_NOT_SUPPORTED cpu_to_le32(0xC01E0401) -#define STATUS_GRAPHICS_NOT_A_LINKED_ADAPTER cpu_to_le32(0xC01E0430) -#define STATUS_GRAPHICS_LEADLINK_NOT_ENUMERATED cpu_to_le32(0xC01E0431) -#define STATUS_GRAPHICS_CHAINLINKS_NOT_ENUMERATED cpu_to_le32(0xC01E0432) -#define STATUS_GRAPHICS_ADAPTER_CHAIN_NOT_READY cpu_to_le32(0xC01E0433) -#define STATUS_GRAPHICS_CHAINLINKS_NOT_STARTED cpu_to_le32(0xC01E0434) -#define STATUS_GRAPHICS_CHAINLINKS_NOT_POWERED_ON cpu_to_le32(0xC01E0435) -#define STATUS_GRAPHICS_INCONSISTENT_DEVICE_LINK_STATE cpu_to_le32(0xC01E0436) -#define STATUS_GRAPHICS_NOT_POST_DEVICE_DRIVER cpu_to_le32(0xC01E0438) -#define STATUS_GRAPHICS_ADAPTER_ACCESS_NOT_EXCLUDED cpu_to_le32(0xC01E043B) -#define STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_DOES_NOT_HAVE_COPP_SEMANTICS cpu_to_le32(0xC01E051C) -#define STATUS_GRAPHICS_OPM_INVALID_INFORMATION_REQUEST cpu_to_le32(0xC01E051D) -#define STATUS_GRAPHICS_OPM_DRIVER_INTERNAL_ERROR cpu_to_le32(0xC01E051E) -#define STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_DOES_NOT_HAVE_OPM_SEMANTICS cpu_to_le32(0xC01E051F) -#define STATUS_GRAPHICS_OPM_SIGNALING_NOT_SUPPORTED cpu_to_le32(0xC01E0520) -#define STATUS_GRAPHICS_OPM_INVALID_CONFIGURATION_REQUEST cpu_to_le32(0xC01E0521) -#define STATUS_GRAPHICS_OPM_NOT_SUPPORTED cpu_to_le32(0xC01E0500) -#define STATUS_GRAPHICS_COPP_NOT_SUPPORTED cpu_to_le32(0xC01E0501) -#define STATUS_GRAPHICS_UAB_NOT_SUPPORTED cpu_to_le32(0xC01E0502) -#define STATUS_GRAPHICS_OPM_INVALID_ENCRYPTED_PARAMETERS cpu_to_le32(0xC01E0503) -#define STATUS_GRAPHICS_OPM_PARAMETER_ARRAY_TOO_SMALL cpu_to_le32(0xC01E0504) -#define STATUS_GRAPHICS_OPM_NO_PROTECTED_OUTPUTS_EXIST cpu_to_le32(0xC01E0505) -#define STATUS_GRAPHICS_PVP_NO_DISPLAY_DEVICE_CORRESPONDS_TO_NAME cpu_to_le32(0xC01E0506) -#define STATUS_GRAPHICS_PVP_DISPLAY_DEVICE_NOT_ATTACHED_TO_DESKTOP cpu_to_le32(0xC01E0507) -#define STATUS_GRAPHICS_PVP_MIRRORING_DEVICES_NOT_SUPPORTED cpu_to_le32(0xC01E0508) -#define STATUS_GRAPHICS_OPM_INVALID_POINTER cpu_to_le32(0xC01E050A) -#define STATUS_GRAPHICS_OPM_INTERNAL_ERROR cpu_to_le32(0xC01E050B) -#define STATUS_GRAPHICS_OPM_INVALID_HANDLE cpu_to_le32(0xC01E050C) -#define STATUS_GRAPHICS_PVP_NO_MONITORS_CORRESPOND_TO_DISPLAY_DEVICE cpu_to_le32(0xC01E050D) -#define STATUS_GRAPHICS_PVP_INVALID_CERTIFICATE_LENGTH cpu_to_le32(0xC01E050E) -#define STATUS_GRAPHICS_OPM_SPANNING_MODE_ENABLED cpu_to_le32(0xC01E050F) -#define STATUS_GRAPHICS_OPM_THEATER_MODE_ENABLED cpu_to_le32(0xC01E0510) -#define STATUS_GRAPHICS_PVP_HFS_FAILED cpu_to_le32(0xC01E0511) -#define STATUS_GRAPHICS_OPM_INVALID_SRM cpu_to_le32(0xC01E0512) -#define STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_HDCP cpu_to_le32(0xC01E0513) -#define STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_ACP cpu_to_le32(0xC01E0514) -#define STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_CGMSA cpu_to_le32(0xC01E0515) -#define STATUS_GRAPHICS_OPM_HDCP_SRM_NEVER_SET cpu_to_le32(0xC01E0516) -#define STATUS_GRAPHICS_OPM_RESOLUTION_TOO_HIGH cpu_to_le32(0xC01E0517) -#define STATUS_GRAPHICS_OPM_ALL_HDCP_HARDWARE_ALREADY_IN_USE cpu_to_le32(0xC01E0518) -#define STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_NO_LONGER_EXISTS cpu_to_le32(0xC01E051A) -#define STATUS_GRAPHICS_OPM_SESSION_TYPE_CHANGE_IN_PROGRESS cpu_to_le32(0xC01E051B) -#define STATUS_GRAPHICS_I2C_NOT_SUPPORTED cpu_to_le32(0xC01E0580) -#define STATUS_GRAPHICS_I2C_DEVICE_DOES_NOT_EXIST cpu_to_le32(0xC01E0581) -#define STATUS_GRAPHICS_I2C_ERROR_TRANSMITTING_DATA cpu_to_le32(0xC01E0582) -#define STATUS_GRAPHICS_I2C_ERROR_RECEIVING_DATA cpu_to_le32(0xC01E0583) -#define STATUS_GRAPHICS_DDCCI_VCP_NOT_SUPPORTED cpu_to_le32(0xC01E0584) -#define STATUS_GRAPHICS_DDCCI_INVALID_DATA cpu_to_le32(0xC01E0585) -#define STATUS_GRAPHICS_DDCCI_MONITOR_RETURNED_INVALID_TIMING_STATUS_BYTE cpu_to_le32(0xC01E0586) -#define STATUS_GRAPHICS_DDCCI_INVALID_CAPABILITIES_STRING cpu_to_le32(0xC01E0587) -#define STATUS_GRAPHICS_MCA_INTERNAL_ERROR cpu_to_le32(0xC01E0588) -#define STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_COMMAND cpu_to_le32(0xC01E0589) -#define STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_LENGTH cpu_to_le32(0xC01E058A) -#define STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_CHECKSUM cpu_to_le32(0xC01E058B) -#define STATUS_GRAPHICS_INVALID_PHYSICAL_MONITOR_HANDLE cpu_to_le32(0xC01E058C) -#define STATUS_GRAPHICS_MONITOR_NO_LONGER_EXISTS cpu_to_le32(0xC01E058D) -#define STATUS_GRAPHICS_ONLY_CONSOLE_SESSION_SUPPORTED cpu_to_le32(0xC01E05E0) -#define STATUS_GRAPHICS_NO_DISPLAY_DEVICE_CORRESPONDS_TO_NAME cpu_to_le32(0xC01E05E1) -#define STATUS_GRAPHICS_DISPLAY_DEVICE_NOT_ATTACHED_TO_DESKTOP cpu_to_le32(0xC01E05E2) -#define STATUS_GRAPHICS_MIRRORING_DEVICES_NOT_SUPPORTED cpu_to_le32(0xC01E05E3) -#define STATUS_GRAPHICS_INVALID_POINTER cpu_to_le32(0xC01E05E4) -#define STATUS_GRAPHICS_NO_MONITORS_CORRESPOND_TO_DISPLAY_DEVICE cpu_to_le32(0xC01E05E5) -#define STATUS_GRAPHICS_PARAMETER_ARRAY_TOO_SMALL cpu_to_le32(0xC01E05E6) -#define STATUS_GRAPHICS_INTERNAL_ERROR cpu_to_le32(0xC01E05E7) -#define STATUS_GRAPHICS_SESSION_TYPE_CHANGE_IN_PROGRESS cpu_to_le32(0xC01E05E8) -#define STATUS_FVE_LOCKED_VOLUME cpu_to_le32(0xC0210000) -#define STATUS_FVE_NOT_ENCRYPTED cpu_to_le32(0xC0210001) -#define STATUS_FVE_BAD_INFORMATION cpu_to_le32(0xC0210002) -#define STATUS_FVE_TOO_SMALL cpu_to_le32(0xC0210003) -#define STATUS_FVE_FAILED_WRONG_FS cpu_to_le32(0xC0210004) -#define STATUS_FVE_FAILED_BAD_FS cpu_to_le32(0xC0210005) -#define STATUS_FVE_FS_NOT_EXTENDED cpu_to_le32(0xC0210006) -#define STATUS_FVE_FS_MOUNTED cpu_to_le32(0xC0210007) -#define STATUS_FVE_NO_LICENSE cpu_to_le32(0xC0210008) -#define STATUS_FVE_ACTION_NOT_ALLOWED cpu_to_le32(0xC0210009) -#define STATUS_FVE_BAD_DATA cpu_to_le32(0xC021000A) -#define STATUS_FVE_VOLUME_NOT_BOUND cpu_to_le32(0xC021000B) -#define STATUS_FVE_NOT_DATA_VOLUME cpu_to_le32(0xC021000C) -#define STATUS_FVE_CONV_READ_ERROR cpu_to_le32(0xC021000D) -#define STATUS_FVE_CONV_WRITE_ERROR cpu_to_le32(0xC021000E) -#define STATUS_FVE_OVERLAPPED_UPDATE cpu_to_le32(0xC021000F) -#define STATUS_FVE_FAILED_SECTOR_SIZE cpu_to_le32(0xC0210010) -#define STATUS_FVE_FAILED_AUTHENTICATION cpu_to_le32(0xC0210011) -#define STATUS_FVE_NOT_OS_VOLUME cpu_to_le32(0xC0210012) -#define STATUS_FVE_KEYFILE_NOT_FOUND cpu_to_le32(0xC0210013) -#define STATUS_FVE_KEYFILE_INVALID cpu_to_le32(0xC0210014) -#define STATUS_FVE_KEYFILE_NO_VMK cpu_to_le32(0xC0210015) -#define STATUS_FVE_TPM_DISABLED cpu_to_le32(0xC0210016) -#define STATUS_FVE_TPM_SRK_AUTH_NOT_ZERO cpu_to_le32(0xC0210017) -#define STATUS_FVE_TPM_INVALID_PCR cpu_to_le32(0xC0210018) -#define STATUS_FVE_TPM_NO_VMK cpu_to_le32(0xC0210019) -#define STATUS_FVE_PIN_INVALID cpu_to_le32(0xC021001A) -#define STATUS_FVE_AUTH_INVALID_APPLICATION cpu_to_le32(0xC021001B) -#define STATUS_FVE_AUTH_INVALID_CONFIG cpu_to_le32(0xC021001C) -#define STATUS_FVE_DEBUGGER_ENABLED cpu_to_le32(0xC021001D) -#define STATUS_FVE_DRY_RUN_FAILED cpu_to_le32(0xC021001E) -#define STATUS_FVE_BAD_METADATA_POINTER cpu_to_le32(0xC021001F) -#define STATUS_FVE_OLD_METADATA_COPY cpu_to_le32(0xC0210020) -#define STATUS_FVE_REBOOT_REQUIRED cpu_to_le32(0xC0210021) -#define STATUS_FVE_RAW_ACCESS cpu_to_le32(0xC0210022) -#define STATUS_FVE_RAW_BLOCKED cpu_to_le32(0xC0210023) -#define STATUS_FWP_CALLOUT_NOT_FOUND cpu_to_le32(0xC0220001) -#define STATUS_FWP_CONDITION_NOT_FOUND cpu_to_le32(0xC0220002) -#define STATUS_FWP_FILTER_NOT_FOUND cpu_to_le32(0xC0220003) -#define STATUS_FWP_LAYER_NOT_FOUND cpu_to_le32(0xC0220004) -#define STATUS_FWP_PROVIDER_NOT_FOUND cpu_to_le32(0xC0220005) -#define STATUS_FWP_PROVIDER_CONTEXT_NOT_FOUND cpu_to_le32(0xC0220006) -#define STATUS_FWP_SUBLAYER_NOT_FOUND cpu_to_le32(0xC0220007) -#define STATUS_FWP_NOT_FOUND cpu_to_le32(0xC0220008) -#define STATUS_FWP_ALREADY_EXISTS cpu_to_le32(0xC0220009) -#define STATUS_FWP_IN_USE cpu_to_le32(0xC022000A) -#define STATUS_FWP_DYNAMIC_SESSION_IN_PROGRESS cpu_to_le32(0xC022000B) -#define STATUS_FWP_WRONG_SESSION cpu_to_le32(0xC022000C) -#define STATUS_FWP_NO_TXN_IN_PROGRESS cpu_to_le32(0xC022000D) -#define STATUS_FWP_TXN_IN_PROGRESS cpu_to_le32(0xC022000E) -#define STATUS_FWP_TXN_ABORTED cpu_to_le32(0xC022000F) -#define STATUS_FWP_SESSION_ABORTED cpu_to_le32(0xC0220010) -#define STATUS_FWP_INCOMPATIBLE_TXN cpu_to_le32(0xC0220011) -#define STATUS_FWP_TIMEOUT cpu_to_le32(0xC0220012) -#define STATUS_FWP_NET_EVENTS_DISABLED cpu_to_le32(0xC0220013) -#define STATUS_FWP_INCOMPATIBLE_LAYER cpu_to_le32(0xC0220014) -#define STATUS_FWP_KM_CLIENTS_ONLY cpu_to_le32(0xC0220015) -#define STATUS_FWP_LIFETIME_MISMATCH cpu_to_le32(0xC0220016) -#define STATUS_FWP_BUILTIN_OBJECT cpu_to_le32(0xC0220017) -#define STATUS_FWP_TOO_MANY_BOOTTIME_FILTERS cpu_to_le32(0xC0220018) -#define STATUS_FWP_TOO_MANY_CALLOUTS cpu_to_le32(0xC0220018) -#define STATUS_FWP_NOTIFICATION_DROPPED cpu_to_le32(0xC0220019) -#define STATUS_FWP_TRAFFIC_MISMATCH cpu_to_le32(0xC022001A) -#define STATUS_FWP_INCOMPATIBLE_SA_STATE cpu_to_le32(0xC022001B) -#define STATUS_FWP_NULL_POINTER cpu_to_le32(0xC022001C) -#define STATUS_FWP_INVALID_ENUMERATOR cpu_to_le32(0xC022001D) -#define STATUS_FWP_INVALID_FLAGS cpu_to_le32(0xC022001E) -#define STATUS_FWP_INVALID_NET_MASK cpu_to_le32(0xC022001F) -#define STATUS_FWP_INVALID_RANGE cpu_to_le32(0xC0220020) -#define STATUS_FWP_INVALID_INTERVAL cpu_to_le32(0xC0220021) -#define STATUS_FWP_ZERO_LENGTH_ARRAY cpu_to_le32(0xC0220022) -#define STATUS_FWP_NULL_DISPLAY_NAME cpu_to_le32(0xC0220023) -#define STATUS_FWP_INVALID_ACTION_TYPE cpu_to_le32(0xC0220024) -#define STATUS_FWP_INVALID_WEIGHT cpu_to_le32(0xC0220025) -#define STATUS_FWP_MATCH_TYPE_MISMATCH cpu_to_le32(0xC0220026) -#define STATUS_FWP_TYPE_MISMATCH cpu_to_le32(0xC0220027) -#define STATUS_FWP_OUT_OF_BOUNDS cpu_to_le32(0xC0220028) -#define STATUS_FWP_RESERVED cpu_to_le32(0xC0220029) -#define STATUS_FWP_DUPLICATE_CONDITION cpu_to_le32(0xC022002A) -#define STATUS_FWP_DUPLICATE_KEYMOD cpu_to_le32(0xC022002B) -#define STATUS_FWP_ACTION_INCOMPATIBLE_WITH_LAYER cpu_to_le32(0xC022002C) -#define STATUS_FWP_ACTION_INCOMPATIBLE_WITH_SUBLAYER cpu_to_le32(0xC022002D) -#define STATUS_FWP_CONTEXT_INCOMPATIBLE_WITH_LAYER cpu_to_le32(0xC022002E) -#define STATUS_FWP_CONTEXT_INCOMPATIBLE_WITH_CALLOUT cpu_to_le32(0xC022002F) -#define STATUS_FWP_INCOMPATIBLE_AUTH_METHOD cpu_to_le32(0xC0220030) -#define STATUS_FWP_INCOMPATIBLE_DH_GROUP cpu_to_le32(0xC0220031) -#define STATUS_FWP_EM_NOT_SUPPORTED cpu_to_le32(0xC0220032) -#define STATUS_FWP_NEVER_MATCH cpu_to_le32(0xC0220033) -#define STATUS_FWP_PROVIDER_CONTEXT_MISMATCH cpu_to_le32(0xC0220034) -#define STATUS_FWP_INVALID_PARAMETER cpu_to_le32(0xC0220035) -#define STATUS_FWP_TOO_MANY_SUBLAYERS cpu_to_le32(0xC0220036) -#define STATUS_FWP_CALLOUT_NOTIFICATION_FAILED cpu_to_le32(0xC0220037) -#define STATUS_FWP_INCOMPATIBLE_AUTH_CONFIG cpu_to_le32(0xC0220038) -#define STATUS_FWP_INCOMPATIBLE_CIPHER_CONFIG cpu_to_le32(0xC0220039) -#define STATUS_FWP_TCPIP_NOT_READY cpu_to_le32(0xC0220100) -#define STATUS_FWP_INJECT_HANDLE_CLOSING cpu_to_le32(0xC0220101) -#define STATUS_FWP_INJECT_HANDLE_STALE cpu_to_le32(0xC0220102) -#define STATUS_FWP_CANNOT_PEND cpu_to_le32(0xC0220103) -#define STATUS_NDIS_CLOSING cpu_to_le32(0xC0230002) -#define STATUS_NDIS_BAD_VERSION cpu_to_le32(0xC0230004) -#define STATUS_NDIS_BAD_CHARACTERISTICS cpu_to_le32(0xC0230005) -#define STATUS_NDIS_ADAPTER_NOT_FOUND cpu_to_le32(0xC0230006) -#define STATUS_NDIS_OPEN_FAILED cpu_to_le32(0xC0230007) -#define STATUS_NDIS_DEVICE_FAILED cpu_to_le32(0xC0230008) -#define STATUS_NDIS_MULTICAST_FULL cpu_to_le32(0xC0230009) -#define STATUS_NDIS_MULTICAST_EXISTS cpu_to_le32(0xC023000A) -#define STATUS_NDIS_MULTICAST_NOT_FOUND cpu_to_le32(0xC023000B) -#define STATUS_NDIS_REQUEST_ABORTED cpu_to_le32(0xC023000C) -#define STATUS_NDIS_RESET_IN_PROGRESS cpu_to_le32(0xC023000D) -#define STATUS_NDIS_INVALID_PACKET cpu_to_le32(0xC023000F) -#define STATUS_NDIS_INVALID_DEVICE_REQUEST cpu_to_le32(0xC0230010) -#define STATUS_NDIS_ADAPTER_NOT_READY cpu_to_le32(0xC0230011) -#define STATUS_NDIS_INVALID_LENGTH cpu_to_le32(0xC0230014) -#define STATUS_NDIS_INVALID_DATA cpu_to_le32(0xC0230015) -#define STATUS_NDIS_BUFFER_TOO_SHORT cpu_to_le32(0xC0230016) -#define STATUS_NDIS_INVALID_OID cpu_to_le32(0xC0230017) -#define STATUS_NDIS_ADAPTER_REMOVED cpu_to_le32(0xC0230018) -#define STATUS_NDIS_UNSUPPORTED_MEDIA cpu_to_le32(0xC0230019) -#define STATUS_NDIS_GROUP_ADDRESS_IN_USE cpu_to_le32(0xC023001A) -#define STATUS_NDIS_FILE_NOT_FOUND cpu_to_le32(0xC023001B) -#define STATUS_NDIS_ERROR_READING_FILE cpu_to_le32(0xC023001C) -#define STATUS_NDIS_ALREADY_MAPPED cpu_to_le32(0xC023001D) -#define STATUS_NDIS_RESOURCE_CONFLICT cpu_to_le32(0xC023001E) -#define STATUS_NDIS_MEDIA_DISCONNECTED cpu_to_le32(0xC023001F) -#define STATUS_NDIS_INVALID_ADDRESS cpu_to_le32(0xC0230022) -#define STATUS_NDIS_PAUSED cpu_to_le32(0xC023002A) -#define STATUS_NDIS_INTERFACE_NOT_FOUND cpu_to_le32(0xC023002B) -#define STATUS_NDIS_UNSUPPORTED_REVISION cpu_to_le32(0xC023002C) -#define STATUS_NDIS_INVALID_PORT cpu_to_le32(0xC023002D) -#define STATUS_NDIS_INVALID_PORT_STATE cpu_to_le32(0xC023002E) -#define STATUS_NDIS_LOW_POWER_STATE cpu_to_le32(0xC023002F) -#define STATUS_NDIS_NOT_SUPPORTED cpu_to_le32(0xC02300BB) -#define STATUS_NDIS_DOT11_AUTO_CONFIG_ENABLED cpu_to_le32(0xC0232000) -#define STATUS_NDIS_DOT11_MEDIA_IN_USE cpu_to_le32(0xC0232001) -#define STATUS_NDIS_DOT11_POWER_STATE_INVALID cpu_to_le32(0xC0232002) -#define STATUS_IPSEC_BAD_SPI cpu_to_le32(0xC0360001) -#define STATUS_IPSEC_SA_LIFETIME_EXPIRED cpu_to_le32(0xC0360002) -#define STATUS_IPSEC_WRONG_SA cpu_to_le32(0xC0360003) -#define STATUS_IPSEC_REPLAY_CHECK_FAILED cpu_to_le32(0xC0360004) -#define STATUS_IPSEC_INVALID_PACKET cpu_to_le32(0xC0360005) -#define STATUS_IPSEC_INTEGRITY_CHECK_FAILED cpu_to_le32(0xC0360006) -#define STATUS_IPSEC_CLEAR_TEXT_DROP cpu_to_le32(0xC0360007) +#define STATUS_MCA_OCCURED cpu_to_le32(0xC000036A) // -EIO +#define STATUS_DRIVER_BLOCKED_CRITICAL cpu_to_le32(0xC000036B) // -EIO +#define STATUS_DRIVER_BLOCKED cpu_to_le32(0xC000036C) // -EIO +#define STATUS_DRIVER_DATABASE_ERROR cpu_to_le32(0xC000036D) // -EIO +#define STATUS_SYSTEM_HIVE_TOO_LARGE cpu_to_le32(0xC000036E) // -EIO +#define STATUS_INVALID_IMPORT_OF_NON_DLL cpu_to_le32(0xC000036F) // -EIO +#define STATUS_NO_SECRETS cpu_to_le32(0xC0000371) // -EIO +#define STATUS_ACCESS_DISABLED_NO_SAFER_UI_BY_POLICY cpu_to_le32(0xC0000372) // -EACCES +#define STATUS_FAILED_STACK_SWITCH cpu_to_le32(0xC0000373) // -EIO +#define STATUS_HEAP_CORRUPTION cpu_to_le32(0xC0000374) // -EIO +#define STATUS_SMARTCARD_WRONG_PIN cpu_to_le32(0xC0000380) // -EIO +#define STATUS_SMARTCARD_CARD_BLOCKED cpu_to_le32(0xC0000381) // -EIO +#define STATUS_SMARTCARD_CARD_NOT_AUTHENTICATED cpu_to_le32(0xC0000382) // -EIO +#define STATUS_SMARTCARD_NO_CARD cpu_to_le32(0xC0000383) // -EIO +#define STATUS_SMARTCARD_NO_KEY_CONTAINER cpu_to_le32(0xC0000384) // -EIO +#define STATUS_SMARTCARD_NO_CERTIFICATE cpu_to_le32(0xC0000385) // -EIO +#define STATUS_SMARTCARD_NO_KEYSET cpu_to_le32(0xC0000386) // -EIO +#define STATUS_SMARTCARD_IO_ERROR cpu_to_le32(0xC0000387) // -EIO +#define STATUS_DOWNGRADE_DETECTED cpu_to_le32(0xC0000388) // -EIO +#define STATUS_SMARTCARD_CERT_REVOKED cpu_to_le32(0xC0000389) // -EIO +#define STATUS_ISSUING_CA_UNTRUSTED cpu_to_le32(0xC000038A) // -EIO +#define STATUS_REVOCATION_OFFLINE_C cpu_to_le32(0xC000038B) // -EIO +#define STATUS_PKINIT_CLIENT_FAILURE cpu_to_le32(0xC000038C) // -EIO +#define STATUS_SMARTCARD_CERT_EXPIRED cpu_to_le32(0xC000038D) // -EIO +#define STATUS_DRIVER_FAILED_PRIOR_UNLOAD cpu_to_le32(0xC000038E) // -EIO +#define STATUS_SMARTCARD_SILENT_CONTEXT cpu_to_le32(0xC000038F) // -EIO +#define STATUS_PER_USER_TRUST_QUOTA_EXCEEDED cpu_to_le32(0xC0000401) // -EDQUOT +#define STATUS_ALL_USER_TRUST_QUOTA_EXCEEDED cpu_to_le32(0xC0000402) // -EDQUOT +#define STATUS_USER_DELETE_TRUST_QUOTA_EXCEEDED cpu_to_le32(0xC0000403) // -EDQUOT +#define STATUS_DS_NAME_NOT_UNIQUE cpu_to_le32(0xC0000404) // -EIO +#define STATUS_DS_DUPLICATE_ID_FOUND cpu_to_le32(0xC0000405) // -EIO +#define STATUS_DS_GROUP_CONVERSION_ERROR cpu_to_le32(0xC0000406) // -EIO +#define STATUS_VOLSNAP_PREPARE_HIBERNATE cpu_to_le32(0xC0000407) // -EIO +#define STATUS_USER2USER_REQUIRED cpu_to_le32(0xC0000408) // -EIO +#define STATUS_STACK_BUFFER_OVERRUN cpu_to_le32(0xC0000409) // -EIO +#define STATUS_NO_S4U_PROT_SUPPORT cpu_to_le32(0xC000040A) // -EIO +#define STATUS_CROSSREALM_DELEGATION_FAILURE cpu_to_le32(0xC000040B) // -EIO +#define STATUS_REVOCATION_OFFLINE_KDC cpu_to_le32(0xC000040C) // -EIO +#define STATUS_ISSUING_CA_UNTRUSTED_KDC cpu_to_le32(0xC000040D) // -EIO +#define STATUS_KDC_CERT_EXPIRED cpu_to_le32(0xC000040E) // -EIO +#define STATUS_KDC_CERT_REVOKED cpu_to_le32(0xC000040F) // -EIO +#define STATUS_PARAMETER_QUOTA_EXCEEDED cpu_to_le32(0xC0000410) // -EDQUOT +#define STATUS_HIBERNATION_FAILURE cpu_to_le32(0xC0000411) // -EIO +#define STATUS_DELAY_LOAD_FAILED cpu_to_le32(0xC0000412) // -EIO +#define STATUS_AUTHENTICATION_FIREWALL_FAILED cpu_to_le32(0xC0000413) // -EIO +#define STATUS_VDM_DISALLOWED cpu_to_le32(0xC0000414) // -EIO +#define STATUS_HUNG_DISPLAY_DRIVER_THREAD cpu_to_le32(0xC0000415) // -EIO +#define STATUS_INSUFFICIENT_RESOURCE_FOR_SPECIFIED_SHARED_SECTION_SIZE cpu_to_le32(0xC0000416) // -EIO +#define STATUS_INVALID_CRUNTIME_PARAMETER cpu_to_le32(0xC0000417) // -EIO +#define STATUS_NTLM_BLOCKED cpu_to_le32(0xC0000418) // -EIO +#define STATUS_ASSERTION_FAILURE cpu_to_le32(0xC0000420) // -EIO +#define STATUS_VERIFIER_STOP cpu_to_le32(0xC0000421) // -EIO +#define STATUS_CALLBACK_POP_STACK cpu_to_le32(0xC0000423) // -EIO +#define STATUS_INCOMPATIBLE_DRIVER_BLOCKED cpu_to_le32(0xC0000424) // -EIO +#define STATUS_HIVE_UNLOADED cpu_to_le32(0xC0000425) // -EIO +#define STATUS_COMPRESSION_DISABLED cpu_to_le32(0xC0000426) // -EIO +#define STATUS_FILE_SYSTEM_LIMITATION cpu_to_le32(0xC0000427) // -EIO +#define STATUS_INVALID_IMAGE_HASH cpu_to_le32(0xC0000428) // -EIO +#define STATUS_NOT_CAPABLE cpu_to_le32(0xC0000429) // -EIO +#define STATUS_REQUEST_OUT_OF_SEQUENCE cpu_to_le32(0xC000042A) // -EIO +#define STATUS_IMPLEMENTATION_LIMIT cpu_to_le32(0xC000042B) // -EIO +#define STATUS_ELEVATION_REQUIRED cpu_to_le32(0xC000042C) // -EIO +#define STATUS_BEYOND_VDL cpu_to_le32(0xC0000432) // -EIO +#define STATUS_ENCOUNTERED_WRITE_IN_PROGRESS cpu_to_le32(0xC0000433) // -EIO +#define STATUS_PTE_CHANGED cpu_to_le32(0xC0000434) // -EIO +#define STATUS_PURGE_FAILED cpu_to_le32(0xC0000435) // -EIO +#define STATUS_CRED_REQUIRES_CONFIRMATION cpu_to_le32(0xC0000440) // -EIO +#define STATUS_CS_ENCRYPTION_INVALID_SERVER_RESPONSE cpu_to_le32(0xC0000441) // -EIO +#define STATUS_CS_ENCRYPTION_UNSUPPORTED_SERVER cpu_to_le32(0xC0000442) // -EIO +#define STATUS_CS_ENCRYPTION_EXISTING_ENCRYPTED_FILE cpu_to_le32(0xC0000443) // -EIO +#define STATUS_CS_ENCRYPTION_NEW_ENCRYPTED_FILE cpu_to_le32(0xC0000444) // -EIO +#define STATUS_CS_ENCRYPTION_FILE_NOT_CSE cpu_to_le32(0xC0000445) // -EIO +#define STATUS_INVALID_LABEL cpu_to_le32(0xC0000446) // -EIO +#define STATUS_DRIVER_PROCESS_TERMINATED cpu_to_le32(0xC0000450) // -EIO +#define STATUS_AMBIGUOUS_SYSTEM_DEVICE cpu_to_le32(0xC0000451) // -EIO +#define STATUS_SYSTEM_DEVICE_NOT_FOUND cpu_to_le32(0xC0000452) // -EIO +#define STATUS_RESTART_BOOT_APPLICATION cpu_to_le32(0xC0000453) // -EIO +#define STATUS_INVALID_TASK_NAME cpu_to_le32(0xC0000500) // -EIO +#define STATUS_INVALID_TASK_INDEX cpu_to_le32(0xC0000501) // -EIO +#define STATUS_THREAD_ALREADY_IN_TASK cpu_to_le32(0xC0000502) // -EIO +#define STATUS_CALLBACK_BYPASS cpu_to_le32(0xC0000503) // -EIO +#define STATUS_SERVER_UNAVAILABLE cpu_to_le32(0xC0000466) // -EAGAIN +#define STATUS_FILE_NOT_AVAILABLE cpu_to_le32(0xC0000467) // -EAGAIN +#define STATUS_PORT_CLOSED cpu_to_le32(0xC0000700) // -EIO +#define STATUS_MESSAGE_LOST cpu_to_le32(0xC0000701) // -EIO +#define STATUS_INVALID_MESSAGE cpu_to_le32(0xC0000702) // -EIO +#define STATUS_REQUEST_CANCELED cpu_to_le32(0xC0000703) // -EIO +#define STATUS_RECURSIVE_DISPATCH cpu_to_le32(0xC0000704) // -EIO +#define STATUS_LPC_RECEIVE_BUFFER_EXPECTED cpu_to_le32(0xC0000705) // -EIO +#define STATUS_LPC_INVALID_CONNECTION_USAGE cpu_to_le32(0xC0000706) // -EIO +#define STATUS_LPC_REQUESTS_NOT_ALLOWED cpu_to_le32(0xC0000707) // -EIO +#define STATUS_RESOURCE_IN_USE cpu_to_le32(0xC0000708) // -EIO +#define STATUS_HARDWARE_MEMORY_ERROR cpu_to_le32(0xC0000709) // -EIO +#define STATUS_THREADPOOL_HANDLE_EXCEPTION cpu_to_le32(0xC000070A) // -EIO +#define STATUS_THREADPOOL_SET_EVENT_ON_COMPLETION_FAILED cpu_to_le32(0xC000070B) // -EIO +#define STATUS_THREADPOOL_RELEASE_SEMAPHORE_ON_COMPLETION_FAILED cpu_to_le32(0xC000070C) // -EIO +#define STATUS_THREADPOOL_RELEASE_MUTEX_ON_COMPLETION_FAILED cpu_to_le32(0xC000070D) // -EIO +#define STATUS_THREADPOOL_FREE_LIBRARY_ON_COMPLETION_FAILED cpu_to_le32(0xC000070E) // -EIO +#define STATUS_THREADPOOL_RELEASED_DURING_OPERATION cpu_to_le32(0xC000070F) // -EIO +#define STATUS_CALLBACK_RETURNED_WHILE_IMPERSONATING cpu_to_le32(0xC0000710) // -EIO +#define STATUS_APC_RETURNED_WHILE_IMPERSONATING cpu_to_le32(0xC0000711) // -EIO +#define STATUS_PROCESS_IS_PROTECTED cpu_to_le32(0xC0000712) // -EIO +#define STATUS_MCA_EXCEPTION cpu_to_le32(0xC0000713) // -EIO +#define STATUS_CERTIFICATE_MAPPING_NOT_UNIQUE cpu_to_le32(0xC0000714) // -EIO +#define STATUS_SYMLINK_CLASS_DISABLED cpu_to_le32(0xC0000715) // -EIO +#define STATUS_INVALID_IDN_NORMALIZATION cpu_to_le32(0xC0000716) // -EIO +#define STATUS_NO_UNICODE_TRANSLATION cpu_to_le32(0xC0000717) // -EIO +#define STATUS_ALREADY_REGISTERED cpu_to_le32(0xC0000718) // -EIO +#define STATUS_CONTEXT_MISMATCH cpu_to_le32(0xC0000719) // -EIO +#define STATUS_PORT_ALREADY_HAS_COMPLETION_LIST cpu_to_le32(0xC000071A) // -EIO +#define STATUS_CALLBACK_RETURNED_THREAD_PRIORITY cpu_to_le32(0xC000071B) // -EIO +#define STATUS_INVALID_THREAD cpu_to_le32(0xC000071C) // -EIO +#define STATUS_CALLBACK_RETURNED_TRANSACTION cpu_to_le32(0xC000071D) // -EIO +#define STATUS_CALLBACK_RETURNED_LDR_LOCK cpu_to_le32(0xC000071E) // -EIO +#define STATUS_CALLBACK_RETURNED_LANG cpu_to_le32(0xC000071F) // -EIO +#define STATUS_CALLBACK_RETURNED_PRI_BACK cpu_to_le32(0xC0000720) // -EIO +#define STATUS_CALLBACK_RETURNED_THREAD_AFFINITY cpu_to_le32(0xC0000721) // -EIO +#define STATUS_DISK_REPAIR_DISABLED cpu_to_le32(0xC0000800) // -EIO +#define STATUS_DS_DOMAIN_RENAME_IN_PROGRESS cpu_to_le32(0xC0000801) // -EIO +#define STATUS_DISK_QUOTA_EXCEEDED cpu_to_le32(0xC0000802) // -EDQUOT +#define STATUS_CONTENT_BLOCKED cpu_to_le32(0xC0000804) // -EIO +#define STATUS_BAD_CLUSTERS cpu_to_le32(0xC0000805) // -EIO +#define STATUS_VOLUME_DIRTY cpu_to_le32(0xC0000806) // -EIO +#define STATUS_FILE_CHECKED_OUT cpu_to_le32(0xC0000901) // -EIO +#define STATUS_CHECKOUT_REQUIRED cpu_to_le32(0xC0000902) // -EIO +#define STATUS_BAD_FILE_TYPE cpu_to_le32(0xC0000903) // -EIO +#define STATUS_FILE_TOO_LARGE cpu_to_le32(0xC0000904) // -EIO +#define STATUS_FORMS_AUTH_REQUIRED cpu_to_le32(0xC0000905) // -EIO +#define STATUS_VIRUS_INFECTED cpu_to_le32(0xC0000906) // -EIO +#define STATUS_VIRUS_DELETED cpu_to_le32(0xC0000907) // -EIO +#define STATUS_BAD_MCFG_TABLE cpu_to_le32(0xC0000908) // -EIO +#define STATUS_WOW_ASSERTION cpu_to_le32(0xC0009898) // -EIO +#define STATUS_INVALID_SIGNATURE cpu_to_le32(0xC000A000) // -EIO +#define STATUS_HMAC_NOT_SUPPORTED cpu_to_le32(0xC000A001) // -EIO +#define STATUS_IPSEC_QUEUE_OVERFLOW cpu_to_le32(0xC000A010) // -EIO +#define STATUS_ND_QUEUE_OVERFLOW cpu_to_le32(0xC000A011) // -EIO +#define STATUS_HOPLIMIT_EXCEEDED cpu_to_le32(0xC000A012) // -EIO +#define STATUS_PROTOCOL_NOT_SUPPORTED cpu_to_le32(0xC000A013) // -EOPNOTSUPP +#define STATUS_LOST_WRITEBEHIND_DATA_NETWORK_DISCONNECTED cpu_to_le32(0xC000A080) // -EIO +#define STATUS_LOST_WRITEBEHIND_DATA_NETWORK_SERVER_ERROR cpu_to_le32(0xC000A081) // -EIO +#define STATUS_LOST_WRITEBEHIND_DATA_LOCAL_DISK_ERROR cpu_to_le32(0xC000A082) // -EIO +#define STATUS_XML_PARSE_ERROR cpu_to_le32(0xC000A083) // -EIO +#define STATUS_XMLDSIG_ERROR cpu_to_le32(0xC000A084) // -EIO +#define STATUS_WRONG_COMPARTMENT cpu_to_le32(0xC000A085) // -EIO +#define STATUS_AUTHIP_FAILURE cpu_to_le32(0xC000A086) // -EIO +#define DBG_NO_STATE_CHANGE cpu_to_le32(0xC0010001) // -EIO +#define DBG_APP_NOT_IDLE cpu_to_le32(0xC0010002) // -EIO +#define RPC_NT_INVALID_STRING_BINDING cpu_to_le32(0xC0020001) // -EIO +#define RPC_NT_WRONG_KIND_OF_BINDING cpu_to_le32(0xC0020002) // -EIO +#define RPC_NT_INVALID_BINDING cpu_to_le32(0xC0020003) // -EIO +#define RPC_NT_PROTSEQ_NOT_SUPPORTED cpu_to_le32(0xC0020004) // -EOPNOTSUPP +#define RPC_NT_INVALID_RPC_PROTSEQ cpu_to_le32(0xC0020005) // -EIO +#define RPC_NT_INVALID_STRING_UUID cpu_to_le32(0xC0020006) // -EIO +#define RPC_NT_INVALID_ENDPOINT_FORMAT cpu_to_le32(0xC0020007) // -EIO +#define RPC_NT_INVALID_NET_ADDR cpu_to_le32(0xC0020008) // -EIO +#define RPC_NT_NO_ENDPOINT_FOUND cpu_to_le32(0xC0020009) // -EIO +#define RPC_NT_INVALID_TIMEOUT cpu_to_le32(0xC002000A) // -EINVAL +#define RPC_NT_OBJECT_NOT_FOUND cpu_to_le32(0xC002000B) // -ENOENT +#define RPC_NT_ALREADY_REGISTERED cpu_to_le32(0xC002000C) // -EIO +#define RPC_NT_TYPE_ALREADY_REGISTERED cpu_to_le32(0xC002000D) // -EIO +#define RPC_NT_ALREADY_LISTENING cpu_to_le32(0xC002000E) // -EIO +#define RPC_NT_NO_PROTSEQS_REGISTERED cpu_to_le32(0xC002000F) // -EIO +#define RPC_NT_NOT_LISTENING cpu_to_le32(0xC0020010) // -EIO +#define RPC_NT_UNKNOWN_MGR_TYPE cpu_to_le32(0xC0020011) // -EIO +#define RPC_NT_UNKNOWN_IF cpu_to_le32(0xC0020012) // -EIO +#define RPC_NT_NO_BINDINGS cpu_to_le32(0xC0020013) // -EIO +#define RPC_NT_NO_PROTSEQS cpu_to_le32(0xC0020014) // -EIO +#define RPC_NT_CANT_CREATE_ENDPOINT cpu_to_le32(0xC0020015) // -EIO +#define RPC_NT_OUT_OF_RESOURCES cpu_to_le32(0xC0020016) // -EIO +#define RPC_NT_SERVER_UNAVAILABLE cpu_to_le32(0xC0020017) // -EIO +#define RPC_NT_SERVER_TOO_BUSY cpu_to_le32(0xC0020018) // -EBUSY +#define RPC_NT_INVALID_NETWORK_OPTIONS cpu_to_le32(0xC0020019) // -EIO +#define RPC_NT_NO_CALL_ACTIVE cpu_to_le32(0xC002001A) // -EIO +#define RPC_NT_CALL_FAILED cpu_to_le32(0xC002001B) // -EIO +#define RPC_NT_CALL_FAILED_DNE cpu_to_le32(0xC002001C) // -EIO +#define RPC_NT_PROTOCOL_ERROR cpu_to_le32(0xC002001D) // -EIO +#define RPC_NT_UNSUPPORTED_TRANS_SYN cpu_to_le32(0xC002001F) // -EIO +#define RPC_NT_UNSUPPORTED_TYPE cpu_to_le32(0xC0020021) // -EIO +#define RPC_NT_INVALID_TAG cpu_to_le32(0xC0020022) // -EIO +#define RPC_NT_INVALID_BOUND cpu_to_le32(0xC0020023) // -EIO +#define RPC_NT_NO_ENTRY_NAME cpu_to_le32(0xC0020024) // -EIO +#define RPC_NT_INVALID_NAME_SYNTAX cpu_to_le32(0xC0020025) // -EIO +#define RPC_NT_UNSUPPORTED_NAME_SYNTAX cpu_to_le32(0xC0020026) // -EIO +#define RPC_NT_UUID_NO_ADDRESS cpu_to_le32(0xC0020028) // -EIO +#define RPC_NT_DUPLICATE_ENDPOINT cpu_to_le32(0xC0020029) // -ENOTUNIQ +#define RPC_NT_UNKNOWN_AUTHN_TYPE cpu_to_le32(0xC002002A) // -EIO +#define RPC_NT_MAX_CALLS_TOO_SMALL cpu_to_le32(0xC002002B) // -EIO +#define RPC_NT_STRING_TOO_LONG cpu_to_le32(0xC002002C) // -EIO +#define RPC_NT_PROTSEQ_NOT_FOUND cpu_to_le32(0xC002002D) // -EIO +#define RPC_NT_PROCNUM_OUT_OF_RANGE cpu_to_le32(0xC002002E) // -EIO +#define RPC_NT_BINDING_HAS_NO_AUTH cpu_to_le32(0xC002002F) // -EIO +#define RPC_NT_UNKNOWN_AUTHN_SERVICE cpu_to_le32(0xC0020030) // -EIO +#define RPC_NT_UNKNOWN_AUTHN_LEVEL cpu_to_le32(0xC0020031) // -EIO +#define RPC_NT_INVALID_AUTH_IDENTITY cpu_to_le32(0xC0020032) // -EIO +#define RPC_NT_UNKNOWN_AUTHZ_SERVICE cpu_to_le32(0xC0020033) // -EIO +#define EPT_NT_INVALID_ENTRY cpu_to_le32(0xC0020034) // -EIO +#define EPT_NT_CANT_PERFORM_OP cpu_to_le32(0xC0020035) // -EIO +#define EPT_NT_NOT_REGISTERED cpu_to_le32(0xC0020036) // -EIO +#define RPC_NT_NOTHING_TO_EXPORT cpu_to_le32(0xC0020037) // -EIO +#define RPC_NT_INCOMPLETE_NAME cpu_to_le32(0xC0020038) // -EIO +#define RPC_NT_INVALID_VERS_OPTION cpu_to_le32(0xC0020039) // -EIO +#define RPC_NT_NO_MORE_MEMBERS cpu_to_le32(0xC002003A) // -EIO +#define RPC_NT_NOT_ALL_OBJS_UNEXPORTED cpu_to_le32(0xC002003B) // -EIO +#define RPC_NT_INTERFACE_NOT_FOUND cpu_to_le32(0xC002003C) // -EIO +#define RPC_NT_ENTRY_ALREADY_EXISTS cpu_to_le32(0xC002003D) // -EIO +#define RPC_NT_ENTRY_NOT_FOUND cpu_to_le32(0xC002003E) // -EIO +#define RPC_NT_NAME_SERVICE_UNAVAILABLE cpu_to_le32(0xC002003F) // -EIO +#define RPC_NT_INVALID_NAF_ID cpu_to_le32(0xC0020040) // -EIO +#define RPC_NT_CANNOT_SUPPORT cpu_to_le32(0xC0020041) // -EOPNOTSUPP +#define RPC_NT_NO_CONTEXT_AVAILABLE cpu_to_le32(0xC0020042) // -EIO +#define RPC_NT_INTERNAL_ERROR cpu_to_le32(0xC0020043) // -EIO +#define RPC_NT_ZERO_DIVIDE cpu_to_le32(0xC0020044) // -EIO +#define RPC_NT_ADDRESS_ERROR cpu_to_le32(0xC0020045) // -EIO +#define RPC_NT_FP_DIV_ZERO cpu_to_le32(0xC0020046) // -EIO +#define RPC_NT_FP_UNDERFLOW cpu_to_le32(0xC0020047) // -EIO +#define RPC_NT_FP_OVERFLOW cpu_to_le32(0xC0020048) // -EIO +#define RPC_NT_CALL_IN_PROGRESS cpu_to_le32(0xC0020049) // -EIO +#define RPC_NT_NO_MORE_BINDINGS cpu_to_le32(0xC002004A) // -EIO +#define RPC_NT_GROUP_MEMBER_NOT_FOUND cpu_to_le32(0xC002004B) // -EIO +#define EPT_NT_CANT_CREATE cpu_to_le32(0xC002004C) // -EIO +#define RPC_NT_INVALID_OBJECT cpu_to_le32(0xC002004D) // -EIO +#define RPC_NT_NO_INTERFACES cpu_to_le32(0xC002004F) // -EIO +#define RPC_NT_CALL_CANCELLED cpu_to_le32(0xC0020050) // -EIO +#define RPC_NT_BINDING_INCOMPLETE cpu_to_le32(0xC0020051) // -EIO +#define RPC_NT_COMM_FAILURE cpu_to_le32(0xC0020052) // -EIO +#define RPC_NT_UNSUPPORTED_AUTHN_LEVEL cpu_to_le32(0xC0020053) // -EIO +#define RPC_NT_NO_PRINC_NAME cpu_to_le32(0xC0020054) // -EIO +#define RPC_NT_NOT_RPC_ERROR cpu_to_le32(0xC0020055) // -EIO +#define RPC_NT_SEC_PKG_ERROR cpu_to_le32(0xC0020057) // -EIO +#define RPC_NT_NOT_CANCELLED cpu_to_le32(0xC0020058) // -EIO +#define RPC_NT_INVALID_ASYNC_HANDLE cpu_to_le32(0xC0020062) // -EIO +#define RPC_NT_INVALID_ASYNC_CALL cpu_to_le32(0xC0020063) // -EIO +#define RPC_NT_PROXY_ACCESS_DENIED cpu_to_le32(0xC0020064) // -EACCES +#define RPC_NT_NO_MORE_ENTRIES cpu_to_le32(0xC0030001) // -EIO +#define RPC_NT_SS_CHAR_TRANS_OPEN_FAIL cpu_to_le32(0xC0030002) // -EIO +#define RPC_NT_SS_CHAR_TRANS_SHORT_FILE cpu_to_le32(0xC0030003) // -EIO +#define RPC_NT_SS_IN_NULL_CONTEXT cpu_to_le32(0xC0030004) // -EIO +#define RPC_NT_SS_CONTEXT_MISMATCH cpu_to_le32(0xC0030005) // -EIO +#define RPC_NT_SS_CONTEXT_DAMAGED cpu_to_le32(0xC0030006) // -EIO +#define RPC_NT_SS_HANDLES_MISMATCH cpu_to_le32(0xC0030007) // -EIO +#define RPC_NT_SS_CANNOT_GET_CALL_HANDLE cpu_to_le32(0xC0030008) // -EIO +#define RPC_NT_NULL_REF_POINTER cpu_to_le32(0xC0030009) // -EIO +#define RPC_NT_ENUM_VALUE_OUT_OF_RANGE cpu_to_le32(0xC003000A) // -EIO +#define RPC_NT_BYTE_COUNT_TOO_SMALL cpu_to_le32(0xC003000B) // -EIO +#define RPC_NT_BAD_STUB_DATA cpu_to_le32(0xC003000C) // -EIO +#define RPC_NT_INVALID_ES_ACTION cpu_to_le32(0xC0030059) // -EIO +#define RPC_NT_WRONG_ES_VERSION cpu_to_le32(0xC003005A) // -EIO +#define RPC_NT_WRONG_STUB_VERSION cpu_to_le32(0xC003005B) // -EIO +#define RPC_NT_INVALID_PIPE_OBJECT cpu_to_le32(0xC003005C) // -EIO +#define RPC_NT_INVALID_PIPE_OPERATION cpu_to_le32(0xC003005D) // -EIO +#define RPC_NT_WRONG_PIPE_VERSION cpu_to_le32(0xC003005E) // -EIO +#define RPC_NT_PIPE_CLOSED cpu_to_le32(0xC003005F) // -EIO +#define RPC_NT_PIPE_DISCIPLINE_ERROR cpu_to_le32(0xC0030060) // -EIO +#define RPC_NT_PIPE_EMPTY cpu_to_le32(0xC0030061) // -EIO +#define STATUS_PNP_BAD_MPS_TABLE cpu_to_le32(0xC0040035) // -EIO +#define STATUS_PNP_TRANSLATION_FAILED cpu_to_le32(0xC0040036) // -EIO +#define STATUS_PNP_IRQ_TRANSLATION_FAILED cpu_to_le32(0xC0040037) // -EIO +#define STATUS_PNP_INVALID_ID cpu_to_le32(0xC0040038) // -EIO +#define STATUS_IO_REISSUE_AS_CACHED cpu_to_le32(0xC0040039) // -EIO +#define STATUS_CTX_WINSTATION_NAME_INVALID cpu_to_le32(0xC00A0001) // -EIO +#define STATUS_CTX_INVALID_PD cpu_to_le32(0xC00A0002) // -EIO +#define STATUS_CTX_PD_NOT_FOUND cpu_to_le32(0xC00A0003) // -EIO +#define STATUS_CTX_CLOSE_PENDING cpu_to_le32(0xC00A0006) // -EIO +#define STATUS_CTX_NO_OUTBUF cpu_to_le32(0xC00A0007) // -EIO +#define STATUS_CTX_MODEM_INF_NOT_FOUND cpu_to_le32(0xC00A0008) // -EIO +#define STATUS_CTX_INVALID_MODEMNAME cpu_to_le32(0xC00A0009) // -EIO +#define STATUS_CTX_RESPONSE_ERROR cpu_to_le32(0xC00A000A) // -EIO +#define STATUS_CTX_MODEM_RESPONSE_TIMEOUT cpu_to_le32(0xC00A000B) // -ETIMEDOUT +#define STATUS_CTX_MODEM_RESPONSE_NO_CARRIER cpu_to_le32(0xC00A000C) // -EIO +#define STATUS_CTX_MODEM_RESPONSE_NO_DIALTONE cpu_to_le32(0xC00A000D) // -EIO +#define STATUS_CTX_MODEM_RESPONSE_BUSY cpu_to_le32(0xC00A000E) // -EBUSY +#define STATUS_CTX_MODEM_RESPONSE_VOICE cpu_to_le32(0xC00A000F) // -EIO +#define STATUS_CTX_TD_ERROR cpu_to_le32(0xC00A0010) // -EIO +#define STATUS_CTX_LICENSE_CLIENT_INVALID cpu_to_le32(0xC00A0012) // -EIO +#define STATUS_CTX_LICENSE_NOT_AVAILABLE cpu_to_le32(0xC00A0013) // -EIO +#define STATUS_CTX_LICENSE_EXPIRED cpu_to_le32(0xC00A0014) // -EIO +#define STATUS_CTX_WINSTATION_NOT_FOUND cpu_to_le32(0xC00A0015) // -EIO +#define STATUS_CTX_WINSTATION_NAME_COLLISION cpu_to_le32(0xC00A0016) // -EIO +#define STATUS_CTX_WINSTATION_BUSY cpu_to_le32(0xC00A0017) // -EBUSY +#define STATUS_CTX_BAD_VIDEO_MODE cpu_to_le32(0xC00A0018) // -EIO +#define STATUS_CTX_GRAPHICS_INVALID cpu_to_le32(0xC00A0022) // -EIO +#define STATUS_CTX_NOT_CONSOLE cpu_to_le32(0xC00A0024) // -EIO +#define STATUS_CTX_CLIENT_QUERY_TIMEOUT cpu_to_le32(0xC00A0026) // -EIO +#define STATUS_CTX_CONSOLE_DISCONNECT cpu_to_le32(0xC00A0027) // -EIO +#define STATUS_CTX_CONSOLE_CONNECT cpu_to_le32(0xC00A0028) // -EIO +#define STATUS_CTX_SHADOW_DENIED cpu_to_le32(0xC00A002A) // -EIO +#define STATUS_CTX_WINSTATION_ACCESS_DENIED cpu_to_le32(0xC00A002B) // -EACCES +#define STATUS_CTX_INVALID_WD cpu_to_le32(0xC00A002E) // -EIO +#define STATUS_CTX_WD_NOT_FOUND cpu_to_le32(0xC00A002F) // -EIO +#define STATUS_CTX_SHADOW_INVALID cpu_to_le32(0xC00A0030) // -EIO +#define STATUS_CTX_SHADOW_DISABLED cpu_to_le32(0xC00A0031) // -EIO +#define STATUS_RDP_PROTOCOL_ERROR cpu_to_le32(0xC00A0032) // -EIO +#define STATUS_CTX_CLIENT_LICENSE_NOT_SET cpu_to_le32(0xC00A0033) // -EIO +#define STATUS_CTX_CLIENT_LICENSE_IN_USE cpu_to_le32(0xC00A0034) // -EIO +#define STATUS_CTX_SHADOW_ENDED_BY_MODE_CHANGE cpu_to_le32(0xC00A0035) // -EIO +#define STATUS_CTX_SHADOW_NOT_RUNNING cpu_to_le32(0xC00A0036) // -EIO +#define STATUS_CTX_LOGON_DISABLED cpu_to_le32(0xC00A0037) // -EIO +#define STATUS_CTX_SECURITY_LAYER_ERROR cpu_to_le32(0xC00A0038) // -EIO +#define STATUS_TS_INCOMPATIBLE_SESSIONS cpu_to_le32(0xC00A0039) // -EIO +#define STATUS_MUI_FILE_NOT_FOUND cpu_to_le32(0xC00B0001) // -EIO +#define STATUS_MUI_INVALID_FILE cpu_to_le32(0xC00B0002) // -EIO +#define STATUS_MUI_INVALID_RC_CONFIG cpu_to_le32(0xC00B0003) // -EIO +#define STATUS_MUI_INVALID_LOCALE_NAME cpu_to_le32(0xC00B0004) // -EIO +#define STATUS_MUI_INVALID_ULTIMATEFALLBACK_NAME cpu_to_le32(0xC00B0005) // -EIO +#define STATUS_MUI_FILE_NOT_LOADED cpu_to_le32(0xC00B0006) // -EIO +#define STATUS_RESOURCE_ENUM_USER_STOP cpu_to_le32(0xC00B0007) // -EIO +#define STATUS_CLUSTER_INVALID_NODE cpu_to_le32(0xC0130001) // -EIO +#define STATUS_CLUSTER_NODE_EXISTS cpu_to_le32(0xC0130002) // -EIO +#define STATUS_CLUSTER_JOIN_IN_PROGRESS cpu_to_le32(0xC0130003) // -EIO +#define STATUS_CLUSTER_NODE_NOT_FOUND cpu_to_le32(0xC0130004) // -EIO +#define STATUS_CLUSTER_LOCAL_NODE_NOT_FOUND cpu_to_le32(0xC0130005) // -EIO +#define STATUS_CLUSTER_NETWORK_EXISTS cpu_to_le32(0xC0130006) // -EIO +#define STATUS_CLUSTER_NETWORK_NOT_FOUND cpu_to_le32(0xC0130007) // -EIO +#define STATUS_CLUSTER_NETINTERFACE_EXISTS cpu_to_le32(0xC0130008) // -EIO +#define STATUS_CLUSTER_NETINTERFACE_NOT_FOUND cpu_to_le32(0xC0130009) // -EIO +#define STATUS_CLUSTER_INVALID_REQUEST cpu_to_le32(0xC013000A) // -EIO +#define STATUS_CLUSTER_INVALID_NETWORK_PROVIDER cpu_to_le32(0xC013000B) // -EIO +#define STATUS_CLUSTER_NODE_DOWN cpu_to_le32(0xC013000C) // -EIO +#define STATUS_CLUSTER_NODE_UNREACHABLE cpu_to_le32(0xC013000D) // -EIO +#define STATUS_CLUSTER_NODE_NOT_MEMBER cpu_to_le32(0xC013000E) // -EIO +#define STATUS_CLUSTER_JOIN_NOT_IN_PROGRESS cpu_to_le32(0xC013000F) // -EIO +#define STATUS_CLUSTER_INVALID_NETWORK cpu_to_le32(0xC0130010) // -EIO +#define STATUS_CLUSTER_NO_NET_ADAPTERS cpu_to_le32(0xC0130011) // -EIO +#define STATUS_CLUSTER_NODE_UP cpu_to_le32(0xC0130012) // -EIO +#define STATUS_CLUSTER_NODE_PAUSED cpu_to_le32(0xC0130013) // -EIO +#define STATUS_CLUSTER_NODE_NOT_PAUSED cpu_to_le32(0xC0130014) // -EIO +#define STATUS_CLUSTER_NO_SECURITY_CONTEXT cpu_to_le32(0xC0130015) // -EIO +#define STATUS_CLUSTER_NETWORK_NOT_INTERNAL cpu_to_le32(0xC0130016) // -EIO +#define STATUS_CLUSTER_POISONED cpu_to_le32(0xC0130017) // -EIO +#define STATUS_ACPI_INVALID_OPCODE cpu_to_le32(0xC0140001) // -EIO +#define STATUS_ACPI_STACK_OVERFLOW cpu_to_le32(0xC0140002) // -EIO +#define STATUS_ACPI_ASSERT_FAILED cpu_to_le32(0xC0140003) // -EIO +#define STATUS_ACPI_INVALID_INDEX cpu_to_le32(0xC0140004) // -EIO +#define STATUS_ACPI_INVALID_ARGUMENT cpu_to_le32(0xC0140005) // -EIO +#define STATUS_ACPI_FATAL cpu_to_le32(0xC0140006) // -EIO +#define STATUS_ACPI_INVALID_SUPERNAME cpu_to_le32(0xC0140007) // -EIO +#define STATUS_ACPI_INVALID_ARGTYPE cpu_to_le32(0xC0140008) // -EIO +#define STATUS_ACPI_INVALID_OBJTYPE cpu_to_le32(0xC0140009) // -EIO +#define STATUS_ACPI_INVALID_TARGETTYPE cpu_to_le32(0xC014000A) // -EIO +#define STATUS_ACPI_INCORRECT_ARGUMENT_COUNT cpu_to_le32(0xC014000B) // -EIO +#define STATUS_ACPI_ADDRESS_NOT_MAPPED cpu_to_le32(0xC014000C) // -EIO +#define STATUS_ACPI_INVALID_EVENTTYPE cpu_to_le32(0xC014000D) // -EIO +#define STATUS_ACPI_HANDLER_COLLISION cpu_to_le32(0xC014000E) // -EIO +#define STATUS_ACPI_INVALID_DATA cpu_to_le32(0xC014000F) // -EIO +#define STATUS_ACPI_INVALID_REGION cpu_to_le32(0xC0140010) // -EIO +#define STATUS_ACPI_INVALID_ACCESS_SIZE cpu_to_le32(0xC0140011) // -EIO +#define STATUS_ACPI_ACQUIRE_GLOBAL_LOCK cpu_to_le32(0xC0140012) // -EIO +#define STATUS_ACPI_ALREADY_INITIALIZED cpu_to_le32(0xC0140013) // -EIO +#define STATUS_ACPI_NOT_INITIALIZED cpu_to_le32(0xC0140014) // -EIO +#define STATUS_ACPI_INVALID_MUTEX_LEVEL cpu_to_le32(0xC0140015) // -EIO +#define STATUS_ACPI_MUTEX_NOT_OWNED cpu_to_le32(0xC0140016) // -EIO +#define STATUS_ACPI_MUTEX_NOT_OWNER cpu_to_le32(0xC0140017) // -EIO +#define STATUS_ACPI_RS_ACCESS cpu_to_le32(0xC0140018) // -EIO +#define STATUS_ACPI_INVALID_TABLE cpu_to_le32(0xC0140019) // -EIO +#define STATUS_ACPI_REG_HANDLER_FAILED cpu_to_le32(0xC0140020) // -EIO +#define STATUS_ACPI_POWER_REQUEST_FAILED cpu_to_le32(0xC0140021) // -EIO +#define STATUS_SXS_SECTION_NOT_FOUND cpu_to_le32(0xC0150001) // -EIO +#define STATUS_SXS_CANT_GEN_ACTCTX cpu_to_le32(0xC0150002) // -EIO +#define STATUS_SXS_INVALID_ACTCTXDATA_FORMAT cpu_to_le32(0xC0150003) // -EIO +#define STATUS_SXS_ASSEMBLY_NOT_FOUND cpu_to_le32(0xC0150004) // -EIO +#define STATUS_SXS_MANIFEST_FORMAT_ERROR cpu_to_le32(0xC0150005) // -EIO +#define STATUS_SXS_MANIFEST_PARSE_ERROR cpu_to_le32(0xC0150006) // -EIO +#define STATUS_SXS_ACTIVATION_CONTEXT_DISABLED cpu_to_le32(0xC0150007) // -EIO +#define STATUS_SXS_KEY_NOT_FOUND cpu_to_le32(0xC0150008) // -EIO +#define STATUS_SXS_VERSION_CONFLICT cpu_to_le32(0xC0150009) // -EIO +#define STATUS_SXS_WRONG_SECTION_TYPE cpu_to_le32(0xC015000A) // -EIO +#define STATUS_SXS_THREAD_QUERIES_DISABLED cpu_to_le32(0xC015000B) // -EIO +#define STATUS_SXS_ASSEMBLY_MISSING cpu_to_le32(0xC015000C) // -EIO +#define STATUS_SXS_PROCESS_DEFAULT_ALREADY_SET cpu_to_le32(0xC015000E) // -EIO +#define STATUS_SXS_EARLY_DEACTIVATION cpu_to_le32(0xC015000F) // -EIO +#define STATUS_SXS_INVALID_DEACTIVATION cpu_to_le32(0xC0150010) // -EIO +#define STATUS_SXS_MULTIPLE_DEACTIVATION cpu_to_le32(0xC0150011) // -EIO +#define STATUS_SXS_SYSTEM_DEFAULT_ACTIVATION_CONTEXT_EMPTY cpu_to_le32(0xC0150012) // -EIO +#define STATUS_SXS_PROCESS_TERMINATION_REQUESTED cpu_to_le32(0xC0150013) // -EIO +#define STATUS_SXS_CORRUPT_ACTIVATION_STACK cpu_to_le32(0xC0150014) // -EIO +#define STATUS_SXS_CORRUPTION cpu_to_le32(0xC0150015) // -EIO +#define STATUS_SXS_INVALID_IDENTITY_ATTRIBUTE_VALUE cpu_to_le32(0xC0150016) // -EIO +#define STATUS_SXS_INVALID_IDENTITY_ATTRIBUTE_NAME cpu_to_le32(0xC0150017) // -EIO +#define STATUS_SXS_IDENTITY_DUPLICATE_ATTRIBUTE cpu_to_le32(0xC0150018) // -EIO +#define STATUS_SXS_IDENTITY_PARSE_ERROR cpu_to_le32(0xC0150019) // -EIO +#define STATUS_SXS_COMPONENT_STORE_CORRUPT cpu_to_le32(0xC015001A) // -EIO +#define STATUS_SXS_FILE_HASH_MISMATCH cpu_to_le32(0xC015001B) // -EIO +#define STATUS_SXS_MANIFEST_IDENTITY_SAME_BUT_CONTENTS_DIFFERENT cpu_to_le32(0xC015001C) // -EIO +#define STATUS_SXS_IDENTITIES_DIFFERENT cpu_to_le32(0xC015001D) // -EIO +#define STATUS_SXS_ASSEMBLY_IS_NOT_A_DEPLOYMENT cpu_to_le32(0xC015001E) // -EIO +#define STATUS_SXS_FILE_NOT_PART_OF_ASSEMBLY cpu_to_le32(0xC015001F) // -EIO +#define STATUS_ADVANCED_INSTALLER_FAILED cpu_to_le32(0xC0150020) // -EIO +#define STATUS_XML_ENCODING_MISMATCH cpu_to_le32(0xC0150021) // -EIO +#define STATUS_SXS_MANIFEST_TOO_BIG cpu_to_le32(0xC0150022) // -EIO +#define STATUS_SXS_SETTING_NOT_REGISTERED cpu_to_le32(0xC0150023) // -EIO +#define STATUS_SXS_TRANSACTION_CLOSURE_INCOMPLETE cpu_to_le32(0xC0150024) // -EIO +#define STATUS_SMI_PRIMITIVE_INSTALLER_FAILED cpu_to_le32(0xC0150025) // -EIO +#define STATUS_GENERIC_COMMAND_FAILED cpu_to_le32(0xC0150026) // -EIO +#define STATUS_SXS_FILE_HASH_MISSING cpu_to_le32(0xC0150027) // -EIO +#define STATUS_TRANSACTIONAL_CONFLICT cpu_to_le32(0xC0190001) // -EIO +#define STATUS_INVALID_TRANSACTION cpu_to_le32(0xC0190002) // -EIO +#define STATUS_TRANSACTION_NOT_ACTIVE cpu_to_le32(0xC0190003) // -EIO +#define STATUS_TM_INITIALIZATION_FAILED cpu_to_le32(0xC0190004) // -EIO +#define STATUS_RM_NOT_ACTIVE cpu_to_le32(0xC0190005) // -EIO +#define STATUS_RM_METADATA_CORRUPT cpu_to_le32(0xC0190006) // -EIO +#define STATUS_TRANSACTION_NOT_JOINED cpu_to_le32(0xC0190007) // -EIO +#define STATUS_DIRECTORY_NOT_RM cpu_to_le32(0xC0190008) // -EIO +#define STATUS_TRANSACTIONS_UNSUPPORTED_REMOTE cpu_to_le32(0xC019000A) // -EIO +#define STATUS_LOG_RESIZE_INVALID_SIZE cpu_to_le32(0xC019000B) // -EIO +#define STATUS_REMOTE_FILE_VERSION_MISMATCH cpu_to_le32(0xC019000C) // -EIO +#define STATUS_CRM_PROTOCOL_ALREADY_EXISTS cpu_to_le32(0xC019000F) // -EIO +#define STATUS_TRANSACTION_PROPAGATION_FAILED cpu_to_le32(0xC0190010) // -EIO +#define STATUS_CRM_PROTOCOL_NOT_FOUND cpu_to_le32(0xC0190011) // -EIO +#define STATUS_TRANSACTION_SUPERIOR_EXISTS cpu_to_le32(0xC0190012) // -EIO +#define STATUS_TRANSACTION_REQUEST_NOT_VALID cpu_to_le32(0xC0190013) // -EIO +#define STATUS_TRANSACTION_NOT_REQUESTED cpu_to_le32(0xC0190014) // -EIO +#define STATUS_TRANSACTION_ALREADY_ABORTED cpu_to_le32(0xC0190015) // -EIO +#define STATUS_TRANSACTION_ALREADY_COMMITTED cpu_to_le32(0xC0190016) // -EIO +#define STATUS_TRANSACTION_INVALID_MARSHALL_BUFFER cpu_to_le32(0xC0190017) // -EIO +#define STATUS_CURRENT_TRANSACTION_NOT_VALID cpu_to_le32(0xC0190018) // -EIO +#define STATUS_LOG_GROWTH_FAILED cpu_to_le32(0xC0190019) // -EIO +#define STATUS_OBJECT_NO_LONGER_EXISTS cpu_to_le32(0xC0190021) // -EIO +#define STATUS_STREAM_MINIVERSION_NOT_FOUND cpu_to_le32(0xC0190022) // -EIO +#define STATUS_STREAM_MINIVERSION_NOT_VALID cpu_to_le32(0xC0190023) // -EIO +#define STATUS_MINIVERSION_INACCESSIBLE_FROM_SPECIFIED_TRANSACTION cpu_to_le32(0xC0190024) // -EIO +#define STATUS_CANT_OPEN_MINIVERSION_WITH_MODIFY_INTENT cpu_to_le32(0xC0190025) // -EIO +#define STATUS_CANT_CREATE_MORE_STREAM_MINIVERSIONS cpu_to_le32(0xC0190026) // -EIO +#define STATUS_HANDLE_NO_LONGER_VALID cpu_to_le32(0xC0190028) // -EIO +#define STATUS_LOG_CORRUPTION_DETECTED cpu_to_le32(0xC0190030) // -EIO +#define STATUS_RM_DISCONNECTED cpu_to_le32(0xC0190032) // -EIO +#define STATUS_ENLISTMENT_NOT_SUPERIOR cpu_to_le32(0xC0190033) // -EIO +#define STATUS_FILE_IDENTITY_NOT_PERSISTENT cpu_to_le32(0xC0190036) // -EIO +#define STATUS_CANT_BREAK_TRANSACTIONAL_DEPENDENCY cpu_to_le32(0xC0190037) // -EIO +#define STATUS_CANT_CROSS_RM_BOUNDARY cpu_to_le32(0xC0190038) // -EIO +#define STATUS_TXF_DIR_NOT_EMPTY cpu_to_le32(0xC0190039) // -EIO +#define STATUS_INDOUBT_TRANSACTIONS_EXIST cpu_to_le32(0xC019003A) // -EIO +#define STATUS_TM_VOLATILE cpu_to_le32(0xC019003B) // -EIO +#define STATUS_ROLLBACK_TIMER_EXPIRED cpu_to_le32(0xC019003C) // -EIO +#define STATUS_TXF_ATTRIBUTE_CORRUPT cpu_to_le32(0xC019003D) // -EIO +#define STATUS_EFS_NOT_ALLOWED_IN_TRANSACTION cpu_to_le32(0xC019003E) // -EIO +#define STATUS_TRANSACTIONAL_OPEN_NOT_ALLOWED cpu_to_le32(0xC019003F) // -EIO +#define STATUS_TRANSACTED_MAPPING_UNSUPPORTED_REMOTE cpu_to_le32(0xC0190040) // -EIO +#define STATUS_TRANSACTION_REQUIRED_PROMOTION cpu_to_le32(0xC0190043) // -EIO +#define STATUS_CANNOT_EXECUTE_FILE_IN_TRANSACTION cpu_to_le32(0xC0190044) // -EIO +#define STATUS_TRANSACTIONS_NOT_FROZEN cpu_to_le32(0xC0190045) // -EIO +#define STATUS_TRANSACTION_FREEZE_IN_PROGRESS cpu_to_le32(0xC0190046) // -EIO +#define STATUS_NOT_SNAPSHOT_VOLUME cpu_to_le32(0xC0190047) // -EIO +#define STATUS_NO_SAVEPOINT_WITH_OPEN_FILES cpu_to_le32(0xC0190048) // -EIO +#define STATUS_SPARSE_NOT_ALLOWED_IN_TRANSACTION cpu_to_le32(0xC0190049) // -EIO +#define STATUS_TM_IDENTITY_MISMATCH cpu_to_le32(0xC019004A) // -EIO +#define STATUS_FLOATED_SECTION cpu_to_le32(0xC019004B) // -EIO +#define STATUS_CANNOT_ACCEPT_TRANSACTED_WORK cpu_to_le32(0xC019004C) // -EIO +#define STATUS_CANNOT_ABORT_TRANSACTIONS cpu_to_le32(0xC019004D) // -EIO +#define STATUS_TRANSACTION_NOT_FOUND cpu_to_le32(0xC019004E) // -EIO +#define STATUS_RESOURCEMANAGER_NOT_FOUND cpu_to_le32(0xC019004F) // -EIO +#define STATUS_ENLISTMENT_NOT_FOUND cpu_to_le32(0xC0190050) // -EIO +#define STATUS_TRANSACTIONMANAGER_NOT_FOUND cpu_to_le32(0xC0190051) // -EIO +#define STATUS_TRANSACTIONMANAGER_NOT_ONLINE cpu_to_le32(0xC0190052) // -EIO +#define STATUS_TRANSACTIONMANAGER_RECOVERY_NAME_COLLISION cpu_to_le32(0xC0190053) // -EIO +#define STATUS_TRANSACTION_NOT_ROOT cpu_to_le32(0xC0190054) // -EIO +#define STATUS_TRANSACTION_OBJECT_EXPIRED cpu_to_le32(0xC0190055) // -EIO +#define STATUS_COMPRESSION_NOT_ALLOWED_IN_TRANSACTION cpu_to_le32(0xC0190056) // -EIO +#define STATUS_TRANSACTION_RESPONSE_NOT_ENLISTED cpu_to_le32(0xC0190057) // -EIO +#define STATUS_TRANSACTION_RECORD_TOO_LONG cpu_to_le32(0xC0190058) // -EIO +#define STATUS_NO_LINK_TRACKING_IN_TRANSACTION cpu_to_le32(0xC0190059) // -EIO +#define STATUS_OPERATION_NOT_SUPPORTED_IN_TRANSACTION cpu_to_le32(0xC019005A) // -EOPNOTSUPP +#define STATUS_TRANSACTION_INTEGRITY_VIOLATED cpu_to_le32(0xC019005B) // -EIO +#define STATUS_LOG_SECTOR_INVALID cpu_to_le32(0xC01A0001) // -EIO +#define STATUS_LOG_SECTOR_PARITY_INVALID cpu_to_le32(0xC01A0002) // -EIO +#define STATUS_LOG_SECTOR_REMAPPED cpu_to_le32(0xC01A0003) // -EIO +#define STATUS_LOG_BLOCK_INCOMPLETE cpu_to_le32(0xC01A0004) // -EIO +#define STATUS_LOG_INVALID_RANGE cpu_to_le32(0xC01A0005) // -EIO +#define STATUS_LOG_BLOCKS_EXHAUSTED cpu_to_le32(0xC01A0006) // -EIO +#define STATUS_LOG_READ_CONTEXT_INVALID cpu_to_le32(0xC01A0007) // -EIO +#define STATUS_LOG_RESTART_INVALID cpu_to_le32(0xC01A0008) // -EIO +#define STATUS_LOG_BLOCK_VERSION cpu_to_le32(0xC01A0009) // -EIO +#define STATUS_LOG_BLOCK_INVALID cpu_to_le32(0xC01A000A) // -EIO +#define STATUS_LOG_READ_MODE_INVALID cpu_to_le32(0xC01A000B) // -EIO +#define STATUS_LOG_METADATA_CORRUPT cpu_to_le32(0xC01A000D) // -EIO +#define STATUS_LOG_METADATA_INVALID cpu_to_le32(0xC01A000E) // -EIO +#define STATUS_LOG_METADATA_INCONSISTENT cpu_to_le32(0xC01A000F) // -EIO +#define STATUS_LOG_RESERVATION_INVALID cpu_to_le32(0xC01A0010) // -EIO +#define STATUS_LOG_CANT_DELETE cpu_to_le32(0xC01A0011) // -EIO +#define STATUS_LOG_CONTAINER_LIMIT_EXCEEDED cpu_to_le32(0xC01A0012) // -EIO +#define STATUS_LOG_START_OF_LOG cpu_to_le32(0xC01A0013) // -EIO +#define STATUS_LOG_POLICY_ALREADY_INSTALLED cpu_to_le32(0xC01A0014) // -EIO +#define STATUS_LOG_POLICY_NOT_INSTALLED cpu_to_le32(0xC01A0015) // -EIO +#define STATUS_LOG_POLICY_INVALID cpu_to_le32(0xC01A0016) // -EIO +#define STATUS_LOG_POLICY_CONFLICT cpu_to_le32(0xC01A0017) // -EIO +#define STATUS_LOG_PINNED_ARCHIVE_TAIL cpu_to_le32(0xC01A0018) // -EIO +#define STATUS_LOG_RECORD_NONEXISTENT cpu_to_le32(0xC01A0019) // -EIO +#define STATUS_LOG_RECORDS_RESERVED_INVALID cpu_to_le32(0xC01A001A) // -EIO +#define STATUS_LOG_SPACE_RESERVED_INVALID cpu_to_le32(0xC01A001B) // -EIO +#define STATUS_LOG_TAIL_INVALID cpu_to_le32(0xC01A001C) // -EIO +#define STATUS_LOG_FULL cpu_to_le32(0xC01A001D) // -EIO +#define STATUS_LOG_MULTIPLEXED cpu_to_le32(0xC01A001E) // -EIO +#define STATUS_LOG_DEDICATED cpu_to_le32(0xC01A001F) // -EIO +#define STATUS_LOG_ARCHIVE_NOT_IN_PROGRESS cpu_to_le32(0xC01A0020) // -EIO +#define STATUS_LOG_ARCHIVE_IN_PROGRESS cpu_to_le32(0xC01A0021) // -EIO +#define STATUS_LOG_EPHEMERAL cpu_to_le32(0xC01A0022) // -EIO +#define STATUS_LOG_NOT_ENOUGH_CONTAINERS cpu_to_le32(0xC01A0023) // -EIO +#define STATUS_LOG_CLIENT_ALREADY_REGISTERED cpu_to_le32(0xC01A0024) // -EIO +#define STATUS_LOG_CLIENT_NOT_REGISTERED cpu_to_le32(0xC01A0025) // -EIO +#define STATUS_LOG_FULL_HANDLER_IN_PROGRESS cpu_to_le32(0xC01A0026) // -EIO +#define STATUS_LOG_CONTAINER_READ_FAILED cpu_to_le32(0xC01A0027) // -EIO +#define STATUS_LOG_CONTAINER_WRITE_FAILED cpu_to_le32(0xC01A0028) // -EIO +#define STATUS_LOG_CONTAINER_OPEN_FAILED cpu_to_le32(0xC01A0029) // -EIO +#define STATUS_LOG_CONTAINER_STATE_INVALID cpu_to_le32(0xC01A002A) // -EIO +#define STATUS_LOG_STATE_INVALID cpu_to_le32(0xC01A002B) // -EIO +#define STATUS_LOG_PINNED cpu_to_le32(0xC01A002C) // -EIO +#define STATUS_LOG_METADATA_FLUSH_FAILED cpu_to_le32(0xC01A002D) // -EIO +#define STATUS_LOG_INCONSISTENT_SECURITY cpu_to_le32(0xC01A002E) // -EIO +#define STATUS_LOG_APPENDED_FLUSH_FAILED cpu_to_le32(0xC01A002F) // -EIO +#define STATUS_LOG_PINNED_RESERVATION cpu_to_le32(0xC01A0030) // -EIO +#define STATUS_VIDEO_HUNG_DISPLAY_DRIVER_THREAD cpu_to_le32(0xC01B00EA) // -EIO +#define STATUS_FLT_NO_HANDLER_DEFINED cpu_to_le32(0xC01C0001) // -EIO +#define STATUS_FLT_CONTEXT_ALREADY_DEFINED cpu_to_le32(0xC01C0002) // -EIO +#define STATUS_FLT_INVALID_ASYNCHRONOUS_REQUEST cpu_to_le32(0xC01C0003) // -EIO +#define STATUS_FLT_DISALLOW_FAST_IO cpu_to_le32(0xC01C0004) // -EIO +#define STATUS_FLT_INVALID_NAME_REQUEST cpu_to_le32(0xC01C0005) // -EIO +#define STATUS_FLT_NOT_SAFE_TO_POST_OPERATION cpu_to_le32(0xC01C0006) // -EIO +#define STATUS_FLT_NOT_INITIALIZED cpu_to_le32(0xC01C0007) // -EIO +#define STATUS_FLT_FILTER_NOT_READY cpu_to_le32(0xC01C0008) // -EIO +#define STATUS_FLT_POST_OPERATION_CLEANUP cpu_to_le32(0xC01C0009) // -EIO +#define STATUS_FLT_INTERNAL_ERROR cpu_to_le32(0xC01C000A) // -EIO +#define STATUS_FLT_DELETING_OBJECT cpu_to_le32(0xC01C000B) // -EIO +#define STATUS_FLT_MUST_BE_NONPAGED_POOL cpu_to_le32(0xC01C000C) // -EIO +#define STATUS_FLT_DUPLICATE_ENTRY cpu_to_le32(0xC01C000D) // -EIO +#define STATUS_FLT_CBDQ_DISABLED cpu_to_le32(0xC01C000E) // -EIO +#define STATUS_FLT_DO_NOT_ATTACH cpu_to_le32(0xC01C000F) // -EIO +#define STATUS_FLT_DO_NOT_DETACH cpu_to_le32(0xC01C0010) // -EIO +#define STATUS_FLT_INSTANCE_ALTITUDE_COLLISION cpu_to_le32(0xC01C0011) // -EIO +#define STATUS_FLT_INSTANCE_NAME_COLLISION cpu_to_le32(0xC01C0012) // -EIO +#define STATUS_FLT_FILTER_NOT_FOUND cpu_to_le32(0xC01C0013) // -EIO +#define STATUS_FLT_VOLUME_NOT_FOUND cpu_to_le32(0xC01C0014) // -EIO +#define STATUS_FLT_INSTANCE_NOT_FOUND cpu_to_le32(0xC01C0015) // -EIO +#define STATUS_FLT_CONTEXT_ALLOCATION_NOT_FOUND cpu_to_le32(0xC01C0016) // -EIO +#define STATUS_FLT_INVALID_CONTEXT_REGISTRATION cpu_to_le32(0xC01C0017) // -EIO +#define STATUS_FLT_NAME_CACHE_MISS cpu_to_le32(0xC01C0018) // -EIO +#define STATUS_FLT_NO_DEVICE_OBJECT cpu_to_le32(0xC01C0019) // -EIO +#define STATUS_FLT_VOLUME_ALREADY_MOUNTED cpu_to_le32(0xC01C001A) // -EIO +#define STATUS_FLT_ALREADY_ENLISTED cpu_to_le32(0xC01C001B) // -EIO +#define STATUS_FLT_CONTEXT_ALREADY_LINKED cpu_to_le32(0xC01C001C) // -EIO +#define STATUS_FLT_NO_WAITER_FOR_REPLY cpu_to_le32(0xC01C0020) // -EIO +#define STATUS_MONITOR_NO_DESCRIPTOR cpu_to_le32(0xC01D0001) // -EIO +#define STATUS_MONITOR_UNKNOWN_DESCRIPTOR_FORMAT cpu_to_le32(0xC01D0002) // -EIO +#define STATUS_MONITOR_INVALID_DESCRIPTOR_CHECKSUM cpu_to_le32(0xC01D0003) // -EIO +#define STATUS_MONITOR_INVALID_STANDARD_TIMING_BLOCK cpu_to_le32(0xC01D0004) // -EIO +#define STATUS_MONITOR_WMI_DATABLOCK_REGISTRATION_FAILED cpu_to_le32(0xC01D0005) // -EIO +#define STATUS_MONITOR_INVALID_SERIAL_NUMBER_MONDSC_BLOCK cpu_to_le32(0xC01D0006) // -EIO +#define STATUS_MONITOR_INVALID_USER_FRIENDLY_MONDSC_BLOCK cpu_to_le32(0xC01D0007) // -EIO +#define STATUS_MONITOR_NO_MORE_DESCRIPTOR_DATA cpu_to_le32(0xC01D0008) // -EIO +#define STATUS_MONITOR_INVALID_DETAILED_TIMING_BLOCK cpu_to_le32(0xC01D0009) // -EIO +#define STATUS_GRAPHICS_NOT_EXCLUSIVE_MODE_OWNER cpu_to_le32(0xC01E0000) // -EIO +#define STATUS_GRAPHICS_INSUFFICIENT_DMA_BUFFER cpu_to_le32(0xC01E0001) // -EIO +#define STATUS_GRAPHICS_INVALID_DISPLAY_ADAPTER cpu_to_le32(0xC01E0002) // -EIO +#define STATUS_GRAPHICS_ADAPTER_WAS_RESET cpu_to_le32(0xC01E0003) // -EIO +#define STATUS_GRAPHICS_INVALID_DRIVER_MODEL cpu_to_le32(0xC01E0004) // -EIO +#define STATUS_GRAPHICS_PRESENT_MODE_CHANGED cpu_to_le32(0xC01E0005) // -EIO +#define STATUS_GRAPHICS_PRESENT_OCCLUDED cpu_to_le32(0xC01E0006) // -EIO +#define STATUS_GRAPHICS_PRESENT_DENIED cpu_to_le32(0xC01E0007) // -EIO +#define STATUS_GRAPHICS_CANNOTCOLORCONVERT cpu_to_le32(0xC01E0008) // -EIO +#define STATUS_GRAPHICS_NO_VIDEO_MEMORY cpu_to_le32(0xC01E0100) // -EIO +#define STATUS_GRAPHICS_CANT_LOCK_MEMORY cpu_to_le32(0xC01E0101) // -EIO +#define STATUS_GRAPHICS_ALLOCATION_BUSY cpu_to_le32(0xC01E0102) // -EBUSY +#define STATUS_GRAPHICS_TOO_MANY_REFERENCES cpu_to_le32(0xC01E0103) // -EIO +#define STATUS_GRAPHICS_TRY_AGAIN_LATER cpu_to_le32(0xC01E0104) // -EIO +#define STATUS_GRAPHICS_TRY_AGAIN_NOW cpu_to_le32(0xC01E0105) // -EIO +#define STATUS_GRAPHICS_ALLOCATION_INVALID cpu_to_le32(0xC01E0106) // -EIO +#define STATUS_GRAPHICS_UNSWIZZLING_APERTURE_UNAVAILABLE cpu_to_le32(0xC01E0107) // -EIO +#define STATUS_GRAPHICS_UNSWIZZLING_APERTURE_UNSUPPORTED cpu_to_le32(0xC01E0108) // -EIO +#define STATUS_GRAPHICS_CANT_EVICT_PINNED_ALLOCATION cpu_to_le32(0xC01E0109) // -EIO +#define STATUS_GRAPHICS_INVALID_ALLOCATION_USAGE cpu_to_le32(0xC01E0110) // -EIO +#define STATUS_GRAPHICS_CANT_RENDER_LOCKED_ALLOCATION cpu_to_le32(0xC01E0111) // -EIO +#define STATUS_GRAPHICS_ALLOCATION_CLOSED cpu_to_le32(0xC01E0112) // -EIO +#define STATUS_GRAPHICS_INVALID_ALLOCATION_INSTANCE cpu_to_le32(0xC01E0113) // -EIO +#define STATUS_GRAPHICS_INVALID_ALLOCATION_HANDLE cpu_to_le32(0xC01E0114) // -EIO +#define STATUS_GRAPHICS_WRONG_ALLOCATION_DEVICE cpu_to_le32(0xC01E0115) // -EIO +#define STATUS_GRAPHICS_ALLOCATION_CONTENT_LOST cpu_to_le32(0xC01E0116) // -EIO +#define STATUS_GRAPHICS_GPU_EXCEPTION_ON_DEVICE cpu_to_le32(0xC01E0200) // -EIO +#define STATUS_GRAPHICS_INVALID_VIDPN_TOPOLOGY cpu_to_le32(0xC01E0300) // -EIO +#define STATUS_GRAPHICS_VIDPN_TOPOLOGY_NOT_SUPPORTED cpu_to_le32(0xC01E0301) // -EIO +#define STATUS_GRAPHICS_VIDPN_TOPOLOGY_CURRENTLY_NOT_SUPPORTED cpu_to_le32(0xC01E0302) // -EIO +#define STATUS_GRAPHICS_INVALID_VIDPN cpu_to_le32(0xC01E0303) // -EIO +#define STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE cpu_to_le32(0xC01E0304) // -EIO +#define STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_TARGET cpu_to_le32(0xC01E0305) // -EIO +#define STATUS_GRAPHICS_VIDPN_MODALITY_NOT_SUPPORTED cpu_to_le32(0xC01E0306) // -EIO +#define STATUS_GRAPHICS_INVALID_VIDPN_SOURCEMODESET cpu_to_le32(0xC01E0308) // -EIO +#define STATUS_GRAPHICS_INVALID_VIDPN_TARGETMODESET cpu_to_le32(0xC01E0309) // -EIO +#define STATUS_GRAPHICS_INVALID_FREQUENCY cpu_to_le32(0xC01E030A) // -EIO +#define STATUS_GRAPHICS_INVALID_ACTIVE_REGION cpu_to_le32(0xC01E030B) // -EIO +#define STATUS_GRAPHICS_INVALID_TOTAL_REGION cpu_to_le32(0xC01E030C) // -EIO +#define STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE_MODE cpu_to_le32(0xC01E0310) // -EIO +#define STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_TARGET_MODE cpu_to_le32(0xC01E0311) // -EIO +#define STATUS_GRAPHICS_PINNED_MODE_MUST_REMAIN_IN_SET cpu_to_le32(0xC01E0312) // -EIO +#define STATUS_GRAPHICS_PATH_ALREADY_IN_TOPOLOGY cpu_to_le32(0xC01E0313) // -EIO +#define STATUS_GRAPHICS_MODE_ALREADY_IN_MODESET cpu_to_le32(0xC01E0314) // -EIO +#define STATUS_GRAPHICS_INVALID_VIDEOPRESENTSOURCESET cpu_to_le32(0xC01E0315) // -EIO +#define STATUS_GRAPHICS_INVALID_VIDEOPRESENTTARGETSET cpu_to_le32(0xC01E0316) // -EIO +#define STATUS_GRAPHICS_SOURCE_ALREADY_IN_SET cpu_to_le32(0xC01E0317) // -EIO +#define STATUS_GRAPHICS_TARGET_ALREADY_IN_SET cpu_to_le32(0xC01E0318) // -EIO +#define STATUS_GRAPHICS_INVALID_VIDPN_PRESENT_PATH cpu_to_le32(0xC01E0319) // -EIO +#define STATUS_GRAPHICS_NO_RECOMMENDED_VIDPN_TOPOLOGY cpu_to_le32(0xC01E031A) // -EIO +#define STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGESET cpu_to_le32(0xC01E031B) // -EIO +#define STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGE cpu_to_le32(0xC01E031C) // -EIO +#define STATUS_GRAPHICS_FREQUENCYRANGE_NOT_IN_SET cpu_to_le32(0xC01E031D) // -EIO +#define STATUS_GRAPHICS_FREQUENCYRANGE_ALREADY_IN_SET cpu_to_le32(0xC01E031F) // -EIO +#define STATUS_GRAPHICS_STALE_MODESET cpu_to_le32(0xC01E0320) // -EIO +#define STATUS_GRAPHICS_INVALID_MONITOR_SOURCEMODESET cpu_to_le32(0xC01E0321) // -EIO +#define STATUS_GRAPHICS_INVALID_MONITOR_SOURCE_MODE cpu_to_le32(0xC01E0322) // -EIO +#define STATUS_GRAPHICS_NO_RECOMMENDED_FUNCTIONAL_VIDPN cpu_to_le32(0xC01E0323) // -EIO +#define STATUS_GRAPHICS_MODE_ID_MUST_BE_UNIQUE cpu_to_le32(0xC01E0324) // -EIO +#define STATUS_GRAPHICS_EMPTY_ADAPTER_MONITOR_MODE_SUPPORT_INTERSECTION cpu_to_le32(0xC01E0325) // -EIO +#define STATUS_GRAPHICS_VIDEO_PRESENT_TARGETS_LESS_THAN_SOURCES cpu_to_le32(0xC01E0326) // -EIO +#define STATUS_GRAPHICS_PATH_NOT_IN_TOPOLOGY cpu_to_le32(0xC01E0327) // -EIO +#define STATUS_GRAPHICS_ADAPTER_MUST_HAVE_AT_LEAST_ONE_SOURCE cpu_to_le32(0xC01E0328) // -EIO +#define STATUS_GRAPHICS_ADAPTER_MUST_HAVE_AT_LEAST_ONE_TARGET cpu_to_le32(0xC01E0329) // -EIO +#define STATUS_GRAPHICS_INVALID_MONITORDESCRIPTORSET cpu_to_le32(0xC01E032A) // -EIO +#define STATUS_GRAPHICS_INVALID_MONITORDESCRIPTOR cpu_to_le32(0xC01E032B) // -EIO +#define STATUS_GRAPHICS_MONITORDESCRIPTOR_NOT_IN_SET cpu_to_le32(0xC01E032C) // -EIO +#define STATUS_GRAPHICS_MONITORDESCRIPTOR_ALREADY_IN_SET cpu_to_le32(0xC01E032D) // -EIO +#define STATUS_GRAPHICS_MONITORDESCRIPTOR_ID_MUST_BE_UNIQUE cpu_to_le32(0xC01E032E) // -EIO +#define STATUS_GRAPHICS_INVALID_VIDPN_TARGET_SUBSET_TYPE cpu_to_le32(0xC01E032F) // -EIO +#define STATUS_GRAPHICS_RESOURCES_NOT_RELATED cpu_to_le32(0xC01E0330) // -EIO +#define STATUS_GRAPHICS_SOURCE_ID_MUST_BE_UNIQUE cpu_to_le32(0xC01E0331) // -EIO +#define STATUS_GRAPHICS_TARGET_ID_MUST_BE_UNIQUE cpu_to_le32(0xC01E0332) // -EIO +#define STATUS_GRAPHICS_NO_AVAILABLE_VIDPN_TARGET cpu_to_le32(0xC01E0333) // -EIO +#define STATUS_GRAPHICS_MONITOR_COULD_NOT_BE_ASSOCIATED_WITH_ADAPTER cpu_to_le32(0xC01E0334) // -EIO +#define STATUS_GRAPHICS_NO_VIDPNMGR cpu_to_le32(0xC01E0335) // -EIO +#define STATUS_GRAPHICS_NO_ACTIVE_VIDPN cpu_to_le32(0xC01E0336) // -EIO +#define STATUS_GRAPHICS_STALE_VIDPN_TOPOLOGY cpu_to_le32(0xC01E0337) // -EIO +#define STATUS_GRAPHICS_MONITOR_NOT_CONNECTED cpu_to_le32(0xC01E0338) // -EIO +#define STATUS_GRAPHICS_SOURCE_NOT_IN_TOPOLOGY cpu_to_le32(0xC01E0339) // -EIO +#define STATUS_GRAPHICS_INVALID_PRIMARYSURFACE_SIZE cpu_to_le32(0xC01E033A) // -EIO +#define STATUS_GRAPHICS_INVALID_VISIBLEREGION_SIZE cpu_to_le32(0xC01E033B) // -EIO +#define STATUS_GRAPHICS_INVALID_STRIDE cpu_to_le32(0xC01E033C) // -EIO +#define STATUS_GRAPHICS_INVALID_PIXELFORMAT cpu_to_le32(0xC01E033D) // -EIO +#define STATUS_GRAPHICS_INVALID_COLORBASIS cpu_to_le32(0xC01E033E) // -EIO +#define STATUS_GRAPHICS_INVALID_PIXELVALUEACCESSMODE cpu_to_le32(0xC01E033F) // -EIO +#define STATUS_GRAPHICS_TARGET_NOT_IN_TOPOLOGY cpu_to_le32(0xC01E0340) // -EIO +#define STATUS_GRAPHICS_NO_DISPLAY_MODE_MANAGEMENT_SUPPORT cpu_to_le32(0xC01E0341) // -EIO +#define STATUS_GRAPHICS_VIDPN_SOURCE_IN_USE cpu_to_le32(0xC01E0342) // -EIO +#define STATUS_GRAPHICS_CANT_ACCESS_ACTIVE_VIDPN cpu_to_le32(0xC01E0343) // -EIO +#define STATUS_GRAPHICS_INVALID_PATH_IMPORTANCE_ORDINAL cpu_to_le32(0xC01E0344) // -EIO +#define STATUS_GRAPHICS_INVALID_PATH_CONTENT_GEOMETRY_TRANSFORMATION cpu_to_le32(0xC01E0345) // -EIO +#define STATUS_GRAPHICS_PATH_CONTENT_GEOMETRY_TRANSFORMATION_NOT_SUPPORTED cpu_to_le32(0xC01E0346) // -EIO +#define STATUS_GRAPHICS_INVALID_GAMMA_RAMP cpu_to_le32(0xC01E0347) // -EIO +#define STATUS_GRAPHICS_GAMMA_RAMP_NOT_SUPPORTED cpu_to_le32(0xC01E0348) // -EIO +#define STATUS_GRAPHICS_MULTISAMPLING_NOT_SUPPORTED cpu_to_le32(0xC01E0349) // -EIO +#define STATUS_GRAPHICS_MODE_NOT_IN_MODESET cpu_to_le32(0xC01E034A) // -EIO +#define STATUS_GRAPHICS_INVALID_VIDPN_TOPOLOGY_RECOMMENDATION_REASON cpu_to_le32(0xC01E034D) // -EIO +#define STATUS_GRAPHICS_INVALID_PATH_CONTENT_TYPE cpu_to_le32(0xC01E034E) // -EIO +#define STATUS_GRAPHICS_INVALID_COPYPROTECTION_TYPE cpu_to_le32(0xC01E034F) // -EIO +#define STATUS_GRAPHICS_UNASSIGNED_MODESET_ALREADY_EXISTS cpu_to_le32(0xC01E0350) // -EIO +#define STATUS_GRAPHICS_INVALID_SCANLINE_ORDERING cpu_to_le32(0xC01E0352) // -EIO +#define STATUS_GRAPHICS_TOPOLOGY_CHANGES_NOT_ALLOWED cpu_to_le32(0xC01E0353) // -EIO +#define STATUS_GRAPHICS_NO_AVAILABLE_IMPORTANCE_ORDINALS cpu_to_le32(0xC01E0354) // -EIO +#define STATUS_GRAPHICS_INCOMPATIBLE_PRIVATE_FORMAT cpu_to_le32(0xC01E0355) // -EIO +#define STATUS_GRAPHICS_INVALID_MODE_PRUNING_ALGORITHM cpu_to_le32(0xC01E0356) // -EIO +#define STATUS_GRAPHICS_INVALID_MONITOR_CAPABILITY_ORIGIN cpu_to_le32(0xC01E0357) // -EIO +#define STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGE_CONSTRAINT cpu_to_le32(0xC01E0358) // -EIO +#define STATUS_GRAPHICS_MAX_NUM_PATHS_REACHED cpu_to_le32(0xC01E0359) // -EIO +#define STATUS_GRAPHICS_CANCEL_VIDPN_TOPOLOGY_AUGMENTATION cpu_to_le32(0xC01E035A) // -EIO +#define STATUS_GRAPHICS_INVALID_CLIENT_TYPE cpu_to_le32(0xC01E035B) // -EIO +#define STATUS_GRAPHICS_CLIENTVIDPN_NOT_SET cpu_to_le32(0xC01E035C) // -EIO +#define STATUS_GRAPHICS_SPECIFIED_CHILD_ALREADY_CONNECTED cpu_to_le32(0xC01E0400) // -EIO +#define STATUS_GRAPHICS_CHILD_DESCRIPTOR_NOT_SUPPORTED cpu_to_le32(0xC01E0401) // -EIO +#define STATUS_GRAPHICS_NOT_A_LINKED_ADAPTER cpu_to_le32(0xC01E0430) // -EIO +#define STATUS_GRAPHICS_LEADLINK_NOT_ENUMERATED cpu_to_le32(0xC01E0431) // -EIO +#define STATUS_GRAPHICS_CHAINLINKS_NOT_ENUMERATED cpu_to_le32(0xC01E0432) // -EIO +#define STATUS_GRAPHICS_ADAPTER_CHAIN_NOT_READY cpu_to_le32(0xC01E0433) // -EIO +#define STATUS_GRAPHICS_CHAINLINKS_NOT_STARTED cpu_to_le32(0xC01E0434) // -EIO +#define STATUS_GRAPHICS_CHAINLINKS_NOT_POWERED_ON cpu_to_le32(0xC01E0435) // -EIO +#define STATUS_GRAPHICS_INCONSISTENT_DEVICE_LINK_STATE cpu_to_le32(0xC01E0436) // -EIO +#define STATUS_GRAPHICS_NOT_POST_DEVICE_DRIVER cpu_to_le32(0xC01E0438) // -EIO +#define STATUS_GRAPHICS_ADAPTER_ACCESS_NOT_EXCLUDED cpu_to_le32(0xC01E043B) // -EIO +#define STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_DOES_NOT_HAVE_COPP_SEMANTICS cpu_to_le32(0xC01E051C) // -EIO +#define STATUS_GRAPHICS_OPM_INVALID_INFORMATION_REQUEST cpu_to_le32(0xC01E051D) // -EIO +#define STATUS_GRAPHICS_OPM_DRIVER_INTERNAL_ERROR cpu_to_le32(0xC01E051E) // -EIO +#define STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_DOES_NOT_HAVE_OPM_SEMANTICS cpu_to_le32(0xC01E051F) // -EIO +#define STATUS_GRAPHICS_OPM_SIGNALING_NOT_SUPPORTED cpu_to_le32(0xC01E0520) // -EIO +#define STATUS_GRAPHICS_OPM_INVALID_CONFIGURATION_REQUEST cpu_to_le32(0xC01E0521) // -EIO +#define STATUS_GRAPHICS_OPM_NOT_SUPPORTED cpu_to_le32(0xC01E0500) // -EIO +#define STATUS_GRAPHICS_COPP_NOT_SUPPORTED cpu_to_le32(0xC01E0501) // -EIO +#define STATUS_GRAPHICS_UAB_NOT_SUPPORTED cpu_to_le32(0xC01E0502) // -EIO +#define STATUS_GRAPHICS_OPM_INVALID_ENCRYPTED_PARAMETERS cpu_to_le32(0xC01E0503) // -EIO +#define STATUS_GRAPHICS_OPM_PARAMETER_ARRAY_TOO_SMALL cpu_to_le32(0xC01E0504) // -EIO +#define STATUS_GRAPHICS_OPM_NO_PROTECTED_OUTPUTS_EXIST cpu_to_le32(0xC01E0505) // -EIO +#define STATUS_GRAPHICS_PVP_NO_DISPLAY_DEVICE_CORRESPONDS_TO_NAME cpu_to_le32(0xC01E0506) // -EIO +#define STATUS_GRAPHICS_PVP_DISPLAY_DEVICE_NOT_ATTACHED_TO_DESKTOP cpu_to_le32(0xC01E0507) // -EIO +#define STATUS_GRAPHICS_PVP_MIRRORING_DEVICES_NOT_SUPPORTED cpu_to_le32(0xC01E0508) // -EIO +#define STATUS_GRAPHICS_OPM_INVALID_POINTER cpu_to_le32(0xC01E050A) // -EIO +#define STATUS_GRAPHICS_OPM_INTERNAL_ERROR cpu_to_le32(0xC01E050B) // -EIO +#define STATUS_GRAPHICS_OPM_INVALID_HANDLE cpu_to_le32(0xC01E050C) // -EIO +#define STATUS_GRAPHICS_PVP_NO_MONITORS_CORRESPOND_TO_DISPLAY_DEVICE cpu_to_le32(0xC01E050D) // -EIO +#define STATUS_GRAPHICS_PVP_INVALID_CERTIFICATE_LENGTH cpu_to_le32(0xC01E050E) // -EIO +#define STATUS_GRAPHICS_OPM_SPANNING_MODE_ENABLED cpu_to_le32(0xC01E050F) // -EIO +#define STATUS_GRAPHICS_OPM_THEATER_MODE_ENABLED cpu_to_le32(0xC01E0510) // -EIO +#define STATUS_GRAPHICS_PVP_HFS_FAILED cpu_to_le32(0xC01E0511) // -EIO +#define STATUS_GRAPHICS_OPM_INVALID_SRM cpu_to_le32(0xC01E0512) // -EIO +#define STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_HDCP cpu_to_le32(0xC01E0513) // -EIO +#define STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_ACP cpu_to_le32(0xC01E0514) // -EIO +#define STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_CGMSA cpu_to_le32(0xC01E0515) // -EIO +#define STATUS_GRAPHICS_OPM_HDCP_SRM_NEVER_SET cpu_to_le32(0xC01E0516) // -EIO +#define STATUS_GRAPHICS_OPM_RESOLUTION_TOO_HIGH cpu_to_le32(0xC01E0517) // -EIO +#define STATUS_GRAPHICS_OPM_ALL_HDCP_HARDWARE_ALREADY_IN_USE cpu_to_le32(0xC01E0518) // -EIO +#define STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_NO_LONGER_EXISTS cpu_to_le32(0xC01E051A) // -EIO +#define STATUS_GRAPHICS_OPM_SESSION_TYPE_CHANGE_IN_PROGRESS cpu_to_le32(0xC01E051B) // -EIO +#define STATUS_GRAPHICS_I2C_NOT_SUPPORTED cpu_to_le32(0xC01E0580) // -EIO +#define STATUS_GRAPHICS_I2C_DEVICE_DOES_NOT_EXIST cpu_to_le32(0xC01E0581) // -EIO +#define STATUS_GRAPHICS_I2C_ERROR_TRANSMITTING_DATA cpu_to_le32(0xC01E0582) // -EIO +#define STATUS_GRAPHICS_I2C_ERROR_RECEIVING_DATA cpu_to_le32(0xC01E0583) // -EIO +#define STATUS_GRAPHICS_DDCCI_VCP_NOT_SUPPORTED cpu_to_le32(0xC01E0584) // -EIO +#define STATUS_GRAPHICS_DDCCI_INVALID_DATA cpu_to_le32(0xC01E0585) // -EIO +#define STATUS_GRAPHICS_DDCCI_MONITOR_RETURNED_INVALID_TIMING_STATUS_BYTE cpu_to_le32(0xC01E0586) // -EIO +#define STATUS_GRAPHICS_DDCCI_INVALID_CAPABILITIES_STRING cpu_to_le32(0xC01E0587) // -EIO +#define STATUS_GRAPHICS_MCA_INTERNAL_ERROR cpu_to_le32(0xC01E0588) // -EIO +#define STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_COMMAND cpu_to_le32(0xC01E0589) // -EIO +#define STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_LENGTH cpu_to_le32(0xC01E058A) // -EIO +#define STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_CHECKSUM cpu_to_le32(0xC01E058B) // -EIO +#define STATUS_GRAPHICS_INVALID_PHYSICAL_MONITOR_HANDLE cpu_to_le32(0xC01E058C) // -EIO +#define STATUS_GRAPHICS_MONITOR_NO_LONGER_EXISTS cpu_to_le32(0xC01E058D) // -EIO +#define STATUS_GRAPHICS_ONLY_CONSOLE_SESSION_SUPPORTED cpu_to_le32(0xC01E05E0) // -EIO +#define STATUS_GRAPHICS_NO_DISPLAY_DEVICE_CORRESPONDS_TO_NAME cpu_to_le32(0xC01E05E1) // -EIO +#define STATUS_GRAPHICS_DISPLAY_DEVICE_NOT_ATTACHED_TO_DESKTOP cpu_to_le32(0xC01E05E2) // -EIO +#define STATUS_GRAPHICS_MIRRORING_DEVICES_NOT_SUPPORTED cpu_to_le32(0xC01E05E3) // -EIO +#define STATUS_GRAPHICS_INVALID_POINTER cpu_to_le32(0xC01E05E4) // -EIO +#define STATUS_GRAPHICS_NO_MONITORS_CORRESPOND_TO_DISPLAY_DEVICE cpu_to_le32(0xC01E05E5) // -EIO +#define STATUS_GRAPHICS_PARAMETER_ARRAY_TOO_SMALL cpu_to_le32(0xC01E05E6) // -EIO +#define STATUS_GRAPHICS_INTERNAL_ERROR cpu_to_le32(0xC01E05E7) // -EIO +#define STATUS_GRAPHICS_SESSION_TYPE_CHANGE_IN_PROGRESS cpu_to_le32(0xC01E05E8) // -EIO +#define STATUS_FVE_LOCKED_VOLUME cpu_to_le32(0xC0210000) // -EIO +#define STATUS_FVE_NOT_ENCRYPTED cpu_to_le32(0xC0210001) // -EIO +#define STATUS_FVE_BAD_INFORMATION cpu_to_le32(0xC0210002) // -EIO +#define STATUS_FVE_TOO_SMALL cpu_to_le32(0xC0210003) // -EIO +#define STATUS_FVE_FAILED_WRONG_FS cpu_to_le32(0xC0210004) // -EIO +#define STATUS_FVE_FAILED_BAD_FS cpu_to_le32(0xC0210005) // -EIO +#define STATUS_FVE_FS_NOT_EXTENDED cpu_to_le32(0xC0210006) // -EIO +#define STATUS_FVE_FS_MOUNTED cpu_to_le32(0xC0210007) // -EIO +#define STATUS_FVE_NO_LICENSE cpu_to_le32(0xC0210008) // -EIO +#define STATUS_FVE_ACTION_NOT_ALLOWED cpu_to_le32(0xC0210009) // -EIO +#define STATUS_FVE_BAD_DATA cpu_to_le32(0xC021000A) // -EIO +#define STATUS_FVE_VOLUME_NOT_BOUND cpu_to_le32(0xC021000B) // -EIO +#define STATUS_FVE_NOT_DATA_VOLUME cpu_to_le32(0xC021000C) // -EIO +#define STATUS_FVE_CONV_READ_ERROR cpu_to_le32(0xC021000D) // -EIO +#define STATUS_FVE_CONV_WRITE_ERROR cpu_to_le32(0xC021000E) // -EIO +#define STATUS_FVE_OVERLAPPED_UPDATE cpu_to_le32(0xC021000F) // -EIO +#define STATUS_FVE_FAILED_SECTOR_SIZE cpu_to_le32(0xC0210010) // -EIO +#define STATUS_FVE_FAILED_AUTHENTICATION cpu_to_le32(0xC0210011) // -EIO +#define STATUS_FVE_NOT_OS_VOLUME cpu_to_le32(0xC0210012) // -EIO +#define STATUS_FVE_KEYFILE_NOT_FOUND cpu_to_le32(0xC0210013) // -EIO +#define STATUS_FVE_KEYFILE_INVALID cpu_to_le32(0xC0210014) // -EIO +#define STATUS_FVE_KEYFILE_NO_VMK cpu_to_le32(0xC0210015) // -EIO +#define STATUS_FVE_TPM_DISABLED cpu_to_le32(0xC0210016) // -EIO +#define STATUS_FVE_TPM_SRK_AUTH_NOT_ZERO cpu_to_le32(0xC0210017) // -EIO +#define STATUS_FVE_TPM_INVALID_PCR cpu_to_le32(0xC0210018) // -EIO +#define STATUS_FVE_TPM_NO_VMK cpu_to_le32(0xC0210019) // -EIO +#define STATUS_FVE_PIN_INVALID cpu_to_le32(0xC021001A) // -EIO +#define STATUS_FVE_AUTH_INVALID_APPLICATION cpu_to_le32(0xC021001B) // -EIO +#define STATUS_FVE_AUTH_INVALID_CONFIG cpu_to_le32(0xC021001C) // -EIO +#define STATUS_FVE_DEBUGGER_ENABLED cpu_to_le32(0xC021001D) // -EIO +#define STATUS_FVE_DRY_RUN_FAILED cpu_to_le32(0xC021001E) // -EIO +#define STATUS_FVE_BAD_METADATA_POINTER cpu_to_le32(0xC021001F) // -EIO +#define STATUS_FVE_OLD_METADATA_COPY cpu_to_le32(0xC0210020) // -EIO +#define STATUS_FVE_REBOOT_REQUIRED cpu_to_le32(0xC0210021) // -EIO +#define STATUS_FVE_RAW_ACCESS cpu_to_le32(0xC0210022) // -EIO +#define STATUS_FVE_RAW_BLOCKED cpu_to_le32(0xC0210023) // -EIO +#define STATUS_FWP_CALLOUT_NOT_FOUND cpu_to_le32(0xC0220001) // -EIO +#define STATUS_FWP_CONDITION_NOT_FOUND cpu_to_le32(0xC0220002) // -EIO +#define STATUS_FWP_FILTER_NOT_FOUND cpu_to_le32(0xC0220003) // -EIO +#define STATUS_FWP_LAYER_NOT_FOUND cpu_to_le32(0xC0220004) // -EIO +#define STATUS_FWP_PROVIDER_NOT_FOUND cpu_to_le32(0xC0220005) // -EIO +#define STATUS_FWP_PROVIDER_CONTEXT_NOT_FOUND cpu_to_le32(0xC0220006) // -EIO +#define STATUS_FWP_SUBLAYER_NOT_FOUND cpu_to_le32(0xC0220007) // -EIO +#define STATUS_FWP_NOT_FOUND cpu_to_le32(0xC0220008) // -EIO +#define STATUS_FWP_ALREADY_EXISTS cpu_to_le32(0xC0220009) // -EIO +#define STATUS_FWP_IN_USE cpu_to_le32(0xC022000A) // -EIO +#define STATUS_FWP_DYNAMIC_SESSION_IN_PROGRESS cpu_to_le32(0xC022000B) // -EIO +#define STATUS_FWP_WRONG_SESSION cpu_to_le32(0xC022000C) // -EIO +#define STATUS_FWP_NO_TXN_IN_PROGRESS cpu_to_le32(0xC022000D) // -EIO +#define STATUS_FWP_TXN_IN_PROGRESS cpu_to_le32(0xC022000E) // -EIO +#define STATUS_FWP_TXN_ABORTED cpu_to_le32(0xC022000F) // -EIO +#define STATUS_FWP_SESSION_ABORTED cpu_to_le32(0xC0220010) // -EIO +#define STATUS_FWP_INCOMPATIBLE_TXN cpu_to_le32(0xC0220011) // -EIO +#define STATUS_FWP_TIMEOUT cpu_to_le32(0xC0220012) // -ETIMEDOUT +#define STATUS_FWP_NET_EVENTS_DISABLED cpu_to_le32(0xC0220013) // -EIO +#define STATUS_FWP_INCOMPATIBLE_LAYER cpu_to_le32(0xC0220014) // -EIO +#define STATUS_FWP_KM_CLIENTS_ONLY cpu_to_le32(0xC0220015) // -EIO +#define STATUS_FWP_LIFETIME_MISMATCH cpu_to_le32(0xC0220016) // -EIO +#define STATUS_FWP_BUILTIN_OBJECT cpu_to_le32(0xC0220017) // -EIO +#define STATUS_FWP_TOO_MANY_BOOTTIME_FILTERS cpu_to_le32(0xC0220018) // -EIO +#define STATUS_FWP_TOO_MANY_CALLOUTS cpu_to_le32(0xC0220018) // -EIO +#define STATUS_FWP_NOTIFICATION_DROPPED cpu_to_le32(0xC0220019) // -EIO +#define STATUS_FWP_TRAFFIC_MISMATCH cpu_to_le32(0xC022001A) // -EIO +#define STATUS_FWP_INCOMPATIBLE_SA_STATE cpu_to_le32(0xC022001B) // -EIO +#define STATUS_FWP_NULL_POINTER cpu_to_le32(0xC022001C) // -EIO +#define STATUS_FWP_INVALID_ENUMERATOR cpu_to_le32(0xC022001D) // -EIO +#define STATUS_FWP_INVALID_FLAGS cpu_to_le32(0xC022001E) // -EIO +#define STATUS_FWP_INVALID_NET_MASK cpu_to_le32(0xC022001F) // -EIO +#define STATUS_FWP_INVALID_RANGE cpu_to_le32(0xC0220020) // -EIO +#define STATUS_FWP_INVALID_INTERVAL cpu_to_le32(0xC0220021) // -EIO +#define STATUS_FWP_ZERO_LENGTH_ARRAY cpu_to_le32(0xC0220022) // -EIO +#define STATUS_FWP_NULL_DISPLAY_NAME cpu_to_le32(0xC0220023) // -EIO +#define STATUS_FWP_INVALID_ACTION_TYPE cpu_to_le32(0xC0220024) // -EIO +#define STATUS_FWP_INVALID_WEIGHT cpu_to_le32(0xC0220025) // -EIO +#define STATUS_FWP_MATCH_TYPE_MISMATCH cpu_to_le32(0xC0220026) // -EIO +#define STATUS_FWP_TYPE_MISMATCH cpu_to_le32(0xC0220027) // -EIO +#define STATUS_FWP_OUT_OF_BOUNDS cpu_to_le32(0xC0220028) // -EIO +#define STATUS_FWP_RESERVED cpu_to_le32(0xC0220029) // -EIO +#define STATUS_FWP_DUPLICATE_CONDITION cpu_to_le32(0xC022002A) // -EIO +#define STATUS_FWP_DUPLICATE_KEYMOD cpu_to_le32(0xC022002B) // -EIO +#define STATUS_FWP_ACTION_INCOMPATIBLE_WITH_LAYER cpu_to_le32(0xC022002C) // -EIO +#define STATUS_FWP_ACTION_INCOMPATIBLE_WITH_SUBLAYER cpu_to_le32(0xC022002D) // -EIO +#define STATUS_FWP_CONTEXT_INCOMPATIBLE_WITH_LAYER cpu_to_le32(0xC022002E) // -EIO +#define STATUS_FWP_CONTEXT_INCOMPATIBLE_WITH_CALLOUT cpu_to_le32(0xC022002F) // -EIO +#define STATUS_FWP_INCOMPATIBLE_AUTH_METHOD cpu_to_le32(0xC0220030) // -EIO +#define STATUS_FWP_INCOMPATIBLE_DH_GROUP cpu_to_le32(0xC0220031) // -EIO +#define STATUS_FWP_EM_NOT_SUPPORTED cpu_to_le32(0xC0220032) // -EOPNOTSUPP +#define STATUS_FWP_NEVER_MATCH cpu_to_le32(0xC0220033) // -EIO +#define STATUS_FWP_PROVIDER_CONTEXT_MISMATCH cpu_to_le32(0xC0220034) // -EIO +#define STATUS_FWP_INVALID_PARAMETER cpu_to_le32(0xC0220035) // -EIO +#define STATUS_FWP_TOO_MANY_SUBLAYERS cpu_to_le32(0xC0220036) // -EIO +#define STATUS_FWP_CALLOUT_NOTIFICATION_FAILED cpu_to_le32(0xC0220037) // -EIO +#define STATUS_FWP_INCOMPATIBLE_AUTH_CONFIG cpu_to_le32(0xC0220038) // -EIO +#define STATUS_FWP_INCOMPATIBLE_CIPHER_CONFIG cpu_to_le32(0xC0220039) // -EIO +#define STATUS_FWP_TCPIP_NOT_READY cpu_to_le32(0xC0220100) // -EIO +#define STATUS_FWP_INJECT_HANDLE_CLOSING cpu_to_le32(0xC0220101) // -EIO +#define STATUS_FWP_INJECT_HANDLE_STALE cpu_to_le32(0xC0220102) // -EIO +#define STATUS_FWP_CANNOT_PEND cpu_to_le32(0xC0220103) // -EIO +#define STATUS_NDIS_CLOSING cpu_to_le32(0xC0230002) // -EIO +#define STATUS_NDIS_BAD_VERSION cpu_to_le32(0xC0230004) // -EIO +#define STATUS_NDIS_BAD_CHARACTERISTICS cpu_to_le32(0xC0230005) // -EIO +#define STATUS_NDIS_ADAPTER_NOT_FOUND cpu_to_le32(0xC0230006) // -EIO +#define STATUS_NDIS_OPEN_FAILED cpu_to_le32(0xC0230007) // -EIO +#define STATUS_NDIS_DEVICE_FAILED cpu_to_le32(0xC0230008) // -EIO +#define STATUS_NDIS_MULTICAST_FULL cpu_to_le32(0xC0230009) // -EIO +#define STATUS_NDIS_MULTICAST_EXISTS cpu_to_le32(0xC023000A) // -EIO +#define STATUS_NDIS_MULTICAST_NOT_FOUND cpu_to_le32(0xC023000B) // -EIO +#define STATUS_NDIS_REQUEST_ABORTED cpu_to_le32(0xC023000C) // -EIO +#define STATUS_NDIS_RESET_IN_PROGRESS cpu_to_le32(0xC023000D) // -EIO +#define STATUS_NDIS_INVALID_PACKET cpu_to_le32(0xC023000F) // -EIO +#define STATUS_NDIS_INVALID_DEVICE_REQUEST cpu_to_le32(0xC0230010) // -EIO +#define STATUS_NDIS_ADAPTER_NOT_READY cpu_to_le32(0xC0230011) // -EIO +#define STATUS_NDIS_INVALID_LENGTH cpu_to_le32(0xC0230014) // -EIO +#define STATUS_NDIS_INVALID_DATA cpu_to_le32(0xC0230015) // -EIO +#define STATUS_NDIS_BUFFER_TOO_SHORT cpu_to_le32(0xC0230016) // -ENOBUFS +#define STATUS_NDIS_INVALID_OID cpu_to_le32(0xC0230017) // -EIO +#define STATUS_NDIS_ADAPTER_REMOVED cpu_to_le32(0xC0230018) // -EIO +#define STATUS_NDIS_UNSUPPORTED_MEDIA cpu_to_le32(0xC0230019) // -EIO +#define STATUS_NDIS_GROUP_ADDRESS_IN_USE cpu_to_le32(0xC023001A) // -EIO +#define STATUS_NDIS_FILE_NOT_FOUND cpu_to_le32(0xC023001B) // -EIO +#define STATUS_NDIS_ERROR_READING_FILE cpu_to_le32(0xC023001C) // -EIO +#define STATUS_NDIS_ALREADY_MAPPED cpu_to_le32(0xC023001D) // -EIO +#define STATUS_NDIS_RESOURCE_CONFLICT cpu_to_le32(0xC023001E) // -EIO +#define STATUS_NDIS_MEDIA_DISCONNECTED cpu_to_le32(0xC023001F) // -EIO +#define STATUS_NDIS_INVALID_ADDRESS cpu_to_le32(0xC0230022) // -EIO +#define STATUS_NDIS_PAUSED cpu_to_le32(0xC023002A) // -EIO +#define STATUS_NDIS_INTERFACE_NOT_FOUND cpu_to_le32(0xC023002B) // -EIO +#define STATUS_NDIS_UNSUPPORTED_REVISION cpu_to_le32(0xC023002C) // -EIO +#define STATUS_NDIS_INVALID_PORT cpu_to_le32(0xC023002D) // -EIO +#define STATUS_NDIS_INVALID_PORT_STATE cpu_to_le32(0xC023002E) // -EIO +#define STATUS_NDIS_LOW_POWER_STATE cpu_to_le32(0xC023002F) // -EIO +#define STATUS_NDIS_NOT_SUPPORTED cpu_to_le32(0xC02300BB) // -ENOSYS +#define STATUS_NDIS_DOT11_AUTO_CONFIG_ENABLED cpu_to_le32(0xC0232000) // -EIO +#define STATUS_NDIS_DOT11_MEDIA_IN_USE cpu_to_le32(0xC0232001) // -EIO +#define STATUS_NDIS_DOT11_POWER_STATE_INVALID cpu_to_le32(0xC0232002) // -EIO +#define STATUS_IPSEC_BAD_SPI cpu_to_le32(0xC0360001) // -EIO +#define STATUS_IPSEC_SA_LIFETIME_EXPIRED cpu_to_le32(0xC0360002) // -EIO +#define STATUS_IPSEC_WRONG_SA cpu_to_le32(0xC0360003) // -EIO +#define STATUS_IPSEC_REPLAY_CHECK_FAILED cpu_to_le32(0xC0360004) // -EIO +#define STATUS_IPSEC_INVALID_PACKET cpu_to_le32(0xC0360005) // -EIO +#define STATUS_IPSEC_INTEGRITY_CHECK_FAILED cpu_to_le32(0xC0360006) // -EIO +#define STATUS_IPSEC_CLEAR_TEXT_DROP cpu_to_le32(0xC0360007) // -EIO /* See MS-SMB2 3.3.5.4 */ -#define STATUS_SMB_NO_PREAUTH_INTEGRITY_HASH_OVERLAP cpu_to_le32(0xC05D0000) +#define STATUS_SMB_NO_PREAUTH_INTEGRITY_HASH_OVERLAP cpu_to_le32(0xC05D0000) // -EIO + +#endif /* FS_SMB_COMMON_SMB2STATUS_H */ diff --git a/fs/smb/common/smbdirect/smbdirect_socket.h b/fs/smb/common/smbdirect/smbdirect_socket.h index ee4c2726771a..95265192bb01 100644 --- a/fs/smb/common/smbdirect/smbdirect_socket.h +++ b/fs/smb/common/smbdirect/smbdirect_socket.h @@ -163,6 +163,17 @@ struct smbdirect_socket { } mem; /* + * This is a coordination for smbdirect_send_batch. + * + * There's only one possible credit, which means + * only one instance is running at a time. + */ + struct { + atomic_t count; + wait_queue_head_t wait_queue; + } bcredits; + + /* * The local credit state for ib_post_send() */ struct { @@ -239,6 +250,7 @@ struct smbdirect_socket { */ struct { u16 target; + atomic_t available; atomic_t count; } credits; @@ -370,6 +382,9 @@ static __always_inline void smbdirect_socket_init(struct smbdirect_socket *sc) INIT_DELAYED_WORK(&sc->idle.timer_work, __smbdirect_socket_disabled_work); disable_delayed_work_sync(&sc->idle.timer_work); + atomic_set(&sc->send_io.bcredits.count, 0); + init_waitqueue_head(&sc->send_io.bcredits.wait_queue); + atomic_set(&sc->send_io.lcredits.count, 0); init_waitqueue_head(&sc->send_io.lcredits.wait_queue); @@ -387,6 +402,7 @@ static __always_inline void smbdirect_socket_init(struct smbdirect_socket *sc) INIT_WORK(&sc->recv_io.posted.refill_work, __smbdirect_socket_disabled_work); disable_work_sync(&sc->recv_io.posted.refill_work); + atomic_set(&sc->recv_io.credits.available, 0); atomic_set(&sc->recv_io.credits.count, 0); INIT_LIST_HEAD(&sc->recv_io.reassembly.list); @@ -483,6 +499,8 @@ struct smbdirect_send_batch { */ bool need_invalidate_rkey; u32 remote_key; + + int credit; }; struct smbdirect_recv_io { diff --git a/fs/smb/server/Makefile b/fs/smb/server/Makefile index 7d6337a7dee4..6407ba6b9340 100644 --- a/fs/smb/server/Makefile +++ b/fs/smb/server/Makefile @@ -18,3 +18,4 @@ $(obj)/ksmbd_spnego_negtokeninit.asn1.o: $(obj)/ksmbd_spnego_negtokeninit.asn1.c $(obj)/ksmbd_spnego_negtokentarg.asn1.o: $(obj)/ksmbd_spnego_negtokentarg.asn1.c $(obj)/ksmbd_spnego_negtokentarg.asn1.h ksmbd-$(CONFIG_SMB_SERVER_SMBDIRECT) += transport_rdma.o +ksmbd-$(CONFIG_PROC_FS) += proc.o diff --git a/fs/smb/server/connection.c b/fs/smb/server/connection.c index 6cac48c8fbe8..e7e3e77006b1 100644 --- a/fs/smb/server/connection.c +++ b/fs/smb/server/connection.c @@ -14,6 +14,7 @@ #include "connection.h" #include "transport_tcp.h" #include "transport_rdma.h" +#include "misc.h" static DEFINE_MUTEX(init_lock); @@ -22,6 +23,62 @@ static struct ksmbd_conn_ops default_conn_ops; DEFINE_HASHTABLE(conn_list, CONN_HASH_BITS); DECLARE_RWSEM(conn_list_lock); +#ifdef CONFIG_PROC_FS +static struct proc_dir_entry *proc_clients; + +static int proc_show_clients(struct seq_file *m, void *v) +{ + struct ksmbd_conn *conn; + struct timespec64 now, t; + int i; + + seq_printf(m, "#%-20s %-10s %-10s %-10s %-10s %-10s\n", + "<name>", "<dialect>", "<credits>", "<open files>", + "<requests>", "<last active>"); + + down_read(&conn_list_lock); + hash_for_each(conn_list, i, conn, hlist) { + jiffies_to_timespec64(jiffies - conn->last_active, &t); + ktime_get_real_ts64(&now); + t = timespec64_sub(now, t); +#if IS_ENABLED(CONFIG_IPV6) + if (!conn->inet_addr) + seq_printf(m, "%-20pI6c", &conn->inet6_addr); + else +#endif + seq_printf(m, "%-20pI4", &conn->inet_addr); + seq_printf(m, " 0x%-10x %-10u %-12d %-10d %ptT\n", + conn->dialect, + conn->total_credits, + atomic_read(&conn->stats.open_files_count), + atomic_read(&conn->req_running), + &t); + } + up_read(&conn_list_lock); + return 0; +} + +static int create_proc_clients(void) +{ + proc_clients = ksmbd_proc_create("clients", + proc_show_clients, NULL); + if (!proc_clients) + return -ENOMEM; + return 0; +} + +static void delete_proc_clients(void) +{ + if (proc_clients) { + proc_remove(proc_clients); + proc_clients = NULL; + } +} +#else +static int create_proc_clients(void) { return 0; } +static void delete_proc_clients(void) {} +#endif + /** * ksmbd_conn_free() - free resources of the connection instance * @@ -472,6 +529,7 @@ int ksmbd_conn_transport_init(void) } out: mutex_unlock(&init_lock); + create_proc_clients(); return ret; } @@ -502,6 +560,7 @@ again: void ksmbd_conn_transport_destroy(void) { + delete_proc_clients(); mutex_lock(&init_lock); ksmbd_tcp_destroy(); ksmbd_rdma_stop_listening(); diff --git a/fs/smb/server/connection.h b/fs/smb/server/connection.h index 7f9bcd9817b5..1e2587036bca 100644 --- a/fs/smb/server/connection.h +++ b/fs/smb/server/connection.h @@ -7,6 +7,7 @@ #define __KSMBD_CONNECTION_H__ #include <linux/list.h> +#include <linux/inet.h> #include <linux/ip.h> #include <net/sock.h> #include <net/tcp.h> @@ -33,7 +34,7 @@ enum { KSMBD_SESS_RELEASING }; -struct ksmbd_stats { +struct ksmbd_conn_stats { atomic_t open_files_count; atomic64_t request_served; }; @@ -78,7 +79,7 @@ struct ksmbd_conn { struct list_head requests; struct list_head async_requests; int connection_type; - struct ksmbd_stats stats; + struct ksmbd_conn_stats stats; char ClientGUID[SMB2_CLIENT_GUID_SIZE]; struct ntlmssp_auth ntlmssp; diff --git a/fs/smb/server/mgmt/tree_connect.c b/fs/smb/server/mgmt/tree_connect.c index d3483d9c757c..57dd47ef688c 100644 --- a/fs/smb/server/mgmt/tree_connect.c +++ b/fs/smb/server/mgmt/tree_connect.c @@ -9,6 +9,7 @@ #include "../transport_ipc.h" #include "../connection.h" +#include "../stats.h" #include "tree_connect.h" #include "user_config.h" @@ -79,12 +80,15 @@ ksmbd_tree_conn_connect(struct ksmbd_work *work, const char *share_name) status.tree_conn = tree_conn; atomic_set(&tree_conn->refcount, 1); + down_write(&sess->tree_conns_lock); ret = xa_err(xa_store(&sess->tree_conns, tree_conn->id, tree_conn, KSMBD_DEFAULT_GFP)); + up_write(&sess->tree_conns_lock); if (ret) { status.ret = -ENOMEM; goto out_error; } + ksmbd_counter_inc(KSMBD_COUNTER_TREE_CONNS); kvfree(resp); return status; @@ -103,29 +107,36 @@ void ksmbd_tree_connect_put(struct ksmbd_tree_connect *tcon) kfree(tcon); } -int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess, - struct ksmbd_tree_connect *tree_conn) +static int __ksmbd_tree_conn_disconnect(struct ksmbd_session *sess, + struct ksmbd_tree_connect *tree_conn) { int ret; - write_lock(&sess->tree_conns_lock); - xa_erase(&sess->tree_conns, tree_conn->id); - write_unlock(&sess->tree_conns_lock); - 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)) kfree(tree_conn); return ret; } +int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess, + struct ksmbd_tree_connect *tree_conn) +{ + down_write(&sess->tree_conns_lock); + xa_erase(&sess->tree_conns, tree_conn->id); + up_write(&sess->tree_conns_lock); + + return __ksmbd_tree_conn_disconnect(sess, tree_conn); +} + struct ksmbd_tree_connect *ksmbd_tree_conn_lookup(struct ksmbd_session *sess, unsigned int id) { struct ksmbd_tree_connect *tcon; - read_lock(&sess->tree_conns_lock); + down_read(&sess->tree_conns_lock); tcon = xa_load(&sess->tree_conns, id); if (tcon) { if (tcon->t_state != TREE_CONNECTED) @@ -133,7 +144,7 @@ struct ksmbd_tree_connect *ksmbd_tree_conn_lookup(struct ksmbd_session *sess, else if (!atomic_inc_not_zero(&tcon->refcount)) tcon = NULL; } - read_unlock(&sess->tree_conns_lock); + up_read(&sess->tree_conns_lock); return tcon; } @@ -147,18 +158,19 @@ int ksmbd_tree_conn_session_logoff(struct ksmbd_session *sess) if (!sess) return -EINVAL; + down_write(&sess->tree_conns_lock); xa_for_each(&sess->tree_conns, id, tc) { - write_lock(&sess->tree_conns_lock); if (tc->t_state == TREE_DISCONNECTED) { - write_unlock(&sess->tree_conns_lock); ret = -ENOENT; continue; } tc->t_state = TREE_DISCONNECTED; - write_unlock(&sess->tree_conns_lock); - ret |= ksmbd_tree_conn_disconnect(sess, tc); + xa_erase(&sess->tree_conns, tc->id); + ret |= __ksmbd_tree_conn_disconnect(sess, tc); } xa_destroy(&sess->tree_conns); + up_write(&sess->tree_conns_lock); + return ret; } diff --git a/fs/smb/server/mgmt/user_config.c b/fs/smb/server/mgmt/user_config.c index 56c9a38ca878..3267b86b8c16 100644 --- a/fs/smb/server/mgmt/user_config.c +++ b/fs/smb/server/mgmt/user_config.c @@ -90,11 +90,9 @@ void ksmbd_free_user(struct ksmbd_user *user) kfree(user); } -int ksmbd_anonymous_user(struct ksmbd_user *user) +bool ksmbd_anonymous_user(struct ksmbd_user *user) { - if (user->name[0] == '\0') - return 1; - return 0; + return user->name[0] == '\0'; } bool ksmbd_compare_user(struct ksmbd_user *u1, struct ksmbd_user *u2) diff --git a/fs/smb/server/mgmt/user_config.h b/fs/smb/server/mgmt/user_config.h index 8c227b8d4954..cc460b4ff7d3 100644 --- a/fs/smb/server/mgmt/user_config.h +++ b/fs/smb/server/mgmt/user_config.h @@ -65,6 +65,6 @@ struct ksmbd_user *ksmbd_login_user(const char *account); struct ksmbd_user *ksmbd_alloc_user(struct ksmbd_login_response *resp, struct ksmbd_login_response_ext *resp_ext); void ksmbd_free_user(struct ksmbd_user *user); -int ksmbd_anonymous_user(struct ksmbd_user *user); +bool ksmbd_anonymous_user(struct ksmbd_user *user); bool ksmbd_compare_user(struct ksmbd_user *u1, struct ksmbd_user *u2); #endif /* __USER_CONFIG_MANAGEMENT_H__ */ diff --git a/fs/smb/server/mgmt/user_session.c b/fs/smb/server/mgmt/user_session.c index 7d880ff34402..957a12de6a9d 100644 --- a/fs/smb/server/mgmt/user_session.c +++ b/fs/smb/server/mgmt/user_session.c @@ -12,9 +12,12 @@ #include "user_session.h" #include "user_config.h" #include "tree_connect.h" +#include "share_config.h" #include "../transport_ipc.h" #include "../connection.h" #include "../vfs_cache.h" +#include "../misc.h" +#include "../stats.h" static DEFINE_IDA(session_ida); @@ -27,17 +30,236 @@ struct ksmbd_session_rpc { unsigned int method; }; +#ifdef CONFIG_PROC_FS + +static const struct ksmbd_const_name ksmbd_sess_cap_const_names[] = { + {SMB2_GLOBAL_CAP_DFS, "dfs"}, + {SMB2_GLOBAL_CAP_LEASING, "lease"}, + {SMB2_GLOBAL_CAP_LARGE_MTU, "large-mtu"}, + {SMB2_GLOBAL_CAP_MULTI_CHANNEL, "multi-channel"}, + {SMB2_GLOBAL_CAP_PERSISTENT_HANDLES, "persistent-handles"}, + {SMB2_GLOBAL_CAP_DIRECTORY_LEASING, "dir-lease"}, + {SMB2_GLOBAL_CAP_ENCRYPTION, "encryption"} +}; + +static const struct ksmbd_const_name ksmbd_cipher_const_names[] = { + {le16_to_cpu(SMB2_ENCRYPTION_AES128_CCM), "aes128-ccm"}, + {le16_to_cpu(SMB2_ENCRYPTION_AES128_GCM), "aes128-gcm"}, + {le16_to_cpu(SMB2_ENCRYPTION_AES256_CCM), "aes256-ccm"}, + {le16_to_cpu(SMB2_ENCRYPTION_AES256_GCM), "aes256-gcm"}, +}; + +static const struct ksmbd_const_name ksmbd_signing_const_names[] = { + {SIGNING_ALG_HMAC_SHA256, "hmac-sha256"}, + {SIGNING_ALG_AES_CMAC, "aes-cmac"}, + {SIGNING_ALG_AES_GMAC, "aes-gmac"}, +}; + +static const char *session_state_string(struct ksmbd_session *session) +{ + switch (session->state) { + case SMB2_SESSION_VALID: + return "valid"; + case SMB2_SESSION_IN_PROGRESS: + return "progress"; + case SMB2_SESSION_EXPIRED: + return "expired"; + default: + return ""; + } +} + +static const char *session_user_name(struct ksmbd_session *session) +{ + if (user_guest(session->user)) + return "(Guest)"; + else if (ksmbd_anonymous_user(session->user)) + return "(Anonymous)"; + return session->user->name; +} + +static int show_proc_session(struct seq_file *m, void *v) +{ + struct ksmbd_session *sess; + struct ksmbd_tree_connect *tree_conn; + struct ksmbd_share_config *share_conf; + struct channel *chan; + unsigned long id; + int i = 0; + + sess = (struct ksmbd_session *)m->private; + ksmbd_user_session_get(sess); + + i = 0; + down_read(&sess->chann_lock); + xa_for_each(&sess->ksmbd_chann_list, id, chan) { +#if IS_ENABLED(CONFIG_IPV6) + if (chan->conn->inet_addr) + seq_printf(m, "%-20s\t%pI4\n", "client", + &chan->conn->inet_addr); + else + seq_printf(m, "%-20s\t%pI6c\n", "client", + &chan->conn->inet6_addr); +#else + seq_printf(m, "%-20s\t%pI4\n", "client", + &chan->conn->inet_addr); +#endif + seq_printf(m, "%-20s\t%s\n", "user", session_user_name(sess)); + seq_printf(m, "%-20s\t%llu\n", "id", sess->id); + seq_printf(m, "%-20s\t%s\n", "state", + session_state_string(sess)); + + seq_printf(m, "%-20s\t", "capabilities"); + ksmbd_proc_show_flag_names(m, + ksmbd_sess_cap_const_names, + ARRAY_SIZE(ksmbd_sess_cap_const_names), + chan->conn->vals->req_capabilities); + + if (sess->sign) { + seq_printf(m, "%-20s\t", "signing"); + ksmbd_proc_show_const_name(m, "%s\t", + ksmbd_signing_const_names, + ARRAY_SIZE(ksmbd_signing_const_names), + le16_to_cpu(chan->conn->signing_algorithm)); + } else if (sess->enc) { + seq_printf(m, "%-20s\t", "encryption"); + ksmbd_proc_show_const_name(m, "%s\t", + ksmbd_cipher_const_names, + ARRAY_SIZE(ksmbd_cipher_const_names), + le16_to_cpu(chan->conn->cipher_type)); + } + i++; + } + up_read(&sess->chann_lock); + + seq_printf(m, "%-20s\t%d\n", "channels", i); + + i = 0; + down_read(&sess->tree_conns_lock); + xa_for_each(&sess->tree_conns, id, tree_conn) { + share_conf = tree_conn->share_conf; + seq_printf(m, "%-20s\t%s\t%8d", "share", + share_conf->name, tree_conn->id); + if (test_share_config_flag(share_conf, KSMBD_SHARE_FLAG_PIPE)) + seq_printf(m, " %s ", "pipe"); + else + seq_printf(m, " %s ", "disk"); + seq_putc(m, '\n'); + } + up_read(&sess->tree_conns_lock); + + ksmbd_user_session_put(sess); + return 0; +} + +void ksmbd_proc_show_flag_names(struct seq_file *m, + const struct ksmbd_const_name *table, + int count, + unsigned int flags) +{ + int i; + + for (i = 0; i < count; i++) { + if (table[i].const_value & flags) + seq_printf(m, "0x%08x\t", table[i].const_value); + } + seq_putc(m, '\n'); +} + +void ksmbd_proc_show_const_name(struct seq_file *m, + const char *format, + const struct ksmbd_const_name *table, + int count, + unsigned int const_value) +{ + int i; + + for (i = 0; i < count; i++) { + if (table[i].const_value & const_value) + seq_printf(m, format, table[i].name); + } + seq_putc(m, '\n'); +} + +static int create_proc_session(struct ksmbd_session *sess) +{ + char name[30]; + + snprintf(name, sizeof(name), "sessions/%llu", sess->id); + sess->proc_entry = ksmbd_proc_create(name, + show_proc_session, sess); + return 0; +} + +static void delete_proc_session(struct ksmbd_session *sess) +{ + if (sess->proc_entry) + proc_remove(sess->proc_entry); +} + +static int show_proc_sessions(struct seq_file *m, void *v) +{ + struct ksmbd_session *session; + struct channel *chan; + int i; + unsigned long id; + + seq_printf(m, "#%-40s %-15s %-10s %-10s\n", + "<client>", "<user>", "<sess_id>", "<state>"); + + down_read(&sessions_table_lock); + hash_for_each(sessions_table, i, session, hlist) { + down_read(&session->chann_lock); + xa_for_each(&session->ksmbd_chann_list, id, chan) { + down_read(&chan->conn->session_lock); + ksmbd_user_session_get(session); + +#if IS_ENABLED(CONFIG_IPV6) + if (!chan->conn->inet_addr) + seq_printf(m, " %-40pI6c", &chan->conn->inet6_addr); + else +#endif + seq_printf(m, " %-40pI4", &chan->conn->inet_addr); + seq_printf(m, " %-15s %-10llu %-10s\n", + session_user_name(session), + session->id, + session_state_string(session)); + + ksmbd_user_session_put(session); + up_read(&chan->conn->session_lock); + } + up_read(&session->chann_lock); + } + up_read(&sessions_table_lock); + return 0; +} + +int create_proc_sessions(void) +{ + if (!ksmbd_proc_create("sessions/sessions", + show_proc_sessions, NULL)) + return -ENOMEM; + return 0; +} +#else +int create_proc_sessions(void) { return 0; } +static int create_proc_session(struct ksmbd_session *sess) { return 0; } +static void delete_proc_session(struct ksmbd_session *sess) {} +#endif + static void free_channel_list(struct ksmbd_session *sess) { struct channel *chann; unsigned long index; + down_write(&sess->chann_lock); xa_for_each(&sess->ksmbd_chann_list, index, chann) { xa_erase(&sess->ksmbd_chann_list, index); kfree(chann); } xa_destroy(&sess->ksmbd_chann_list); + up_write(&sess->chann_lock); } static void __session_rpc_close(struct ksmbd_session *sess, @@ -159,6 +381,8 @@ void ksmbd_session_destroy(struct ksmbd_session *sess) if (!sess) return; + delete_proc_session(sess); + if (sess->user) ksmbd_free_user(sess->user); @@ -220,7 +444,9 @@ static int ksmbd_chann_del(struct ksmbd_conn *conn, struct ksmbd_session *sess) { struct channel *chann; + down_write(&sess->chann_lock); chann = xa_erase(&sess->ksmbd_chann_list, (long)conn); + up_write(&sess->chann_lock); if (!chann) return -ENOENT; @@ -451,9 +677,10 @@ static struct ksmbd_session *__session_create(int protocol) xa_init(&sess->ksmbd_chann_list); xa_init(&sess->rpc_handle_list); sess->sequence_number = 1; - rwlock_init(&sess->tree_conns_lock); atomic_set(&sess->refcnt, 2); + init_rwsem(&sess->tree_conns_lock); init_rwsem(&sess->rpc_lock); + init_rwsem(&sess->chann_lock); ret = __init_smb2_session(sess); if (ret) @@ -465,6 +692,8 @@ static struct ksmbd_session *__session_create(int protocol) hash_add(sessions_table, &sess->hlist, sess->id); up_write(&sessions_table_lock); + create_proc_session(sess); + ksmbd_counter_inc(KSMBD_COUNTER_SESSIONS); return sess; error: diff --git a/fs/smb/server/mgmt/user_session.h b/fs/smb/server/mgmt/user_session.h index c5749d6ec715..6aebd385be84 100644 --- a/fs/smb/server/mgmt/user_session.h +++ b/fs/smb/server/mgmt/user_session.h @@ -41,7 +41,6 @@ struct ksmbd_session { bool sign; bool enc; - bool is_anonymous; int state; __u8 *Preauth_HashValue; @@ -49,6 +48,7 @@ struct ksmbd_session { char sess_key[CIFS_KEY_SIZE]; struct hlist_node hlist; + struct rw_semaphore chann_lock; struct xarray ksmbd_chann_list; struct xarray tree_conns; struct ida tree_conn_ida; @@ -60,8 +60,11 @@ struct ksmbd_session { struct ksmbd_file_table file_table; unsigned long last_active; - rwlock_t tree_conns_lock; + struct rw_semaphore tree_conns_lock; +#ifdef CONFIG_PROC_FS + struct proc_dir_entry *proc_entry; +#endif atomic_t refcnt; struct rw_semaphore rpc_lock; }; @@ -111,4 +114,5 @@ void ksmbd_session_rpc_close(struct ksmbd_session *sess, int id); int ksmbd_session_rpc_method(struct ksmbd_session *sess, int id); void ksmbd_user_session_get(struct ksmbd_session *sess); void ksmbd_user_session_put(struct ksmbd_session *sess); +int create_proc_sessions(void); #endif /* __USER_SESSION_MANAGEMENT_H__ */ diff --git a/fs/smb/server/misc.h b/fs/smb/server/misc.h index 1facfcd21200..13423696ae8c 100644 --- a/fs/smb/server/misc.h +++ b/fs/smb/server/misc.h @@ -6,6 +6,9 @@ #ifndef __KSMBD_MISC_H__ #define __KSMBD_MISC_H__ +#ifdef CONFIG_PROC_FS +#include <linux/proc_fs.h> +#endif struct ksmbd_share_config; struct nls_table; struct kstat; @@ -34,4 +37,31 @@ char *ksmbd_convert_dir_info_name(struct ksmbd_dir_info *d_info, struct timespec64 ksmbd_NTtimeToUnix(__le64 ntutc); u64 ksmbd_UnixTimeToNT(struct timespec64 t); long long ksmbd_systime(void); + +#ifdef CONFIG_PROC_FS +struct ksmbd_const_name { + unsigned int const_value; + const char *name; +}; + +void ksmbd_proc_init(void); +void ksmbd_proc_cleanup(void); +void ksmbd_proc_reset(void); +struct proc_dir_entry *ksmbd_proc_create(const char *name, + int (*show)(struct seq_file *m, void *v), + void *v); +void ksmbd_proc_show_flag_names(struct seq_file *m, + const struct ksmbd_const_name *table, + int count, + unsigned int flags); +void ksmbd_proc_show_const_name(struct seq_file *m, + const char *format, + const struct ksmbd_const_name *table, + int count, + unsigned int const_value); +#else +static inline void ksmbd_proc_init(void) {} +static inline void ksmbd_proc_cleanup(void) {} +static inline void ksmbd_proc_reset(void) {} +#endif #endif /* __KSMBD_MISC_H__ */ diff --git a/fs/smb/server/proc.c b/fs/smb/server/proc.c new file mode 100644 index 000000000000..101a2cc45a44 --- /dev/null +++ b/fs/smb/server/proc.c @@ -0,0 +1,134 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2025, LG Electronics. + * Author(s): Hyunchul Lee <hyc.lee@gmail.com> + * Copyright (C) 2025, Samsung Electronics. + * Author(s): Vedansh Bhardwaj <v.bhardwaj@samsung.com> + */ + +#include <linux/module.h> +#include <linux/proc_fs.h> +#include <linux/seq_file.h> + +#include "misc.h" +#include "server.h" +#include "stats.h" +#include "smb_common.h" +#include "smb2pdu.h" + +static struct proc_dir_entry *ksmbd_proc_fs; +struct ksmbd_counters ksmbd_counters; + +struct proc_dir_entry *ksmbd_proc_create(const char *name, + int (*show)(struct seq_file *m, void *v), + void *v) +{ + return proc_create_single_data(name, 0400, ksmbd_proc_fs, + show, v); +} + +struct ksmbd_const_smb2_process_req { + unsigned int const_value; + const char *name; +}; + +static const struct ksmbd_const_smb2_process_req smb2_process_req[KSMBD_COUNTER_MAX_REQS] = { + {le16_to_cpu(SMB2_NEGOTIATE), "SMB2_NEGOTIATE"}, + {le16_to_cpu(SMB2_SESSION_SETUP), "SMB2_SESSION_SETUP"}, + {le16_to_cpu(SMB2_LOGOFF), "SMB2_LOGOFF"}, + {le16_to_cpu(SMB2_TREE_CONNECT), "SMB2_TREE_CONNECT"}, + {le16_to_cpu(SMB2_TREE_DISCONNECT), "SMB2_TREE_DISCONNECT"}, + {le16_to_cpu(SMB2_CREATE), "SMB2_CREATE"}, + {le16_to_cpu(SMB2_CLOSE), "SMB2_CLOSE"}, + {le16_to_cpu(SMB2_FLUSH), "SMB2_FLUSH"}, + {le16_to_cpu(SMB2_READ), "SMB2_READ"}, + {le16_to_cpu(SMB2_WRITE), "SMB2_WRITE"}, + {le16_to_cpu(SMB2_LOCK), "SMB2_LOCK"}, + {le16_to_cpu(SMB2_IOCTL), "SMB2_IOCTL"}, + {le16_to_cpu(SMB2_CANCEL), "SMB2_CANCEL"}, + {le16_to_cpu(SMB2_ECHO), "SMB2_ECHO"}, + {le16_to_cpu(SMB2_QUERY_DIRECTORY), "SMB2_QUERY_DIRECTORY"}, + {le16_to_cpu(SMB2_CHANGE_NOTIFY), "SMB2_CHANGE_NOTIFY"}, + {le16_to_cpu(SMB2_QUERY_INFO), "SMB2_QUERY_INFO"}, + {le16_to_cpu(SMB2_SET_INFO), "SMB2_SET_INFO"}, + {le16_to_cpu(SMB2_OPLOCK_BREAK), "SMB2_OPLOCK_BREAK"}, +}; + +static int proc_show_ksmbd_stats(struct seq_file *m, void *v) +{ + int i; + + seq_puts(m, "Server\n"); + seq_printf(m, "name: %s\n", ksmbd_server_string()); + seq_printf(m, "netbios: %s\n", ksmbd_netbios_name()); + seq_printf(m, "work group: %s\n", ksmbd_work_group()); + seq_printf(m, "min protocol: %s\n", ksmbd_get_protocol_string(server_conf.min_protocol)); + seq_printf(m, "max protocol: %s\n", ksmbd_get_protocol_string(server_conf.max_protocol)); + seq_printf(m, "flags: 0x%08x\n", server_conf.flags); + seq_printf(m, "share_fake_fscaps: 0x%08x\n", + server_conf.share_fake_fscaps); + seq_printf(m, "sessions: %lld\n", + ksmbd_counter_sum(KSMBD_COUNTER_SESSIONS)); + seq_printf(m, "tree connects: %lld\n", + ksmbd_counter_sum(KSMBD_COUNTER_TREE_CONNS)); + seq_printf(m, "read bytes: %lld\n", + ksmbd_counter_sum(KSMBD_COUNTER_READ_BYTES)); + seq_printf(m, "written bytes: %lld\n", + ksmbd_counter_sum(KSMBD_COUNTER_WRITE_BYTES)); + + seq_puts(m, "\nSMB2\n"); + for (i = 0; i < KSMBD_COUNTER_MAX_REQS; i++) + seq_printf(m, "%-20s:\t%lld\n", smb2_process_req[i].name, + ksmbd_counter_sum(KSMBD_COUNTER_FIRST_REQ + i)); + return 0; +} + +void ksmbd_proc_cleanup(void) +{ + int i; + + if (!ksmbd_proc_fs) + return; + + proc_remove(ksmbd_proc_fs); + + for (i = 0; i < ARRAY_SIZE(ksmbd_counters.counters); i++) + percpu_counter_destroy(&ksmbd_counters.counters[i]); + + ksmbd_proc_fs = NULL; +} + +void ksmbd_proc_reset(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ksmbd_counters.counters); i++) + percpu_counter_set(&ksmbd_counters.counters[i], 0); +} + +void ksmbd_proc_init(void) +{ + int i; + int retval; + + ksmbd_proc_fs = proc_mkdir("fs/ksmbd", NULL); + if (!ksmbd_proc_fs) + return; + + if (!proc_mkdir_mode("sessions", 0400, ksmbd_proc_fs)) + goto err_out; + + for (i = 0; i < ARRAY_SIZE(ksmbd_counters.counters); i++) { + retval = percpu_counter_init(&ksmbd_counters.counters[i], 0, GFP_KERNEL); + if (retval) + goto err_out; + } + + if (!ksmbd_proc_create("server", proc_show_ksmbd_stats, NULL)) + goto err_out; + + ksmbd_proc_reset(); + return; +err_out: + ksmbd_proc_cleanup(); +} diff --git a/fs/smb/server/server.c b/fs/smb/server/server.c index 554ae90df906..c2c074346da1 100644 --- a/fs/smb/server/server.c +++ b/fs/smb/server/server.c @@ -21,6 +21,8 @@ #include "mgmt/user_session.h" #include "crypto_ctx.h" #include "auth.h" +#include "misc.h" +#include "stats.h" int ksmbd_debug_types; @@ -126,25 +128,27 @@ static int __process_request(struct ksmbd_work *work, struct ksmbd_conn *conn, andx_again: if (command >= conn->max_cmds) { conn->ops->set_rsp_status(work, STATUS_INVALID_PARAMETER); - return SERVER_HANDLER_CONTINUE; + return SERVER_HANDLER_ABORT; } cmds = &conn->cmds[command]; if (!cmds->proc) { ksmbd_debug(SMB, "*** not implemented yet cmd = %x\n", command); conn->ops->set_rsp_status(work, STATUS_NOT_IMPLEMENTED); - return SERVER_HANDLER_CONTINUE; + return SERVER_HANDLER_ABORT; } if (work->sess && conn->ops->is_sign_req(work, command)) { ret = conn->ops->check_sign_req(work); if (!ret) { conn->ops->set_rsp_status(work, STATUS_ACCESS_DENIED); - return SERVER_HANDLER_CONTINUE; + return SERVER_HANDLER_ABORT; } } ret = cmds->proc(work); + if (conn->ops->inc_reqs) + conn->ops->inc_reqs(command); if (ret < 0) ksmbd_debug(CONN, "Failed to process %u [%d]\n", command, ret); @@ -359,6 +363,7 @@ static void server_ctrl_handle_init(struct server_ctrl_struct *ctrl) { int ret; + ksmbd_proc_reset(); ret = ksmbd_conn_transport_init(); if (ret) { server_queue_ctrl_reset_work(); @@ -531,6 +536,7 @@ static int ksmbd_server_shutdown(void) { WRITE_ONCE(server_conf.state, SERVER_STATE_SHUTTING_DOWN); + ksmbd_proc_cleanup(); class_unregister(&ksmbd_control_class); ksmbd_workqueue_destroy(); ksmbd_ipc_release(); @@ -554,6 +560,9 @@ static int __init ksmbd_server_init(void) return ret; } + ksmbd_proc_init(); + create_proc_sessions(); + ksmbd_server_tcp_callbacks_init(); ret = server_conf_init(); diff --git a/fs/smb/server/smb2ops.c b/fs/smb/server/smb2ops.c index edd7eca0714a..c9a32ee096b5 100644 --- a/fs/smb/server/smb2ops.c +++ b/fs/smb/server/smb2ops.c @@ -11,6 +11,7 @@ #include "connection.h" #include "smb_common.h" #include "server.h" +#include "stats.h" static struct smb_version_values smb21_server_values = { .version_string = SMB21_VERSION_STRING, @@ -121,6 +122,7 @@ static struct smb_version_values smb311_server_values = { static struct smb_version_ops smb2_0_server_ops = { .get_cmd_val = get_smb2_cmd_val, + .inc_reqs = ksmbd_counter_inc_reqs, .init_rsp_hdr = init_smb2_rsp_hdr, .set_rsp_status = set_smb2_rsp_status, .allocate_rsp_buf = smb2_allocate_rsp_buf, @@ -134,6 +136,7 @@ static struct smb_version_ops smb2_0_server_ops = { static struct smb_version_ops smb3_0_server_ops = { .get_cmd_val = get_smb2_cmd_val, + .inc_reqs = ksmbd_counter_inc_reqs, .init_rsp_hdr = init_smb2_rsp_hdr, .set_rsp_status = set_smb2_rsp_status, .allocate_rsp_buf = smb2_allocate_rsp_buf, @@ -152,6 +155,7 @@ static struct smb_version_ops smb3_0_server_ops = { static struct smb_version_ops smb3_11_server_ops = { .get_cmd_val = get_smb2_cmd_val, + .inc_reqs = ksmbd_counter_inc_reqs, .init_rsp_hdr = init_smb2_rsp_hdr, .set_rsp_status = set_smb2_rsp_status, .allocate_rsp_buf = smb2_allocate_rsp_buf, diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index 2fcd0d4d1fb0..cbb31efdbaa2 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -39,6 +39,7 @@ #include "mgmt/user_session.h" #include "mgmt/ksmbd_ida.h" #include "ndr.h" +#include "stats.h" #include "transport_tcp.h" static void __wbuf(struct ksmbd_work *work, void **req, void **rsp) @@ -79,7 +80,13 @@ static inline bool check_session_id(struct ksmbd_conn *conn, u64 id) struct channel *lookup_chann_list(struct ksmbd_session *sess, struct ksmbd_conn *conn) { - return xa_load(&sess->ksmbd_chann_list, (long)conn); + struct channel *chann; + + down_read(&sess->chann_lock); + chann = xa_load(&sess->ksmbd_chann_list, (long)conn); + up_read(&sess->chann_lock); + + return chann; } /** @@ -1558,8 +1565,10 @@ binding_session: return -ENOMEM; chann->conn = conn; + down_write(&sess->chann_lock); old = xa_store(&sess->ksmbd_chann_list, (long)conn, chann, KSMBD_DEFAULT_GFP); + up_write(&sess->chann_lock); if (xa_is_err(old)) { kfree(chann); return xa_err(old); @@ -1651,8 +1660,10 @@ binding_session: return -ENOMEM; chann->conn = conn; + down_write(&sess->chann_lock); old = xa_store(&sess->ksmbd_chann_list, (long)conn, chann, KSMBD_DEFAULT_GFP); + up_write(&sess->chann_lock); if (xa_is_err(old)) { kfree(chann); return xa_err(old); @@ -2026,9 +2037,9 @@ int smb2_tree_connect(struct ksmbd_work *work) if (conn->posix_ext_supported) status.tree_conn->posix_extensions = true; - write_lock(&sess->tree_conns_lock); + down_write(&sess->tree_conns_lock); status.tree_conn->t_state = TREE_CONNECTED; - write_unlock(&sess->tree_conns_lock); + up_write(&sess->tree_conns_lock); rsp->StructureSize = cpu_to_le16(16); out_err1: if (server_conf.flags & KSMBD_GLOBAL_FLAG_DURABLE_HANDLE && share && @@ -2182,16 +2193,16 @@ int smb2_tree_disconnect(struct ksmbd_work *work) ksmbd_close_tree_conn_fds(work); - write_lock(&sess->tree_conns_lock); + down_write(&sess->tree_conns_lock); if (tcon->t_state == TREE_DISCONNECTED) { - write_unlock(&sess->tree_conns_lock); + up_write(&sess->tree_conns_lock); rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED; err = -ENOENT; goto err_out; } tcon->t_state = TREE_DISCONNECTED; - write_unlock(&sess->tree_conns_lock); + up_write(&sess->tree_conns_lock); err = ksmbd_tree_conn_disconnect(sess, tcon); if (err) { diff --git a/fs/smb/server/smb_common.c b/fs/smb/server/smb_common.c index 1cd7e738434d..741aabdfcef5 100644 --- a/fs/smb/server/smb_common.c +++ b/fs/smb/server/smb_common.c @@ -98,6 +98,30 @@ inline int ksmbd_max_protocol(void) return SMB311_PROT; } +static const struct { + int version; + const char *string; +} version_strings[] = { +#ifdef CONFIG_SMB_INSECURE_SERVER + {SMB1_PROT, SMB1_VERSION_STRING}, +#endif + {SMB2_PROT, SMB20_VERSION_STRING}, + {SMB21_PROT, SMB21_VERSION_STRING}, + {SMB30_PROT, SMB30_VERSION_STRING}, + {SMB302_PROT, SMB302_VERSION_STRING}, + {SMB311_PROT, SMB311_VERSION_STRING}, +}; + +const char *ksmbd_get_protocol_string(int version) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(version_strings); i++) { + if (version_strings[i].version == version) + return version_strings[i].string; + } + return ""; +} int ksmbd_lookup_protocol_idx(char *str) { int offt = ARRAY_SIZE(smb1_protos) - 1; diff --git a/fs/smb/server/smb_common.h b/fs/smb/server/smb_common.h index ddd6867c50b2..ca7e3610d074 100644 --- a/fs/smb/server/smb_common.h +++ b/fs/smb/server/smb_common.h @@ -143,6 +143,7 @@ struct file_id_both_directory_info { struct smb_version_ops { u16 (*get_cmd_val)(struct ksmbd_work *swork); + void (*inc_reqs)(unsigned int cmd); int (*init_rsp_hdr)(struct ksmbd_work *swork); void (*set_rsp_status)(struct ksmbd_work *swork, __le32 err); int (*allocate_rsp_buf)(struct ksmbd_work *work); @@ -165,6 +166,7 @@ struct smb_version_cmds { int ksmbd_min_protocol(void); int ksmbd_max_protocol(void); +const char *ksmbd_get_protocol_string(int version); int ksmbd_lookup_protocol_idx(char *str); diff --git a/fs/smb/server/stats.h b/fs/smb/server/stats.h new file mode 100644 index 000000000000..b60c30c69077 --- /dev/null +++ b/fs/smb/server/stats.h @@ -0,0 +1,73 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2025, LG Electronics. + * Author(s): Hyunchul Lee <hyc.lee@gmail.com> + * Copyright (C) 2025, Samsung Electronics. + * Author(s): Vedansh Bhardwaj <v.bhardwaj@samsung.com> + */ + +#ifndef __KSMBD_STATS_H__ +#define __KSMBD_STATS_H__ + +#define KSMBD_COUNTER_MAX_REQS 19 + +enum { + KSMBD_COUNTER_SESSIONS = 0, + KSMBD_COUNTER_TREE_CONNS, + KSMBD_COUNTER_REQUESTS, + KSMBD_COUNTER_READ_BYTES, + KSMBD_COUNTER_WRITE_BYTES, + KSMBD_COUNTER_FIRST_REQ, + KSMBD_COUNTER_LAST_REQ = KSMBD_COUNTER_FIRST_REQ + + KSMBD_COUNTER_MAX_REQS - 1, + KSMBD_COUNTER_MAX, +}; + +#ifdef CONFIG_PROC_FS +extern struct ksmbd_counters ksmbd_counters; + +struct ksmbd_counters { + struct percpu_counter counters[KSMBD_COUNTER_MAX]; +}; + +static inline void ksmbd_counter_inc(int type) +{ + percpu_counter_inc(&ksmbd_counters.counters[type]); +} + +static inline void ksmbd_counter_dec(int type) +{ + percpu_counter_dec(&ksmbd_counters.counters[type]); +} + +static inline void ksmbd_counter_add(int type, s64 value) +{ + percpu_counter_add(&ksmbd_counters.counters[type], value); +} + +static inline void ksmbd_counter_sub(int type, s64 value) +{ + percpu_counter_sub(&ksmbd_counters.counters[type], value); +} + +static inline void ksmbd_counter_inc_reqs(unsigned int cmd) +{ + if (cmd < KSMBD_COUNTER_MAX_REQS) + percpu_counter_inc(&ksmbd_counters.counters[KSMBD_COUNTER_FIRST_REQ + cmd]); +} + +static inline s64 ksmbd_counter_sum(int type) +{ + return percpu_counter_sum_positive(&ksmbd_counters.counters[type]); +} +#else + +static inline void ksmbd_counter_inc(int type) {} +static inline void ksmbd_counter_dec(int type) {} +static inline void ksmbd_counter_add(int type, s64 value) {} +static inline void ksmbd_counter_sub(int type, s64 value) {} +static inline void ksmbd_counter_inc_reqs(unsigned int cmd) {} +static inline s64 ksmbd_counter_sum(int type) { return 0; } +#endif + +#endif diff --git a/fs/smb/server/transport_rdma.c b/fs/smb/server/transport_rdma.c index f585359684d4..fb36fb9d5214 100644 --- a/fs/smb/server/transport_rdma.c +++ b/fs/smb/server/transport_rdma.c @@ -61,9 +61,6 @@ * Those may change after a SMB_DIRECT negotiation */ -/* Set 445 port to SMB Direct port by default */ -static int smb_direct_port = SMB_DIRECT_PORT_INFINIBAND; - /* The local peer's maximum number of credits to grant to the peer */ static int smb_direct_receive_credit_max = 255; @@ -73,8 +70,23 @@ static int smb_direct_send_credit_target = 255; /* The maximum single message size can be sent to remote peer */ static int smb_direct_max_send_size = 1364; -/* The maximum fragmented upper-layer payload receive size supported */ -static int smb_direct_max_fragmented_recv_size = 1024 * 1024; +/* + * The maximum fragmented upper-layer payload receive size supported + * + * Assume max_payload_per_credit is + * smb_direct_receive_credit_max - 24 = 1340 + * + * The maximum number would be + * smb_direct_receive_credit_max * max_payload_per_credit + * + * 1340 * 255 = 341700 (0x536C4) + * + * The minimum value from the spec is 131072 (0x20000) + * + * For now we use the logic we used before: + * (1364 * 255) / 2 = 173910 (0x2A756) + */ +static int smb_direct_max_fragmented_recv_size = (1364 * 255) / 2; /* The maximum single-message size which can be received */ static int smb_direct_max_receive_size = 1364; @@ -90,8 +102,9 @@ struct smb_direct_device { }; static struct smb_direct_listener { + int port; struct rdma_cm_id *cm_id; -} smb_direct_listener; +} smb_direct_ib_listener, smb_direct_iw_listener; static struct workqueue_struct *smb_direct_wq; @@ -221,6 +234,7 @@ static void smb_direct_disconnect_wake_up_all(struct smbdirect_socket *sc) * in order to notice the broken connection. */ wake_up_all(&sc->status_wait); + wake_up_all(&sc->send_io.bcredits.wait_queue); wake_up_all(&sc->send_io.lcredits.wait_queue); wake_up_all(&sc->send_io.credits.wait_queue); wake_up_all(&sc->send_io.pending.zero_wait_queue); @@ -644,6 +658,7 @@ static void recv_done(struct ib_cq *cq, struct ib_wc *wc) struct smbdirect_data_transfer *data_transfer = (struct smbdirect_data_transfer *)recvmsg->packet; u32 remaining_data_length, data_offset, data_length; + int current_recv_credits; u16 old_recv_credit_target; if (wc->byte_len < @@ -682,7 +697,7 @@ static void recv_done(struct ib_cq *cq, struct ib_wc *wc) } atomic_dec(&sc->recv_io.posted.count); - atomic_dec(&sc->recv_io.credits.count); + current_recv_credits = atomic_dec_return(&sc->recv_io.credits.count); old_recv_credit_target = sc->recv_io.credits.target; sc->recv_io.credits.target = @@ -702,7 +717,8 @@ static void recv_done(struct ib_cq *cq, struct ib_wc *wc) wake_up(&sc->send_io.credits.wait_queue); if (data_length) { - if (sc->recv_io.credits.target > old_recv_credit_target) + if (current_recv_credits <= (sc->recv_io.credits.target / 4) || + sc->recv_io.credits.target > old_recv_credit_target) queue_work(sc->workqueue, &sc->recv_io.posted.refill_work); enqueue_reassembly(sc, recvmsg, (int)data_length); @@ -1028,6 +1044,17 @@ static void smb_direct_post_recv_credits(struct work_struct *work) } } + atomic_add(credits, &sc->recv_io.credits.available); + + /* + * If the last send credit is waiting for credits + * it can grant we need to wake it up + */ + if (credits && + atomic_read(&sc->send_io.bcredits.count) == 0 && + atomic_read(&sc->send_io.credits.count) == 0) + wake_up(&sc->send_io.credits.wait_queue); + if (credits) queue_work(sc->workqueue, &sc->idle.immediate_work); } @@ -1045,6 +1072,31 @@ static void send_done(struct ib_cq *cq, struct ib_wc *wc) ib_wc_status_msg(wc->status), wc->status, wc->opcode); + if (unlikely(!(sendmsg->wr.send_flags & IB_SEND_SIGNALED))) { + /* + * This happens when smbdirect_send_io is a sibling + * before the final message, it is signaled on + * error anyway, so we need to skip + * smbdirect_connection_free_send_io here, + * otherwise is will destroy the memory + * of the siblings too, which will cause + * use after free problems for the others + * triggered from ib_drain_qp(). + */ + if (wc->status != IB_WC_SUCCESS) + goto skip_free; + + /* + * This should not happen! + * But we better just close the + * connection... + */ + pr_err("unexpected send completion wc->status=%s (%d) wc->opcode=%d\n", + ib_wc_status_msg(wc->status), wc->status, wc->opcode); + smb_direct_disconnect_rdma_connection(sc); + return; + } + /* * Free possible siblings and then the main send_io */ @@ -1058,6 +1110,7 @@ static void send_done(struct ib_cq *cq, struct ib_wc *wc) lcredits += 1; if (wc->status != IB_WC_SUCCESS || wc->opcode != IB_WC_SEND) { +skip_free: pr_err("Send error. status='%s (%d)', opcode=%d\n", ib_wc_status_msg(wc->status), wc->status, wc->opcode); @@ -1074,19 +1127,37 @@ static void send_done(struct ib_cq *cq, struct ib_wc *wc) static int manage_credits_prior_sending(struct smbdirect_socket *sc) { + int missing; + int available; int new_credits; if (atomic_read(&sc->recv_io.credits.count) >= sc->recv_io.credits.target) return 0; - new_credits = atomic_read(&sc->recv_io.posted.count); - if (new_credits == 0) + missing = (int)sc->recv_io.credits.target - atomic_read(&sc->recv_io.credits.count); + available = atomic_xchg(&sc->recv_io.credits.available, 0); + new_credits = (u16)min3(U16_MAX, missing, available); + if (new_credits <= 0) { + /* + * If credits are available, but not granted + * we need to re-add them again. + */ + if (available) + atomic_add(available, &sc->recv_io.credits.available); return 0; + } - new_credits -= atomic_read(&sc->recv_io.credits.count); - if (new_credits <= 0) - return 0; + if (new_credits < available) { + /* + * Readd the remaining available again. + */ + available -= new_credits; + atomic_add(available, &sc->recv_io.credits.available); + } + /* + * Remember we granted the credits + */ atomic_add(new_credits, &sc->recv_io.credits.count); return new_credits; } @@ -1130,6 +1201,7 @@ static void smb_direct_send_ctx_init(struct smbdirect_send_batch *send_ctx, send_ctx->wr_cnt = 0; send_ctx->need_invalidate_rkey = need_invalidate_rkey; send_ctx->remote_key = remote_key; + send_ctx->credit = 0; } static int smb_direct_flush_send_list(struct smbdirect_socket *sc, @@ -1137,10 +1209,10 @@ static int smb_direct_flush_send_list(struct smbdirect_socket *sc, bool is_last) { struct smbdirect_send_io *first, *last; - int ret; + int ret = 0; if (list_empty(&send_ctx->msg_list)) - return 0; + goto release_credit; first = list_first_entry(&send_ctx->msg_list, struct smbdirect_send_io, @@ -1182,6 +1254,13 @@ static int smb_direct_flush_send_list(struct smbdirect_socket *sc, smb_direct_free_sendmsg(sc, last); } +release_credit: + if (is_last && !ret && send_ctx->credit) { + atomic_add(send_ctx->credit, &sc->send_io.bcredits.count); + send_ctx->credit = 0; + wake_up(&sc->send_io.bcredits.wait_queue); + } + return ret; } @@ -1207,6 +1286,25 @@ static int wait_for_credits(struct smbdirect_socket *sc, } while (true); } +static int wait_for_send_bcredit(struct smbdirect_socket *sc, + struct smbdirect_send_batch *send_ctx) +{ + int ret; + + if (send_ctx->credit) + return 0; + + ret = wait_for_credits(sc, + &sc->send_io.bcredits.wait_queue, + &sc->send_io.bcredits.count, + 1); + if (ret) + return ret; + + send_ctx->credit = 1; + return 0; +} + static int wait_for_send_lcredit(struct smbdirect_socket *sc, struct smbdirect_send_batch *send_ctx) { @@ -1256,6 +1354,7 @@ static int calc_rw_credits(struct smbdirect_socket *sc, static int smb_direct_create_header(struct smbdirect_socket *sc, int size, int remaining_data_length, + int new_credits, struct smbdirect_send_io **sendmsg_out) { struct smbdirect_socket_parameters *sp = &sc->parameters; @@ -1271,7 +1370,7 @@ static int smb_direct_create_header(struct smbdirect_socket *sc, /* Fill in the packet header */ packet = (struct smbdirect_data_transfer *)sendmsg->packet; packet->credits_requested = cpu_to_le16(sp->send_credit_target); - packet->credits_granted = cpu_to_le16(manage_credits_prior_sending(sc)); + packet->credits_granted = cpu_to_le16(new_credits); packet->flags = 0; if (manage_keep_alive_before_sending(sc)) @@ -1353,14 +1452,12 @@ static int get_sg_list(void *buf, int size, struct scatterlist *sg_list, int nen static int get_mapped_sg_list(struct ib_device *device, void *buf, int size, struct scatterlist *sg_list, int nentries, - enum dma_data_direction dir) + enum dma_data_direction dir, int *npages) { - int npages; - - npages = get_sg_list(buf, size, sg_list, nentries); - if (npages < 0) + *npages = get_sg_list(buf, size, sg_list, nentries); + if (*npages < 0) return -EINVAL; - return ib_dma_map_sg(device, sg_list, npages, dir); + return ib_dma_map_sg(device, sg_list, *npages, dir); } static int post_sendmsg(struct smbdirect_socket *sc, @@ -1410,6 +1507,17 @@ static int smb_direct_post_send_data(struct smbdirect_socket *sc, struct smbdirect_send_io *msg; int data_length; struct scatterlist sg[SMBDIRECT_SEND_IO_MAX_SGE - 1]; + struct smbdirect_send_batch _send_ctx; + int new_credits; + + if (!send_ctx) { + smb_direct_send_ctx_init(&_send_ctx, false, 0); + send_ctx = &_send_ctx; + } + + ret = wait_for_send_bcredit(sc, send_ctx); + if (ret) + goto bcredit_failed; ret = wait_for_send_lcredit(sc, send_ctx); if (ret) @@ -1419,24 +1527,42 @@ static int smb_direct_post_send_data(struct smbdirect_socket *sc, if (ret) goto credit_failed; + new_credits = manage_credits_prior_sending(sc); + if (new_credits == 0 && + atomic_read(&sc->send_io.credits.count) == 0 && + atomic_read(&sc->recv_io.credits.count) == 0) { + queue_work(sc->workqueue, &sc->recv_io.posted.refill_work); + ret = wait_event_interruptible(sc->send_io.credits.wait_queue, + atomic_read(&sc->send_io.credits.count) >= 1 || + atomic_read(&sc->recv_io.credits.available) >= 1 || + sc->status != SMBDIRECT_SOCKET_CONNECTED); + if (sc->status != SMBDIRECT_SOCKET_CONNECTED) + ret = -ENOTCONN; + if (ret < 0) + goto credit_failed; + + new_credits = manage_credits_prior_sending(sc); + } + data_length = 0; for (i = 0; i < niov; i++) data_length += iov[i].iov_len; ret = smb_direct_create_header(sc, data_length, remaining_data_length, - &msg); + new_credits, &msg); if (ret) goto header_failed; for (i = 0; i < niov; i++) { struct ib_sge *sge; int sg_cnt; + int npages; sg_init_table(sg, SMBDIRECT_SEND_IO_MAX_SGE - 1); sg_cnt = get_mapped_sg_list(sc->ib.dev, iov[i].iov_base, iov[i].iov_len, sg, SMBDIRECT_SEND_IO_MAX_SGE - 1, - DMA_TO_DEVICE); + DMA_TO_DEVICE, &npages); if (sg_cnt <= 0) { pr_err("failed to map buffer\n"); ret = -ENOMEM; @@ -1444,7 +1570,7 @@ static int smb_direct_post_send_data(struct smbdirect_socket *sc, } else if (sg_cnt + msg->num_sge > SMBDIRECT_SEND_IO_MAX_SGE) { pr_err("buffer not fitted into sges\n"); ret = -E2BIG; - ib_dma_unmap_sg(sc->ib.dev, sg, sg_cnt, + ib_dma_unmap_sg(sc->ib.dev, sg, npages, DMA_TO_DEVICE); goto err; } @@ -1461,6 +1587,13 @@ static int smb_direct_post_send_data(struct smbdirect_socket *sc, ret = post_sendmsg(sc, send_ctx, msg); if (ret) goto err; + + if (send_ctx == &_send_ctx) { + ret = smb_direct_flush_send_list(sc, send_ctx, true); + if (ret) + goto err; + } + return 0; err: smb_direct_free_sendmsg(sc, msg); @@ -1469,6 +1602,9 @@ header_failed: credit_failed: atomic_inc(&sc->send_io.lcredits.count); lcredit_failed: + atomic_add(send_ctx->credit, &sc->send_io.bcredits.count); + send_ctx->credit = 0; +bcredit_failed: return ret; } @@ -1940,6 +2076,7 @@ static int smb_direct_send_negotiate_response(struct smbdirect_socket *sc, resp->max_fragmented_size = cpu_to_le32(sp->max_fragmented_recv_size); + atomic_set(&sc->send_io.bcredits.count, 1); sc->recv_io.expected = SMBDIRECT_EXPECT_DATA_TRANSFER; sc->status = SMBDIRECT_SOCKET_CONNECTED; } @@ -2409,6 +2546,29 @@ static int smb_direct_prepare(struct ksmbd_transport *t) le32_to_cpu(req->max_receive_size)); sp->max_fragmented_send_size = le32_to_cpu(req->max_fragmented_size); + /* + * The maximum fragmented upper-layer payload receive size supported + * + * Assume max_payload_per_credit is + * smb_direct_receive_credit_max - 24 = 1340 + * + * The maximum number would be + * smb_direct_receive_credit_max * max_payload_per_credit + * + * 1340 * 255 = 341700 (0x536C4) + * + * The minimum value from the spec is 131072 (0x20000) + * + * For now we use the logic we used before: + * (1364 * 255) / 2 = 173910 (0x2A756) + * + * We need to adjust this here in case the peer + * lowered sp->max_recv_size. + * + * TODO: instead of adjusting max_fragmented_recv_size + * we should adjust the number of available buffers, + * but for now we keep the current logic. + */ sp->max_fragmented_recv_size = (sp->recv_credit_max * sp->max_recv_size) / 2; sc->recv_io.credits.target = le16_to_cpu(req->credits_requested); @@ -2496,6 +2656,7 @@ static bool rdma_frwr_is_supported(struct ib_device_attr *attrs) static int smb_direct_handle_connect_request(struct rdma_cm_id *new_cm_id, struct rdma_cm_event *event) { + struct smb_direct_listener *listener = new_cm_id->context; struct smb_direct_transport *t; struct smbdirect_socket *sc; struct smbdirect_socket_parameters *sp; @@ -2584,7 +2745,7 @@ static int smb_direct_handle_connect_request(struct rdma_cm_id *new_cm_id, handler = kthread_run(ksmbd_conn_handler_loop, KSMBD_TRANS(t)->conn, "ksmbd:r%u", - smb_direct_port); + listener->port); if (IS_ERR(handler)) { ret = PTR_ERR(handler); pr_err("Can't start thread\n"); @@ -2621,39 +2782,73 @@ static int smb_direct_listen_handler(struct rdma_cm_id *cm_id, return 0; } -static int smb_direct_listen(int port) +static int smb_direct_listen(struct smb_direct_listener *listener, + int port) { int ret; struct rdma_cm_id *cm_id; + u8 node_type = RDMA_NODE_UNSPECIFIED; struct sockaddr_in sin = { .sin_family = AF_INET, .sin_addr.s_addr = htonl(INADDR_ANY), .sin_port = htons(port), }; + switch (port) { + case SMB_DIRECT_PORT_IWARP: + /* + * only allow iWarp devices + * for port 5445. + */ + node_type = RDMA_NODE_RNIC; + break; + case SMB_DIRECT_PORT_INFINIBAND: + /* + * only allow InfiniBand, RoCEv1 or RoCEv2 + * devices for port 445. + * + * (Basically don't allow iWarp devices) + */ + node_type = RDMA_NODE_IB_CA; + break; + default: + pr_err("unsupported smbdirect port=%d!\n", port); + return -ENODEV; + } + cm_id = rdma_create_id(&init_net, smb_direct_listen_handler, - &smb_direct_listener, RDMA_PS_TCP, IB_QPT_RC); + listener, RDMA_PS_TCP, IB_QPT_RC); if (IS_ERR(cm_id)) { pr_err("Can't create cm id: %ld\n", PTR_ERR(cm_id)); return PTR_ERR(cm_id); } + ret = rdma_restrict_node_type(cm_id, node_type); + if (ret) { + pr_err("rdma_restrict_node_type(%u) failed %d\n", + node_type, ret); + goto err; + } + ret = rdma_bind_addr(cm_id, (struct sockaddr *)&sin); if (ret) { pr_err("Can't bind: %d\n", ret); goto err; } - smb_direct_listener.cm_id = cm_id; - ret = rdma_listen(cm_id, 10); if (ret) { pr_err("Can't listen: %d\n", ret); goto err; } + + listener->port = port; + listener->cm_id = cm_id; + return 0; err: - smb_direct_listener.cm_id = NULL; + listener->port = 0; + listener->cm_id = NULL; rdma_destroy_id(cm_id); return ret; } @@ -2662,10 +2857,6 @@ static int smb_direct_ib_client_add(struct ib_device *ib_dev) { struct smb_direct_device *smb_dev; - /* Set 5445 port if device type is iWARP(No IB) */ - if (ib_dev->node_type != RDMA_NODE_IB_CA) - smb_direct_port = SMB_DIRECT_PORT_IWARP; - if (!rdma_frwr_is_supported(&ib_dev->attrs)) return 0; @@ -2708,7 +2899,9 @@ int ksmbd_rdma_init(void) { int ret; - smb_direct_listener.cm_id = NULL; + smb_direct_ib_listener = smb_direct_iw_listener = (struct smb_direct_listener) { + .cm_id = NULL, + }; ret = ib_register_client(&smb_direct_ib_client); if (ret) { @@ -2724,31 +2917,53 @@ int ksmbd_rdma_init(void) smb_direct_wq = alloc_workqueue("ksmbd-smb_direct-wq", WQ_HIGHPRI | WQ_MEM_RECLAIM | WQ_PERCPU, 0); - if (!smb_direct_wq) - return -ENOMEM; + if (!smb_direct_wq) { + ret = -ENOMEM; + goto err; + } - ret = smb_direct_listen(smb_direct_port); + ret = smb_direct_listen(&smb_direct_ib_listener, + SMB_DIRECT_PORT_INFINIBAND); if (ret) { - destroy_workqueue(smb_direct_wq); - smb_direct_wq = NULL; - pr_err("Can't listen: %d\n", ret); - return ret; + pr_err("Can't listen on InfiniBand/RoCEv1/RoCEv2: %d\n", ret); + goto err; } - ksmbd_debug(RDMA, "init RDMA listener. cm_id=%p\n", - smb_direct_listener.cm_id); + ksmbd_debug(RDMA, "InfiniBand/RoCEv1/RoCEv2 RDMA listener. cm_id=%p\n", + smb_direct_ib_listener.cm_id); + + ret = smb_direct_listen(&smb_direct_iw_listener, + SMB_DIRECT_PORT_IWARP); + if (ret) { + pr_err("Can't listen on iWarp: %d\n", ret); + goto err; + } + + ksmbd_debug(RDMA, "iWarp RDMA listener. cm_id=%p\n", + smb_direct_iw_listener.cm_id); + return 0; +err: + ksmbd_rdma_stop_listening(); + ksmbd_rdma_destroy(); + return ret; } void ksmbd_rdma_stop_listening(void) { - if (!smb_direct_listener.cm_id) + if (!smb_direct_ib_listener.cm_id && !smb_direct_iw_listener.cm_id) return; ib_unregister_client(&smb_direct_ib_client); - rdma_destroy_id(smb_direct_listener.cm_id); - smb_direct_listener.cm_id = NULL; + if (smb_direct_ib_listener.cm_id) + rdma_destroy_id(smb_direct_ib_listener.cm_id); + if (smb_direct_iw_listener.cm_id) + rdma_destroy_id(smb_direct_iw_listener.cm_id); + + smb_direct_ib_listener = smb_direct_iw_listener = (struct smb_direct_listener) { + .cm_id = NULL, + }; } void ksmbd_rdma_destroy(void) diff --git a/fs/smb/server/transport_tcp.c b/fs/smb/server/transport_tcp.c index 4bb07937d7ef..2436dabada95 100644 --- a/fs/smb/server/transport_tcp.c +++ b/fs/smb/server/transport_tcp.c @@ -40,6 +40,7 @@ static const struct ksmbd_transport_ops ksmbd_tcp_transport_ops; static void tcp_stop_kthread(struct task_struct *kthread); static struct interface *alloc_iface(char *ifname); +static void ksmbd_tcp_disconnect(struct ksmbd_transport *t); #define KSMBD_TRANS(t) (&(t)->transport) #define TCP_TRANS(t) ((struct tcp_transport *)container_of(t, \ @@ -202,7 +203,7 @@ static int ksmbd_tcp_new_connection(struct socket *client_sk) if (IS_ERR(handler)) { pr_err("cannot start conn thread\n"); rc = PTR_ERR(handler); - free_transport(t); + ksmbd_tcp_disconnect(KSMBD_TRANS(t)); } return rc; } diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c index f891344bd76b..fbdc854dba0f 100644 --- a/fs/smb/server/vfs.c +++ b/fs/smb/server/vfs.c @@ -31,6 +31,7 @@ #include "ndr.h" #include "auth.h" #include "misc.h" +#include "stats.h" #include "smb_common.h" #include "mgmt/share_config.h" @@ -54,7 +55,6 @@ static int ksmbd_vfs_path_lookup(struct ksmbd_share_config *share_conf, struct path *path, bool for_remove) { struct qstr last; - struct filename *filename __free(putname) = NULL; const struct path *root_share_path = &share_conf->vfs_path; int err, type; struct dentry *d; @@ -66,10 +66,7 @@ static int ksmbd_vfs_path_lookup(struct ksmbd_share_config *share_conf, flags |= LOOKUP_BENEATH; } - filename = getname_kernel(pathname); - if (IS_ERR(filename)) - return PTR_ERR(filename); - + CLASS(filename_kernel, filename)(pathname); err = vfs_path_parent_lookup(filename, flags, path, &last, &type, root_share_path); @@ -384,6 +381,7 @@ int ksmbd_vfs_read(struct ksmbd_work *work, struct ksmbd_file *fp, size_t count, } filp->f_pos = *pos; + ksmbd_counter_add(KSMBD_COUNTER_READ_BYTES, (s64)nbytes); return nbytes; } @@ -521,6 +519,7 @@ int ksmbd_vfs_write(struct ksmbd_work *work, struct ksmbd_file *fp, pr_err("fsync failed for filename = %pD, err = %d\n", fp->filp, err); } + ksmbd_counter_add(KSMBD_COUNTER_WRITE_BYTES, (s64)*written); out: return err; @@ -667,7 +666,6 @@ int ksmbd_vfs_rename(struct ksmbd_work *work, const struct path *old_path, struct path new_path; struct qstr new_last; struct renamedata rd; - struct filename *to; struct ksmbd_share_config *share_conf = work->tcon->share_conf; struct ksmbd_file *parent_fp; int new_type; @@ -676,11 +674,7 @@ int ksmbd_vfs_rename(struct ksmbd_work *work, const struct path *old_path, if (ksmbd_override_fsids(work)) return -ENOMEM; - to = getname_kernel(newname); - if (IS_ERR(to)) { - err = PTR_ERR(to); - goto revert_fsids; - } + CLASS(filename_kernel, to)(newname); retry: err = vfs_path_parent_lookup(to, lookup_flags | LOOKUP_BENEATH, @@ -739,8 +733,6 @@ out2: goto retry; } out1: - putname(to); -revert_fsids: ksmbd_revert_fsids(work); return err; } @@ -1227,7 +1219,7 @@ int ksmbd_vfs_kern_path(struct ksmbd_work *work, char *filepath, } /** - * ksmbd_vfs_kern_path_start_remove() - lookup a file and get path info prior to removal + * ksmbd_vfs_kern_path_start_removing() - lookup a file and get path info prior to removal * @work: work * @filepath: file path that is relative to share * @flags: lookup flags diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c index 6ef116585af6..e302e403e55a 100644 --- a/fs/smb/server/vfs_cache.c +++ b/fs/smb/server/vfs_cache.c @@ -16,10 +16,12 @@ #include "oplock.h" #include "vfs.h" #include "connection.h" +#include "misc.h" #include "mgmt/tree_connect.h" #include "mgmt/user_session.h" #include "smb_common.h" #include "server.h" +#include "smb2pdu.h" #define S_DEL_PENDING 1 #define S_DEL_ON_CLS 2 @@ -34,6 +36,97 @@ static struct ksmbd_file_table global_ft; static atomic_long_t fd_limit; static struct kmem_cache *filp_cache; +#define OPLOCK_NONE 0 +#define OPLOCK_EXCLUSIVE 1 +#define OPLOCK_BATCH 2 +#define OPLOCK_READ 3 /* level 2 oplock */ + +#ifdef CONFIG_PROC_FS + +static const struct ksmbd_const_name ksmbd_lease_const_names[] = { + {le32_to_cpu(SMB2_LEASE_NONE_LE), "LEASE_NONE"}, + {le32_to_cpu(SMB2_LEASE_READ_CACHING_LE), "LEASE_R"}, + {le32_to_cpu(SMB2_LEASE_HANDLE_CACHING_LE), "LEASE_H"}, + {le32_to_cpu(SMB2_LEASE_WRITE_CACHING_LE), "LEASE_W"}, + {le32_to_cpu(SMB2_LEASE_READ_CACHING_LE | + SMB2_LEASE_HANDLE_CACHING_LE), "LEASE_RH"}, + {le32_to_cpu(SMB2_LEASE_READ_CACHING_LE | + SMB2_LEASE_WRITE_CACHING_LE), "LEASE_RW"}, + {le32_to_cpu(SMB2_LEASE_HANDLE_CACHING_LE | + SMB2_LEASE_WRITE_CACHING_LE), "LEASE_WH"}, + {le32_to_cpu(SMB2_LEASE_READ_CACHING_LE | + SMB2_LEASE_HANDLE_CACHING_LE | + SMB2_LEASE_WRITE_CACHING_LE), "LEASE_RWH"}, +}; + +static const struct ksmbd_const_name ksmbd_oplock_const_names[] = { + {SMB2_OPLOCK_LEVEL_NONE, "OPLOCK_NONE"}, + {SMB2_OPLOCK_LEVEL_II, "OPLOCK_II"}, + {SMB2_OPLOCK_LEVEL_EXCLUSIVE, "OPLOCK_EXECL"}, + {SMB2_OPLOCK_LEVEL_BATCH, "OPLOCK_BATCH"}, +}; + +static int proc_show_files(struct seq_file *m, void *v) +{ + struct ksmbd_file *fp = NULL; + unsigned int id; + struct oplock_info *opinfo; + + seq_printf(m, "#%-10s %-10s %-10s %-10s %-15s %-10s %-10s %s\n", + "<tree id>", "<pid>", "<vid>", "<refcnt>", + "<oplock>", "<daccess>", "<saccess>", + "<name>"); + + read_lock(&global_ft.lock); + idr_for_each_entry(global_ft.idr, fp, id) { + seq_printf(m, "%#-10x %#-10llx %#-10llx %#-10x", + fp->tcon->id, + fp->persistent_id, + fp->volatile_id, + atomic_read(&fp->refcount)); + + rcu_read_lock(); + opinfo = rcu_dereference(fp->f_opinfo); + rcu_read_unlock(); + + if (!opinfo) { + seq_printf(m, " %-15s", " "); + } else { + const struct ksmbd_const_name *const_names; + int count; + unsigned int level; + + if (opinfo->is_lease) { + const_names = ksmbd_lease_const_names; + count = ARRAY_SIZE(ksmbd_lease_const_names); + level = le32_to_cpu(opinfo->o_lease->state); + } else { + const_names = ksmbd_oplock_const_names; + count = ARRAY_SIZE(ksmbd_oplock_const_names); + level = opinfo->level; + } + ksmbd_proc_show_const_name(m, " %-15s", + const_names, count, level); + } + + seq_printf(m, " %#010x %#010x %s\n", + le32_to_cpu(fp->daccess), + le32_to_cpu(fp->saccess), + fp->filp->f_path.dentry->d_name.name); + } + read_unlock(&global_ft.lock); + return 0; +} + +static int create_proc_files(void) +{ + ksmbd_proc_create("files", proc_show_files, NULL); + return 0; +} +#else +static int create_proc_files(void) { return 0; } +#endif + static bool durable_scavenger_running; static DEFINE_MUTEX(durable_scavenger_lock); static wait_queue_head_t dh_wq; @@ -949,6 +1042,7 @@ void ksmbd_close_session_fds(struct ksmbd_work *work) int ksmbd_init_global_file_table(void) { + create_proc_files(); return ksmbd_init_file_table(&global_ft); } diff --git a/fs/splice.c b/fs/splice.c index d338fe56b50b..5fb07c01936f 100644 --- a/fs/splice.c +++ b/fs/splice.c @@ -1467,7 +1467,7 @@ static ssize_t iter_to_pipe(struct iov_iter *from, n = DIV_ROUND_UP(left + start, PAGE_SIZE); for (i = 0; i < n; i++) { - int size = min_t(int, left, PAGE_SIZE - start); + int size = umin(left, PAGE_SIZE - start); buf.page = pages[i]; buf.offset = start; diff --git a/fs/squashfs/dir.c b/fs/squashfs/dir.c index a2ade63eccdf..cd3598bd034f 100644 --- a/fs/squashfs/dir.c +++ b/fs/squashfs/dir.c @@ -15,6 +15,7 @@ */ #include <linux/fs.h> +#include <linux/filelock.h> #include <linux/vfs.h> #include <linux/slab.h> @@ -220,4 +221,5 @@ const struct file_operations squashfs_dir_ops = { .read = generic_read_dir, .iterate_shared = squashfs_readdir, .llseek = generic_file_llseek, + .setlease = generic_setlease, }; diff --git a/fs/squashfs/file.c b/fs/squashfs/file.c index 1582e0637a7e..4be92206e755 100644 --- a/fs/squashfs/file.c +++ b/fs/squashfs/file.c @@ -28,6 +28,7 @@ */ #include <linux/fs.h> +#include <linux/filelock.h> #include <linux/vfs.h> #include <linux/kernel.h> #include <linux/slab.h> @@ -775,5 +776,6 @@ const struct file_operations squashfs_file_operations = { .llseek = squashfs_llseek, .read_iter = generic_file_read_iter, .mmap_prepare = generic_file_readonly_mmap_prepare, - .splice_read = filemap_splice_read + .splice_read = filemap_splice_read, + .setlease = generic_setlease, }; diff --git a/fs/stat.c b/fs/stat.c index 6c79661e1b96..89909746bed1 100644 --- a/fs/stat.c +++ b/fs/stat.c @@ -365,17 +365,13 @@ retry: int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat, int flags) { - int ret; - int statx_flags = flags | AT_NO_AUTOMOUNT; - struct filename *name = getname_maybe_null(filename, flags); + CLASS(filename_maybe_null, name)(filename, flags); if (!name && dfd >= 0) return vfs_fstat(dfd, stat); - ret = vfs_statx(dfd, name, statx_flags, stat, STATX_BASIC_STATS); - putname(name); - - return ret; + return vfs_statx(dfd, name, flags | AT_NO_AUTOMOUNT, + stat, STATX_BASIC_STATS); } #ifdef __ARCH_WANT_OLD_STAT @@ -564,20 +560,17 @@ static int do_readlinkat(int dfd, const char __user *pathname, char __user *buf, int bufsiz) { struct path path; - struct filename *name; int error; - unsigned int lookup_flags = LOOKUP_EMPTY; + unsigned int lookup_flags = 0; if (bufsiz <= 0) return -EINVAL; + CLASS(filename_flags, name)(pathname, LOOKUP_EMPTY); retry: - name = getname_flags(pathname, lookup_flags); error = filename_lookup(dfd, name, lookup_flags, &path, NULL); - if (unlikely(error)) { - putname(name); + if (unlikely(error)) return error; - } /* * AFS mountpoints allow readlink(2) but are not symlinks @@ -593,7 +586,6 @@ retry: error = (name->name[0] == '\0') ? -ENOENT : -EINVAL; } path_put(&path); - putname(name); if (retry_estale(error, lookup_flags)) { lookup_flags |= LOOKUP_REVAL; goto retry; @@ -814,16 +806,12 @@ SYSCALL_DEFINE5(statx, unsigned int, mask, struct statx __user *, buffer) { - int ret; - struct filename *name = getname_maybe_null(filename, flags); + CLASS(filename_maybe_null, name)(filename, flags); if (!name && dfd >= 0) return do_statx_fd(dfd, flags & ~AT_NO_AUTOMOUNT, mask, buffer); - ret = do_statx(dfd, name, flags, mask, buffer); - putname(name); - - return ret; + return do_statx(dfd, name, flags, mask, buffer); } #if defined(CONFIG_COMPAT) && defined(__ARCH_WANT_COMPAT_STAT) diff --git a/fs/statfs.c b/fs/statfs.c index a45ac85e6048..377bcef7a561 100644 --- a/fs/statfs.c +++ b/fs/statfs.c @@ -99,8 +99,9 @@ int user_statfs(const char __user *pathname, struct kstatfs *st) struct path path; int error; unsigned int lookup_flags = LOOKUP_FOLLOW|LOOKUP_AUTOMOUNT; + CLASS(filename, name)(pathname); retry: - error = user_path_at(AT_FDCWD, pathname, lookup_flags, &path); + error = filename_lookup(AT_FDCWD, name, lookup_flags, &path, NULL); if (!error) { error = vfs_statfs(&path, st); path_put(&path); diff --git a/fs/super.c b/fs/super.c index 3d85265d1400..784b5297a7d7 100644 --- a/fs/super.c +++ b/fs/super.c @@ -36,6 +36,7 @@ #include <linux/lockdep.h> #include <linux/user_namespace.h> #include <linux/fs_context.h> +#include <linux/fserror.h> #include <uapi/linux/mount.h> #include "internal.h" @@ -363,6 +364,7 @@ static struct super_block *alloc_super(struct file_system_type *type, int flags, spin_lock_init(&s->s_inode_list_lock); INIT_LIST_HEAD(&s->s_inodes_wb); spin_lock_init(&s->s_inode_wblist_lock); + fserror_mount(s); s->s_count = 1; atomic_set(&s->s_active, 1); @@ -618,10 +620,12 @@ void generic_shutdown_super(struct super_block *sb) const struct super_operations *sop = sb->s_op; if (sb->s_root) { + fsnotify_sb_delete(sb); shrink_dcache_for_umount(sb); sync_filesystem(sb); sb->s_flags &= ~SB_ACTIVE; + fserror_unmount(sb); cgroup_writeback_umount(sb); /* Evict all inodes with zero refcount. */ @@ -629,9 +633,8 @@ void generic_shutdown_super(struct super_block *sb) /* * Clean up and evict any inodes that still have references due - * to fsnotify or the security policy. + * to the security policy. */ - fsnotify_sb_delete(sb); security_sb_delete(sb); if (sb->s_dio_done_wq) { diff --git a/fs/sync.c b/fs/sync.c index 431fc5f5be06..f310a550316f 100644 --- a/fs/sync.c +++ b/fs/sync.c @@ -104,8 +104,6 @@ void ksys_sync(void) iterate_supers(sync_fs_one_sb, &wait); sync_bdevs(false); sync_bdevs(true); - if (unlikely(laptop_mode)) - laptop_sync_completion(); } SYSCALL_DEFINE0(sync) @@ -183,8 +181,8 @@ int vfs_fsync_range(struct file *file, loff_t start, loff_t end, int datasync) if (!file->f_op->fsync) return -EINVAL; - if (!datasync && (inode_state_read_once(inode) & I_DIRTY_TIME)) - mark_inode_dirty_sync(inode); + if (!datasync) + sync_lazytime(inode); return file->f_op->fsync(file, start, end, datasync); } EXPORT_SYMBOL(vfs_fsync_range); diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index 3825e780cc58..a8176c875f55 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -689,7 +689,6 @@ int sysfs_file_change_owner(struct kobject *kobj, const char *name, kuid_t kuid, return error; } -EXPORT_SYMBOL_GPL(sysfs_file_change_owner); /** * sysfs_change_owner - change owner of the given object. @@ -736,7 +735,6 @@ int sysfs_change_owner(struct kobject *kobj, kuid_t kuid, kgid_t kgid) return 0; } -EXPORT_SYMBOL_GPL(sysfs_change_owner); /** * sysfs_emit - scnprintf equivalent, aware of PAGE_SIZE buffer. diff --git a/fs/tests/exec_kunit.c b/fs/tests/exec_kunit.c index 7c77d039680b..f412d1a0f6bb 100644 --- a/fs/tests/exec_kunit.c +++ b/fs/tests/exec_kunit.c @@ -90,9 +90,6 @@ static const struct bprm_stack_limits_result bprm_stack_limits_results[] = { { { .p = ULONG_MAX, .rlim_stack.rlim_cur = 4 * (_STK_LIM / 4 * 3), .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 * 3), - .argc = 0, .envc = 0 }, - .expected_argmin = ULONG_MAX - (_STK_LIM / 4 * 3) + sizeof(void *) }, /* But raising it any further will see no increase. */ { { .p = ULONG_MAX, .rlim_stack.rlim_cur = 4 * (_STK_LIM / 4 * 3 + sizeof(void *)), .argc = 0, .envc = 0 }, @@ -103,9 +100,6 @@ static const struct bprm_stack_limits_result bprm_stack_limits_results[] = { { { .p = ULONG_MAX, .rlim_stack.rlim_cur = 4 * _STK_LIM, .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 *) }, }; static void exec_test_bprm_stack_limits(struct kunit *test) diff --git a/fs/ubifs/file.c b/fs/ubifs/file.c index c3265b8804f5..3dc3ca1cd803 100644 --- a/fs/ubifs/file.c +++ b/fs/ubifs/file.c @@ -1361,17 +1361,8 @@ static inline int mctime_update_needed(const struct inode *inode, return 0; } -/** - * ubifs_update_time - update time of inode. - * @inode: inode to update - * @flags: time updating control flag determines updating - * which time fields of @inode - * - * This function updates time of the inode. - * - * Returns: %0 for success or a negative error code otherwise. - */ -int ubifs_update_time(struct inode *inode, int flags) +int ubifs_update_time(struct inode *inode, enum fs_update_time type, + unsigned int flags) { struct ubifs_inode *ui = ubifs_inode(inode); struct ubifs_info *c = inode->i_sb->s_fs_info; @@ -1379,17 +1370,22 @@ int ubifs_update_time(struct inode *inode, int flags) .dirtied_ino_d = ALIGN(ui->data_len, 8) }; int err, release; - if (!IS_ENABLED(CONFIG_UBIFS_ATIME_SUPPORT)) { - generic_update_time(inode, flags); - return 0; - } + /* ubifs sets S_NOCMTIME on all inodes, this should not happen. */ + if (WARN_ON_ONCE(type != FS_UPD_ATIME)) + return -EIO; + + if (!IS_ENABLED(CONFIG_UBIFS_ATIME_SUPPORT)) + return generic_update_time(inode, type, flags); + + if (flags & IOCB_NOWAIT) + return -EAGAIN; err = ubifs_budget_space(c, &req); if (err) return err; mutex_lock(&ui->ui_mutex); - inode_update_timestamps(inode, flags); + inode_update_time(inode, type, flags); release = ui->dirty; __mark_inode_dirty(inode, I_DIRTY_SYNC); mutex_unlock(&ui->ui_mutex); diff --git a/fs/ubifs/ubifs.h b/fs/ubifs/ubifs.h index 118392aa9f2a..b62a154c7bd4 100644 --- a/fs/ubifs/ubifs.h +++ b/fs/ubifs/ubifs.h @@ -2018,7 +2018,8 @@ int ubifs_calc_dark(const struct ubifs_info *c, int spc); int ubifs_fsync(struct file *file, loff_t start, loff_t end, int datasync); int ubifs_setattr(struct mnt_idmap *idmap, struct dentry *dentry, struct iattr *attr); -int ubifs_update_time(struct inode *inode, int flags); +int ubifs_update_time(struct inode *inode, enum fs_update_time type, + unsigned int flags); /* dir.c */ struct inode *ubifs_new_inode(struct ubifs_info *c, struct inode *dir, diff --git a/fs/udf/dir.c b/fs/udf/dir.c index 5023dfe191e8..5bf75638f352 100644 --- a/fs/udf/dir.c +++ b/fs/udf/dir.c @@ -24,6 +24,7 @@ #include <linux/string.h> #include <linux/errno.h> +#include <linux/filelock.h> #include <linux/mm.h> #include <linux/slab.h> #include <linux/bio.h> @@ -157,4 +158,5 @@ const struct file_operations udf_dir_operations = { .iterate_shared = udf_readdir, .unlocked_ioctl = udf_ioctl, .fsync = generic_file_fsync, + .setlease = generic_setlease, }; diff --git a/fs/udf/file.c b/fs/udf/file.c index 0d76c4f37b3e..32ae7cfd72c5 100644 --- a/fs/udf/file.c +++ b/fs/udf/file.c @@ -28,6 +28,7 @@ #include <linux/string.h> /* memset */ #include <linux/capability.h> #include <linux/errno.h> +#include <linux/filelock.h> #include <linux/pagemap.h> #include <linux/uio.h> @@ -208,6 +209,7 @@ const struct file_operations udf_file_operations = { .splice_read = filemap_splice_read, .splice_write = iter_file_splice_write, .llseek = generic_file_llseek, + .setlease = generic_setlease, }; static int udf_setattr(struct mnt_idmap *idmap, struct dentry *dentry, diff --git a/fs/udf/udf_sb.h b/fs/udf/udf_sb.h index 08ec8756b948..8399accc788d 100644 --- a/fs/udf/udf_sb.h +++ b/fs/udf/udf_sb.h @@ -55,8 +55,6 @@ #define MF_DUPLICATE_MD 0x01 #define MF_MIRROR_FE_LOADED 0x02 -#define EFSCORRUPTED EUCLEAN - struct udf_meta_data { __u32 s_meta_file_loc; __u32 s_mirror_file_loc; diff --git a/fs/udf/unicode.c b/fs/udf/unicode.c index 32c7f3d27f74..87580ff827ee 100644 --- a/fs/udf/unicode.c +++ b/fs/udf/unicode.c @@ -16,6 +16,7 @@ #include "udfdecl.h" +#include <linux/hex.h> #include <linux/kernel.h> #include <linux/string.h> /* for memset */ #include <linux/nls.h> diff --git a/fs/ufs/dir.c b/fs/ufs/dir.c index 0388a1bae326..43f1578ab866 100644 --- a/fs/ufs/dir.c +++ b/fs/ufs/dir.c @@ -19,6 +19,7 @@ #include <linux/time.h> #include <linux/fs.h> +#include <linux/filelock.h> #include <linux/swap.h> #include <linux/iversion.h> @@ -653,4 +654,5 @@ const struct file_operations ufs_dir_operations = { .iterate_shared = ufs_readdir, .fsync = generic_file_fsync, .llseek = ufs_dir_llseek, + .setlease = generic_setlease, }; diff --git a/fs/ufs/file.c b/fs/ufs/file.c index c2a391c17df7..809c7a4603f8 100644 --- a/fs/ufs/file.c +++ b/fs/ufs/file.c @@ -25,6 +25,7 @@ */ #include <linux/fs.h> +#include <linux/filelock.h> #include "ufs_fs.h" #include "ufs.h" @@ -43,4 +44,5 @@ const struct file_operations ufs_file_operations = { .fsync = generic_file_fsync, .splice_read = filemap_splice_read, .splice_write = iter_file_splice_write, + .setlease = generic_setlease, }; diff --git a/fs/utimes.c b/fs/utimes.c index 86f8ce8cd6b1..e22664e4115f 100644 --- a/fs/utimes.c +++ b/fs/utimes.c @@ -8,6 +8,7 @@ #include <linux/compat.h> #include <asm/unistd.h> #include <linux/filelock.h> +#include "internal.h" static bool nsec_valid(long nsec) { @@ -89,21 +90,18 @@ static int do_utimes_path(int dfd, const char __user *filename, if (!(flags & AT_SYMLINK_NOFOLLOW)) lookup_flags |= LOOKUP_FOLLOW; - if (flags & AT_EMPTY_PATH) - lookup_flags |= LOOKUP_EMPTY; + CLASS(filename_uflags, name)(filename, flags); retry: - error = user_path_at(dfd, filename, lookup_flags, &path); + error = filename_lookup(dfd, name, lookup_flags, &path, NULL); if (error) return error; - error = vfs_utimes(&path, times); path_put(&path); if (retry_estale(error, lookup_flags)) { lookup_flags |= LOOKUP_REVAL; goto retry; } - return error; } diff --git a/fs/vboxsf/file.c b/fs/vboxsf/file.c index 4bebd947314a..111752010edb 100644 --- a/fs/vboxsf/file.c +++ b/fs/vboxsf/file.c @@ -218,7 +218,6 @@ const struct file_operations vboxsf_reg_fops = { .release = vboxsf_file_release, .fsync = noop_fsync, .splice_read = filemap_splice_read, - .setlease = simple_nosetlease, }; const struct inode_operations vboxsf_reg_iops = { diff --git a/fs/verity/Makefile b/fs/verity/Makefile index 435559a4fa9e..ddb4a88a0d60 100644 --- a/fs/verity/Makefile +++ b/fs/verity/Makefile @@ -5,6 +5,7 @@ obj-$(CONFIG_FS_VERITY) += enable.o \ init.o \ measure.o \ open.o \ + pagecache.o \ read_metadata.o \ verify.o diff --git a/fs/verity/enable.c b/fs/verity/enable.c index 95ec42b84797..c9448074cce1 100644 --- a/fs/verity/enable.c +++ b/fs/verity/enable.c @@ -41,14 +41,15 @@ static int hash_one_block(const struct merkle_tree_params *params, return 0; } -static int write_merkle_tree_block(struct inode *inode, const u8 *buf, +static int write_merkle_tree_block(struct file *file, const u8 *buf, unsigned long index, const struct merkle_tree_params *params) { + struct inode *inode = file_inode(file); u64 pos = (u64)index << params->log_blocksize; int err; - err = inode->i_sb->s_vop->write_merkle_tree_block(inode, buf, pos, + err = inode->i_sb->s_vop->write_merkle_tree_block(file, buf, pos, params->block_size); if (err) fsverity_err(inode, "Error %d writing Merkle tree block %lu", @@ -135,7 +136,7 @@ static int build_merkle_tree(struct file *filp, err = hash_one_block(params, &buffers[level]); if (err) goto out; - err = write_merkle_tree_block(inode, + err = write_merkle_tree_block(filp, buffers[level].data, level_offset[level], params); @@ -155,7 +156,7 @@ static int build_merkle_tree(struct file *filp, err = hash_one_block(params, &buffers[level]); if (err) goto out; - err = write_merkle_tree_block(inode, + err = write_merkle_tree_block(filp, buffers[level].data, level_offset[level], params); @@ -265,8 +266,25 @@ static int enable_verity(struct file *filp, } /* + * Add the fsverity_info into the hash table before finishing the + * initialization so that we don't have to undo the enabling when memory + * allocation for the hash table fails. This is safe because looking up + * the fsverity_info always first checks the S_VERITY flag on the inode, + * which will only be set at the very end of the ->end_enable_verity + * method. + */ + err = fsverity_set_info(vi); + if (err) { + fsverity_free_info(vi); + goto rollback; + } + + /* * Tell the filesystem to finish enabling verity on the file. - * Serialized with ->begin_enable_verity() by the inode lock. + * Serialized with ->begin_enable_verity() by the inode lock. The file + * system needs to set the S_VERITY flag on the inode at the very end of + * the method, at which point the fsverity information can be accessed + * by other threads. */ inode_lock(inode); err = vops->end_enable_verity(filp, desc, desc_size, params.tree_size); @@ -274,19 +292,10 @@ static int enable_verity(struct file *filp, if (err) { fsverity_err(inode, "%ps() failed with err %d", vops->end_enable_verity, err); - fsverity_free_info(vi); + fsverity_remove_info(vi); } else if (WARN_ON_ONCE(!IS_VERITY(inode))) { + fsverity_remove_info(vi); err = -EINVAL; - fsverity_free_info(vi); - } else { - /* Successfully enabled verity */ - - /* - * Readers can start using the inode's verity info immediately, - * so it can't be rolled back once set. So don't set it until - * just after the filesystem has successfully enabled verity. - */ - fsverity_set_info(inode, vi); } out: kfree(params.hashstate); diff --git a/fs/verity/fsverity_private.h b/fs/verity/fsverity_private.h index dd20b138d452..2887cb849cec 100644 --- a/fs/verity/fsverity_private.h +++ b/fs/verity/fsverity_private.h @@ -11,6 +11,7 @@ #define pr_fmt(fmt) "fs-verity: " fmt #include <linux/fsverity.h> +#include <linux/rhashtable.h> /* * Implementation limit: maximum depth of the Merkle tree. For now 8 is plenty; @@ -63,17 +64,18 @@ struct merkle_tree_params { * fsverity_info - cached verity metadata for an inode * * When a verity file is first opened, an instance of this struct is allocated - * and a pointer to it is stored in the file's in-memory inode. It remains - * until the inode is evicted. It caches information about the Merkle tree - * that's needed to efficiently verify data read from the file. It also caches - * the file digest. The Merkle tree pages themselves are not cached here, but - * the filesystem may cache them. + * and a pointer to it is stored in the global hash table, indexed by the inode + * pointer value. It remains alive until the inode is evicted. It caches + * information about the Merkle tree that's needed to efficiently verify data + * read from the file. It also caches the file digest. The Merkle tree pages + * themselves are not cached here, but the filesystem may cache them. */ struct fsverity_info { + struct rhash_head rhash_head; struct merkle_tree_params tree_params; u8 root_hash[FS_VERITY_MAX_DIGEST_SIZE]; u8 file_digest[FS_VERITY_MAX_DIGEST_SIZE]; - const struct inode *inode; + struct inode *inode; unsigned long *hash_block_verified; }; @@ -124,12 +126,12 @@ int fsverity_init_merkle_tree_params(struct merkle_tree_params *params, unsigned int log_blocksize, const u8 *salt, size_t salt_size); -struct fsverity_info *fsverity_create_info(const struct inode *inode, +struct fsverity_info *fsverity_create_info(struct inode *inode, struct fsverity_descriptor *desc); -void fsverity_set_info(struct inode *inode, struct fsverity_info *vi); - +int fsverity_set_info(struct fsverity_info *vi); void fsverity_free_info(struct fsverity_info *vi); +void fsverity_remove_info(struct fsverity_info *vi); int fsverity_get_descriptor(struct inode *inode, struct fsverity_descriptor **desc_ret); diff --git a/fs/verity/measure.c b/fs/verity/measure.c index 388734132f01..6a35623ebdf0 100644 --- a/fs/verity/measure.c +++ b/fs/verity/measure.c @@ -162,7 +162,7 @@ __bpf_kfunc int bpf_get_fsverity_digest(struct file *file, struct bpf_dynptr *di __bpf_kfunc_end_defs(); BTF_KFUNCS_START(fsverity_set_ids) -BTF_ID_FLAGS(func, bpf_get_fsverity_digest, KF_TRUSTED_ARGS) +BTF_ID_FLAGS(func, bpf_get_fsverity_digest) BTF_KFUNCS_END(fsverity_set_ids) static int bpf_get_fsverity_digest_filter(const struct bpf_prog *prog, u32 kfunc_id) diff --git a/fs/verity/open.c b/fs/verity/open.c index 77b1c977af02..dfa0d1afe0fe 100644 --- a/fs/verity/open.c +++ b/fs/verity/open.c @@ -12,6 +12,14 @@ #include <linux/slab.h> static struct kmem_cache *fsverity_info_cachep; +static struct rhashtable fsverity_info_hash; + +static const struct rhashtable_params fsverity_info_hash_params = { + .key_len = sizeof_field(struct fsverity_info, inode), + .key_offset = offsetof(struct fsverity_info, inode), + .head_offset = offsetof(struct fsverity_info, rhash_head), + .automatic_shrinking = true, +}; /** * fsverity_init_merkle_tree_params() - initialize Merkle tree parameters @@ -175,7 +183,7 @@ static void compute_file_digest(const struct fsverity_hash_alg *hash_alg, * appended builtin signature), and check the signature if present. The * fsverity_descriptor must have already undergone basic validation. */ -struct fsverity_info *fsverity_create_info(const struct inode *inode, +struct fsverity_info *fsverity_create_info(struct inode *inode, struct fsverity_descriptor *desc) { struct fsverity_info *vi; @@ -241,33 +249,19 @@ fail: return ERR_PTR(err); } -void fsverity_set_info(struct inode *inode, struct fsverity_info *vi) +int fsverity_set_info(struct fsverity_info *vi) { - /* - * Multiple tasks may race to set the inode's verity info pointer, so - * use cmpxchg_release(). This pairs with the smp_load_acquire() in - * fsverity_get_info(). I.e., publish the pointer with a RELEASE - * barrier so that other tasks can ACQUIRE it. - */ - if (cmpxchg_release(fsverity_info_addr(inode), NULL, vi) != NULL) { - /* Lost the race, so free the verity info we allocated. */ - fsverity_free_info(vi); - /* - * Afterwards, the caller may access the inode's verity info - * directly, so make sure to ACQUIRE the winning verity info. - */ - (void)fsverity_get_info(inode); - } + return rhashtable_lookup_insert_fast(&fsverity_info_hash, + &vi->rhash_head, + fsverity_info_hash_params); } -void fsverity_free_info(struct fsverity_info *vi) +struct fsverity_info *__fsverity_get_info(const struct inode *inode) { - if (!vi) - return; - kfree(vi->tree_params.hashstate); - kvfree(vi->hash_block_verified); - kmem_cache_free(fsverity_info_cachep, vi); + return rhashtable_lookup_fast(&fsverity_info_hash, &inode, + fsverity_info_hash_params); } +EXPORT_SYMBOL_GPL(__fsverity_get_info); static bool validate_fsverity_descriptor(struct inode *inode, const struct fsverity_descriptor *desc, @@ -352,7 +346,7 @@ int fsverity_get_descriptor(struct inode *inode, static int ensure_verity_info(struct inode *inode) { - struct fsverity_info *vi = fsverity_get_info(inode); + struct fsverity_info *vi = fsverity_get_info(inode), *found; struct fsverity_descriptor *desc; int err; @@ -369,8 +363,19 @@ static int ensure_verity_info(struct inode *inode) goto out_free_desc; } - fsverity_set_info(inode, vi); - err = 0; + /* + * Multiple tasks may race to set the inode's verity info, in which case + * we might find an existing fsverity_info in the hash table. + */ + found = rhashtable_lookup_get_insert_fast(&fsverity_info_hash, + &vi->rhash_head, + fsverity_info_hash_params); + if (found) { + fsverity_free_info(vi); + if (IS_ERR(found)) + err = PTR_ERR(found); + } + out_free_desc: kfree(desc); return err; @@ -384,25 +389,32 @@ int __fsverity_file_open(struct inode *inode, struct file *filp) } EXPORT_SYMBOL_GPL(__fsverity_file_open); -int __fsverity_prepare_setattr(struct dentry *dentry, struct iattr *attr) +void fsverity_free_info(struct fsverity_info *vi) { - if (attr->ia_valid & ATTR_SIZE) - return -EPERM; - return 0; + kfree(vi->tree_params.hashstate); + kvfree(vi->hash_block_verified); + kmem_cache_free(fsverity_info_cachep, vi); } -EXPORT_SYMBOL_GPL(__fsverity_prepare_setattr); -void __fsverity_cleanup_inode(struct inode *inode) +void fsverity_remove_info(struct fsverity_info *vi) { - struct fsverity_info **vi_addr = fsverity_info_addr(inode); + rhashtable_remove_fast(&fsverity_info_hash, &vi->rhash_head, + fsverity_info_hash_params); + fsverity_free_info(vi); +} - fsverity_free_info(*vi_addr); - *vi_addr = NULL; +void fsverity_cleanup_inode(struct inode *inode) +{ + struct fsverity_info *vi = fsverity_get_info(inode); + + if (vi) + fsverity_remove_info(vi); } -EXPORT_SYMBOL_GPL(__fsverity_cleanup_inode); void __init fsverity_init_info_cache(void) { + if (rhashtable_init(&fsverity_info_hash, &fsverity_info_hash_params)) + panic("failed to initialize fsverity hash\n"); fsverity_info_cachep = KMEM_CACHE_USERCOPY( fsverity_info, SLAB_RECLAIM_ACCOUNT | SLAB_PANIC, diff --git a/fs/verity/pagecache.c b/fs/verity/pagecache.c new file mode 100644 index 000000000000..1819314ecaa3 --- /dev/null +++ b/fs/verity/pagecache.c @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2019 Google LLC + */ + +#include <linux/export.h> +#include <linux/fsverity.h> +#include <linux/pagemap.h> + +/** + * generic_read_merkle_tree_page - generic ->read_merkle_tree_page helper + * @inode: inode containing the Merkle tree + * @index: 0-based index of the Merkle tree page in the inode + * + * The caller needs to adjust @index from the Merkle-tree relative index passed + * to ->read_merkle_tree_page to the actual index where the Merkle tree is + * stored in the page cache for @inode. + */ +struct page *generic_read_merkle_tree_page(struct inode *inode, pgoff_t index) +{ + struct folio *folio; + + folio = read_mapping_folio(inode->i_mapping, index, NULL); + if (IS_ERR(folio)) + return ERR_CAST(folio); + return folio_file_page(folio, index); +} +EXPORT_SYMBOL_GPL(generic_read_merkle_tree_page); + +/** + * generic_readahead_merkle_tree() - generic ->readahead_merkle_tree helper + * @inode: inode containing the Merkle tree + * @index: 0-based index of the first Merkle tree page to read ahead in the + * inode + * @nr_pages: the number of Merkle tree pages that should be read ahead + * + * The caller needs to adjust @index from the Merkle-tree relative index passed + * to ->read_merkle_tree_page to the actual index where the Merkle tree is + * stored in the page cache for @inode. + */ +void generic_readahead_merkle_tree(struct inode *inode, pgoff_t index, + unsigned long nr_pages) +{ + struct folio *folio; + + lockdep_assert_held(&inode->i_mapping->invalidate_lock); + + folio = __filemap_get_folio(inode->i_mapping, index, FGP_ACCESSED, 0); + if (folio == ERR_PTR(-ENOENT) || + (!IS_ERR(folio) && !folio_test_uptodate(folio))) { + DEFINE_READAHEAD(ractl, NULL, NULL, inode->i_mapping, index); + + page_cache_ra_unbounded(&ractl, nr_pages, 0); + } + if (!IS_ERR(folio)) + folio_put(folio); +} +EXPORT_SYMBOL_GPL(generic_readahead_merkle_tree); diff --git a/fs/verity/read_metadata.c b/fs/verity/read_metadata.c index cba5d6af4e04..b4c0892430cd 100644 --- a/fs/verity/read_metadata.c +++ b/fs/verity/read_metadata.c @@ -28,24 +28,33 @@ static int fsverity_read_merkle_tree(struct inode *inode, if (offset >= end_offset) return 0; offs_in_page = offset_in_page(offset); + index = offset >> PAGE_SHIFT; last_index = (end_offset - 1) >> PAGE_SHIFT; /* + * Kick off readahead for the range we are going to read to ensure a + * single large sequential read instead of lots of small ones. + */ + if (inode->i_sb->s_vop->readahead_merkle_tree) { + filemap_invalidate_lock_shared(inode->i_mapping); + inode->i_sb->s_vop->readahead_merkle_tree( + inode, index, last_index - index + 1); + filemap_invalidate_unlock_shared(inode->i_mapping); + } + + /* * Iterate through each Merkle tree page in the requested range and copy * the requested portion to userspace. Note that the Merkle tree block * size isn't important here, as we are returning a byte stream; i.e., * we can just work with pages even if the tree block size != PAGE_SIZE. */ - for (index = offset >> PAGE_SHIFT; index <= last_index; index++) { - unsigned long num_ra_pages = - min_t(unsigned long, last_index - index + 1, - inode->i_sb->s_bdi->io_pages); + for (; index <= last_index; index++) { unsigned int bytes_to_copy = min_t(u64, end_offset - offset, PAGE_SIZE - offs_in_page); struct page *page; const void *virt; - page = vops->read_merkle_tree_page(inode, index, num_ra_pages); + page = vops->read_merkle_tree_page(inode, index); if (IS_ERR(page)) { err = PTR_ERR(page); fsverity_err(inode, diff --git a/fs/verity/verify.c b/fs/verity/verify.c index 86067c8b40cf..31797f9b24d0 100644 --- a/fs/verity/verify.c +++ b/fs/verity/verify.c @@ -19,9 +19,7 @@ struct fsverity_pending_block { }; struct fsverity_verification_context { - struct inode *inode; struct fsverity_info *vi; - unsigned long max_ra_pages; /* * This is the queue of data blocks that are pending verification. When @@ -37,6 +35,50 @@ struct fsverity_verification_context { static struct workqueue_struct *fsverity_read_workqueue; +/** + * fsverity_readahead() - kick off readahead on fsverity hashes + * @vi: fsverity_info for the inode to be read + * @index: first file data page index that is being read + * @nr_pages: number of file data pages to be read + * + * Start readahead on the fsverity hashes that are needed to verify the file + * data in the range from @index to @index + @nr_pages (exclusive upper bound). + * + * To be called from the file systems' ->read_folio and ->readahead methods to + * ensure that the hashes are already cached on completion of the file data + * read if possible. + */ +void fsverity_readahead(struct fsverity_info *vi, pgoff_t index, + unsigned long nr_pages) +{ + struct inode *inode = vi->inode; + const struct merkle_tree_params *params = &vi->tree_params; + u64 start_hidx = (u64)index << params->log_blocks_per_page; + u64 end_hidx = + (((u64)index + nr_pages) << params->log_blocks_per_page) - 1; + int level; + + if (!inode->i_sb->s_vop->readahead_merkle_tree) + return; + + for (level = 0; level < params->num_levels; level++) { + unsigned long level_start = params->level_start[level]; + unsigned long next_start_hidx = start_hidx >> params->log_arity; + unsigned long next_end_hidx = end_hidx >> params->log_arity; + pgoff_t start_idx = (level_start + next_start_hidx) >> + params->log_blocks_per_page; + pgoff_t end_idx = (level_start + next_end_hidx) >> + params->log_blocks_per_page; + + inode->i_sb->s_vop->readahead_merkle_tree( + inode, start_idx, end_idx - start_idx + 1); + + start_hidx = next_start_hidx; + end_hidx = next_end_hidx; + } +} +EXPORT_SYMBOL_GPL(fsverity_readahead); + /* * Returns true if the hash block with index @hblock_idx in the tree, located in * @hpage, has already been verified. @@ -113,10 +155,10 @@ static bool is_hash_block_verified(struct fsverity_info *vi, struct page *hpage, * * Return: %true if the data block is valid, else %false. */ -static bool verify_data_block(struct inode *inode, struct fsverity_info *vi, - const struct fsverity_pending_block *dblock, - unsigned long max_ra_pages) +static bool verify_data_block(struct fsverity_info *vi, + const struct fsverity_pending_block *dblock) { + struct inode *inode = vi->inode; const u64 data_pos = dblock->pos; const struct merkle_tree_params *params = &vi->tree_params; const unsigned int hsize = params->digest_size; @@ -200,8 +242,7 @@ static bool verify_data_block(struct inode *inode, struct fsverity_info *vi, (params->block_size - 1); hpage = inode->i_sb->s_vop->read_merkle_tree_page(inode, - hpage_idx, level == 0 ? min(max_ra_pages, - params->tree_pages - hpage_idx) : 0); + hpage_idx); if (IS_ERR(hpage)) { fsverity_err(inode, "Error %ld reading Merkle tree page %lu", @@ -272,14 +313,9 @@ error: static void fsverity_init_verification_context(struct fsverity_verification_context *ctx, - struct inode *inode, - unsigned long max_ra_pages) + struct fsverity_info *vi) { - struct fsverity_info *vi = *fsverity_info_addr(inode); - - ctx->inode = inode; ctx->vi = vi; - ctx->max_ra_pages = max_ra_pages; ctx->num_pending = 0; if (vi->tree_params.hash_alg->algo_id == HASH_ALGO_SHA256 && sha256_finup_2x_is_optimized()) @@ -322,8 +358,7 @@ fsverity_verify_pending_blocks(struct fsverity_verification_context *ctx) } for (i = 0; i < ctx->num_pending; i++) { - if (!verify_data_block(ctx->inode, vi, &ctx->pending_blocks[i], - ctx->max_ra_pages)) + if (!verify_data_block(vi, &ctx->pending_blocks[i])) return false; } fsverity_clear_pending_blocks(ctx); @@ -359,6 +394,7 @@ static bool fsverity_add_data_blocks(struct fsverity_verification_context *ctx, /** * fsverity_verify_blocks() - verify data in a folio + * @vi: fsverity_info for the inode to be read * @folio: the folio containing the data to verify * @len: the length of the data to verify in the folio * @offset: the offset of the data to verify in the folio @@ -369,11 +405,12 @@ static bool fsverity_add_data_blocks(struct fsverity_verification_context *ctx, * * Return: %true if the data is valid, else %false. */ -bool fsverity_verify_blocks(struct folio *folio, size_t len, size_t offset) +bool fsverity_verify_blocks(struct fsverity_info *vi, struct folio *folio, + size_t len, size_t offset) { struct fsverity_verification_context ctx; - fsverity_init_verification_context(&ctx, folio->mapping->host, 0); + fsverity_init_verification_context(&ctx, vi); if (fsverity_add_data_blocks(&ctx, folio, len, offset) && fsverity_verify_pending_blocks(&ctx)) @@ -386,6 +423,7 @@ EXPORT_SYMBOL_GPL(fsverity_verify_blocks); #ifdef CONFIG_BLOCK /** * fsverity_verify_bio() - verify a 'read' bio that has just completed + * @vi: fsverity_info for the inode to be read * @bio: the bio to verify * * Verify the bio's data against the file's Merkle tree. All bio data segments @@ -398,27 +436,12 @@ EXPORT_SYMBOL_GPL(fsverity_verify_blocks); * filesystems) must instead call fsverity_verify_page() directly on each page. * All filesystems must also call fsverity_verify_page() on holes. */ -void fsverity_verify_bio(struct bio *bio) +void fsverity_verify_bio(struct fsverity_info *vi, struct bio *bio) { - struct inode *inode = bio_first_folio_all(bio)->mapping->host; struct fsverity_verification_context ctx; struct folio_iter fi; - unsigned long max_ra_pages = 0; - - if (bio->bi_opf & REQ_RAHEAD) { - /* - * If this bio is for data readahead, then we also do readahead - * of the first (largest) level of the Merkle tree. Namely, - * when a Merkle tree page is read, we also try to piggy-back on - * some additional pages -- up to 1/4 the number of data pages. - * - * This improves sequential read performance, as it greatly - * reduces the number of I/O requests made to the Merkle tree. - */ - max_ra_pages = bio->bi_iter.bi_size >> (PAGE_SHIFT + 2); - } - fsverity_init_verification_context(&ctx, inode, max_ra_pages); + fsverity_init_verification_context(&ctx, vi); bio_for_each_folio_all(fi, bio) { if (!fsverity_add_data_blocks(&ctx, fi.folio, fi.length, diff --git a/fs/xattr.c b/fs/xattr.c index 32d445fb60aa..3e49e612e1ba 100644 --- a/fs/xattr.c +++ b/fs/xattr.c @@ -649,7 +649,6 @@ int file_setxattr(struct file *f, struct kernel_xattr_ctx *ctx) return error; } -/* unconditionally consumes filename */ int filename_setxattr(int dfd, struct filename *filename, unsigned int lookup_flags, struct kernel_xattr_ctx *ctx) { @@ -659,7 +658,7 @@ int filename_setxattr(int dfd, struct filename *filename, retry: error = filename_lookup(dfd, filename, lookup_flags, &path, NULL); if (error) - goto out; + return error; error = mnt_want_write(path.mnt); if (!error) { error = do_setxattr(mnt_idmap(path.mnt), path.dentry, ctx); @@ -670,9 +669,6 @@ retry: lookup_flags |= LOOKUP_REVAL; goto retry; } - -out: - putname(filename); return error; } @@ -688,7 +684,6 @@ static int path_setxattrat(int dfd, const char __user *pathname, .kname = &kname, .flags = flags, }; - struct filename *filename; unsigned int lookup_flags = 0; int error; @@ -702,7 +697,7 @@ static int path_setxattrat(int dfd, const char __user *pathname, if (error) return error; - filename = getname_maybe_null(pathname, at_flags); + CLASS(filename_maybe_null, filename)(pathname, at_flags); if (!filename && dfd >= 0) { CLASS(fd, f)(dfd); if (fd_empty(f)) @@ -804,7 +799,6 @@ ssize_t file_getxattr(struct file *f, struct kernel_xattr_ctx *ctx) return do_getxattr(file_mnt_idmap(f), f->f_path.dentry, ctx); } -/* unconditionally consumes filename */ ssize_t filename_getxattr(int dfd, struct filename *filename, unsigned int lookup_flags, struct kernel_xattr_ctx *ctx) { @@ -813,15 +807,13 @@ ssize_t filename_getxattr(int dfd, struct filename *filename, retry: error = filename_lookup(dfd, filename, lookup_flags, &path, NULL); if (error) - goto out; + return error; error = do_getxattr(mnt_idmap(path.mnt), path.dentry, ctx); path_put(&path); if (retry_estale(error, lookup_flags)) { lookup_flags |= LOOKUP_REVAL; goto retry; } -out: - putname(filename); return error; } @@ -836,7 +828,6 @@ static ssize_t path_getxattrat(int dfd, const char __user *pathname, .kname = &kname, .flags = 0, }; - struct filename *filename; ssize_t error; if ((at_flags & ~(AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH)) != 0) @@ -846,7 +837,7 @@ static ssize_t path_getxattrat(int dfd, const char __user *pathname, if (error) return error; - filename = getname_maybe_null(pathname, at_flags); + CLASS(filename_maybe_null, filename)(pathname, at_flags); if (!filename && dfd >= 0) { CLASS(fd, f)(dfd); if (fd_empty(f)) @@ -943,7 +934,6 @@ ssize_t file_listxattr(struct file *f, char __user *list, size_t size) return listxattr(f->f_path.dentry, list, size); } -/* unconditionally consumes filename */ static ssize_t filename_listxattr(int dfd, struct filename *filename, unsigned int lookup_flags, @@ -954,15 +944,13 @@ ssize_t filename_listxattr(int dfd, struct filename *filename, retry: error = filename_lookup(dfd, filename, lookup_flags, &path, NULL); if (error) - goto out; + return error; error = listxattr(path.dentry, list, size); path_put(&path); if (retry_estale(error, lookup_flags)) { lookup_flags |= LOOKUP_REVAL; goto retry; } -out: - putname(filename); return error; } @@ -970,13 +958,12 @@ static ssize_t path_listxattrat(int dfd, const char __user *pathname, unsigned int at_flags, char __user *list, size_t size) { - struct filename *filename; int lookup_flags; if ((at_flags & ~(AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH)) != 0) return -EINVAL; - filename = getname_maybe_null(pathname, at_flags); + CLASS(filename_maybe_null, filename)(pathname, at_flags); if (!filename) { CLASS(fd, f)(dfd); if (fd_empty(f)) @@ -1036,7 +1023,6 @@ static int file_removexattr(struct file *f, struct xattr_name *kname) return error; } -/* unconditionally consumes filename */ static int filename_removexattr(int dfd, struct filename *filename, unsigned int lookup_flags, struct xattr_name *kname) { @@ -1046,7 +1032,7 @@ static int filename_removexattr(int dfd, struct filename *filename, retry: error = filename_lookup(dfd, filename, lookup_flags, &path, NULL); if (error) - goto out; + return error; error = mnt_want_write(path.mnt); if (!error) { error = removexattr(mnt_idmap(path.mnt), path.dentry, kname->name); @@ -1057,8 +1043,6 @@ retry: lookup_flags |= LOOKUP_REVAL; goto retry; } -out: - putname(filename); return error; } @@ -1066,7 +1050,6 @@ static int path_removexattrat(int dfd, const char __user *pathname, unsigned int at_flags, const char __user *name) { struct xattr_name kname; - struct filename *filename; unsigned int lookup_flags; int error; @@ -1077,7 +1060,7 @@ static int path_removexattrat(int dfd, const char __user *pathname, if (error) return error; - filename = getname_maybe_null(pathname, at_flags); + CLASS(filename_maybe_null, filename)(pathname, at_flags); if (!filename) { CLASS(fd, f)(dfd); if (fd_empty(f)) diff --git a/fs/xfs/Makefile b/fs/xfs/Makefile index 5bf501cf8271..9f7133e02576 100644 --- a/fs/xfs/Makefile +++ b/fs/xfs/Makefile @@ -88,6 +88,7 @@ xfs-y += xfs_aops.o \ xfs_globals.o \ xfs_handle.o \ xfs_health.o \ + xfs_healthmon.o \ xfs_icache.o \ xfs_ioctl.o \ xfs_iomap.o \ @@ -105,6 +106,7 @@ xfs-y += xfs_aops.o \ xfs_symlink.o \ xfs_sysfs.o \ xfs_trans.o \ + xfs_verify_media.o \ xfs_xattr.o # low-level transaction/log code diff --git a/fs/xfs/libxfs/xfs_ag.c b/fs/xfs/libxfs/xfs_ag.c index e6ba914f6d06..586918ed1cbf 100644 --- a/fs/xfs/libxfs/xfs_ag.c +++ b/fs/xfs/libxfs/xfs_ag.c @@ -5,7 +5,7 @@ * All rights reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/libxfs/xfs_ag_resv.c b/fs/xfs/libxfs/xfs_ag_resv.c index 8ac8230c3d3c..c4cdcc570d61 100644 --- a/fs/xfs/libxfs/xfs_ag_resv.c +++ b/fs/xfs/libxfs/xfs_ag_resv.c @@ -3,7 +3,7 @@ * Copyright (C) 2016 Oracle. All Rights Reserved. * Author: Darrick J. Wong <darrick.wong@oracle.com> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/libxfs/xfs_alloc.c b/fs/xfs/libxfs/xfs_alloc.c index ad381c73abc4..d99602bcc16f 100644 --- a/fs/xfs/libxfs/xfs_alloc.c +++ b/fs/xfs/libxfs/xfs_alloc.c @@ -3,7 +3,7 @@ * Copyright (c) 2000-2002,2005 Silicon Graphics, Inc. * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_format.h" #include "xfs_log_format.h" @@ -376,8 +376,8 @@ xfs_alloc_compute_diff( xfs_agblock_t freeend; /* end of freespace extent */ xfs_agblock_t newbno1; /* return block number */ xfs_agblock_t newbno2; /* other new block number */ - xfs_extlen_t newlen1=0; /* length with newbno1 */ - xfs_extlen_t newlen2=0; /* length with newbno2 */ + xfs_extlen_t newlen1 = 0; /* length with newbno1 */ + xfs_extlen_t newlen2 = 0; /* length with newbno2 */ xfs_agblock_t wantend; /* end of target extent */ bool userdata = datatype & XFS_ALLOC_USERDATA; @@ -577,8 +577,8 @@ xfs_alloc_fixup_trees( int i; /* operation results */ xfs_agblock_t nfbno1; /* first new free startblock */ xfs_agblock_t nfbno2; /* second new free startblock */ - xfs_extlen_t nflen1=0; /* first new free length */ - xfs_extlen_t nflen2=0; /* second new free length */ + xfs_extlen_t nflen1 = 0; /* first new free length */ + xfs_extlen_t nflen2 = 0; /* second new free length */ struct xfs_mount *mp; bool fixup_longest = false; diff --git a/fs/xfs/libxfs/xfs_alloc_btree.c b/fs/xfs/libxfs/xfs_alloc_btree.c index fa1f03c1331e..29f6ec1c3f6f 100644 --- a/fs/xfs/libxfs/xfs_alloc_btree.c +++ b/fs/xfs/libxfs/xfs_alloc_btree.c @@ -3,7 +3,7 @@ * Copyright (c) 2000-2001,2005 Silicon Graphics, Inc. * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c index 8c04acd30d48..93caa1dae501 100644 --- a/fs/xfs/libxfs/xfs_attr.c +++ b/fs/xfs/libxfs/xfs_attr.c @@ -3,7 +3,7 @@ * Copyright (c) 2000-2005 Silicon Graphics, Inc. * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" @@ -50,7 +50,6 @@ STATIC int xfs_attr_shortform_addname(xfs_da_args_t *args); */ STATIC int xfs_attr_leaf_get(xfs_da_args_t *args); STATIC int xfs_attr_leaf_removename(xfs_da_args_t *args); -STATIC int xfs_attr_leaf_hasname(struct xfs_da_args *args, struct xfs_buf **bp); /* * Internal routines when attribute list is more than one block. @@ -351,16 +350,14 @@ xfs_attr_set_resv( */ STATIC int xfs_attr_try_sf_addname( - struct xfs_inode *dp, struct xfs_da_args *args) { - int error; /* * Build initial attribute list (if required). */ - if (dp->i_af.if_format == XFS_DINODE_FMT_EXTENTS) + if (args->dp->i_af.if_format == XFS_DINODE_FMT_EXTENTS) xfs_attr_shortform_create(args); error = xfs_attr_shortform_addname(args); @@ -372,9 +369,9 @@ xfs_attr_try_sf_addname( * NOTE: this is also the error path (EEXIST, etc). */ if (!error) - xfs_trans_ichgtime(args->trans, dp, XFS_ICHGTIME_CHG); + xfs_trans_ichgtime(args->trans, args->dp, XFS_ICHGTIME_CHG); - if (xfs_has_wsync(dp->i_mount)) + if (xfs_has_wsync(args->dp->i_mount)) xfs_trans_set_sync(args->trans); return error; @@ -385,10 +382,9 @@ xfs_attr_sf_addname( struct xfs_attr_intent *attr) { struct xfs_da_args *args = attr->xattri_da_args; - struct xfs_inode *dp = args->dp; int error = 0; - error = xfs_attr_try_sf_addname(dp, args); + error = xfs_attr_try_sf_addname(args); if (error != -ENOSPC) { ASSERT(!error || error == -EEXIST); attr->xattri_dela_state = XFS_DAS_DONE; @@ -979,11 +975,12 @@ xfs_attr_lookup( return error; if (xfs_attr_is_leaf(dp)) { - error = xfs_attr_leaf_hasname(args, &bp); - - if (bp) - xfs_trans_brelse(args->trans, bp); - + error = xfs_attr3_leaf_read(args->trans, args->dp, args->owner, + 0, &bp); + if (error) + return error; + error = xfs_attr3_leaf_lookup_int(bp, args); + xfs_trans_brelse(args->trans, bp); return error; } @@ -1032,6 +1029,95 @@ trans_cancel: } /* + * Decide if it is theoretically possible to try to bypass the attr intent + * mechanism for better performance. Other constraints (e.g. available space + * in the existing structure) are not considered here. + */ +static inline bool +xfs_attr_can_shortcut( + const struct xfs_inode *ip) +{ + return xfs_inode_has_attr_fork(ip) && xfs_attr_is_shortform(ip); +} + +/* Try to set an attr in one transaction or fall back to attr intents. */ +int +xfs_attr_setname( + struct xfs_da_args *args, + int rmt_blks) +{ + int error; + + if (!rmt_blks && xfs_attr_can_shortcut(args->dp)) { + args->op_flags |= XFS_DA_OP_ADDNAME; + + error = xfs_attr_try_sf_addname(args); + if (error != -ENOSPC) + return error; + } + + xfs_attr_defer_add(args, XFS_ATTR_DEFER_SET); + return 0; +} + +/* Try to remove an attr in one transaction or fall back to attr intents. */ +int +xfs_attr_removename( + struct xfs_da_args *args) +{ + if (xfs_attr_can_shortcut(args->dp)) + return xfs_attr_sf_removename(args); + + xfs_attr_defer_add(args, XFS_ATTR_DEFER_REMOVE); + return 0; +} + +/* Try to replace an attr in one transaction or fall back to attr intents. */ +int +xfs_attr_replacename( + struct xfs_da_args *args, + int rmt_blks) +{ + int error; + + if (rmt_blks || !xfs_attr_can_shortcut(args->dp)) { + xfs_attr_defer_add(args, XFS_ATTR_DEFER_REPLACE); + return 0; + } + + error = xfs_attr_shortform_replace(args); + if (error != -ENOSPC) + return error; + + args->op_flags |= XFS_DA_OP_ADDNAME | XFS_DA_OP_REPLACE; + + error = xfs_attr_sf_removename(args); + if (error) + return error; + + if (args->attr_filter & XFS_ATTR_PARENT) { + /* + * Move the new name/value to the regular name/value slots and + * zero out the new name/value slots because we don't need to + * log them for a PPTR_SET operation. + */ + xfs_attr_update_pptr_replace_args(args); + args->new_name = NULL; + args->new_namelen = 0; + args->new_value = NULL; + args->new_valuelen = 0; + } + args->op_flags &= ~XFS_DA_OP_REPLACE; + + error = xfs_attr_try_sf_addname(args); + if (error != -ENOSPC) + return error; + + xfs_attr_defer_add(args, XFS_ATTR_DEFER_SET); + return 0; +} + +/* * Make a change to the xattr structure. * * The caller must have initialized @args, attached dquots, and must not hold @@ -1111,14 +1197,19 @@ xfs_attr_set( case -EEXIST: if (op == XFS_ATTRUPDATE_REMOVE) { /* if no value, we are performing a remove operation */ - xfs_attr_defer_add(args, XFS_ATTR_DEFER_REMOVE); + error = xfs_attr_removename(args); + if (error) + goto out_trans_cancel; break; } /* Pure create fails if the attr already exists */ if (op == XFS_ATTRUPDATE_CREATE) goto out_trans_cancel; - xfs_attr_defer_add(args, XFS_ATTR_DEFER_REPLACE); + + error = xfs_attr_replacename(args, rmt_blks); + if (error) + goto out_trans_cancel; break; case -ENOATTR: /* Can't remove what isn't there. */ @@ -1128,7 +1219,10 @@ xfs_attr_set( /* Pure replace fails if no existing attr to replace. */ if (op == XFS_ATTRUPDATE_REPLACE) goto out_trans_cancel; - xfs_attr_defer_add(args, XFS_ATTR_DEFER_SET); + + error = xfs_attr_setname(args, rmt_blks); + if (error) + goto out_trans_cancel; break; default: goto out_trans_cancel; @@ -1223,27 +1317,6 @@ xfs_attr_shortform_addname( *========================================================================*/ /* - * Return EEXIST if attr is found, or ENOATTR if not - */ -STATIC int -xfs_attr_leaf_hasname( - struct xfs_da_args *args, - struct xfs_buf **bp) -{ - int error = 0; - - error = xfs_attr3_leaf_read(args->trans, args->dp, args->owner, 0, bp); - if (error) - return error; - - error = xfs_attr3_leaf_lookup_int(*bp, args); - if (error != -ENOATTR && error != -EEXIST) - xfs_trans_brelse(args->trans, *bp); - - return error; -} - -/* * Remove a name from the leaf attribute list structure * * This leaf block cannot have a "remote" value, we only call this routine @@ -1253,25 +1326,22 @@ STATIC int xfs_attr_leaf_removename( struct xfs_da_args *args) { - struct xfs_inode *dp; - struct xfs_buf *bp; + struct xfs_inode *dp = args->dp; int error, forkoff; + struct xfs_buf *bp; trace_xfs_attr_leaf_removename(args); - /* - * Remove the attribute. - */ - dp = args->dp; - - error = xfs_attr_leaf_hasname(args, &bp); - if (error == -ENOATTR) { + error = xfs_attr3_leaf_read(args->trans, args->dp, args->owner, 0, &bp); + if (error) + return error; + error = xfs_attr3_leaf_lookup_int(bp, args); + if (error != -EEXIST) { xfs_trans_brelse(args->trans, bp); - if (args->op_flags & XFS_DA_OP_RECOVERY) + if (error == -ENOATTR && (args->op_flags & XFS_DA_OP_RECOVERY)) return 0; return error; - } else if (error != -EEXIST) - return error; + } xfs_attr3_leaf_remove(bp, args); @@ -1295,23 +1365,20 @@ xfs_attr_leaf_removename( * Returns 0 on successful retrieval, otherwise an error. */ STATIC int -xfs_attr_leaf_get(xfs_da_args_t *args) +xfs_attr_leaf_get( + struct xfs_da_args *args) { - struct xfs_buf *bp; - int error; + struct xfs_buf *bp; + int error; trace_xfs_attr_leaf_get(args); - error = xfs_attr_leaf_hasname(args, &bp); - - if (error == -ENOATTR) { - xfs_trans_brelse(args->trans, bp); - return error; - } else if (error != -EEXIST) + error = xfs_attr3_leaf_read(args->trans, args->dp, args->owner, 0, &bp); + if (error) return error; - - - error = xfs_attr3_leaf_getvalue(bp, args); + error = xfs_attr3_leaf_lookup_int(bp, args); + if (error == -EEXIST) + error = xfs_attr3_leaf_getvalue(bp, args); xfs_trans_brelse(args->trans, bp); return error; } diff --git a/fs/xfs/libxfs/xfs_attr.h b/fs/xfs/libxfs/xfs_attr.h index 0e51d0723f9a..8244305949de 100644 --- a/fs/xfs/libxfs/xfs_attr.h +++ b/fs/xfs/libxfs/xfs_attr.h @@ -573,7 +573,7 @@ struct xfs_trans_res xfs_attr_set_resv(const struct xfs_da_args *args); */ static inline bool xfs_attr_is_shortform( - struct xfs_inode *ip) + const struct xfs_inode *ip) { return ip->i_af.if_format == XFS_DINODE_FMT_LOCAL || (ip->i_af.if_format == XFS_DINODE_FMT_EXTENTS && @@ -649,4 +649,8 @@ void xfs_attr_intent_destroy_cache(void); int xfs_attr_sf_totsize(struct xfs_inode *dp); int xfs_attr_add_fork(struct xfs_inode *ip, int size, int rsvd); +int xfs_attr_setname(struct xfs_da_args *args, int rmt_blks); +int xfs_attr_removename(struct xfs_da_args *args); +int xfs_attr_replacename(struct xfs_da_args *args, int rmt_blks); + #endif /* __XFS_ATTR_H__ */ diff --git a/fs/xfs/libxfs/xfs_attr_leaf.c b/fs/xfs/libxfs/xfs_attr_leaf.c index 91c1b30ebaab..47f48ae555c0 100644 --- a/fs/xfs/libxfs/xfs_attr_leaf.c +++ b/fs/xfs/libxfs/xfs_attr_leaf.c @@ -4,7 +4,7 @@ * Copyright (c) 2013 Red Hat, Inc. * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" @@ -75,6 +75,59 @@ STATIC void xfs_attr3_leaf_moveents(struct xfs_da_args *args, int move_count); STATIC int xfs_attr_leaf_entsize(xfs_attr_leafblock_t *leaf, int index); +/* Compute the byte offset of the end of the leaf entry array. */ +static inline int +xfs_attr_leaf_entries_end( + unsigned int hdrcount, + const struct xfs_attr_leafblock *leaf) +{ + return hdrcount * sizeof(struct xfs_attr_leaf_entry) + + xfs_attr3_leaf_hdr_size(leaf); +} + +static inline bool +ichdr_freemaps_overlap( + const struct xfs_attr3_icleaf_hdr *ichdr, + unsigned int x, + unsigned int y) +{ + const unsigned int xend = + ichdr->freemap[x].base + ichdr->freemap[x].size; + const unsigned int yend = + ichdr->freemap[y].base + ichdr->freemap[y].size; + + /* empty slots do not overlap */ + if (!ichdr->freemap[x].size || !ichdr->freemap[y].size) + return false; + + return ichdr->freemap[x].base < yend && xend > ichdr->freemap[y].base; +} + +static inline xfs_failaddr_t +xfs_attr_leaf_ichdr_freemaps_verify( + const struct xfs_attr3_icleaf_hdr *ichdr, + const struct xfs_attr_leafblock *leaf) +{ + unsigned int entries_end = + xfs_attr_leaf_entries_end(ichdr->count, leaf); + int i; + + if (ichdr_freemaps_overlap(ichdr, 0, 1)) + return __this_address; + if (ichdr_freemaps_overlap(ichdr, 0, 2)) + return __this_address; + if (ichdr_freemaps_overlap(ichdr, 1, 2)) + return __this_address; + + for (i = 0; i < XFS_ATTR_LEAF_MAPSIZE; i++) { + if (ichdr->freemap[i].size > 0 && + ichdr->freemap[i].base < entries_end) + return __this_address; + } + + return NULL; +} + /* * attr3 block 'firstused' conversion helpers. * @@ -218,6 +271,8 @@ xfs_attr3_leaf_hdr_to_disk( hdr3->freemap[i].base = cpu_to_be16(from->freemap[i].base); hdr3->freemap[i].size = cpu_to_be16(from->freemap[i].size); } + + ASSERT(xfs_attr_leaf_ichdr_freemaps_verify(from, to) == NULL); return; } to->hdr.info.forw = cpu_to_be32(from->forw); @@ -233,6 +288,8 @@ xfs_attr3_leaf_hdr_to_disk( to->hdr.freemap[i].base = cpu_to_be16(from->freemap[i].base); to->hdr.freemap[i].size = cpu_to_be16(from->freemap[i].size); } + + ASSERT(xfs_attr_leaf_ichdr_freemaps_verify(from, to) == NULL); } static xfs_failaddr_t @@ -385,6 +442,10 @@ xfs_attr3_leaf_verify( return __this_address; } + fa = xfs_attr_leaf_ichdr_freemaps_verify(&ichdr, leaf); + if (fa) + return fa; + return NULL; } @@ -782,6 +843,44 @@ xfs_attr_sf_findname( } /* + * Replace a shortform xattr if it's the right length. Returns 0 on success, + * -ENOSPC if the length is wrong, or -ENOATTR if the attr was not found. + */ +int +xfs_attr_shortform_replace( + struct xfs_da_args *args) +{ + struct xfs_attr_sf_entry *sfe; + + ASSERT(args->dp->i_af.if_format == XFS_DINODE_FMT_LOCAL); + + trace_xfs_attr_sf_replace(args); + + sfe = xfs_attr_sf_findname(args); + if (!sfe) + return -ENOATTR; + + if (args->attr_filter & XFS_ATTR_PARENT) { + if (sfe->namelen != args->new_namelen || + sfe->valuelen != args->new_valuelen) + return -ENOSPC; + + memcpy(sfe->nameval, args->new_name, sfe->namelen); + memcpy(&sfe->nameval[sfe->namelen], args->new_value, + sfe->valuelen); + } else { + if (sfe->valuelen != args->valuelen) + return -ENOSPC; + memcpy(&sfe->nameval[sfe->namelen], args->value, + sfe->valuelen); + } + + xfs_trans_log_inode(args->trans, args->dp, + XFS_ILOG_CORE | XFS_ILOG_ADATA); + return 0; +} + +/* * Add a name/value pair to the shortform attribute list. * Overflow from the inode has already been checked for. */ @@ -1409,8 +1508,7 @@ xfs_attr3_leaf_add( * Search through freemap for first-fit on new name length. * (may need to figure in size of entry struct too) */ - tablesize = (ichdr.count + 1) * sizeof(xfs_attr_leaf_entry_t) - + xfs_attr3_leaf_hdr_size(leaf); + tablesize = xfs_attr_leaf_entries_end(ichdr.count + 1, leaf); for (sum = 0, i = XFS_ATTR_LEAF_MAPSIZE - 1; i >= 0; i--) { if (tablesize > ichdr.firstused) { sum += ichdr.freemap[i].size; @@ -1476,6 +1574,7 @@ xfs_attr3_leaf_add_work( struct xfs_attr_leaf_name_local *name_loc; struct xfs_attr_leaf_name_remote *name_rmt; struct xfs_mount *mp; + int old_end, new_end; int tmp; int i; @@ -1568,17 +1667,48 @@ xfs_attr3_leaf_add_work( if (be16_to_cpu(entry->nameidx) < ichdr->firstused) ichdr->firstused = be16_to_cpu(entry->nameidx); - ASSERT(ichdr->firstused >= ichdr->count * sizeof(xfs_attr_leaf_entry_t) - + xfs_attr3_leaf_hdr_size(leaf)); - tmp = (ichdr->count - 1) * sizeof(xfs_attr_leaf_entry_t) - + xfs_attr3_leaf_hdr_size(leaf); + new_end = xfs_attr_leaf_entries_end(ichdr->count, leaf); + old_end = new_end - sizeof(struct xfs_attr_leaf_entry); + + ASSERT(ichdr->firstused >= new_end); for (i = 0; i < XFS_ATTR_LEAF_MAPSIZE; i++) { - if (ichdr->freemap[i].base == tmp) { - ichdr->freemap[i].base += sizeof(xfs_attr_leaf_entry_t); + int diff = 0; + + if (ichdr->freemap[i].base == old_end) { + /* + * This freemap entry starts at the old end of the + * leaf entry array, so we need to adjust its base + * upward to accomodate the larger array. + */ + diff = sizeof(struct xfs_attr_leaf_entry); + } else if (ichdr->freemap[i].size > 0 && + ichdr->freemap[i].base < new_end) { + /* + * This freemap entry starts in the space claimed by + * the new leaf entry. Adjust its base upward to + * reflect that. + */ + diff = new_end - ichdr->freemap[i].base; + } + + if (diff) { + ichdr->freemap[i].base += diff; ichdr->freemap[i].size -= - min_t(uint16_t, ichdr->freemap[i].size, - sizeof(xfs_attr_leaf_entry_t)); + min_t(uint16_t, ichdr->freemap[i].size, diff); + } + + /* + * Don't leave zero-length freemaps with nonzero base lying + * around, because we don't want the code in _remove that + * matches on base address to get confused and create + * overlapping freemaps. If we end up with no freemap entries + * then the next _add will compact the leaf block and + * regenerate the freemaps. + */ + if (ichdr->freemap[i].size == 0 && ichdr->freemap[i].base > 0) { + ichdr->freemap[i].base = 0; + ichdr->holes = 1; } } ichdr->usedbytes += xfs_attr_leaf_entsize(leaf, args->index); @@ -1623,6 +1753,10 @@ xfs_attr3_leaf_compact( ichdr_dst->freemap[0].base = xfs_attr3_leaf_hdr_size(leaf_src); ichdr_dst->freemap[0].size = ichdr_dst->firstused - ichdr_dst->freemap[0].base; + ichdr_dst->freemap[1].base = 0; + ichdr_dst->freemap[2].base = 0; + ichdr_dst->freemap[1].size = 0; + ichdr_dst->freemap[2].size = 0; /* write the header back to initialise the underlying buffer */ xfs_attr3_leaf_hdr_to_disk(args->geo, leaf_dst, ichdr_dst); @@ -1774,8 +1908,8 @@ xfs_attr3_leaf_rebalance( /* * leaf2 is the destination, compact it if it looks tight. */ - max = ichdr2.firstused - xfs_attr3_leaf_hdr_size(leaf1); - max -= ichdr2.count * sizeof(xfs_attr_leaf_entry_t); + max = ichdr2.firstused - + xfs_attr_leaf_entries_end(ichdr2.count, leaf1); if (space > max) xfs_attr3_leaf_compact(args, &ichdr2, blk2->bp); @@ -1803,8 +1937,8 @@ xfs_attr3_leaf_rebalance( /* * leaf1 is the destination, compact it if it looks tight. */ - max = ichdr1.firstused - xfs_attr3_leaf_hdr_size(leaf1); - max -= ichdr1.count * sizeof(xfs_attr_leaf_entry_t); + max = ichdr1.firstused - + xfs_attr_leaf_entries_end(ichdr1.count, leaf1); if (space > max) xfs_attr3_leaf_compact(args, &ichdr1, blk1->bp); @@ -2010,9 +2144,7 @@ xfs_attr3_leaf_toosmall( blk = &state->path.blk[ state->path.active-1 ]; leaf = blk->bp->b_addr; xfs_attr3_leaf_hdr_from_disk(state->args->geo, &ichdr, leaf); - bytes = xfs_attr3_leaf_hdr_size(leaf) + - ichdr.count * sizeof(xfs_attr_leaf_entry_t) + - ichdr.usedbytes; + bytes = xfs_attr_leaf_entries_end(ichdr.count, leaf) + ichdr.usedbytes; if (bytes > (state->args->geo->blksize >> 1)) { *action = 0; /* blk over 50%, don't try to join */ return 0; @@ -2070,9 +2202,8 @@ xfs_attr3_leaf_toosmall( bytes = state->args->geo->blksize - (state->args->geo->blksize >> 2) - ichdr.usedbytes - ichdr2.usedbytes - - ((ichdr.count + ichdr2.count) * - sizeof(xfs_attr_leaf_entry_t)) - - xfs_attr3_leaf_hdr_size(leaf); + xfs_attr_leaf_entries_end(ichdr.count + ichdr2.count, + leaf); xfs_trans_brelse(state->args->trans, bp); if (bytes >= 0) @@ -2134,8 +2265,7 @@ xfs_attr3_leaf_remove( ASSERT(ichdr.count > 0 && ichdr.count < args->geo->blksize / 8); ASSERT(args->index >= 0 && args->index < ichdr.count); - ASSERT(ichdr.firstused >= ichdr.count * sizeof(*entry) + - xfs_attr3_leaf_hdr_size(leaf)); + ASSERT(ichdr.firstused >= xfs_attr_leaf_entries_end(ichdr.count, leaf)); entry = &xfs_attr3_leaf_entryp(leaf)[args->index]; @@ -2148,8 +2278,7 @@ xfs_attr3_leaf_remove( * find smallest free region in case we need to replace it, * adjust any map that borders the entry table, */ - tablesize = ichdr.count * sizeof(xfs_attr_leaf_entry_t) - + xfs_attr3_leaf_hdr_size(leaf); + tablesize = xfs_attr_leaf_entries_end(ichdr.count, leaf); tmp = ichdr.freemap[0].size; before = after = -1; smallest = XFS_ATTR_LEAF_MAPSIZE - 1; @@ -2256,8 +2385,7 @@ xfs_attr3_leaf_remove( * Check if leaf is less than 50% full, caller may want to * "join" the leaf with a sibling if so. */ - tmp = ichdr.usedbytes + xfs_attr3_leaf_hdr_size(leaf) + - ichdr.count * sizeof(xfs_attr_leaf_entry_t); + tmp = ichdr.usedbytes + xfs_attr_leaf_entries_end(ichdr.count, leaf); return tmp < args->geo->magicpct; /* leaf is < 37% full */ } @@ -2580,11 +2708,11 @@ xfs_attr3_leaf_moveents( ichdr_s->magic == XFS_ATTR3_LEAF_MAGIC); ASSERT(ichdr_s->magic == ichdr_d->magic); ASSERT(ichdr_s->count > 0 && ichdr_s->count < args->geo->blksize / 8); - ASSERT(ichdr_s->firstused >= (ichdr_s->count * sizeof(*entry_s)) - + xfs_attr3_leaf_hdr_size(leaf_s)); + ASSERT(ichdr_s->firstused >= + xfs_attr_leaf_entries_end(ichdr_s->count, leaf_s)); ASSERT(ichdr_d->count < args->geo->blksize / 8); - ASSERT(ichdr_d->firstused >= (ichdr_d->count * sizeof(*entry_d)) - + xfs_attr3_leaf_hdr_size(leaf_d)); + ASSERT(ichdr_d->firstused >= + xfs_attr_leaf_entries_end(ichdr_d->count, leaf_d)); ASSERT(start_s < ichdr_s->count); ASSERT(start_d <= ichdr_d->count); @@ -2644,8 +2772,7 @@ xfs_attr3_leaf_moveents( ichdr_d->usedbytes += tmp; ichdr_s->count -= 1; ichdr_d->count += 1; - tmp = ichdr_d->count * sizeof(xfs_attr_leaf_entry_t) - + xfs_attr3_leaf_hdr_size(leaf_d); + tmp = xfs_attr_leaf_entries_end(ichdr_d->count, leaf_d); ASSERT(ichdr_d->firstused >= tmp); #ifdef GROT } @@ -2681,8 +2808,8 @@ xfs_attr3_leaf_moveents( /* * Fill in the freemap information */ - ichdr_d->freemap[0].base = xfs_attr3_leaf_hdr_size(leaf_d); - ichdr_d->freemap[0].base += ichdr_d->count * sizeof(xfs_attr_leaf_entry_t); + ichdr_d->freemap[0].base = + xfs_attr_leaf_entries_end(ichdr_d->count, leaf_d); ichdr_d->freemap[0].size = ichdr_d->firstused - ichdr_d->freemap[0].base; ichdr_d->freemap[1].base = 0; ichdr_d->freemap[2].base = 0; diff --git a/fs/xfs/libxfs/xfs_attr_leaf.h b/fs/xfs/libxfs/xfs_attr_leaf.h index 589f810eedc0..aca46da2bc50 100644 --- a/fs/xfs/libxfs/xfs_attr_leaf.h +++ b/fs/xfs/libxfs/xfs_attr_leaf.h @@ -46,6 +46,7 @@ struct xfs_attr3_icleaf_hdr { * Internal routines when attribute fork size < XFS_LITINO(mp). */ void xfs_attr_shortform_create(struct xfs_da_args *args); +int xfs_attr_shortform_replace(struct xfs_da_args *args); void xfs_attr_shortform_add(struct xfs_da_args *args, int forkoff); int xfs_attr_shortform_getvalue(struct xfs_da_args *args); int xfs_attr_shortform_to_leaf(struct xfs_da_args *args); diff --git a/fs/xfs/libxfs/xfs_attr_remote.c b/fs/xfs/libxfs/xfs_attr_remote.c index bff3dc226f81..e6c8dd1a997a 100644 --- a/fs/xfs/libxfs/xfs_attr_remote.c +++ b/fs/xfs/libxfs/xfs_attr_remote.c @@ -4,7 +4,7 @@ * Copyright (c) 2013 Red Hat, Inc. * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/libxfs/xfs_bit.c b/fs/xfs/libxfs/xfs_bit.c index 40ce5f3094d1..f05a07c0f75d 100644 --- a/fs/xfs/libxfs/xfs_bit.c +++ b/fs/xfs/libxfs/xfs_bit.c @@ -3,7 +3,7 @@ * Copyright (c) 2000-2005 Silicon Graphics, Inc. * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_log_format.h" #include "xfs_bit.h" diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c index 53ef4b7e504d..7a4c8f1aa76c 100644 --- a/fs/xfs/libxfs/xfs_bmap.c +++ b/fs/xfs/libxfs/xfs_bmap.c @@ -3,7 +3,7 @@ * Copyright (c) 2000-2006 Silicon Graphics, Inc. * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/libxfs/xfs_bmap_btree.c b/fs/xfs/libxfs/xfs_bmap_btree.c index 188feac04b60..1c7165df483a 100644 --- a/fs/xfs/libxfs/xfs_bmap_btree.c +++ b/fs/xfs/libxfs/xfs_bmap_btree.c @@ -3,7 +3,7 @@ * Copyright (c) 2000-2003,2005 Silicon Graphics, Inc. * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/libxfs/xfs_btree.c b/fs/xfs/libxfs/xfs_btree.c index dbe9df8c3300..7012f3570c8d 100644 --- a/fs/xfs/libxfs/xfs_btree.c +++ b/fs/xfs/libxfs/xfs_btree.c @@ -3,7 +3,7 @@ * Copyright (c) 2000-2002,2005 Silicon Graphics, Inc. * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/libxfs/xfs_btree_mem.c b/fs/xfs/libxfs/xfs_btree_mem.c index f2f7b4305413..37136a70e56d 100644 --- a/fs/xfs/libxfs/xfs_btree_mem.c +++ b/fs/xfs/libxfs/xfs_btree_mem.c @@ -3,7 +3,7 @@ * Copyright (c) 2021-2024 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/libxfs/xfs_btree_staging.c b/fs/xfs/libxfs/xfs_btree_staging.c index 5ed84f9cc877..4300c058807b 100644 --- a/fs/xfs/libxfs/xfs_btree_staging.c +++ b/fs/xfs/libxfs/xfs_btree_staging.c @@ -3,7 +3,7 @@ * Copyright (C) 2020 Oracle. All Rights Reserved. * Author: Darrick J. Wong <darrick.wong@oracle.com> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/libxfs/xfs_da_btree.c b/fs/xfs/libxfs/xfs_da_btree.c index 90f7fc219fcc..766631f0562e 100644 --- a/fs/xfs/libxfs/xfs_da_btree.c +++ b/fs/xfs/libxfs/xfs_da_btree.c @@ -4,7 +4,7 @@ * Copyright (c) 2013 Red Hat, Inc. * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/libxfs/xfs_da_format.h b/fs/xfs/libxfs/xfs_da_format.h index 86de99e2f757..7d55307e619f 100644 --- a/fs/xfs/libxfs/xfs_da_format.h +++ b/fs/xfs/libxfs/xfs_da_format.h @@ -746,7 +746,7 @@ struct xfs_attr3_leafblock { #define XFS_ATTR_LEAF_NAME_ALIGN ((uint)sizeof(xfs_dablk_t)) static inline int -xfs_attr3_leaf_hdr_size(struct xfs_attr_leafblock *leafp) +xfs_attr3_leaf_hdr_size(const struct xfs_attr_leafblock *leafp) { if (leafp->hdr.info.magic == cpu_to_be16(XFS_ATTR3_LEAF_MAGIC)) return sizeof(struct xfs_attr3_leaf_hdr); diff --git a/fs/xfs/libxfs/xfs_defer.c b/fs/xfs/libxfs/xfs_defer.c index 5b377cbbb1f7..c39e40dcb0b0 100644 --- a/fs/xfs/libxfs/xfs_defer.c +++ b/fs/xfs/libxfs/xfs_defer.c @@ -3,7 +3,7 @@ * Copyright (C) 2016 Oracle. All Rights Reserved. * Author: Darrick J. Wong <darrick.wong@oracle.com> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" @@ -565,7 +565,7 @@ xfs_defer_relog( continue; trace_xfs_defer_relog_intent((*tpp)->t_mountp, dfp); - XFS_STATS_INC((*tpp)->t_mountp, defer_relog); + XFS_STATS_INC((*tpp)->t_mountp, xs_defer_relog); xfs_defer_relog_intent(*tpp, dfp); } diff --git a/fs/xfs/libxfs/xfs_dir2.c b/fs/xfs/libxfs/xfs_dir2.c index 82a338458a51..107c1a5b8a96 100644 --- a/fs/xfs/libxfs/xfs_dir2.c +++ b/fs/xfs/libxfs/xfs_dir2.c @@ -3,7 +3,7 @@ * Copyright (c) 2000-2001,2005 Silicon Graphics, Inc. * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/libxfs/xfs_dir2_block.c b/fs/xfs/libxfs/xfs_dir2_block.c index 0f93ed1a4a74..6d70e6b429e7 100644 --- a/fs/xfs/libxfs/xfs_dir2_block.c +++ b/fs/xfs/libxfs/xfs_dir2_block.c @@ -4,7 +4,7 @@ * Copyright (c) 2013 Red Hat, Inc. * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/libxfs/xfs_dir2_data.c b/fs/xfs/libxfs/xfs_dir2_data.c index a16b05c43e2e..80ba94f51e5c 100644 --- a/fs/xfs/libxfs/xfs_dir2_data.c +++ b/fs/xfs/libxfs/xfs_dir2_data.c @@ -4,7 +4,7 @@ * Copyright (c) 2013 Red Hat, Inc. * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/libxfs/xfs_dir2_leaf.c b/fs/xfs/libxfs/xfs_dir2_leaf.c index 71c2f22a3f6e..bc909543eb74 100644 --- a/fs/xfs/libxfs/xfs_dir2_leaf.c +++ b/fs/xfs/libxfs/xfs_dir2_leaf.c @@ -4,7 +4,7 @@ * Copyright (c) 2013 Red Hat, Inc. * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/libxfs/xfs_dir2_node.c b/fs/xfs/libxfs/xfs_dir2_node.c index fe8d4fa13128..ed0b5287a44f 100644 --- a/fs/xfs/libxfs/xfs_dir2_node.c +++ b/fs/xfs/libxfs/xfs_dir2_node.c @@ -4,7 +4,7 @@ * Copyright (c) 2013 Red Hat, Inc. * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/libxfs/xfs_dir2_sf.c b/fs/xfs/libxfs/xfs_dir2_sf.c index 17a20384c8b7..1a67cdd6a707 100644 --- a/fs/xfs/libxfs/xfs_dir2_sf.c +++ b/fs/xfs/libxfs/xfs_dir2_sf.c @@ -3,7 +3,7 @@ * Copyright (c) 2000-2003,2005 Silicon Graphics, Inc. * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/libxfs/xfs_dquot_buf.c b/fs/xfs/libxfs/xfs_dquot_buf.c index dceef2abd4e2..ce767b40482f 100644 --- a/fs/xfs/libxfs/xfs_dquot_buf.c +++ b/fs/xfs/libxfs/xfs_dquot_buf.c @@ -4,7 +4,7 @@ * Copyright (c) 2013 Red Hat, Inc. * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/libxfs/xfs_errortag.h b/fs/xfs/libxfs/xfs_errortag.h index 57e47077c75a..6de207fed2d8 100644 --- a/fs/xfs/libxfs/xfs_errortag.h +++ b/fs/xfs/libxfs/xfs_errortag.h @@ -53,7 +53,7 @@ * Drop-writes support removed because write error handling cannot trash * pre-existing delalloc extents in any useful way anymore. We retain the * definition so that we can reject it as an invalid value in - * xfs_errortag_valid(). + * xfs_errortag_add(). */ #define XFS_ERRTAG_DROP_WRITES 28 #define XFS_ERRTAG_LOG_BAD_CRC 29 @@ -74,7 +74,8 @@ #define XFS_ERRTAG_EXCHMAPS_FINISH_ONE 44 #define XFS_ERRTAG_METAFILE_RESV_CRITICAL 45 #define XFS_ERRTAG_FORCE_ZERO_RANGE 46 -#define XFS_ERRTAG_MAX 47 +#define XFS_ERRTAG_ZONE_RESET 47 +#define XFS_ERRTAG_MAX 48 /* * Random factors for above tags, 1 means always, 2 means 1/2 time, etc. @@ -135,7 +136,8 @@ XFS_ERRTAG(WB_DELAY_MS, wb_delay_ms, 3000) \ XFS_ERRTAG(WRITE_DELAY_MS, write_delay_ms, 3000) \ XFS_ERRTAG(EXCHMAPS_FINISH_ONE, exchmaps_finish_one, 1) \ XFS_ERRTAG(METAFILE_RESV_CRITICAL, metafile_resv_crit, 4) \ -XFS_ERRTAG(FORCE_ZERO_RANGE, force_zero_range, 4) +XFS_ERRTAG(FORCE_ZERO_RANGE, force_zero_range, 4) \ +XFS_ERRTAG(ZONE_RESET, zone_reset, 1) #endif /* XFS_ERRTAG */ #endif /* __XFS_ERRORTAG_H_ */ diff --git a/fs/xfs/libxfs/xfs_exchmaps.c b/fs/xfs/libxfs/xfs_exchmaps.c index 932ee4619e9e..5d28f4eac527 100644 --- a/fs/xfs/libxfs/xfs_exchmaps.c +++ b/fs/xfs/libxfs/xfs_exchmaps.c @@ -3,7 +3,7 @@ * Copyright (c) 2020-2024 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/libxfs/xfs_fs.h b/fs/xfs/libxfs/xfs_fs.h index 12463ba766da..d165de607d17 100644 --- a/fs/xfs/libxfs/xfs_fs.h +++ b/fs/xfs/libxfs/xfs_fs.h @@ -1003,6 +1003,191 @@ struct xfs_rtgroup_geometry { #define XFS_RTGROUP_GEOM_SICK_RMAPBT (1U << 3) /* reverse mappings */ #define XFS_RTGROUP_GEOM_SICK_REFCNTBT (1U << 4) /* reference counts */ +/* Health monitor event domains */ + +/* affects the whole fs */ +#define XFS_HEALTH_MONITOR_DOMAIN_MOUNT (0) + +/* metadata health events */ +#define XFS_HEALTH_MONITOR_DOMAIN_FS (1) +#define XFS_HEALTH_MONITOR_DOMAIN_AG (2) +#define XFS_HEALTH_MONITOR_DOMAIN_INODE (3) +#define XFS_HEALTH_MONITOR_DOMAIN_RTGROUP (4) + +/* disk events */ +#define XFS_HEALTH_MONITOR_DOMAIN_DATADEV (5) +#define XFS_HEALTH_MONITOR_DOMAIN_RTDEV (6) +#define XFS_HEALTH_MONITOR_DOMAIN_LOGDEV (7) + +/* file range events */ +#define XFS_HEALTH_MONITOR_DOMAIN_FILERANGE (8) + +/* Health monitor event types */ + +/* status of the monitor itself */ +#define XFS_HEALTH_MONITOR_TYPE_RUNNING (0) +#define XFS_HEALTH_MONITOR_TYPE_LOST (1) + +/* filesystem was unmounted */ +#define XFS_HEALTH_MONITOR_TYPE_UNMOUNT (2) + +/* metadata health events */ +#define XFS_HEALTH_MONITOR_TYPE_SICK (3) +#define XFS_HEALTH_MONITOR_TYPE_CORRUPT (4) +#define XFS_HEALTH_MONITOR_TYPE_HEALTHY (5) + +/* filesystem shutdown */ +#define XFS_HEALTH_MONITOR_TYPE_SHUTDOWN (6) + +/* media errors */ +#define XFS_HEALTH_MONITOR_TYPE_MEDIA_ERROR (7) + +/* pagecache I/O to a file range failed */ +#define XFS_HEALTH_MONITOR_TYPE_BUFREAD (8) +#define XFS_HEALTH_MONITOR_TYPE_BUFWRITE (9) + +/* direct I/O to a file range failed */ +#define XFS_HEALTH_MONITOR_TYPE_DIOREAD (10) +#define XFS_HEALTH_MONITOR_TYPE_DIOWRITE (11) + +/* out of band media error reported for a file range */ +#define XFS_HEALTH_MONITOR_TYPE_DATALOST (12) + +/* lost events */ +struct xfs_health_monitor_lost { + __u64 count; +}; + +/* fs/rt metadata */ +struct xfs_health_monitor_fs { + /* XFS_FSOP_GEOM_SICK_* flags */ + __u32 mask; +}; + +/* ag/rtgroup metadata */ +struct xfs_health_monitor_group { + /* XFS_{AG,RTGROUP}_SICK_* flags */ + __u32 mask; + __u32 gno; +}; + +/* inode metadata */ +struct xfs_health_monitor_inode { + /* XFS_BS_SICK_* flags */ + __u32 mask; + __u32 gen; + __u64 ino; +}; + +/* shutdown reasons */ +#define XFS_HEALTH_SHUTDOWN_META_IO_ERROR (1u << 0) +#define XFS_HEALTH_SHUTDOWN_LOG_IO_ERROR (1u << 1) +#define XFS_HEALTH_SHUTDOWN_FORCE_UMOUNT (1u << 2) +#define XFS_HEALTH_SHUTDOWN_CORRUPT_INCORE (1u << 3) +#define XFS_HEALTH_SHUTDOWN_CORRUPT_ONDISK (1u << 4) +#define XFS_HEALTH_SHUTDOWN_DEVICE_REMOVED (1u << 5) + +/* shutdown */ +struct xfs_health_monitor_shutdown { + /* XFS_HEALTH_SHUTDOWN_* flags */ + __u32 reasons; +}; + +/* file range events */ +struct xfs_health_monitor_filerange { + __u64 pos; + __u64 len; + __u64 ino; + __u32 gen; + __u32 error; +}; + +/* disk media errors */ +struct xfs_health_monitor_media { + __u64 daddr; + __u64 bbcount; +}; + +struct xfs_health_monitor_event { + /* XFS_HEALTH_MONITOR_DOMAIN_* */ + __u32 domain; + + /* XFS_HEALTH_MONITOR_TYPE_* */ + __u32 type; + + /* Timestamp of the event, in nanoseconds since the Unix epoch */ + __u64 time_ns; + + /* + * Details of the event. The primary clients are written in python + * and rust, so break this up because bindgen hates anonymous structs + * and unions. + */ + union { + struct xfs_health_monitor_lost lost; + struct xfs_health_monitor_fs fs; + struct xfs_health_monitor_group group; + struct xfs_health_monitor_inode inode; + struct xfs_health_monitor_shutdown shutdown; + struct xfs_health_monitor_media media; + struct xfs_health_monitor_filerange filerange; + } e; + + /* zeroes */ + __u64 pad[2]; +}; + +struct xfs_health_monitor { + __u64 flags; /* flags */ + __u8 format; /* output format */ + __u8 pad[23]; /* zeroes */ +}; + +/* Return all health status events, not just deltas */ +#define XFS_HEALTH_MONITOR_VERBOSE (1ULL << 0) + +#define XFS_HEALTH_MONITOR_ALL (XFS_HEALTH_MONITOR_VERBOSE) + +/* Initial return format version */ +#define XFS_HEALTH_MONITOR_FMT_V0 (0) + +/* + * Check that a given fd points to the same filesystem that the health monitor + * is monitoring. + */ +struct xfs_health_file_on_monitored_fs { + __s32 fd; + __u32 flags; /* zero for now */ +}; + +/* Verify the media of the underlying devices */ +struct xfs_verify_media { + __u32 me_dev; /* I: XFS_DEV_{DATA,LOG,RT} */ + __u32 me_flags; /* I: XFS_VERIFY_MEDIA_* */ + + /* + * IO: inclusive start of disk range to verify, in 512b blocks. + * Will be adjusted upwards as media verification succeeds. + */ + __u64 me_start_daddr; + + /* + * IO: exclusive end of the disk range to verify, in 512b blocks. + * Can be adjusted downwards to match device size. + */ + __u64 me_end_daddr; + + __u32 me_ioerror; /* O: I/O error (positive) */ + __u32 me_max_io_size; /* I: maximum IO size in bytes */ + + __u32 me_rest_us; /* I: rest time between IOs, usecs */ + __u32 me_pad; /* zero */ +}; + +#define XFS_VERIFY_MEDIA_REPORT (1 << 0) /* report to fsnotify */ + +#define XFS_VERIFY_MEDIA_FLAGS (XFS_VERIFY_MEDIA_REPORT) + /* * ioctl commands that are used by Linux filesystems */ @@ -1042,6 +1227,10 @@ struct xfs_rtgroup_geometry { #define XFS_IOC_GETPARENTS_BY_HANDLE _IOWR('X', 63, struct xfs_getparents_by_handle) #define XFS_IOC_SCRUBV_METADATA _IOWR('X', 64, struct xfs_scrub_vec_head) #define XFS_IOC_RTGROUP_GEOMETRY _IOWR('X', 65, struct xfs_rtgroup_geometry) +#define XFS_IOC_HEALTH_MONITOR _IOW ('X', 68, struct xfs_health_monitor) +#define XFS_IOC_HEALTH_FD_ON_MONITORED_FS \ + _IOW ('X', 69, struct xfs_health_file_on_monitored_fs) +#define XFS_IOC_VERIFY_MEDIA _IOWR('X', 70, struct xfs_verify_media) /* * ioctl commands that replace IRIX syssgi()'s diff --git a/fs/xfs/libxfs/xfs_group.c b/fs/xfs/libxfs/xfs_group.c index 792f76d2e2a0..2ff9d1e56b47 100644 --- a/fs/xfs/libxfs/xfs_group.c +++ b/fs/xfs/libxfs/xfs_group.c @@ -3,7 +3,7 @@ * Copyright (c) 2018 Red Hat, Inc. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_shared.h" #include "xfs_format.h" #include "xfs_trans_resv.h" diff --git a/fs/xfs/libxfs/xfs_health.h b/fs/xfs/libxfs/xfs_health.h index b31000f7190c..1d45cf5789e8 100644 --- a/fs/xfs/libxfs/xfs_health.h +++ b/fs/xfs/libxfs/xfs_health.h @@ -289,4 +289,9 @@ void xfs_bulkstat_health(struct xfs_inode *ip, struct xfs_bulkstat *bs); #define xfs_metadata_is_sick(error) \ (unlikely((error) == -EFSCORRUPTED || (error) == -EFSBADCRC)) +unsigned int xfs_healthmon_inode_mask(unsigned int sick_mask); +unsigned int xfs_healthmon_rtgroup_mask(unsigned int sick_mask); +unsigned int xfs_healthmon_perag_mask(unsigned int sick_mask); +unsigned int xfs_healthmon_fs_mask(unsigned int sick_mask); + #endif /* __XFS_HEALTH_H__ */ diff --git a/fs/xfs/libxfs/xfs_ialloc.c b/fs/xfs/libxfs/xfs_ialloc.c index c19d6d713780..dcef06ec0a02 100644 --- a/fs/xfs/libxfs/xfs_ialloc.c +++ b/fs/xfs/libxfs/xfs_ialloc.c @@ -3,7 +3,7 @@ * Copyright (c) 2000-2002,2005 Silicon Graphics, Inc. * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/libxfs/xfs_ialloc_btree.c b/fs/xfs/libxfs/xfs_ialloc_btree.c index 100afdd66cdd..1376e8630449 100644 --- a/fs/xfs/libxfs/xfs_ialloc_btree.c +++ b/fs/xfs/libxfs/xfs_ialloc_btree.c @@ -3,7 +3,7 @@ * Copyright (c) 2000-2001,2005 Silicon Graphics, Inc. * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/libxfs/xfs_iext_tree.c b/fs/xfs/libxfs/xfs_iext_tree.c index 8796f2b3e534..5b2b926ab228 100644 --- a/fs/xfs/libxfs/xfs_iext_tree.c +++ b/fs/xfs/libxfs/xfs_iext_tree.c @@ -3,7 +3,7 @@ * Copyright (c) 2017 Christoph Hellwig. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_shared.h" #include "xfs_format.h" #include "xfs_bit.h" diff --git a/fs/xfs/libxfs/xfs_inode_buf.c b/fs/xfs/libxfs/xfs_inode_buf.c index b1812b2c3cce..a017016e9075 100644 --- a/fs/xfs/libxfs/xfs_inode_buf.c +++ b/fs/xfs/libxfs/xfs_inode_buf.c @@ -3,7 +3,7 @@ * Copyright (c) 2000-2006 Silicon Graphics, Inc. * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/libxfs/xfs_inode_fork.c b/fs/xfs/libxfs/xfs_inode_fork.c index 1772d82f2d68..d14a7f2f4c03 100644 --- a/fs/xfs/libxfs/xfs_inode_fork.c +++ b/fs/xfs/libxfs/xfs_inode_fork.c @@ -4,7 +4,7 @@ * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/libxfs/xfs_inode_util.c b/fs/xfs/libxfs/xfs_inode_util.c index 309ce6dd5553..551fa51befb6 100644 --- a/fs/xfs/libxfs/xfs_inode_util.c +++ b/fs/xfs/libxfs/xfs_inode_util.c @@ -4,7 +4,7 @@ * All Rights Reserved. */ #include <linux/iversion.h> -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/libxfs/xfs_log_format.h b/fs/xfs/libxfs/xfs_log_format.h index 908e7060428c..3f5a24dda907 100644 --- a/fs/xfs/libxfs/xfs_log_format.h +++ b/fs/xfs/libxfs/xfs_log_format.h @@ -184,13 +184,6 @@ struct xlog_rec_header { #define XLOG_REC_SIZE_OTHER offsetofend(struct xlog_rec_header, h_size) #endif /* __i386__ */ -/* not an on-disk structure, but needed by log recovery in userspace */ -struct xfs_log_iovec { - void *i_addr; /* beginning address of region */ - int i_len; /* length in bytes of region */ - uint i_type; /* type of region */ -}; - /* * Transaction Header definitions. * diff --git a/fs/xfs/libxfs/xfs_log_rlimit.c b/fs/xfs/libxfs/xfs_log_rlimit.c index 34bba96d30ca..37712b2f8757 100644 --- a/fs/xfs/libxfs/xfs_log_rlimit.c +++ b/fs/xfs/libxfs/xfs_log_rlimit.c @@ -3,7 +3,7 @@ * Copyright (c) 2013 Jie Liu. * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/libxfs/xfs_metadir.c b/fs/xfs/libxfs/xfs_metadir.c index 178e89711cb7..3e5c61188927 100644 --- a/fs/xfs/libxfs/xfs_metadir.c +++ b/fs/xfs/libxfs/xfs_metadir.c @@ -3,7 +3,7 @@ * Copyright (c) 2018-2024 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/libxfs/xfs_metafile.c b/fs/xfs/libxfs/xfs_metafile.c index b02e3d6c0868..cf239f862212 100644 --- a/fs/xfs/libxfs/xfs_metafile.c +++ b/fs/xfs/libxfs/xfs_metafile.c @@ -3,7 +3,7 @@ * Copyright (c) 2018-2024 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/libxfs/xfs_parent.c b/fs/xfs/libxfs/xfs_parent.c index 69366c44a701..3509cc4b2175 100644 --- a/fs/xfs/libxfs/xfs_parent.c +++ b/fs/xfs/libxfs/xfs_parent.c @@ -3,7 +3,7 @@ * Copyright (c) 2022-2024 Oracle. * All rights reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_format.h" #include "xfs_da_format.h" @@ -29,6 +29,7 @@ #include "xfs_trans_space.h" #include "xfs_attr_item.h" #include "xfs_health.h" +#include "xfs_attr_leaf.h" struct kmem_cache *xfs_parent_args_cache; @@ -202,8 +203,8 @@ xfs_parent_addname( xfs_inode_to_parent_rec(&ppargs->rec, dp); xfs_parent_da_args_init(&ppargs->args, tp, &ppargs->rec, child, child->i_ino, parent_name); - xfs_attr_defer_add(&ppargs->args, XFS_ATTR_DEFER_SET); - return 0; + + return xfs_attr_setname(&ppargs->args, 0); } /* Remove a parent pointer to reflect a dirent removal. */ @@ -224,8 +225,8 @@ xfs_parent_removename( xfs_inode_to_parent_rec(&ppargs->rec, dp); xfs_parent_da_args_init(&ppargs->args, tp, &ppargs->rec, child, child->i_ino, parent_name); - xfs_attr_defer_add(&ppargs->args, XFS_ATTR_DEFER_REMOVE); - return 0; + + return xfs_attr_removename(&ppargs->args); } /* Replace one parent pointer with another to reflect a rename. */ @@ -250,12 +251,13 @@ xfs_parent_replacename( child->i_ino, old_name); xfs_inode_to_parent_rec(&ppargs->new_rec, new_dp); + ppargs->args.new_name = new_name->name; ppargs->args.new_namelen = new_name->len; ppargs->args.new_value = &ppargs->new_rec; ppargs->args.new_valuelen = sizeof(struct xfs_parent_rec); - xfs_attr_defer_add(&ppargs->args, XFS_ATTR_DEFER_REPLACE); - return 0; + + return xfs_attr_replacename(&ppargs->args, 0); } /* diff --git a/fs/xfs/libxfs/xfs_refcount.c b/fs/xfs/libxfs/xfs_refcount.c index 2484dc9f6d7e..915ec85530e7 100644 --- a/fs/xfs/libxfs/xfs_refcount.c +++ b/fs/xfs/libxfs/xfs_refcount.c @@ -3,7 +3,7 @@ * Copyright (C) 2016 Oracle. All Rights Reserved. * Author: Darrick J. Wong <darrick.wong@oracle.com> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/libxfs/xfs_refcount_btree.c b/fs/xfs/libxfs/xfs_refcount_btree.c index 06da3ca14727..7e5f92c1ac56 100644 --- a/fs/xfs/libxfs/xfs_refcount_btree.c +++ b/fs/xfs/libxfs/xfs_refcount_btree.c @@ -3,7 +3,7 @@ * Copyright (C) 2016 Oracle. All Rights Reserved. * Author: Darrick J. Wong <darrick.wong@oracle.com> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/libxfs/xfs_rmap.c b/fs/xfs/libxfs/xfs_rmap.c index 83e0488ff773..e78133c908ca 100644 --- a/fs/xfs/libxfs/xfs_rmap.c +++ b/fs/xfs/libxfs/xfs_rmap.c @@ -3,7 +3,7 @@ * Copyright (c) 2014 Red Hat, Inc. * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/libxfs/xfs_rmap_btree.c b/fs/xfs/libxfs/xfs_rmap_btree.c index bf16aee50d73..10b3272238eb 100644 --- a/fs/xfs/libxfs/xfs_rmap_btree.c +++ b/fs/xfs/libxfs/xfs_rmap_btree.c @@ -3,7 +3,7 @@ * Copyright (c) 2014 Red Hat, Inc. * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/libxfs/xfs_rtbitmap.c b/fs/xfs/libxfs/xfs_rtbitmap.c index 618061d898d4..bc4c0a99f4dd 100644 --- a/fs/xfs/libxfs/xfs_rtbitmap.c +++ b/fs/xfs/libxfs/xfs_rtbitmap.c @@ -3,7 +3,7 @@ * Copyright (c) 2000-2005 Silicon Graphics, Inc. * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/libxfs/xfs_rtgroup.c b/fs/xfs/libxfs/xfs_rtgroup.c index be16efaa6925..09328f2d1575 100644 --- a/fs/xfs/libxfs/xfs_rtgroup.c +++ b/fs/xfs/libxfs/xfs_rtgroup.c @@ -3,7 +3,7 @@ * Copyright (c) 2022-2024 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/libxfs/xfs_rtgroup.h b/fs/xfs/libxfs/xfs_rtgroup.h index 73cace4d25c7..c0b9f9f2c413 100644 --- a/fs/xfs/libxfs/xfs_rtgroup.h +++ b/fs/xfs/libxfs/xfs_rtgroup.h @@ -371,4 +371,19 @@ xfs_rtgs_to_rfsbs( return xfs_groups_to_rfsbs(mp, nr_groups, XG_TYPE_RTG); } +/* + * Return the "raw" size of a group on the hardware device. This includes the + * daddr gaps present for XFS_SB_FEAT_INCOMPAT_ZONE_GAPS file systems. + */ +static inline xfs_rgblock_t +xfs_rtgroup_raw_size( + struct xfs_mount *mp) +{ + struct xfs_groups *g = &mp->m_groups[XG_TYPE_RTG]; + + if (g->has_daddr_gaps) + return 1U << g->blklog; + return g->blocks; +} + #endif /* __LIBXFS_RTGROUP_H */ diff --git a/fs/xfs/libxfs/xfs_rtrefcount_btree.c b/fs/xfs/libxfs/xfs_rtrefcount_btree.c index ac11e94b42ae..c1518267eb17 100644 --- a/fs/xfs/libxfs/xfs_rtrefcount_btree.c +++ b/fs/xfs/libxfs/xfs_rtrefcount_btree.c @@ -3,7 +3,7 @@ * Copyright (c) 2021-2024 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/libxfs/xfs_rtrmap_btree.c b/fs/xfs/libxfs/xfs_rtrmap_btree.c index 55f903165769..00557b7ef298 100644 --- a/fs/xfs/libxfs/xfs_rtrmap_btree.c +++ b/fs/xfs/libxfs/xfs_rtrmap_btree.c @@ -3,7 +3,7 @@ * Copyright (c) 2018-2024 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/libxfs/xfs_sb.c b/fs/xfs/libxfs/xfs_sb.c index 94c272a2ae26..38d16fe1f6d8 100644 --- a/fs/xfs/libxfs/xfs_sb.c +++ b/fs/xfs/libxfs/xfs_sb.c @@ -3,7 +3,7 @@ * Copyright (c) 2000-2005 Silicon Graphics, Inc. * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/libxfs/xfs_symlink_remote.c b/fs/xfs/libxfs/xfs_symlink_remote.c index fb47a76ead18..f9a5966d8048 100644 --- a/fs/xfs/libxfs/xfs_symlink_remote.c +++ b/fs/xfs/libxfs/xfs_symlink_remote.c @@ -4,7 +4,7 @@ * Copyright (c) 2012-2013 Red Hat, Inc. * All rights reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_format.h" #include "xfs_log_format.h" diff --git a/fs/xfs/libxfs/xfs_trans_inode.c b/fs/xfs/libxfs/xfs_trans_inode.c index c962ad64b0c1..1a0fdcbf39fa 100644 --- a/fs/xfs/libxfs/xfs_trans_inode.c +++ b/fs/xfs/libxfs/xfs_trans_inode.c @@ -3,7 +3,7 @@ * Copyright (c) 2000,2005 Silicon Graphics, Inc. * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/libxfs/xfs_trans_resv.c b/fs/xfs/libxfs/xfs_trans_resv.c index 86a111d0f2fc..3151e97ca8ff 100644 --- a/fs/xfs/libxfs/xfs_trans_resv.c +++ b/fs/xfs/libxfs/xfs_trans_resv.c @@ -4,7 +4,7 @@ * Copyright (C) 2010 Red Hat, Inc. * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/libxfs/xfs_trans_space.c b/fs/xfs/libxfs/xfs_trans_space.c index b9dc3752f702..9b8f495c9049 100644 --- a/fs/xfs/libxfs/xfs_trans_space.c +++ b/fs/xfs/libxfs/xfs_trans_space.c @@ -3,7 +3,7 @@ * Copyright (c) 2000,2005 Silicon Graphics, Inc. * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/libxfs/xfs_types.c b/fs/xfs/libxfs/xfs_types.c index 1faf04204c5d..67c947a47f14 100644 --- a/fs/xfs/libxfs/xfs_types.c +++ b/fs/xfs/libxfs/xfs_types.c @@ -4,7 +4,7 @@ * Copyright (C) 2017 Oracle. * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_format.h" #include "xfs_shared.h" diff --git a/fs/xfs/libxfs/xfs_zones.c b/fs/xfs/libxfs/xfs_zones.c index b40f71f878b5..24e350c31933 100644 --- a/fs/xfs/libxfs/xfs_zones.c +++ b/fs/xfs/libxfs/xfs_zones.c @@ -3,7 +3,7 @@ * Copyright (c) 2023-2025 Christoph Hellwig. * Copyright (c) 2024-2025, Western Digital Corporation or its affiliates. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" @@ -15,173 +15,102 @@ #include "xfs_zones.h" static bool -xfs_zone_validate_empty( +xfs_validate_blk_zone_seq( + struct xfs_mount *mp, struct blk_zone *zone, - struct xfs_rtgroup *rtg, + unsigned int zone_no, xfs_rgblock_t *write_pointer) { - struct xfs_mount *mp = rtg_mount(rtg); - - if (rtg_rmap(rtg)->i_used_blocks > 0) { - xfs_warn(mp, "empty zone %u has non-zero used counter (0x%x).", - rtg_rgno(rtg), rtg_rmap(rtg)->i_used_blocks); - return false; - } - - *write_pointer = 0; - return true; -} - -static bool -xfs_zone_validate_wp( - struct blk_zone *zone, - struct xfs_rtgroup *rtg, - xfs_rgblock_t *write_pointer) -{ - struct xfs_mount *mp = rtg_mount(rtg); - xfs_rtblock_t wp_fsb = xfs_daddr_to_rtb(mp, zone->wp); - - if (rtg_rmap(rtg)->i_used_blocks > rtg->rtg_extents) { - xfs_warn(mp, "zone %u has too large used counter (0x%x).", - rtg_rgno(rtg), rtg_rmap(rtg)->i_used_blocks); - return false; - } - - if (xfs_rtb_to_rgno(mp, wp_fsb) != rtg_rgno(rtg)) { - xfs_warn(mp, "zone %u write pointer (0x%llx) outside of zone.", - rtg_rgno(rtg), wp_fsb); - return false; - } - - *write_pointer = xfs_rtb_to_rgbno(mp, wp_fsb); - if (*write_pointer >= rtg->rtg_extents) { - xfs_warn(mp, "zone %u has invalid write pointer (0x%x).", - rtg_rgno(rtg), *write_pointer); - return false; - } - - return true; -} - -static bool -xfs_zone_validate_full( - struct blk_zone *zone, - struct xfs_rtgroup *rtg, - xfs_rgblock_t *write_pointer) -{ - struct xfs_mount *mp = rtg_mount(rtg); - - if (rtg_rmap(rtg)->i_used_blocks > rtg->rtg_extents) { - xfs_warn(mp, "zone %u has too large used counter (0x%x).", - rtg_rgno(rtg), rtg_rmap(rtg)->i_used_blocks); - return false; - } - - *write_pointer = rtg->rtg_extents; - return true; -} - -static bool -xfs_zone_validate_seq( - struct blk_zone *zone, - struct xfs_rtgroup *rtg, - xfs_rgblock_t *write_pointer) -{ - struct xfs_mount *mp = rtg_mount(rtg); - switch (zone->cond) { case BLK_ZONE_COND_EMPTY: - return xfs_zone_validate_empty(zone, rtg, write_pointer); + *write_pointer = 0; + return true; case BLK_ZONE_COND_IMP_OPEN: case BLK_ZONE_COND_EXP_OPEN: case BLK_ZONE_COND_CLOSED: case BLK_ZONE_COND_ACTIVE: - return xfs_zone_validate_wp(zone, rtg, write_pointer); + if (zone->wp < zone->start || + zone->wp >= zone->start + zone->capacity) { + xfs_warn(mp, + "zone %u write pointer (%llu) outside of zone.", + zone_no, zone->wp); + return false; + } + + *write_pointer = XFS_BB_TO_FSB(mp, zone->wp - zone->start); + return true; case BLK_ZONE_COND_FULL: - return xfs_zone_validate_full(zone, rtg, write_pointer); + *write_pointer = XFS_BB_TO_FSB(mp, zone->capacity); + return true; case BLK_ZONE_COND_NOT_WP: case BLK_ZONE_COND_OFFLINE: case BLK_ZONE_COND_READONLY: xfs_warn(mp, "zone %u has unsupported zone condition 0x%x.", - rtg_rgno(rtg), zone->cond); + zone_no, zone->cond); return false; default: xfs_warn(mp, "zone %u has unknown zone condition 0x%x.", - rtg_rgno(rtg), zone->cond); + zone_no, zone->cond); return false; } } static bool -xfs_zone_validate_conv( +xfs_validate_blk_zone_conv( + struct xfs_mount *mp, struct blk_zone *zone, - struct xfs_rtgroup *rtg) + unsigned int zone_no) { - struct xfs_mount *mp = rtg_mount(rtg); - switch (zone->cond) { case BLK_ZONE_COND_NOT_WP: return true; default: xfs_warn(mp, "conventional zone %u has unsupported zone condition 0x%x.", - rtg_rgno(rtg), zone->cond); + zone_no, zone->cond); return false; } } bool -xfs_zone_validate( +xfs_validate_blk_zone( + struct xfs_mount *mp, struct blk_zone *zone, - struct xfs_rtgroup *rtg, + unsigned int zone_no, + uint32_t expected_size, + uint32_t expected_capacity, xfs_rgblock_t *write_pointer) { - struct xfs_mount *mp = rtg_mount(rtg); - struct xfs_groups *g = &mp->m_groups[XG_TYPE_RTG]; - uint32_t expected_size; - /* * Check that the zone capacity matches the rtgroup size stored in the * superblock. Note that all zones including the last one must have a * uniform capacity. */ - if (XFS_BB_TO_FSB(mp, zone->capacity) != g->blocks) { + if (XFS_BB_TO_FSB(mp, zone->capacity) != expected_capacity) { xfs_warn(mp, -"zone %u capacity (0x%llx) does not match RT group size (0x%x).", - rtg_rgno(rtg), XFS_BB_TO_FSB(mp, zone->capacity), - g->blocks); +"zone %u capacity (%llu) does not match RT group size (%u).", + zone_no, XFS_BB_TO_FSB(mp, zone->capacity), + expected_capacity); return false; } - if (g->has_daddr_gaps) { - expected_size = 1 << g->blklog; - } else { - if (zone->len != zone->capacity) { - xfs_warn(mp, -"zone %u has capacity != size ((0x%llx vs 0x%llx)", - rtg_rgno(rtg), - XFS_BB_TO_FSB(mp, zone->len), - XFS_BB_TO_FSB(mp, zone->capacity)); - return false; - } - expected_size = g->blocks; - } - if (XFS_BB_TO_FSB(mp, zone->len) != expected_size) { xfs_warn(mp, -"zone %u length (0x%llx) does match geometry (0x%x).", - rtg_rgno(rtg), XFS_BB_TO_FSB(mp, zone->len), +"zone %u length (%llu) does not match geometry (%u).", + zone_no, XFS_BB_TO_FSB(mp, zone->len), expected_size); + return false; } switch (zone->type) { case BLK_ZONE_TYPE_CONVENTIONAL: - return xfs_zone_validate_conv(zone, rtg); + return xfs_validate_blk_zone_conv(mp, zone, zone_no); case BLK_ZONE_TYPE_SEQWRITE_REQ: - return xfs_zone_validate_seq(zone, rtg, write_pointer); + return xfs_validate_blk_zone_seq(mp, zone, zone_no, + write_pointer); default: xfs_warn(mp, "zoned %u has unsupported type 0x%x.", - rtg_rgno(rtg), zone->type); + zone_no, zone->type); return false; } } diff --git a/fs/xfs/libxfs/xfs_zones.h b/fs/xfs/libxfs/xfs_zones.h index 5fefd132e002..c16089c9a652 100644 --- a/fs/xfs/libxfs/xfs_zones.h +++ b/fs/xfs/libxfs/xfs_zones.h @@ -3,6 +3,7 @@ #define _LIBXFS_ZONES_H struct xfs_rtgroup; +struct blk_zone; /* * In order to guarantee forward progress for GC we need to reserve at least @@ -36,7 +37,8 @@ struct xfs_rtgroup; */ #define XFS_DEFAULT_MAX_OPEN_ZONES 128 -bool xfs_zone_validate(struct blk_zone *zone, struct xfs_rtgroup *rtg, - xfs_rgblock_t *write_pointer); +bool xfs_validate_blk_zone(struct xfs_mount *mp, struct blk_zone *zone, + unsigned int zone_no, uint32_t expected_size, + uint32_t expected_capacity, xfs_rgblock_t *write_pointer); #endif /* _LIBXFS_ZONES_H */ diff --git a/fs/xfs/scrub/agb_bitmap.c b/fs/xfs/scrub/agb_bitmap.c index 573e4e062754..0194e3aaa1fa 100644 --- a/fs/xfs/scrub/agb_bitmap.c +++ b/fs/xfs/scrub/agb_bitmap.c @@ -3,7 +3,7 @@ * Copyright (C) 2018-2023 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_shared.h" #include "xfs_bit.h" #include "xfs_format.h" diff --git a/fs/xfs/scrub/agheader.c b/fs/xfs/scrub/agheader.c index 303374df44bd..7ffe4b0ef0f1 100644 --- a/fs/xfs/scrub/agheader.c +++ b/fs/xfs/scrub/agheader.c @@ -3,7 +3,7 @@ * Copyright (C) 2017-2023 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/scrub/agheader_repair.c b/fs/xfs/scrub/agheader_repair.c index cd6f0223879f..15d58eedb387 100644 --- a/fs/xfs/scrub/agheader_repair.c +++ b/fs/xfs/scrub/agheader_repair.c @@ -3,7 +3,7 @@ * Copyright (C) 2018-2023 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" @@ -837,8 +837,12 @@ xrep_agi_buf_cleanup( { struct xrep_agi *ragi = buf; - xfarray_destroy(ragi->iunlink_prev); - xfarray_destroy(ragi->iunlink_next); + if (ragi->iunlink_prev) + xfarray_destroy(ragi->iunlink_prev); + ragi->iunlink_prev = NULL; + if (ragi->iunlink_next) + xfarray_destroy(ragi->iunlink_next); + ragi->iunlink_next = NULL; xagino_bitmap_destroy(&ragi->iunlink_bmp); } @@ -1708,7 +1712,6 @@ xrep_agi( { struct xrep_agi *ragi; struct xfs_mount *mp = sc->mp; - char *descr; unsigned int i; int error; @@ -1742,17 +1745,13 @@ xrep_agi( xagino_bitmap_init(&ragi->iunlink_bmp); sc->buf_cleanup = xrep_agi_buf_cleanup; - descr = xchk_xfile_ag_descr(sc, "iunlinked next pointers"); - error = xfarray_create(descr, 0, sizeof(xfs_agino_t), - &ragi->iunlink_next); - kfree(descr); + error = xfarray_create("iunlinked next pointers", 0, + sizeof(xfs_agino_t), &ragi->iunlink_next); if (error) return error; - descr = xchk_xfile_ag_descr(sc, "iunlinked prev pointers"); - error = xfarray_create(descr, 0, sizeof(xfs_agino_t), - &ragi->iunlink_prev); - kfree(descr); + error = xfarray_create("iunlinked prev pointers", 0, + sizeof(xfs_agino_t), &ragi->iunlink_prev); if (error) return error; diff --git a/fs/xfs/scrub/alloc.c b/fs/xfs/scrub/alloc.c index 8b282138097f..48edaa2cb1e0 100644 --- a/fs/xfs/scrub/alloc.c +++ b/fs/xfs/scrub/alloc.c @@ -3,7 +3,7 @@ * Copyright (C) 2017-2023 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/scrub/alloc_repair.c b/fs/xfs/scrub/alloc_repair.c index bed6a09aa791..5b4c2a39a155 100644 --- a/fs/xfs/scrub/alloc_repair.c +++ b/fs/xfs/scrub/alloc_repair.c @@ -3,7 +3,7 @@ * Copyright (C) 2018-2023 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" @@ -850,7 +850,6 @@ xrep_allocbt( struct xrep_abt *ra; struct xfs_mount *mp = sc->mp; unsigned int busy_gen; - char *descr; int error; /* We require the rmapbt to rebuild anything. */ @@ -876,11 +875,9 @@ xrep_allocbt( } /* Set up enough storage to handle maximally fragmented free space. */ - descr = xchk_xfile_ag_descr(sc, "free space records"); - error = xfarray_create(descr, mp->m_sb.sb_agblocks / 2, + error = xfarray_create("free space records", mp->m_sb.sb_agblocks / 2, sizeof(struct xfs_alloc_rec_incore), &ra->free_records); - kfree(descr); if (error) goto out_ra; @@ -926,7 +923,22 @@ xrep_revalidate_allocbt( if (error) goto out; + /* + * If the bnobt is still corrupt, we've failed to repair the filesystem + * and should just bail out. + * + * If the bnobt fails cross-examination with the cntbt, the scan will + * free the cntbt cursor, so we need to mark the repair incomplete + * and avoid walking off the end of the NULL cntbt cursor. + */ + if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) + goto out; + sc->sm->sm_type = XFS_SCRUB_TYPE_CNTBT; + if (!sc->sa.cnt_cur) { + xchk_set_incomplete(sc); + goto out; + } error = xchk_allocbt(sc); out: sc->sm->sm_type = old_type; diff --git a/fs/xfs/scrub/attr.c b/fs/xfs/scrub/attr.c index 708334f9b2bd..c3c122ea2d32 100644 --- a/fs/xfs/scrub/attr.c +++ b/fs/xfs/scrub/attr.c @@ -3,7 +3,7 @@ * Copyright (C) 2017-2023 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" @@ -288,32 +288,6 @@ xchk_xattr_set_map( } /* - * Check the leaf freemap from the usage bitmap. Returns false if the - * attr freemap has problems or points to used space. - */ -STATIC bool -xchk_xattr_check_freemap( - struct xfs_scrub *sc, - struct xfs_attr3_icleaf_hdr *leafhdr) -{ - struct xchk_xattr_buf *ab = sc->buf; - unsigned int mapsize = sc->mp->m_attr_geo->blksize; - int i; - - /* Construct bitmap of freemap contents. */ - bitmap_zero(ab->freemap, mapsize); - for (i = 0; i < XFS_ATTR_LEAF_MAPSIZE; i++) { - if (!xchk_xattr_set_map(sc, ab->freemap, - leafhdr->freemap[i].base, - leafhdr->freemap[i].size)) - return false; - } - - /* Look for bits that are set in freemap and are marked in use. */ - return !bitmap_intersects(ab->freemap, ab->usedmap, mapsize); -} - -/* * Check this leaf entry's relations to everything else. * Returns the number of bytes used for the name/value data. */ @@ -364,7 +338,10 @@ xchk_xattr_entry( rentry = xfs_attr3_leaf_name_remote(leaf, idx); namesize = xfs_attr_leaf_entsize_remote(rentry->namelen); name_end = (char *)rentry + namesize; - if (rentry->namelen == 0 || rentry->valueblk == 0) + if (rentry->namelen == 0) + xchk_da_set_corrupt(ds, level); + if (rentry->valueblk == 0 && + !(ent->flags & XFS_ATTR_INCOMPLETE)) xchk_da_set_corrupt(ds, level); } if (name_end > buf_end) @@ -403,6 +380,7 @@ xchk_xattr_block( *last_checked = blk->blkno; bitmap_zero(ab->usedmap, mp->m_attr_geo->blksize); + bitmap_zero(ab->freemap, mp->m_attr_geo->blksize); /* Check all the padding. */ if (xfs_has_crc(ds->sc->mp)) { @@ -449,6 +427,9 @@ xchk_xattr_block( if ((char *)&entries[leafhdr.count] > (char *)leaf + leafhdr.firstused) xchk_da_set_corrupt(ds, level); + if (ds->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) + goto out; + buf_end = (char *)bp->b_addr + mp->m_attr_geo->blksize; for (i = 0, ent = entries; i < leafhdr.count; ent++, i++) { /* Mark the leaf entry itself. */ @@ -467,7 +448,29 @@ xchk_xattr_block( goto out; } - if (!xchk_xattr_check_freemap(ds->sc, &leafhdr)) + /* Construct bitmap of freemap contents. */ + for (i = 0; i < XFS_ATTR_LEAF_MAPSIZE; i++) { + if (!xchk_xattr_set_map(ds->sc, ab->freemap, + leafhdr.freemap[i].base, + leafhdr.freemap[i].size)) + xchk_da_set_corrupt(ds, level); + + /* + * freemap entries with zero length and nonzero base can cause + * problems with older kernels, so we mark these for preening + * even though there's no inconsistency. + */ + if (leafhdr.freemap[i].size == 0 && + leafhdr.freemap[i].base > 0) + xchk_da_set_preen(ds, level); + + if (ds->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) + goto out; + } + + /* Look for bits that are set in freemap and are marked in use. */ + if (bitmap_intersects(ab->freemap, ab->usedmap, + mp->m_attr_geo->blksize)) xchk_da_set_corrupt(ds, level); if (leafhdr.usedbytes != usedbytes) diff --git a/fs/xfs/scrub/attr_repair.c b/fs/xfs/scrub/attr_repair.c index 09d63aa10314..a924b467a844 100644 --- a/fs/xfs/scrub/attr_repair.c +++ b/fs/xfs/scrub/attr_repair.c @@ -3,7 +3,7 @@ * Copyright (c) 2018-2024 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" @@ -1516,8 +1516,10 @@ xrep_xattr_teardown( xfblob_destroy(rx->pptr_names); if (rx->pptr_recs) xfarray_destroy(rx->pptr_recs); - xfblob_destroy(rx->xattr_blobs); - xfarray_destroy(rx->xattr_records); + if (rx->xattr_blobs) + xfblob_destroy(rx->xattr_blobs); + if (rx->xattr_records) + xfarray_destroy(rx->xattr_records); mutex_destroy(&rx->lock); kfree(rx); } @@ -1529,7 +1531,6 @@ xrep_xattr_setup_scan( struct xrep_xattr **rxp) { struct xrep_xattr *rx; - char *descr; int max_len; int error; @@ -1555,35 +1556,26 @@ xrep_xattr_setup_scan( goto out_rx; /* Set up some staging for salvaged attribute keys and values */ - descr = xchk_xfile_ino_descr(sc, "xattr keys"); - error = xfarray_create(descr, 0, sizeof(struct xrep_xattr_key), + error = xfarray_create("xattr keys", 0, sizeof(struct xrep_xattr_key), &rx->xattr_records); - kfree(descr); if (error) goto out_rx; - descr = xchk_xfile_ino_descr(sc, "xattr names"); - error = xfblob_create(descr, &rx->xattr_blobs); - kfree(descr); + error = xfblob_create("xattr names", &rx->xattr_blobs); if (error) goto out_keys; if (xfs_has_parent(sc->mp)) { ASSERT(sc->flags & XCHK_FSGATES_DIRENTS); - descr = xchk_xfile_ino_descr(sc, - "xattr retained parent pointer entries"); - error = xfarray_create(descr, 0, + error = xfarray_create("xattr parent pointer entries", 0, sizeof(struct xrep_xattr_pptr), &rx->pptr_recs); - kfree(descr); if (error) goto out_values; - descr = xchk_xfile_ino_descr(sc, - "xattr retained parent pointer names"); - error = xfblob_create(descr, &rx->pptr_names); - kfree(descr); + error = xfblob_create("xattr parent pointer names", + &rx->pptr_names); if (error) goto out_pprecs; diff --git a/fs/xfs/scrub/bitmap.c b/fs/xfs/scrub/bitmap.c index 7ba35a7a7920..51f3171bc6c8 100644 --- a/fs/xfs/scrub/bitmap.c +++ b/fs/xfs/scrub/bitmap.c @@ -3,7 +3,7 @@ * Copyright (C) 2018-2023 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_bit.h" diff --git a/fs/xfs/scrub/bmap.c b/fs/xfs/scrub/bmap.c index 4f1e2574660d..d40534bf9ef9 100644 --- a/fs/xfs/scrub/bmap.c +++ b/fs/xfs/scrub/bmap.c @@ -3,7 +3,7 @@ * Copyright (C) 2017-2023 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/scrub/bmap_repair.c b/fs/xfs/scrub/bmap_repair.c index 1084213b8e9b..0a83d5845379 100644 --- a/fs/xfs/scrub/bmap_repair.c +++ b/fs/xfs/scrub/bmap_repair.c @@ -3,7 +3,7 @@ * Copyright (C) 2018-2023 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" @@ -923,7 +923,6 @@ xrep_bmap( bool allow_unwritten) { struct xrep_bmap *rb; - char *descr; xfs_extnum_t max_bmbt_recs; bool large_extcount; int error = 0; @@ -945,11 +944,8 @@ xrep_bmap( /* Set up enough storage to handle the max records for this fork. */ large_extcount = xfs_has_large_extent_counts(sc->mp); max_bmbt_recs = xfs_iext_max_nextents(large_extcount, whichfork); - descr = xchk_xfile_ino_descr(sc, "%s fork mapping records", - whichfork == XFS_DATA_FORK ? "data" : "attr"); - error = xfarray_create(descr, max_bmbt_recs, + error = xfarray_create("fork mapping records", max_bmbt_recs, sizeof(struct xfs_bmbt_rec), &rb->bmap_records); - kfree(descr); if (error) goto out_rb; diff --git a/fs/xfs/scrub/btree.c b/fs/xfs/scrub/btree.c index cd6f0ff382a7..1089b1f4c5df 100644 --- a/fs/xfs/scrub/btree.c +++ b/fs/xfs/scrub/btree.c @@ -3,7 +3,7 @@ * Copyright (C) 2017-2023 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" @@ -42,6 +42,8 @@ __xchk_btree_process_error( break; case -EFSBADCRC: case -EFSCORRUPTED: + case -EIO: + case -ENODATA: /* Note the badness but don't abort. */ sc->sm->sm_flags |= errflag; *error = 0; @@ -370,12 +372,15 @@ xchk_btree_check_block_owner( { xfs_agnumber_t agno; xfs_agblock_t agbno; + bool is_bnobt, is_rmapbt; bool init_sa; int error = 0; if (!bs->cur) return 0; + is_bnobt = xfs_btree_is_bno(bs->cur->bc_ops); + is_rmapbt = xfs_btree_is_rmap(bs->cur->bc_ops); agno = xfs_daddr_to_agno(bs->cur->bc_mp, daddr); agbno = xfs_daddr_to_agbno(bs->cur->bc_mp, daddr); @@ -398,11 +403,11 @@ xchk_btree_check_block_owner( * have to nullify it (to shut down further block owner checks) if * self-xref encounters problems. */ - if (!bs->sc->sa.bno_cur && xfs_btree_is_bno(bs->cur->bc_ops)) + if (!bs->sc->sa.bno_cur && is_bnobt) bs->cur = NULL; xchk_xref_is_only_owned_by(bs->sc, agbno, 1, bs->oinfo); - if (!bs->sc->sa.rmap_cur && xfs_btree_is_rmap(bs->cur->bc_ops)) + if (!bs->sc->sa.rmap_cur && is_rmapbt) bs->cur = NULL; out_free: diff --git a/fs/xfs/scrub/common.c b/fs/xfs/scrub/common.c index 7bfa37c99480..20e63069088b 100644 --- a/fs/xfs/scrub/common.c +++ b/fs/xfs/scrub/common.c @@ -3,7 +3,7 @@ * Copyright (C) 2017-2023 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" @@ -103,6 +103,8 @@ __xchk_process_error( break; case -EFSBADCRC: case -EFSCORRUPTED: + case -EIO: + case -ENODATA: /* Note the badness but don't abort. */ sc->sm->sm_flags |= errflag; *error = 0; @@ -177,6 +179,8 @@ __xchk_fblock_process_error( break; case -EFSBADCRC: case -EFSCORRUPTED: + case -EIO: + case -ENODATA: /* Note the badness but don't abort. */ sc->sm->sm_flags |= errflag; *error = 0; @@ -1395,6 +1399,9 @@ xchk_metadata_inode_subtype( int error; sub = xchk_scrub_create_subord(sc, scrub_type); + if (!sub) + return -ENOMEM; + error = sub->sc.ops->scrub(&sub->sc); xchk_scrub_free_subord(sub); return error; diff --git a/fs/xfs/scrub/common.h b/fs/xfs/scrub/common.h index ddbc065c798c..f2ecc68538f0 100644 --- a/fs/xfs/scrub/common.h +++ b/fs/xfs/scrub/common.h @@ -247,31 +247,6 @@ static inline bool xchk_could_repair(const struct xfs_scrub *sc) int xchk_metadata_inode_forks(struct xfs_scrub *sc); /* - * Helper macros to allocate and format xfile description strings. - * Callers must kfree the pointer returned. - */ -#define xchk_xfile_descr(sc, fmt, ...) \ - kasprintf(XCHK_GFP_FLAGS, "XFS (%s): " fmt, \ - (sc)->mp->m_super->s_id, ##__VA_ARGS__) -#define xchk_xfile_ag_descr(sc, fmt, ...) \ - kasprintf(XCHK_GFP_FLAGS, "XFS (%s): AG 0x%x " fmt, \ - (sc)->mp->m_super->s_id, \ - (sc)->sa.pag ? \ - pag_agno((sc)->sa.pag) : (sc)->sm->sm_agno, \ - ##__VA_ARGS__) -#define xchk_xfile_ino_descr(sc, fmt, ...) \ - kasprintf(XCHK_GFP_FLAGS, "XFS (%s): inode 0x%llx " fmt, \ - (sc)->mp->m_super->s_id, \ - (sc)->ip ? (sc)->ip->i_ino : (sc)->sm->sm_ino, \ - ##__VA_ARGS__) -#define xchk_xfile_rtgroup_descr(sc, fmt, ...) \ - kasprintf(XCHK_GFP_FLAGS, "XFS (%s): rtgroup 0x%x " fmt, \ - (sc)->mp->m_super->s_id, \ - (sc)->sa.pag ? \ - rtg_rgno((sc)->sr.rtg) : (sc)->sm->sm_agno, \ - ##__VA_ARGS__) - -/* * Setting up a hook to wait for intents to drain is costly -- we have to take * the CPU hotplug lock and force an i-cache flush on all CPUs once to set it * up, and again to tear it down. These costs add up quickly, so we only want diff --git a/fs/xfs/scrub/cow_repair.c b/fs/xfs/scrub/cow_repair.c index b2a83801412e..33749cf43520 100644 --- a/fs/xfs/scrub/cow_repair.c +++ b/fs/xfs/scrub/cow_repair.c @@ -3,7 +3,7 @@ * Copyright (C) 2022-2023 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/scrub/dabtree.c b/fs/xfs/scrub/dabtree.c index 056de4819f86..5858d4d5e279 100644 --- a/fs/xfs/scrub/dabtree.c +++ b/fs/xfs/scrub/dabtree.c @@ -3,7 +3,7 @@ * Copyright (C) 2017-2023 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" @@ -45,6 +45,8 @@ xchk_da_process_error( break; case -EFSBADCRC: case -EFSCORRUPTED: + case -EIO: + case -ENODATA: /* Note the badness but don't abort. */ sc->sm->sm_flags |= XFS_SCRUB_OFLAG_CORRUPT; *error = 0; diff --git a/fs/xfs/scrub/dir.c b/fs/xfs/scrub/dir.c index c877bde71e62..91228623d016 100644 --- a/fs/xfs/scrub/dir.c +++ b/fs/xfs/scrub/dir.c @@ -3,7 +3,7 @@ * Copyright (C) 2017-2023 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" @@ -1102,22 +1102,17 @@ xchk_directory( sd->xname.name = sd->namebuf; if (xfs_has_parent(sc->mp)) { - char *descr; - /* * Set up some staging memory for dirents that we can't check * due to locking contention. */ - descr = xchk_xfile_ino_descr(sc, "slow directory entries"); - error = xfarray_create(descr, 0, sizeof(struct xchk_dirent), - &sd->dir_entries); - kfree(descr); + error = xfarray_create("slow directory entries", 0, + sizeof(struct xchk_dirent), &sd->dir_entries); if (error) goto out_sd; - descr = xchk_xfile_ino_descr(sc, "slow directory entry names"); - error = xfblob_create(descr, &sd->dir_names); - kfree(descr); + error = xfblob_create("slow directory entry names", + &sd->dir_names); if (error) goto out_entries; } diff --git a/fs/xfs/scrub/dir_repair.c b/fs/xfs/scrub/dir_repair.c index 8d3b550990b5..f105e49f654b 100644 --- a/fs/xfs/scrub/dir_repair.c +++ b/fs/xfs/scrub/dir_repair.c @@ -3,7 +3,7 @@ * Copyright (c) 2020-2024 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" @@ -172,8 +172,12 @@ xrep_dir_teardown( struct xrep_dir *rd = sc->buf; xrep_findparent_scan_teardown(&rd->pscan); - xfblob_destroy(rd->dir_names); - xfarray_destroy(rd->dir_entries); + if (rd->dir_names) + xfblob_destroy(rd->dir_names); + rd->dir_names = NULL; + if (rd->dir_entries) + xfarray_destroy(rd->dir_entries); + rd->dir_names = NULL; } /* Set up for a directory repair. */ @@ -1784,20 +1788,15 @@ xrep_dir_setup_scan( struct xrep_dir *rd) { struct xfs_scrub *sc = rd->sc; - char *descr; int error; /* Set up some staging memory for salvaging dirents. */ - descr = xchk_xfile_ino_descr(sc, "directory entries"); - error = xfarray_create(descr, 0, sizeof(struct xrep_dirent), - &rd->dir_entries); - kfree(descr); + error = xfarray_create("directory entries", 0, + sizeof(struct xrep_dirent), &rd->dir_entries); if (error) return error; - descr = xchk_xfile_ino_descr(sc, "directory entry names"); - error = xfblob_create(descr, &rd->dir_names); - kfree(descr); + error = xfblob_create("directory entry names", &rd->dir_names); if (error) goto out_xfarray; diff --git a/fs/xfs/scrub/dirtree.c b/fs/xfs/scrub/dirtree.c index 3a9cdf8738b6..e95dc74f1145 100644 --- a/fs/xfs/scrub/dirtree.c +++ b/fs/xfs/scrub/dirtree.c @@ -3,7 +3,7 @@ * Copyright (c) 2023-2024 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" @@ -81,8 +81,12 @@ xchk_dirtree_buf_cleanup( kfree(path); } - xfblob_destroy(dl->path_names); - xfarray_destroy(dl->path_steps); + if (dl->path_names) + xfblob_destroy(dl->path_names); + dl->path_names = NULL; + if (dl->path_steps) + xfarray_destroy(dl->path_steps); + dl->path_steps = NULL; mutex_destroy(&dl->lock); } @@ -92,7 +96,6 @@ xchk_setup_dirtree( struct xfs_scrub *sc) { struct xchk_dirtree *dl; - char *descr; int error; xchk_fsgates_enable(sc, XCHK_FSGATES_DIRENTS); @@ -116,16 +119,12 @@ xchk_setup_dirtree( mutex_init(&dl->lock); - descr = xchk_xfile_ino_descr(sc, "dirtree path steps"); - error = xfarray_create(descr, 0, sizeof(struct xchk_dirpath_step), - &dl->path_steps); - kfree(descr); + error = xfarray_create("dirtree path steps", 0, + sizeof(struct xchk_dirpath_step), &dl->path_steps); if (error) goto out_dl; - descr = xchk_xfile_ino_descr(sc, "dirtree path names"); - error = xfblob_create(descr, &dl->path_names); - kfree(descr); + error = xfblob_create("dirtree path names", &dl->path_names); if (error) goto out_steps; diff --git a/fs/xfs/scrub/dirtree_repair.c b/fs/xfs/scrub/dirtree_repair.c index 5c04e70ba951..019feaf0d606 100644 --- a/fs/xfs/scrub/dirtree_repair.c +++ b/fs/xfs/scrub/dirtree_repair.c @@ -3,7 +3,7 @@ * Copyright (c) 2023-2024 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/scrub/dqiterate.c b/fs/xfs/scrub/dqiterate.c index 20c4daedd48d..10950e4bd4c3 100644 --- a/fs/xfs/scrub/dqiterate.c +++ b/fs/xfs/scrub/dqiterate.c @@ -3,7 +3,7 @@ * Copyright (C) 2023 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_bit.h" diff --git a/fs/xfs/scrub/findparent.c b/fs/xfs/scrub/findparent.c index 84487072b6dd..2076f028d271 100644 --- a/fs/xfs/scrub/findparent.c +++ b/fs/xfs/scrub/findparent.c @@ -3,7 +3,7 @@ * Copyright (c) 2020-2024 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/scrub/fscounters.c b/fs/xfs/scrub/fscounters.c index cebd0d526926..b35f65b537ba 100644 --- a/fs/xfs/scrub/fscounters.c +++ b/fs/xfs/scrub/fscounters.c @@ -3,7 +3,7 @@ * Copyright (C) 2019-2023 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/scrub/fscounters_repair.c b/fs/xfs/scrub/fscounters_repair.c index f0d2b04644e4..783e409f8f3c 100644 --- a/fs/xfs/scrub/fscounters_repair.c +++ b/fs/xfs/scrub/fscounters_repair.c @@ -3,7 +3,7 @@ * Copyright (c) 2018-2024 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/scrub/health.c b/fs/xfs/scrub/health.c index 3c0f25098b69..2171bcf0f6c1 100644 --- a/fs/xfs/scrub/health.c +++ b/fs/xfs/scrub/health.c @@ -3,7 +3,7 @@ * Copyright (C) 2019-2023 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/scrub/ialloc.c b/fs/xfs/scrub/ialloc.c index 4dc7c83dc08a..911dc0f9a79d 100644 --- a/fs/xfs/scrub/ialloc.c +++ b/fs/xfs/scrub/ialloc.c @@ -3,7 +3,7 @@ * Copyright (C) 2017-2023 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/scrub/ialloc_repair.c b/fs/xfs/scrub/ialloc_repair.c index 14e48d3f1912..9b63b9d19e1b 100644 --- a/fs/xfs/scrub/ialloc_repair.c +++ b/fs/xfs/scrub/ialloc_repair.c @@ -3,7 +3,7 @@ * Copyright (C) 2018-2023 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" @@ -797,7 +797,6 @@ xrep_iallocbt( { struct xrep_ibt *ri; struct xfs_mount *mp = sc->mp; - char *descr; xfs_agino_t first_agino, last_agino; int error = 0; @@ -816,11 +815,9 @@ xrep_iallocbt( /* Set up enough storage to handle an AG with nothing but inodes. */ xfs_agino_range(mp, pag_agno(sc->sa.pag), &first_agino, &last_agino); last_agino /= XFS_INODES_PER_CHUNK; - descr = xchk_xfile_ag_descr(sc, "inode index records"); - error = xfarray_create(descr, last_agino, + error = xfarray_create("inode index records", last_agino, sizeof(struct xfs_inobt_rec_incore), &ri->inode_records); - kfree(descr); if (error) goto out_ri; @@ -866,10 +863,24 @@ xrep_revalidate_iallocbt( if (error) goto out; - if (xfs_has_finobt(sc->mp)) { - sc->sm->sm_type = XFS_SCRUB_TYPE_FINOBT; - error = xchk_iallocbt(sc); + /* + * If the inobt is still corrupt, we've failed to repair the filesystem + * and should just bail out. + * + * If the inobt fails cross-examination with the finobt, the scan will + * free the finobt cursor, so we need to mark the repair incomplete + * and avoid walking off the end of the NULL finobt cursor. + */ + if (!xfs_has_finobt(sc->mp) || + (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)) + goto out; + + sc->sm->sm_type = XFS_SCRUB_TYPE_FINOBT; + if (!sc->sa.fino_cur) { + xchk_set_incomplete(sc); + goto out; } + error = xchk_iallocbt(sc); out: sc->sm->sm_type = old_type; diff --git a/fs/xfs/scrub/inode.c b/fs/xfs/scrub/inode.c index bb3f475b6353..948d04dcba2a 100644 --- a/fs/xfs/scrub/inode.c +++ b/fs/xfs/scrub/inode.c @@ -3,7 +3,7 @@ * Copyright (C) 2017-2023 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/scrub/inode_repair.c b/fs/xfs/scrub/inode_repair.c index 4f7040c9ddf0..bf182a18f115 100644 --- a/fs/xfs/scrub/inode_repair.c +++ b/fs/xfs/scrub/inode_repair.c @@ -3,7 +3,7 @@ * Copyright (C) 2018-2023 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/scrub/iscan.c b/fs/xfs/scrub/iscan.c index 84f117667ca2..2a974eed00cc 100644 --- a/fs/xfs/scrub/iscan.c +++ b/fs/xfs/scrub/iscan.c @@ -3,7 +3,7 @@ * Copyright (c) 2021-2024 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/scrub/listxattr.c b/fs/xfs/scrub/listxattr.c index 256ff7700c94..0863db64b1b2 100644 --- a/fs/xfs/scrub/listxattr.c +++ b/fs/xfs/scrub/listxattr.c @@ -3,7 +3,7 @@ * Copyright (c) 2022-2024 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/scrub/metapath.c b/fs/xfs/scrub/metapath.c index 378ec7c8d38e..3d9de59c1758 100644 --- a/fs/xfs/scrub/metapath.c +++ b/fs/xfs/scrub/metapath.c @@ -3,7 +3,7 @@ * Copyright (c) 2023-2024 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/scrub/newbt.c b/fs/xfs/scrub/newbt.c index 951ae8b71566..43e868f829aa 100644 --- a/fs/xfs/scrub/newbt.c +++ b/fs/xfs/scrub/newbt.c @@ -3,7 +3,7 @@ * Copyright (C) 2022-2023 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/scrub/nlinks.c b/fs/xfs/scrub/nlinks.c index 091c79e432e5..e80fe7395d78 100644 --- a/fs/xfs/scrub/nlinks.c +++ b/fs/xfs/scrub/nlinks.c @@ -3,7 +3,7 @@ * Copyright (c) 2021-2024 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" @@ -971,7 +971,8 @@ xchk_nlinks_teardown_scan( xfs_dir_hook_del(xnc->sc->mp, &xnc->dhook); - xfarray_destroy(xnc->nlinks); + if (xnc->nlinks) + xfarray_destroy(xnc->nlinks); xnc->nlinks = NULL; xchk_iscan_teardown(&xnc->collect_iscan); @@ -990,7 +991,6 @@ xchk_nlinks_setup_scan( struct xchk_nlink_ctrs *xnc) { struct xfs_mount *mp = sc->mp; - char *descr; unsigned long long max_inos; xfs_agnumber_t last_agno = mp->m_sb.sb_agcount - 1; xfs_agino_t first_agino, last_agino; @@ -1007,10 +1007,9 @@ xchk_nlinks_setup_scan( */ xfs_agino_range(mp, last_agno, &first_agino, &last_agino); max_inos = XFS_AGINO_TO_INO(mp, last_agno, last_agino) + 1; - descr = xchk_xfile_descr(sc, "file link counts"); - error = xfarray_create(descr, min(XFS_MAXINUMBER + 1, max_inos), + error = xfarray_create("file link counts", + min(XFS_MAXINUMBER + 1, max_inos), sizeof(struct xchk_nlink), &xnc->nlinks); - kfree(descr); if (error) goto out_teardown; diff --git a/fs/xfs/scrub/nlinks_repair.c b/fs/xfs/scrub/nlinks_repair.c index 6ef2ee9c3814..9049215c6eae 100644 --- a/fs/xfs/scrub/nlinks_repair.c +++ b/fs/xfs/scrub/nlinks_repair.c @@ -3,7 +3,7 @@ * Copyright (c) 2021-2024 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/scrub/orphanage.c b/fs/xfs/scrub/orphanage.c index 4e550a1d5353..52a108f6d5f4 100644 --- a/fs/xfs/scrub/orphanage.c +++ b/fs/xfs/scrub/orphanage.c @@ -3,7 +3,7 @@ * Copyright (c) 2021-2024 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/scrub/parent.c b/fs/xfs/scrub/parent.c index 11d5de10fd56..5a259570b154 100644 --- a/fs/xfs/scrub/parent.c +++ b/fs/xfs/scrub/parent.c @@ -3,7 +3,7 @@ * Copyright (C) 2017-2023 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" @@ -755,7 +755,6 @@ xchk_parent_pptr( struct xfs_scrub *sc) { struct xchk_pptrs *pp; - char *descr; int error; pp = kvzalloc(sizeof(struct xchk_pptrs), XCHK_GFP_FLAGS); @@ -768,16 +767,12 @@ xchk_parent_pptr( * Set up some staging memory for parent pointers that we can't check * due to locking contention. */ - descr = xchk_xfile_ino_descr(sc, "slow parent pointer entries"); - error = xfarray_create(descr, 0, sizeof(struct xchk_pptr), - &pp->pptr_entries); - kfree(descr); + error = xfarray_create("slow parent pointer entries", 0, + sizeof(struct xchk_pptr), &pp->pptr_entries); if (error) goto out_pp; - descr = xchk_xfile_ino_descr(sc, "slow parent pointer names"); - error = xfblob_create(descr, &pp->pptr_names); - kfree(descr); + error = xfblob_create("slow parent pointer names", &pp->pptr_names); if (error) goto out_entries; diff --git a/fs/xfs/scrub/parent_repair.c b/fs/xfs/scrub/parent_repair.c index 2949feda6271..83a8205ae2f1 100644 --- a/fs/xfs/scrub/parent_repair.c +++ b/fs/xfs/scrub/parent_repair.c @@ -3,7 +3,7 @@ * Copyright (c) 2020-2024 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" @@ -1497,7 +1497,6 @@ xrep_parent_setup_scan( struct xrep_parent *rp) { struct xfs_scrub *sc = rp->sc; - char *descr; struct xfs_da_geometry *geo = sc->mp->m_attr_geo; int max_len; int error; @@ -1525,32 +1524,22 @@ xrep_parent_setup_scan( goto out_xattr_name; /* Set up some staging memory for logging parent pointer updates. */ - descr = xchk_xfile_ino_descr(sc, "parent pointer entries"); - error = xfarray_create(descr, 0, sizeof(struct xrep_pptr), - &rp->pptr_recs); - kfree(descr); + error = xfarray_create("parent pointer entries", 0, + sizeof(struct xrep_pptr), &rp->pptr_recs); if (error) goto out_xattr_value; - descr = xchk_xfile_ino_descr(sc, "parent pointer names"); - error = xfblob_create(descr, &rp->pptr_names); - kfree(descr); + error = xfblob_create("parent pointer names", &rp->pptr_names); if (error) goto out_recs; /* Set up some storage for copying attrs before the mapping exchange */ - descr = xchk_xfile_ino_descr(sc, - "parent pointer retained xattr entries"); - error = xfarray_create(descr, 0, sizeof(struct xrep_parent_xattr), - &rp->xattr_records); - kfree(descr); + error = xfarray_create("parent pointer xattr entries", 0, + sizeof(struct xrep_parent_xattr), &rp->xattr_records); if (error) goto out_names; - descr = xchk_xfile_ino_descr(sc, - "parent pointer retained xattr values"); - error = xfblob_create(descr, &rp->xattr_blobs); - kfree(descr); + error = xfblob_create("parent pointer xattr values", &rp->xattr_blobs); if (error) goto out_attr_keys; diff --git a/fs/xfs/scrub/quota.c b/fs/xfs/scrub/quota.c index 5c5374c44c5a..1d25bd5b892e 100644 --- a/fs/xfs/scrub/quota.c +++ b/fs/xfs/scrub/quota.c @@ -3,7 +3,7 @@ * Copyright (C) 2017-2023 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_bit.h" diff --git a/fs/xfs/scrub/quota_repair.c b/fs/xfs/scrub/quota_repair.c index b1d661aa5f06..487bd4f68ebb 100644 --- a/fs/xfs/scrub/quota_repair.c +++ b/fs/xfs/scrub/quota_repair.c @@ -3,7 +3,7 @@ * Copyright (C) 2018-2023 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/scrub/quotacheck.c b/fs/xfs/scrub/quotacheck.c index d412a8359784..e8cba19334a0 100644 --- a/fs/xfs/scrub/quotacheck.c +++ b/fs/xfs/scrub/quotacheck.c @@ -3,7 +3,7 @@ * Copyright (c) 2020-2024 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" @@ -741,7 +741,6 @@ xqcheck_setup_scan( struct xfs_scrub *sc, struct xqcheck *xqc) { - char *descr; struct xfs_quotainfo *qi = sc->mp->m_quotainfo; unsigned long long max_dquots = XFS_DQ_ID_MAX + 1ULL; int error; @@ -756,28 +755,22 @@ xqcheck_setup_scan( error = -ENOMEM; if (xfs_this_quota_on(sc->mp, XFS_DQTYPE_USER)) { - descr = xchk_xfile_descr(sc, "user dquot records"); - error = xfarray_create(descr, max_dquots, + error = xfarray_create("user dquot records", max_dquots, sizeof(struct xqcheck_dquot), &xqc->ucounts); - kfree(descr); if (error) goto out_teardown; } if (xfs_this_quota_on(sc->mp, XFS_DQTYPE_GROUP)) { - descr = xchk_xfile_descr(sc, "group dquot records"); - error = xfarray_create(descr, max_dquots, + error = xfarray_create("group dquot records", max_dquots, sizeof(struct xqcheck_dquot), &xqc->gcounts); - kfree(descr); if (error) goto out_teardown; } if (xfs_this_quota_on(sc->mp, XFS_DQTYPE_PROJ)) { - descr = xchk_xfile_descr(sc, "project dquot records"); - error = xfarray_create(descr, max_dquots, + error = xfarray_create("project dquot records", max_dquots, sizeof(struct xqcheck_dquot), &xqc->pcounts); - kfree(descr); if (error) goto out_teardown; } diff --git a/fs/xfs/scrub/quotacheck_repair.c b/fs/xfs/scrub/quotacheck_repair.c index 51be8d8d261b..dbb522e1513b 100644 --- a/fs/xfs/scrub/quotacheck_repair.c +++ b/fs/xfs/scrub/quotacheck_repair.c @@ -3,7 +3,7 @@ * Copyright (c) 2020-2024 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/scrub/rcbag.c b/fs/xfs/scrub/rcbag.c index e1e52bc20713..c1a97a073d92 100644 --- a/fs/xfs/scrub/rcbag.c +++ b/fs/xfs/scrub/rcbag.c @@ -3,7 +3,7 @@ * Copyright (c) 2022-2024 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/scrub/rcbag_btree.c b/fs/xfs/scrub/rcbag_btree.c index 9a4ef823c5a7..367f8ccf55c4 100644 --- a/fs/xfs/scrub/rcbag_btree.c +++ b/fs/xfs/scrub/rcbag_btree.c @@ -3,7 +3,7 @@ * Copyright (c) 2022-2024 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/scrub/readdir.c b/fs/xfs/scrub/readdir.c index 01c9a2dc0f2c..c66ec9093a38 100644 --- a/fs/xfs/scrub/readdir.c +++ b/fs/xfs/scrub/readdir.c @@ -3,7 +3,7 @@ * Copyright (C) 2022-2023 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/scrub/reap.c b/fs/xfs/scrub/reap.c index 07f5bb8a6421..fff23932828b 100644 --- a/fs/xfs/scrub/reap.c +++ b/fs/xfs/scrub/reap.c @@ -3,7 +3,7 @@ * Copyright (C) 2022-2023 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/scrub/refcount.c b/fs/xfs/scrub/refcount.c index d46528023015..bf87025f24fc 100644 --- a/fs/xfs/scrub/refcount.c +++ b/fs/xfs/scrub/refcount.c @@ -3,7 +3,7 @@ * Copyright (C) 2017-2023 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/scrub/refcount_repair.c b/fs/xfs/scrub/refcount_repair.c index 9c8cb5332da0..507993e0fb0f 100644 --- a/fs/xfs/scrub/refcount_repair.c +++ b/fs/xfs/scrub/refcount_repair.c @@ -3,7 +3,7 @@ * Copyright (C) 2018-2023 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" @@ -123,13 +123,7 @@ int xrep_setup_ag_refcountbt( struct xfs_scrub *sc) { - char *descr; - int error; - - descr = xchk_xfile_ag_descr(sc, "rmap record bag"); - error = xrep_setup_xfbtree(sc, descr); - kfree(descr); - return error; + return xrep_setup_xfbtree(sc, "rmap record bag"); } /* Check for any obvious conflicts with this shared/CoW staging extent. */ @@ -704,7 +698,6 @@ xrep_refcountbt( { struct xrep_refc *rr; struct xfs_mount *mp = sc->mp; - char *descr; int error; /* We require the rmapbt to rebuild anything. */ @@ -717,11 +710,9 @@ xrep_refcountbt( rr->sc = sc; /* Set up enough storage to handle one refcount record per block. */ - descr = xchk_xfile_ag_descr(sc, "reference count records"); - error = xfarray_create(descr, mp->m_sb.sb_agblocks, + error = xfarray_create("reference count records", mp->m_sb.sb_agblocks, sizeof(struct xfs_refcount_irec), &rr->refcount_records); - kfree(descr); if (error) goto out_rr; diff --git a/fs/xfs/scrub/repair.c b/fs/xfs/scrub/repair.c index efd5a7ccdf62..ac8c592579bd 100644 --- a/fs/xfs/scrub/repair.c +++ b/fs/xfs/scrub/repair.c @@ -3,7 +3,7 @@ * Copyright (C) 2018-2023 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" @@ -1136,6 +1136,9 @@ xrep_metadata_inode_subtype( * setup/teardown routines. */ sub = xchk_scrub_create_subord(sc, scrub_type); + if (!sub) + return -ENOMEM; + error = sub->sc.ops->scrub(&sub->sc); if (error) goto out; diff --git a/fs/xfs/scrub/rgsuper.c b/fs/xfs/scrub/rgsuper.c index d189732d0e24..482f899a518a 100644 --- a/fs/xfs/scrub/rgsuper.c +++ b/fs/xfs/scrub/rgsuper.c @@ -3,7 +3,7 @@ * Copyright (c) 2022-2024 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/scrub/rmap.c b/fs/xfs/scrub/rmap.c index 39e9ad7cd8ae..2c25910e2903 100644 --- a/fs/xfs/scrub/rmap.c +++ b/fs/xfs/scrub/rmap.c @@ -3,7 +3,7 @@ * Copyright (C) 2017-2023 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/scrub/rmap_repair.c b/fs/xfs/scrub/rmap_repair.c index 17d4a38d735c..ab7053e25e1c 100644 --- a/fs/xfs/scrub/rmap_repair.c +++ b/fs/xfs/scrub/rmap_repair.c @@ -3,7 +3,7 @@ * Copyright (c) 2018-2024 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" @@ -164,14 +164,11 @@ xrep_setup_ag_rmapbt( struct xfs_scrub *sc) { struct xrep_rmap *rr; - char *descr; int error; xchk_fsgates_enable(sc, XCHK_FSGATES_RMAP); - descr = xchk_xfile_ag_descr(sc, "reverse mapping records"); - error = xrep_setup_xfbtree(sc, descr); - kfree(descr); + error = xrep_setup_xfbtree(sc, "reverse mapping records"); if (error) return error; diff --git a/fs/xfs/scrub/rtbitmap.c b/fs/xfs/scrub/rtbitmap.c index d5ff8609dbfb..4bcfd99cec17 100644 --- a/fs/xfs/scrub/rtbitmap.c +++ b/fs/xfs/scrub/rtbitmap.c @@ -3,7 +3,7 @@ * Copyright (C) 2017-2023 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/scrub/rtbitmap_repair.c b/fs/xfs/scrub/rtbitmap_repair.c index 203a1a97c502..f4ca86a2ea1b 100644 --- a/fs/xfs/scrub/rtbitmap_repair.c +++ b/fs/xfs/scrub/rtbitmap_repair.c @@ -3,7 +3,7 @@ * Copyright (C) 2020-2023 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" @@ -43,7 +43,6 @@ xrep_setup_rtbitmap( struct xchk_rtbitmap *rtb) { struct xfs_mount *mp = sc->mp; - char *descr; unsigned long long blocks = mp->m_sb.sb_rbmblocks; int error; @@ -52,9 +51,8 @@ xrep_setup_rtbitmap( return error; /* Create an xfile to hold our reconstructed bitmap. */ - descr = xchk_xfile_rtgroup_descr(sc, "bitmap file"); - error = xfile_create(descr, blocks * mp->m_sb.sb_blocksize, &sc->xfile); - kfree(descr); + error = xfile_create("realtime bitmap file", + blocks * mp->m_sb.sb_blocksize, &sc->xfile); if (error) return error; diff --git a/fs/xfs/scrub/rtrefcount.c b/fs/xfs/scrub/rtrefcount.c index 4c5dffc73641..8cfe2f120b6b 100644 --- a/fs/xfs/scrub/rtrefcount.c +++ b/fs/xfs/scrub/rtrefcount.c @@ -3,7 +3,7 @@ * Copyright (c) 2021-2024 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/scrub/rtrefcount_repair.c b/fs/xfs/scrub/rtrefcount_repair.c index 983362447826..f713daf095fb 100644 --- a/fs/xfs/scrub/rtrefcount_repair.c +++ b/fs/xfs/scrub/rtrefcount_repair.c @@ -3,7 +3,7 @@ * Copyright (c) 2021-2024 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" @@ -128,13 +128,7 @@ int xrep_setup_rtrefcountbt( struct xfs_scrub *sc) { - char *descr; - int error; - - descr = xchk_xfile_ag_descr(sc, "rmap record bag"); - error = xrep_setup_xfbtree(sc, descr); - kfree(descr); - return error; + return xrep_setup_xfbtree(sc, "realtime rmap record bag"); } /* Check for any obvious conflicts with this shared/CoW staging extent. */ @@ -704,7 +698,6 @@ xrep_rtrefcountbt( { struct xrep_rtrefc *rr; struct xfs_mount *mp = sc->mp; - char *descr; int error; /* We require the rmapbt to rebuild anything. */ @@ -722,11 +715,9 @@ xrep_rtrefcountbt( rr->sc = sc; /* Set up enough storage to handle one refcount record per rt extent. */ - descr = xchk_xfile_ag_descr(sc, "reference count records"); - error = xfarray_create(descr, mp->m_sb.sb_rextents, - sizeof(struct xfs_refcount_irec), + error = xfarray_create("realtime reference count records", + mp->m_sb.sb_rextents, sizeof(struct xfs_refcount_irec), &rr->refcount_records); - kfree(descr); if (error) goto out_rr; diff --git a/fs/xfs/scrub/rtrmap.c b/fs/xfs/scrub/rtrmap.c index 12989fe80e8b..8b1a8389d32f 100644 --- a/fs/xfs/scrub/rtrmap.c +++ b/fs/xfs/scrub/rtrmap.c @@ -3,7 +3,7 @@ * Copyright (c) 2018-2024 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/scrub/rtrmap_repair.c b/fs/xfs/scrub/rtrmap_repair.c index 7561941a337a..4610d6d80648 100644 --- a/fs/xfs/scrub/rtrmap_repair.c +++ b/fs/xfs/scrub/rtrmap_repair.c @@ -3,7 +3,7 @@ * Copyright (c) 2020-2024 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" @@ -103,14 +103,11 @@ xrep_setup_rtrmapbt( struct xfs_scrub *sc) { struct xrep_rtrmap *rr; - char *descr; int error; xchk_fsgates_enable(sc, XCHK_FSGATES_RMAP); - descr = xchk_xfile_rtgroup_descr(sc, "reverse mapping records"); - error = xrep_setup_xfbtree(sc, descr); - kfree(descr); + error = xrep_setup_xfbtree(sc, "realtime reverse mapping records"); if (error) return error; diff --git a/fs/xfs/scrub/rtsummary.c b/fs/xfs/scrub/rtsummary.c index 4ac679c1bd29..b510e6bbbd3e 100644 --- a/fs/xfs/scrub/rtsummary.c +++ b/fs/xfs/scrub/rtsummary.c @@ -3,7 +3,7 @@ * Copyright (C) 2017-2023 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" @@ -43,7 +43,6 @@ xchk_setup_rtsummary( struct xfs_scrub *sc) { struct xfs_mount *mp = sc->mp; - char *descr; struct xchk_rtsummary *rts; int error; @@ -70,10 +69,8 @@ xchk_setup_rtsummary( * Create an xfile to construct a new rtsummary file. The xfile allows * us to avoid pinning kernel memory for this purpose. */ - descr = xchk_xfile_descr(sc, "realtime summary file"); - error = xfile_create(descr, XFS_FSB_TO_B(mp, mp->m_rsumblocks), - &sc->xfile); - kfree(descr); + error = xfile_create("realtime summary file", + XFS_FSB_TO_B(mp, mp->m_rsumblocks), &sc->xfile); if (error) return error; diff --git a/fs/xfs/scrub/rtsummary_repair.c b/fs/xfs/scrub/rtsummary_repair.c index d593977d70df..afffbd6e0ad1 100644 --- a/fs/xfs/scrub/rtsummary_repair.c +++ b/fs/xfs/scrub/rtsummary_repair.c @@ -3,7 +3,7 @@ * Copyright (c) 2020-2024 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/scrub/scrub.c b/fs/xfs/scrub/scrub.c index 3c3b0d25006f..c1c6415f5055 100644 --- a/fs/xfs/scrub/scrub.c +++ b/fs/xfs/scrub/scrub.c @@ -3,7 +3,7 @@ * Copyright (C) 2017-2023 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" @@ -634,7 +634,7 @@ xchk_scrub_create_subord( sub = kzalloc(sizeof(*sub), XCHK_GFP_FLAGS); if (!sub) - return ERR_PTR(-ENOMEM); + return NULL; sub->old_smtype = sc->sm->sm_type; sub->old_smflags = sc->sm->sm_flags; diff --git a/fs/xfs/scrub/stats.c b/fs/xfs/scrub/stats.c index f8a37ea97791..4efafc5ae966 100644 --- a/fs/xfs/scrub/stats.c +++ b/fs/xfs/scrub/stats.c @@ -3,7 +3,7 @@ * Copyright (C) 2023 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/scrub/symlink.c b/fs/xfs/scrub/symlink.c index c848bcc07cd5..91d40b9fb5c6 100644 --- a/fs/xfs/scrub/symlink.c +++ b/fs/xfs/scrub/symlink.c @@ -3,7 +3,7 @@ * Copyright (C) 2017-2023 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/scrub/symlink_repair.c b/fs/xfs/scrub/symlink_repair.c index df629892462f..25416dfb5189 100644 --- a/fs/xfs/scrub/symlink_repair.c +++ b/fs/xfs/scrub/symlink_repair.c @@ -3,7 +3,7 @@ * Copyright (c) 2018-2024 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/scrub/tempfile.c b/fs/xfs/scrub/tempfile.c index cf99e0ca51b0..8d754df72aa5 100644 --- a/fs/xfs/scrub/tempfile.c +++ b/fs/xfs/scrub/tempfile.c @@ -3,7 +3,7 @@ * Copyright (c) 2021-2024 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/scrub/trace.c b/fs/xfs/scrub/trace.c index 987313a52e64..70d353287993 100644 --- a/fs/xfs/scrub/trace.c +++ b/fs/xfs/scrub/trace.c @@ -3,7 +3,7 @@ * Copyright (C) 2017-2023 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/scrub/xfarray.c b/fs/xfs/scrub/xfarray.c index ed2e8c64b1a8..c7c4a71b6fa7 100644 --- a/fs/xfs/scrub/xfarray.c +++ b/fs/xfs/scrub/xfarray.c @@ -3,7 +3,7 @@ * Copyright (C) 2021-2023 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/scrub/xfblob.c b/fs/xfs/scrub/xfblob.c index 6ef2a9637f16..96fc360312de 100644 --- a/fs/xfs/scrub/xfblob.c +++ b/fs/xfs/scrub/xfblob.c @@ -3,7 +3,7 @@ * Copyright (c) 2021-2024 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/scrub/xfile.c b/fs/xfs/scrub/xfile.c index c753c79df203..2998c9b62f4b 100644 --- a/fs/xfs/scrub/xfile.c +++ b/fs/xfs/scrub/xfile.c @@ -3,7 +3,7 @@ * Copyright (C) 2018-2023 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/xfs.h b/fs/xfs/xfs.h deleted file mode 100644 index 9355ccad9503..000000000000 --- a/fs/xfs/xfs.h +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (c) 2000-2003,2005 Silicon Graphics, Inc. - * All Rights Reserved. - */ -#ifndef __XFS_H__ -#define __XFS_H__ - -#ifdef CONFIG_XFS_DEBUG -#define DEBUG 1 -#endif - -#ifdef CONFIG_XFS_DEBUG_EXPENSIVE -#define DEBUG_EXPENSIVE 1 -#endif - -#ifdef CONFIG_XFS_ASSERT_FATAL -#define XFS_ASSERT_FATAL 1 -#endif - -#ifdef CONFIG_XFS_WARN -#define XFS_WARN 1 -#endif - - -#include "xfs_linux.h" - -#endif /* __XFS_H__ */ diff --git a/fs/xfs/xfs_acl.c b/fs/xfs/xfs_acl.c index c7c3dcfa2718..fdfca6fc75b6 100644 --- a/fs/xfs/xfs_acl.c +++ b/fs/xfs/xfs_acl.c @@ -3,7 +3,7 @@ * Copyright (c) 2008, Christoph Hellwig * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_shared.h" #include "xfs_format.h" #include "xfs_log_format.h" diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c index 56a544638491..76678814f46f 100644 --- a/fs/xfs/xfs_aops.c +++ b/fs/xfs/xfs_aops.c @@ -4,7 +4,7 @@ * Copyright (c) 2016-2025 Christoph Hellwig. * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_shared.h" #include "xfs_format.h" #include "xfs_log_format.h" @@ -103,7 +103,7 @@ xfs_ioend_put_open_zones( * IO write completion. */ STATIC void -xfs_end_ioend( +xfs_end_ioend_write( struct iomap_ioend *ioend) { struct xfs_inode *ip = XFS_I(ioend->io_inode); @@ -202,7 +202,11 @@ xfs_end_io( io_list))) { list_del_init(&ioend->io_list); iomap_ioend_try_merge(ioend, &tmp); - xfs_end_ioend(ioend); + if (bio_op(&ioend->io_bio) == REQ_OP_READ) + iomap_finish_ioends(ioend, + blk_status_to_errno(ioend->io_bio.bi_status)); + else + xfs_end_ioend_write(ioend); cond_resched(); } } diff --git a/fs/xfs/xfs_attr_inactive.c b/fs/xfs/xfs_attr_inactive.c index 319004bf089f..92331991f9fd 100644 --- a/fs/xfs/xfs_attr_inactive.c +++ b/fs/xfs/xfs_attr_inactive.c @@ -4,7 +4,7 @@ * Copyright (c) 2013 Red Hat, Inc. * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/xfs_attr_item.c b/fs/xfs/xfs_attr_item.c index e8fa326ac995..354472bf45f1 100644 --- a/fs/xfs/xfs_attr_item.c +++ b/fs/xfs/xfs_attr_item.c @@ -4,7 +4,7 @@ * Author: Allison Henderson <allison.henderson@oracle.com> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_format.h" #include "xfs_trans_resv.h" @@ -192,10 +192,9 @@ xfs_attri_item_size( STATIC void xfs_attri_item_format( struct xfs_log_item *lip, - struct xfs_log_vec *lv) + struct xlog_format_buf *lfb) { struct xfs_attri_log_item *attrip = ATTRI_ITEM(lip); - struct xfs_log_iovec *vecp = NULL; struct xfs_attri_log_nameval *nv = attrip->attri_nameval; attrip->attri_format.alfi_type = XFS_LI_ATTRI; @@ -220,24 +219,23 @@ xfs_attri_item_format( if (nv->new_value.iov_len > 0) attrip->attri_format.alfi_size++; - xlog_copy_iovec(lv, &vecp, XLOG_REG_TYPE_ATTRI_FORMAT, - &attrip->attri_format, + xlog_format_copy(lfb, XLOG_REG_TYPE_ATTRI_FORMAT, &attrip->attri_format, sizeof(struct xfs_attri_log_format)); - xlog_copy_iovec(lv, &vecp, XLOG_REG_TYPE_ATTR_NAME, nv->name.iov_base, + xlog_format_copy(lfb, XLOG_REG_TYPE_ATTR_NAME, nv->name.iov_base, nv->name.iov_len); if (nv->new_name.iov_len > 0) - xlog_copy_iovec(lv, &vecp, XLOG_REG_TYPE_ATTR_NEWNAME, - nv->new_name.iov_base, nv->new_name.iov_len); + xlog_format_copy(lfb, XLOG_REG_TYPE_ATTR_NEWNAME, + nv->new_name.iov_base, nv->new_name.iov_len); if (nv->value.iov_len > 0) - xlog_copy_iovec(lv, &vecp, XLOG_REG_TYPE_ATTR_VALUE, - nv->value.iov_base, nv->value.iov_len); + xlog_format_copy(lfb, XLOG_REG_TYPE_ATTR_VALUE, + nv->value.iov_base, nv->value.iov_len); if (nv->new_value.iov_len > 0) - xlog_copy_iovec(lv, &vecp, XLOG_REG_TYPE_ATTR_NEWVALUE, - nv->new_value.iov_base, nv->new_value.iov_len); + xlog_format_copy(lfb, XLOG_REG_TYPE_ATTR_NEWVALUE, + nv->new_value.iov_base, nv->new_value.iov_len); } /* @@ -322,16 +320,15 @@ xfs_attrd_item_size( */ STATIC void xfs_attrd_item_format( - struct xfs_log_item *lip, - struct xfs_log_vec *lv) + struct xfs_log_item *lip, + struct xlog_format_buf *lfb) { struct xfs_attrd_log_item *attrdp = ATTRD_ITEM(lip); - struct xfs_log_iovec *vecp = NULL; attrdp->attrd_format.alfd_type = XFS_LI_ATTRD; attrdp->attrd_format.alfd_size = 1; - xlog_copy_iovec(lv, &vecp, XLOG_REG_TYPE_ATTRD_FORMAT, + xlog_format_copy(lfb, XLOG_REG_TYPE_ATTRD_FORMAT, &attrdp->attrd_format, sizeof(struct xfs_attrd_log_format)); } diff --git a/fs/xfs/xfs_attr_list.c b/fs/xfs/xfs_attr_list.c index 379b48d015d2..114566b1ae5c 100644 --- a/fs/xfs/xfs_attr_list.c +++ b/fs/xfs/xfs_attr_list.c @@ -4,7 +4,7 @@ * Copyright (c) 2013 Red Hat, Inc. * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/xfs_bio_io.c b/fs/xfs/xfs_bio_io.c index 2a736d10eafb..b87e7975b613 100644 --- a/fs/xfs/xfs_bio_io.c +++ b/fs/xfs/xfs_bio_io.c @@ -2,7 +2,7 @@ /* * Copyright (c) 2019 Christoph Hellwig. */ -#include "xfs.h" +#include "xfs_platform.h" static inline unsigned int bio_max_vecs(unsigned int count) { diff --git a/fs/xfs/xfs_bmap_item.c b/fs/xfs/xfs_bmap_item.c index 80f0c4bcc483..e8775f254c89 100644 --- a/fs/xfs/xfs_bmap_item.c +++ b/fs/xfs/xfs_bmap_item.c @@ -3,7 +3,7 @@ * Copyright (C) 2016 Oracle. All Rights Reserved. * Author: Darrick J. Wong <darrick.wong@oracle.com> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_format.h" #include "xfs_log_format.h" @@ -92,10 +92,9 @@ unsigned int xfs_bui_log_space(unsigned int nr) STATIC void xfs_bui_item_format( struct xfs_log_item *lip, - struct xfs_log_vec *lv) + struct xlog_format_buf *lfb) { struct xfs_bui_log_item *buip = BUI_ITEM(lip); - struct xfs_log_iovec *vecp = NULL; ASSERT(atomic_read(&buip->bui_next_extent) == buip->bui_format.bui_nextents); @@ -103,7 +102,7 @@ xfs_bui_item_format( buip->bui_format.bui_type = XFS_LI_BUI; buip->bui_format.bui_size = 1; - xlog_copy_iovec(lv, &vecp, XLOG_REG_TYPE_BUI_FORMAT, &buip->bui_format, + xlog_format_copy(lfb, XLOG_REG_TYPE_BUI_FORMAT, &buip->bui_format, xfs_bui_log_format_sizeof(buip->bui_format.bui_nextents)); } @@ -188,15 +187,14 @@ unsigned int xfs_bud_log_space(void) STATIC void xfs_bud_item_format( struct xfs_log_item *lip, - struct xfs_log_vec *lv) + struct xlog_format_buf *lfb) { struct xfs_bud_log_item *budp = BUD_ITEM(lip); - struct xfs_log_iovec *vecp = NULL; budp->bud_format.bud_type = XFS_LI_BUD; budp->bud_format.bud_size = 1; - xlog_copy_iovec(lv, &vecp, XLOG_REG_TYPE_BUD_FORMAT, &budp->bud_format, + xlog_format_copy(lfb, XLOG_REG_TYPE_BUD_FORMAT, &budp->bud_format, sizeof(struct xfs_bud_log_format)); } diff --git a/fs/xfs/xfs_bmap_util.c b/fs/xfs/xfs_bmap_util.c index 2208a720ec3f..0ab00615f1ad 100644 --- a/fs/xfs/xfs_bmap_util.c +++ b/fs/xfs/xfs_bmap_util.c @@ -4,7 +4,7 @@ * Copyright (c) 2012 Red Hat, Inc. * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/xfs_buf.c b/fs/xfs/xfs_buf.c index 47edf3041631..db46883991de 100644 --- a/fs/xfs/xfs_buf.c +++ b/fs/xfs/xfs_buf.c @@ -3,7 +3,7 @@ * Copyright (c) 2000-2006 Silicon Graphics, Inc. * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include <linux/backing-dev.h> #include <linux/dax.h> diff --git a/fs/xfs/xfs_buf_item.c b/fs/xfs/xfs_buf_item.c index f4c5be67826e..8487635579e5 100644 --- a/fs/xfs/xfs_buf_item.c +++ b/fs/xfs/xfs_buf_item.c @@ -3,7 +3,7 @@ * Copyright (c) 2000-2005 Silicon Graphics, Inc. * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" @@ -263,24 +263,21 @@ xfs_buf_item_size( static inline void xfs_buf_item_copy_iovec( - struct xfs_log_vec *lv, - struct xfs_log_iovec **vecp, + struct xlog_format_buf *lfb, struct xfs_buf *bp, uint offset, int first_bit, uint nbits) { offset += first_bit * XFS_BLF_CHUNK; - xlog_copy_iovec(lv, vecp, XLOG_REG_TYPE_BCHUNK, - xfs_buf_offset(bp, offset), + xlog_format_copy(lfb, XLOG_REG_TYPE_BCHUNK, xfs_buf_offset(bp, offset), nbits * XFS_BLF_CHUNK); } static void xfs_buf_item_format_segment( struct xfs_buf_log_item *bip, - struct xfs_log_vec *lv, - struct xfs_log_iovec **vecp, + struct xlog_format_buf *lfb, uint offset, struct xfs_buf_log_format *blfp) { @@ -308,7 +305,7 @@ xfs_buf_item_format_segment( return; } - blfp = xlog_copy_iovec(lv, vecp, XLOG_REG_TYPE_BFORMAT, blfp, base_size); + blfp = xlog_format_copy(lfb, XLOG_REG_TYPE_BFORMAT, blfp, base_size); blfp->blf_size = 1; if (bip->bli_flags & XFS_BLI_STALE) { @@ -331,8 +328,7 @@ xfs_buf_item_format_segment( nbits = xfs_contig_bits(blfp->blf_data_map, blfp->blf_map_size, first_bit); ASSERT(nbits > 0); - xfs_buf_item_copy_iovec(lv, vecp, bp, offset, - first_bit, nbits); + xfs_buf_item_copy_iovec(lfb, bp, offset, first_bit, nbits); blfp->blf_size++; /* @@ -357,11 +353,10 @@ xfs_buf_item_format_segment( STATIC void xfs_buf_item_format( struct xfs_log_item *lip, - struct xfs_log_vec *lv) + struct xlog_format_buf *lfb) { struct xfs_buf_log_item *bip = BUF_ITEM(lip); struct xfs_buf *bp = bip->bli_buf; - struct xfs_log_iovec *vecp = NULL; uint offset = 0; int i; @@ -398,7 +393,7 @@ xfs_buf_item_format( } for (i = 0; i < bip->bli_format_count; i++) { - xfs_buf_item_format_segment(bip, lv, &vecp, offset, + xfs_buf_item_format_segment(bip, lfb, offset, &bip->bli_formats[i]); offset += BBTOB(bp->b_maps[i].bm_len); } diff --git a/fs/xfs/xfs_buf_item_recover.c b/fs/xfs/xfs_buf_item_recover.c index e4c8af873632..77ad071ebe78 100644 --- a/fs/xfs/xfs_buf_item_recover.c +++ b/fs/xfs/xfs_buf_item_recover.c @@ -3,7 +3,7 @@ * Copyright (c) 2000-2006 Silicon Graphics, Inc. * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/xfs_buf_mem.c b/fs/xfs/xfs_buf_mem.c index dcbfa274e06d..0106da0a9f44 100644 --- a/fs/xfs/xfs_buf_mem.c +++ b/fs/xfs/xfs_buf_mem.c @@ -3,7 +3,7 @@ * Copyright (c) 2023-2024 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_buf.h" #include "xfs_buf_mem.h" diff --git a/fs/xfs/xfs_dahash_test.c b/fs/xfs/xfs_dahash_test.c index 0dab5941e080..f1ee2643b948 100644 --- a/fs/xfs/xfs_dahash_test.c +++ b/fs/xfs/xfs_dahash_test.c @@ -3,7 +3,7 @@ * Copyright (C) 2023 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/xfs_dir2_readdir.c b/fs/xfs/xfs_dir2_readdir.c index 06ac5a7de60a..60a80d4173f7 100644 --- a/fs/xfs/xfs_dir2_readdir.c +++ b/fs/xfs/xfs_dir2_readdir.c @@ -4,7 +4,7 @@ * Copyright (c) 2013 Red Hat, Inc. * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/xfs_discard.c b/fs/xfs/xfs_discard.c index b6ffe4807a11..31477a74b523 100644 --- a/fs/xfs/xfs_discard.c +++ b/fs/xfs/xfs_discard.c @@ -3,7 +3,7 @@ * Copyright (C) 2010, 2023 Red Hat, Inc. * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_shared.h" #include "xfs_format.h" #include "xfs_log_format.h" diff --git a/fs/xfs/xfs_dquot.c b/fs/xfs/xfs_dquot.c index 612ca682a513..2b208e2c5264 100644 --- a/fs/xfs/xfs_dquot.c +++ b/fs/xfs/xfs_dquot.c @@ -3,7 +3,7 @@ * Copyright (c) 2000-2003 Silicon Graphics, Inc. * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_format.h" #include "xfs_log_format.h" diff --git a/fs/xfs/xfs_dquot_item.c b/fs/xfs/xfs_dquot_item.c index b374cd9f1900..491e2a7053a3 100644 --- a/fs/xfs/xfs_dquot_item.c +++ b/fs/xfs/xfs_dquot_item.c @@ -3,7 +3,7 @@ * Copyright (c) 2000-2003 Silicon Graphics, Inc. * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" @@ -44,25 +44,24 @@ xfs_qm_dquot_logitem_size( STATIC void xfs_qm_dquot_logitem_format( struct xfs_log_item *lip, - struct xfs_log_vec *lv) + struct xlog_format_buf *lfb) { struct xfs_disk_dquot ddq; struct xfs_dq_logitem *qlip = DQUOT_ITEM(lip); - struct xfs_log_iovec *vecp = NULL; struct xfs_dq_logformat *qlf; - qlf = xlog_prepare_iovec(lv, &vecp, XLOG_REG_TYPE_QFORMAT); + qlf = xlog_format_start(lfb, XLOG_REG_TYPE_QFORMAT); qlf->qlf_type = XFS_LI_DQUOT; qlf->qlf_size = 2; qlf->qlf_id = qlip->qli_dquot->q_id; qlf->qlf_blkno = qlip->qli_dquot->q_blkno; qlf->qlf_len = 1; qlf->qlf_boffset = qlip->qli_dquot->q_bufoffset; - xlog_finish_iovec(lv, vecp, sizeof(struct xfs_dq_logformat)); + xlog_format_commit(lfb, sizeof(struct xfs_dq_logformat)); xfs_dquot_to_disk(&ddq, qlip->qli_dquot); - xlog_copy_iovec(lv, &vecp, XLOG_REG_TYPE_DQUOT, &ddq, + xlog_format_copy(lfb, XLOG_REG_TYPE_DQUOT, &ddq, sizeof(struct xfs_disk_dquot)); } diff --git a/fs/xfs/xfs_dquot_item_recover.c b/fs/xfs/xfs_dquot_item_recover.c index 89bc9bcaf51e..fe419b28de22 100644 --- a/fs/xfs/xfs_dquot_item_recover.c +++ b/fs/xfs/xfs_dquot_item_recover.c @@ -3,7 +3,7 @@ * Copyright (c) 2000-2006 Silicon Graphics, Inc. * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/xfs_drain.c b/fs/xfs/xfs_drain.c index fa5f31931efd..1ad67f6c1fbf 100644 --- a/fs/xfs/xfs_drain.c +++ b/fs/xfs/xfs_drain.c @@ -3,7 +3,7 @@ * Copyright (C) 2022-2023 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/xfs_error.c b/fs/xfs/xfs_error.c index 39830b252ac8..d652240a1dca 100644 --- a/fs/xfs/xfs_error.c +++ b/fs/xfs/xfs_error.c @@ -3,7 +3,7 @@ * Copyright (c) 2000-2001,2005 Silicon Graphics, Inc. * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_shared.h" #include "xfs_format.h" #include "xfs_fs.h" @@ -22,6 +22,12 @@ static const unsigned int xfs_errortag_random_default[] = { XFS_ERRTAGS }; #undef XFS_ERRTAG +#define XFS_ERRTAG(_tag, _name, _default) \ + [XFS_ERRTAG_##_tag] = __stringify(_name), +#include "xfs_errortag.h" +static const char *xfs_errortag_names[] = { XFS_ERRTAGS }; +#undef XFS_ERRTAG + struct xfs_errortag_attr { struct attribute attr; unsigned int tag; @@ -50,17 +56,18 @@ xfs_errortag_attr_store( { struct xfs_mount *mp = to_mp(kobject); unsigned int error_tag = to_attr(attr)->tag; + unsigned int val; int ret; if (strcmp(buf, "default") == 0) { - mp->m_errortag[error_tag] = - xfs_errortag_random_default[error_tag]; + val = xfs_errortag_random_default[error_tag]; } else { - ret = kstrtouint(buf, 0, &mp->m_errortag[error_tag]); + ret = kstrtouint(buf, 0, &val); if (ret) return ret; } + WRITE_ONCE(mp->m_errortag[error_tag], val); return count; } @@ -71,9 +78,9 @@ xfs_errortag_attr_show( char *buf) { struct xfs_mount *mp = to_mp(kobject); - unsigned int error_tag = to_attr(attr)->tag; - return snprintf(buf, PAGE_SIZE, "%u\n", mp->m_errortag[error_tag]); + return snprintf(buf, PAGE_SIZE, "%u\n", + READ_ONCE(mp->m_errortag[to_attr(attr)->tag])); } static const struct sysfs_ops xfs_errortag_sysfs_ops = { @@ -114,18 +121,8 @@ int xfs_errortag_init( struct xfs_mount *mp) { - int ret; - - mp->m_errortag = kzalloc(sizeof(unsigned int) * XFS_ERRTAG_MAX, - GFP_KERNEL | __GFP_RETRY_MAYFAIL); - if (!mp->m_errortag) - return -ENOMEM; - - ret = xfs_sysfs_init(&mp->m_errortag_kobj, &xfs_errortag_ktype, + return xfs_sysfs_init(&mp->m_errortag_kobj, &xfs_errortag_ktype, &mp->m_kobj, "errortag"); - if (ret) - kfree(mp->m_errortag); - return ret; } void @@ -133,33 +130,6 @@ xfs_errortag_del( struct xfs_mount *mp) { xfs_sysfs_del(&mp->m_errortag_kobj); - kfree(mp->m_errortag); -} - -static bool -xfs_errortag_valid( - unsigned int error_tag) -{ - if (error_tag >= XFS_ERRTAG_MAX) - return false; - - /* Error out removed injection types */ - if (error_tag == XFS_ERRTAG_DROP_WRITES) - return false; - return true; -} - -bool -xfs_errortag_enabled( - struct xfs_mount *mp, - unsigned int tag) -{ - if (!mp->m_errortag) - return false; - if (!xfs_errortag_valid(tag)) - return false; - - return mp->m_errortag[tag] != 0; } bool @@ -171,21 +141,7 @@ xfs_errortag_test( { unsigned int randfactor; - /* - * To be able to use error injection anywhere, we need to ensure error - * injection mechanism is already initialized. - * - * Code paths like I/O completion can be called before the - * initialization is complete, but be able to inject errors in such - * places is still useful. - */ - if (!mp->m_errortag) - return false; - - if (!xfs_errortag_valid(error_tag)) - return false; - - randfactor = mp->m_errortag[error_tag]; + randfactor = READ_ONCE(mp->m_errortag[error_tag]); if (!randfactor || get_random_u32_below(randfactor)) return false; @@ -195,6 +151,27 @@ xfs_errortag_test( return true; } +void +xfs_errortag_delay( + struct xfs_mount *mp, + const char *file, + int line, + unsigned int error_tag) +{ + unsigned int delay = READ_ONCE(mp->m_errortag[error_tag]); + + might_sleep(); + + if (!delay) + return; + + xfs_warn_ratelimited(mp, +"Injecting %ums delay at file %s, line %d, on filesystem \"%s\"", + delay, file, line, + mp->m_super->s_id); + mdelay(delay); +} + int xfs_errortag_add( struct xfs_mount *mp, @@ -202,17 +179,60 @@ xfs_errortag_add( { BUILD_BUG_ON(ARRAY_SIZE(xfs_errortag_random_default) != XFS_ERRTAG_MAX); - if (!xfs_errortag_valid(error_tag)) + if (error_tag >= XFS_ERRTAG_MAX) + return -EINVAL; + + /* Error out removed injection types */ + switch (error_tag) { + case XFS_ERRTAG_DROP_WRITES: return -EINVAL; - mp->m_errortag[error_tag] = xfs_errortag_random_default[error_tag]; + default: + break; + } + + WRITE_ONCE(mp->m_errortag[error_tag], + xfs_errortag_random_default[error_tag]); return 0; } int +xfs_errortag_add_name( + struct xfs_mount *mp, + const char *tag_name) +{ + unsigned int i; + + for (i = 0; i < XFS_ERRTAG_MAX; i++) { + if (xfs_errortag_names[i] && + !strcmp(xfs_errortag_names[i], tag_name)) + return xfs_errortag_add(mp, i); + } + + return -EINVAL; +} + +void +xfs_errortag_copy( + struct xfs_mount *dst_mp, + struct xfs_mount *src_mp) +{ + unsigned int val, i; + + for (i = 0; i < XFS_ERRTAG_MAX; i++) { + val = READ_ONCE(src_mp->m_errortag[i]); + if (val) + WRITE_ONCE(dst_mp->m_errortag[i], val); + } +} + +int xfs_errortag_clearall( struct xfs_mount *mp) { - memset(mp->m_errortag, 0, sizeof(unsigned int) * XFS_ERRTAG_MAX); + unsigned int i; + + for (i = 0; i < XFS_ERRTAG_MAX; i++) + WRITE_ONCE(mp->m_errortag[i], 0); return 0; } #endif /* DEBUG */ diff --git a/fs/xfs/xfs_error.h b/fs/xfs/xfs_error.h index fe6a71bbe9cd..05fc1d1cf521 100644 --- a/fs/xfs/xfs_error.h +++ b/fs/xfs/xfs_error.h @@ -40,28 +40,23 @@ bool xfs_errortag_test(struct xfs_mount *mp, const char *file, int line, unsigned int error_tag); #define XFS_TEST_ERROR(mp, tag) \ xfs_errortag_test((mp), __FILE__, __LINE__, (tag)) -bool xfs_errortag_enabled(struct xfs_mount *mp, unsigned int tag); +void xfs_errortag_delay(struct xfs_mount *mp, const char *file, int line, + unsigned int error_tag); #define XFS_ERRORTAG_DELAY(mp, tag) \ - do { \ - might_sleep(); \ - if (!xfs_errortag_enabled((mp), (tag))) \ - break; \ - xfs_warn_ratelimited((mp), \ -"Injecting %ums delay at file %s, line %d, on filesystem \"%s\"", \ - (mp)->m_errortag[(tag)], __FILE__, __LINE__, \ - (mp)->m_super->s_id); \ - mdelay((mp)->m_errortag[(tag)]); \ - } while (0) - + xfs_errortag_delay((mp), __FILE__, __LINE__, (tag)) int xfs_errortag_add(struct xfs_mount *mp, unsigned int error_tag); +int xfs_errortag_add_name(struct xfs_mount *mp, const char *tag_name); +void xfs_errortag_copy(struct xfs_mount *dst_mp, struct xfs_mount *src_mp); int xfs_errortag_clearall(struct xfs_mount *mp); #else #define xfs_errortag_init(mp) (0) #define xfs_errortag_del(mp) #define XFS_TEST_ERROR(mp, tag) (false) #define XFS_ERRORTAG_DELAY(mp, tag) ((void)0) -#define xfs_errortag_add(mp, tag) (ENOSYS) -#define xfs_errortag_clearall(mp) (ENOSYS) +#define xfs_errortag_add(mp, tag) (-ENOSYS) +#define xfs_errortag_copy(dst_mp, src_mp) ((void)0) +#define xfs_errortag_add_name(mp, tag_name) (-ENOSYS) +#define xfs_errortag_clearall(mp) (-ENOSYS) #endif /* DEBUG */ /* diff --git a/fs/xfs/xfs_exchmaps_item.c b/fs/xfs/xfs_exchmaps_item.c index 229cbe0adf17..13a42467370f 100644 --- a/fs/xfs/xfs_exchmaps_item.c +++ b/fs/xfs/xfs_exchmaps_item.c @@ -3,7 +3,7 @@ * Copyright (c) 2020-2024 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_format.h" #include "xfs_log_format.h" @@ -83,16 +83,14 @@ xfs_xmi_item_size( STATIC void xfs_xmi_item_format( struct xfs_log_item *lip, - struct xfs_log_vec *lv) + struct xlog_format_buf *lfb) { struct xfs_xmi_log_item *xmi_lip = XMI_ITEM(lip); - struct xfs_log_iovec *vecp = NULL; xmi_lip->xmi_format.xmi_type = XFS_LI_XMI; xmi_lip->xmi_format.xmi_size = 1; - xlog_copy_iovec(lv, &vecp, XLOG_REG_TYPE_XMI_FORMAT, - &xmi_lip->xmi_format, + xlog_format_copy(lfb, XLOG_REG_TYPE_XMI_FORMAT, &xmi_lip->xmi_format, sizeof(struct xfs_xmi_log_format)); } @@ -166,15 +164,14 @@ xfs_xmd_item_size( STATIC void xfs_xmd_item_format( struct xfs_log_item *lip, - struct xfs_log_vec *lv) + struct xlog_format_buf *lfb) { struct xfs_xmd_log_item *xmd_lip = XMD_ITEM(lip); - struct xfs_log_iovec *vecp = NULL; xmd_lip->xmd_format.xmd_type = XFS_LI_XMD; xmd_lip->xmd_format.xmd_size = 1; - xlog_copy_iovec(lv, &vecp, XLOG_REG_TYPE_XMD_FORMAT, &xmd_lip->xmd_format, + xlog_format_copy(lfb, XLOG_REG_TYPE_XMD_FORMAT, &xmd_lip->xmd_format, sizeof(struct xfs_xmd_log_format)); } diff --git a/fs/xfs/xfs_exchrange.c b/fs/xfs/xfs_exchrange.c index 0b41bdfecdfb..5c083f29ea65 100644 --- a/fs/xfs/xfs_exchrange.c +++ b/fs/xfs/xfs_exchrange.c @@ -3,7 +3,7 @@ * Copyright (c) 2020-2024 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_shared.h" #include "xfs_format.h" #include "xfs_log_format.h" diff --git a/fs/xfs/xfs_export.c b/fs/xfs/xfs_export.c index 201489d3de08..e3e3c3c89840 100644 --- a/fs/xfs/xfs_export.c +++ b/fs/xfs/xfs_export.c @@ -3,7 +3,7 @@ * Copyright (c) 2004-2005 Silicon Graphics, Inc. * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_shared.h" #include "xfs_format.h" #include "xfs_log_format.h" diff --git a/fs/xfs/xfs_extent_busy.c b/fs/xfs/xfs_extent_busy.c index da3161572735..cfecb2959472 100644 --- a/fs/xfs/xfs_extent_busy.c +++ b/fs/xfs/xfs_extent_busy.c @@ -5,7 +5,7 @@ * Copyright (c) 2011 Christoph Hellwig. * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_format.h" #include "xfs_log_format.h" diff --git a/fs/xfs/xfs_extfree_item.c b/fs/xfs/xfs_extfree_item.c index 418ddab590e0..749a4eb9793c 100644 --- a/fs/xfs/xfs_extfree_item.c +++ b/fs/xfs/xfs_extfree_item.c @@ -3,7 +3,7 @@ * Copyright (c) 2000-2001,2005 Silicon Graphics, Inc. * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_format.h" #include "xfs_log_format.h" @@ -98,10 +98,9 @@ unsigned int xfs_efi_log_space(unsigned int nr) STATIC void xfs_efi_item_format( struct xfs_log_item *lip, - struct xfs_log_vec *lv) + struct xlog_format_buf *lfb) { struct xfs_efi_log_item *efip = EFI_ITEM(lip); - struct xfs_log_iovec *vecp = NULL; ASSERT(atomic_read(&efip->efi_next_extent) == efip->efi_format.efi_nextents); @@ -110,7 +109,7 @@ xfs_efi_item_format( efip->efi_format.efi_type = lip->li_type; efip->efi_format.efi_size = 1; - xlog_copy_iovec(lv, &vecp, XLOG_REG_TYPE_EFI_FORMAT, &efip->efi_format, + xlog_format_copy(lfb, XLOG_REG_TYPE_EFI_FORMAT, &efip->efi_format, xfs_efi_log_format_sizeof(efip->efi_format.efi_nextents)); } @@ -277,10 +276,9 @@ unsigned int xfs_efd_log_space(unsigned int nr) STATIC void xfs_efd_item_format( struct xfs_log_item *lip, - struct xfs_log_vec *lv) + struct xlog_format_buf *lfb) { struct xfs_efd_log_item *efdp = EFD_ITEM(lip); - struct xfs_log_iovec *vecp = NULL; ASSERT(efdp->efd_next_extent == efdp->efd_format.efd_nextents); ASSERT(lip->li_type == XFS_LI_EFD || lip->li_type == XFS_LI_EFD_RT); @@ -288,7 +286,7 @@ xfs_efd_item_format( efdp->efd_format.efd_type = lip->li_type; efdp->efd_format.efd_size = 1; - xlog_copy_iovec(lv, &vecp, XLOG_REG_TYPE_EFD_FORMAT, &efdp->efd_format, + xlog_format_copy(lfb, XLOG_REG_TYPE_EFD_FORMAT, &efdp->efd_format, xfs_efd_log_format_sizeof(efdp->efd_format.efd_nextents)); } diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c index 7874cf745af3..43d088a3bceb 100644 --- a/fs/xfs/xfs_file.c +++ b/fs/xfs/xfs_file.c @@ -3,7 +3,7 @@ * Copyright (c) 2000-2005 Silicon Graphics, Inc. * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" @@ -36,6 +36,7 @@ #include <linux/mman.h> #include <linux/fadvise.h> #include <linux/mount.h> +#include <linux/filelock.h> static const struct vm_operations_struct xfs_file_vm_ops; @@ -224,12 +225,34 @@ xfs_ilock_iocb_for_write( return 0; } +/* + * Bounce buffering dio reads need a user context to copy back the data. + * Use an ioend to provide that. + */ +static void +xfs_dio_read_bounce_submit_io( + const struct iomap_iter *iter, + struct bio *bio, + loff_t file_offset) +{ + iomap_init_ioend(iter->inode, bio, file_offset, IOMAP_IOEND_DIRECT); + bio->bi_end_io = xfs_end_bio; + submit_bio(bio); +} + +static const struct iomap_dio_ops xfs_dio_read_bounce_ops = { + .submit_io = xfs_dio_read_bounce_submit_io, + .bio_set = &iomap_ioend_bioset, +}; + STATIC ssize_t xfs_file_dio_read( struct kiocb *iocb, struct iov_iter *to) { struct xfs_inode *ip = XFS_I(file_inode(iocb->ki_filp)); + unsigned int dio_flags = 0; + const struct iomap_dio_ops *dio_ops = NULL; ssize_t ret; trace_xfs_file_direct_read(iocb, to); @@ -242,7 +265,12 @@ xfs_file_dio_read( ret = xfs_ilock_iocb(iocb, XFS_IOLOCK_SHARED); if (ret) return ret; - ret = iomap_dio_rw(iocb, to, &xfs_read_iomap_ops, NULL, 0, NULL, 0); + if (mapping_stable_writes(iocb->ki_filp->f_mapping)) { + dio_ops = &xfs_dio_read_bounce_ops; + dio_flags |= IOMAP_DIO_BOUNCE; + } + ret = iomap_dio_rw(iocb, to, &xfs_read_iomap_ops, dio_ops, dio_flags, + NULL, 0); xfs_iunlock(ip, XFS_IOLOCK_SHARED); return ret; @@ -703,6 +731,8 @@ xfs_file_dio_write_aligned( xfs_ilock_demote(ip, XFS_IOLOCK_EXCL); iolock = XFS_IOLOCK_SHARED; } + if (mapping_stable_writes(iocb->ki_filp->f_mapping)) + dio_flags |= IOMAP_DIO_BOUNCE; trace_xfs_file_direct_write(iocb, from); ret = iomap_dio_rw(iocb, from, ops, dops, dio_flags, ac, 0); out_unlock: @@ -750,6 +780,7 @@ xfs_file_dio_write_atomic( { unsigned int iolock = XFS_IOLOCK_SHARED; ssize_t ret, ocount = iov_iter_count(from); + unsigned int dio_flags = 0; const struct iomap_ops *dops; /* @@ -777,8 +808,10 @@ retry: } trace_xfs_file_direct_write(iocb, from); - ret = iomap_dio_rw(iocb, from, dops, &xfs_dio_write_ops, - 0, NULL, 0); + if (mapping_stable_writes(iocb->ki_filp->f_mapping)) + dio_flags |= IOMAP_DIO_BOUNCE; + ret = iomap_dio_rw(iocb, from, dops, &xfs_dio_write_ops, dio_flags, + NULL, 0); /* * The retry mechanism is based on the ->iomap_begin method returning @@ -867,6 +900,9 @@ retry_exclusive: if (flags & IOMAP_DIO_FORCE_WAIT) inode_dio_wait(VFS_I(ip)); + if (mapping_stable_writes(iocb->ki_filp->f_mapping)) + flags |= IOMAP_DIO_BOUNCE; + trace_xfs_file_direct_write(iocb, from); ret = iomap_dio_rw(iocb, from, &xfs_direct_write_iomap_ops, &xfs_dio_write_ops, flags, NULL, 0); @@ -2007,6 +2043,7 @@ const struct file_operations xfs_file_operations = { .fop_flags = FOP_MMAP_SYNC | FOP_BUFFER_RASYNC | FOP_BUFFER_WASYNC | FOP_DIO_PARALLEL_WRITE | FOP_DONTCACHE, + .setlease = generic_setlease, }; const struct file_operations xfs_dir_file_operations = { @@ -2019,4 +2056,5 @@ const struct file_operations xfs_dir_file_operations = { .compat_ioctl = xfs_file_compat_ioctl, #endif .fsync = xfs_dir_fsync, + .setlease = generic_setlease, }; diff --git a/fs/xfs/xfs_filestream.c b/fs/xfs/xfs_filestream.c index 044918fbae06..44e1b14069a3 100644 --- a/fs/xfs/xfs_filestream.c +++ b/fs/xfs/xfs_filestream.c @@ -4,7 +4,7 @@ * Copyright (c) 2014 Christoph Hellwig. * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_shared.h" #include "xfs_format.h" #include "xfs_log_format.h" diff --git a/fs/xfs/xfs_fsmap.c b/fs/xfs/xfs_fsmap.c index af68c7de8ee8..098c2b50bc6f 100644 --- a/fs/xfs/xfs_fsmap.c +++ b/fs/xfs/xfs_fsmap.c @@ -3,7 +3,7 @@ * Copyright (C) 2017 Oracle. All Rights Reserved. * Author: Darrick J. Wong <darrick.wong@oracle.com> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/xfs_fsops.c b/fs/xfs/xfs_fsops.c index 0ada73569394..17255c41786b 100644 --- a/fs/xfs/xfs_fsops.c +++ b/fs/xfs/xfs_fsops.c @@ -3,7 +3,7 @@ * Copyright (c) 2000-2005 Silicon Graphics, Inc. * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" @@ -25,6 +25,9 @@ #include "xfs_rtrmap_btree.h" #include "xfs_rtrefcount_btree.h" #include "xfs_metafile.h" +#include "xfs_healthmon.h" + +#include <linux/fserror.h> /* * Write new AG headers to disk. Non-transactional, but need to be @@ -540,6 +543,9 @@ xfs_do_force_shutdown( "Please unmount the filesystem and rectify the problem(s)"); if (xfs_error_level >= XFS_ERRLEVEL_HIGH) xfs_stack_trace(); + + fserror_report_shutdown(mp->m_super, GFP_KERNEL); + xfs_healthmon_report_shutdown(mp, flags); } /* diff --git a/fs/xfs/xfs_globals.c b/fs/xfs/xfs_globals.c index 566fd663c95b..60efe8246304 100644 --- a/fs/xfs/xfs_globals.c +++ b/fs/xfs/xfs_globals.c @@ -3,7 +3,7 @@ * Copyright (c) 2000-2005 Silicon Graphics, Inc. * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_error.h" /* diff --git a/fs/xfs/xfs_handle.c b/fs/xfs/xfs_handle.c index 5a3e3bf4e7cc..d1291ca15239 100644 --- a/fs/xfs/xfs_handle.c +++ b/fs/xfs/xfs_handle.c @@ -4,7 +4,7 @@ * Copyright (c) 2022-2024 Oracle. * All rights reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_format.h" #include "xfs_log_format.h" diff --git a/fs/xfs/xfs_health.c b/fs/xfs/xfs_health.c index 3c1557fb1cf0..169123772cb3 100644 --- a/fs/xfs/xfs_health.c +++ b/fs/xfs/xfs_health.c @@ -3,7 +3,7 @@ * Copyright (C) 2019 Oracle. All Rights Reserved. * Author: Darrick J. Wong <darrick.wong@oracle.com> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" @@ -19,6 +19,9 @@ #include "xfs_da_btree.h" #include "xfs_quota_defs.h" #include "xfs_rtgroup.h" +#include "xfs_healthmon.h" + +#include <linux/fserror.h> static void xfs_health_unmount_group( @@ -105,12 +108,19 @@ xfs_fs_mark_sick( struct xfs_mount *mp, unsigned int mask) { + unsigned int old_mask; + ASSERT(!(mask & ~XFS_SICK_FS_ALL)); trace_xfs_fs_mark_sick(mp, mask); spin_lock(&mp->m_sb_lock); + old_mask = mp->m_fs_sick; mp->m_fs_sick |= mask; spin_unlock(&mp->m_sb_lock); + + fserror_report_metadata(mp->m_super, -EFSCORRUPTED, GFP_NOFS); + if (mask) + xfs_healthmon_report_fs(mp, XFS_HEALTHMON_SICK, old_mask, mask); } /* Mark per-fs metadata as having been checked and found unhealthy by fsck. */ @@ -119,13 +129,21 @@ xfs_fs_mark_corrupt( struct xfs_mount *mp, unsigned int mask) { + unsigned int old_mask; + ASSERT(!(mask & ~XFS_SICK_FS_ALL)); trace_xfs_fs_mark_corrupt(mp, mask); spin_lock(&mp->m_sb_lock); + old_mask = mp->m_fs_sick; mp->m_fs_sick |= mask; mp->m_fs_checked |= mask; spin_unlock(&mp->m_sb_lock); + + fserror_report_metadata(mp->m_super, -EFSCORRUPTED, GFP_NOFS); + if (mask) + xfs_healthmon_report_fs(mp, XFS_HEALTHMON_CORRUPT, old_mask, + mask); } /* Mark a per-fs metadata healed. */ @@ -134,15 +152,22 @@ xfs_fs_mark_healthy( struct xfs_mount *mp, unsigned int mask) { + unsigned int old_mask; + ASSERT(!(mask & ~XFS_SICK_FS_ALL)); trace_xfs_fs_mark_healthy(mp, mask); spin_lock(&mp->m_sb_lock); + old_mask = mp->m_fs_sick; mp->m_fs_sick &= ~mask; if (!(mp->m_fs_sick & XFS_SICK_FS_PRIMARY)) mp->m_fs_sick &= ~XFS_SICK_FS_SECONDARY; mp->m_fs_checked |= mask; spin_unlock(&mp->m_sb_lock); + + if (mask) + xfs_healthmon_report_fs(mp, XFS_HEALTHMON_HEALTHY, old_mask, + mask); } /* Sample which per-fs metadata are unhealthy. */ @@ -192,12 +217,20 @@ xfs_group_mark_sick( struct xfs_group *xg, unsigned int mask) { + unsigned int old_mask; + xfs_group_check_mask(xg, mask); trace_xfs_group_mark_sick(xg, mask); spin_lock(&xg->xg_state_lock); + old_mask = xg->xg_sick; xg->xg_sick |= mask; spin_unlock(&xg->xg_state_lock); + + fserror_report_metadata(xg->xg_mount->m_super, -EFSCORRUPTED, GFP_NOFS); + if (mask) + xfs_healthmon_report_group(xg, XFS_HEALTHMON_SICK, old_mask, + mask); } /* @@ -208,13 +241,21 @@ xfs_group_mark_corrupt( struct xfs_group *xg, unsigned int mask) { + unsigned int old_mask; + xfs_group_check_mask(xg, mask); trace_xfs_group_mark_corrupt(xg, mask); spin_lock(&xg->xg_state_lock); + old_mask = xg->xg_sick; xg->xg_sick |= mask; xg->xg_checked |= mask; spin_unlock(&xg->xg_state_lock); + + fserror_report_metadata(xg->xg_mount->m_super, -EFSCORRUPTED, GFP_NOFS); + if (mask) + xfs_healthmon_report_group(xg, XFS_HEALTHMON_CORRUPT, old_mask, + mask); } /* @@ -225,15 +266,22 @@ xfs_group_mark_healthy( struct xfs_group *xg, unsigned int mask) { + unsigned int old_mask; + xfs_group_check_mask(xg, mask); trace_xfs_group_mark_healthy(xg, mask); spin_lock(&xg->xg_state_lock); + old_mask = xg->xg_sick; xg->xg_sick &= ~mask; if (!(xg->xg_sick & XFS_SICK_AG_PRIMARY)) xg->xg_sick &= ~XFS_SICK_AG_SECONDARY; xg->xg_checked |= mask; spin_unlock(&xg->xg_state_lock); + + if (mask) + xfs_healthmon_report_group(xg, XFS_HEALTHMON_HEALTHY, old_mask, + mask); } /* Sample which per-ag metadata are unhealthy. */ @@ -272,10 +320,13 @@ xfs_inode_mark_sick( struct xfs_inode *ip, unsigned int mask) { + unsigned int old_mask; + ASSERT(!(mask & ~XFS_SICK_INO_ALL)); trace_xfs_inode_mark_sick(ip, mask); spin_lock(&ip->i_flags_lock); + old_mask = ip->i_sick; ip->i_sick |= mask; spin_unlock(&ip->i_flags_lock); @@ -287,6 +338,11 @@ xfs_inode_mark_sick( spin_lock(&VFS_I(ip)->i_lock); inode_state_clear(VFS_I(ip), I_DONTCACHE); spin_unlock(&VFS_I(ip)->i_lock); + + fserror_report_file_metadata(VFS_I(ip), -EFSCORRUPTED, GFP_NOFS); + if (mask) + xfs_healthmon_report_inode(ip, XFS_HEALTHMON_SICK, old_mask, + mask); } /* Mark inode metadata as having been checked and found unhealthy by fsck. */ @@ -295,10 +351,13 @@ xfs_inode_mark_corrupt( struct xfs_inode *ip, unsigned int mask) { + unsigned int old_mask; + ASSERT(!(mask & ~XFS_SICK_INO_ALL)); trace_xfs_inode_mark_corrupt(ip, mask); spin_lock(&ip->i_flags_lock); + old_mask = ip->i_sick; ip->i_sick |= mask; ip->i_checked |= mask; spin_unlock(&ip->i_flags_lock); @@ -311,6 +370,11 @@ xfs_inode_mark_corrupt( spin_lock(&VFS_I(ip)->i_lock); inode_state_clear(VFS_I(ip), I_DONTCACHE); spin_unlock(&VFS_I(ip)->i_lock); + + fserror_report_file_metadata(VFS_I(ip), -EFSCORRUPTED, GFP_NOFS); + if (mask) + xfs_healthmon_report_inode(ip, XFS_HEALTHMON_CORRUPT, old_mask, + mask); } /* Mark parts of an inode healed. */ @@ -319,15 +383,22 @@ xfs_inode_mark_healthy( struct xfs_inode *ip, unsigned int mask) { + unsigned int old_mask; + ASSERT(!(mask & ~XFS_SICK_INO_ALL)); trace_xfs_inode_mark_healthy(ip, mask); spin_lock(&ip->i_flags_lock); + old_mask = ip->i_sick; ip->i_sick &= ~mask; if (!(ip->i_sick & XFS_SICK_INO_PRIMARY)) ip->i_sick &= ~XFS_SICK_INO_SECONDARY; ip->i_checked |= mask; spin_unlock(&ip->i_flags_lock); + + if (mask) + xfs_healthmon_report_inode(ip, XFS_HEALTHMON_HEALTHY, old_mask, + mask); } /* Sample which parts of an inode are unhealthy. */ @@ -407,6 +478,25 @@ xfs_fsop_geom_health( } } +/* + * Translate XFS_SICK_FS_* into XFS_FSOP_GEOM_SICK_* except for the rt free + * space codes, which are sent via the rtgroup events. + */ +unsigned int +xfs_healthmon_fs_mask( + unsigned int sick_mask) +{ + const struct ioctl_sick_map *m; + unsigned int ioctl_mask = 0; + + for_each_sick_map(fs_map, m) { + if (sick_mask & m->sick_mask) + ioctl_mask |= m->ioctl_mask; + } + + return ioctl_mask; +} + static const struct ioctl_sick_map ag_map[] = { { XFS_SICK_AG_SB, XFS_AG_GEOM_SICK_SB }, { XFS_SICK_AG_AGF, XFS_AG_GEOM_SICK_AGF }, @@ -443,6 +533,22 @@ xfs_ag_geom_health( } } +/* Translate XFS_SICK_AG_* into XFS_AG_GEOM_SICK_*. */ +unsigned int +xfs_healthmon_perag_mask( + unsigned int sick_mask) +{ + const struct ioctl_sick_map *m; + unsigned int ioctl_mask = 0; + + for_each_sick_map(ag_map, m) { + if (sick_mask & m->sick_mask) + ioctl_mask |= m->ioctl_mask; + } + + return ioctl_mask; +} + static const struct ioctl_sick_map rtgroup_map[] = { { XFS_SICK_RG_SUPER, XFS_RTGROUP_GEOM_SICK_SUPER }, { XFS_SICK_RG_BITMAP, XFS_RTGROUP_GEOM_SICK_BITMAP }, @@ -473,6 +579,22 @@ xfs_rtgroup_geom_health( } } +/* Translate XFS_SICK_RG_* into XFS_RTGROUP_GEOM_SICK_*. */ +unsigned int +xfs_healthmon_rtgroup_mask( + unsigned int sick_mask) +{ + const struct ioctl_sick_map *m; + unsigned int ioctl_mask = 0; + + for_each_sick_map(rtgroup_map, m) { + if (sick_mask & m->sick_mask) + ioctl_mask |= m->ioctl_mask; + } + + return ioctl_mask; +} + static const struct ioctl_sick_map ino_map[] = { { XFS_SICK_INO_CORE, XFS_BS_SICK_INODE }, { XFS_SICK_INO_BMBTD, XFS_BS_SICK_BMBTD }, @@ -511,6 +633,22 @@ xfs_bulkstat_health( } } +/* Translate XFS_SICK_INO_* into XFS_BS_SICK_*. */ +unsigned int +xfs_healthmon_inode_mask( + unsigned int sick_mask) +{ + const struct ioctl_sick_map *m; + unsigned int ioctl_mask = 0; + + for_each_sick_map(ino_map, m) { + if (sick_mask & m->sick_mask) + ioctl_mask |= m->ioctl_mask; + } + + return ioctl_mask; +} + /* Mark a block mapping sick. */ void xfs_bmap_mark_sick( diff --git a/fs/xfs/xfs_healthmon.c b/fs/xfs/xfs_healthmon.c new file mode 100644 index 000000000000..ca7352dcd182 --- /dev/null +++ b/fs/xfs/xfs_healthmon.c @@ -0,0 +1,1255 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2024-2026 Oracle. All Rights Reserved. + * Author: Darrick J. Wong <djwong@kernel.org> + */ +#include "xfs_platform.h" +#include "xfs_fs.h" +#include "xfs_shared.h" +#include "xfs_format.h" +#include "xfs_log_format.h" +#include "xfs_trans_resv.h" +#include "xfs_mount.h" +#include "xfs_inode.h" +#include "xfs_trace.h" +#include "xfs_ag.h" +#include "xfs_btree.h" +#include "xfs_da_format.h" +#include "xfs_da_btree.h" +#include "xfs_quota_defs.h" +#include "xfs_rtgroup.h" +#include "xfs_health.h" +#include "xfs_healthmon.h" +#include "xfs_fsops.h" +#include "xfs_notify_failure.h" +#include "xfs_file.h" +#include "xfs_ioctl.h" + +#include <linux/anon_inodes.h> +#include <linux/eventpoll.h> +#include <linux/poll.h> +#include <linux/fserror.h> + +/* + * Live Health Monitoring + * ====================== + * + * Autonomous self-healing of XFS filesystems requires a means for the kernel + * to send filesystem health events to a monitoring daemon in userspace. To + * accomplish this, we establish a thread_with_file kthread object to handle + * translating internal events about filesystem health into a format that can + * be parsed easily by userspace. When those internal events occur, the core + * filesystem code calls this health monitor to convey the events to userspace. + * Userspace reads events from the file descriptor returned by the ioctl. + * + * The healthmon abstraction has a weak reference to the host filesystem mount + * so that the queueing and processing of the events do not pin the mount and + * cannot slow down the main filesystem. The healthmon object can exist past + * the end of the filesystem mount. + */ + +/* sign of a detached health monitor */ +#define DETACHED_MOUNT_COOKIE ((uintptr_t)0) + +/* Constrain the number of event objects that can build up in memory. */ +#define XFS_HEALTHMON_MAX_EVENTS (SZ_32K / \ + sizeof(struct xfs_healthmon_event)) + +/* Constrain the size of the output buffer for read_iter. */ +#define XFS_HEALTHMON_MAX_OUTBUF SZ_64K + +/* spinlock for atomically updating xfs_mount <-> xfs_healthmon pointers */ +static DEFINE_SPINLOCK(xfs_healthmon_lock); + +/* Grab a reference to the healthmon object for a given mount, if any. */ +static struct xfs_healthmon * +xfs_healthmon_get( + struct xfs_mount *mp) +{ + struct xfs_healthmon *hm; + + rcu_read_lock(); + hm = mp->m_healthmon; + if (hm && !refcount_inc_not_zero(&hm->ref)) + hm = NULL; + rcu_read_unlock(); + + return hm; +} + +/* + * Release the reference to a healthmon object. If there are no more holders, + * free the health monitor after an RCU grace period to eliminate possibility + * of races with xfs_healthmon_get. + */ +static void +xfs_healthmon_put( + struct xfs_healthmon *hm) +{ + if (refcount_dec_and_test(&hm->ref)) { + struct xfs_healthmon_event *event; + struct xfs_healthmon_event *next = hm->first_event; + + while ((event = next) != NULL) { + trace_xfs_healthmon_drop(hm, event); + next = event->next; + kfree(event); + } + + kfree(hm->unmount_event); + kfree(hm->buffer); + mutex_destroy(&hm->lock); + kfree_rcu_mightsleep(hm); + } +} + +/* Attach a health monitor to an xfs_mount. Only one allowed at a time. */ +STATIC int +xfs_healthmon_attach( + struct xfs_mount *mp, + struct xfs_healthmon *hm) +{ + spin_lock(&xfs_healthmon_lock); + if (mp->m_healthmon != NULL) { + spin_unlock(&xfs_healthmon_lock); + return -EEXIST; + } + + refcount_inc(&hm->ref); + mp->m_healthmon = hm; + hm->mount_cookie = (uintptr_t)mp->m_super; + spin_unlock(&xfs_healthmon_lock); + + return 0; +} + +/* Detach a xfs mount from a specific healthmon instance. */ +STATIC void +xfs_healthmon_detach( + struct xfs_healthmon *hm) +{ + spin_lock(&xfs_healthmon_lock); + if (hm->mount_cookie == DETACHED_MOUNT_COOKIE) { + spin_unlock(&xfs_healthmon_lock); + return; + } + + XFS_M((struct super_block *)hm->mount_cookie)->m_healthmon = NULL; + hm->mount_cookie = DETACHED_MOUNT_COOKIE; + spin_unlock(&xfs_healthmon_lock); + + trace_xfs_healthmon_detach(hm); + xfs_healthmon_put(hm); +} + +static inline void xfs_healthmon_bump_events(struct xfs_healthmon *hm) +{ + hm->events++; + hm->total_events++; +} + +static inline void xfs_healthmon_bump_lost(struct xfs_healthmon *hm) +{ + hm->lost_prev_event++; + hm->total_lost++; +} + +/* + * If possible, merge a new event into an existing event. Returns whether or + * not it merged anything. + */ +static bool +xfs_healthmon_merge_events( + struct xfs_healthmon_event *existing, + const struct xfs_healthmon_event *new) +{ + if (!existing) + return false; + + /* type and domain must match to merge events */ + if (existing->type != new->type || + existing->domain != new->domain) + return false; + + switch (existing->type) { + case XFS_HEALTHMON_RUNNING: + case XFS_HEALTHMON_UNMOUNT: + /* should only ever be one of these events anyway */ + return false; + + case XFS_HEALTHMON_LOST: + existing->lostcount += new->lostcount; + return true; + + case XFS_HEALTHMON_SICK: + case XFS_HEALTHMON_CORRUPT: + case XFS_HEALTHMON_HEALTHY: + switch (existing->domain) { + case XFS_HEALTHMON_FS: + existing->fsmask |= new->fsmask; + return true; + case XFS_HEALTHMON_AG: + case XFS_HEALTHMON_RTGROUP: + if (existing->group == new->group){ + existing->grpmask |= new->grpmask; + return true; + } + return false; + case XFS_HEALTHMON_INODE: + if (existing->ino == new->ino && + existing->gen == new->gen) { + existing->imask |= new->imask; + return true; + } + return false; + default: + ASSERT(0); + return false; + } + return false; + + case XFS_HEALTHMON_SHUTDOWN: + /* yes, we can race to shutdown */ + existing->flags |= new->flags; + return true; + + case XFS_HEALTHMON_MEDIA_ERROR: + /* physically adjacent errors can merge */ + if (existing->daddr + existing->bbcount == new->daddr) { + existing->bbcount += new->bbcount; + return true; + } + if (new->daddr + new->bbcount == existing->daddr) { + existing->daddr = new->daddr; + existing->bbcount += new->bbcount; + return true; + } + return false; + + case XFS_HEALTHMON_BUFREAD: + case XFS_HEALTHMON_BUFWRITE: + case XFS_HEALTHMON_DIOREAD: + case XFS_HEALTHMON_DIOWRITE: + case XFS_HEALTHMON_DATALOST: + /* logically adjacent file ranges can merge */ + if (existing->fino != new->fino || existing->fgen != new->fgen) + return false; + + if (existing->fpos + existing->flen == new->fpos) { + existing->flen += new->flen; + return true; + } + + if (new->fpos + new->flen == existing->fpos) { + existing->fpos = new->fpos; + existing->flen += new->flen; + return true; + } + return false; + } + + return false; +} + +/* Insert an event onto the start of the queue. */ +static inline void +__xfs_healthmon_insert( + struct xfs_healthmon *hm, + struct xfs_healthmon_event *event) +{ + struct timespec64 now; + + ktime_get_coarse_real_ts64(&now); + event->time_ns = (now.tv_sec * NSEC_PER_SEC) + now.tv_nsec; + + event->next = hm->first_event; + if (!hm->first_event) + hm->first_event = event; + if (!hm->last_event) + hm->last_event = event; + xfs_healthmon_bump_events(hm); + wake_up(&hm->wait); + + trace_xfs_healthmon_insert(hm, event); +} + +/* Push an event onto the end of the queue. */ +static inline void +__xfs_healthmon_push( + struct xfs_healthmon *hm, + struct xfs_healthmon_event *event) +{ + struct timespec64 now; + + ktime_get_coarse_real_ts64(&now); + event->time_ns = (now.tv_sec * NSEC_PER_SEC) + now.tv_nsec; + + if (!hm->first_event) + hm->first_event = event; + if (hm->last_event) + hm->last_event->next = event; + hm->last_event = event; + event->next = NULL; + xfs_healthmon_bump_events(hm); + wake_up(&hm->wait); + + trace_xfs_healthmon_push(hm, event); +} + +/* Deal with any previously lost events */ +static int +xfs_healthmon_clear_lost_prev( + struct xfs_healthmon *hm) +{ + struct xfs_healthmon_event lost_event = { + .type = XFS_HEALTHMON_LOST, + .domain = XFS_HEALTHMON_MOUNT, + .lostcount = hm->lost_prev_event, + }; + struct xfs_healthmon_event *event = NULL; + + if (xfs_healthmon_merge_events(hm->last_event, &lost_event)) { + trace_xfs_healthmon_merge(hm, hm->last_event); + wake_up(&hm->wait); + goto cleared; + } + + if (hm->events < XFS_HEALTHMON_MAX_EVENTS) + event = kmemdup(&lost_event, sizeof(struct xfs_healthmon_event), + GFP_NOFS); + if (!event) + return -ENOMEM; + + __xfs_healthmon_push(hm, event); +cleared: + hm->lost_prev_event = 0; + return 0; +} + +/* + * Push an event onto the end of the list after dealing with lost events and + * possibly full queues. + */ +STATIC int +xfs_healthmon_push( + struct xfs_healthmon *hm, + const struct xfs_healthmon_event *template) +{ + struct xfs_healthmon_event *event = NULL; + int error = 0; + + /* + * Locklessly check if the health monitor has already detached from the + * mount. If so, ignore the event. If we race with deactivation, + * we'll queue the event but never send it. + */ + if (hm->mount_cookie == DETACHED_MOUNT_COOKIE) + return -ESHUTDOWN; + + mutex_lock(&hm->lock); + + /* Report previously lost events before we do anything else */ + if (hm->lost_prev_event) { + error = xfs_healthmon_clear_lost_prev(hm); + if (error) + goto out_unlock; + } + + /* Try to merge with the newest event */ + if (xfs_healthmon_merge_events(hm->last_event, template)) { + trace_xfs_healthmon_merge(hm, hm->last_event); + wake_up(&hm->wait); + goto out_unlock; + } + + /* Only create a heap event object if we're not already at capacity. */ + if (hm->events < XFS_HEALTHMON_MAX_EVENTS) + event = kmemdup(template, sizeof(struct xfs_healthmon_event), + GFP_NOFS); + if (!event) { + /* No memory means we lose the event */ + trace_xfs_healthmon_lost_event(hm); + xfs_healthmon_bump_lost(hm); + error = -ENOMEM; + goto out_unlock; + } + + __xfs_healthmon_push(hm, event); + +out_unlock: + mutex_unlock(&hm->lock); + return error; +} + +/* + * Report that the filesystem is being unmounted, then detach the xfs mount + * from this healthmon instance. + */ +void +xfs_healthmon_unmount( + struct xfs_mount *mp) +{ + struct xfs_healthmon *hm = xfs_healthmon_get(mp); + + if (!hm) + return; + + trace_xfs_healthmon_report_unmount(hm); + + /* + * Insert the unmount notification at the start of the event queue so + * that userspace knows the filesystem went away as soon as possible. + * There's nothing actionable for userspace after an unmount. Once + * we've inserted the unmount event, hm no longer owns that event. + */ + __xfs_healthmon_insert(hm, hm->unmount_event); + hm->unmount_event = NULL; + + xfs_healthmon_detach(hm); + xfs_healthmon_put(hm); +} + +/* Compute the reporting mask for non-unmount metadata health events. */ +static inline unsigned int +metadata_event_mask( + struct xfs_healthmon *hm, + enum xfs_healthmon_type type, + unsigned int old_mask, + unsigned int new_mask) +{ + /* If we want all events, return all events. */ + if (hm->verbose) + return new_mask; + + switch (type) { + case XFS_HEALTHMON_SICK: + /* Always report runtime corruptions */ + return new_mask; + case XFS_HEALTHMON_CORRUPT: + /* Only report new fsck errors */ + return new_mask & ~old_mask; + case XFS_HEALTHMON_HEALTHY: + /* Only report healthy metadata that got fixed */ + return new_mask & old_mask; + default: + ASSERT(0); + break; + } + + return 0; +} + +/* Report XFS_FS_SICK_* events to healthmon */ +void +xfs_healthmon_report_fs( + struct xfs_mount *mp, + enum xfs_healthmon_type type, + unsigned int old_mask, + unsigned int new_mask) +{ + struct xfs_healthmon_event event = { + .type = type, + .domain = XFS_HEALTHMON_FS, + }; + struct xfs_healthmon *hm = xfs_healthmon_get(mp); + + if (!hm) + return; + + event.fsmask = metadata_event_mask(hm, type, old_mask, new_mask) & + ~XFS_SICK_FS_SECONDARY; + trace_xfs_healthmon_report_fs(hm, old_mask, new_mask, &event); + + if (event.fsmask) + xfs_healthmon_push(hm, &event); + + xfs_healthmon_put(hm); +} + +/* Report XFS_SICK_(AG|RG)* flags to healthmon */ +void +xfs_healthmon_report_group( + struct xfs_group *xg, + enum xfs_healthmon_type type, + unsigned int old_mask, + unsigned int new_mask) +{ + struct xfs_healthmon_event event = { + .type = type, + .group = xg->xg_gno, + }; + struct xfs_healthmon *hm = xfs_healthmon_get(xg->xg_mount); + + if (!hm) + return; + + switch (xg->xg_type) { + case XG_TYPE_RTG: + event.domain = XFS_HEALTHMON_RTGROUP; + event.grpmask = metadata_event_mask(hm, type, old_mask, + new_mask) & + ~XFS_SICK_RG_SECONDARY; + break; + case XG_TYPE_AG: + event.domain = XFS_HEALTHMON_AG; + event.grpmask = metadata_event_mask(hm, type, old_mask, + new_mask) & + ~XFS_SICK_AG_SECONDARY; + break; + default: + ASSERT(0); + break; + } + + trace_xfs_healthmon_report_group(hm, old_mask, new_mask, &event); + + if (event.grpmask) + xfs_healthmon_push(hm, &event); + + xfs_healthmon_put(hm); +} + +/* Report XFS_SICK_INO_* flags to healthmon */ +void +xfs_healthmon_report_inode( + struct xfs_inode *ip, + enum xfs_healthmon_type type, + unsigned int old_mask, + unsigned int new_mask) +{ + struct xfs_healthmon_event event = { + .type = type, + .domain = XFS_HEALTHMON_INODE, + .ino = ip->i_ino, + .gen = VFS_I(ip)->i_generation, + }; + struct xfs_healthmon *hm = xfs_healthmon_get(ip->i_mount); + + if (!hm) + return; + + event.imask = metadata_event_mask(hm, type, old_mask, new_mask) & + ~XFS_SICK_INO_SECONDARY; + trace_xfs_healthmon_report_inode(hm, old_mask, event.imask, &event); + + if (event.imask) + xfs_healthmon_push(hm, &event); + + xfs_healthmon_put(hm); +} + +/* Add a shutdown event to the reporting queue. */ +void +xfs_healthmon_report_shutdown( + struct xfs_mount *mp, + uint32_t flags) +{ + struct xfs_healthmon_event event = { + .type = XFS_HEALTHMON_SHUTDOWN, + .domain = XFS_HEALTHMON_MOUNT, + .flags = flags, + }; + struct xfs_healthmon *hm = xfs_healthmon_get(mp); + + if (!hm) + return; + + trace_xfs_healthmon_report_shutdown(hm, flags); + + xfs_healthmon_push(hm, &event); + xfs_healthmon_put(hm); +} + +static inline enum xfs_healthmon_domain +media_error_domain( + enum xfs_device fdev) +{ + switch (fdev) { + case XFS_DEV_DATA: + return XFS_HEALTHMON_DATADEV; + case XFS_DEV_LOG: + return XFS_HEALTHMON_LOGDEV; + case XFS_DEV_RT: + return XFS_HEALTHMON_RTDEV; + } + + ASSERT(0); + return 0; +} + +/* Add a media error event to the reporting queue. */ +void +xfs_healthmon_report_media( + struct xfs_mount *mp, + enum xfs_device fdev, + xfs_daddr_t daddr, + uint64_t bbcount) +{ + struct xfs_healthmon_event event = { + .type = XFS_HEALTHMON_MEDIA_ERROR, + .domain = media_error_domain(fdev), + .daddr = daddr, + .bbcount = bbcount, + }; + struct xfs_healthmon *hm = xfs_healthmon_get(mp); + + if (!hm) + return; + + trace_xfs_healthmon_report_media(hm, fdev, &event); + + xfs_healthmon_push(hm, &event); + xfs_healthmon_put(hm); +} + +static inline enum xfs_healthmon_type file_ioerr_type(enum fserror_type action) +{ + switch (action) { + case FSERR_BUFFERED_READ: + return XFS_HEALTHMON_BUFREAD; + case FSERR_BUFFERED_WRITE: + return XFS_HEALTHMON_BUFWRITE; + case FSERR_DIRECTIO_READ: + return XFS_HEALTHMON_DIOREAD; + case FSERR_DIRECTIO_WRITE: + return XFS_HEALTHMON_DIOWRITE; + case FSERR_DATA_LOST: + return XFS_HEALTHMON_DATALOST; + case FSERR_METADATA: + /* filtered out by xfs_fs_report_error */ + break; + } + + ASSERT(0); + return -1; +} + +/* Add a file io error event to the reporting queue. */ +void +xfs_healthmon_report_file_ioerror( + struct xfs_inode *ip, + const struct fserror_event *p) +{ + struct xfs_healthmon_event event = { + .type = file_ioerr_type(p->type), + .domain = XFS_HEALTHMON_FILERANGE, + .fino = ip->i_ino, + .fgen = VFS_I(ip)->i_generation, + .fpos = p->pos, + .flen = p->len, + /* send positive error number to userspace */ + .error = -p->error, + }; + struct xfs_healthmon *hm = xfs_healthmon_get(ip->i_mount); + + if (!hm) + return; + + trace_xfs_healthmon_report_file_ioerror(hm, p); + + xfs_healthmon_push(hm, &event); + xfs_healthmon_put(hm); +} + +static inline void +xfs_healthmon_reset_outbuf( + struct xfs_healthmon *hm) +{ + hm->buftail = 0; + hm->bufhead = 0; +} + +struct flags_map { + unsigned int in_mask; + unsigned int out_mask; +}; + +static const struct flags_map shutdown_map[] = { + { SHUTDOWN_META_IO_ERROR, XFS_HEALTH_SHUTDOWN_META_IO_ERROR }, + { SHUTDOWN_LOG_IO_ERROR, XFS_HEALTH_SHUTDOWN_LOG_IO_ERROR }, + { SHUTDOWN_FORCE_UMOUNT, XFS_HEALTH_SHUTDOWN_FORCE_UMOUNT }, + { SHUTDOWN_CORRUPT_INCORE, XFS_HEALTH_SHUTDOWN_CORRUPT_INCORE }, + { SHUTDOWN_CORRUPT_ONDISK, XFS_HEALTH_SHUTDOWN_CORRUPT_ONDISK }, + { SHUTDOWN_DEVICE_REMOVED, XFS_HEALTH_SHUTDOWN_DEVICE_REMOVED }, +}; + +static inline unsigned int +__map_flags( + const struct flags_map *map, + size_t array_len, + unsigned int flags) +{ + const struct flags_map *m; + unsigned int ret = 0; + + for (m = map; m < map + array_len; m++) { + if (flags & m->in_mask) + ret |= m->out_mask; + } + + return ret; +} + +#define map_flags(map, flags) __map_flags((map), ARRAY_SIZE(map), (flags)) + +static inline unsigned int shutdown_mask(unsigned int in) +{ + return map_flags(shutdown_map, in); +} + +static const unsigned int domain_map[] = { + [XFS_HEALTHMON_MOUNT] = XFS_HEALTH_MONITOR_DOMAIN_MOUNT, + [XFS_HEALTHMON_FS] = XFS_HEALTH_MONITOR_DOMAIN_FS, + [XFS_HEALTHMON_AG] = XFS_HEALTH_MONITOR_DOMAIN_AG, + [XFS_HEALTHMON_INODE] = XFS_HEALTH_MONITOR_DOMAIN_INODE, + [XFS_HEALTHMON_RTGROUP] = XFS_HEALTH_MONITOR_DOMAIN_RTGROUP, + [XFS_HEALTHMON_DATADEV] = XFS_HEALTH_MONITOR_DOMAIN_DATADEV, + [XFS_HEALTHMON_RTDEV] = XFS_HEALTH_MONITOR_DOMAIN_RTDEV, + [XFS_HEALTHMON_LOGDEV] = XFS_HEALTH_MONITOR_DOMAIN_LOGDEV, + [XFS_HEALTHMON_FILERANGE] = XFS_HEALTH_MONITOR_DOMAIN_FILERANGE, +}; + +static const unsigned int type_map[] = { + [XFS_HEALTHMON_RUNNING] = XFS_HEALTH_MONITOR_TYPE_RUNNING, + [XFS_HEALTHMON_LOST] = XFS_HEALTH_MONITOR_TYPE_LOST, + [XFS_HEALTHMON_SICK] = XFS_HEALTH_MONITOR_TYPE_SICK, + [XFS_HEALTHMON_CORRUPT] = XFS_HEALTH_MONITOR_TYPE_CORRUPT, + [XFS_HEALTHMON_HEALTHY] = XFS_HEALTH_MONITOR_TYPE_HEALTHY, + [XFS_HEALTHMON_UNMOUNT] = XFS_HEALTH_MONITOR_TYPE_UNMOUNT, + [XFS_HEALTHMON_SHUTDOWN] = XFS_HEALTH_MONITOR_TYPE_SHUTDOWN, + [XFS_HEALTHMON_MEDIA_ERROR] = XFS_HEALTH_MONITOR_TYPE_MEDIA_ERROR, + [XFS_HEALTHMON_BUFREAD] = XFS_HEALTH_MONITOR_TYPE_BUFREAD, + [XFS_HEALTHMON_BUFWRITE] = XFS_HEALTH_MONITOR_TYPE_BUFWRITE, + [XFS_HEALTHMON_DIOREAD] = XFS_HEALTH_MONITOR_TYPE_DIOREAD, + [XFS_HEALTHMON_DIOWRITE] = XFS_HEALTH_MONITOR_TYPE_DIOWRITE, + [XFS_HEALTHMON_DATALOST] = XFS_HEALTH_MONITOR_TYPE_DATALOST, +}; + +/* Render event as a V0 structure */ +STATIC int +xfs_healthmon_format_v0( + struct xfs_healthmon *hm, + const struct xfs_healthmon_event *event) +{ + struct xfs_health_monitor_event hme = { + .time_ns = event->time_ns, + }; + + trace_xfs_healthmon_format(hm, event); + + if (event->domain < 0 || event->domain >= ARRAY_SIZE(domain_map) || + event->type < 0 || event->type >= ARRAY_SIZE(type_map)) + return -EFSCORRUPTED; + + hme.domain = domain_map[event->domain]; + hme.type = type_map[event->type]; + + /* fill in the event-specific details */ + switch (event->domain) { + case XFS_HEALTHMON_MOUNT: + switch (event->type) { + case XFS_HEALTHMON_LOST: + hme.e.lost.count = event->lostcount; + break; + case XFS_HEALTHMON_SHUTDOWN: + hme.e.shutdown.reasons = shutdown_mask(event->flags); + break; + default: + break; + } + break; + case XFS_HEALTHMON_FS: + hme.e.fs.mask = xfs_healthmon_fs_mask(event->fsmask); + break; + case XFS_HEALTHMON_RTGROUP: + hme.e.group.mask = xfs_healthmon_rtgroup_mask(event->grpmask); + hme.e.group.gno = event->group; + break; + case XFS_HEALTHMON_AG: + hme.e.group.mask = xfs_healthmon_perag_mask(event->grpmask); + hme.e.group.gno = event->group; + break; + case XFS_HEALTHMON_INODE: + hme.e.inode.mask = xfs_healthmon_inode_mask(event->imask); + hme.e.inode.ino = event->ino; + hme.e.inode.gen = event->gen; + break; + case XFS_HEALTHMON_DATADEV: + case XFS_HEALTHMON_LOGDEV: + case XFS_HEALTHMON_RTDEV: + hme.e.media.daddr = event->daddr; + hme.e.media.bbcount = event->bbcount; + break; + case XFS_HEALTHMON_FILERANGE: + hme.e.filerange.ino = event->fino; + hme.e.filerange.gen = event->fgen; + hme.e.filerange.pos = event->fpos; + hme.e.filerange.len = event->flen; + hme.e.filerange.error = abs(event->error); + break; + default: + break; + } + + ASSERT(hm->bufhead + sizeof(hme) <= hm->bufsize); + + /* copy formatted object to the outbuf */ + if (hm->bufhead + sizeof(hme) <= hm->bufsize) { + memcpy(hm->buffer + hm->bufhead, &hme, sizeof(hme)); + hm->bufhead += sizeof(hme); + } + + return 0; +} + +/* How many bytes are waiting in the outbuf to be copied? */ +static inline size_t +xfs_healthmon_outbuf_bytes( + struct xfs_healthmon *hm) +{ + if (hm->bufhead > hm->buftail) + return hm->bufhead - hm->buftail; + return 0; +} + +/* + * Do we have something for userspace to read? This can mean unmount events, + * events pending in the queue, or pending bytes in the outbuf. + */ +static inline bool +xfs_healthmon_has_eventdata( + struct xfs_healthmon *hm) +{ + /* + * If the health monitor is already detached from the xfs_mount, we + * want reads to return 0 bytes even if there are no events, because + * userspace interprets that as EOF. If we race with deactivation, + * read_iter will take the necessary locks to discover that there are + * no events to send. + */ + if (hm->mount_cookie == DETACHED_MOUNT_COOKIE) + return true; + + /* + * Either there are events waiting to be formatted into the buffer, or + * there's unread bytes in the buffer. + */ + return hm->events > 0 || xfs_healthmon_outbuf_bytes(hm) > 0; +} + +/* Try to copy the rest of the outbuf to the iov iter. */ +STATIC ssize_t +xfs_healthmon_copybuf( + struct xfs_healthmon *hm, + struct iov_iter *to) +{ + size_t to_copy; + size_t w = 0; + + trace_xfs_healthmon_copybuf(hm, to); + + to_copy = xfs_healthmon_outbuf_bytes(hm); + if (to_copy) { + w = copy_to_iter(hm->buffer + hm->buftail, to_copy, to); + if (!w) + return -EFAULT; + + hm->buftail += w; + } + + /* + * Nothing left to copy? Reset the output buffer cursors to the start + * since there's no live data in the buffer. + */ + if (xfs_healthmon_outbuf_bytes(hm) == 0) + xfs_healthmon_reset_outbuf(hm); + return w; +} + +/* + * Return a health monitoring event for formatting into the output buffer if + * there's enough space in the outbuf and an event waiting for us. Caller + * must hold i_rwsem on the healthmon file. + */ +static inline struct xfs_healthmon_event * +xfs_healthmon_format_pop( + struct xfs_healthmon *hm) +{ + struct xfs_healthmon_event *event; + + if (hm->bufhead + sizeof(*event) > hm->bufsize) + return NULL; + + mutex_lock(&hm->lock); + event = hm->first_event; + if (event) { + if (hm->last_event == event) + hm->last_event = NULL; + hm->first_event = event->next; + hm->events--; + + trace_xfs_healthmon_pop(hm, event); + } + mutex_unlock(&hm->lock); + return event; +} + +/* Allocate formatting buffer */ +STATIC int +xfs_healthmon_alloc_outbuf( + struct xfs_healthmon *hm, + size_t user_bufsize) +{ + void *outbuf; + size_t bufsize = + min(XFS_HEALTHMON_MAX_OUTBUF, max(PAGE_SIZE, user_bufsize)); + + outbuf = kzalloc(bufsize, GFP_KERNEL); + if (!outbuf) { + if (bufsize == PAGE_SIZE) + return -ENOMEM; + + bufsize = PAGE_SIZE; + outbuf = kzalloc(bufsize, GFP_KERNEL); + if (!outbuf) + return -ENOMEM; + } + + hm->buffer = outbuf; + hm->bufsize = bufsize; + hm->bufhead = 0; + hm->buftail = 0; + + return 0; +} + +/* + * Convey queued event data to userspace. First copy any remaining bytes in + * the outbuf, then format the oldest event into the outbuf and copy that too. + */ +STATIC ssize_t +xfs_healthmon_read_iter( + struct kiocb *iocb, + struct iov_iter *to) +{ + struct file *file = iocb->ki_filp; + struct inode *inode = file_inode(file); + struct xfs_healthmon *hm = file->private_data; + struct xfs_healthmon_event *event; + size_t copied = 0; + ssize_t ret = 0; + + if (file->f_flags & O_NONBLOCK) { + if (!xfs_healthmon_has_eventdata(hm) || !inode_trylock(inode)) + return -EAGAIN; + } else { + ret = wait_event_interruptible(hm->wait, + xfs_healthmon_has_eventdata(hm)); + if (ret) + return ret; + + inode_lock(inode); + } + + if (hm->bufsize == 0) { + ret = xfs_healthmon_alloc_outbuf(hm, iov_iter_count(to)); + if (ret) + goto out_unlock; + } + + trace_xfs_healthmon_read_start(hm); + + /* + * If there's anything left in the output buffer, copy that before + * formatting more events. + */ + ret = xfs_healthmon_copybuf(hm, to); + if (ret < 0) + goto out_unlock; + copied += ret; + + while (iov_iter_count(to) > 0) { + /* Format the next events into the outbuf until it's full. */ + while ((event = xfs_healthmon_format_pop(hm)) != NULL) { + ret = xfs_healthmon_format_v0(hm, event); + kfree(event); + if (ret) + goto out_unlock; + } + + /* Copy anything formatted into outbuf to userspace */ + ret = xfs_healthmon_copybuf(hm, to); + if (ret <= 0) + break; + + copied += ret; + } + +out_unlock: + trace_xfs_healthmon_read_finish(hm); + inode_unlock(inode); + return copied ?: ret; +} + +/* Poll for available events. */ +STATIC __poll_t +xfs_healthmon_poll( + struct file *file, + struct poll_table_struct *wait) +{ + struct xfs_healthmon *hm = file->private_data; + __poll_t mask = 0; + + poll_wait(file, &hm->wait, wait); + + if (xfs_healthmon_has_eventdata(hm)) + mask |= EPOLLIN; + return mask; +} + +/* Free the health monitoring information. */ +STATIC int +xfs_healthmon_release( + struct inode *inode, + struct file *file) +{ + struct xfs_healthmon *hm = file->private_data; + + trace_xfs_healthmon_release(hm); + + /* + * We might be closing the healthmon file before the filesystem + * unmounts, because userspace processes can terminate at any time and + * for any reason. Null out xfs_mount::m_healthmon so that another + * process can create another health monitor file. + */ + xfs_healthmon_detach(hm); + + /* + * Wake up any readers that might be left. There shouldn't be any + * because the only users of the waiter are read and poll. + */ + wake_up_all(&hm->wait); + + xfs_healthmon_put(hm); + return 0; +} + +/* Validate ioctl parameters. */ +static inline bool +xfs_healthmon_validate( + const struct xfs_health_monitor *hmo) +{ + if (hmo->flags & ~XFS_HEALTH_MONITOR_ALL) + return false; + if (hmo->format != XFS_HEALTH_MONITOR_FMT_V0) + return false; + if (memchr_inv(&hmo->pad, 0, sizeof(hmo->pad))) + return false; + return true; +} + +/* Emit some data about the health monitoring fd. */ +static void +xfs_healthmon_show_fdinfo( + struct seq_file *m, + struct file *file) +{ + struct xfs_healthmon *hm = file->private_data; + + mutex_lock(&hm->lock); + seq_printf(m, "state:\t%s\ndev:\t%d:%d\nformat:\tv0\nevents:\t%llu\nlost:\t%llu\n", + hm->mount_cookie == DETACHED_MOUNT_COOKIE ? + "dead" : "alive", + MAJOR(hm->dev), MINOR(hm->dev), + hm->total_events, + hm->total_lost); + mutex_unlock(&hm->lock); +} + +/* Reconfigure the health monitor. */ +STATIC long +xfs_healthmon_reconfigure( + struct file *file, + unsigned int cmd, + void __user *arg) +{ + struct xfs_health_monitor hmo; + struct xfs_healthmon *hm = file->private_data; + + if (copy_from_user(&hmo, arg, sizeof(hmo))) + return -EFAULT; + + if (!xfs_healthmon_validate(&hmo)) + return -EINVAL; + + mutex_lock(&hm->lock); + hm->verbose = !!(hmo.flags & XFS_HEALTH_MONITOR_VERBOSE); + mutex_unlock(&hm->lock); + + return 0; +} + +/* Does the fd point to the same filesystem as the one we're monitoring? */ +STATIC long +xfs_healthmon_file_on_monitored_fs( + struct file *file, + unsigned int cmd, + void __user *arg) +{ + struct xfs_health_file_on_monitored_fs hms; + struct xfs_healthmon *hm = file->private_data; + struct inode *hms_inode; + + if (copy_from_user(&hms, arg, sizeof(hms))) + return -EFAULT; + + if (hms.flags) + return -EINVAL; + + CLASS(fd, hms_fd)(hms.fd); + if (fd_empty(hms_fd)) + return -EBADF; + + hms_inode = file_inode(fd_file(hms_fd)); + mutex_lock(&hm->lock); + if (hm->mount_cookie != (uintptr_t)hms_inode->i_sb) { + mutex_unlock(&hm->lock); + return -ESTALE; + } + + mutex_unlock(&hm->lock); + return 0; +} + +/* Handle ioctls for the health monitoring thread. */ +STATIC long +xfs_healthmon_ioctl( + struct file *file, + unsigned int cmd, + unsigned long p) +{ + void __user *arg = (void __user *)p; + + switch (cmd) { + case XFS_IOC_HEALTH_MONITOR: + return xfs_healthmon_reconfigure(file, cmd, arg); + case XFS_IOC_HEALTH_FD_ON_MONITORED_FS: + return xfs_healthmon_file_on_monitored_fs(file, cmd, arg); + default: + break; + } + + return -ENOTTY; +} + +static const struct file_operations xfs_healthmon_fops = { + .owner = THIS_MODULE, + .show_fdinfo = xfs_healthmon_show_fdinfo, + .read_iter = xfs_healthmon_read_iter, + .poll = xfs_healthmon_poll, + .release = xfs_healthmon_release, + .unlocked_ioctl = xfs_healthmon_ioctl, +}; + +/* + * Create a health monitoring file. Returns an index to the fd table or a + * negative errno. + */ +long +xfs_ioc_health_monitor( + struct file *file, + struct xfs_health_monitor __user *arg) +{ + struct xfs_health_monitor hmo; + struct xfs_healthmon_event *running_event; + struct xfs_healthmon *hm; + struct xfs_inode *ip = XFS_I(file_inode(file)); + struct xfs_mount *mp = ip->i_mount; + int ret; + + /* + * The only intended user of the health monitoring system should be the + * xfs_healer daemon running on behalf of the whole filesystem in the + * initial user namespace. IOWs, we don't allow unprivileged userspace + * (they can use fsnotify) nor do we allow containers. + */ + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if (ip->i_ino != mp->m_sb.sb_rootino) + return -EPERM; + if (current_user_ns() != &init_user_ns) + return -EPERM; + + if (copy_from_user(&hmo, arg, sizeof(hmo))) + return -EFAULT; + + if (!xfs_healthmon_validate(&hmo)) + return -EINVAL; + + hm = kzalloc(sizeof(*hm), GFP_KERNEL); + if (!hm) + return -ENOMEM; + hm->dev = mp->m_super->s_dev; + refcount_set(&hm->ref, 1); + + mutex_init(&hm->lock); + init_waitqueue_head(&hm->wait); + + if (hmo.flags & XFS_HEALTH_MONITOR_VERBOSE) + hm->verbose = true; + + /* Queue up the first event that lets the client know we're running. */ + running_event = kzalloc(sizeof(struct xfs_healthmon_event), GFP_NOFS); + if (!running_event) { + ret = -ENOMEM; + goto out_hm; + } + running_event->type = XFS_HEALTHMON_RUNNING; + running_event->domain = XFS_HEALTHMON_MOUNT; + __xfs_healthmon_insert(hm, running_event); + + /* + * Preallocate the unmount event so that we can't fail to notify the + * filesystem later. This is key for triggering fast exit of the + * xfs_healer daemon. + */ + hm->unmount_event = kzalloc(sizeof(struct xfs_healthmon_event), + GFP_NOFS); + if (!hm->unmount_event) { + ret = -ENOMEM; + goto out_hm; + } + hm->unmount_event->type = XFS_HEALTHMON_UNMOUNT; + hm->unmount_event->domain = XFS_HEALTHMON_MOUNT; + + /* + * Try to attach this health monitor to the xfs_mount. The monitor is + * considered live and will receive events if this succeeds. + */ + ret = xfs_healthmon_attach(mp, hm); + if (ret) + goto out_hm; + + /* + * Create the anonymous file and install a fd for it. If it succeeds, + * the file owns hm and can go away at any time, so we must not access + * it again. This must go last because we can't undo a fd table + * installation. + */ + ret = anon_inode_getfd("xfs_healthmon", &xfs_healthmon_fops, hm, + O_CLOEXEC | O_RDONLY); + if (ret < 0) + goto out_mp; + + trace_xfs_healthmon_create(mp->m_super->s_dev, hmo.flags, hmo.format); + + return ret; + +out_mp: + xfs_healthmon_detach(hm); +out_hm: + ASSERT(refcount_read(&hm->ref) == 1); + xfs_healthmon_put(hm); + return ret; +} diff --git a/fs/xfs/xfs_healthmon.h b/fs/xfs/xfs_healthmon.h new file mode 100644 index 000000000000..0e936507037f --- /dev/null +++ b/fs/xfs/xfs_healthmon.h @@ -0,0 +1,184 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2024-2026 Oracle. All Rights Reserved. + * Author: Darrick J. Wong <djwong@kernel.org> + */ +#ifndef __XFS_HEALTHMON_H__ +#define __XFS_HEALTHMON_H__ + +struct xfs_healthmon { + /* + * Weak reference to the xfs filesystem that is being monitored. It + * will be set to zero when the filesystem detaches from the monitor. + * Do not dereference this pointer. + */ + uintptr_t mount_cookie; + + /* + * Device number of the filesystem being monitored. This is for + * consistent tracing even after unmount. + */ + dev_t dev; + + /* + * Reference count of this structure. The open healthmon fd holds one + * ref, the xfs_mount holds another ref if it points to this object, + * and running event handlers hold their own refs. + */ + refcount_t ref; + + /* lock for event list and event counters */ + struct mutex lock; + + /* list of event objects */ + struct xfs_healthmon_event *first_event; + struct xfs_healthmon_event *last_event; + + /* preallocated event for unmount */ + struct xfs_healthmon_event *unmount_event; + + /* number of events in the list */ + unsigned int events; + + /* do we want all events? */ + bool verbose:1; + + /* waiter so read/poll can sleep until the arrival of events */ + struct wait_queue_head wait; + + /* + * Buffer for formatting events for a read_iter call. Events are + * formatted into the buffer at bufhead, and buftail determines where + * to start a copy_iter to get those events to userspace. All buffer + * fields are protected by inode_lock. + */ + char *buffer; + size_t bufsize; + size_t bufhead; + size_t buftail; + + /* did we lose previous events? */ + unsigned long long lost_prev_event; + + /* total counts of events observed and lost events */ + unsigned long long total_events; + unsigned long long total_lost; +}; + +void xfs_healthmon_unmount(struct xfs_mount *mp); + +enum xfs_healthmon_type { + XFS_HEALTHMON_RUNNING, /* monitor running */ + XFS_HEALTHMON_LOST, /* message lost */ + XFS_HEALTHMON_UNMOUNT, /* filesystem is unmounting */ + + /* filesystem shutdown */ + XFS_HEALTHMON_SHUTDOWN, + + /* metadata health events */ + XFS_HEALTHMON_SICK, /* runtime corruption observed */ + XFS_HEALTHMON_CORRUPT, /* fsck reported corruption */ + XFS_HEALTHMON_HEALTHY, /* fsck reported healthy structure */ + + /* media errors */ + XFS_HEALTHMON_MEDIA_ERROR, + + /* file range events */ + XFS_HEALTHMON_BUFREAD, + XFS_HEALTHMON_BUFWRITE, + XFS_HEALTHMON_DIOREAD, + XFS_HEALTHMON_DIOWRITE, + XFS_HEALTHMON_DATALOST, +}; + +enum xfs_healthmon_domain { + XFS_HEALTHMON_MOUNT, /* affects the whole fs */ + + /* metadata health events */ + XFS_HEALTHMON_FS, /* main filesystem metadata */ + XFS_HEALTHMON_AG, /* allocation group metadata */ + XFS_HEALTHMON_INODE, /* inode metadata */ + XFS_HEALTHMON_RTGROUP, /* realtime group metadata */ + + /* media errors */ + XFS_HEALTHMON_DATADEV, + XFS_HEALTHMON_RTDEV, + XFS_HEALTHMON_LOGDEV, + + /* file range events */ + XFS_HEALTHMON_FILERANGE, +}; + +struct xfs_healthmon_event { + struct xfs_healthmon_event *next; + + enum xfs_healthmon_type type; + enum xfs_healthmon_domain domain; + + uint64_t time_ns; + + union { + /* lost events */ + struct { + uint64_t lostcount; + }; + /* fs/rt metadata */ + struct { + /* XFS_SICK_* flags */ + unsigned int fsmask; + }; + /* ag/rtgroup metadata */ + struct { + /* XFS_SICK_(AG|RG)* flags */ + unsigned int grpmask; + unsigned int group; + }; + /* inode metadata */ + struct { + /* XFS_SICK_INO_* flags */ + unsigned int imask; + uint32_t gen; + xfs_ino_t ino; + }; + /* shutdown */ + struct { + unsigned int flags; + }; + /* media errors */ + struct { + xfs_daddr_t daddr; + uint64_t bbcount; + }; + /* file range events */ + struct { + xfs_ino_t fino; + loff_t fpos; + uint64_t flen; + uint32_t fgen; + int error; + }; + }; +}; + +void xfs_healthmon_report_fs(struct xfs_mount *mp, + enum xfs_healthmon_type type, unsigned int old_mask, + unsigned int new_mask); +void xfs_healthmon_report_group(struct xfs_group *xg, + enum xfs_healthmon_type type, unsigned int old_mask, + unsigned int new_mask); +void xfs_healthmon_report_inode(struct xfs_inode *ip, + enum xfs_healthmon_type type, unsigned int old_mask, + unsigned int new_mask); + +void xfs_healthmon_report_shutdown(struct xfs_mount *mp, uint32_t flags); + +void xfs_healthmon_report_media(struct xfs_mount *mp, enum xfs_device fdev, + xfs_daddr_t daddr, uint64_t bbcount); + +void xfs_healthmon_report_file_ioerror(struct xfs_inode *ip, + const struct fserror_event *p); + +long xfs_ioc_health_monitor(struct file *file, + struct xfs_health_monitor __user *arg); + +#endif /* __XFS_HEALTHMON_H__ */ diff --git a/fs/xfs/xfs_hooks.c b/fs/xfs/xfs_hooks.c index a58d1de2d37d..a09109e692b1 100644 --- a/fs/xfs/xfs_hooks.c +++ b/fs/xfs/xfs_hooks.c @@ -3,7 +3,7 @@ * Copyright (c) 2022-2024 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/xfs_icache.c b/fs/xfs/xfs_icache.c index 23a920437fe4..dbaab4ae709f 100644 --- a/fs/xfs/xfs_icache.c +++ b/fs/xfs/xfs_icache.c @@ -3,7 +3,7 @@ * Copyright (c) 2000-2005 Silicon Graphics, Inc. * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/xfs_icreate_item.c b/fs/xfs/xfs_icreate_item.c index f83ec2bd0583..95b0eba242e9 100644 --- a/fs/xfs/xfs_icreate_item.c +++ b/fs/xfs/xfs_icreate_item.c @@ -3,7 +3,7 @@ * Copyright (c) 2008-2010, 2013 Dave Chinner * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" @@ -49,13 +49,11 @@ xfs_icreate_item_size( STATIC void xfs_icreate_item_format( struct xfs_log_item *lip, - struct xfs_log_vec *lv) + struct xlog_format_buf *lfb) { struct xfs_icreate_item *icp = ICR_ITEM(lip); - struct xfs_log_iovec *vecp = NULL; - xlog_copy_iovec(lv, &vecp, XLOG_REG_TYPE_ICREATE, - &icp->ic_format, + xlog_format_copy(lfb, XLOG_REG_TYPE_ICREATE, &icp->ic_format, sizeof(struct xfs_icreate_log)); } diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index f1f88e48fe22..50c0404f9064 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c @@ -5,7 +5,7 @@ */ #include <linux/iversion.h> -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/xfs_inode_item.c b/fs/xfs/xfs_inode_item.c index 2eb0c6011a2e..8913036b8024 100644 --- a/fs/xfs/xfs_inode_item.c +++ b/fs/xfs/xfs_inode_item.c @@ -3,7 +3,7 @@ * Copyright (c) 2000-2002,2005 Silicon Graphics, Inc. * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" @@ -336,8 +336,7 @@ STATIC void xfs_inode_item_format_data_fork( struct xfs_inode_log_item *iip, struct xfs_inode_log_format *ilf, - struct xfs_log_vec *lv, - struct xfs_log_iovec **vecp) + struct xlog_format_buf *lfb) { struct xfs_inode *ip = iip->ili_inode; size_t data_bytes; @@ -354,9 +353,9 @@ xfs_inode_item_format_data_fork( ASSERT(xfs_iext_count(&ip->i_df) > 0); - p = xlog_prepare_iovec(lv, vecp, XLOG_REG_TYPE_IEXT); + p = xlog_format_start(lfb, XLOG_REG_TYPE_IEXT); data_bytes = xfs_iextents_copy(ip, p, XFS_DATA_FORK); - xlog_finish_iovec(lv, *vecp, data_bytes); + xlog_format_commit(lfb, data_bytes); ASSERT(data_bytes <= ip->i_df.if_bytes); @@ -374,7 +373,7 @@ xfs_inode_item_format_data_fork( if ((iip->ili_fields & XFS_ILOG_DBROOT) && ip->i_df.if_broot_bytes > 0) { ASSERT(ip->i_df.if_broot != NULL); - xlog_copy_iovec(lv, vecp, XLOG_REG_TYPE_IBROOT, + xlog_format_copy(lfb, XLOG_REG_TYPE_IBROOT, ip->i_df.if_broot, ip->i_df.if_broot_bytes); ilf->ilf_dsize = ip->i_df.if_broot_bytes; @@ -392,8 +391,9 @@ xfs_inode_item_format_data_fork( ip->i_df.if_bytes > 0) { ASSERT(ip->i_df.if_data != NULL); ASSERT(ip->i_disk_size > 0); - xlog_copy_iovec(lv, vecp, XLOG_REG_TYPE_ILOCAL, - ip->i_df.if_data, ip->i_df.if_bytes); + xlog_format_copy(lfb, XLOG_REG_TYPE_ILOCAL, + ip->i_df.if_data, + ip->i_df.if_bytes); ilf->ilf_dsize = (unsigned)ip->i_df.if_bytes; ilf->ilf_size++; } else { @@ -416,8 +416,7 @@ STATIC void xfs_inode_item_format_attr_fork( struct xfs_inode_log_item *iip, struct xfs_inode_log_format *ilf, - struct xfs_log_vec *lv, - struct xfs_log_iovec **vecp) + struct xlog_format_buf *lfb) { struct xfs_inode *ip = iip->ili_inode; size_t data_bytes; @@ -435,9 +434,9 @@ xfs_inode_item_format_attr_fork( ASSERT(xfs_iext_count(&ip->i_af) == ip->i_af.if_nextents); - p = xlog_prepare_iovec(lv, vecp, XLOG_REG_TYPE_IATTR_EXT); + p = xlog_format_start(lfb, XLOG_REG_TYPE_IATTR_EXT); data_bytes = xfs_iextents_copy(ip, p, XFS_ATTR_FORK); - xlog_finish_iovec(lv, *vecp, data_bytes); + xlog_format_commit(lfb, data_bytes); ilf->ilf_asize = data_bytes; ilf->ilf_size++; @@ -453,7 +452,7 @@ xfs_inode_item_format_attr_fork( ip->i_af.if_broot_bytes > 0) { ASSERT(ip->i_af.if_broot != NULL); - xlog_copy_iovec(lv, vecp, XLOG_REG_TYPE_IATTR_BROOT, + xlog_format_copy(lfb, XLOG_REG_TYPE_IATTR_BROOT, ip->i_af.if_broot, ip->i_af.if_broot_bytes); ilf->ilf_asize = ip->i_af.if_broot_bytes; @@ -469,8 +468,9 @@ xfs_inode_item_format_attr_fork( if ((iip->ili_fields & XFS_ILOG_ADATA) && ip->i_af.if_bytes > 0) { ASSERT(ip->i_af.if_data != NULL); - xlog_copy_iovec(lv, vecp, XLOG_REG_TYPE_IATTR_LOCAL, - ip->i_af.if_data, ip->i_af.if_bytes); + xlog_format_copy(lfb, XLOG_REG_TYPE_IATTR_LOCAL, + ip->i_af.if_data, + ip->i_af.if_bytes); ilf->ilf_asize = (unsigned)ip->i_af.if_bytes; ilf->ilf_size++; } else { @@ -619,14 +619,13 @@ xfs_inode_to_log_dinode( static void xfs_inode_item_format_core( struct xfs_inode *ip, - struct xfs_log_vec *lv, - struct xfs_log_iovec **vecp) + struct xlog_format_buf *lfb) { struct xfs_log_dinode *dic; - dic = xlog_prepare_iovec(lv, vecp, XLOG_REG_TYPE_ICORE); + dic = xlog_format_start(lfb, XLOG_REG_TYPE_ICORE); xfs_inode_to_log_dinode(ip, dic, ip->i_itemp->ili_item.li_lsn); - xlog_finish_iovec(lv, *vecp, xfs_log_dinode_size(ip->i_mount)); + xlog_format_commit(lfb, xfs_log_dinode_size(ip->i_mount)); } /* @@ -644,14 +643,13 @@ xfs_inode_item_format_core( STATIC void xfs_inode_item_format( struct xfs_log_item *lip, - struct xfs_log_vec *lv) + struct xlog_format_buf *lfb) { struct xfs_inode_log_item *iip = INODE_ITEM(lip); struct xfs_inode *ip = iip->ili_inode; - struct xfs_log_iovec *vecp = NULL; struct xfs_inode_log_format *ilf; - ilf = xlog_prepare_iovec(lv, &vecp, XLOG_REG_TYPE_IFORMAT); + ilf = xlog_format_start(lfb, XLOG_REG_TYPE_IFORMAT); ilf->ilf_type = XFS_LI_INODE; ilf->ilf_ino = ip->i_ino; ilf->ilf_blkno = ip->i_imap.im_blkno; @@ -668,13 +666,12 @@ xfs_inode_item_format( ilf->ilf_asize = 0; ilf->ilf_pad = 0; memset(&ilf->ilf_u, 0, sizeof(ilf->ilf_u)); + xlog_format_commit(lfb, sizeof(*ilf)); - xlog_finish_iovec(lv, vecp, sizeof(*ilf)); - - xfs_inode_item_format_core(ip, lv, &vecp); - xfs_inode_item_format_data_fork(iip, ilf, lv, &vecp); + xfs_inode_item_format_core(ip, lfb); + xfs_inode_item_format_data_fork(iip, ilf, lfb); if (xfs_inode_has_attr_fork(ip)) { - xfs_inode_item_format_attr_fork(iip, ilf, lv, &vecp); + xfs_inode_item_format_attr_fork(iip, ilf, lfb); } else { iip->ili_fields &= ~(XFS_ILOG_ADATA | XFS_ILOG_ABROOT | XFS_ILOG_AEXT); diff --git a/fs/xfs/xfs_inode_item_recover.c b/fs/xfs/xfs_inode_item_recover.c index 9d1999d41be1..5d93228783eb 100644 --- a/fs/xfs/xfs_inode_item_recover.c +++ b/fs/xfs/xfs_inode_item_recover.c @@ -3,7 +3,7 @@ * Copyright (c) 2000-2006 Silicon Graphics, Inc. * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/xfs_ioctl.c b/fs/xfs/xfs_ioctl.c index 59eaad774371..4eeda4d4e3ab 100644 --- a/fs/xfs/xfs_ioctl.c +++ b/fs/xfs/xfs_ioctl.c @@ -3,7 +3,7 @@ * Copyright (c) 2000-2005 Silicon Graphics, Inc. * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" @@ -41,6 +41,8 @@ #include "xfs_exchrange.h" #include "xfs_handle.h" #include "xfs_rtgroup.h" +#include "xfs_healthmon.h" +#include "xfs_verify_media.h" #include <linux/mount.h> #include <linux/fileattr.h> @@ -1419,6 +1421,11 @@ xfs_file_ioctl( case XFS_IOC_COMMIT_RANGE: return xfs_ioc_commit_range(filp, arg); + case XFS_IOC_HEALTH_MONITOR: + return xfs_ioc_health_monitor(filp, arg); + case XFS_IOC_VERIFY_MEDIA: + return xfs_ioc_verify_media(filp, arg); + default: return -ENOTTY; } diff --git a/fs/xfs/xfs_ioctl32.c b/fs/xfs/xfs_ioctl32.c index b64785dc4354..c66e192448a8 100644 --- a/fs/xfs/xfs_ioctl32.c +++ b/fs/xfs/xfs_ioctl32.c @@ -5,7 +5,7 @@ */ #include <linux/mount.h> #include <linux/fsmap.h> -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/xfs_iomap.c b/fs/xfs/xfs_iomap.c index 37a1b33e9045..be86d43044df 100644 --- a/fs/xfs/xfs_iomap.c +++ b/fs/xfs/xfs_iomap.c @@ -4,7 +4,7 @@ * Copyright (c) 2016-2018 Christoph Hellwig. * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/xfs_iops.c b/fs/xfs/xfs_iops.c index ad94fbf55014..208543e57eda 100644 --- a/fs/xfs/xfs_iops.c +++ b/fs/xfs/xfs_iops.c @@ -3,7 +3,7 @@ * Copyright (c) 2000-2005 Silicon Graphics, Inc. * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" @@ -1184,26 +1184,33 @@ xfs_vn_setattr( STATIC int xfs_vn_update_time( struct inode *inode, - int flags) + enum fs_update_time type, + unsigned int flags) { struct xfs_inode *ip = XFS_I(inode); struct xfs_mount *mp = ip->i_mount; int log_flags = XFS_ILOG_TIMESTAMP; struct xfs_trans *tp; int error; - struct timespec64 now; trace_xfs_update_time(ip); if (inode->i_sb->s_flags & SB_LAZYTIME) { - if (!((flags & S_VERSION) && - inode_maybe_inc_iversion(inode, false))) { - generic_update_time(inode, flags); + int dirty; + + dirty = inode_update_time(inode, type, flags); + if (dirty <= 0) + return dirty; + if (dirty == I_DIRTY_TIME) { + __mark_inode_dirty(inode, I_DIRTY_TIME); return 0; } /* Capture the iversion update that just occurred */ log_flags |= XFS_ILOG_CORE; + } else { + if (flags & IOCB_NOWAIT) + return -EAGAIN; } error = xfs_trans_alloc(mp, &M_RES(mp)->tr_fsyncts, 0, 0, 0, &tp); @@ -1211,21 +1218,31 @@ xfs_vn_update_time( return error; xfs_ilock(ip, XFS_ILOCK_EXCL); - if (flags & (S_CTIME|S_MTIME)) - now = inode_set_ctime_current(inode); + if (type == FS_UPD_ATIME) + inode_set_atime_to_ts(inode, current_time(inode)); else - now = current_time(inode); - - if (flags & S_MTIME) - inode_set_mtime_to_ts(inode, now); - if (flags & S_ATIME) - inode_set_atime_to_ts(inode, now); - + inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode)); xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL); xfs_trans_log_inode(tp, ip, log_flags); return xfs_trans_commit(tp); } +static void +xfs_vn_sync_lazytime( + struct inode *inode) +{ + struct xfs_inode *ip = XFS_I(inode); + struct xfs_mount *mp = ip->i_mount; + struct xfs_trans *tp; + + if (xfs_trans_alloc(mp, &M_RES(mp)->tr_fsyncts, 0, 0, 0, &tp)) + return; + xfs_ilock(ip, XFS_ILOCK_EXCL); + xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL); + xfs_trans_log_inode(tp, ip, XFS_ILOG_TIMESTAMP); + xfs_trans_commit(tp); +} + STATIC int xfs_vn_fiemap( struct inode *inode, @@ -1269,6 +1286,7 @@ static const struct inode_operations xfs_inode_operations = { .listxattr = xfs_vn_listxattr, .fiemap = xfs_vn_fiemap, .update_time = xfs_vn_update_time, + .sync_lazytime = xfs_vn_sync_lazytime, .fileattr_get = xfs_fileattr_get, .fileattr_set = xfs_fileattr_set, }; @@ -1295,6 +1313,7 @@ static const struct inode_operations xfs_dir_inode_operations = { .setattr = xfs_vn_setattr, .listxattr = xfs_vn_listxattr, .update_time = xfs_vn_update_time, + .sync_lazytime = xfs_vn_sync_lazytime, .tmpfile = xfs_vn_tmpfile, .fileattr_get = xfs_fileattr_get, .fileattr_set = xfs_fileattr_set, @@ -1322,6 +1341,7 @@ static const struct inode_operations xfs_dir_ci_inode_operations = { .setattr = xfs_vn_setattr, .listxattr = xfs_vn_listxattr, .update_time = xfs_vn_update_time, + .sync_lazytime = xfs_vn_sync_lazytime, .tmpfile = xfs_vn_tmpfile, .fileattr_get = xfs_fileattr_get, .fileattr_set = xfs_fileattr_set, @@ -1333,6 +1353,7 @@ static const struct inode_operations xfs_symlink_inode_operations = { .setattr = xfs_vn_setattr, .listxattr = xfs_vn_listxattr, .update_time = xfs_vn_update_time, + .sync_lazytime = xfs_vn_sync_lazytime, .fileattr_get = xfs_fileattr_get, .fileattr_set = xfs_fileattr_set, }; diff --git a/fs/xfs/xfs_itable.c b/fs/xfs/xfs_itable.c index 2aa37a4d2706..9faff287f747 100644 --- a/fs/xfs/xfs_itable.c +++ b/fs/xfs/xfs_itable.c @@ -3,7 +3,7 @@ * Copyright (c) 2000-2002,2005 Silicon Graphics, Inc. * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/xfs_iunlink_item.c b/fs/xfs/xfs_iunlink_item.c index 1fd70a7aed63..a03a48eeb9a8 100644 --- a/fs/xfs/xfs_iunlink_item.c +++ b/fs/xfs/xfs_iunlink_item.c @@ -3,7 +3,7 @@ * Copyright (c) 2020-2022, Red Hat, Inc. * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/xfs_iwalk.c b/fs/xfs/xfs_iwalk.c index c1c31d1a8e21..ed4033006868 100644 --- a/fs/xfs/xfs_iwalk.c +++ b/fs/xfs/xfs_iwalk.c @@ -3,7 +3,7 @@ * Copyright (C) 2019 Oracle. All Rights Reserved. * Author: Darrick J. Wong <darrick.wong@oracle.com> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/xfs_log.c b/fs/xfs/xfs_log.c index d4544ccafea5..a26378ca247d 100644 --- a/fs/xfs/xfs_log.c +++ b/fs/xfs/xfs_log.c @@ -3,7 +3,7 @@ * Copyright (c) 2000-2005 Silicon Graphics, Inc. * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" @@ -22,6 +22,15 @@ #include "xfs_health.h" #include "xfs_zone_alloc.h" +struct xlog_write_data { + struct xlog_ticket *ticket; + struct xlog_in_core *iclog; + uint32_t bytes_left; + uint32_t record_cnt; + uint32_t data_cnt; + int log_offset; +}; + struct kmem_cache *xfs_log_ticket_cache; /* Local miscellaneous function prototypes */ @@ -43,10 +52,7 @@ STATIC void xlog_state_do_callback( STATIC int xlog_state_get_iclog_space( struct xlog *log, - int len, - struct xlog_in_core **iclog, - struct xlog_ticket *ticket, - int *logoffsetp); + struct xlog_write_data *data); STATIC void xlog_sync( struct xlog *log, @@ -74,62 +80,6 @@ xlog_iclogs_empty( static int xfs_log_cover(struct xfs_mount *); -/* - * We need to make sure the buffer pointer returned is naturally aligned for the - * biggest basic data type we put into it. We have already accounted for this - * padding when sizing the buffer. - * - * However, this padding does not get written into the log, and hence we have to - * track the space used by the log vectors separately to prevent log space hangs - * due to inaccurate accounting (i.e. a leak) of the used log space through the - * CIL context ticket. - * - * We also add space for the xlog_op_header that describes this region in the - * log. This prepends the data region we return to the caller to copy their data - * into, so do all the static initialisation of the ophdr now. Because the ophdr - * is not 8 byte aligned, we have to be careful to ensure that we align the - * start of the buffer such that the region we return to the call is 8 byte - * aligned and packed against the tail of the ophdr. - */ -void * -xlog_prepare_iovec( - struct xfs_log_vec *lv, - struct xfs_log_iovec **vecp, - uint type) -{ - struct xfs_log_iovec *vec = *vecp; - struct xlog_op_header *oph; - uint32_t len; - void *buf; - - if (vec) { - ASSERT(vec - lv->lv_iovecp < lv->lv_niovecs); - vec++; - } else { - vec = &lv->lv_iovecp[0]; - } - - len = lv->lv_buf_used + sizeof(struct xlog_op_header); - if (!IS_ALIGNED(len, sizeof(uint64_t))) { - lv->lv_buf_used = round_up(len, sizeof(uint64_t)) - - sizeof(struct xlog_op_header); - } - - vec->i_type = type; - vec->i_addr = lv->lv_buf + lv->lv_buf_used; - - oph = vec->i_addr; - oph->oh_clientid = XFS_TRANSACTION; - oph->oh_res2 = 0; - oph->oh_flags = 0; - - buf = vec->i_addr + sizeof(struct xlog_op_header); - ASSERT(IS_ALIGNED((unsigned long)buf, sizeof(uint64_t))); - - *vecp = vec; - return buf; -} - static inline void xlog_grant_sub_space( struct xlog_grant_head *head, @@ -848,6 +798,27 @@ xlog_wait_on_iclog( return 0; } +int +xlog_write_one_vec( + struct xlog *log, + struct xfs_cil_ctx *ctx, + struct xfs_log_iovec *reg, + struct xlog_ticket *ticket) +{ + struct xfs_log_vec lv = { + .lv_niovecs = 1, + .lv_iovecp = reg, + .lv_bytes = reg->i_len, + }; + LIST_HEAD (lv_chain); + + /* account for space used by record data */ + ticket->t_curr_res -= lv.lv_bytes; + + list_add(&lv.lv_list, &lv_chain); + return xlog_write(log, ctx, &lv_chain, ticket, lv.lv_bytes); +} + /* * Write out an unmount record using the ticket provided. We have to account for * the data space used in the unmount ticket as this write is not done from a @@ -876,21 +847,8 @@ xlog_write_unmount_record( .i_len = sizeof(unmount_rec), .i_type = XLOG_REG_TYPE_UNMOUNT, }; - struct xfs_log_vec vec = { - .lv_niovecs = 1, - .lv_iovecp = ®, - }; - LIST_HEAD(lv_chain); - list_add(&vec.lv_list, &lv_chain); - - BUILD_BUG_ON((sizeof(struct xlog_op_header) + - sizeof(struct xfs_unmount_log_format)) != - sizeof(unmount_rec)); - /* account for space used by record data */ - ticket->t_curr_res -= sizeof(unmount_rec); - - return xlog_write(log, NULL, &lv_chain, ticket, reg.i_len); + return xlog_write_one_vec(log, NULL, ®, ticket); } /* @@ -1922,25 +1880,36 @@ xlog_print_trans( } } +static inline uint32_t xlog_write_space_left(struct xlog_write_data *data) +{ + return data->iclog->ic_size - data->log_offset; +} + +static void * +xlog_write_space_advance( + struct xlog_write_data *data, + unsigned int len) +{ + void *p = data->iclog->ic_datap + data->log_offset; + + ASSERT(xlog_write_space_left(data) >= len); + ASSERT(data->log_offset % sizeof(int32_t) == 0); + ASSERT(len % sizeof(int32_t) == 0); + + data->data_cnt += len; + data->log_offset += len; + data->bytes_left -= len; + return p; +} + static inline void xlog_write_iovec( - struct xlog_in_core *iclog, - uint32_t *log_offset, - void *data, - uint32_t write_len, - int *bytes_left, - uint32_t *record_cnt, - uint32_t *data_cnt) + struct xlog_write_data *data, + void *buf, + uint32_t buf_len) { - ASSERT(*log_offset < iclog->ic_log->l_iclog_size); - ASSERT(*log_offset % sizeof(int32_t) == 0); - ASSERT(write_len % sizeof(int32_t) == 0); - - memcpy(iclog->ic_datap + *log_offset, data, write_len); - *log_offset += write_len; - *bytes_left -= write_len; - (*record_cnt)++; - *data_cnt += write_len; + memcpy(xlog_write_space_advance(data, buf_len), buf, buf_len); + data->record_cnt++; } /* @@ -1950,17 +1919,12 @@ xlog_write_iovec( static void xlog_write_full( struct xfs_log_vec *lv, - struct xlog_ticket *ticket, - struct xlog_in_core *iclog, - uint32_t *log_offset, - uint32_t *len, - uint32_t *record_cnt, - uint32_t *data_cnt) + struct xlog_write_data *data) { int index; - ASSERT(*log_offset + *len <= iclog->ic_size || - iclog->ic_state == XLOG_STATE_WANT_SYNC); + ASSERT(data->bytes_left <= xlog_write_space_left(data) || + data->iclog->ic_state == XLOG_STATE_WANT_SYNC); /* * Ordered log vectors have no regions to write so this @@ -1970,40 +1934,32 @@ xlog_write_full( struct xfs_log_iovec *reg = &lv->lv_iovecp[index]; struct xlog_op_header *ophdr = reg->i_addr; - ophdr->oh_tid = cpu_to_be32(ticket->t_tid); - xlog_write_iovec(iclog, log_offset, reg->i_addr, - reg->i_len, len, record_cnt, data_cnt); + ophdr->oh_tid = cpu_to_be32(data->ticket->t_tid); + xlog_write_iovec(data, reg->i_addr, reg->i_len); } } static int xlog_write_get_more_iclog_space( - struct xlog_ticket *ticket, - struct xlog_in_core **iclogp, - uint32_t *log_offset, - uint32_t len, - uint32_t *record_cnt, - uint32_t *data_cnt) + struct xlog_write_data *data) { - struct xlog_in_core *iclog = *iclogp; - struct xlog *log = iclog->ic_log; + struct xlog *log = data->iclog->ic_log; int error; spin_lock(&log->l_icloglock); - ASSERT(iclog->ic_state == XLOG_STATE_WANT_SYNC); - xlog_state_finish_copy(log, iclog, *record_cnt, *data_cnt); - error = xlog_state_release_iclog(log, iclog, ticket); + ASSERT(data->iclog->ic_state == XLOG_STATE_WANT_SYNC); + xlog_state_finish_copy(log, data->iclog, data->record_cnt, + data->data_cnt); + error = xlog_state_release_iclog(log, data->iclog, data->ticket); spin_unlock(&log->l_icloglock); if (error) return error; - error = xlog_state_get_iclog_space(log, len, &iclog, ticket, - log_offset); + error = xlog_state_get_iclog_space(log, data); if (error) return error; - *record_cnt = 0; - *data_cnt = 0; - *iclogp = iclog; + data->record_cnt = 0; + data->data_cnt = 0; return 0; } @@ -2016,14 +1972,8 @@ xlog_write_get_more_iclog_space( static int xlog_write_partial( struct xfs_log_vec *lv, - struct xlog_ticket *ticket, - struct xlog_in_core **iclogp, - uint32_t *log_offset, - uint32_t *len, - uint32_t *record_cnt, - uint32_t *data_cnt) + struct xlog_write_data *data) { - struct xlog_in_core *iclog = *iclogp; struct xlog_op_header *ophdr; int index = 0; uint32_t rlen; @@ -2045,25 +1995,22 @@ xlog_write_partial( * Hence if there isn't space for region data after the * opheader, then we need to start afresh with a new iclog. */ - if (iclog->ic_size - *log_offset <= + if (xlog_write_space_left(data) <= sizeof(struct xlog_op_header)) { - error = xlog_write_get_more_iclog_space(ticket, - &iclog, log_offset, *len, record_cnt, - data_cnt); + error = xlog_write_get_more_iclog_space(data); if (error) return error; } ophdr = reg->i_addr; - rlen = min_t(uint32_t, reg->i_len, iclog->ic_size - *log_offset); + rlen = min_t(uint32_t, reg->i_len, xlog_write_space_left(data)); - ophdr->oh_tid = cpu_to_be32(ticket->t_tid); + ophdr->oh_tid = cpu_to_be32(data->ticket->t_tid); ophdr->oh_len = cpu_to_be32(rlen - sizeof(struct xlog_op_header)); if (rlen != reg->i_len) ophdr->oh_flags |= XLOG_CONTINUE_TRANS; - xlog_write_iovec(iclog, log_offset, reg->i_addr, - rlen, len, record_cnt, data_cnt); + xlog_write_iovec(data, reg->i_addr, rlen); /* If we wrote the whole region, move to the next. */ if (rlen == reg->i_len) @@ -2098,22 +2045,20 @@ xlog_write_partial( * consumes hasn't been accounted to the lv we are * writing. */ - error = xlog_write_get_more_iclog_space(ticket, - &iclog, log_offset, - *len + sizeof(struct xlog_op_header), - record_cnt, data_cnt); + data->bytes_left += sizeof(struct xlog_op_header); + error = xlog_write_get_more_iclog_space(data); if (error) return error; - ophdr = iclog->ic_datap + *log_offset; - ophdr->oh_tid = cpu_to_be32(ticket->t_tid); + ophdr = xlog_write_space_advance(data, + sizeof(struct xlog_op_header)); + ophdr->oh_tid = cpu_to_be32(data->ticket->t_tid); ophdr->oh_clientid = XFS_TRANSACTION; ophdr->oh_res2 = 0; ophdr->oh_flags = XLOG_WAS_CONT_TRANS; - ticket->t_curr_res -= sizeof(struct xlog_op_header); - *log_offset += sizeof(struct xlog_op_header); - *data_cnt += sizeof(struct xlog_op_header); + data->ticket->t_curr_res -= + sizeof(struct xlog_op_header); /* * If rlen fits in the iclog, then end the region @@ -2121,26 +2066,19 @@ xlog_write_partial( */ reg_offset += rlen; rlen = reg->i_len - reg_offset; - if (rlen <= iclog->ic_size - *log_offset) + if (rlen <= xlog_write_space_left(data)) ophdr->oh_flags |= XLOG_END_TRANS; else ophdr->oh_flags |= XLOG_CONTINUE_TRANS; - rlen = min_t(uint32_t, rlen, iclog->ic_size - *log_offset); + rlen = min_t(uint32_t, rlen, + xlog_write_space_left(data)); ophdr->oh_len = cpu_to_be32(rlen); - xlog_write_iovec(iclog, log_offset, - reg->i_addr + reg_offset, - rlen, len, record_cnt, data_cnt); - + xlog_write_iovec(data, reg->i_addr + reg_offset, rlen); } while (ophdr->oh_flags & XLOG_CONTINUE_TRANS); } - /* - * No more iovecs remain in this logvec so return the next log vec to - * the caller so it can go back to fast path copying. - */ - *iclogp = iclog; return 0; } @@ -2193,12 +2131,12 @@ xlog_write( uint32_t len) { - struct xlog_in_core *iclog = NULL; struct xfs_log_vec *lv; - uint32_t record_cnt = 0; - uint32_t data_cnt = 0; - int error = 0; - int log_offset; + struct xlog_write_data data = { + .ticket = ticket, + .bytes_left = len, + }; + int error; if (ticket->t_curr_res < 0) { xfs_alert_tag(log->l_mp, XFS_PTAG_LOGRES, @@ -2207,12 +2145,11 @@ xlog_write( xlog_force_shutdown(log, SHUTDOWN_LOG_IO_ERROR); } - error = xlog_state_get_iclog_space(log, len, &iclog, ticket, - &log_offset); + error = xlog_state_get_iclog_space(log, &data); if (error) return error; - ASSERT(log_offset <= iclog->ic_size - 1); + ASSERT(xlog_write_space_left(&data) > 0); /* * If we have a context pointer, pass it the first iclog we are @@ -2220,7 +2157,7 @@ xlog_write( * ordering. */ if (ctx) - xlog_cil_set_ctx_write_state(ctx, iclog); + xlog_cil_set_ctx_write_state(ctx, data.iclog); list_for_each_entry(lv, lv_chain, lv_list) { /* @@ -2228,10 +2165,8 @@ xlog_write( * the partial copy loop which can handle this case. */ if (lv->lv_niovecs && - lv->lv_bytes > iclog->ic_size - log_offset) { - error = xlog_write_partial(lv, ticket, &iclog, - &log_offset, &len, &record_cnt, - &data_cnt); + lv->lv_bytes > xlog_write_space_left(&data)) { + error = xlog_write_partial(lv, &data); if (error) { /* * We have no iclog to release, so just return @@ -2240,11 +2175,10 @@ xlog_write( return error; } } else { - xlog_write_full(lv, ticket, iclog, &log_offset, - &len, &record_cnt, &data_cnt); + xlog_write_full(lv, &data); } } - ASSERT(len == 0); + ASSERT(data.bytes_left == 0); /* * We've already been guaranteed that the last writes will fit inside @@ -2253,8 +2187,8 @@ xlog_write( * iclog with the number of bytes written here. */ spin_lock(&log->l_icloglock); - xlog_state_finish_copy(log, iclog, record_cnt, 0); - error = xlog_state_release_iclog(log, iclog, ticket); + xlog_state_finish_copy(log, data.iclog, data.record_cnt, 0); + error = xlog_state_release_iclog(log, data.iclog, ticket); spin_unlock(&log->l_icloglock); return error; @@ -2576,10 +2510,7 @@ xlog_state_done_syncing( STATIC int xlog_state_get_iclog_space( struct xlog *log, - int len, - struct xlog_in_core **iclogp, - struct xlog_ticket *ticket, - int *logoffsetp) + struct xlog_write_data *data) { int log_offset; struct xlog_rec_header *head; @@ -2614,7 +2545,7 @@ restart: * must be written. */ if (log_offset == 0) { - ticket->t_curr_res -= log->l_iclog_hsize; + data->ticket->t_curr_res -= log->l_iclog_hsize; head->h_cycle = cpu_to_be32(log->l_curr_cycle); head->h_lsn = cpu_to_be64( xlog_assign_lsn(log->l_curr_cycle, log->l_curr_block)); @@ -2644,7 +2575,8 @@ restart: * reference to the iclog. */ if (!atomic_add_unless(&iclog->ic_refcnt, -1, 1)) - error = xlog_state_release_iclog(log, iclog, ticket); + error = xlog_state_release_iclog(log, iclog, + data->ticket); spin_unlock(&log->l_icloglock); if (error) return error; @@ -2657,16 +2589,16 @@ restart: * iclogs (to mark it taken), this particular iclog will release/sync * to disk in xlog_write(). */ - if (len <= iclog->ic_size - iclog->ic_offset) - iclog->ic_offset += len; + if (data->bytes_left <= iclog->ic_size - iclog->ic_offset) + iclog->ic_offset += data->bytes_left; else xlog_state_switch_iclogs(log, iclog, iclog->ic_size); - *iclogp = iclog; + data->iclog = iclog; ASSERT(iclog->ic_offset <= iclog->ic_size); spin_unlock(&log->l_icloglock); - *logoffsetp = log_offset; + data->log_offset = log_offset; return 0; } diff --git a/fs/xfs/xfs_log.h b/fs/xfs/xfs_log.h index dcc1f44ed68f..0f23812b0b31 100644 --- a/fs/xfs/xfs_log.h +++ b/fs/xfs/xfs_log.h @@ -6,20 +6,9 @@ #ifndef __XFS_LOG_H__ #define __XFS_LOG_H__ +struct xlog_format_buf; struct xfs_cil_ctx; -struct xfs_log_vec { - struct list_head lv_list; /* CIL lv chain ptrs */ - uint32_t lv_order_id; /* chain ordering info */ - int lv_niovecs; /* number of iovecs in lv */ - struct xfs_log_iovec *lv_iovecp; /* iovec array */ - struct xfs_log_item *lv_item; /* owner */ - char *lv_buf; /* formatted buffer */ - int lv_bytes; /* accounted space in buffer */ - int lv_buf_used; /* buffer space used so far */ - int lv_alloc_size; /* size of allocated lv */ -}; - /* Region types for iovec's i_type */ #define XLOG_REG_TYPE_BFORMAT 1 #define XLOG_REG_TYPE_BCHUNK 2 @@ -70,58 +59,24 @@ xlog_calc_iovec_len(int len) return roundup(len, sizeof(uint32_t)); } -void *xlog_prepare_iovec(struct xfs_log_vec *lv, struct xfs_log_iovec **vecp, - uint type); - -static inline void -xlog_finish_iovec(struct xfs_log_vec *lv, struct xfs_log_iovec *vec, - int data_len) -{ - struct xlog_op_header *oph = vec->i_addr; - int len; - - /* - * Always round up the length to the correct alignment so callers don't - * need to know anything about this log vec layout requirement. This - * means we have to zero the area the data to be written does not cover. - * This is complicated by fact the payload region is offset into the - * logvec region by the opheader that tracks the payload. - */ - len = xlog_calc_iovec_len(data_len); - if (len - data_len != 0) { - char *buf = vec->i_addr + sizeof(struct xlog_op_header); - - memset(buf + data_len, 0, len - data_len); - } - - /* - * The opheader tracks aligned payload length, whilst the logvec tracks - * the overall region length. - */ - oph->oh_len = cpu_to_be32(len); - - len += sizeof(struct xlog_op_header); - lv->lv_buf_used += len; - lv->lv_bytes += len; - vec->i_len = len; - - /* Catch buffer overruns */ - ASSERT((void *)lv->lv_buf + lv->lv_bytes <= - (void *)lv + lv->lv_alloc_size); -} +void *xlog_format_start(struct xlog_format_buf *lfb, uint16_t type); +void xlog_format_commit(struct xlog_format_buf *lfb, unsigned int data_len); /* * Copy the amount of data requested by the caller into a new log iovec. */ static inline void * -xlog_copy_iovec(struct xfs_log_vec *lv, struct xfs_log_iovec **vecp, - uint type, void *data, int len) +xlog_format_copy( + struct xlog_format_buf *lfb, + uint16_t type, + void *data, + unsigned int len) { void *buf; - buf = xlog_prepare_iovec(lv, vecp, type); + buf = xlog_format_start(lfb, type); memcpy(buf, data, len); - xlog_finish_iovec(lv, *vecp, len); + xlog_format_commit(lfb, len); return buf; } diff --git a/fs/xfs/xfs_log_cil.c b/fs/xfs/xfs_log_cil.c index 778ac47adb8c..566976b8fef3 100644 --- a/fs/xfs/xfs_log_cil.c +++ b/fs/xfs/xfs_log_cil.c @@ -3,7 +3,7 @@ * Copyright (c) 2010 Red Hat, Inc. All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_format.h" #include "xfs_log_format.h" @@ -409,6 +409,102 @@ xfs_cil_prepare_item( lv->lv_item->li_seq = log->l_cilp->xc_ctx->sequence; } +struct xlog_format_buf { + struct xfs_log_vec *lv; + unsigned int idx; +}; + +/* + * We need to make sure the buffer pointer returned is naturally aligned for the + * biggest basic data type we put into it. We have already accounted for this + * padding when sizing the buffer. + * + * However, this padding does not get written into the log, and hence we have to + * track the space used by the log vectors separately to prevent log space hangs + * due to inaccurate accounting (i.e. a leak) of the used log space through the + * CIL context ticket. + * + * We also add space for the xlog_op_header that describes this region in the + * log. This prepends the data region we return to the caller to copy their data + * into, so do all the static initialisation of the ophdr now. Because the ophdr + * is not 8 byte aligned, we have to be careful to ensure that we align the + * start of the buffer such that the region we return to the call is 8 byte + * aligned and packed against the tail of the ophdr. + */ +void * +xlog_format_start( + struct xlog_format_buf *lfb, + uint16_t type) +{ + struct xfs_log_vec *lv = lfb->lv; + struct xfs_log_iovec *vec = &lv->lv_iovecp[lfb->idx]; + struct xlog_op_header *oph; + uint32_t len; + void *buf; + + ASSERT(lfb->idx < lv->lv_niovecs); + + len = lv->lv_buf_used + sizeof(struct xlog_op_header); + if (!IS_ALIGNED(len, sizeof(uint64_t))) { + lv->lv_buf_used = round_up(len, sizeof(uint64_t)) - + sizeof(struct xlog_op_header); + } + + vec->i_type = type; + vec->i_addr = lv->lv_buf + lv->lv_buf_used; + + oph = vec->i_addr; + oph->oh_clientid = XFS_TRANSACTION; + oph->oh_res2 = 0; + oph->oh_flags = 0; + + buf = vec->i_addr + sizeof(struct xlog_op_header); + ASSERT(IS_ALIGNED((unsigned long)buf, sizeof(uint64_t))); + return buf; +} + +void +xlog_format_commit( + struct xlog_format_buf *lfb, + unsigned int data_len) +{ + struct xfs_log_vec *lv = lfb->lv; + struct xfs_log_iovec *vec = &lv->lv_iovecp[lfb->idx]; + struct xlog_op_header *oph = vec->i_addr; + int len; + + /* + * Always round up the length to the correct alignment so callers don't + * need to know anything about this log vec layout requirement. This + * means we have to zero the area the data to be written does not cover. + * This is complicated by fact the payload region is offset into the + * logvec region by the opheader that tracks the payload. + */ + len = xlog_calc_iovec_len(data_len); + if (len - data_len != 0) { + char *buf = vec->i_addr + sizeof(struct xlog_op_header); + + memset(buf + data_len, 0, len - data_len); + } + + /* + * The opheader tracks aligned payload length, whilst the logvec tracks + * the overall region length. + */ + oph->oh_len = cpu_to_be32(len); + + len += sizeof(struct xlog_op_header); + lv->lv_buf_used += len; + lv->lv_bytes += len; + vec->i_len = len; + + /* Catch buffer overruns */ + ASSERT((void *)lv->lv_buf + lv->lv_bytes <= + (void *)lv + lv->lv_alloc_size); + + lfb->idx++; +} + /* * Format log item into a flat buffers * @@ -454,6 +550,7 @@ xlog_cil_insert_format_items( list_for_each_entry(lip, &tp->t_items, li_trans) { struct xfs_log_vec *lv = lip->li_lv; struct xfs_log_vec *shadow = lip->li_lv_shadow; + struct xlog_format_buf lfb = { }; /* Skip items which aren't dirty in this transaction. */ if (!test_bit(XFS_LI_DIRTY, &lip->li_flags)) @@ -501,8 +598,9 @@ xlog_cil_insert_format_items( lv->lv_item = lip; } + lfb.lv = lv; ASSERT(IS_ALIGNED((unsigned long)lv->lv_buf, sizeof(uint64_t))); - lip->li_ops->iop_format(lip, lv); + lip->li_ops->iop_format(lip, &lfb); xfs_cil_prepare_item(log, lip, lv, diff_len); } } @@ -1098,13 +1196,7 @@ xlog_cil_write_commit_record( .i_len = sizeof(struct xlog_op_header), .i_type = XLOG_REG_TYPE_COMMIT, }; - struct xfs_log_vec vec = { - .lv_niovecs = 1, - .lv_iovecp = ®, - }; int error; - LIST_HEAD(lv_chain); - list_add(&vec.lv_list, &lv_chain); if (xlog_is_shutdown(log)) return -EIO; @@ -1112,10 +1204,7 @@ xlog_cil_write_commit_record( error = xlog_cil_order_write(ctx->cil, ctx->sequence, _COMMIT_RECORD); if (error) return error; - - /* account for space used by record data */ - ctx->ticket->t_curr_res -= reg.i_len; - error = xlog_write(log, ctx, &lv_chain, ctx->ticket, reg.i_len); + error = xlog_write_one_vec(log, ctx, ®, ctx->ticket); if (error) xlog_force_shutdown(log, SHUTDOWN_LOG_IO_ERROR); return error; diff --git a/fs/xfs/xfs_log_priv.h b/fs/xfs/xfs_log_priv.h index 0fe59f0525aa..cf1e4ce61a8c 100644 --- a/fs/xfs/xfs_log_priv.h +++ b/fs/xfs/xfs_log_priv.h @@ -13,6 +13,24 @@ struct xlog; struct xlog_ticket; struct xfs_mount; +struct xfs_log_iovec { + void *i_addr;/* beginning address of region */ + int i_len; /* length in bytes of region */ + uint i_type; /* type of region */ +}; + +struct xfs_log_vec { + struct list_head lv_list; /* CIL lv chain ptrs */ + uint32_t lv_order_id; /* chain ordering info */ + int lv_niovecs; /* number of iovecs in lv */ + struct xfs_log_iovec *lv_iovecp; /* iovec array */ + struct xfs_log_item *lv_item; /* owner */ + char *lv_buf; /* formatted buffer */ + int lv_bytes; /* accounted space in buffer */ + int lv_buf_used; /* buffer space used so far */ + int lv_alloc_size; /* size of allocated lv */ +}; + /* * get client id from packed copy. * @@ -507,6 +525,8 @@ void xlog_print_trans(struct xfs_trans *); int xlog_write(struct xlog *log, struct xfs_cil_ctx *ctx, struct list_head *lv_chain, struct xlog_ticket *tic, uint32_t len); +int xlog_write_one_vec(struct xlog *log, struct xfs_cil_ctx *ctx, + struct xfs_log_iovec *reg, struct xlog_ticket *ticket); void xfs_log_ticket_ungrant(struct xlog *log, struct xlog_ticket *ticket); void xfs_log_ticket_regrant(struct xlog *log, struct xlog_ticket *ticket); diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c index 03e42c7dab56..935905743f94 100644 --- a/fs/xfs/xfs_log_recover.c +++ b/fs/xfs/xfs_log_recover.c @@ -3,7 +3,7 @@ * Copyright (c) 2000-2006 Silicon Graphics, Inc. * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" @@ -2953,18 +2953,23 @@ xlog_valid_rec_header( xfs_daddr_t blkno, int bufsize) { + struct xfs_mount *mp = log->l_mp; + u32 h_version = be32_to_cpu(rhead->h_version); int hlen; - if (XFS_IS_CORRUPT(log->l_mp, + if (XFS_IS_CORRUPT(mp, rhead->h_magicno != cpu_to_be32(XLOG_HEADER_MAGIC_NUM))) return -EFSCORRUPTED; - if (XFS_IS_CORRUPT(log->l_mp, - (!rhead->h_version || - (be32_to_cpu(rhead->h_version) & - (~XLOG_VERSION_OKBITS))))) { - xfs_warn(log->l_mp, "%s: unrecognised log version (%d).", - __func__, be32_to_cpu(rhead->h_version)); - return -EFSCORRUPTED; + + /* + * The log version must match the superblock + */ + if (xfs_has_logv2(mp)) { + if (XFS_IS_CORRUPT(mp, h_version != XLOG_VERSION_2)) + return -EFSCORRUPTED; + } else { + if (XFS_IS_CORRUPT(mp, h_version != XLOG_VERSION_1)) + return -EFSCORRUPTED; } /* @@ -2972,12 +2977,12 @@ xlog_valid_rec_header( * and h_len must not be greater than LR buffer size. */ hlen = be32_to_cpu(rhead->h_len); - if (XFS_IS_CORRUPT(log->l_mp, hlen <= 0 || hlen > bufsize)) + if (XFS_IS_CORRUPT(mp, hlen <= 0 || hlen > bufsize)) return -EFSCORRUPTED; - if (XFS_IS_CORRUPT(log->l_mp, - blkno > log->l_logBBsize || blkno > INT_MAX)) + if (XFS_IS_CORRUPT(mp, blkno > log->l_logBBsize || blkno > INT_MAX)) return -EFSCORRUPTED; + return 0; } diff --git a/fs/xfs/xfs_message.c b/fs/xfs/xfs_message.c index 19aba2c3d525..fd297082aeb8 100644 --- a/fs/xfs/xfs_message.c +++ b/fs/xfs/xfs_message.c @@ -3,7 +3,7 @@ * Copyright (c) 2011 Red Hat, Inc. All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_error.h" #include "xfs_shared.h" @@ -149,14 +149,6 @@ xfs_warn_experimental( .opstate = XFS_OPSTATE_WARNED_LARP, .name = "logged extended attributes", }, - [XFS_EXPERIMENTAL_LBS] = { - .opstate = XFS_OPSTATE_WARNED_LBS, - .name = "large block size", - }, - [XFS_EXPERIMENTAL_METADIR] = { - .opstate = XFS_OPSTATE_WARNED_METADIR, - .name = "metadata directory tree", - }, [XFS_EXPERIMENTAL_ZONED] = { .opstate = XFS_OPSTATE_WARNED_ZONED, .name = "zoned RT device", diff --git a/fs/xfs/xfs_message.h b/fs/xfs/xfs_message.h index d68e72379f9d..49b0ef40d299 100644 --- a/fs/xfs/xfs_message.h +++ b/fs/xfs/xfs_message.h @@ -93,8 +93,6 @@ void xfs_buf_alert_ratelimited(struct xfs_buf *bp, const char *rlmsg, enum xfs_experimental_feat { XFS_EXPERIMENTAL_SHRINK, XFS_EXPERIMENTAL_LARP, - XFS_EXPERIMENTAL_LBS, - XFS_EXPERIMENTAL_METADIR, XFS_EXPERIMENTAL_ZONED, XFS_EXPERIMENTAL_MAX, diff --git a/fs/xfs/xfs_mount.c b/fs/xfs/xfs_mount.c index 0953f6ae94ab..9c295abd0a0a 100644 --- a/fs/xfs/xfs_mount.c +++ b/fs/xfs/xfs_mount.c @@ -3,7 +3,7 @@ * Copyright (c) 2000-2005 Silicon Graphics, Inc. * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" @@ -41,6 +41,7 @@ #include "xfs_rtrefcount_btree.h" #include "scrub/stats.h" #include "xfs_zone_alloc.h" +#include "xfs_healthmon.h" static DEFINE_MUTEX(xfs_uuid_table_mutex); static int xfs_uuid_table_size; @@ -625,6 +626,7 @@ xfs_unmount_flush_inodes( cancel_delayed_work_sync(&mp->m_reclaim_work); xfs_reclaim_inodes(mp); xfs_health_unmount(mp); + xfs_healthmon_unmount(mp); } static void diff --git a/fs/xfs/xfs_mount.h b/fs/xfs/xfs_mount.h index b871dfde372b..61c71128d171 100644 --- a/fs/xfs/xfs_mount.h +++ b/fs/xfs/xfs_mount.h @@ -13,6 +13,7 @@ struct xfs_ail; struct xfs_quotainfo; struct xfs_da_geometry; struct xfs_perag; +struct xfs_healthmon; /* dynamic preallocation free space thresholds, 5% down to 1% */ enum { @@ -342,6 +343,9 @@ typedef struct xfs_mount { /* Hook to feed dirent updates to an active online repair. */ struct xfs_hooks m_dir_update_hooks; + + /* Private data referring to a health monitor object. */ + struct xfs_healthmon *m_healthmon; } xfs_mount_t; #define M_IGEO(mp) (&(mp)->m_ino_geo) diff --git a/fs/xfs/xfs_mru_cache.c b/fs/xfs/xfs_mru_cache.c index 73b7e72944e4..4e417747688f 100644 --- a/fs/xfs/xfs_mru_cache.c +++ b/fs/xfs/xfs_mru_cache.c @@ -3,7 +3,7 @@ * Copyright (c) 2006-2007 Silicon Graphics, Inc. * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_mru_cache.h" /* diff --git a/fs/xfs/xfs_notify_failure.c b/fs/xfs/xfs_notify_failure.c index b17672889942..6be19fa1ebe2 100644 --- a/fs/xfs/xfs_notify_failure.c +++ b/fs/xfs/xfs_notify_failure.c @@ -3,7 +3,7 @@ * Copyright (c) 2022 Fujitsu. All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_shared.h" #include "xfs_format.h" #include "xfs_log_format.h" @@ -22,10 +22,12 @@ #include "xfs_notify_failure.h" #include "xfs_rtgroup.h" #include "xfs_rtrmap_btree.h" +#include "xfs_healthmon.h" #include <linux/mm.h> #include <linux/dax.h> #include <linux/fs.h> +#include <linux/fserror.h> struct xfs_failure_info { xfs_agblock_t startblock; @@ -116,6 +118,9 @@ xfs_dax_failure_fn( invalidate_inode_pages2_range(mapping, pgoff, pgoff + pgcnt - 1); + fserror_report_data_lost(VFS_I(ip), (u64)pgoff << PAGE_SHIFT, + (u64)pgcnt << PAGE_SHIFT, GFP_NOFS); + xfs_irele(ip); return error; } @@ -215,6 +220,8 @@ xfs_dax_notify_logdev_failure( if (error) return error; + xfs_healthmon_report_media(mp, XFS_DEV_LOG, daddr, bblen); + /* * In the pre-remove case the failure notification is attempting to * trigger a force unmount. The expectation is that the device is @@ -248,16 +255,20 @@ xfs_dax_notify_dev_failure( uint64_t bblen; struct xfs_group *xg = NULL; - if (!xfs_has_rmapbt(mp)) { - xfs_debug(mp, "notify_failure() needs rmapbt enabled!"); - return -EOPNOTSUPP; - } - error = xfs_dax_translate_range(xfs_group_type_buftarg(mp, type), offset, len, &daddr, &bblen); if (error) return error; + xfs_healthmon_report_media(mp, + type == XG_TYPE_RTG ? XFS_DEV_RT : XFS_DEV_DATA, + daddr, bblen); + + if (!xfs_has_rmapbt(mp)) { + xfs_debug(mp, "notify_failure() needs rmapbt enabled!"); + return -EOPNOTSUPP; + } + if (type == XG_TYPE_RTG) { start_bno = xfs_daddr_to_rtb(mp, daddr); end_bno = xfs_daddr_to_rtb(mp, daddr + bblen - 1); diff --git a/fs/xfs/xfs_linux.h b/fs/xfs/xfs_platform.h index 4dd747bdbcca..1e59bf94d1f2 100644 --- a/fs/xfs/xfs_linux.h +++ b/fs/xfs/xfs_platform.h @@ -3,24 +3,11 @@ * Copyright (c) 2000-2005 Silicon Graphics, Inc. * All Rights Reserved. */ -#ifndef __XFS_LINUX__ -#define __XFS_LINUX__ +#ifndef _XFS_PLATFORM_H +#define _XFS_PLATFORM_H #include <linux/types.h> #include <linux/uuid.h> - -/* - * Kernel specific type declarations for XFS - */ - -typedef __s64 xfs_off_t; /* <file offset> type */ -typedef unsigned long long xfs_ino_t; /* <inode> type */ -typedef __s64 xfs_daddr_t; /* <disk address> type */ -typedef __u32 xfs_dev_t; -typedef __u32 xfs_nlink_t; - -#include "xfs_types.h" - #include <linux/semaphore.h> #include <linux/mm.h> #include <linux/sched/mm.h> @@ -63,7 +50,6 @@ typedef __u32 xfs_nlink_t; #include <linux/xattr.h> #include <linux/mnt_idmapping.h> #include <linux/debugfs.h> - #include <asm/page.h> #include <asm/div64.h> #include <asm/param.h> @@ -71,6 +57,32 @@ typedef __u32 xfs_nlink_t; #include <asm/byteorder.h> #include <linux/unaligned.h> +#ifdef CONFIG_XFS_DEBUG +#define DEBUG 1 +#endif + +#ifdef CONFIG_XFS_DEBUG_EXPENSIVE +#define DEBUG_EXPENSIVE 1 +#endif + +#ifdef CONFIG_XFS_ASSERT_FATAL +#define XFS_ASSERT_FATAL 1 +#endif + +#ifdef CONFIG_XFS_WARN +#define XFS_WARN 1 +#endif + +/* + * Kernel specific type declarations for XFS + */ +typedef __s64 xfs_off_t; /* <file offset> type */ +typedef unsigned long long xfs_ino_t; /* <inode> type */ +typedef __s64 xfs_daddr_t; /* <disk address> type */ +typedef __u32 xfs_dev_t; +typedef __u32 xfs_nlink_t; + +#include "xfs_types.h" #include "xfs_fs.h" #include "xfs_stats.h" #include "xfs_sysctl.h" @@ -121,8 +133,6 @@ typedef __u32 xfs_nlink_t; #define ENOATTR ENODATA /* Attribute not found */ #define EWRONGFS EINVAL /* Mount with wrong filesystem type */ -#define EFSCORRUPTED EUCLEAN /* Filesystem is corrupted */ -#define EFSBADCRC EBADMSG /* Bad CRC detected */ #define __return_address __builtin_return_address(0) @@ -281,4 +291,4 @@ kmem_to_page(void *addr) return virt_to_page(addr); } -#endif /* __XFS_LINUX__ */ +#endif /* _XFS_PLATFORM_H */ diff --git a/fs/xfs/xfs_pnfs.c b/fs/xfs/xfs_pnfs.c index afe7497012d4..221e55887a2a 100644 --- a/fs/xfs/xfs_pnfs.c +++ b/fs/xfs/xfs_pnfs.c @@ -2,7 +2,7 @@ /* * Copyright (c) 2014 Christoph Hellwig. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_shared.h" #include "xfs_format.h" #include "xfs_log_format.h" diff --git a/fs/xfs/xfs_pwork.c b/fs/xfs/xfs_pwork.c index c283b801cc5d..7c79ab0db0e2 100644 --- a/fs/xfs/xfs_pwork.c +++ b/fs/xfs/xfs_pwork.c @@ -3,7 +3,7 @@ * Copyright (C) 2019 Oracle. All Rights Reserved. * Author: Darrick J. Wong <darrick.wong@oracle.com> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/xfs_qm.c b/fs/xfs/xfs_qm.c index 95be67ac6eb4..a3e7d4a107d4 100644 --- a/fs/xfs/xfs_qm.c +++ b/fs/xfs/xfs_qm.c @@ -3,7 +3,7 @@ * Copyright (c) 2000-2005 Silicon Graphics, Inc. * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/xfs_qm_bhv.c b/fs/xfs/xfs_qm_bhv.c index edc0aef3cf34..a094b8252ffd 100644 --- a/fs/xfs/xfs_qm_bhv.c +++ b/fs/xfs/xfs_qm_bhv.c @@ -3,7 +3,7 @@ * Copyright (c) 2000-2006 Silicon Graphics, Inc. * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/xfs_qm_syscalls.c b/fs/xfs/xfs_qm_syscalls.c index 022e2179c06b..d50b7318cb5c 100644 --- a/fs/xfs/xfs_qm_syscalls.c +++ b/fs/xfs/xfs_qm_syscalls.c @@ -5,7 +5,7 @@ */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/xfs_quotaops.c b/fs/xfs/xfs_quotaops.c index 94fbe3d99ec7..8804508cc2b8 100644 --- a/fs/xfs/xfs_quotaops.c +++ b/fs/xfs/xfs_quotaops.c @@ -3,7 +3,7 @@ * Copyright (c) 2008, Christoph Hellwig * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_shared.h" #include "xfs_format.h" #include "xfs_log_format.h" diff --git a/fs/xfs/xfs_refcount_item.c b/fs/xfs/xfs_refcount_item.c index 3728234699a2..881c3f3a6a24 100644 --- a/fs/xfs/xfs_refcount_item.c +++ b/fs/xfs/xfs_refcount_item.c @@ -3,7 +3,7 @@ * Copyright (C) 2016 Oracle. All Rights Reserved. * Author: Darrick J. Wong <darrick.wong@oracle.com> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_format.h" #include "xfs_log_format.h" @@ -93,10 +93,9 @@ unsigned int xfs_cui_log_space(unsigned int nr) STATIC void xfs_cui_item_format( struct xfs_log_item *lip, - struct xfs_log_vec *lv) + struct xlog_format_buf *lfb) { struct xfs_cui_log_item *cuip = CUI_ITEM(lip); - struct xfs_log_iovec *vecp = NULL; ASSERT(atomic_read(&cuip->cui_next_extent) == cuip->cui_format.cui_nextents); @@ -105,7 +104,7 @@ xfs_cui_item_format( cuip->cui_format.cui_type = lip->li_type; cuip->cui_format.cui_size = 1; - xlog_copy_iovec(lv, &vecp, XLOG_REG_TYPE_CUI_FORMAT, &cuip->cui_format, + xlog_format_copy(lfb, XLOG_REG_TYPE_CUI_FORMAT, &cuip->cui_format, xfs_cui_log_format_sizeof(cuip->cui_format.cui_nextents)); } @@ -199,17 +198,16 @@ unsigned int xfs_cud_log_space(void) STATIC void xfs_cud_item_format( struct xfs_log_item *lip, - struct xfs_log_vec *lv) + struct xlog_format_buf *lfb) { struct xfs_cud_log_item *cudp = CUD_ITEM(lip); - struct xfs_log_iovec *vecp = NULL; ASSERT(lip->li_type == XFS_LI_CUD || lip->li_type == XFS_LI_CUD_RT); cudp->cud_format.cud_type = lip->li_type; cudp->cud_format.cud_size = 1; - xlog_copy_iovec(lv, &vecp, XLOG_REG_TYPE_CUD_FORMAT, &cudp->cud_format, + xlog_format_copy(lfb, XLOG_REG_TYPE_CUD_FORMAT, &cudp->cud_format, sizeof(struct xfs_cud_log_format)); } diff --git a/fs/xfs/xfs_reflink.c b/fs/xfs/xfs_reflink.c index 3f177b4ec131..db23a0f231d6 100644 --- a/fs/xfs/xfs_reflink.c +++ b/fs/xfs/xfs_reflink.c @@ -3,7 +3,7 @@ * Copyright (C) 2016 Oracle. All Rights Reserved. * Author: Darrick J. Wong <darrick.wong@oracle.com> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/xfs_rmap_item.c b/fs/xfs/xfs_rmap_item.c index 15f0903f6fd4..a39fe08dcd8f 100644 --- a/fs/xfs/xfs_rmap_item.c +++ b/fs/xfs/xfs_rmap_item.c @@ -3,7 +3,7 @@ * Copyright (C) 2016 Oracle. All Rights Reserved. * Author: Darrick J. Wong <darrick.wong@oracle.com> */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_format.h" #include "xfs_log_format.h" @@ -92,10 +92,9 @@ unsigned int xfs_rui_log_space(unsigned int nr) STATIC void xfs_rui_item_format( struct xfs_log_item *lip, - struct xfs_log_vec *lv) + struct xlog_format_buf *lfb) { struct xfs_rui_log_item *ruip = RUI_ITEM(lip); - struct xfs_log_iovec *vecp = NULL; ASSERT(atomic_read(&ruip->rui_next_extent) == ruip->rui_format.rui_nextents); @@ -105,7 +104,7 @@ xfs_rui_item_format( ruip->rui_format.rui_type = lip->li_type; ruip->rui_format.rui_size = 1; - xlog_copy_iovec(lv, &vecp, XLOG_REG_TYPE_RUI_FORMAT, &ruip->rui_format, + xlog_format_copy(lfb, XLOG_REG_TYPE_RUI_FORMAT, &ruip->rui_format, xfs_rui_log_format_sizeof(ruip->rui_format.rui_nextents)); } @@ -200,17 +199,16 @@ unsigned int xfs_rud_log_space(void) STATIC void xfs_rud_item_format( struct xfs_log_item *lip, - struct xfs_log_vec *lv) + struct xlog_format_buf *lfb) { struct xfs_rud_log_item *rudp = RUD_ITEM(lip); - struct xfs_log_iovec *vecp = NULL; ASSERT(lip->li_type == XFS_LI_RUD || lip->li_type == XFS_LI_RUD_RT); rudp->rud_format.rud_type = lip->li_type; rudp->rud_format.rud_size = 1; - xlog_copy_iovec(lv, &vecp, XLOG_REG_TYPE_RUD_FORMAT, &rudp->rud_format, + xlog_format_copy(lfb, XLOG_REG_TYPE_RUD_FORMAT, &rudp->rud_format, sizeof(struct xfs_rud_log_format)); } diff --git a/fs/xfs/xfs_rtalloc.c b/fs/xfs/xfs_rtalloc.c index a12ffed12391..90a94a5b6f7e 100644 --- a/fs/xfs/xfs_rtalloc.c +++ b/fs/xfs/xfs_rtalloc.c @@ -3,7 +3,7 @@ * Copyright (c) 2000-2005 Silicon Graphics, Inc. * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/xfs_stats.c b/fs/xfs/xfs_stats.c index 35c7fb3ba324..017db0361cd8 100644 --- a/fs/xfs/xfs_stats.c +++ b/fs/xfs/xfs_stats.c @@ -3,7 +3,7 @@ * Copyright (c) 2000-2003,2005 Silicon Graphics, Inc. * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" struct xstats xfsstats; @@ -23,7 +23,8 @@ int xfs_stats_format(struct xfsstats __percpu *stats, char *buf) uint64_t xs_xstrat_bytes = 0; uint64_t xs_write_bytes = 0; uint64_t xs_read_bytes = 0; - uint64_t defer_relog = 0; + uint64_t xs_defer_relog = 0; + uint64_t xs_gc_bytes = 0; static const struct xstats_entry { char *desc; @@ -57,7 +58,8 @@ int xfs_stats_format(struct xfsstats __percpu *stats, char *buf) { "rtrmapbt_mem", xfsstats_offset(xs_rtrefcbt_2) }, { "rtrefcntbt", xfsstats_offset(xs_qm_dqreclaims)}, /* we print both series of quota information together */ - { "qm", xfsstats_offset(xs_xstrat_bytes)}, + { "qm", xfsstats_offset(xs_gc_read_calls)}, + { "zoned", xfsstats_offset(__pad1)}, }; /* Loop over all stats groups */ @@ -76,19 +78,21 @@ int xfs_stats_format(struct xfsstats __percpu *stats, char *buf) xs_xstrat_bytes += per_cpu_ptr(stats, i)->s.xs_xstrat_bytes; xs_write_bytes += per_cpu_ptr(stats, i)->s.xs_write_bytes; xs_read_bytes += per_cpu_ptr(stats, i)->s.xs_read_bytes; - defer_relog += per_cpu_ptr(stats, i)->s.defer_relog; + xs_defer_relog += per_cpu_ptr(stats, i)->s.xs_defer_relog; + xs_gc_bytes += per_cpu_ptr(stats, i)->s.xs_gc_bytes; } len += scnprintf(buf + len, PATH_MAX-len, "xpc %llu %llu %llu\n", xs_xstrat_bytes, xs_write_bytes, xs_read_bytes); len += scnprintf(buf + len, PATH_MAX-len, "defer_relog %llu\n", - defer_relog); + xs_defer_relog); len += scnprintf(buf + len, PATH_MAX-len, "debug %u\n", #if defined(DEBUG) 1); #else 0); #endif + len += scnprintf(buf + len, PATH_MAX-len, "gc xpc %llu\n", xs_gc_bytes); return len; } diff --git a/fs/xfs/xfs_stats.h b/fs/xfs/xfs_stats.h index 15ba1abcf253..153d2381d0a8 100644 --- a/fs/xfs/xfs_stats.h +++ b/fs/xfs/xfs_stats.h @@ -138,11 +138,17 @@ struct __xfsstats { uint32_t xs_qm_dqwants; uint32_t xs_qm_dquot; uint32_t xs_qm_dquot_unused; +/* Zone GC counters */ + uint32_t xs_gc_read_calls; + uint32_t xs_gc_write_calls; + uint32_t xs_gc_zone_reset_calls; + uint32_t __pad1; /* Extra precision counters */ uint64_t xs_xstrat_bytes; uint64_t xs_write_bytes; uint64_t xs_read_bytes; - uint64_t defer_relog; + uint64_t xs_defer_relog; + uint64_t xs_gc_bytes; }; #define xfsstats_offset(f) (offsetof(struct __xfsstats, f)/sizeof(uint32_t)) diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c index bc71aa9dcee8..8586f044a14b 100644 --- a/fs/xfs/xfs_super.c +++ b/fs/xfs/xfs_super.c @@ -4,7 +4,7 @@ * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_shared.h" #include "xfs_format.h" #include "xfs_log_format.h" @@ -40,6 +40,8 @@ #include "xfs_defer.h" #include "xfs_attr_item.h" #include "xfs_xattr.h" +#include "xfs_error.h" +#include "xfs_errortag.h" #include "xfs_iunlink_item.h" #include "xfs_dahash_test.h" #include "xfs_rtbitmap.h" @@ -47,12 +49,14 @@ #include "xfs_parent.h" #include "xfs_rtalloc.h" #include "xfs_zone_alloc.h" +#include "xfs_healthmon.h" #include "scrub/stats.h" #include "scrub/rcbag_btree.h" #include <linux/magic.h> #include <linux/fs_context.h> #include <linux/fs_parser.h> +#include <linux/fserror.h> static const struct super_operations xfs_super_operations; @@ -111,7 +115,7 @@ enum { Opt_prjquota, Opt_uquota, Opt_gquota, Opt_pquota, Opt_uqnoenforce, Opt_gqnoenforce, Opt_pqnoenforce, Opt_qnoenforce, Opt_discard, Opt_nodiscard, Opt_dax, Opt_dax_enum, Opt_max_open_zones, - Opt_lifetime, Opt_nolifetime, Opt_max_atomic_write, + Opt_lifetime, Opt_nolifetime, Opt_max_atomic_write, Opt_errortag, }; #define fsparam_dead(NAME) \ @@ -170,6 +174,7 @@ static const struct fs_parameter_spec xfs_fs_parameters[] = { fsparam_flag("lifetime", Opt_lifetime), fsparam_flag("nolifetime", Opt_nolifetime), fsparam_string("max_atomic_write", Opt_max_atomic_write), + fsparam_string("errortag", Opt_errortag), {} }; @@ -712,34 +717,6 @@ xfs_fs_destroy_inode( xfs_inode_mark_reclaimable(ip); } -static void -xfs_fs_dirty_inode( - struct inode *inode, - int flags) -{ - struct xfs_inode *ip = XFS_I(inode); - struct xfs_mount *mp = ip->i_mount; - struct xfs_trans *tp; - - if (!(inode->i_sb->s_flags & SB_LAZYTIME)) - return; - - /* - * Only do the timestamp update if the inode is dirty (I_DIRTY_SYNC) - * and has dirty timestamp (I_DIRTY_TIME). I_DIRTY_TIME can be passed - * in flags possibly together with I_DIRTY_SYNC. - */ - if ((flags & ~I_DIRTY_TIME) != I_DIRTY_SYNC || !(flags & I_DIRTY_TIME)) - return; - - if (xfs_trans_alloc(mp, &M_RES(mp)->tr_fsyncts, 0, 0, 0, &tp)) - return; - xfs_ilock(ip, XFS_ILOCK_EXCL); - xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL); - xfs_trans_log_inode(tp, ip, XFS_ILOG_TIMESTAMP); - xfs_trans_commit(tp); -} - /* * Slab object creation initialisation for the XFS inode. * This covers only the idempotent fields in the XFS inode; @@ -822,6 +799,9 @@ xfs_mount_free( debugfs_remove(mp->m_debugfs); kfree(mp->m_rtname); kfree(mp->m_logname); +#ifdef DEBUG + kfree(mp->m_errortag); +#endif kfree(mp); } @@ -845,15 +825,6 @@ xfs_fs_sync_fs( if (error) return error; - if (laptop_mode) { - /* - * The disk must be active because we're syncing. - * We schedule log work now (now that the disk is - * active) instead of later (when it might not be). - */ - flush_delayed_work(&mp->m_log->l_work); - } - /* * If we are called with page faults frozen out, it means we are about * to freeze the transaction subsystem. Take the opportunity to shut @@ -1301,10 +1272,18 @@ xfs_fs_show_stats( return 0; } +static void +xfs_fs_report_error( + const struct fserror_event *event) +{ + /* healthmon already knows about non-inode and metadata errors */ + if (event->inode && event->type != FSERR_METADATA) + xfs_healthmon_report_file_ioerror(XFS_I(event->inode), event); +} + static const struct super_operations xfs_super_operations = { .alloc_inode = xfs_fs_alloc_inode, .destroy_inode = xfs_fs_destroy_inode, - .dirty_inode = xfs_fs_dirty_inode, .drop_inode = xfs_fs_drop_inode, .evict_inode = xfs_fs_evict_inode, .put_super = xfs_fs_put_super, @@ -1317,6 +1296,7 @@ static const struct super_operations xfs_super_operations = { .free_cached_objects = xfs_fs_free_cached_objects, .shutdown = xfs_fs_shutdown, .show_stats = xfs_fs_show_stats, + .report_error = xfs_fs_report_error, }; static int @@ -1577,6 +1557,8 @@ xfs_fs_parse_param( return -EINVAL; } return 0; + case Opt_errortag: + return xfs_errortag_add_name(parsing_mp, param->string); default: xfs_warn(parsing_mp, "unknown mount option [%s].", param->key); return -EINVAL; @@ -1835,8 +1817,6 @@ xfs_fs_fill_super( error = -ENOSYS; goto out_free_sb; } - - xfs_warn_experimental(mp, XFS_EXPERIMENTAL_LBS); } /* Ensure this filesystem fits in the page cache limits */ @@ -1922,8 +1902,6 @@ xfs_fs_fill_super( goto out_filestream_unmount; } xfs_warn_experimental(mp, XFS_EXPERIMENTAL_ZONED); - } else if (xfs_has_metadir(mp)) { - xfs_warn_experimental(mp, XFS_EXPERIMENTAL_METADIR); } if (xfs_has_reflink(mp)) { @@ -2172,6 +2150,8 @@ xfs_fs_reconfigure( if (error) return error; + xfs_errortag_copy(mp, new_mp); + /* Validate new max_atomic_write option before making other changes */ if (mp->m_awu_max_bytes != new_mp->m_awu_max_bytes) { error = xfs_set_max_atomic_write_opt(mp, @@ -2258,6 +2238,14 @@ xfs_init_fs_context( mp = kzalloc(sizeof(struct xfs_mount), GFP_KERNEL); if (!mp) return -ENOMEM; +#ifdef DEBUG + mp->m_errortag = kcalloc(XFS_ERRTAG_MAX, sizeof(*mp->m_errortag), + GFP_KERNEL); + if (!mp->m_errortag) { + kfree(mp); + return -ENOMEM; + } +#endif spin_lock_init(&mp->m_sb_lock); for (i = 0; i < XG_TYPE_MAX; i++) diff --git a/fs/xfs/xfs_symlink.c b/fs/xfs/xfs_symlink.c index 4252b07cd251..c4da624fb296 100644 --- a/fs/xfs/xfs_symlink.c +++ b/fs/xfs/xfs_symlink.c @@ -4,7 +4,7 @@ * Copyright (c) 2012-2013 Red Hat, Inc. * All rights reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_shared.h" #include "xfs_fs.h" #include "xfs_format.h" diff --git a/fs/xfs/xfs_sysctl.c b/fs/xfs/xfs_sysctl.c index 9918f14b4874..7f32d282dc88 100644 --- a/fs/xfs/xfs_sysctl.c +++ b/fs/xfs/xfs_sysctl.c @@ -3,7 +3,7 @@ * Copyright (c) 2001-2005 Silicon Graphics, Inc. * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_error.h" static struct ctl_table_header *xfs_table_header; diff --git a/fs/xfs/xfs_sysfs.c b/fs/xfs/xfs_sysfs.c index 7a5c5ef2db92..6c7909838234 100644 --- a/fs/xfs/xfs_sysfs.c +++ b/fs/xfs/xfs_sysfs.c @@ -4,7 +4,7 @@ * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_shared.h" #include "xfs_format.h" #include "xfs_log_format.h" diff --git a/fs/xfs/xfs_trace.c b/fs/xfs/xfs_trace.c index a60556dbd172..912713a8a019 100644 --- a/fs/xfs/xfs_trace.c +++ b/fs/xfs/xfs_trace.c @@ -3,7 +3,7 @@ * Copyright (c) 2009, Christoph Hellwig * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_bit.h" @@ -51,6 +51,11 @@ #include "xfs_rtgroup.h" #include "xfs_zone_alloc.h" #include "xfs_zone_priv.h" +#include "xfs_health.h" +#include "xfs_healthmon.h" +#include "xfs_notify_failure.h" +#include "xfs_file.h" +#include <linux/fserror.h> /* * We include this last to have the helpers above available for the trace diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h index f70afbf3cb19..813e5a9f57eb 100644 --- a/fs/xfs/xfs_trace.h +++ b/fs/xfs/xfs_trace.h @@ -103,6 +103,9 @@ struct xfs_refcount_intent; struct xfs_metadir_update; struct xfs_rtgroup; struct xfs_open_zone; +struct xfs_healthmon_event; +struct xfs_healthmon; +struct fserror_event; #define XFS_ATTR_FILTER_FLAGS \ { XFS_ATTR_ROOT, "ROOT" }, \ @@ -2410,6 +2413,7 @@ DEFINE_ATTR_EVENT(xfs_attr_sf_addname); DEFINE_ATTR_EVENT(xfs_attr_sf_create); DEFINE_ATTR_EVENT(xfs_attr_sf_lookup); DEFINE_ATTR_EVENT(xfs_attr_sf_remove); +DEFINE_ATTR_EVENT(xfs_attr_sf_replace); DEFINE_ATTR_EVENT(xfs_attr_sf_to_leaf); DEFINE_ATTR_EVENT(xfs_attr_leaf_add); @@ -5906,6 +5910,515 @@ DEFINE_EVENT(xfs_freeblocks_resv_class, name, \ DEFINE_FREEBLOCKS_RESV_EVENT(xfs_freecounter_reserved); DEFINE_FREEBLOCKS_RESV_EVENT(xfs_freecounter_enospc); +TRACE_EVENT(xfs_healthmon_lost_event, + TP_PROTO(const struct xfs_healthmon *hm), + TP_ARGS(hm), + TP_STRUCT__entry( + __field(dev_t, dev) + __field(unsigned long long, lost_prev) + ), + TP_fast_assign( + __entry->dev = hm->dev; + __entry->lost_prev = hm->lost_prev_event; + ), + TP_printk("dev %d:%d lost_prev %llu", + MAJOR(__entry->dev), MINOR(__entry->dev), + __entry->lost_prev) +); + +#define XFS_HEALTHMON_FLAGS_STRINGS \ + { XFS_HEALTH_MONITOR_VERBOSE, "verbose" } +#define XFS_HEALTHMON_FMT_STRINGS \ + { XFS_HEALTH_MONITOR_FMT_V0, "v0" } + +TRACE_EVENT(xfs_healthmon_create, + TP_PROTO(dev_t dev, u64 flags, u8 format), + TP_ARGS(dev, flags, format), + TP_STRUCT__entry( + __field(dev_t, dev) + __field(u64, flags) + __field(u8, format) + ), + TP_fast_assign( + __entry->dev = dev; + __entry->flags = flags; + __entry->format = format; + ), + TP_printk("dev %d:%d flags %s format %s", + MAJOR(__entry->dev), MINOR(__entry->dev), + __print_flags(__entry->flags, "|", XFS_HEALTHMON_FLAGS_STRINGS), + __print_symbolic(__entry->format, XFS_HEALTHMON_FMT_STRINGS)) +); + +TRACE_EVENT(xfs_healthmon_copybuf, + TP_PROTO(const struct xfs_healthmon *hm, const struct iov_iter *iov), + TP_ARGS(hm, iov), + TP_STRUCT__entry( + __field(dev_t, dev) + __field(size_t, bufsize) + __field(size_t, inpos) + __field(size_t, outpos) + __field(size_t, to_copy) + __field(size_t, iter_count) + ), + TP_fast_assign( + __entry->dev = hm->dev; + __entry->bufsize = hm->bufsize; + __entry->inpos = hm->bufhead; + __entry->outpos = hm->buftail; + if (hm->bufhead > hm->buftail) + __entry->to_copy = hm->bufhead - hm->buftail; + else + __entry->to_copy = 0; + __entry->iter_count = iov_iter_count(iov); + ), + TP_printk("dev %d:%d bufsize %zu in_pos %zu out_pos %zu to_copy %zu iter_count %zu", + MAJOR(__entry->dev), MINOR(__entry->dev), + __entry->bufsize, + __entry->inpos, + __entry->outpos, + __entry->to_copy, + __entry->iter_count) +); + +DECLARE_EVENT_CLASS(xfs_healthmon_class, + TP_PROTO(const struct xfs_healthmon *hm), + TP_ARGS(hm), + TP_STRUCT__entry( + __field(dev_t, dev) + __field(unsigned int, events) + __field(unsigned long long, lost_prev) + ), + TP_fast_assign( + __entry->dev = hm->dev; + __entry->events = hm->events; + __entry->lost_prev = hm->lost_prev_event; + ), + TP_printk("dev %d:%d events %u lost_prev? %llu", + MAJOR(__entry->dev), MINOR(__entry->dev), + __entry->events, + __entry->lost_prev) +); +#define DEFINE_HEALTHMON_EVENT(name) \ +DEFINE_EVENT(xfs_healthmon_class, name, \ + TP_PROTO(const struct xfs_healthmon *hm), \ + TP_ARGS(hm)) +DEFINE_HEALTHMON_EVENT(xfs_healthmon_read_start); +DEFINE_HEALTHMON_EVENT(xfs_healthmon_read_finish); +DEFINE_HEALTHMON_EVENT(xfs_healthmon_release); +DEFINE_HEALTHMON_EVENT(xfs_healthmon_detach); +DEFINE_HEALTHMON_EVENT(xfs_healthmon_report_unmount); + +#define XFS_HEALTHMON_TYPE_STRINGS \ + { XFS_HEALTHMON_LOST, "lost" }, \ + { XFS_HEALTHMON_UNMOUNT, "unmount" }, \ + { XFS_HEALTHMON_SICK, "sick" }, \ + { XFS_HEALTHMON_CORRUPT, "corrupt" }, \ + { XFS_HEALTHMON_HEALTHY, "healthy" }, \ + { XFS_HEALTHMON_SHUTDOWN, "shutdown" } + +#define XFS_HEALTHMON_DOMAIN_STRINGS \ + { XFS_HEALTHMON_MOUNT, "mount" }, \ + { XFS_HEALTHMON_FS, "fs" }, \ + { XFS_HEALTHMON_AG, "ag" }, \ + { XFS_HEALTHMON_INODE, "inode" }, \ + { XFS_HEALTHMON_RTGROUP, "rtgroup" } + +TRACE_DEFINE_ENUM(XFS_HEALTHMON_LOST); +TRACE_DEFINE_ENUM(XFS_HEALTHMON_SHUTDOWN); +TRACE_DEFINE_ENUM(XFS_HEALTHMON_UNMOUNT); +TRACE_DEFINE_ENUM(XFS_HEALTHMON_SICK); +TRACE_DEFINE_ENUM(XFS_HEALTHMON_CORRUPT); +TRACE_DEFINE_ENUM(XFS_HEALTHMON_HEALTHY); + +TRACE_DEFINE_ENUM(XFS_HEALTHMON_MOUNT); +TRACE_DEFINE_ENUM(XFS_HEALTHMON_FS); +TRACE_DEFINE_ENUM(XFS_HEALTHMON_AG); +TRACE_DEFINE_ENUM(XFS_HEALTHMON_INODE); +TRACE_DEFINE_ENUM(XFS_HEALTHMON_RTGROUP); + +DECLARE_EVENT_CLASS(xfs_healthmon_event_class, + TP_PROTO(const struct xfs_healthmon *hm, + const struct xfs_healthmon_event *event), + TP_ARGS(hm, event), + TP_STRUCT__entry( + __field(dev_t, dev) + __field(unsigned int, type) + __field(unsigned int, domain) + __field(unsigned int, mask) + __field(unsigned long long, ino) + __field(unsigned int, gen) + __field(unsigned int, group) + __field(unsigned long long, offset) + __field(unsigned long long, length) + __field(unsigned long long, lostcount) + ), + TP_fast_assign( + __entry->dev = hm->dev; + __entry->type = event->type; + __entry->domain = event->domain; + __entry->mask = 0; + __entry->group = 0; + __entry->ino = 0; + __entry->gen = 0; + __entry->offset = 0; + __entry->length = 0; + __entry->lostcount = 0; + switch (__entry->domain) { + case XFS_HEALTHMON_MOUNT: + switch (__entry->type) { + case XFS_HEALTHMON_SHUTDOWN: + __entry->mask = event->flags; + break; + case XFS_HEALTHMON_LOST: + __entry->lostcount = event->lostcount; + break; + } + break; + case XFS_HEALTHMON_FS: + __entry->mask = event->fsmask; + break; + case XFS_HEALTHMON_AG: + case XFS_HEALTHMON_RTGROUP: + __entry->mask = event->grpmask; + __entry->group = event->group; + break; + case XFS_HEALTHMON_INODE: + __entry->mask = event->imask; + __entry->ino = event->ino; + __entry->gen = event->gen; + break; + case XFS_HEALTHMON_DATADEV: + case XFS_HEALTHMON_LOGDEV: + case XFS_HEALTHMON_RTDEV: + __entry->offset = event->daddr; + __entry->length = event->bbcount; + break; + case XFS_HEALTHMON_FILERANGE: + __entry->ino = event->fino; + __entry->gen = event->fgen; + __entry->offset = event->fpos; + __entry->length = event->flen; + break; + } + ), + TP_printk("dev %d:%d type %s domain %s mask 0x%x ino 0x%llx gen 0x%x offset 0x%llx len 0x%llx group 0x%x lost %llu", + MAJOR(__entry->dev), MINOR(__entry->dev), + __print_symbolic(__entry->type, XFS_HEALTHMON_TYPE_STRINGS), + __print_symbolic(__entry->domain, XFS_HEALTHMON_DOMAIN_STRINGS), + __entry->mask, + __entry->ino, + __entry->gen, + __entry->offset, + __entry->length, + __entry->group, + __entry->lostcount) +); +#define DEFINE_HEALTHMONEVENT_EVENT(name) \ +DEFINE_EVENT(xfs_healthmon_event_class, name, \ + TP_PROTO(const struct xfs_healthmon *hm, \ + const struct xfs_healthmon_event *event), \ + TP_ARGS(hm, event)) +DEFINE_HEALTHMONEVENT_EVENT(xfs_healthmon_insert); +DEFINE_HEALTHMONEVENT_EVENT(xfs_healthmon_push); +DEFINE_HEALTHMONEVENT_EVENT(xfs_healthmon_pop); +DEFINE_HEALTHMONEVENT_EVENT(xfs_healthmon_format); +DEFINE_HEALTHMONEVENT_EVENT(xfs_healthmon_format_overflow); +DEFINE_HEALTHMONEVENT_EVENT(xfs_healthmon_drop); +DEFINE_HEALTHMONEVENT_EVENT(xfs_healthmon_merge); + +TRACE_EVENT(xfs_healthmon_report_fs, + TP_PROTO(const struct xfs_healthmon *hm, + unsigned int old_mask, unsigned int new_mask, + const struct xfs_healthmon_event *event), + TP_ARGS(hm, old_mask, new_mask, event), + TP_STRUCT__entry( + __field(dev_t, dev) + __field(unsigned int, type) + __field(unsigned int, domain) + __field(unsigned int, old_mask) + __field(unsigned int, new_mask) + __field(unsigned int, fsmask) + ), + TP_fast_assign( + __entry->dev = hm->dev; + __entry->type = event->type; + __entry->domain = event->domain; + __entry->old_mask = old_mask; + __entry->new_mask = new_mask; + __entry->fsmask = event->fsmask; + ), + TP_printk("dev %d:%d type %s domain %s oldmask 0x%x newmask 0x%x fsmask 0x%x", + MAJOR(__entry->dev), MINOR(__entry->dev), + __print_symbolic(__entry->type, XFS_HEALTHMON_TYPE_STRINGS), + __print_symbolic(__entry->domain, XFS_HEALTHMON_DOMAIN_STRINGS), + __entry->old_mask, + __entry->new_mask, + __entry->fsmask) +); + +TRACE_EVENT(xfs_healthmon_report_group, + TP_PROTO(const struct xfs_healthmon *hm, + unsigned int old_mask, unsigned int new_mask, + const struct xfs_healthmon_event *event), + TP_ARGS(hm, old_mask, new_mask, event), + TP_STRUCT__entry( + __field(dev_t, dev) + __field(unsigned int, type) + __field(unsigned int, domain) + __field(unsigned int, old_mask) + __field(unsigned int, new_mask) + __field(unsigned int, grpmask) + __field(unsigned int, group) + ), + TP_fast_assign( + __entry->dev = hm->dev; + __entry->type = event->type; + __entry->domain = event->domain; + __entry->old_mask = old_mask; + __entry->new_mask = new_mask; + __entry->grpmask = event->grpmask; + __entry->group = event->group; + ), + TP_printk("dev %d:%d type %s domain %s oldmask 0x%x newmask 0x%x grpmask 0x%x group 0x%x", + MAJOR(__entry->dev), MINOR(__entry->dev), + __print_symbolic(__entry->type, XFS_HEALTHMON_TYPE_STRINGS), + __print_symbolic(__entry->domain, XFS_HEALTHMON_DOMAIN_STRINGS), + __entry->old_mask, + __entry->new_mask, + __entry->grpmask, + __entry->group) +); + +TRACE_EVENT(xfs_healthmon_report_inode, + TP_PROTO(const struct xfs_healthmon *hm, + unsigned int old_mask, unsigned int new_mask, + const struct xfs_healthmon_event *event), + TP_ARGS(hm, old_mask, new_mask, event), + TP_STRUCT__entry( + __field(dev_t, dev) + __field(unsigned int, type) + __field(unsigned int, domain) + __field(unsigned int, old_mask) + __field(unsigned int, new_mask) + __field(unsigned int, imask) + __field(unsigned long long, ino) + __field(unsigned int, gen) + ), + TP_fast_assign( + __entry->dev = hm->dev; + __entry->type = event->type; + __entry->domain = event->domain; + __entry->old_mask = old_mask; + __entry->new_mask = new_mask; + __entry->imask = event->imask; + __entry->ino = event->ino; + __entry->gen = event->gen; + ), + TP_printk("dev %d:%d type %s domain %s oldmask 0x%x newmask 0x%x imask 0x%x ino 0x%llx gen 0x%x", + MAJOR(__entry->dev), MINOR(__entry->dev), + __print_symbolic(__entry->type, XFS_HEALTHMON_TYPE_STRINGS), + __print_symbolic(__entry->domain, XFS_HEALTHMON_DOMAIN_STRINGS), + __entry->old_mask, + __entry->new_mask, + __entry->imask, + __entry->ino, + __entry->gen) +); + +TRACE_EVENT(xfs_healthmon_report_shutdown, + TP_PROTO(const struct xfs_healthmon *hm, uint32_t shutdown_flags), + TP_ARGS(hm, shutdown_flags), + TP_STRUCT__entry( + __field(dev_t, dev) + __field(uint32_t, shutdown_flags) + ), + TP_fast_assign( + __entry->dev = hm->dev; + __entry->shutdown_flags = shutdown_flags; + ), + TP_printk("dev %d:%d shutdown_flags %s", + MAJOR(__entry->dev), MINOR(__entry->dev), + __print_flags(__entry->shutdown_flags, "|", XFS_SHUTDOWN_STRINGS)) +); + +#define XFS_DEVICE_STRINGS \ + { XFS_DEV_DATA, "datadev" }, \ + { XFS_DEV_RT, "rtdev" }, \ + { XFS_DEV_LOG, "logdev" } + +TRACE_DEFINE_ENUM(XFS_DEV_DATA); +TRACE_DEFINE_ENUM(XFS_DEV_RT); +TRACE_DEFINE_ENUM(XFS_DEV_LOG); + +TRACE_EVENT(xfs_healthmon_report_media, + TP_PROTO(const struct xfs_healthmon *hm, enum xfs_device fdev, + const struct xfs_healthmon_event *event), + TP_ARGS(hm, fdev, event), + TP_STRUCT__entry( + __field(dev_t, dev) + __field(unsigned int, error_dev) + __field(uint64_t, daddr) + __field(uint64_t, bbcount) + ), + TP_fast_assign( + __entry->dev = hm->dev; + __entry->error_dev = fdev; + __entry->daddr = event->daddr; + __entry->bbcount = event->bbcount; + ), + TP_printk("dev %d:%d %s daddr 0x%llx bbcount 0x%llx", + MAJOR(__entry->dev), MINOR(__entry->dev), + __print_symbolic(__entry->error_dev, XFS_DEVICE_STRINGS), + __entry->daddr, + __entry->bbcount) +); + +#define FS_ERROR_STRINGS \ + { FSERR_BUFFERED_READ, "buffered_read" }, \ + { FSERR_BUFFERED_WRITE, "buffered_write" }, \ + { FSERR_DIRECTIO_READ, "directio_read" }, \ + { FSERR_DIRECTIO_WRITE, "directio_write" }, \ + { FSERR_DATA_LOST, "data_lost" }, \ + { FSERR_METADATA, "metadata" } + +TRACE_DEFINE_ENUM(FSERR_BUFFERED_READ); +TRACE_DEFINE_ENUM(FSERR_BUFFERED_WRITE); +TRACE_DEFINE_ENUM(FSERR_DIRECTIO_READ); +TRACE_DEFINE_ENUM(FSERR_DIRECTIO_WRITE); +TRACE_DEFINE_ENUM(FSERR_DATA_LOST); +TRACE_DEFINE_ENUM(FSERR_METADATA); + +TRACE_EVENT(xfs_healthmon_report_file_ioerror, + TP_PROTO(const struct xfs_healthmon *hm, + const struct fserror_event *p), + TP_ARGS(hm, p), + TP_STRUCT__entry( + __field(dev_t, dev) + __field(unsigned int, type) + __field(unsigned long long, ino) + __field(unsigned int, gen) + __field(long long, pos) + __field(unsigned long long, len) + __field(int, error) + ), + TP_fast_assign( + __entry->dev = hm->dev; + __entry->type = p->type; + __entry->ino = XFS_I(p->inode)->i_ino; + __entry->gen = p->inode->i_generation; + __entry->pos = p->pos; + __entry->len = p->len; + __entry->error = p->error; + ), + TP_printk("dev %d:%d ino 0x%llx gen 0x%x op %s pos 0x%llx bytecount 0x%llx error %d", + MAJOR(__entry->dev), MINOR(__entry->dev), + __entry->ino, + __entry->gen, + __print_symbolic(__entry->type, FS_ERROR_STRINGS), + __entry->pos, + __entry->len, + __entry->error) +); + +TRACE_EVENT(xfs_verify_media, + TP_PROTO(const struct xfs_mount *mp, const struct xfs_verify_media *me, + dev_t fdev, xfs_daddr_t daddr, uint64_t bbcount, + const struct folio *folio), + TP_ARGS(mp, me, fdev, daddr, bbcount, folio), + TP_STRUCT__entry( + __field(dev_t, dev) + __field(dev_t, fdev) + __field(xfs_daddr_t, start_daddr) + __field(xfs_daddr_t, end_daddr) + __field(unsigned int, flags) + __field(xfs_daddr_t, daddr) + __field(uint64_t, bbcount) + __field(unsigned int, bufsize) + ), + TP_fast_assign( + __entry->dev = mp->m_ddev_targp->bt_dev; + __entry->fdev = fdev; + __entry->start_daddr = me->me_start_daddr; + __entry->end_daddr = me->me_end_daddr; + __entry->flags = me->me_flags; + __entry->daddr = daddr; + __entry->bbcount = bbcount; + __entry->bufsize = folio_size(folio); + ), + TP_printk("dev %d:%d fdev %d:%d start_daddr 0x%llx end_daddr 0x%llx flags 0x%x daddr 0x%llx bbcount 0x%llx bufsize 0x%x", + MAJOR(__entry->dev), MINOR(__entry->dev), + MAJOR(__entry->fdev), MINOR(__entry->fdev), + __entry->start_daddr, + __entry->end_daddr, + __entry->flags, + __entry->daddr, + __entry->bbcount, + __entry->bufsize) +); + +TRACE_EVENT(xfs_verify_media_end, + TP_PROTO(const struct xfs_mount *mp, const struct xfs_verify_media *me, + dev_t fdev), + TP_ARGS(mp, me, fdev), + TP_STRUCT__entry( + __field(dev_t, dev) + __field(dev_t, fdev) + __field(xfs_daddr_t, start_daddr) + __field(xfs_daddr_t, end_daddr) + __field(int, ioerror) + ), + TP_fast_assign( + __entry->dev = mp->m_ddev_targp->bt_dev; + __entry->fdev = fdev; + __entry->start_daddr = me->me_start_daddr; + __entry->end_daddr = me->me_end_daddr; + __entry->ioerror = me->me_ioerror; + ), + TP_printk("dev %d:%d fdev %d:%d start_daddr 0x%llx end_daddr 0x%llx ioerror %d", + MAJOR(__entry->dev), MINOR(__entry->dev), + MAJOR(__entry->fdev), MINOR(__entry->fdev), + __entry->start_daddr, + __entry->end_daddr, + __entry->ioerror) +); + +TRACE_EVENT(xfs_verify_media_error, + TP_PROTO(const struct xfs_mount *mp, const struct xfs_verify_media *me, + dev_t fdev, xfs_daddr_t daddr, uint64_t bbcount, + blk_status_t status), + TP_ARGS(mp, me, fdev, daddr, bbcount, status), + TP_STRUCT__entry( + __field(dev_t, dev) + __field(dev_t, fdev) + __field(xfs_daddr_t, start_daddr) + __field(xfs_daddr_t, end_daddr) + __field(unsigned int, flags) + __field(xfs_daddr_t, daddr) + __field(uint64_t, bbcount) + __field(int, error) + ), + TP_fast_assign( + __entry->dev = mp->m_ddev_targp->bt_dev; + __entry->fdev = fdev; + __entry->start_daddr = me->me_start_daddr; + __entry->end_daddr = me->me_end_daddr; + __entry->flags = me->me_flags; + __entry->daddr = daddr; + __entry->bbcount = bbcount; + __entry->error = blk_status_to_errno(status); + ), + TP_printk("dev %d:%d fdev %d:%d start_daddr 0x%llx end_daddr 0x%llx flags 0x%x daddr 0x%llx bbcount 0x%llx error %d", + MAJOR(__entry->dev), MINOR(__entry->dev), + MAJOR(__entry->fdev), MINOR(__entry->fdev), + __entry->start_daddr, + __entry->end_daddr, + __entry->flags, + __entry->daddr, + __entry->bbcount, + __entry->error) +); + #endif /* _TRACE_XFS_H */ #undef TRACE_INCLUDE_PATH diff --git a/fs/xfs/xfs_trans.c b/fs/xfs/xfs_trans.c index 474f5a04ec63..bcc470f56e46 100644 --- a/fs/xfs/xfs_trans.c +++ b/fs/xfs/xfs_trans.c @@ -4,7 +4,7 @@ * Copyright (C) 2010 Red Hat, Inc. * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" @@ -124,8 +124,6 @@ xfs_trans_dup( ntp->t_rtx_res = tp->t_rtx_res - tp->t_rtx_res_used; tp->t_rtx_res = tp->t_rtx_res_used; - xfs_trans_switch_context(tp, ntp); - /* move deferred ops over to the new tp */ xfs_defer_move(ntp, tp); @@ -1043,6 +1041,12 @@ xfs_trans_roll( * locked be logged in the prior and the next transactions. */ tp = *tpp; + /* + * __xfs_trans_commit cleared the NOFS flag by calling into + * xfs_trans_free. Set it again here before doing memory + * allocations. + */ + xfs_trans_set_context(tp); error = xfs_log_regrant(tp->t_mountp, tp->t_ticket); if (error) return error; diff --git a/fs/xfs/xfs_trans.h b/fs/xfs/xfs_trans.h index 7fb860f645a3..eb83c5dac032 100644 --- a/fs/xfs/xfs_trans.h +++ b/fs/xfs/xfs_trans.h @@ -9,6 +9,7 @@ /* kernel only transaction subsystem defines */ struct xlog; +struct xlog_format_buf; struct xfs_buf; struct xfs_buftarg; struct xfs_efd_log_item; @@ -70,7 +71,8 @@ struct xfs_log_item { struct xfs_item_ops { unsigned flags; void (*iop_size)(struct xfs_log_item *, int *, int *); - void (*iop_format)(struct xfs_log_item *, struct xfs_log_vec *); + void (*iop_format)(struct xfs_log_item *lip, + struct xlog_format_buf *lfb); void (*iop_pin)(struct xfs_log_item *); void (*iop_unpin)(struct xfs_log_item *, int remove); uint64_t (*iop_sort)(struct xfs_log_item *lip); @@ -278,13 +280,4 @@ xfs_trans_clear_context( memalloc_nofs_restore(tp->t_pflags); } -static inline void -xfs_trans_switch_context( - struct xfs_trans *old_tp, - struct xfs_trans *new_tp) -{ - new_tp->t_pflags = old_tp->t_pflags; - old_tp->t_pflags = 0; -} - #endif /* __XFS_TRANS_H__ */ diff --git a/fs/xfs/xfs_trans_ail.c b/fs/xfs/xfs_trans_ail.c index 38983c6777df..363d7f88c2c6 100644 --- a/fs/xfs/xfs_trans_ail.c +++ b/fs/xfs/xfs_trans_ail.c @@ -4,7 +4,7 @@ * Copyright (c) 2008 Dave Chinner * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/xfs_trans_buf.c b/fs/xfs/xfs_trans_buf.c index 53af546c0b23..95db73a37e57 100644 --- a/fs/xfs/xfs_trans_buf.c +++ b/fs/xfs/xfs_trans_buf.c @@ -3,7 +3,7 @@ * Copyright (c) 2000-2002,2005 Silicon Graphics, Inc. * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/xfs_trans_dquot.c b/fs/xfs/xfs_trans_dquot.c index c842ce06acd6..eaf9de6e07fd 100644 --- a/fs/xfs/xfs_trans_dquot.c +++ b/fs/xfs/xfs_trans_dquot.c @@ -3,7 +3,7 @@ * Copyright (c) 2000-2002 Silicon Graphics, Inc. * All Rights Reserved. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" diff --git a/fs/xfs/xfs_verify_media.c b/fs/xfs/xfs_verify_media.c new file mode 100644 index 000000000000..069cd371619d --- /dev/null +++ b/fs/xfs/xfs_verify_media.c @@ -0,0 +1,445 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2026 Oracle. All Rights Reserved. + * Author: Darrick J. Wong <djwong@kernel.org> + */ +#include "xfs_platform.h" +#include "xfs_shared.h" +#include "xfs_format.h" +#include "xfs_log_format.h" +#include "xfs_trans_resv.h" +#include "xfs_mount.h" +#include "xfs_bit.h" +#include "xfs_btree.h" +#include "xfs_inode.h" +#include "xfs_icache.h" +#include "xfs_trans.h" +#include "xfs_alloc.h" +#include "xfs_ag.h" +#include "xfs_rmap.h" +#include "xfs_rmap_btree.h" +#include "xfs_rtgroup.h" +#include "xfs_rtrmap_btree.h" +#include "xfs_health.h" +#include "xfs_healthmon.h" +#include "xfs_trace.h" +#include "xfs_verify_media.h" + +#include <linux/fserror.h> + +struct xfs_group_data_lost { + xfs_agblock_t startblock; + xfs_extlen_t blockcount; +}; + +/* Report lost file data from rmap records */ +static int +xfs_verify_report_data_lost( + struct xfs_btree_cur *cur, + const struct xfs_rmap_irec *rec, + void *data) +{ + struct xfs_mount *mp = cur->bc_mp; + struct xfs_inode *ip; + struct xfs_group_data_lost *lost = data; + xfs_fileoff_t fileoff = rec->rm_offset; + xfs_extlen_t blocks = rec->rm_blockcount; + const bool is_attr = + (rec->rm_flags & XFS_RMAP_ATTR_FORK); + const xfs_agblock_t lost_end = + lost->startblock + lost->blockcount; + const xfs_agblock_t rmap_end = + rec->rm_startblock + rec->rm_blockcount; + int error = 0; + + if (XFS_RMAP_NON_INODE_OWNER(rec->rm_owner)) + return 0; + + error = xfs_iget(mp, cur->bc_tp, rec->rm_owner, 0, 0, &ip); + if (error) + return 0; + + if (rec->rm_flags & XFS_RMAP_BMBT_BLOCK) { + xfs_bmap_mark_sick(ip, is_attr ? XFS_ATTR_FORK : XFS_DATA_FORK); + goto out_rele; + } + + if (is_attr) { + xfs_inode_mark_sick(ip, XFS_SICK_INO_XATTR); + goto out_rele; + } + + if (lost->startblock > rec->rm_startblock) { + fileoff += lost->startblock - rec->rm_startblock; + blocks -= lost->startblock - rec->rm_startblock; + } + if (rmap_end > lost_end) + blocks -= rmap_end - lost_end; + + fserror_report_data_lost(VFS_I(ip), XFS_FSB_TO_B(mp, fileoff), + XFS_FSB_TO_B(mp, blocks), GFP_NOFS); + +out_rele: + xfs_irele(ip); + return 0; +} + +/* Walk reverse mappings to look for all file data loss */ +static int +xfs_verify_report_losses( + struct xfs_mount *mp, + enum xfs_group_type type, + xfs_daddr_t daddr, + u64 bblen) +{ + struct xfs_group *xg = NULL; + struct xfs_trans *tp; + xfs_fsblock_t start_bno, end_bno; + uint32_t start_gno, end_gno; + int error; + + if (type == XG_TYPE_RTG) { + start_bno = xfs_daddr_to_rtb(mp, daddr); + end_bno = xfs_daddr_to_rtb(mp, daddr + bblen - 1); + } else { + start_bno = XFS_DADDR_TO_FSB(mp, daddr); + end_bno = XFS_DADDR_TO_FSB(mp, daddr + bblen - 1); + } + + tp = xfs_trans_alloc_empty(mp); + start_gno = xfs_fsb_to_gno(mp, start_bno, type); + end_gno = xfs_fsb_to_gno(mp, end_bno, type); + while ((xg = xfs_group_next_range(mp, xg, start_gno, end_gno, type))) { + struct xfs_buf *agf_bp = NULL; + struct xfs_rtgroup *rtg = NULL; + struct xfs_btree_cur *cur; + struct xfs_rmap_irec ri_low = { }; + struct xfs_rmap_irec ri_high; + struct xfs_group_data_lost lost; + + if (type == XG_TYPE_AG) { + struct xfs_perag *pag = to_perag(xg); + + error = xfs_alloc_read_agf(pag, tp, 0, &agf_bp); + if (error) { + xfs_perag_put(pag); + break; + } + + cur = xfs_rmapbt_init_cursor(mp, tp, agf_bp, pag); + } else { + rtg = to_rtg(xg); + xfs_rtgroup_lock(rtg, XFS_RTGLOCK_RMAP); + cur = xfs_rtrmapbt_init_cursor(tp, rtg); + } + + /* + * Set the rmap range from ri_low to ri_high, which represents + * a [start, end] where we looking for the files or metadata. + */ + memset(&ri_high, 0xFF, sizeof(ri_high)); + if (xg->xg_gno == start_gno) + ri_low.rm_startblock = + xfs_fsb_to_gbno(mp, start_bno, type); + if (xg->xg_gno == end_gno) + ri_high.rm_startblock = + xfs_fsb_to_gbno(mp, end_bno, type); + + lost.startblock = ri_low.rm_startblock; + lost.blockcount = min(xg->xg_block_count, + ri_high.rm_startblock + 1) - + ri_low.rm_startblock; + + error = xfs_rmap_query_range(cur, &ri_low, &ri_high, + xfs_verify_report_data_lost, &lost); + xfs_btree_del_cursor(cur, error); + if (agf_bp) + xfs_trans_brelse(tp, agf_bp); + if (rtg) + xfs_rtgroup_unlock(rtg, XFS_RTGLOCK_RMAP); + if (error) { + xfs_group_put(xg); + break; + } + } + + xfs_trans_cancel(tp); + return 0; +} + +/* + * Compute the desired verify IO size. + * + * To minimize command overhead, we'd like to create bios that are 1MB, though + * we allow the user to ask for a smaller size. + */ +static unsigned int +xfs_verify_iosize( + const struct xfs_verify_media *me, + struct xfs_buftarg *btp, + uint64_t bbcount) +{ + unsigned int iosize = + min_not_zero(SZ_1M, me->me_max_io_size); + + BUILD_BUG_ON(BBSHIFT != SECTOR_SHIFT); + ASSERT(BBTOB(bbcount) >= bdev_logical_block_size(btp->bt_bdev)); + + return clamp(iosize, bdev_logical_block_size(btp->bt_bdev), + BBTOB(bbcount)); +} + +/* Allocate as much memory as we can get for verification buffer. */ +static struct folio * +xfs_verify_alloc_folio( + const unsigned int iosize) +{ + unsigned int order = get_order(iosize); + + while (order > 0) { + struct folio *folio = + folio_alloc(GFP_KERNEL | __GFP_NORETRY, order); + + if (folio) + return folio; + order--; + } + + return folio_alloc(GFP_KERNEL, 0); +} + +/* Report any kind of problem verifying media */ +static void +xfs_verify_media_error( + struct xfs_mount *mp, + struct xfs_verify_media *me, + struct xfs_buftarg *btp, + xfs_daddr_t daddr, + unsigned int bio_bbcount, + blk_status_t bio_status) +{ + trace_xfs_verify_media_error(mp, me, btp->bt_bdev->bd_dev, daddr, + bio_bbcount, bio_status); + + /* + * Pass any error, I/O or otherwise, up to the caller if we didn't + * successfully verify any bytes at all. + */ + if (me->me_start_daddr == daddr) + me->me_ioerror = -blk_status_to_errno(bio_status); + + /* + * PI validation failures, medium errors, or general IO errors are + * treated as indicators of data loss. Everything else are (hopefully) + * transient errors and are not reported to healthmon or fsnotify. + */ + switch (bio_status) { + case BLK_STS_PROTECTION: + case BLK_STS_IOERR: + case BLK_STS_MEDIUM: + break; + default: + return; + } + + if (!(me->me_flags & XFS_VERIFY_MEDIA_REPORT)) + return; + + xfs_healthmon_report_media(mp, me->me_dev, daddr, bio_bbcount); + + if (!xfs_has_rmapbt(mp)) + return; + + switch (me->me_dev) { + case XFS_DEV_DATA: + xfs_verify_report_losses(mp, XG_TYPE_AG, daddr, bio_bbcount); + break; + case XFS_DEV_RT: + xfs_verify_report_losses(mp, XG_TYPE_RTG, daddr, bio_bbcount); + break; + } +} + +/* Verify the media of an xfs device by submitting read requests to the disk. */ +static int +xfs_verify_media( + struct xfs_mount *mp, + struct xfs_verify_media *me) +{ + struct xfs_buftarg *btp = NULL; + struct bio *bio; + struct folio *folio; + xfs_daddr_t daddr; + uint64_t bbcount; + int error = 0; + + me->me_ioerror = 0; + + switch (me->me_dev) { + case XFS_DEV_DATA: + btp = mp->m_ddev_targp; + break; + case XFS_DEV_LOG: + if (mp->m_logdev_targp->bt_bdev != mp->m_ddev_targp->bt_bdev) + btp = mp->m_logdev_targp; + break; + case XFS_DEV_RT: + btp = mp->m_rtdev_targp; + break; + } + if (!btp) + return -ENODEV; + + /* + * If the caller told us to verify beyond the end of the disk, tell the + * user exactly where that was. + */ + if (me->me_end_daddr > btp->bt_nr_sectors) + me->me_end_daddr = btp->bt_nr_sectors; + + /* start and end have to be aligned to the lba size */ + if (!IS_ALIGNED(BBTOB(me->me_start_daddr | me->me_end_daddr), + bdev_logical_block_size(btp->bt_bdev))) + return -EINVAL; + + /* + * end_daddr is the exclusive end of the range, so if start_daddr + * reaches there (or beyond), there's no work to be done. + */ + if (me->me_start_daddr >= me->me_end_daddr) + return 0; + + /* + * There are three ranges involved here: + * + * - [me->me_start_daddr, me->me_end_daddr) is the range that the + * user wants to verify. end_daddr can be beyond the end of the + * disk; we'll constrain it to the end if necessary. + * + * - [daddr, me->me_end_daddr) is the range that we have not yet + * verified. We update daddr after each successful read. + * me->me_start_daddr is set to daddr before returning. + * + * - [daddr, daddr + bio_bbcount) is the range that we're currently + * verifying. + */ + daddr = me->me_start_daddr; + bbcount = min_t(sector_t, me->me_end_daddr, btp->bt_nr_sectors) - + me->me_start_daddr; + + folio = xfs_verify_alloc_folio(xfs_verify_iosize(me, btp, bbcount)); + if (!folio) + return -ENOMEM; + + trace_xfs_verify_media(mp, me, btp->bt_bdev->bd_dev, daddr, bbcount, + folio); + + bio = bio_alloc(btp->bt_bdev, 1, REQ_OP_READ, GFP_KERNEL); + if (!bio) { + error = -ENOMEM; + goto out_folio; + } + + while (bbcount > 0) { + unsigned int bio_bbcount; + blk_status_t bio_status; + + bio_reset(bio, btp->bt_bdev, REQ_OP_READ); + bio->bi_iter.bi_sector = daddr; + bio_add_folio_nofail(bio, folio, + min(bbcount << SECTOR_SHIFT, folio_size(folio)), + 0); + + /* + * Save the length of the bio before we submit it, because we + * need the original daddr and length for reporting IO errors + * if the bio fails. + */ + bio_bbcount = bio->bi_iter.bi_size >> SECTOR_SHIFT; + submit_bio_wait(bio); + bio_status = bio->bi_status; + if (bio_status != BLK_STS_OK) { + xfs_verify_media_error(mp, me, btp, daddr, bio_bbcount, + bio_status); + error = 0; + break; + } + + daddr += bio_bbcount; + bbcount -= bio_bbcount; + + if (bbcount == 0) + break; + + if (me->me_rest_us) { + ktime_t expires; + + expires = ktime_add_ns(ktime_get(), + me->me_rest_us * 1000); + set_current_state(TASK_KILLABLE); + schedule_hrtimeout(&expires, HRTIMER_MODE_ABS); + } + + if (fatal_signal_pending(current)) { + error = -EINTR; + break; + } + + cond_resched(); + } + + bio_put(bio); +out_folio: + folio_put(folio); + + if (error) + return error; + + /* + * Advance start_daddr to the end of what we verified if there wasn't + * an operational error. + */ + me->me_start_daddr = daddr; + trace_xfs_verify_media_end(mp, me, btp->bt_bdev->bd_dev); + return 0; +} + +int +xfs_ioc_verify_media( + struct file *file, + struct xfs_verify_media __user *arg) +{ + struct xfs_verify_media me; + struct xfs_inode *ip = XFS_I(file_inode(file)); + struct xfs_mount *mp = ip->i_mount; + int error; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + if (copy_from_user(&me, arg, sizeof(me))) + return -EFAULT; + + if (me.me_pad) + return -EINVAL; + if (me.me_flags & ~XFS_VERIFY_MEDIA_FLAGS) + return -EINVAL; + + switch (me.me_dev) { + case XFS_DEV_DATA: + case XFS_DEV_LOG: + case XFS_DEV_RT: + break; + default: + return -EINVAL; + } + + error = xfs_verify_media(mp, &me); + if (error) + return error; + + if (copy_to_user(arg, &me, sizeof(me))) + return -EFAULT; + + return 0; +} diff --git a/fs/xfs/xfs_verify_media.h b/fs/xfs/xfs_verify_media.h new file mode 100644 index 000000000000..dc6eee9c8863 --- /dev/null +++ b/fs/xfs/xfs_verify_media.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2026 Oracle. All Rights Reserved. + * Author: Darrick J. Wong <djwong@kernel.org> + */ +#ifndef __XFS_VERIFY_MEDIA_H__ +#define __XFS_VERIFY_MEDIA_H__ + +struct xfs_verify_media; +int xfs_ioc_verify_media(struct file *file, + struct xfs_verify_media __user *arg); + +#endif /* __XFS_VERIFY_MEDIA_H__ */ diff --git a/fs/xfs/xfs_xattr.c b/fs/xfs/xfs_xattr.c index ac5cecec9aa1..a735f16d9cd8 100644 --- a/fs/xfs/xfs_xattr.c +++ b/fs/xfs/xfs_xattr.c @@ -4,7 +4,7 @@ * Portions Copyright (C) 2000-2008 Silicon Graphics, Inc. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_shared.h" #include "xfs_format.h" #include "xfs_log_format.h" diff --git a/fs/xfs/xfs_zone_alloc.c b/fs/xfs/xfs_zone_alloc.c index bbcf21704ea0..b60952565737 100644 --- a/fs/xfs/xfs_zone_alloc.c +++ b/fs/xfs/xfs_zone_alloc.c @@ -3,7 +3,7 @@ * Copyright (c) 2023-2025 Christoph Hellwig. * Copyright (c) 2024-2025, Western Digital Corporation or its affiliates. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_shared.h" #include "xfs_format.h" #include "xfs_log_format.h" @@ -408,31 +408,6 @@ xfs_zone_free_blocks( return 0; } -static struct xfs_group * -xfs_find_free_zone( - struct xfs_mount *mp, - unsigned long start, - unsigned long end) -{ - struct xfs_zone_info *zi = mp->m_zone_info; - XA_STATE (xas, &mp->m_groups[XG_TYPE_RTG].xa, start); - struct xfs_group *xg; - - xas_lock(&xas); - xas_for_each_marked(&xas, xg, end, XFS_RTG_FREE) - if (atomic_inc_not_zero(&xg->xg_active_ref)) - goto found; - xas_unlock(&xas); - return NULL; - -found: - xas_clear_mark(&xas, XFS_RTG_FREE); - atomic_dec(&zi->zi_nr_free_zones); - zi->zi_free_zone_cursor = xg->xg_gno; - xas_unlock(&xas); - return xg; -} - static struct xfs_open_zone * xfs_init_open_zone( struct xfs_rtgroup *rtg, @@ -472,13 +447,25 @@ xfs_open_zone( bool is_gc) { struct xfs_zone_info *zi = mp->m_zone_info; + XA_STATE (xas, &mp->m_groups[XG_TYPE_RTG].xa, 0); struct xfs_group *xg; - xg = xfs_find_free_zone(mp, zi->zi_free_zone_cursor, ULONG_MAX); - if (!xg) - xg = xfs_find_free_zone(mp, 0, zi->zi_free_zone_cursor); - if (!xg) - return NULL; + /* + * Pick the free zone with lowest index. Zones in the beginning of the + * address space typically provides higher bandwidth than those at the + * end of the address space on HDDs. + */ + xas_lock(&xas); + xas_for_each_marked(&xas, xg, ULONG_MAX, XFS_RTG_FREE) + if (atomic_inc_not_zero(&xg->xg_active_ref)) + goto found; + xas_unlock(&xas); + return NULL; + +found: + xas_clear_mark(&xas, XFS_RTG_FREE); + atomic_dec(&zi->zi_nr_free_zones); + xas_unlock(&xas); set_current_state(TASK_RUNNING); return xfs_init_open_zone(to_rtg(xg), 0, write_hint, is_gc); @@ -976,46 +963,106 @@ xfs_free_open_zones( } struct xfs_init_zones { - struct xfs_mount *mp; + uint32_t zone_size; + uint32_t zone_capacity; uint64_t available; uint64_t reclaimable; }; +/* + * For sequential write required zones, we restart writing at the hardware write + * pointer returned by xfs_validate_blk_zone(). + * + * For conventional zones or conventional devices we have to query the rmap to + * find the highest recorded block and set the write pointer to the block after + * that. In case of a power loss this misses blocks where the data I/O has + * completed but not recorded in the rmap yet, and it also rewrites blocks if + * the most recently written ones got deleted again before unmount, but this is + * the best we can do without hardware support. + */ +static int +xfs_query_write_pointer( + struct xfs_init_zones *iz, + struct xfs_rtgroup *rtg, + xfs_rgblock_t *write_pointer) +{ + struct xfs_mount *mp = rtg_mount(rtg); + struct block_device *bdev = mp->m_rtdev_targp->bt_bdev; + sector_t start = xfs_gbno_to_daddr(&rtg->rtg_group, 0); + xfs_rgblock_t highest_rgbno; + struct blk_zone zone = {}; + int error; + + if (bdev_is_zoned(bdev)) { + error = blkdev_get_zone_info(bdev, start, &zone); + if (error) + return error; + if (zone.start != start) { + xfs_warn(mp, "mismatched zone start: 0x%llx/0x%llx.", + zone.start, start); + return -EFSCORRUPTED; + } + + if (!xfs_validate_blk_zone(mp, &zone, rtg_rgno(rtg), + iz->zone_size, iz->zone_capacity, + write_pointer)) + return -EFSCORRUPTED; + + /* + * Use the hardware write pointer returned by + * xfs_validate_blk_zone for sequential write required zones, + * else fall through to the rmap-based estimation below. + */ + if (zone.cond != BLK_ZONE_COND_NOT_WP) + return 0; + } + + xfs_rtgroup_lock(rtg, XFS_RTGLOCK_RMAP); + highest_rgbno = xfs_rtrmap_highest_rgbno(rtg); + xfs_rtgroup_unlock(rtg, XFS_RTGLOCK_RMAP); + + if (highest_rgbno == NULLRGBLOCK) + *write_pointer = 0; + else + *write_pointer = highest_rgbno + 1; + return 0; +} + static int xfs_init_zone( struct xfs_init_zones *iz, struct xfs_rtgroup *rtg, - struct blk_zone *zone) + xfs_rgblock_t write_pointer) { struct xfs_mount *mp = rtg_mount(rtg); struct xfs_zone_info *zi = mp->m_zone_info; uint32_t used = rtg_rmap(rtg)->i_used_blocks; - xfs_rgblock_t write_pointer, highest_rgbno; int error; - if (zone && !xfs_zone_validate(zone, rtg, &write_pointer)) + if (write_pointer > rtg->rtg_extents) { + xfs_warn(mp, "zone %u has invalid write pointer (0x%x).", + rtg_rgno(rtg), write_pointer); return -EFSCORRUPTED; + } - /* - * For sequential write required zones we retrieved the hardware write - * pointer above. - * - * For conventional zones or conventional devices we don't have that - * luxury. Instead query the rmap to find the highest recorded block - * and set the write pointer to the block after that. In case of a - * power loss this misses blocks where the data I/O has completed but - * not recorded in the rmap yet, and it also rewrites blocks if the most - * recently written ones got deleted again before unmount, but this is - * the best we can do without hardware support. - */ - if (!zone || zone->cond == BLK_ZONE_COND_NOT_WP) { - xfs_rtgroup_lock(rtg, XFS_RTGLOCK_RMAP); - highest_rgbno = xfs_rtrmap_highest_rgbno(rtg); - if (highest_rgbno == NULLRGBLOCK) - write_pointer = 0; - else - write_pointer = highest_rgbno + 1; - xfs_rtgroup_unlock(rtg, XFS_RTGLOCK_RMAP); + if (used > rtg->rtg_extents) { + xfs_warn(mp, +"zone %u has used counter (0x%x) larger than zone capacity (0x%llx).", + rtg_rgno(rtg), used, rtg->rtg_extents); + return -EFSCORRUPTED; + } + + if (used > write_pointer) { + xfs_warn(mp, +"zone %u has used counter (0x%x) larger than write pointer (0x%x).", + rtg_rgno(rtg), used, write_pointer); + return -EFSCORRUPTED; + } + + if (write_pointer == 0 && used != 0) { + xfs_warn(mp, "empty zone %u has non-zero used counter (0x%x).", + rtg_rgno(rtg), used); + return -EFSCORRUPTED; } /* @@ -1056,35 +1103,6 @@ xfs_init_zone( return 0; } -static int -xfs_get_zone_info_cb( - struct blk_zone *zone, - unsigned int idx, - void *data) -{ - struct xfs_init_zones *iz = data; - struct xfs_mount *mp = iz->mp; - xfs_fsblock_t zsbno = xfs_daddr_to_rtb(mp, zone->start); - xfs_rgnumber_t rgno; - struct xfs_rtgroup *rtg; - int error; - - if (xfs_rtb_to_rgbno(mp, zsbno) != 0) { - xfs_warn(mp, "mismatched zone start 0x%llx.", zsbno); - return -EFSCORRUPTED; - } - - rgno = xfs_rtb_to_rgno(mp, zsbno); - rtg = xfs_rtgroup_grab(mp, rgno); - if (!rtg) { - xfs_warn(mp, "realtime group not found for zone %u.", rgno); - return -EFSCORRUPTED; - } - error = xfs_init_zone(iz, rtg, zone); - xfs_rtgroup_rele(rtg); - return error; -} - /* * Calculate the max open zone limit based on the of number of backing zones * available. @@ -1219,13 +1237,13 @@ xfs_mount_zones( struct xfs_mount *mp) { struct xfs_init_zones iz = { - .mp = mp, + .zone_capacity = mp->m_groups[XG_TYPE_RTG].blocks, + .zone_size = xfs_rtgroup_raw_size(mp), }; - struct xfs_buftarg *bt = mp->m_rtdev_targp; - xfs_extlen_t zone_blocks = mp->m_groups[XG_TYPE_RTG].blocks; + struct xfs_rtgroup *rtg = NULL; int error; - if (!bt) { + if (!mp->m_rtdev_targp) { xfs_notice(mp, "RT device missing."); return -EINVAL; } @@ -1253,7 +1271,7 @@ xfs_mount_zones( return -ENOMEM; xfs_info(mp, "%u zones of %u blocks (%u max open zones)", - mp->m_sb.sb_rgcount, zone_blocks, mp->m_max_open_zones); + mp->m_sb.sb_rgcount, iz.zone_capacity, mp->m_max_open_zones); trace_xfs_zones_mount(mp); /* @@ -1277,24 +1295,18 @@ xfs_mount_zones( * or beneficial. */ mp->m_super->s_min_writeback_pages = - XFS_FSB_TO_B(mp, min(zone_blocks, XFS_MAX_BMBT_EXTLEN)) >> + XFS_FSB_TO_B(mp, min(iz.zone_capacity, XFS_MAX_BMBT_EXTLEN)) >> PAGE_SHIFT; - if (bdev_is_zoned(bt->bt_bdev)) { - error = blkdev_report_zones_cached(bt->bt_bdev, - XFS_FSB_TO_BB(mp, mp->m_sb.sb_rtstart), - mp->m_sb.sb_rgcount, xfs_get_zone_info_cb, &iz); - if (error < 0) + while ((rtg = xfs_rtgroup_next(mp, rtg))) { + xfs_rgblock_t write_pointer; + + error = xfs_query_write_pointer(&iz, rtg, &write_pointer); + if (!error) + error = xfs_init_zone(&iz, rtg, write_pointer); + if (error) { + xfs_rtgroup_rele(rtg); goto out_free_zone_info; - } else { - struct xfs_rtgroup *rtg = NULL; - - while ((rtg = xfs_rtgroup_next(mp, rtg))) { - error = xfs_init_zone(&iz, rtg, NULL); - if (error) { - xfs_rtgroup_rele(rtg); - goto out_free_zone_info; - } } } diff --git a/fs/xfs/xfs_zone_gc.c b/fs/xfs/xfs_zone_gc.c index 3c52cc1497d4..1f1f9fc973af 100644 --- a/fs/xfs/xfs_zone_gc.c +++ b/fs/xfs/xfs_zone_gc.c @@ -3,7 +3,7 @@ * Copyright (c) 2023-2025 Christoph Hellwig. * Copyright (c) 2024-2025, Western Digital Corporation or its affiliates. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_shared.h" #include "xfs_format.h" #include "xfs_log_format.h" @@ -16,6 +16,8 @@ #include "xfs_rmap.h" #include "xfs_rtbitmap.h" #include "xfs_rtrmap_btree.h" +#include "xfs_errortag.h" +#include "xfs_error.h" #include "xfs_zone_alloc.h" #include "xfs_zone_priv.h" #include "xfs_zones.h" @@ -50,23 +52,11 @@ */ /* - * Size of each GC scratch pad. This is also the upper bound for each - * GC I/O, which helps to keep latency down. + * Size of each GC scratch allocation, and the number of buffers. */ -#define XFS_GC_CHUNK_SIZE SZ_1M - -/* - * Scratchpad data to read GCed data into. - * - * The offset member tracks where the next allocation starts, and freed tracks - * the amount of space that is not used anymore. - */ -#define XFS_ZONE_GC_NR_SCRATCH 2 -struct xfs_zone_scratch { - struct folio *folio; - unsigned int offset; - unsigned int freed; -}; +#define XFS_GC_BUF_SIZE SZ_1M +#define XFS_GC_NR_BUFS 2 +static_assert(XFS_GC_NR_BUFS < BIO_MAX_VECS); /* * Chunk that is read and written for each GC operation. @@ -141,10 +131,17 @@ struct xfs_zone_gc_data { struct bio_set bio_set; /* - * Scratchpad used, and index to indicated which one is used. + * Scratchpad to buffer GC data, organized as a ring buffer over + * discontiguous folios. scratch_head is where the buffer is filled, + * scratch_tail tracks the buffer space freed, and scratch_available + * counts the space available in the ring buffer between the head and + * the tail. */ - struct xfs_zone_scratch scratch[XFS_ZONE_GC_NR_SCRATCH]; - unsigned int scratch_idx; + struct folio *scratch_folios[XFS_GC_NR_BUFS]; + unsigned int scratch_size; + unsigned int scratch_available; + unsigned int scratch_head; + unsigned int scratch_tail; /* * List of bios currently being read, written and reset. @@ -210,20 +207,17 @@ xfs_zone_gc_data_alloc( if (!data->iter.recs) goto out_free_data; - /* - * We actually only need a single bio_vec. It would be nice to have - * a flag that only allocates the inline bvecs and not the separate - * bvec pool. - */ if (bioset_init(&data->bio_set, 16, offsetof(struct xfs_gc_bio, bio), BIOSET_NEED_BVECS)) goto out_free_recs; - for (i = 0; i < XFS_ZONE_GC_NR_SCRATCH; i++) { - data->scratch[i].folio = - folio_alloc(GFP_KERNEL, get_order(XFS_GC_CHUNK_SIZE)); - if (!data->scratch[i].folio) + for (i = 0; i < XFS_GC_NR_BUFS; i++) { + data->scratch_folios[i] = + folio_alloc(GFP_KERNEL, get_order(XFS_GC_BUF_SIZE)); + if (!data->scratch_folios[i]) goto out_free_scratch; } + data->scratch_size = XFS_GC_BUF_SIZE * XFS_GC_NR_BUFS; + data->scratch_available = data->scratch_size; INIT_LIST_HEAD(&data->reading); INIT_LIST_HEAD(&data->writing); INIT_LIST_HEAD(&data->resetting); @@ -232,7 +226,7 @@ xfs_zone_gc_data_alloc( out_free_scratch: while (--i >= 0) - folio_put(data->scratch[i].folio); + folio_put(data->scratch_folios[i]); bioset_exit(&data->bio_set); out_free_recs: kfree(data->iter.recs); @@ -247,8 +241,8 @@ xfs_zone_gc_data_free( { int i; - for (i = 0; i < XFS_ZONE_GC_NR_SCRATCH; i++) - folio_put(data->scratch[i].folio); + for (i = 0; i < XFS_GC_NR_BUFS; i++) + folio_put(data->scratch_folios[i]); bioset_exit(&data->bio_set); kfree(data->iter.recs); kfree(data); @@ -586,26 +580,6 @@ xfs_zone_gc_ensure_target( return oz; } -static unsigned int -xfs_zone_gc_scratch_available( - struct xfs_zone_gc_data *data) -{ - return XFS_GC_CHUNK_SIZE - data->scratch[data->scratch_idx].offset; -} - -static bool -xfs_zone_gc_space_available( - struct xfs_zone_gc_data *data) -{ - struct xfs_open_zone *oz; - - oz = xfs_zone_gc_ensure_target(data->mp); - if (!oz) - return false; - return oz->oz_allocated < rtg_blocks(oz->oz_rtg) && - xfs_zone_gc_scratch_available(data); -} - static void xfs_zone_gc_end_io( struct bio *bio) @@ -632,8 +606,7 @@ xfs_zone_gc_alloc_blocks( if (!oz) return NULL; - *count_fsb = min(*count_fsb, - XFS_B_TO_FSB(mp, xfs_zone_gc_scratch_available(data))); + *count_fsb = min(*count_fsb, XFS_B_TO_FSB(mp, data->scratch_available)); /* * Directly allocate GC blocks from the reserved pool. @@ -664,6 +637,28 @@ xfs_zone_gc_alloc_blocks( return oz; } +static void +xfs_zone_gc_add_data( + struct xfs_gc_bio *chunk) +{ + struct xfs_zone_gc_data *data = chunk->data; + unsigned int len = chunk->len; + unsigned int off = data->scratch_head; + + do { + unsigned int this_off = off % XFS_GC_BUF_SIZE; + unsigned int this_len = min(len, XFS_GC_BUF_SIZE - this_off); + + bio_add_folio_nofail(&chunk->bio, + data->scratch_folios[off / XFS_GC_BUF_SIZE], + this_len, this_off); + len -= this_len; + off += this_len; + if (off == data->scratch_size) + off = 0; + } while (len); +} + static bool xfs_zone_gc_start_chunk( struct xfs_zone_gc_data *data) @@ -677,6 +672,7 @@ xfs_zone_gc_start_chunk( struct xfs_inode *ip; struct bio *bio; xfs_daddr_t daddr; + unsigned int len; bool is_seq; if (xfs_is_shutdown(mp)) @@ -691,17 +687,19 @@ xfs_zone_gc_start_chunk( return false; } - bio = bio_alloc_bioset(bdev, 1, REQ_OP_READ, GFP_NOFS, &data->bio_set); + len = XFS_FSB_TO_B(mp, irec.rm_blockcount); + bio = bio_alloc_bioset(bdev, + min(howmany(len, XFS_GC_BUF_SIZE) + 1, XFS_GC_NR_BUFS), + REQ_OP_READ, GFP_NOFS, &data->bio_set); chunk = container_of(bio, struct xfs_gc_bio, bio); chunk->ip = ip; chunk->offset = XFS_FSB_TO_B(mp, irec.rm_offset); - chunk->len = XFS_FSB_TO_B(mp, irec.rm_blockcount); + chunk->len = len; chunk->old_startblock = xfs_rgbno_to_rtb(iter->victim_rtg, irec.rm_startblock); chunk->new_daddr = daddr; chunk->is_seq = is_seq; - chunk->scratch = &data->scratch[data->scratch_idx]; chunk->data = data; chunk->oz = oz; chunk->victim_rtg = iter->victim_rtg; @@ -710,13 +708,12 @@ xfs_zone_gc_start_chunk( bio->bi_iter.bi_sector = xfs_rtb_to_daddr(mp, chunk->old_startblock); bio->bi_end_io = xfs_zone_gc_end_io; - bio_add_folio_nofail(bio, chunk->scratch->folio, chunk->len, - chunk->scratch->offset); - chunk->scratch->offset += chunk->len; - if (chunk->scratch->offset == XFS_GC_CHUNK_SIZE) { - data->scratch_idx = - (data->scratch_idx + 1) % XFS_ZONE_GC_NR_SCRATCH; - } + xfs_zone_gc_add_data(chunk); + data->scratch_head = (data->scratch_head + len) % data->scratch_size; + data->scratch_available -= len; + + XFS_STATS_INC(mp, xs_gc_read_calls); + WRITE_ONCE(chunk->state, XFS_GC_BIO_NEW); list_add_tail(&chunk->entry, &data->reading); xfs_zone_gc_iter_advance(iter, irec.rm_blockcount); @@ -811,8 +808,6 @@ xfs_zone_gc_write_chunk( { struct xfs_zone_gc_data *data = chunk->data; struct xfs_mount *mp = chunk->ip->i_mount; - phys_addr_t bvec_paddr = - bvec_phys(bio_first_bvec_all(&chunk->bio)); struct xfs_gc_bio *split_chunk; if (chunk->bio.bi_status) @@ -822,13 +817,13 @@ xfs_zone_gc_write_chunk( return; } + XFS_STATS_INC(mp, xs_gc_write_calls); + XFS_STATS_ADD(mp, xs_gc_bytes, chunk->len); + WRITE_ONCE(chunk->state, XFS_GC_BIO_NEW); list_move_tail(&chunk->entry, &data->writing); - bio_reset(&chunk->bio, mp->m_rtdev_targp->bt_bdev, REQ_OP_WRITE); - bio_add_folio_nofail(&chunk->bio, chunk->scratch->folio, chunk->len, - offset_in_folio(chunk->scratch->folio, bvec_paddr)); - + bio_reuse(&chunk->bio, REQ_OP_WRITE); while ((split_chunk = xfs_zone_gc_split_write(data, chunk))) xfs_zone_gc_submit_write(data, split_chunk); xfs_zone_gc_submit_write(data, chunk); @@ -839,6 +834,7 @@ xfs_zone_gc_finish_chunk( struct xfs_gc_bio *chunk) { uint iolock = XFS_IOLOCK_EXCL | XFS_MMAPLOCK_EXCL; + struct xfs_zone_gc_data *data = chunk->data; struct xfs_inode *ip = chunk->ip; struct xfs_mount *mp = ip->i_mount; int error; @@ -850,11 +846,9 @@ xfs_zone_gc_finish_chunk( return; } - chunk->scratch->freed += chunk->len; - if (chunk->scratch->freed == chunk->scratch->offset) { - chunk->scratch->offset = 0; - chunk->scratch->freed = 0; - } + data->scratch_tail = + (data->scratch_tail + chunk->len) % data->scratch_size; + data->scratch_available += chunk->len; /* * Cycle through the iolock and wait for direct I/O and layouts to @@ -906,39 +900,64 @@ out: bio_put(&chunk->bio); } -static bool -xfs_zone_gc_prepare_reset( - struct bio *bio, - struct xfs_rtgroup *rtg) +static void +xfs_submit_zone_reset_bio( + struct xfs_rtgroup *rtg, + struct bio *bio) { + struct xfs_mount *mp = rtg_mount(rtg); + trace_xfs_zone_reset(rtg); ASSERT(rtg_rmap(rtg)->i_used_blocks == 0); + + if (XFS_TEST_ERROR(mp, XFS_ERRTAG_ZONE_RESET)) { + bio_io_error(bio); + return; + } + + XFS_STATS_INC(mp, xs_gc_zone_reset_calls); + bio->bi_iter.bi_sector = xfs_gbno_to_daddr(&rtg->rtg_group, 0); if (!bdev_zone_is_seq(bio->bi_bdev, bio->bi_iter.bi_sector)) { - if (!bdev_max_discard_sectors(bio->bi_bdev)) - return false; - bio->bi_opf = REQ_OP_DISCARD | REQ_SYNC; - bio->bi_iter.bi_size = - XFS_FSB_TO_B(rtg_mount(rtg), rtg_blocks(rtg)); + /* + * Also use the bio to drive the state machine when neither + * zone reset nor discard is supported to keep things simple. + */ + if (!bdev_max_discard_sectors(bio->bi_bdev)) { + bio_endio(bio); + return; + } + bio->bi_opf &= ~REQ_OP_ZONE_RESET; + bio->bi_opf |= REQ_OP_DISCARD; + bio->bi_iter.bi_size = XFS_FSB_TO_B(mp, rtg_blocks(rtg)); } - return true; + submit_bio(bio); +} + +static void xfs_bio_wait_endio(struct bio *bio) +{ + complete(bio->bi_private); } int xfs_zone_gc_reset_sync( struct xfs_rtgroup *rtg) { - int error = 0; + DECLARE_COMPLETION_ONSTACK(done); struct bio bio; + int error; bio_init(&bio, rtg_mount(rtg)->m_rtdev_targp->bt_bdev, NULL, 0, - REQ_OP_ZONE_RESET); - if (xfs_zone_gc_prepare_reset(&bio, rtg)) - error = submit_bio_wait(&bio); - bio_uninit(&bio); + REQ_OP_ZONE_RESET | REQ_SYNC); + bio.bi_private = &done; + bio.bi_end_io = xfs_bio_wait_endio; + xfs_submit_zone_reset_bio(rtg, &bio); + wait_for_completion_io(&done); + error = blk_status_to_errno(bio.bi_status); + bio_uninit(&bio); return error; } @@ -973,15 +992,7 @@ xfs_zone_gc_reset_zones( chunk->data = data; WRITE_ONCE(chunk->state, XFS_GC_BIO_NEW); list_add_tail(&chunk->entry, &data->resetting); - - /* - * Also use the bio to drive the state machine when neither - * zone reset nor discard is supported to keep things simple. - */ - if (xfs_zone_gc_prepare_reset(bio, rtg)) - submit_bio(bio); - else - bio_endio(bio); + xfs_submit_zone_reset_bio(rtg, bio); } while (next); } @@ -989,9 +1000,15 @@ static bool xfs_zone_gc_should_start_new_work( struct xfs_zone_gc_data *data) { + struct xfs_open_zone *oz; + if (xfs_is_shutdown(data->mp)) return false; - if (!xfs_zone_gc_space_available(data)) + if (!data->scratch_available) + return false; + + oz = xfs_zone_gc_ensure_target(data->mp); + if (!oz || oz->oz_allocated == rtg_blocks(oz->oz_rtg)) return false; if (!data->iter.victim_rtg) { diff --git a/fs/xfs/xfs_zone_info.c b/fs/xfs/xfs_zone_info.c index 07e30c596975..53eabbc3334c 100644 --- a/fs/xfs/xfs_zone_info.c +++ b/fs/xfs/xfs_zone_info.c @@ -3,7 +3,7 @@ * Copyright (c) 2023-2025 Christoph Hellwig. * Copyright (c) 2024-2025, Western Digital Corporation or its affiliates. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_shared.h" #include "xfs_format.h" #include "xfs_trans_resv.h" diff --git a/fs/xfs/xfs_zone_priv.h b/fs/xfs/xfs_zone_priv.h index ce7f0e2f4598..8fbf9a52964e 100644 --- a/fs/xfs/xfs_zone_priv.h +++ b/fs/xfs/xfs_zone_priv.h @@ -72,7 +72,6 @@ struct xfs_zone_info { /* * Free zone search cursor and number of free zones: */ - unsigned long zi_free_zone_cursor; atomic_t zi_nr_free_zones; /* diff --git a/fs/xfs/xfs_zone_space_resv.c b/fs/xfs/xfs_zone_space_resv.c index fc1a4d1ce10c..5c6e6ef627e4 100644 --- a/fs/xfs/xfs_zone_space_resv.c +++ b/fs/xfs/xfs_zone_space_resv.c @@ -3,7 +3,7 @@ * Copyright (c) 2023-2025 Christoph Hellwig. * Copyright (c) 2024-2025, Western Digital Corporation or its affiliates. */ -#include "xfs.h" +#include "xfs_platform.h" #include "xfs_shared.h" #include "xfs_format.h" #include "xfs_trans_resv.h" |
