btrfs: Convert to using fs_context Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
diff --git a/fs/btrfs/compression.c b/fs/btrfs/compression.c index 2955a4e..73afa20 100644 --- a/fs/btrfs/compression.c +++ b/fs/btrfs/compression.c
@@ -1588,15 +1588,3 @@ int btrfs_compress_heuristic(struct inode *inode, u64 start, u64 end) __free_workspace(0, ws_list, true); return ret; } - -unsigned int btrfs_compress_str2level(const char *str) -{ - if (strncmp(str, "zlib", 4) != 0) - return 0; - - /* Accepted form: zlib:1 up to zlib:9 and nothing left after the number */ - if (str[4] == ':' && '1' <= str[5] && str[5] <= '9' && str[6] == 0) - return str[5] - '0'; - - return BTRFS_ZLIB_DEFAULT_LEVEL; -}
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index e204336..4e46ff4 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h
@@ -31,6 +31,34 @@ #include "extent_io.h" #include "extent_map.h" #include "async-thread.h" +#include "compression.h" + +struct btrfs_fs_context { + /* Track the options that were specified */ + unsigned long specified[256 / (sizeof(long) * 8)]; + + char **devices; + char *subvol_name; + u64 subvol_objectid; + + unsigned int nr_devices; + unsigned int mount_opt; + unsigned int mount_opt_mask; /* Bits to be changed */ + unsigned int mount_opt_explicit; /* Changes set by params */ + enum btrfs_compression_type compress_type; + unsigned int compress_level; + + u64 max_inline; + u32 metadata_ratio; + u32 thread_pool_size; + u32 commit_interval; +#ifdef CONFIG_BTRFS_FS_CHECK_INTEGRITY + u32 check_integrity_print_mask; +#endif + + struct vfsmount *root_mnt; + bool root; +}; struct btrfs_trans_handle; struct btrfs_transaction; @@ -1017,8 +1045,6 @@ struct btrfs_fs_info { u32 data_chunk_allocations; u32 metadata_ratio; - void *bdev_holder; - /* private scrub information */ struct mutex scrub_lock; atomic_t scrubs_running; @@ -1100,9 +1126,6 @@ struct btrfs_fs_info { struct mutex unused_bg_unpin_mutex; struct mutex delete_unused_bgs_mutex; - /* For btrfs to record security options */ - struct security_mnt_opts security_opts; - /* * Chunks that can't be freed yet (under a trim/discard operation) * and will be latter freed. Protected by fs_info->chunk_mutex. @@ -1325,58 +1348,59 @@ static inline u32 BTRFS_MAX_XATTR_SIZE(const struct btrfs_fs_info *info) * Note: don't forget to add new options to btrfs_alloc_fs_info() and * btrfs_show_options(). */ -#define BTRFS_MOUNT_DATASUM (1 << 0) -#define BTRFS_MOUNT_DATACOW (1 << 1) -#define BTRFS_MOUNT_BARRIER (1 << 2) -#define BTRFS_MOUNT_SSD (1 << 3) -#define BTRFS_MOUNT_DEGRADED (1 << 4) -#define BTRFS_MOUNT_COMPRESS (1 << 5) -#define BTRFS_MOUNT_TREELOG (1 << 6) -#define BTRFS_MOUNT_FLUSHONCOMMIT (1 << 7) -#define BTRFS_MOUNT_SSD_SPREAD (1 << 8) -#define BTRFS_MOUNT_NOSSD (1 << 9) -#define BTRFS_MOUNT_DISCARD (1 << 10) -#define BTRFS_MOUNT_FORCE_COMPRESS (1 << 11) -#define BTRFS_MOUNT_SPACE_CACHE (1 << 12) -#define BTRFS_MOUNT_CLEAR_CACHE (1 << 13) -#define BTRFS_MOUNT_USER_SUBVOL_RM_ALLOWED (1 << 14) -#define BTRFS_MOUNT_ENOSPC_DEBUG (1 << 15) -#define BTRFS_MOUNT_AUTO_DEFRAG (1 << 16) -#define BTRFS_MOUNT_INODE_MAP_CACHE (1 << 17) -#define BTRFS_MOUNT_USEBACKUPROOT (1 << 18) -#define BTRFS_MOUNT_SKIP_BALANCE (1 << 19) -#define BTRFS_MOUNT_CHECK_INTEGRITY (1 << 20) -#define BTRFS_MOUNT_CHECK_INTEGRITY_INCLUDING_EXTENT_DATA (1 << 21) -#define BTRFS_MOUNT_PANIC_ON_FATAL_ERROR (1 << 22) -#define BTRFS_MOUNT_RESCAN_UUID_TREE (1 << 23) -#define BTRFS_MOUNT_FRAGMENT_DATA (1 << 24) -#define BTRFS_MOUNT_FRAGMENT_METADATA (1 << 25) -#define BTRFS_MOUNT_FREE_SPACE_TREE (1 << 26) -#define BTRFS_MOUNT_NOLOGREPLAY (1 << 27) -#define BTRFS_MOUNT_REF_VERIFY (1 << 28) +enum btrfs_opt { + BTRFS_MOUNT_DATASUM = 0, + BTRFS_MOUNT_DATACOW = 1, + BTRFS_MOUNT_BARRIER = 2, + BTRFS_MOUNT_SSD = 3, + BTRFS_MOUNT_DEGRADED = 4, + BTRFS_MOUNT_COMPRESS = 5, + BTRFS_MOUNT_TREELOG = 6, + BTRFS_MOUNT_FLUSHONCOMMIT = 7, + BTRFS_MOUNT_SSD_SPREAD = 8, + BTRFS_MOUNT_NOSSD = 9, + BTRFS_MOUNT_DISCARD = 10, + BTRFS_MOUNT_FORCE_COMPRESS = 11, + BTRFS_MOUNT_SPACE_CACHE = 12, + BTRFS_MOUNT_CLEAR_CACHE = 13, + BTRFS_MOUNT_USER_SUBVOL_RM_ALLOWED = 14, + BTRFS_MOUNT_ENOSPC_DEBUG = 15, + BTRFS_MOUNT_AUTO_DEFRAG = 16, + BTRFS_MOUNT_INODE_MAP_CACHE = 17, + BTRFS_MOUNT_USEBACKUPROOT = 18, + BTRFS_MOUNT_SKIP_BALANCE = 19, + BTRFS_MOUNT_CHECK_INTEGRITY = 20, + BTRFS_MOUNT_CHECK_INTEGRITY_INCLUDING_EXTENT_DATA = 21, + BTRFS_MOUNT_PANIC_ON_FATAL_ERROR = 22, + BTRFS_MOUNT_RESCAN_UUID_TREE = 23, + BTRFS_MOUNT_FRAGMENT_DATA = 24, + BTRFS_MOUNT_FRAGMENT_METADATA = 25, + BTRFS_MOUNT_FREE_SPACE_TREE = 26, + BTRFS_MOUNT_NOLOGREPLAY = 27, + BTRFS_MOUNT_REF_VERIFY = 28, +}; #define BTRFS_DEFAULT_COMMIT_INTERVAL (30) #define BTRFS_DEFAULT_MAX_INLINE (2048) -#define btrfs_clear_opt(o, opt) ((o) &= ~BTRFS_MOUNT_##opt) -#define btrfs_set_opt(o, opt) ((o) |= BTRFS_MOUNT_##opt) -#define btrfs_raw_test_opt(o, opt) ((o) & BTRFS_MOUNT_##opt) -#define btrfs_test_opt(fs_info, opt) ((fs_info)->mount_opt & \ - BTRFS_MOUNT_##opt) +#define btrfs_clear_opt(o, opt) ((o) &= ~(1 << BTRFS_MOUNT_##opt)) +#define btrfs_set_opt(o, opt) ((o) |= (1 << BTRFS_MOUNT_##opt)) +#define btrfs_raw_test_opt(o, opt) ((o) & (1 << BTRFS_MOUNT_##opt)) +#define btrfs_test_opt(fs_info, opt) btrfs_raw_test_opt((fs_info)->mount_opt, opt) #define btrfs_set_and_info(fs_info, opt, fmt, args...) \ -{ \ +do { \ if (!btrfs_test_opt(fs_info, opt)) \ btrfs_info(fs_info, fmt, ##args); \ btrfs_set_opt(fs_info->mount_opt, opt); \ -} +} while (0) #define btrfs_clear_and_info(fs_info, opt, fmt, args...) \ -{ \ +do { \ if (btrfs_test_opt(fs_info, opt)) \ btrfs_info(fs_info, fmt, ##args); \ btrfs_clear_opt(fs_info->mount_opt, opt); \ -} +} while (0) #ifdef CONFIG_BTRFS_DEBUG static inline int @@ -1418,20 +1442,20 @@ btrfs_should_fragment_free_space(struct btrfs_block_group_cache *block_group) */ #define btrfs_set_pending_and_info(info, opt, fmt, args...) \ do { \ - if (!btrfs_raw_test_opt((info)->mount_opt, opt)) { \ - btrfs_info((info), fmt, ##args); \ - btrfs_set_pending((info), SET_##opt); \ - btrfs_clear_pending((info), CLEAR_##opt); \ - } \ + if (!btrfs_test_opt((info), opt)) { \ + btrfs_info((info), fmt, ##args); \ + btrfs_set_pending((info), SET_##opt); \ + btrfs_clear_pending((info), CLEAR_##opt); \ + } \ } while(0) #define btrfs_clear_pending_and_info(info, opt, fmt, args...) \ do { \ - if (btrfs_raw_test_opt((info)->mount_opt, opt)) { \ - btrfs_info((info), fmt, ##args); \ - btrfs_set_pending((info), CLEAR_##opt); \ - btrfs_clear_pending((info), SET_##opt); \ - } \ + if (btrfs_test_opt((info), opt)) { \ + btrfs_info((info), fmt, ##args); \ + btrfs_set_pending((info), CLEAR_##opt); \ + btrfs_clear_pending((info), SET_##opt); \ + } \ } while(0) /* @@ -2960,7 +2984,6 @@ static inline void free_fs_info(struct btrfs_fs_info *fs_info) kfree(fs_info->free_space_root); kfree(fs_info->super_copy); kfree(fs_info->super_for_commit); - security_free_mnt_opts(&fs_info->security_opts); kvfree(fs_info); } @@ -3246,14 +3269,15 @@ int btrfs_sysfs_add_mounted(struct btrfs_fs_info *fs_info); void btrfs_sysfs_remove_mounted(struct btrfs_fs_info *fs_info); /* fs_params.c */ -int btrfs_parse_options(struct btrfs_fs_info *info, char *options, - unsigned long new_flags); -int btrfs_parse_device_options(const char *options, fmode_t flags, void *holder); -int btrfs_parse_subvol_options(const char *options, char **subvol_name, - u64 *subvol_objectid); +extern const struct fs_parameter_description btrfs_fs_parameters; +int btrfs_init_fs_context(struct fs_context *fc, struct dentry *reference); +void btrfs_apply_configuration(struct fs_context *fc, struct super_block *sb); int btrfs_show_options(struct seq_file *seq, struct dentry *dentry); /* super.c */ +extern struct file_system_type btrfs_fs_type; +int btrfs_get_tree(struct fs_context *fc); +int btrfs_reconfigure(struct fs_context *fc); int btrfs_sync_fs(struct super_block *sb, int wait); static inline __printf(2, 3) __cold
diff --git a/fs/btrfs/dev-replace.c b/fs/btrfs/dev-replace.c index 2aa48aec..efd4d61 100644 --- a/fs/btrfs/dev-replace.c +++ b/fs/btrfs/dev-replace.c
@@ -196,7 +196,7 @@ static int btrfs_init_dev_replace_tgtdev(struct btrfs_fs_info *fs_info, } bdev = blkdev_get_by_path(device_path, FMODE_WRITE | FMODE_EXCL, - fs_info->bdev_holder); + &btrfs_fs_type); if (IS_ERR(bdev)) { btrfs_err(fs_info, "target device %s is invalid!", device_path); return PTR_ERR(bdev);
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index 99bea5c..0e5c37f 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c
@@ -2597,9 +2597,9 @@ static int btrfs_validate_write_super(struct btrfs_fs_info *fs_info, return ret; } -int open_ctree(struct super_block *sb, - struct btrfs_fs_devices *fs_devices, - char *options) +int open_ctree(struct fs_context *fc, + struct super_block *sb, + struct btrfs_fs_devices *fs_devices) { u32 sectorsize; u32 nodesize; @@ -2698,21 +2698,15 @@ int open_ctree(struct super_block *sb, atomic_set(&fs_info->reada_works_cnt, 0); atomic64_set(&fs_info->tree_mod_seq, 0); fs_info->sb = sb; - fs_info->max_inline = BTRFS_DEFAULT_MAX_INLINE; - fs_info->metadata_ratio = 0; fs_info->defrag_inodes = RB_ROOT; atomic64_set(&fs_info->free_chunk_space, 0); fs_info->tree_mod_log = RB_ROOT; - fs_info->commit_interval = BTRFS_DEFAULT_COMMIT_INTERVAL; fs_info->avg_delayed_ref_runtime = NSEC_PER_SEC >> 6; /* div by 64 */ /* readahead state */ INIT_RADIX_TREE(&fs_info->reada_tree, GFP_NOFS & ~__GFP_DIRECT_RECLAIM); spin_lock_init(&fs_info->reada_lock); btrfs_init_ref_verify(fs_info); - fs_info->thread_pool_size = min_t(unsigned long, - num_online_cpus() + 2, 8); - INIT_LIST_HEAD(&fs_info->ordered_roots); spin_lock_init(&fs_info->ordered_root_lock); @@ -2730,11 +2724,7 @@ int open_ctree(struct super_block *sb, goto fail_iput; } btrfs_init_delayed_root(fs_info->delayed_root); - btrfs_init_scrub(fs_info); -#ifdef CONFIG_BTRFS_FS_CHECK_INTEGRITY - fs_info->check_integrity_print_mask = 0; -#endif btrfs_init_balance(fs_info); btrfs_init_async_reclaim_work(&fs_info->async_reclaim_work); @@ -2845,17 +2835,12 @@ int open_ctree(struct super_block *sb, generation = btrfs_super_generation(disk_super); find_oldest_super_backup(fs_info, generation); - /* - * In the long term, we'll store the compression type in the super - * block, and it'll be used for per file compression control. - */ - fs_info->compress_type = BTRFS_COMPRESS_ZLIB; + if (btrfs_fs_compat_ro(fs_info, FREE_SPACE_TREE)) + btrfs_set_opt(fs_info->mount_opt, FREE_SPACE_TREE); + else if (generation) + btrfs_set_opt(fs_info->mount_opt, SPACE_CACHE); - ret = btrfs_parse_options(fs_info, options, sb->s_flags); - if (ret) { - err = ret; - goto fail_alloc; - } + btrfs_apply_configuration(fc, sb); features = btrfs_super_incompat_flags(disk_super) & ~BTRFS_FEATURE_INCOMPAT_SUPP;
diff --git a/fs/btrfs/disk-io.h b/fs/btrfs/disk-io.h index 4cccba2..73115b8 100644 --- a/fs/btrfs/disk-io.h +++ b/fs/btrfs/disk-io.h
@@ -49,9 +49,8 @@ struct extent_buffer *btrfs_find_create_tree_block( struct btrfs_fs_info *fs_info, u64 bytenr); void clean_tree_block(struct btrfs_fs_info *fs_info, struct extent_buffer *buf); -int open_ctree(struct super_block *sb, - struct btrfs_fs_devices *fs_devices, - char *options); +int open_ctree(struct fs_context *fc, struct super_block *sb, + struct btrfs_fs_devices *fs_devices); void close_ctree(struct btrfs_fs_info *fs_info); int write_all_supers(struct btrfs_fs_info *fs_info, int max_mirrors); struct buffer_head *btrfs_read_dev_super(struct block_device *bdev);
diff --git a/fs/btrfs/fs_params.c b/fs/btrfs/fs_params.c index a09bb1f..b91bcc3 100644 --- a/fs/btrfs/fs_params.c +++ b/fs/btrfs/fs_params.c
@@ -1,719 +1,885 @@ // SPDX-License-Identifier: GPL-2.0 /* + * Filesystem parameterisation. + * * Copyright (C) 2007 Oracle. All rights reserved. + * Copyright (C) 2018 Red Hat, Inc. All rights reserved. */ -#include <linux/kernel.h> -#include <linux/parser.h> +#include <linux/mount.h> +#include <linux/fs_context.h> +#include <linux/fs_parser.h> #include "ctree.h" -#include "btrfs_inode.h" -#include "compression.h" #include "volumes.h" +#include "btrfs_inode.h" +#include "rcu-string.h" -enum { - Opt_acl, Opt_noacl, +enum btrfs_param { + Opt_acl, + Opt_alloc_start, + Opt_autodefrag, + Opt_barrier, + Opt_check_integrity, + Opt_check_integrity_including_extent_data, + Opt_check_integrity_print_mask, Opt_clear_cache, Opt_commit_interval, Opt_compress, Opt_compress_force, - Opt_compress_force_type, - Opt_compress_type, + Opt_datacow, + Opt_datasum, Opt_degraded, Opt_device, + Opt_discard, + Opt_enospc_debug, Opt_fatal_errors, - Opt_flushoncommit, Opt_noflushoncommit, - Opt_inode_cache, Opt_noinode_cache, + Opt_flushoncommit, + Opt_fragment, + Opt_inode_cache, Opt_max_inline, - Opt_barrier, Opt_nobarrier, - Opt_datacow, Opt_nodatacow, - Opt_datasum, Opt_nodatasum, - Opt_defrag, Opt_nodefrag, - Opt_discard, Opt_nodiscard, + Opt_metadata_ratio, Opt_nologreplay, - Opt_norecovery, - Opt_ratio, + Opt_nossd, + Opt_recovery, + Opt_ref_verify, Opt_rescan_uuid_tree, Opt_skip_balance, - Opt_space_cache, Opt_no_space_cache, - Opt_space_cache_version, - Opt_ssd, Opt_nossd, - Opt_ssd_spread, Opt_nossd_spread, + Opt_source, + Opt_space_cache, + Opt_ssd, + Opt_ssd_spread, Opt_subvol, - Opt_subvol_empty, Opt_subvolid, + Opt_subvolrootid, Opt_thread_pool, - Opt_treelog, Opt_notreelog, + Opt_treelog, Opt_usebackuproot, Opt_user_subvol_rm_allowed, - - /* Deprecated options */ - Opt_alloc_start, - Opt_recovery, - Opt_subvolrootid, - - /* Debugging options */ - Opt_check_integrity, - Opt_check_integrity_including_extent_data, - Opt_check_integrity_print_mask, - Opt_enospc_debug, Opt_noenospc_debug, -#ifdef CONFIG_BTRFS_DEBUG - Opt_fragment_data, Opt_fragment_metadata, Opt_fragment_all, -#endif -#ifdef CONFIG_BTRFS_FS_REF_VERIFY - Opt_ref_verify, -#endif - Opt_err, + nr__btrfs_params }; -static const match_table_t tokens = { - {Opt_acl, "acl"}, - {Opt_noacl, "noacl"}, - {Opt_clear_cache, "clear_cache"}, - {Opt_commit_interval, "commit=%u"}, - {Opt_compress, "compress"}, - {Opt_compress_type, "compress=%s"}, - {Opt_compress_force, "compress-force"}, - {Opt_compress_force_type, "compress-force=%s"}, - {Opt_degraded, "degraded"}, - {Opt_device, "device=%s"}, - {Opt_fatal_errors, "fatal_errors=%s"}, - {Opt_flushoncommit, "flushoncommit"}, - {Opt_noflushoncommit, "noflushoncommit"}, - {Opt_inode_cache, "inode_cache"}, - {Opt_noinode_cache, "noinode_cache"}, - {Opt_max_inline, "max_inline=%s"}, - {Opt_barrier, "barrier"}, - {Opt_nobarrier, "nobarrier"}, - {Opt_datacow, "datacow"}, - {Opt_nodatacow, "nodatacow"}, - {Opt_datasum, "datasum"}, - {Opt_nodatasum, "nodatasum"}, - {Opt_defrag, "autodefrag"}, - {Opt_nodefrag, "noautodefrag"}, - {Opt_discard, "discard"}, - {Opt_nodiscard, "nodiscard"}, - {Opt_nologreplay, "nologreplay"}, - {Opt_norecovery, "norecovery"}, - {Opt_ratio, "metadata_ratio=%u"}, - {Opt_rescan_uuid_tree, "rescan_uuid_tree"}, - {Opt_skip_balance, "skip_balance"}, - {Opt_space_cache, "space_cache"}, - {Opt_no_space_cache, "nospace_cache"}, - {Opt_space_cache_version, "space_cache=%s"}, - {Opt_ssd, "ssd"}, - {Opt_nossd, "nossd"}, - {Opt_ssd_spread, "ssd_spread"}, - {Opt_nossd_spread, "nossd_spread"}, - {Opt_subvol, "subvol=%s"}, - {Opt_subvol_empty, "subvol="}, - {Opt_subvolid, "subvolid=%s"}, - {Opt_thread_pool, "thread_pool=%u"}, - {Opt_treelog, "treelog"}, - {Opt_notreelog, "notreelog"}, - {Opt_usebackuproot, "usebackuproot"}, - {Opt_user_subvol_rm_allowed, "user_subvol_rm_allowed"}, +enum { + Opt_compress__zlib = 0, /* Arg is optional; default is 0 */ + Opt_compress__zlib_1, + Opt_compress__zlib_2, + Opt_compress__zlib_3, + Opt_compress__zlib_4, + Opt_compress__zlib_5, + Opt_compress__zlib_6, + Opt_compress__zlib_7, + Opt_compress__zlib_8, + Opt_compress__zlib_9, + Opt_compress__lzo, + Opt_compress__zstd, + Opt_compress__no, +}; - /* Deprecated options */ - {Opt_alloc_start, "alloc_start=%s"}, - {Opt_recovery, "recovery"}, - {Opt_subvolrootid, "subvolrootid=%d"}, +enum btrfs_space_cache { + Opt_space_cache__v1 = 0, /* Arg is optional; default is 0 */ + Opt_space_cache__v2, + Opt_space_cache__no, +}; - /* Debugging options */ - {Opt_check_integrity, "check_int"}, - {Opt_check_integrity_including_extent_data, "check_int_data"}, - {Opt_check_integrity_print_mask, "check_int_print_mask=%u"}, - {Opt_enospc_debug, "enospc_debug"}, - {Opt_noenospc_debug, "noenospc_debug"}, -#ifdef CONFIG_BTRFS_DEBUG - {Opt_fragment_data, "fragment=data"}, - {Opt_fragment_metadata, "fragment=metadata"}, - {Opt_fragment_all, "fragment=all"}, -#endif -#ifdef CONFIG_BTRFS_FS_REF_VERIFY - {Opt_ref_verify, "ref_verify"}, -#endif - {Opt_err, NULL}, +enum { + Opt_fatal_errors__bug, + Opt_fatal_errors__panic, +}; + +enum { + Opt_fragment__all, + Opt_fragment__data, + Opt_fragment__metadata, +}; + +static const struct fs_parameter_spec btrfs_param_specs[nr__btrfs_params] = { + [Opt_acl] = { fs_param_is_flag, fs_param_neg_with_no }, + [Opt_alloc_start] = { fs_param_is_string, fs_param_deprecated }, + [Opt_autodefrag] = { fs_param_is_flag, fs_param_neg_with_no }, + [Opt_barrier] = { fs_param_is_flag, fs_param_neg_with_no }, + [Opt_check_integrity] = { fs_param_is_flag }, + [Opt_check_integrity_including_extent_data]= { fs_param_is_flag }, + [Opt_check_integrity_print_mask]= { fs_param_is_u32 }, + [Opt_clear_cache] = { fs_param_is_flag }, + [Opt_commit_interval] = { fs_param_is_u32 }, + [Opt_compress] = { fs_param_is_enum, fs_param_v_optional }, + [Opt_compress_force] = { fs_param_is_enum, fs_param_v_optional }, + [Opt_datacow] = { fs_param_is_flag, fs_param_neg_with_no }, + [Opt_datasum] = { fs_param_is_flag, fs_param_neg_with_no }, + [Opt_degraded] = { fs_param_is_flag }, + [Opt_device] = { fs_param_is_string }, + [Opt_discard] = { fs_param_is_flag, fs_param_neg_with_no }, + [Opt_enospc_debug] = { fs_param_is_flag, fs_param_neg_with_no }, + [Opt_fatal_errors] = { fs_param_is_enum }, + [Opt_flushoncommit] = { fs_param_is_flag, fs_param_neg_with_no }, + [Opt_fragment] = { fs_param_is_enum }, + [Opt_inode_cache] = { fs_param_is_flag, fs_param_neg_with_no }, + [Opt_max_inline] = { fs_param_is_string }, + [Opt_metadata_ratio] = { fs_param_is_u32 }, + [Opt_nologreplay] = { fs_param_is_flag }, + [Opt_nossd] = { fs_param_is_flag }, + [Opt_recovery] = { fs_param_is_flag, fs_param_neg_with_no }, + [Opt_ref_verify] = { fs_param_is_flag }, + [Opt_rescan_uuid_tree] = { fs_param_is_flag }, + [Opt_skip_balance] = { fs_param_is_flag }, + [Opt_source] = { fs_param_is_string }, + [Opt_space_cache] = { fs_param_is_enum, + fs_param_v_optional | fs_param_neg_with_no }, + [Opt_ssd] = { fs_param_is_flag }, + [Opt_ssd_spread] = { fs_param_is_flag }, + [Opt_subvol] = { fs_param_is_string, fs_param_v_optional }, + [Opt_subvolid] = { fs_param_is_u64 }, + [Opt_subvolrootid] = { fs_param_is_s32, fs_param_deprecated }, + [Opt_thread_pool] = { fs_param_is_u32 }, + [Opt_treelog] = { fs_param_is_flag, fs_param_neg_with_no }, + [Opt_usebackuproot] = { fs_param_is_flag }, + [Opt_user_subvol_rm_allowed] = { fs_param_is_flag }, +}; + +static const char *const btrfs_param_keys[nr__btrfs_params] = { + [Opt_acl] = "acl", + [Opt_alloc_start] = "alloc_start", + [Opt_autodefrag] = "autodefrag", + [Opt_barrier] = "barrier", + [Opt_check_integrity] = "check_int", + [Opt_check_integrity_including_extent_data] = "check_int_data", + [Opt_check_integrity_print_mask] = "check_int_print_mask", + [Opt_clear_cache] = "clear_cache", + [Opt_commit_interval] = "commit", + [Opt_compress] = "compress", + [Opt_compress_force] = "compress-force", + [Opt_datacow] = "datacow", + [Opt_datasum] = "datasum", + [Opt_degraded] = "degraded", + [Opt_device] = "device", + [Opt_discard] = "discard", + [Opt_enospc_debug] = "enospc_debug", + [Opt_fatal_errors] = "fatal_errors", + [Opt_flushoncommit] = "flushoncommit", + [Opt_fragment] = "fragment", + [Opt_inode_cache] = "inode_cache", + [Opt_max_inline] = "max_inline", + [Opt_metadata_ratio] = "metadata_ratio", + [Opt_nologreplay] = "nologreplay", + [Opt_nossd] = "nossd", + [Opt_recovery] = "recovery", + [Opt_ref_verify] = "ref_verify", + [Opt_rescan_uuid_tree] = "rescan_uuid_tree", + [Opt_skip_balance] = "skip_balance", + [Opt_source] = "source", + [Opt_space_cache] = "space_cache", + [Opt_ssd] = "ssd", + [Opt_ssd_spread] = "ssd_spread", + [Opt_subvol] = "subvol", + [Opt_subvolid] = "subvolid", + [Opt_subvolrootid] = "subvolrootid", + [Opt_thread_pool] = "thread_pool", + [Opt_treelog] = "treelog", + [Opt_usebackuproot] = "usebackuproot", + [Opt_user_subvol_rm_allowed] = "user_subvol_rm_allowed", +}; + +static const struct fs_parameter_enum btrfs_param_enums[] = { + { Opt_fragment, "all", Opt_fragment__all }, + { Opt_fragment, "data", Opt_fragment__data }, + { Opt_fragment, "metadata", Opt_fragment__metadata }, + { Opt_compress, "zlib", Opt_compress__zlib }, + { Opt_compress, "zlib:1", Opt_compress__zlib_1 }, + { Opt_compress, "zlib:2", Opt_compress__zlib_2 }, + { Opt_compress, "zlib:3", Opt_compress__zlib_3 }, + { Opt_compress, "zlib:4", Opt_compress__zlib_4 }, + { Opt_compress, "zlib:5", Opt_compress__zlib_5 }, + { Opt_compress, "zlib:6", Opt_compress__zlib_6 }, + { Opt_compress, "zlib:7", Opt_compress__zlib_7 }, + { Opt_compress, "zlib:8", Opt_compress__zlib_8 }, + { Opt_compress, "zlib:9", Opt_compress__zlib_9 }, + { Opt_compress, "lzo", Opt_compress__lzo }, + { Opt_compress, "zstd", Opt_compress__zstd }, + { Opt_compress, "no", Opt_compress__no }, + { Opt_compress_force, "zlib", Opt_compress__zlib }, + { Opt_compress_force, "zlib:1", Opt_compress__zlib_1 }, + { Opt_compress_force, "zlib:2", Opt_compress__zlib_2 }, + { Opt_compress_force, "zlib:3", Opt_compress__zlib_3 }, + { Opt_compress_force, "zlib:4", Opt_compress__zlib_4 }, + { Opt_compress_force, "zlib:5", Opt_compress__zlib_5 }, + { Opt_compress_force, "zlib:6", Opt_compress__zlib_6 }, + { Opt_compress_force, "zlib:7", Opt_compress__zlib_7 }, + { Opt_compress_force, "zlib:8", Opt_compress__zlib_8 }, + { Opt_compress_force, "zlib:9", Opt_compress__zlib_9 }, + { Opt_compress_force, "lzo", Opt_compress__lzo }, + { Opt_compress_force, "zstd", Opt_compress__zstd }, + { Opt_compress_force, "no", Opt_compress__no }, + { Opt_space_cache, "v1", Opt_space_cache__v1 }, + { Opt_space_cache, "v2", Opt_space_cache__v2 }, + { Opt_fatal_errors, "bug", Opt_fatal_errors__bug }, + { Opt_fatal_errors, "panic", Opt_fatal_errors__panic }, +}; + +const struct fs_parameter_description btrfs_fs_parameters = { + .name = "btrfs", + .nr_params = nr__btrfs_params, + .nr_enums = ARRAY_SIZE(btrfs_param_enums), + .source_param = Opt_source, + .keys = btrfs_param_keys, + .specs = btrfs_param_specs, + .enums = btrfs_param_enums, +}; + +#define btrfs_mparam_set(ctx, opt) \ + do { \ + ctx->mount_opt |= BTRFS_MOUNT_##opt; \ + ctx->mount_opt_mask |= BTRFS_MOUNT_##opt; \ + } while (0) + +#define btrfs_mparam_clear(ctx, opt) \ + do { \ + ctx->mount_opt &= ~BTRFS_MOUNT_##opt; \ + ctx->mount_opt_mask |= BTRFS_MOUNT_##opt; \ + } while (0) + +struct btrfs_flag_map { + u8 bit; +#define BTRFS_FLAG_MAP_PRESENT 0x80 /* There is a rule here */ +}; + +#define mount_opt(x) { BTRFS_MOUNT_##x | BTRFS_FLAG_MAP_PRESENT } + +static const struct btrfs_flag_map btrfs_flag_map[nr__btrfs_params] = { + [Opt_barrier] = mount_opt(BARRIER), + [Opt_check_integrity] = mount_opt(CHECK_INTEGRITY), + [Opt_check_integrity_including_extent_data] = + mount_opt(CHECK_INTEGRITY_INCLUDING_EXTENT_DATA), + [Opt_clear_cache] = mount_opt(CLEAR_CACHE), + [Opt_compress] = mount_opt(COMPRESS), + [Opt_compress_force] = mount_opt(FORCE_COMPRESS), + [Opt_datacow] = mount_opt(DATACOW), + [Opt_datasum] = mount_opt(DATASUM), + [Opt_autodefrag] = mount_opt(AUTO_DEFRAG), + [Opt_degraded] = mount_opt(DEGRADED), + [Opt_discard] = mount_opt(DISCARD), + [Opt_enospc_debug] = mount_opt(ENOSPC_DEBUG), + [Opt_flushoncommit] = mount_opt(FLUSHONCOMMIT), + [Opt_inode_cache] = mount_opt(INODE_MAP_CACHE), + [Opt_nologreplay] = mount_opt(NOLOGREPLAY), + [Opt_nossd] = mount_opt(NOSSD), + [Opt_ref_verify] = mount_opt(REF_VERIFY), + [Opt_rescan_uuid_tree] = mount_opt(RESCAN_UUID_TREE), + [Opt_skip_balance] = mount_opt(SKIP_BALANCE), + [Opt_ssd] = mount_opt(SSD), + [Opt_ssd_spread] = mount_opt(SSD_SPREAD), + [Opt_treelog] = mount_opt(TREELOG), + [Opt_usebackuproot] = mount_opt(USEBACKUPROOT), + [Opt_user_subvol_rm_allowed] = mount_opt(USER_SUBVOL_RM_ALLOWED), }; /* - * Regular mount options parser. Everything that is needed only when - * reading in a new superblock is parsed here. - * XXX JDM: This needs to be cleaned up for remount. + * Implicit rules of option relationships. + * + * "implies" means turning on option $key turns on $target; turning off $target + * turns off $key. Note that we don't automatically calculate transitivity, + * but rather transitive relationships must be explicitly listed. + * + * "contradicts" means turning on option $key turns off $target and vice versa. + * + * The op is mentioned specifically just in case we want to add other options + * later. */ -int btrfs_parse_options(struct btrfs_fs_info *info, char *options, - unsigned long new_flags) +struct btrfs_implicit_rule { + u8 key; + u8 target; +}; + +#define _(key, op, target_key) { key, target_key } +static const struct btrfs_implicit_rule btrfs_opt_dependencies[] = { + _(Opt_datasum, DEPENDS_ON, Opt_datacow), + _(Opt_compress, DEPENDS_ON, Opt_datacow), + _(Opt_compress, DEPENDS_ON, Opt_datasum), + _(Opt_compress_force, DEPENDS_ON, Opt_datacow), + _(Opt_compress_force, DEPENDS_ON, Opt_datasum), + _(Opt_compress_force, DEPENDS_ON, Opt_compress), + _(Opt_ssd_spread, DEPENDS_ON, Opt_ssd), + _(Opt_check_integrity_including_extent_data, DEPENDS_ON, Opt_check_integrity), +}; + +static const struct btrfs_implicit_rule btrfs_opt_contradictions[] = { + _(Opt_ssd, CONTRADICTS, Opt_nossd), + _(Opt_ssd_spread, CONTRADICTS, Opt_nossd), + _(Opt_nossd, CONTRADICTS, Opt_ssd), + _(Opt_nossd, CONTRADICTS, Opt_ssd_spread), +}; + +/* + * Parse the mount options into a summary of what is explicitly changed. + * + * This is slightly complicated for BtrFS compared to something like NFS as we + * superblock creation process starts by reading the configuration from disk + * and then applies any parameter changes on top of that. + * + * This means that we need to track what changes we're going to make and how + * they interact - but we have to bear in mind that parameters are not + * commutative. + */ +static int btrfs_parse_param(struct fs_context *fc, struct fs_parameter *param) { - substring_t args[MAX_OPT_ARGS]; - char *p, *num; - u64 cache_gen; - int intarg; - int ret = 0; - char *compress_type; - bool compress_force = false; - enum btrfs_compression_type saved_compress_type; - bool saved_compress_force; - int no_compress = 0; + struct fs_parse_result result; + struct btrfs_fs_context *ctx = fc->fs_private; + unsigned int mask; + char **devs; + bool enabled; + int opt, i; + u8 bit; - cache_gen = btrfs_super_cache_generation(info->super_copy); - if (btrfs_fs_compat_ro(info, FREE_SPACE_TREE)) - btrfs_set_opt(info->mount_opt, FREE_SPACE_TREE); - else if (cache_gen) - btrfs_set_opt(info->mount_opt, SPACE_CACHE); + pr_notice("PARM[%s] = %s\n", param->key, param->string); - /* - * Even the options are empty, we still need to do extra check - * against new flags + opt = fs_parse(fc, &btrfs_fs_parameters, param, &result); + if (opt < 0) + return opt; + enabled = !result.negated; + __set_bit(opt, ctx->specified); + + /* Do some initial weeding out of options that might not be or might no + * longer be supported. */ - if (!options) - goto check; - - while ((p = strsep(&options, ",")) != NULL) { - int token; - if (!*p) - continue; - - token = match_token(p, tokens, args); - switch (token) { - case Opt_degraded: - btrfs_info(info, "allowing degraded mounts"); - btrfs_set_opt(info->mount_opt, DEGRADED); + switch (opt) { + case Opt_source: + case Opt_device: + case Opt_subvol: + case Opt_subvolid: + case Opt_subvolrootid: + switch (fc->purpose) { + case FS_CONTEXT_FOR_USER_MOUNT: + case FS_CONTEXT_FOR_KERNEL_MOUNT: + case FS_CONTEXT_FOR_SUBMOUNT: break; - case Opt_subvol: - case Opt_subvol_empty: - case Opt_subvolid: - case Opt_subvolrootid: - case Opt_device: - /* - * These are parsed by btrfs_parse_subvol_options or - * btrfs_parse_device_options and can be ignored here. - */ - break; - case Opt_nodatasum: - btrfs_clear_and_info(info, DATASUM, - "setting nodatasum"); - break; - case Opt_datasum: - if (!btrfs_test_opt(info, DATASUM)) { - if (!btrfs_test_opt(info, DATACOW)) - btrfs_info(info, - "setting datasum, datacow enabled"); - else - btrfs_info(info, "setting datasum"); - } - btrfs_set_opt(info->mount_opt, DATACOW); - btrfs_set_opt(info->mount_opt, DATASUM); - break; - case Opt_nodatacow: - if (btrfs_test_opt(info, DATACOW)) { - if (!btrfs_test_opt(info, COMPRESS) || - !btrfs_test_opt(info, FORCE_COMPRESS)) { - btrfs_info(info, - "setting nodatacow, compression disabled"); - } else { - btrfs_info(info, "setting nodatacow"); - } - } - btrfs_clear_opt(info->mount_opt, COMPRESS); - btrfs_clear_opt(info->mount_opt, FORCE_COMPRESS); - btrfs_clear_opt(info->mount_opt, DATACOW); - btrfs_clear_opt(info->mount_opt, DATASUM); - break; - case Opt_datacow: - btrfs_set_and_info(info, DATACOW, - "setting datacow"); - break; - case Opt_compress_force: - case Opt_compress_force_type: - compress_force = true; - /* Fallthrough */ - case Opt_compress: - case Opt_compress_type: - saved_compress_type = btrfs_test_opt(info, - COMPRESS) ? - info->compress_type : BTRFS_COMPRESS_NONE; - saved_compress_force = - btrfs_test_opt(info, FORCE_COMPRESS); - if (token == Opt_compress || - token == Opt_compress_force || - strncmp(args[0].from, "zlib", 4) == 0) { - compress_type = "zlib"; - - info->compress_type = BTRFS_COMPRESS_ZLIB; - info->compress_level = BTRFS_ZLIB_DEFAULT_LEVEL; - /* - * args[0] contains uninitialized data since - * for these tokens we don't expect any - * parameter. - */ - if (token != Opt_compress && - token != Opt_compress_force) - info->compress_level = - btrfs_compress_str2level(args[0].from); - btrfs_set_opt(info->mount_opt, COMPRESS); - btrfs_set_opt(info->mount_opt, DATACOW); - btrfs_set_opt(info->mount_opt, DATASUM); - no_compress = 0; - } else if (strncmp(args[0].from, "lzo", 3) == 0) { - compress_type = "lzo"; - info->compress_type = BTRFS_COMPRESS_LZO; - btrfs_set_opt(info->mount_opt, COMPRESS); - btrfs_set_opt(info->mount_opt, DATACOW); - btrfs_set_opt(info->mount_opt, DATASUM); - btrfs_set_fs_incompat(info, COMPRESS_LZO); - no_compress = 0; - } else if (strcmp(args[0].from, "zstd") == 0) { - compress_type = "zstd"; - info->compress_type = BTRFS_COMPRESS_ZSTD; - btrfs_set_opt(info->mount_opt, COMPRESS); - btrfs_set_opt(info->mount_opt, DATACOW); - btrfs_set_opt(info->mount_opt, DATASUM); - btrfs_set_fs_incompat(info, COMPRESS_ZSTD); - no_compress = 0; - } else if (strncmp(args[0].from, "no", 2) == 0) { - compress_type = "no"; - btrfs_clear_opt(info->mount_opt, COMPRESS); - btrfs_clear_opt(info->mount_opt, FORCE_COMPRESS); - compress_force = false; - no_compress++; - } else { - ret = -EINVAL; - goto out; - } - - if (compress_force) { - btrfs_set_opt(info->mount_opt, FORCE_COMPRESS); - } else { - /* - * If we remount from compress-force=xxx to - * compress=xxx, we need clear FORCE_COMPRESS - * flag, otherwise, there is no way for users - * to disable forcible compression separately. - */ - btrfs_clear_opt(info->mount_opt, FORCE_COMPRESS); - } - if ((btrfs_test_opt(info, COMPRESS) && - (info->compress_type != saved_compress_type || - compress_force != saved_compress_force)) || - (!btrfs_test_opt(info, COMPRESS) && - no_compress == 1)) { - btrfs_info(info, "%s %s compression, level %d", - (compress_force) ? "force" : "use", - compress_type, info->compress_level); - } - compress_force = false; - break; - case Opt_ssd: - btrfs_set_and_info(info, SSD, - "enabling ssd optimizations"); - btrfs_clear_opt(info->mount_opt, NOSSD); - break; - case Opt_ssd_spread: - btrfs_set_and_info(info, SSD, - "enabling ssd optimizations"); - btrfs_set_and_info(info, SSD_SPREAD, - "using spread ssd allocation scheme"); - btrfs_clear_opt(info->mount_opt, NOSSD); - break; - case Opt_nossd: - btrfs_set_opt(info->mount_opt, NOSSD); - btrfs_clear_and_info(info, SSD, - "not using ssd optimizations"); - /* Fallthrough */ - case Opt_nossd_spread: - btrfs_clear_and_info(info, SSD_SPREAD, - "not using spread ssd allocation scheme"); - break; - case Opt_barrier: - btrfs_set_and_info(info, BARRIER, - "turning on barriers"); - break; - case Opt_nobarrier: - btrfs_clear_and_info(info, BARRIER, - "turning off barriers"); - break; - case Opt_thread_pool: - ret = match_int(&args[0], &intarg); - if (ret) { - goto out; - } else if (intarg == 0) { - ret = -EINVAL; - goto out; - } - info->thread_pool_size = intarg; - break; - case Opt_max_inline: - num = match_strdup(&args[0]); - if (num) { - info->max_inline = memparse(num, NULL); - kfree(num); - - if (info->max_inline) { - info->max_inline = min_t(u64, - info->max_inline, - info->sectorsize); - } - btrfs_info(info, "max_inline at %llu", - info->max_inline); - } else { - ret = -ENOMEM; - goto out; - } - break; - case Opt_alloc_start: - btrfs_info(info, - "option alloc_start is obsolete, ignored"); - break; - case Opt_acl: -#ifdef CONFIG_BTRFS_FS_POSIX_ACL - info->sb->s_flags |= SB_POSIXACL; - break; -#else - btrfs_err(info, "support for ACL not compiled in!"); - ret = -EINVAL; - goto out; + default: + return invalf(fc, "btrfs: %s prohibited for remount", + param->key); + } +#ifndef CONFIG_BTRFS_FS_POSIX_ACL + case Opt_acl: + if (!enabled) + return 0; + return invalf(fc, "btrfs: Support for 'acl' not compiled in!"); #endif - case Opt_noacl: - info->sb->s_flags &= ~SB_POSIXACL; + case Opt_compress: + case Opt_compress_force: + if (result.uint_32 == Opt_compress__no) { + result.negated = true; + enabled = false; + } + break; + case Opt_space_cache: + if (result.negated) + result.uint_32 = Opt_space_cache__no; + break; + case Opt_recovery: + if (enabled) { + warnf(fc, "btrfs: 'recovery' is deprecated, use 'usebackuproot' instead"); + result.key = Opt_usebackuproot; + } else { + btrfs_set_opt(ctx->mount_opt_mask, NOLOGREPLAY); + btrfs_set_opt(ctx->mount_opt, NOLOGREPLAY); + } + break; +#ifndef CONFIG_BTRFS_FS_CHECK_INTEGRITY + case Opt_check_integrity_including_extent_data: + case Opt_check_integrity: + case Opt_check_integrity_print_mask: + return invalf(fc, "btrfs: Support for 'check_int*' not compiled in!"); +#endif +#ifndef CONFIG_BTRFS_FS_REF_VERIFY + case Opt_ref_verify: + return 0; +#endif +#ifndef CONFIG_BTRFS_DEBUG + case Opt_fragment: + return invalf(fc, "btrfs: Support for 'fragment' not compiled in!"); +#endif + } + + /* Map simple option-switching flags into 1-bit changes. */ + bit = btrfs_flag_map[result.key].bit; + if (bit) { + bit &= ~BTRFS_FLAG_MAP_PRESENT; + mask = 1 << bit; + ctx->mount_opt_mask |= mask; + ctx->mount_opt_explicit |= mask; + ctx->mount_opt &= ~mask; + if (enabled) + ctx->mount_opt |= mask; + } + + if (enabled) { + /* Turn on all the options that this one depends on. */ + for (i = 0; i < ARRAY_SIZE(btrfs_opt_dependencies); i++) { + unsigned int target = btrfs_opt_dependencies[i].target; + + if (btrfs_opt_dependencies[i].key != result.key) + continue; + bit = btrfs_flag_map[target].bit; + bit &= ~BTRFS_FLAG_MAP_PRESENT; + mask = 1 << bit; + if (!(ctx->mount_opt & mask)) + ctx->mount_opt_explicit &= ~mask; + ctx->mount_opt_mask |= mask; + ctx->mount_opt |= mask; + } + + /* Turn off all contradictory options. */ + for (i = 0; i < ARRAY_SIZE(btrfs_opt_contradictions); i++) { + unsigned int target = btrfs_opt_contradictions[i].target; + + if (btrfs_opt_contradictions[i].key != result.key) + continue; + bit = btrfs_flag_map[target].bit; + bit &= ~BTRFS_FLAG_MAP_PRESENT; + mask = 1 << bit; + if (!(ctx->mount_opt & mask)) + ctx->mount_opt_explicit &= ~mask; + ctx->mount_opt_mask |= mask; + ctx->mount_opt &= ~mask; + } + } else { + /* Cancel all options that would implicitly turn this on */ + for (i = 0; i < ARRAY_SIZE(btrfs_opt_dependencies); i++) { + unsigned int dep = btrfs_opt_dependencies[i].key; + + if (btrfs_opt_dependencies[i].target != result.key) + continue; + bit = btrfs_flag_map[dep].bit; + bit &= ~BTRFS_FLAG_MAP_PRESENT; + mask = 1 << bit; + if (ctx->mount_opt & mask) + ctx->mount_opt_explicit &= ~mask; + ctx->mount_opt_mask |= mask; + ctx->mount_opt &= ~mask; + } + } + + switch (result.key) { + case Opt_source: + if (fc->source) + return invalf(fc, "btrfs: Multiple sources specified"); + fc->source = param->string; + param->string = NULL; + break; + + case Opt_device: + devs = krealloc(ctx->devices, + sizeof(char *) * (ctx->nr_devices + 1), + GFP_KERNEL); + if (!devs) + return -ENOMEM; + devs[ctx->nr_devices] = param->string; + param->string = NULL; + ctx->devices = devs; + ctx->nr_devices++; + break; + + case Opt_subvol: + kfree(ctx->subvol_name); + ctx->subvol_name = param->string; /* NULL if negative */ + param->string = NULL; + break; + case Opt_subvolid: + /* we want the original fs_tree */ + if (result.uint_64 == 0) + ctx->subvol_objectid = BTRFS_FS_TREE_OBJECTID; + else + ctx->subvol_objectid = result.uint_64; + break; + case Opt_subvolrootid: + break; + + case Opt_compress: + case Opt_compress_force: + switch (result.uint_32) { + case Opt_compress__zlib_1 ... Opt_compress__zlib_9: + ctx->compress_type = BTRFS_COMPRESS_ZLIB; + ctx->compress_level = + result.uint_32 - Opt_compress__zlib_1 + 1; break; - case Opt_notreelog: - btrfs_clear_and_info(info, TREELOG, - "disabling tree log"); + case Opt_compress__zlib: + ctx->compress_type = BTRFS_COMPRESS_ZLIB; + ctx->compress_level = BTRFS_ZLIB_DEFAULT_LEVEL; break; - case Opt_treelog: - btrfs_set_and_info(info, TREELOG, - "enabling tree log"); + case Opt_compress__lzo: + ctx->compress_type = BTRFS_COMPRESS_LZO; break; - case Opt_norecovery: - case Opt_nologreplay: - btrfs_set_and_info(info, NOLOGREPLAY, - "disabling log replay at mount time"); + case Opt_compress__zstd: + ctx->compress_type = BTRFS_COMPRESS_ZSTD; break; - case Opt_flushoncommit: - btrfs_set_and_info(info, FLUSHONCOMMIT, - "turning on flush-on-commit"); + case Opt_compress__no: + ctx->compress_type = BTRFS_COMPRESS_NONE; break; - case Opt_noflushoncommit: - btrfs_clear_and_info(info, FLUSHONCOMMIT, - "turning off flush-on-commit"); + } + break; + + case Opt_thread_pool: + if (result.uint_32 == 0) + return invalf(fc, "btrfs: %s: Bad value", param->key); + ctx->thread_pool_size = result.uint_32; + break; + case Opt_max_inline: + ctx->max_inline = memparse(param->string, NULL); + break; + case Opt_alloc_start: + infof(fc, "btrfs: Option alloc_start is obsolete, ignored"); + break; + case Opt_metadata_ratio: + ctx->metadata_ratio = result.uint_32; + infof(fc, "btrfs: metadata ratio %u", ctx->metadata_ratio); + break; + + case Opt_space_cache: + btrfs_set_opt(ctx->mount_opt_mask, SPACE_CACHE); + btrfs_set_opt(ctx->mount_opt_mask, FREE_SPACE_TREE); + switch (result.uint_32) { + case Opt_space_cache__no: + btrfs_set_opt(ctx->mount_opt_explicit, SPACE_CACHE); + btrfs_set_opt(ctx->mount_opt_explicit, FREE_SPACE_TREE); + btrfs_clear_opt(ctx->mount_opt, SPACE_CACHE); + btrfs_clear_opt(ctx->mount_opt, FREE_SPACE_TREE); break; - case Opt_ratio: - ret = match_int(&args[0], &intarg); - if (ret) - goto out; - info->metadata_ratio = intarg; - btrfs_info(info, "metadata ratio %u", - info->metadata_ratio); + case Opt_space_cache__v1: + btrfs_set_opt(ctx->mount_opt_explicit, SPACE_CACHE); + btrfs_set_opt(ctx->mount_opt, SPACE_CACHE); + btrfs_clear_opt(ctx->mount_opt_explicit, FREE_SPACE_TREE); + btrfs_clear_opt(ctx->mount_opt, FREE_SPACE_TREE); break; - case Opt_discard: - btrfs_set_and_info(info, DISCARD, - "turning on discard"); + case Opt_space_cache__v2: + btrfs_clear_opt(ctx->mount_opt_explicit, SPACE_CACHE); + btrfs_clear_opt(ctx->mount_opt, SPACE_CACHE); + btrfs_set_opt(ctx->mount_opt_explicit, FREE_SPACE_TREE); + btrfs_set_opt(ctx->mount_opt, FREE_SPACE_TREE); break; - case Opt_nodiscard: - btrfs_clear_and_info(info, DISCARD, - "turning off discard"); - break; - case Opt_space_cache: - case Opt_space_cache_version: - if (token == Opt_space_cache || - strcmp(args[0].from, "v1") == 0) { - btrfs_clear_opt(info->mount_opt, - FREE_SPACE_TREE); - btrfs_set_and_info(info, SPACE_CACHE, - "enabling disk space caching"); - } else if (strcmp(args[0].from, "v2") == 0) { - btrfs_clear_opt(info->mount_opt, - SPACE_CACHE); - btrfs_set_and_info(info, FREE_SPACE_TREE, - "enabling free space tree"); - } else { - ret = -EINVAL; - goto out; - } - break; - case Opt_rescan_uuid_tree: - btrfs_set_opt(info->mount_opt, RESCAN_UUID_TREE); - break; - case Opt_no_space_cache: - if (btrfs_test_opt(info, SPACE_CACHE)) { - btrfs_clear_and_info(info, SPACE_CACHE, - "disabling disk space caching"); - } - if (btrfs_test_opt(info, FREE_SPACE_TREE)) { - btrfs_clear_and_info(info, FREE_SPACE_TREE, - "disabling free space tree"); - } - break; - case Opt_inode_cache: - btrfs_set_pending_and_info(info, INODE_MAP_CACHE, - "enabling inode map caching"); - break; - case Opt_noinode_cache: - btrfs_clear_pending_and_info(info, INODE_MAP_CACHE, - "disabling inode map caching"); - break; - case Opt_clear_cache: - btrfs_set_and_info(info, CLEAR_CACHE, - "force clearing of disk cache"); - break; - case Opt_user_subvol_rm_allowed: - btrfs_set_opt(info->mount_opt, USER_SUBVOL_RM_ALLOWED); - break; - case Opt_enospc_debug: - btrfs_set_opt(info->mount_opt, ENOSPC_DEBUG); - break; - case Opt_noenospc_debug: - btrfs_clear_opt(info->mount_opt, ENOSPC_DEBUG); - break; - case Opt_defrag: - btrfs_set_and_info(info, AUTO_DEFRAG, - "enabling auto defrag"); - break; - case Opt_nodefrag: - btrfs_clear_and_info(info, AUTO_DEFRAG, - "disabling auto defrag"); - break; - case Opt_recovery: - btrfs_warn(info, - "'recovery' is deprecated, use 'usebackuproot' instead"); - /* fall through */ - case Opt_usebackuproot: - btrfs_info(info, - "trying to use backup root at mount time"); - btrfs_set_opt(info->mount_opt, USEBACKUPROOT); - break; - case Opt_skip_balance: - btrfs_set_opt(info->mount_opt, SKIP_BALANCE); - break; + } + break; + + case Opt_acl: + if (enabled) + fc->sb_flags |= SB_POSIXACL; + else + fc->sb_flags &= ~SB_POSIXACL; + fc->sb_flags_mask |= SB_POSIXACL; + break; + #ifdef CONFIG_BTRFS_FS_CHECK_INTEGRITY - case Opt_check_integrity_including_extent_data: - btrfs_info(info, - "enabling check integrity including extent data"); - btrfs_set_opt(info->mount_opt, - CHECK_INTEGRITY_INCLUDING_EXTENT_DATA); - btrfs_set_opt(info->mount_opt, CHECK_INTEGRITY); - break; - case Opt_check_integrity: - btrfs_info(info, "enabling check integrity"); - btrfs_set_opt(info->mount_opt, CHECK_INTEGRITY); - break; - case Opt_check_integrity_print_mask: - ret = match_int(&args[0], &intarg); - if (ret) - goto out; - info->check_integrity_print_mask = intarg; - btrfs_info(info, "check_integrity_print_mask 0x%x", - info->check_integrity_print_mask); - break; -#else - case Opt_check_integrity_including_extent_data: - case Opt_check_integrity: - case Opt_check_integrity_print_mask: - btrfs_err(info, - "support for check_integrity* not compiled in!"); - ret = -EINVAL; - goto out; + case Opt_check_integrity_print_mask: + ctx->check_integrity_print_mask = result.uint_32; + break; #endif - case Opt_fatal_errors: - if (strcmp(args[0].from, "panic") == 0) - btrfs_set_opt(info->mount_opt, - PANIC_ON_FATAL_ERROR); - else if (strcmp(args[0].from, "bug") == 0) - btrfs_clear_opt(info->mount_opt, - PANIC_ON_FATAL_ERROR); - else { - ret = -EINVAL; - goto out; - } + + case Opt_fatal_errors: + ctx->mount_opt_explicit |= BTRFS_MOUNT_PANIC_ON_FATAL_ERROR; + switch (result.uint_32) { + case Opt_fatal_errors__bug: + ctx->mount_opt &= ~BTRFS_MOUNT_PANIC_ON_FATAL_ERROR; break; - case Opt_commit_interval: - intarg = 0; - ret = match_int(&args[0], &intarg); - if (ret) - goto out; - if (intarg == 0) { - btrfs_info(info, - "using default commit interval %us", - BTRFS_DEFAULT_COMMIT_INTERVAL); - intarg = BTRFS_DEFAULT_COMMIT_INTERVAL; - } else if (intarg > 300) { - btrfs_warn(info, "excessive commit interval %d", - intarg); - } - info->commit_interval = intarg; + case Opt_fatal_errors__panic: + ctx->mount_opt |= BTRFS_MOUNT_PANIC_ON_FATAL_ERROR; break; + } + break; + case Opt_commit_interval: + if (result.uint_32 == 0) { + infof(fc, "btrfs: Using default commit interval %us", + BTRFS_DEFAULT_COMMIT_INTERVAL); + result.uint_32 = BTRFS_DEFAULT_COMMIT_INTERVAL; + } else if (result.uint_32 > 300) { + warnf(fc, "btrfs: Excessive commit interval %u", + result.uint_32); + } + ctx->commit_interval = result.uint_32; + break; + #ifdef CONFIG_BTRFS_DEBUG - case Opt_fragment_all: - btrfs_info(info, "fragmenting all space"); - btrfs_set_opt(info->mount_opt, FRAGMENT_DATA); - btrfs_set_opt(info->mount_opt, FRAGMENT_METADATA); + case Opt_fragment: + mask = 0; + switch (result.uint_32) { + case Opt_fragment__all: + mask = BTRFS_MOUNT_FRAGMENT_DATA; + mask |= BTRFS_MOUNT_FRAGMENT_METADATA; break; - case Opt_fragment_metadata: - btrfs_info(info, "fragmenting metadata"); - btrfs_set_opt(info->mount_opt, - FRAGMENT_METADATA); + case Opt_fragment__metadata: + mask = BTRFS_MOUNT_FRAGMENT_METADATA; break; - case Opt_fragment_data: - btrfs_info(info, "fragmenting data"); - btrfs_set_opt(info->mount_opt, FRAGMENT_DATA); - break; -#endif -#ifdef CONFIG_BTRFS_FS_REF_VERIFY - case Opt_ref_verify: - btrfs_info(info, "doing ref verification"); - btrfs_set_opt(info->mount_opt, REF_VERIFY); - break; -#endif - case Opt_err: - btrfs_info(info, "unrecognized mount option '%s'", p); - ret = -EINVAL; - goto out; - default: + case Opt_fragment__data: + mask |= BTRFS_MOUNT_FRAGMENT_DATA; break; } + ctx->mount_opt |= mask; + ctx->mount_opt_mask |= mask; + ctx->mount_opt_explicit |= mask; + break; +#endif + default: + break; } -check: - /* - * Extra check for current option against current flag - */ - if (btrfs_test_opt(info, NOLOGREPLAY) && !(new_flags & SB_RDONLY)) { - btrfs_err(info, - "nologreplay must be used with ro mount option"); - ret = -EINVAL; - } -out: - if (btrfs_fs_compat_ro(info, FREE_SPACE_TREE) && - !btrfs_test_opt(info, FREE_SPACE_TREE) && - !btrfs_test_opt(info, CLEAR_CACHE)) { - btrfs_err(info, "cannot disable free space tree"); - ret = -EINVAL; + return 0; +} + +static int btrfs_validate(struct fs_context *fc) +{ + struct btrfs_fs_context *ctx = fc->fs_private; + + if (btrfs_raw_test_opt(ctx->mount_opt_mask, NOLOGREPLAY) && + btrfs_raw_test_opt(ctx->mount_opt, NOLOGREPLAY) && + !(fc->sb_flags & SB_RDONLY)) + return invalf(fc, "nologreplay must be used with ro mount option"); + return 0; +} + +static int btrfs_dup_context(struct fs_context *fc, struct fs_context *src_fc) +{ + struct btrfs_fs_context *ctx, *src = src_fc->fs_private; + int i; + + ctx = kmemdup(src, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + ctx->subvol_name = NULL; + ctx->devices = NULL; + ctx->root_mnt = NULL; + + if (src->subvol_name) { + ctx->subvol_name = kstrdup(src->subvol_name, GFP_KERNEL); + if (!ctx->subvol_name) + goto nomem_ctx; } - if (!ret && btrfs_test_opt(info, SPACE_CACHE)) - btrfs_info(info, "disk space caching is enabled"); - if (!ret && btrfs_test_opt(info, FREE_SPACE_TREE)) - btrfs_info(info, "using free space tree"); - return ret; + + if (ctx->nr_devices) { + ctx->devices = kcalloc(ctx->nr_devices, sizeof(char *), GFP_KERNEL); + if (!ctx->devices) + goto nomem_sub; + for (i = 0; i < ctx->nr_devices; i++) { + ctx->devices[i] = kstrdup(src->devices[i], GFP_KERNEL); + if (!ctx->devices[i]) + goto nomem_devs; + } + } + + if (src_fc->source) { + fc->source = kstrdup(src_fc->source, GFP_KERNEL); + if (!fc->source) + goto nomem_devs; + } + + fc->fs_private = ctx; + return 0; + +nomem_devs: + for (i = 0; i < ctx->nr_devices; i++) + kfree(ctx->devices[i]); + kfree(ctx->devices); +nomem_sub: + kfree(ctx->subvol_name); +nomem_ctx: + kfree(ctx); + return -ENOMEM; +} + +static void btrfs_free_context(struct fs_context *fc) +{ + struct btrfs_fs_context *ctx = fc->fs_private; + struct btrfs_fs_info *info = fc->s_fs_info; + int i; + + if (info) + free_fs_info(info); + if (ctx) { + mntput(ctx->root_mnt); + if (ctx->devices) { + for (i = 0; i < ctx->nr_devices; i++) + kfree(ctx->devices[i]); + kfree(ctx->devices); + } + kfree(ctx->subvol_name); + kfree(ctx); + } +} + +static struct fs_context_operations btrfs_context_ops = { + .free = btrfs_free_context, + .dup = btrfs_dup_context, + .parse_param = btrfs_parse_param, + .validate = btrfs_validate, + .get_tree = btrfs_get_tree, + .reconfigure = btrfs_reconfigure, +}; + +/* + * Set up the filesystem configuration context. + */ +int btrfs_init_fs_context(struct fs_context *fc, struct dentry *reference) +{ + struct btrfs_fs_context *ctx; + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + fc->ops = &btrfs_context_ops; + fc->fs_private = ctx; + return 0; +} + +static const char *btrfs_options[32] = { + [BTRFS_MOUNT_AUTO_DEFRAG] = "auto defrag", + [BTRFS_MOUNT_BARRIER] = "barriers", + [BTRFS_MOUNT_DATACOW] = "datacow", + [BTRFS_MOUNT_DATASUM] = "datasum", + [BTRFS_MOUNT_DEGRADED] = "degraded mounts", + [BTRFS_MOUNT_DISCARD] = "discard", + [BTRFS_MOUNT_FLUSHONCOMMIT] = "flush-on-commit", + [BTRFS_MOUNT_FREE_SPACE_TREE] = "free space tree", + [BTRFS_MOUNT_INODE_MAP_CACHE] = "inode map caching", + [BTRFS_MOUNT_NOLOGREPLAY] = "no log replay at mount time", + [BTRFS_MOUNT_REF_VERIFY] = "ref verification", + [BTRFS_MOUNT_SPACE_CACHE] = "disk space caching", + [BTRFS_MOUNT_SSD] = "ssd optimizations", + [BTRFS_MOUNT_SSD_SPREAD] = "spread ssd allocation scheme", + [BTRFS_MOUNT_TREELOG] = "tree log", +}; + +/* + * Apply the configuration to a superblock. + */ +void btrfs_apply_configuration(struct fs_context *fc, struct super_block *sb) +{ + struct btrfs_fs_context *ctx = fc->fs_private; + struct btrfs_fs_info *info = sb->s_fs_info; + unsigned int changes = (ctx->mount_opt ^ info->mount_opt) & ctx->mount_opt_mask; + unsigned int explicit_changes = changes & ctx->mount_opt_explicit; + unsigned int implicit_changes = changes & ~ctx->mount_opt_explicit; + unsigned int mask, tmp; + int i; + + for (tmp = explicit_changes; tmp; tmp &= ~(1 << i)) { + i = __ffs(tmp); + if (!btrfs_options[i]) + continue; + if (ctx->mount_opt & (1 << i )) + btrfs_info(info, "Enabling %s", btrfs_options[i]); + else + btrfs_info(info, "Disabling %s", btrfs_options[i]); + } + + for (tmp = implicit_changes; tmp; tmp &= ~(1 << i)) { + i = __ffs(tmp); + if (!btrfs_options[i]) + continue; + if (ctx->mount_opt & (1 << i )) + btrfs_info(info, "Implicitly enabling %s", btrfs_options[i]); + else + btrfs_info(info, "Implicitly disabling %s", btrfs_options[i]); + } + + if (btrfs_raw_test_opt(changes, COMPRESS) || + btrfs_raw_test_opt(changes, FORCE_COMPRESS)) { + if (btrfs_raw_test_opt(ctx->mount_opt, COMPRESS)) + btrfs_info(info, "Enabling %s%s compression, level %d", + btrfs_raw_test_opt(changes, FORCE_COMPRESS) ? + "Forced " : "", + btrfs_compress_type2str(ctx->compress_type), + ctx->compress_level); + else if (btrfs_raw_test_opt(explicit_changes, COMPRESS)) + btrfs_info(info, "Disabling compression"); + else + btrfs_info(info, "Implicitly disabling compression"); + } + + if (test_bit(Opt_max_inline, ctx->specified)) { + u64 max_inline = min_t(u64, ctx->max_inline, info->sectorsize); + + ctx->max_inline = max_inline; + if (max_inline != info->max_inline) + btrfs_info(info, "max_inline at %llu", ctx->max_inline); + } + + if (test_bit(Opt_metadata_ratio, ctx->specified) && + ctx->metadata_ratio != info->metadata_ratio) + btrfs_info(info, "Metadata ratio %u", info->metadata_ratio); + + if (btrfs_raw_test_opt(ctx->mount_opt & explicit_changes, CLEAR_CACHE)) + btrfs_info(info, "Force clearing of disk cache"); + if (btrfs_raw_test_opt(ctx->mount_opt & explicit_changes, USEBACKUPROOT)) + btrfs_info(info, "Trying to use backup root at mount time"); + +#ifdef CONFIG_BTRFS_FS_CHECK_INTEGRITY + switch (ctx->mount_opt & explicit_changes & + ((1 << BTRFS_MOUNT_CHECK_INTEGRITY) | + (1 << BTRFS_MOUNT_CHECK_INTEGRITY_INCLUDING_EXTENT_DATA))) { + case (1 << BTRFS_MOUNT_CHECK_INTEGRITY): + btrfs_info(info, "Enabling check integrity"); + break; + case ((1 << BTRFS_MOUNT_CHECK_INTEGRITY) | + (1 << BTRFS_MOUNT_CHECK_INTEGRITY_INCLUDING_EXTENT_DATA)): + btrfs_info(info, "Enabling check integrity including extent data"); + break; + } + if (test_bit(Opt_check_integrity_print_mask, ctx->specified) && + ctx->check_integrity_print_mask != info->check_integrity_print_mask) + btrfs_info(info, "check_integrity_print_mask 0x%x", + ctx->check_integrity_print_mask); +#endif + +#ifdef CONFIG_BTRFS_DEBUG + switch (ctx->mount_opt & explicit_changes & + ((1 << BTRFS_MOUNT_FRAGMENT_DATA) | + (1 << BTRFS_MOUNT_FRAGMENT_METADATA))) { + case 0: + break; + case (1 << BTRFS_MOUNT_FRAGMENT_DATA): + btrfs_info(info, "fragmenting data"); + break; + case (1 << BTRFS_MOUNT_FRAGMENT_METADATA): + btrfs_info(info, "fragmenting metadata"); + break; + default: + btrfs_info(info, "fragmenting all space"); + break; + } +#endif + + /* Actually apply the options */ + mask = changes; + mask &= ~(1 << BTRFS_MOUNT_INODE_MAP_CACHE); + tmp = READ_ONCE(info->mount_opt); + tmp &= ~mask; + tmp |= ctx->mount_opt & mask; + + info->compress_level = ctx->compress_level; + info->compress_type = ctx->compress_type; + WRITE_ONCE(info->mount_opt, tmp); + + if (btrfs_raw_test_opt(explicit_changes, INODE_MAP_CACHE)) { + if (btrfs_test_opt(info, INODE_MAP_CACHE)) { + btrfs_set_pending((info), SET_INODE_MAP_CACHE); + btrfs_clear_pending((info), CLEAR_INODE_MAP_CACHE); + } else { + btrfs_set_pending((info), CLEAR_INODE_MAP_CACHE); + btrfs_clear_pending((info), SET_INODE_MAP_CACHE); + } + } + + if (test_bit(Opt_commit_interval, ctx->specified)) + info->commit_interval = ctx->commit_interval; + if (test_bit(Opt_thread_pool, ctx->specified)) + info->thread_pool_size = ctx->thread_pool_size; +#ifdef CONFIG_BTRFS_FS_CHECK_INTEGRITY + if (test_bit(Opt_check_integrity_print_mask, ctx->specified)) + info->check_integrity_print_mask = ctx->check_integrity_print_mask; +#endif + if (test_bit(Opt_metadata_ratio, ctx->specified)) + info->metadata_ratio = info->metadata_ratio; + if (test_bit(Opt_max_inline, ctx->specified)) + info->max_inline = ctx->max_inline; } /* - * Parse mount options that are required early in the mount process. - * - * All other options will be parsed on much later in the mount process and - * only when we need to allocate a new super block. + * Display the options for /proc/mounts */ -int btrfs_parse_device_options(const char *options, fmode_t flags, void *holder) -{ - substring_t args[MAX_OPT_ARGS]; - char *device_name, *opts, *orig, *p; - struct btrfs_device *device = NULL; - int error = 0; - - lockdep_assert_held(&uuid_mutex); - - if (!options) - return 0; - - /* - * strsep changes the string, duplicate it because btrfs_parse_options - * gets called later - */ - opts = kstrdup(options, GFP_KERNEL); - if (!opts) - return -ENOMEM; - orig = opts; - - while ((p = strsep(&opts, ",")) != NULL) { - int token; - - if (!*p) - continue; - - token = match_token(p, tokens, args); - if (token == Opt_device) { - device_name = match_strdup(&args[0]); - if (!device_name) { - error = -ENOMEM; - goto out; - } - device = btrfs_scan_one_device(device_name, flags, - holder); - kfree(device_name); - if (IS_ERR(device)) { - error = PTR_ERR(device); - goto out; - } - } - } - -out: - kfree(orig); - return error; -} - -/* - * Parse mount options that are related to subvolume id - * - * The value is later passed to mount_subvol() - */ -int btrfs_parse_subvol_options(const char *options, char **subvol_name, - u64 *subvol_objectid) -{ - substring_t args[MAX_OPT_ARGS]; - char *opts, *orig, *p; - int error = 0; - u64 subvolid; - - if (!options) - return 0; - - /* - * strsep changes the string, duplicate it because - * btrfs_parse_device_options gets called later - */ - opts = kstrdup(options, GFP_KERNEL); - if (!opts) - return -ENOMEM; - orig = opts; - - while ((p = strsep(&opts, ",")) != NULL) { - int token; - if (!*p) - continue; - - token = match_token(p, tokens, args); - switch (token) { - case Opt_subvol: - kfree(*subvol_name); - *subvol_name = match_strdup(&args[0]); - if (!*subvol_name) { - error = -ENOMEM; - goto out; - } - break; - case Opt_subvolid: - error = match_u64(&args[0], &subvolid); - if (error) - goto out; - - /* we want the original fs_tree */ - if (subvolid == 0) - subvolid = BTRFS_FS_TREE_OBJECTID; - - *subvol_objectid = subvolid; - break; - case Opt_subvolrootid: - pr_warn("BTRFS: 'subvolrootid' mount option is deprecated and has no effect\n"); - break; - default: - break; - } - } - -out: - kfree(orig); - return error; -} - int btrfs_show_options(struct seq_file *seq, struct dentry *dentry) { struct btrfs_fs_info *info = btrfs_sb(dentry->d_sb);
diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index fc3eda2..2ac2fd2 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c
@@ -17,7 +17,8 @@ #include <linux/writeback.h> #include <linux/statfs.h> #include <linux/compat.h> -#include <linux/parser.h> +#include <linux/fs_context.h> +#include <linux/fs_parser.h> #include <linux/ctype.h> #include <linux/namei.h> #include <linux/miscdevice.h> @@ -50,19 +51,6 @@ static const struct super_operations btrfs_super_ops; -/* - * Types for mounting the default subvolume and a subvolume explicitly - * requested by subvol=/path. That way the callchain is straightforward and we - * don't have to play tricks with the mount options and recursive calls to - * btrfs_mount. - * - * The new btrfs_root_fs_type also servers as a tag for the bdev_holder. - */ -static struct file_system_type btrfs_fs_type; -static struct file_system_type btrfs_root_fs_type; - -static int btrfs_remount(struct super_block *sb, int *flags, char *data); - const char *btrfs_decode_error(int errno) { char *errstr = "unknown"; @@ -468,9 +456,9 @@ static int get_default_subvol_objectid(struct btrfs_fs_info *fs_info, u64 *objec return 0; } -static int btrfs_fill_super(struct super_block *sb, - struct btrfs_fs_devices *fs_devices, - void *data) +static int btrfs_fill_super(struct fs_context *fc, + struct super_block *sb, + struct btrfs_fs_devices *fs_devices) { struct inode *inode; struct btrfs_fs_info *fs_info = btrfs_sb(sb); @@ -496,7 +484,7 @@ static int btrfs_fill_super(struct super_block *sb, return err; } - err = open_ctree(sb, fs_devices, (char *)data); + err = open_ctree(fc, sb, fs_devices); if (err) { btrfs_err(fs_info, "open_ctree failed"); return err; @@ -569,22 +557,14 @@ int btrfs_sync_fs(struct super_block *sb, int wait) return btrfs_commit_transaction(trans); } -static int btrfs_test_super(struct super_block *s, void *data) +static int btrfs_test_super(struct super_block *s, struct fs_context *fc) { - struct btrfs_fs_info *p = data; + struct btrfs_fs_info *p = fc->s_fs_info; struct btrfs_fs_info *fs_info = btrfs_sb(s); return fs_info->fs_devices == p->fs_devices; } -static int btrfs_set_super(struct super_block *s, void *data) -{ - int err = set_anon_super(s, data); - if (!err) - s->s_fs_info = data; - return err; -} - /* * subvolumes are identified by ino 256 */ @@ -595,34 +575,32 @@ static inline int is_subvolume_inode(struct inode *inode) return 0; } -static struct dentry *mount_subvol(const char *subvol_name, u64 subvol_objectid, - const char *device_name, struct vfsmount *mnt) +static int mount_subvol(struct fs_context *fc) { + struct btrfs_fs_context *ctx = fc->fs_private; + struct vfsmount *mnt = ctx->root_mnt; struct dentry *root; int ret; - if (!subvol_name) { - if (!subvol_objectid) { + if (!ctx->subvol_name) { + char *subvol_name; + + if (!ctx->subvol_objectid) { ret = get_default_subvol_objectid(btrfs_sb(mnt->mnt_sb), - &subvol_objectid); - if (ret) { - root = ERR_PTR(ret); - goto out; - } + &ctx->subvol_objectid); + if (ret < 0) + return ret; } subvol_name = get_subvol_name_from_objectid(btrfs_sb(mnt->mnt_sb), - subvol_objectid); - if (IS_ERR(subvol_name)) { - root = ERR_CAST(subvol_name); - subvol_name = NULL; - goto out; - } - + ctx->subvol_objectid); + if (IS_ERR(subvol_name)) + return PTR_ERR(subvol_name); + ctx->subvol_name = subvol_name; } - root = mount_subtree(mnt, subvol_name); - /* mount_subtree() drops our reference on the vfsmount. */ - mnt = NULL; + root = mount_subtree(mnt, ctx->subvol_name); + /* mount_subtree() dropped our reference on the vfsmount. */ + ctx->root_mnt = NULL; if (!IS_ERR(root)) { struct super_block *s = root->d_sb; @@ -633,10 +611,11 @@ static struct dentry *mount_subvol(const char *subvol_name, u64 subvol_objectid, ret = 0; if (!is_subvolume_inode(root_inode)) { btrfs_err(fs_info, "'%s' is not a valid subvolume", - subvol_name); + ctx->subvol_name); ret = -EINVAL; } - if (subvol_objectid && root_objectid != subvol_objectid) { + if (ctx->subvol_objectid && + root_objectid != ctx->subvol_objectid) { /* * This will also catch a race condition where a * subvolume which was passed by ID is renamed and @@ -644,56 +623,19 @@ static struct dentry *mount_subvol(const char *subvol_name, u64 subvol_objectid, */ btrfs_err(fs_info, "subvol '%s' does not match subvolid %llu", - subvol_name, subvol_objectid); + ctx->subvol_name, ctx->subvol_objectid); ret = -EINVAL; } - if (ret) { + if (ret < 0) { dput(root); - root = ERR_PTR(ret); deactivate_locked_super(s); + goto out; } } + fc->root = root; + ret = 0; out: - mntput(mnt); - kfree(subvol_name); - return root; -} - -static int parse_security_options(char *orig_opts, - struct security_mnt_opts *sec_opts) -{ - return security_sb_eat_lsm_opts(orig_opts, sec_opts); -} - -static int setup_security_options(struct btrfs_fs_info *fs_info, - struct super_block *sb, - struct security_mnt_opts *sec_opts) -{ - int ret = 0; - - /* - * Call security_sb_set_mnt_opts() to check whether new sec_opts - * is valid. - */ - ret = security_sb_set_mnt_opts(sb, sec_opts, 0, NULL); - if (ret) - return ret; - -#ifdef CONFIG_SECURITY - if (!fs_info->security_opts.num_mnt_opts) { - /* first time security setup, copy sec_opts to fs_info */ - memcpy(&fs_info->security_opts, sec_opts, sizeof(*sec_opts)); - } else { - /* - * Since SELinux (the only one supporting security_mnt_opts) - * does NOT support changing context during remount/mount of - * the same sb, this must be the same or part of the same - * security options, just free it. - */ - security_free_mnt_opts(sec_opts); - } -#endif return ret; } @@ -711,6 +653,21 @@ static struct btrfs_fs_info *btrfs_alloc_fs_info(void) btrfs_set_opt(fs_info->mount_opt, DATACOW); btrfs_set_opt(fs_info->mount_opt, BARRIER); btrfs_set_opt(fs_info->mount_opt, TREELOG); + + fs_info->commit_interval = BTRFS_DEFAULT_COMMIT_INTERVAL; + fs_info->max_inline = BTRFS_DEFAULT_MAX_INLINE; + fs_info->metadata_ratio = 0; + fs_info->thread_pool_size = min_t(unsigned long, + num_online_cpus() + 2, 8); +#ifdef CONFIG_BTRFS_FS_CHECK_INTEGRITY + fs_info->check_integrity_print_mask = 0; +#endif + + /* + * In the long term, we'll store the compression type in the super + * block, and it'll be used for per file compression control. + */ + fs_info->compress_type = BTRFS_COMPRESS_ZLIB; } return fs_info; @@ -722,28 +679,20 @@ static struct btrfs_fs_info *btrfs_alloc_fs_info(void) * Note: This is based on mount_bdev from fs/super.c with a few additions * for multiple device setup. Make sure to keep it in sync. */ -static struct dentry *btrfs_mount_root(struct file_system_type *fs_type, - int flags, const char *device_name, void *data) +static int btrfs_root_get_tree(struct fs_context *fc) { + struct btrfs_fs_context *ctx = fc->fs_private; struct block_device *bdev = NULL; struct super_block *s; struct btrfs_device *device = NULL; struct btrfs_fs_devices *fs_devices = NULL; struct btrfs_fs_info *fs_info = NULL; - struct security_mnt_opts new_sec_opts; fmode_t mode = FMODE_READ; - int error = 0; + int error = 0, i; - if (!(flags & SB_RDONLY)) + if (!(fc->sb_flags & SB_RDONLY)) mode |= FMODE_WRITE; - security_init_mnt_opts(&new_sec_opts); - if (data) { - error = parse_security_options(data, &new_sec_opts); - if (error) - return ERR_PTR(error); - } - /* * Setup a dummy root and fs_info for test/set super. This is because * we don't actually fill this stuff out until open_ctree, but we need @@ -753,25 +702,30 @@ static struct dentry *btrfs_mount_root(struct file_system_type *fs_type, fs_info = btrfs_alloc_fs_info(); if (!fs_info) { error = -ENOMEM; - goto error_sec_opts; + goto error; } fs_info->super_copy = kzalloc(BTRFS_SUPER_INFO_SIZE, GFP_KERNEL); fs_info->super_for_commit = kzalloc(BTRFS_SUPER_INFO_SIZE, GFP_KERNEL); - security_init_mnt_opts(&fs_info->security_opts); if (!fs_info->super_copy || !fs_info->super_for_commit) { error = -ENOMEM; goto error_fs_info; } mutex_lock(&uuid_mutex); - error = btrfs_parse_device_options(data, mode, fs_type); - if (error) { - mutex_unlock(&uuid_mutex); - goto error_fs_info; + + if (ctx->devices) { + for (i = 0; i < ctx->nr_devices; i++) { + device = btrfs_scan_one_device(ctx->devices[i], mode); + if (IS_ERR(device)) { + mutex_unlock(&uuid_mutex); + error = PTR_ERR(device); + goto error_fs_info; + } + } } - device = btrfs_scan_one_device(device_name, mode, fs_type); + device = btrfs_scan_one_device(fc->source, mode); if (IS_ERR(device)) { mutex_unlock(&uuid_mutex); error = PTR_ERR(device); @@ -781,59 +735,117 @@ static struct dentry *btrfs_mount_root(struct file_system_type *fs_type, fs_devices = device->fs_devices; fs_info->fs_devices = fs_devices; - error = btrfs_open_devices(fs_devices, mode, fs_type); + error = btrfs_open_devices(fs_devices, mode); mutex_unlock(&uuid_mutex); if (error) goto error_fs_info; - if (!(flags & SB_RDONLY) && fs_devices->rw_devices == 0) { + if (!(fc->sb_flags & SB_RDONLY) && fs_devices->rw_devices == 0) { error = -EACCES; goto error_close_devices; } + if (btrfs_fs_compat_ro(fs_info, FREE_SPACE_TREE) && + !btrfs_test_opt(fs_info, FREE_SPACE_TREE) && + !btrfs_test_opt(fs_info, CLEAR_CACHE)) { + btrfs_err(fs_info, "cannot disable free space tree"); + error = -EINVAL; + goto error_close_devices; + } + if (btrfs_test_opt(fs_info, SPACE_CACHE)) + btrfs_info(fs_info, "disk space caching is enabled"); + if (btrfs_test_opt(fs_info, FREE_SPACE_TREE)) + btrfs_info(fs_info, "using free space tree"); + bdev = fs_devices->latest_bdev; - s = sget(fs_type, btrfs_test_super, btrfs_set_super, flags | SB_NOSEC, - fs_info); + fc->s_fs_info = fs_info; + s = sget_fc(fc, btrfs_test_super, set_anon_super_fc); if (IS_ERR(s)) { error = PTR_ERR(s); goto error_close_devices; } if (s->s_root) { - btrfs_close_devices(fs_devices); - free_fs_info(fs_info); - if ((flags ^ s->s_flags) & SB_RDONLY) + btrfs_close_devices(fs_info->fs_devices); + if ((fc->sb_flags ^ s->s_flags) & SB_RDONLY) error = -EBUSY; } else { snprintf(s->s_id, sizeof(s->s_id), "%pg", bdev); - btrfs_sb(s)->bdev_holder = fs_type; - error = btrfs_fill_super(s, fs_devices, data); + error = btrfs_fill_super(fc, s, fs_devices); } - if (error) { - deactivate_locked_super(s); - goto error_sec_opts; - } + if (error) + goto error_super; - fs_info = btrfs_sb(s); - error = setup_security_options(fs_info, s, &new_sec_opts); - if (error) { - deactivate_locked_super(s); - goto error_sec_opts; - } + fc->root = dget(s->s_root); + return 0; - return dget(s->s_root); - +error_super: + deactivate_locked_super(s); error_close_devices: btrfs_close_devices(fs_devices); error_fs_info: free_fs_info(fs_info); -error_sec_opts: - security_free_mnt_opts(&new_sec_opts); - return ERR_PTR(error); +error: + return error; +} + +static int btrfs_mount_root(struct fs_context *fc, unsigned int rdonly) +{ + struct btrfs_fs_context *ctx, *root_ctx; + struct fs_context *root_fc; + struct vfsmount *root_mnt; + int ret; + + root_fc = vfs_dup_fs_context(fc, FS_CONTEXT_FOR_ROOT_MOUNT); + if (IS_ERR(root_fc)) + return PTR_ERR(root_fc); + + root_fc->sb_flags &= ~SB_RDONLY; + root_fc->sb_flags |= rdonly | SB_NOSEC; + root_ctx = root_fc->fs_private; + root_ctx->root_mnt = NULL; + root_ctx->root = true; + + ret = vfs_get_tree(root_fc); + if (ret < 0) + goto error_fc; + + root_mnt = vfs_create_mount(root_fc, 0); + if (IS_ERR(root_mnt)) { + ret = PTR_ERR(root_mnt); + goto error_fc; + } + + ctx = fc->fs_private; + ctx->root_mnt = root_mnt; + ret = 0; + +error_fc: + put_fs_context(root_fc); + return ret; +} + +static int btrfs_reconfigure_root_to_rw(struct fs_context *fc, + struct super_block *sb) +{ + int error; + struct fs_context root_fc = { + .purpose = FS_CONTEXT_FOR_RECONFIGURE, + .fs_type = sb->s_type, + .root = sb->s_root, + .log = fc->log, + .sb_flags = 0, + .sb_flags_mask = SB_RDONLY, + }; + + down_write(&sb->s_umount); + error = btrfs_reconfigure(&root_fc); + up_write(&sb->s_umount); + return error; } /* - * Mount function which is called by VFS layer. + * Superblock creation function which is called by VFS layer. * * In order to allow mounting a subvolume directly, btrfs uses mount_subtree() * which needs vfsmount* of device's root (/). This means device's root has to @@ -842,77 +854,49 @@ static struct dentry *btrfs_mount_root(struct file_system_type *fs_type, * Operation flow: * 1. Parse subvol id related options for later use in mount_subvol(). * - * 2. Mount device's root (/) by calling vfs_kern_mount(). - * - * NOTE: vfs_kern_mount() is used by VFS to call btrfs_mount() in the - * first place. In order to avoid calling btrfs_mount() again, we use - * different file_system_type which is not registered to VFS by - * register_filesystem() (btrfs_root_fs_type). As a result, - * btrfs_mount_root() is called. The return value will be used by - * mount_subtree() in mount_subvol(). + * 2. Mount device's root (/). * * 3. Call mount_subvol() to get the dentry of subvolume. Since there is * "btrfs subvolume set-default", mount_subvol() is called always. */ -static struct dentry *btrfs_mount(struct file_system_type *fs_type, int flags, - const char *device_name, void *data) +int btrfs_get_tree(struct fs_context *fc) { - struct vfsmount *mnt_root; - struct dentry *root; + struct btrfs_fs_context *ctx = fc->fs_private; fmode_t mode = FMODE_READ; - char *subvol_name = NULL; - u64 subvol_objectid = 0; int error = 0; - if (!(flags & SB_RDONLY)) + if (ctx->root) + return btrfs_root_get_tree(fc); + + if (!(fc->sb_flags & SB_RDONLY)) mode |= FMODE_WRITE; - error = btrfs_parse_subvol_options(data, &subvol_name, - &subvol_objectid); - if (error) { - kfree(subvol_name); - return ERR_PTR(error); - } - /* mount device's root (/) */ - mnt_root = vfs_kern_mount(&btrfs_root_fs_type, flags, device_name, data); - if (PTR_ERR_OR_ZERO(mnt_root) == -EBUSY) { - if (flags & SB_RDONLY) { - mnt_root = vfs_kern_mount(&btrfs_root_fs_type, - flags & ~SB_RDONLY, device_name, data); - } else { - mnt_root = vfs_kern_mount(&btrfs_root_fs_type, - flags | SB_RDONLY, device_name, data); - if (IS_ERR(mnt_root)) { - root = ERR_CAST(mnt_root); - goto out; - } + error = btrfs_mount_root(fc, fc->sb_flags & SB_RDONLY); + if (error < 0) { + if (error != -EBUSY) + return error; - down_write(&mnt_root->mnt_sb->s_umount); - error = btrfs_remount(mnt_root->mnt_sb, &flags, NULL); - up_write(&mnt_root->mnt_sb->s_umount); - if (error < 0) { - root = ERR_PTR(error); - mntput(mnt_root); - goto out; - } + error = btrfs_mount_root( + fc, (fc->sb_flags & SB_RDONLY) ^ SB_RDONLY); + if (error < 0) + return error; + + if (!(fc->sb_flags & SB_RDONLY)) { + error = btrfs_reconfigure_root_to_rw(fc, ctx->root_mnt->mnt_sb); + if (error < 0) + return error; } } - if (IS_ERR(mnt_root)) { - root = ERR_CAST(mnt_root); - goto out; - } - /* mount_subvol() will free subvol_name and mnt_root */ - root = mount_subvol(subvol_name, subvol_objectid, device_name, mnt_root); - -out: - return root; + return mount_subvol(fc); } static void btrfs_resize_thread_pool(struct btrfs_fs_info *fs_info, - u32 new_pool_size, u32 old_pool_size) + u32 new_pool_size) { + u32 old_pool_size = fs_info->thread_pool_size; + if (new_pool_size == old_pool_size) return; @@ -943,15 +927,16 @@ static inline void btrfs_remount_prepare(struct btrfs_fs_info *fs_info) } static inline void btrfs_remount_begin(struct btrfs_fs_info *fs_info, - unsigned long old_opts, int flags) + unsigned long old_opts, + struct fs_context *fc) { if (btrfs_raw_test_opt(old_opts, AUTO_DEFRAG) && (!btrfs_raw_test_opt(fs_info->mount_opt, AUTO_DEFRAG) || - (flags & SB_RDONLY))) { + (fc->sb_flags & SB_RDONLY))) { /* wait for any defraggers to finish */ wait_event(fs_info->transaction_wait, (atomic_read(&fs_info->defrag_running) == 0)); - if (flags & SB_RDONLY) + if (fc->sb_flags & SB_RDONLY) sync_filesystem(fs_info->sb); } } @@ -971,8 +956,16 @@ static inline void btrfs_remount_cleanup(struct btrfs_fs_info *fs_info, clear_bit(BTRFS_FS_STATE_REMOUNTING, &fs_info->fs_state); } -static int btrfs_remount(struct super_block *sb, int *flags, char *data) +/* + * Change the configuration of an active superblock according to the supplied + * parameters. Note that the parameter pointer (fc->fs_private) may be NULL in + * the case of umount detach, emergency remount R/O and get_tree remounting as + * R/W. + */ +int btrfs_reconfigure(struct fs_context *fc) { + struct btrfs_fs_context *ctx = fc->fs_private; + struct super_block *sb = fc->root->d_sb; struct btrfs_fs_info *fs_info = btrfs_sb(sb); struct btrfs_root *root = fs_info->tree_root; unsigned old_flags = sb->s_flags; @@ -985,34 +978,16 @@ static int btrfs_remount(struct super_block *sb, int *flags, char *data) sync_filesystem(sb); btrfs_remount_prepare(fs_info); + if (ctx) + btrfs_apply_configuration(fc, sb); + btrfs_remount_begin(fs_info, old_opts, fc); + if (ctx) + btrfs_resize_thread_pool(fs_info, ctx->thread_pool_size); - if (data) { - struct security_mnt_opts new_sec_opts; - - security_init_mnt_opts(&new_sec_opts); - ret = parse_security_options(data, &new_sec_opts); - if (ret) - goto restore; - ret = setup_security_options(fs_info, sb, - &new_sec_opts); - if (ret) { - security_free_mnt_opts(&new_sec_opts); - goto restore; - } - } - - ret = btrfs_parse_options(fs_info, data, *flags); - if (ret) - goto restore; - - btrfs_remount_begin(fs_info, old_opts, *flags); - btrfs_resize_thread_pool(fs_info, - fs_info->thread_pool_size, old_thread_pool_size); - - if ((bool)(*flags & SB_RDONLY) == sb_rdonly(sb)) + if ((bool)(fc->sb_flags & SB_RDONLY) == sb_rdonly(sb)) goto out; - if (*flags & SB_RDONLY) { + if (fc->sb_flags & SB_RDONLY) { /* * this also happens on 'umount -rf' or on shutdown, when * the filesystem is busy. @@ -1113,11 +1088,10 @@ static int btrfs_remount(struct super_block *sb, int *flags, char *data) if (sb_rdonly(sb)) old_flags |= SB_RDONLY; sb->s_flags = old_flags; - fs_info->mount_opt = old_opts; + WRITE_ONCE(fs_info->mount_opt, old_opts); fs_info->compress_type = old_compress_type; fs_info->max_inline = old_max_inline; - btrfs_resize_thread_pool(fs_info, - old_thread_pool_size, fs_info->thread_pool_size); + btrfs_resize_thread_pool(fs_info, old_thread_pool_size); fs_info->metadata_ratio = old_metadata_ratio; btrfs_remount_cleanup(fs_info, old_opts); return ret; @@ -1400,20 +1374,13 @@ static void btrfs_kill_super(struct super_block *sb) free_fs_info(fs_info); } -static struct file_system_type btrfs_fs_type = { - .owner = THIS_MODULE, - .name = "btrfs", - .mount = btrfs_mount, - .kill_sb = btrfs_kill_super, - .fs_flags = FS_REQUIRES_DEV | FS_BINARY_MOUNTDATA, -}; - -static struct file_system_type btrfs_root_fs_type = { - .owner = THIS_MODULE, - .name = "btrfs", - .mount = btrfs_mount_root, - .kill_sb = btrfs_kill_super, - .fs_flags = FS_REQUIRES_DEV | FS_BINARY_MOUNTDATA, +struct file_system_type btrfs_fs_type = { + .owner = THIS_MODULE, + .name = "btrfs", + .init_fs_context = btrfs_init_fs_context, + .parameters = &btrfs_fs_parameters, + .kill_sb = btrfs_kill_super, + .fs_flags = FS_REQUIRES_DEV, }; MODULE_ALIAS_FS("btrfs"); @@ -1449,15 +1416,13 @@ static long btrfs_control_ioctl(struct file *file, unsigned int cmd, switch (cmd) { case BTRFS_IOC_SCAN_DEV: mutex_lock(&uuid_mutex); - device = btrfs_scan_one_device(vol->name, FMODE_READ, - &btrfs_root_fs_type); + device = btrfs_scan_one_device(vol->name, FMODE_READ); ret = PTR_ERR_OR_ZERO(device); mutex_unlock(&uuid_mutex); break; case BTRFS_IOC_DEVICES_READY: mutex_lock(&uuid_mutex); - device = btrfs_scan_one_device(vol->name, FMODE_READ, - &btrfs_root_fs_type); + device = btrfs_scan_one_device(vol->name, FMODE_READ); if (IS_ERR(device)) { mutex_unlock(&uuid_mutex); ret = PTR_ERR(device); @@ -1554,7 +1519,6 @@ static const struct super_operations btrfs_super_ops = { .alloc_inode = btrfs_alloc_inode, .destroy_inode = btrfs_destroy_inode, .statfs = btrfs_statfs, - .remount_fs = btrfs_remount, .freeze_fs = btrfs_freeze, .unfreeze_fs = btrfs_unfreeze, };
diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index f435d39..7178c33 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c
@@ -380,13 +380,13 @@ static noinline struct btrfs_fs_devices *find_fsid(u8 *fsid) } static int -btrfs_get_bdev_and_sb(const char *device_path, fmode_t flags, void *holder, +btrfs_get_bdev_and_sb(const char *device_path, fmode_t flags, int flush, struct block_device **bdev, struct buffer_head **bh) { int ret; - *bdev = blkdev_get_by_path(device_path, flags, holder); + *bdev = blkdev_get_by_path(device_path, flags, &btrfs_fs_type); if (IS_ERR(*bdev)) { ret = PTR_ERR(*bdev); @@ -678,8 +678,7 @@ static void btrfs_free_stale_devices(const char *path, } static int btrfs_open_one_device(struct btrfs_fs_devices *fs_devices, - struct btrfs_device *device, fmode_t flags, - void *holder) + struct btrfs_device *device, fmode_t flags) { struct request_queue *q; struct block_device *bdev; @@ -693,8 +692,7 @@ static int btrfs_open_one_device(struct btrfs_fs_devices *fs_devices, if (!device->name) return -EINVAL; - ret = btrfs_get_bdev_and_sb(device->name->str, flags, holder, 1, - &bdev, &bh); + ret = btrfs_get_bdev_and_sb(device->name->str, flags, 1, &bdev, &bh); if (ret) return ret; @@ -1094,8 +1092,7 @@ int btrfs_close_devices(struct btrfs_fs_devices *fs_devices) return ret; } -static int open_fs_devices(struct btrfs_fs_devices *fs_devices, - fmode_t flags, void *holder) +static int open_fs_devices(struct btrfs_fs_devices *fs_devices, fmode_t flags) { struct btrfs_device *device; struct btrfs_device *latest_dev = NULL; @@ -1105,7 +1102,7 @@ static int open_fs_devices(struct btrfs_fs_devices *fs_devices, list_for_each_entry(device, &fs_devices->devices, dev_list) { /* Just open everything we can; ignore failures here */ - if (btrfs_open_one_device(fs_devices, device, flags, holder)) + if (btrfs_open_one_device(fs_devices, device, flags)) continue; if (!latest_dev || @@ -1137,8 +1134,7 @@ static int devid_cmp(void *priv, struct list_head *a, struct list_head *b) return 0; } -int btrfs_open_devices(struct btrfs_fs_devices *fs_devices, - fmode_t flags, void *holder) +int btrfs_open_devices(struct btrfs_fs_devices *fs_devices, fmode_t flags) { int ret; @@ -1150,7 +1146,7 @@ int btrfs_open_devices(struct btrfs_fs_devices *fs_devices, ret = 0; } else { list_sort(NULL, &fs_devices->devices, devid_cmp); - ret = open_fs_devices(fs_devices, flags, holder); + ret = open_fs_devices(fs_devices, flags); } mutex_unlock(&fs_devices->device_list_mutex); @@ -1213,8 +1209,7 @@ static int btrfs_read_disk_super(struct block_device *bdev, u64 bytenr, * and we are not allowed to call set_blocksize during the scan. The superblock * is read via pagecache */ -struct btrfs_device *btrfs_scan_one_device(const char *path, fmode_t flags, - void *holder) +struct btrfs_device *btrfs_scan_one_device(const char *path, fmode_t flags) { struct btrfs_super_block *disk_super; bool new_device_added = false; @@ -1234,7 +1229,7 @@ struct btrfs_device *btrfs_scan_one_device(const char *path, fmode_t flags, bytenr = btrfs_sb_offset(0); flags |= FMODE_EXCL; - bdev = blkdev_get_by_path(path, flags, holder); + bdev = blkdev_get_by_path(path, flags, &btrfs_fs_type); if (IS_ERR(bdev)) return ERR_CAST(bdev); @@ -2125,8 +2120,7 @@ static struct btrfs_device *btrfs_find_device_by_path( struct buffer_head *bh; struct btrfs_device *device; - ret = btrfs_get_bdev_and_sb(device_path, FMODE_READ, - fs_info->bdev_holder, 0, &bdev, &bh); + ret = btrfs_get_bdev_and_sb(device_path, FMODE_READ, 0, &bdev, &bh); if (ret) return ERR_PTR(ret); disk_super = (struct btrfs_super_block *)bh->b_data; @@ -2343,7 +2337,7 @@ int btrfs_init_new_device(struct btrfs_fs_info *fs_info, const char *device_path return -EROFS; bdev = blkdev_get_by_path(device_path, FMODE_WRITE | FMODE_EXCL, - fs_info->bdev_holder); + &btrfs_fs_type); if (IS_ERR(bdev)) return PTR_ERR(bdev); @@ -6592,7 +6586,7 @@ static struct btrfs_fs_devices *open_seed_devices(struct btrfs_fs_info *fs_info, if (IS_ERR(fs_devices)) return fs_devices; - ret = open_fs_devices(fs_devices, FMODE_READ, fs_info->bdev_holder); + ret = open_fs_devices(fs_devices, FMODE_READ); if (ret) { free_fs_devices(fs_devices); fs_devices = ERR_PTR(ret);
diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h index aefce89..6d359d9 100644 --- a/fs/btrfs/volumes.h +++ b/fs/btrfs/volumes.h
@@ -402,10 +402,8 @@ void btrfs_mapping_init(struct btrfs_mapping_tree *tree); void btrfs_mapping_tree_free(struct btrfs_mapping_tree *tree); blk_status_t btrfs_map_bio(struct btrfs_fs_info *fs_info, struct bio *bio, int mirror_num, int async_submit); -int btrfs_open_devices(struct btrfs_fs_devices *fs_devices, - fmode_t flags, void *holder); -struct btrfs_device *btrfs_scan_one_device(const char *path, - fmode_t flags, void *holder); +int btrfs_open_devices(struct btrfs_fs_devices *fs_devices, fmode_t flags); +struct btrfs_device *btrfs_scan_one_device(const char *path, fmode_t flags); int btrfs_close_devices(struct btrfs_fs_devices *fs_devices); void btrfs_free_extra_devids(struct btrfs_fs_devices *fs_devices, int step); void btrfs_assign_next_active_device(struct btrfs_device *device,