162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* Provide a way to create a superblock configuration context within the kernel 362306a36Sopenharmony_ci * that allows a superblock to be set up prior to mounting. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2017 Red Hat, Inc. All Rights Reserved. 662306a36Sopenharmony_ci * Written by David Howells (dhowells@redhat.com) 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/fs_context.h> 1262306a36Sopenharmony_ci#include <linux/fs_parser.h> 1362306a36Sopenharmony_ci#include <linux/fs.h> 1462306a36Sopenharmony_ci#include <linux/mount.h> 1562306a36Sopenharmony_ci#include <linux/nsproxy.h> 1662306a36Sopenharmony_ci#include <linux/slab.h> 1762306a36Sopenharmony_ci#include <linux/magic.h> 1862306a36Sopenharmony_ci#include <linux/security.h> 1962306a36Sopenharmony_ci#include <linux/mnt_namespace.h> 2062306a36Sopenharmony_ci#include <linux/pid_namespace.h> 2162306a36Sopenharmony_ci#include <linux/user_namespace.h> 2262306a36Sopenharmony_ci#include <net/net_namespace.h> 2362306a36Sopenharmony_ci#include <asm/sections.h> 2462306a36Sopenharmony_ci#include "mount.h" 2562306a36Sopenharmony_ci#include "internal.h" 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cienum legacy_fs_param { 2862306a36Sopenharmony_ci LEGACY_FS_UNSET_PARAMS, 2962306a36Sopenharmony_ci LEGACY_FS_MONOLITHIC_PARAMS, 3062306a36Sopenharmony_ci LEGACY_FS_INDIVIDUAL_PARAMS, 3162306a36Sopenharmony_ci}; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistruct legacy_fs_context { 3462306a36Sopenharmony_ci char *legacy_data; /* Data page for legacy filesystems */ 3562306a36Sopenharmony_ci size_t data_size; 3662306a36Sopenharmony_ci enum legacy_fs_param param_type; 3762306a36Sopenharmony_ci}; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic int legacy_init_fs_context(struct fs_context *fc); 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic const struct constant_table common_set_sb_flag[] = { 4262306a36Sopenharmony_ci { "dirsync", SB_DIRSYNC }, 4362306a36Sopenharmony_ci { "lazytime", SB_LAZYTIME }, 4462306a36Sopenharmony_ci { "mand", SB_MANDLOCK }, 4562306a36Sopenharmony_ci { "ro", SB_RDONLY }, 4662306a36Sopenharmony_ci { "sync", SB_SYNCHRONOUS }, 4762306a36Sopenharmony_ci { }, 4862306a36Sopenharmony_ci}; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic const struct constant_table common_clear_sb_flag[] = { 5162306a36Sopenharmony_ci { "async", SB_SYNCHRONOUS }, 5262306a36Sopenharmony_ci { "nolazytime", SB_LAZYTIME }, 5362306a36Sopenharmony_ci { "nomand", SB_MANDLOCK }, 5462306a36Sopenharmony_ci { "rw", SB_RDONLY }, 5562306a36Sopenharmony_ci { }, 5662306a36Sopenharmony_ci}; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci/* 5962306a36Sopenharmony_ci * Check for a common mount option that manipulates s_flags. 6062306a36Sopenharmony_ci */ 6162306a36Sopenharmony_cistatic int vfs_parse_sb_flag(struct fs_context *fc, const char *key) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci unsigned int token; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci token = lookup_constant(common_set_sb_flag, key, 0); 6662306a36Sopenharmony_ci if (token) { 6762306a36Sopenharmony_ci fc->sb_flags |= token; 6862306a36Sopenharmony_ci fc->sb_flags_mask |= token; 6962306a36Sopenharmony_ci return 0; 7062306a36Sopenharmony_ci } 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci token = lookup_constant(common_clear_sb_flag, key, 0); 7362306a36Sopenharmony_ci if (token) { 7462306a36Sopenharmony_ci fc->sb_flags &= ~token; 7562306a36Sopenharmony_ci fc->sb_flags_mask |= token; 7662306a36Sopenharmony_ci return 0; 7762306a36Sopenharmony_ci } 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci return -ENOPARAM; 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci/** 8362306a36Sopenharmony_ci * vfs_parse_fs_param_source - Handle setting "source" via parameter 8462306a36Sopenharmony_ci * @fc: The filesystem context to modify 8562306a36Sopenharmony_ci * @param: The parameter 8662306a36Sopenharmony_ci * 8762306a36Sopenharmony_ci * This is a simple helper for filesystems to verify that the "source" they 8862306a36Sopenharmony_ci * accept is sane. 8962306a36Sopenharmony_ci * 9062306a36Sopenharmony_ci * Returns 0 on success, -ENOPARAM if this is not "source" parameter, and 9162306a36Sopenharmony_ci * -EINVAL otherwise. In the event of failure, supplementary error information 9262306a36Sopenharmony_ci * is logged. 9362306a36Sopenharmony_ci */ 9462306a36Sopenharmony_ciint vfs_parse_fs_param_source(struct fs_context *fc, struct fs_parameter *param) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci if (strcmp(param->key, "source") != 0) 9762306a36Sopenharmony_ci return -ENOPARAM; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci if (param->type != fs_value_is_string) 10062306a36Sopenharmony_ci return invalf(fc, "Non-string source"); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci if (fc->source) 10362306a36Sopenharmony_ci return invalf(fc, "Multiple sources"); 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci fc->source = param->string; 10662306a36Sopenharmony_ci param->string = NULL; 10762306a36Sopenharmony_ci return 0; 10862306a36Sopenharmony_ci} 10962306a36Sopenharmony_ciEXPORT_SYMBOL(vfs_parse_fs_param_source); 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci/** 11262306a36Sopenharmony_ci * vfs_parse_fs_param - Add a single parameter to a superblock config 11362306a36Sopenharmony_ci * @fc: The filesystem context to modify 11462306a36Sopenharmony_ci * @param: The parameter 11562306a36Sopenharmony_ci * 11662306a36Sopenharmony_ci * A single mount option in string form is applied to the filesystem context 11762306a36Sopenharmony_ci * being set up. Certain standard options (for example "ro") are translated 11862306a36Sopenharmony_ci * into flag bits without going to the filesystem. The active security module 11962306a36Sopenharmony_ci * is allowed to observe and poach options. Any other options are passed over 12062306a36Sopenharmony_ci * to the filesystem to parse. 12162306a36Sopenharmony_ci * 12262306a36Sopenharmony_ci * This may be called multiple times for a context. 12362306a36Sopenharmony_ci * 12462306a36Sopenharmony_ci * Returns 0 on success and a negative error code on failure. In the event of 12562306a36Sopenharmony_ci * failure, supplementary error information may have been set. 12662306a36Sopenharmony_ci */ 12762306a36Sopenharmony_ciint vfs_parse_fs_param(struct fs_context *fc, struct fs_parameter *param) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci int ret; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci if (!param->key) 13262306a36Sopenharmony_ci return invalf(fc, "Unnamed parameter\n"); 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci ret = vfs_parse_sb_flag(fc, param->key); 13562306a36Sopenharmony_ci if (ret != -ENOPARAM) 13662306a36Sopenharmony_ci return ret; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci ret = security_fs_context_parse_param(fc, param); 13962306a36Sopenharmony_ci if (ret != -ENOPARAM) 14062306a36Sopenharmony_ci /* Param belongs to the LSM or is disallowed by the LSM; so 14162306a36Sopenharmony_ci * don't pass to the FS. 14262306a36Sopenharmony_ci */ 14362306a36Sopenharmony_ci return ret; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci if (fc->ops->parse_param) { 14662306a36Sopenharmony_ci ret = fc->ops->parse_param(fc, param); 14762306a36Sopenharmony_ci if (ret != -ENOPARAM) 14862306a36Sopenharmony_ci return ret; 14962306a36Sopenharmony_ci } 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci /* If the filesystem doesn't take any arguments, give it the 15262306a36Sopenharmony_ci * default handling of source. 15362306a36Sopenharmony_ci */ 15462306a36Sopenharmony_ci ret = vfs_parse_fs_param_source(fc, param); 15562306a36Sopenharmony_ci if (ret != -ENOPARAM) 15662306a36Sopenharmony_ci return ret; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci return invalf(fc, "%s: Unknown parameter '%s'", 15962306a36Sopenharmony_ci fc->fs_type->name, param->key); 16062306a36Sopenharmony_ci} 16162306a36Sopenharmony_ciEXPORT_SYMBOL(vfs_parse_fs_param); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci/** 16462306a36Sopenharmony_ci * vfs_parse_fs_string - Convenience function to just parse a string. 16562306a36Sopenharmony_ci * @fc: Filesystem context. 16662306a36Sopenharmony_ci * @key: Parameter name. 16762306a36Sopenharmony_ci * @value: Default value. 16862306a36Sopenharmony_ci * @v_size: Maximum number of bytes in the value. 16962306a36Sopenharmony_ci */ 17062306a36Sopenharmony_ciint vfs_parse_fs_string(struct fs_context *fc, const char *key, 17162306a36Sopenharmony_ci const char *value, size_t v_size) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci int ret; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci struct fs_parameter param = { 17662306a36Sopenharmony_ci .key = key, 17762306a36Sopenharmony_ci .type = fs_value_is_flag, 17862306a36Sopenharmony_ci .size = v_size, 17962306a36Sopenharmony_ci }; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci if (value) { 18262306a36Sopenharmony_ci param.string = kmemdup_nul(value, v_size, GFP_KERNEL); 18362306a36Sopenharmony_ci if (!param.string) 18462306a36Sopenharmony_ci return -ENOMEM; 18562306a36Sopenharmony_ci param.type = fs_value_is_string; 18662306a36Sopenharmony_ci } 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci ret = vfs_parse_fs_param(fc, ¶m); 18962306a36Sopenharmony_ci kfree(param.string); 19062306a36Sopenharmony_ci return ret; 19162306a36Sopenharmony_ci} 19262306a36Sopenharmony_ciEXPORT_SYMBOL(vfs_parse_fs_string); 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci/** 19562306a36Sopenharmony_ci * vfs_parse_monolithic_sep - Parse key[=val][,key[=val]]* mount data 19662306a36Sopenharmony_ci * @fc: The superblock configuration to fill in. 19762306a36Sopenharmony_ci * @data: The data to parse 19862306a36Sopenharmony_ci * @sep: callback for separating next option 19962306a36Sopenharmony_ci * 20062306a36Sopenharmony_ci * Parse a blob of data that's in key[=val][,key[=val]]* form with a custom 20162306a36Sopenharmony_ci * option separator callback. 20262306a36Sopenharmony_ci * 20362306a36Sopenharmony_ci * Returns 0 on success or the error returned by the ->parse_option() fs_context 20462306a36Sopenharmony_ci * operation on failure. 20562306a36Sopenharmony_ci */ 20662306a36Sopenharmony_ciint vfs_parse_monolithic_sep(struct fs_context *fc, void *data, 20762306a36Sopenharmony_ci char *(*sep)(char **)) 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci char *options = data, *key; 21062306a36Sopenharmony_ci int ret = 0; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci if (!options) 21362306a36Sopenharmony_ci return 0; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci ret = security_sb_eat_lsm_opts(options, &fc->security); 21662306a36Sopenharmony_ci if (ret) 21762306a36Sopenharmony_ci return ret; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci while ((key = sep(&options)) != NULL) { 22062306a36Sopenharmony_ci if (*key) { 22162306a36Sopenharmony_ci size_t v_len = 0; 22262306a36Sopenharmony_ci char *value = strchr(key, '='); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci if (value) { 22562306a36Sopenharmony_ci if (value == key) 22662306a36Sopenharmony_ci continue; 22762306a36Sopenharmony_ci *value++ = 0; 22862306a36Sopenharmony_ci v_len = strlen(value); 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci ret = vfs_parse_fs_string(fc, key, value, v_len); 23162306a36Sopenharmony_ci if (ret < 0) 23262306a36Sopenharmony_ci break; 23362306a36Sopenharmony_ci } 23462306a36Sopenharmony_ci } 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci return ret; 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ciEXPORT_SYMBOL(vfs_parse_monolithic_sep); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_cistatic char *vfs_parse_comma_sep(char **s) 24162306a36Sopenharmony_ci{ 24262306a36Sopenharmony_ci return strsep(s, ","); 24362306a36Sopenharmony_ci} 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci/** 24662306a36Sopenharmony_ci * generic_parse_monolithic - Parse key[=val][,key[=val]]* mount data 24762306a36Sopenharmony_ci * @fc: The superblock configuration to fill in. 24862306a36Sopenharmony_ci * @data: The data to parse 24962306a36Sopenharmony_ci * 25062306a36Sopenharmony_ci * Parse a blob of data that's in key[=val][,key[=val]]* form. This can be 25162306a36Sopenharmony_ci * called from the ->monolithic_mount_data() fs_context operation. 25262306a36Sopenharmony_ci * 25362306a36Sopenharmony_ci * Returns 0 on success or the error returned by the ->parse_option() fs_context 25462306a36Sopenharmony_ci * operation on failure. 25562306a36Sopenharmony_ci */ 25662306a36Sopenharmony_ciint generic_parse_monolithic(struct fs_context *fc, void *data) 25762306a36Sopenharmony_ci{ 25862306a36Sopenharmony_ci return vfs_parse_monolithic_sep(fc, data, vfs_parse_comma_sep); 25962306a36Sopenharmony_ci} 26062306a36Sopenharmony_ciEXPORT_SYMBOL(generic_parse_monolithic); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci/** 26362306a36Sopenharmony_ci * alloc_fs_context - Create a filesystem context. 26462306a36Sopenharmony_ci * @fs_type: The filesystem type. 26562306a36Sopenharmony_ci * @reference: The dentry from which this one derives (or NULL) 26662306a36Sopenharmony_ci * @sb_flags: Filesystem/superblock flags (SB_*) 26762306a36Sopenharmony_ci * @sb_flags_mask: Applicable members of @sb_flags 26862306a36Sopenharmony_ci * @purpose: The purpose that this configuration shall be used for. 26962306a36Sopenharmony_ci * 27062306a36Sopenharmony_ci * Open a filesystem and create a mount context. The mount context is 27162306a36Sopenharmony_ci * initialised with the supplied flags and, if a submount/automount from 27262306a36Sopenharmony_ci * another superblock (referred to by @reference) is supplied, may have 27362306a36Sopenharmony_ci * parameters such as namespaces copied across from that superblock. 27462306a36Sopenharmony_ci */ 27562306a36Sopenharmony_cistatic struct fs_context *alloc_fs_context(struct file_system_type *fs_type, 27662306a36Sopenharmony_ci struct dentry *reference, 27762306a36Sopenharmony_ci unsigned int sb_flags, 27862306a36Sopenharmony_ci unsigned int sb_flags_mask, 27962306a36Sopenharmony_ci enum fs_context_purpose purpose) 28062306a36Sopenharmony_ci{ 28162306a36Sopenharmony_ci int (*init_fs_context)(struct fs_context *); 28262306a36Sopenharmony_ci struct fs_context *fc; 28362306a36Sopenharmony_ci int ret = -ENOMEM; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci fc = kzalloc(sizeof(struct fs_context), GFP_KERNEL_ACCOUNT); 28662306a36Sopenharmony_ci if (!fc) 28762306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci fc->purpose = purpose; 29062306a36Sopenharmony_ci fc->sb_flags = sb_flags; 29162306a36Sopenharmony_ci fc->sb_flags_mask = sb_flags_mask; 29262306a36Sopenharmony_ci fc->fs_type = get_filesystem(fs_type); 29362306a36Sopenharmony_ci fc->cred = get_current_cred(); 29462306a36Sopenharmony_ci fc->net_ns = get_net(current->nsproxy->net_ns); 29562306a36Sopenharmony_ci fc->log.prefix = fs_type->name; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci mutex_init(&fc->uapi_mutex); 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci switch (purpose) { 30062306a36Sopenharmony_ci case FS_CONTEXT_FOR_MOUNT: 30162306a36Sopenharmony_ci fc->user_ns = get_user_ns(fc->cred->user_ns); 30262306a36Sopenharmony_ci break; 30362306a36Sopenharmony_ci case FS_CONTEXT_FOR_SUBMOUNT: 30462306a36Sopenharmony_ci fc->user_ns = get_user_ns(reference->d_sb->s_user_ns); 30562306a36Sopenharmony_ci break; 30662306a36Sopenharmony_ci case FS_CONTEXT_FOR_RECONFIGURE: 30762306a36Sopenharmony_ci atomic_inc(&reference->d_sb->s_active); 30862306a36Sopenharmony_ci fc->user_ns = get_user_ns(reference->d_sb->s_user_ns); 30962306a36Sopenharmony_ci fc->root = dget(reference); 31062306a36Sopenharmony_ci break; 31162306a36Sopenharmony_ci } 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci /* TODO: Make all filesystems support this unconditionally */ 31462306a36Sopenharmony_ci init_fs_context = fc->fs_type->init_fs_context; 31562306a36Sopenharmony_ci if (!init_fs_context) 31662306a36Sopenharmony_ci init_fs_context = legacy_init_fs_context; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci ret = init_fs_context(fc); 31962306a36Sopenharmony_ci if (ret < 0) 32062306a36Sopenharmony_ci goto err_fc; 32162306a36Sopenharmony_ci fc->need_free = true; 32262306a36Sopenharmony_ci return fc; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_cierr_fc: 32562306a36Sopenharmony_ci put_fs_context(fc); 32662306a36Sopenharmony_ci return ERR_PTR(ret); 32762306a36Sopenharmony_ci} 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_cistruct fs_context *fs_context_for_mount(struct file_system_type *fs_type, 33062306a36Sopenharmony_ci unsigned int sb_flags) 33162306a36Sopenharmony_ci{ 33262306a36Sopenharmony_ci return alloc_fs_context(fs_type, NULL, sb_flags, 0, 33362306a36Sopenharmony_ci FS_CONTEXT_FOR_MOUNT); 33462306a36Sopenharmony_ci} 33562306a36Sopenharmony_ciEXPORT_SYMBOL(fs_context_for_mount); 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_cistruct fs_context *fs_context_for_reconfigure(struct dentry *dentry, 33862306a36Sopenharmony_ci unsigned int sb_flags, 33962306a36Sopenharmony_ci unsigned int sb_flags_mask) 34062306a36Sopenharmony_ci{ 34162306a36Sopenharmony_ci return alloc_fs_context(dentry->d_sb->s_type, dentry, sb_flags, 34262306a36Sopenharmony_ci sb_flags_mask, FS_CONTEXT_FOR_RECONFIGURE); 34362306a36Sopenharmony_ci} 34462306a36Sopenharmony_ciEXPORT_SYMBOL(fs_context_for_reconfigure); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci/** 34762306a36Sopenharmony_ci * fs_context_for_submount: allocate a new fs_context for a submount 34862306a36Sopenharmony_ci * @type: file_system_type of the new context 34962306a36Sopenharmony_ci * @reference: reference dentry from which to copy relevant info 35062306a36Sopenharmony_ci * 35162306a36Sopenharmony_ci * Allocate a new fs_context suitable for a submount. This also ensures that 35262306a36Sopenharmony_ci * the fc->security object is inherited from @reference (if needed). 35362306a36Sopenharmony_ci */ 35462306a36Sopenharmony_cistruct fs_context *fs_context_for_submount(struct file_system_type *type, 35562306a36Sopenharmony_ci struct dentry *reference) 35662306a36Sopenharmony_ci{ 35762306a36Sopenharmony_ci struct fs_context *fc; 35862306a36Sopenharmony_ci int ret; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci fc = alloc_fs_context(type, reference, 0, 0, FS_CONTEXT_FOR_SUBMOUNT); 36162306a36Sopenharmony_ci if (IS_ERR(fc)) 36262306a36Sopenharmony_ci return fc; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci ret = security_fs_context_submount(fc, reference->d_sb); 36562306a36Sopenharmony_ci if (ret) { 36662306a36Sopenharmony_ci put_fs_context(fc); 36762306a36Sopenharmony_ci return ERR_PTR(ret); 36862306a36Sopenharmony_ci } 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci return fc; 37162306a36Sopenharmony_ci} 37262306a36Sopenharmony_ciEXPORT_SYMBOL(fs_context_for_submount); 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_civoid fc_drop_locked(struct fs_context *fc) 37562306a36Sopenharmony_ci{ 37662306a36Sopenharmony_ci struct super_block *sb = fc->root->d_sb; 37762306a36Sopenharmony_ci dput(fc->root); 37862306a36Sopenharmony_ci fc->root = NULL; 37962306a36Sopenharmony_ci deactivate_locked_super(sb); 38062306a36Sopenharmony_ci} 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_cistatic void legacy_fs_context_free(struct fs_context *fc); 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci/** 38562306a36Sopenharmony_ci * vfs_dup_fs_context - Duplicate a filesystem context. 38662306a36Sopenharmony_ci * @src_fc: The context to copy. 38762306a36Sopenharmony_ci */ 38862306a36Sopenharmony_cistruct fs_context *vfs_dup_fs_context(struct fs_context *src_fc) 38962306a36Sopenharmony_ci{ 39062306a36Sopenharmony_ci struct fs_context *fc; 39162306a36Sopenharmony_ci int ret; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci if (!src_fc->ops->dup) 39462306a36Sopenharmony_ci return ERR_PTR(-EOPNOTSUPP); 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci fc = kmemdup(src_fc, sizeof(struct fs_context), GFP_KERNEL); 39762306a36Sopenharmony_ci if (!fc) 39862306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci mutex_init(&fc->uapi_mutex); 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci fc->fs_private = NULL; 40362306a36Sopenharmony_ci fc->s_fs_info = NULL; 40462306a36Sopenharmony_ci fc->source = NULL; 40562306a36Sopenharmony_ci fc->security = NULL; 40662306a36Sopenharmony_ci get_filesystem(fc->fs_type); 40762306a36Sopenharmony_ci get_net(fc->net_ns); 40862306a36Sopenharmony_ci get_user_ns(fc->user_ns); 40962306a36Sopenharmony_ci get_cred(fc->cred); 41062306a36Sopenharmony_ci if (fc->log.log) 41162306a36Sopenharmony_ci refcount_inc(&fc->log.log->usage); 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci /* Can't call put until we've called ->dup */ 41462306a36Sopenharmony_ci ret = fc->ops->dup(fc, src_fc); 41562306a36Sopenharmony_ci if (ret < 0) 41662306a36Sopenharmony_ci goto err_fc; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci ret = security_fs_context_dup(fc, src_fc); 41962306a36Sopenharmony_ci if (ret < 0) 42062306a36Sopenharmony_ci goto err_fc; 42162306a36Sopenharmony_ci return fc; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_cierr_fc: 42462306a36Sopenharmony_ci put_fs_context(fc); 42562306a36Sopenharmony_ci return ERR_PTR(ret); 42662306a36Sopenharmony_ci} 42762306a36Sopenharmony_ciEXPORT_SYMBOL(vfs_dup_fs_context); 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci/** 43062306a36Sopenharmony_ci * logfc - Log a message to a filesystem context 43162306a36Sopenharmony_ci * @log: The filesystem context to log to, or NULL to use printk. 43262306a36Sopenharmony_ci * @prefix: A string to prefix the output with, or NULL. 43362306a36Sopenharmony_ci * @level: 'w' for a warning, 'e' for an error. Anything else is a notice. 43462306a36Sopenharmony_ci * @fmt: The format of the buffer. 43562306a36Sopenharmony_ci */ 43662306a36Sopenharmony_civoid logfc(struct fc_log *log, const char *prefix, char level, const char *fmt, ...) 43762306a36Sopenharmony_ci{ 43862306a36Sopenharmony_ci va_list va; 43962306a36Sopenharmony_ci struct va_format vaf = {.fmt = fmt, .va = &va}; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci va_start(va, fmt); 44262306a36Sopenharmony_ci if (!log) { 44362306a36Sopenharmony_ci switch (level) { 44462306a36Sopenharmony_ci case 'w': 44562306a36Sopenharmony_ci printk(KERN_WARNING "%s%s%pV\n", prefix ? prefix : "", 44662306a36Sopenharmony_ci prefix ? ": " : "", &vaf); 44762306a36Sopenharmony_ci break; 44862306a36Sopenharmony_ci case 'e': 44962306a36Sopenharmony_ci printk(KERN_ERR "%s%s%pV\n", prefix ? prefix : "", 45062306a36Sopenharmony_ci prefix ? ": " : "", &vaf); 45162306a36Sopenharmony_ci break; 45262306a36Sopenharmony_ci default: 45362306a36Sopenharmony_ci printk(KERN_NOTICE "%s%s%pV\n", prefix ? prefix : "", 45462306a36Sopenharmony_ci prefix ? ": " : "", &vaf); 45562306a36Sopenharmony_ci break; 45662306a36Sopenharmony_ci } 45762306a36Sopenharmony_ci } else { 45862306a36Sopenharmony_ci unsigned int logsize = ARRAY_SIZE(log->buffer); 45962306a36Sopenharmony_ci u8 index; 46062306a36Sopenharmony_ci char *q = kasprintf(GFP_KERNEL, "%c %s%s%pV\n", level, 46162306a36Sopenharmony_ci prefix ? prefix : "", 46262306a36Sopenharmony_ci prefix ? ": " : "", &vaf); 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci index = log->head & (logsize - 1); 46562306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(log->head) != sizeof(u8) || 46662306a36Sopenharmony_ci sizeof(log->tail) != sizeof(u8)); 46762306a36Sopenharmony_ci if ((u8)(log->head - log->tail) == logsize) { 46862306a36Sopenharmony_ci /* The buffer is full, discard the oldest message */ 46962306a36Sopenharmony_ci if (log->need_free & (1 << index)) 47062306a36Sopenharmony_ci kfree(log->buffer[index]); 47162306a36Sopenharmony_ci log->tail++; 47262306a36Sopenharmony_ci } 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci log->buffer[index] = q ? q : "OOM: Can't store error string"; 47562306a36Sopenharmony_ci if (q) 47662306a36Sopenharmony_ci log->need_free |= 1 << index; 47762306a36Sopenharmony_ci else 47862306a36Sopenharmony_ci log->need_free &= ~(1 << index); 47962306a36Sopenharmony_ci log->head++; 48062306a36Sopenharmony_ci } 48162306a36Sopenharmony_ci va_end(va); 48262306a36Sopenharmony_ci} 48362306a36Sopenharmony_ciEXPORT_SYMBOL(logfc); 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci/* 48662306a36Sopenharmony_ci * Free a logging structure. 48762306a36Sopenharmony_ci */ 48862306a36Sopenharmony_cistatic void put_fc_log(struct fs_context *fc) 48962306a36Sopenharmony_ci{ 49062306a36Sopenharmony_ci struct fc_log *log = fc->log.log; 49162306a36Sopenharmony_ci int i; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci if (log) { 49462306a36Sopenharmony_ci if (refcount_dec_and_test(&log->usage)) { 49562306a36Sopenharmony_ci fc->log.log = NULL; 49662306a36Sopenharmony_ci for (i = 0; i <= 7; i++) 49762306a36Sopenharmony_ci if (log->need_free & (1 << i)) 49862306a36Sopenharmony_ci kfree(log->buffer[i]); 49962306a36Sopenharmony_ci kfree(log); 50062306a36Sopenharmony_ci } 50162306a36Sopenharmony_ci } 50262306a36Sopenharmony_ci} 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci/** 50562306a36Sopenharmony_ci * put_fs_context - Dispose of a superblock configuration context. 50662306a36Sopenharmony_ci * @fc: The context to dispose of. 50762306a36Sopenharmony_ci */ 50862306a36Sopenharmony_civoid put_fs_context(struct fs_context *fc) 50962306a36Sopenharmony_ci{ 51062306a36Sopenharmony_ci struct super_block *sb; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci if (fc->root) { 51362306a36Sopenharmony_ci sb = fc->root->d_sb; 51462306a36Sopenharmony_ci dput(fc->root); 51562306a36Sopenharmony_ci fc->root = NULL; 51662306a36Sopenharmony_ci deactivate_super(sb); 51762306a36Sopenharmony_ci } 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci if (fc->need_free && fc->ops && fc->ops->free) 52062306a36Sopenharmony_ci fc->ops->free(fc); 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci security_free_mnt_opts(&fc->security); 52362306a36Sopenharmony_ci put_net(fc->net_ns); 52462306a36Sopenharmony_ci put_user_ns(fc->user_ns); 52562306a36Sopenharmony_ci put_cred(fc->cred); 52662306a36Sopenharmony_ci put_fc_log(fc); 52762306a36Sopenharmony_ci put_filesystem(fc->fs_type); 52862306a36Sopenharmony_ci kfree(fc->source); 52962306a36Sopenharmony_ci kfree(fc); 53062306a36Sopenharmony_ci} 53162306a36Sopenharmony_ciEXPORT_SYMBOL(put_fs_context); 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci/* 53462306a36Sopenharmony_ci * Free the config for a filesystem that doesn't support fs_context. 53562306a36Sopenharmony_ci */ 53662306a36Sopenharmony_cistatic void legacy_fs_context_free(struct fs_context *fc) 53762306a36Sopenharmony_ci{ 53862306a36Sopenharmony_ci struct legacy_fs_context *ctx = fc->fs_private; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci if (ctx) { 54162306a36Sopenharmony_ci if (ctx->param_type == LEGACY_FS_INDIVIDUAL_PARAMS) 54262306a36Sopenharmony_ci kfree(ctx->legacy_data); 54362306a36Sopenharmony_ci kfree(ctx); 54462306a36Sopenharmony_ci } 54562306a36Sopenharmony_ci} 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci/* 54862306a36Sopenharmony_ci * Duplicate a legacy config. 54962306a36Sopenharmony_ci */ 55062306a36Sopenharmony_cistatic int legacy_fs_context_dup(struct fs_context *fc, struct fs_context *src_fc) 55162306a36Sopenharmony_ci{ 55262306a36Sopenharmony_ci struct legacy_fs_context *ctx; 55362306a36Sopenharmony_ci struct legacy_fs_context *src_ctx = src_fc->fs_private; 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci ctx = kmemdup(src_ctx, sizeof(*src_ctx), GFP_KERNEL); 55662306a36Sopenharmony_ci if (!ctx) 55762306a36Sopenharmony_ci return -ENOMEM; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci if (ctx->param_type == LEGACY_FS_INDIVIDUAL_PARAMS) { 56062306a36Sopenharmony_ci ctx->legacy_data = kmemdup(src_ctx->legacy_data, 56162306a36Sopenharmony_ci src_ctx->data_size, GFP_KERNEL); 56262306a36Sopenharmony_ci if (!ctx->legacy_data) { 56362306a36Sopenharmony_ci kfree(ctx); 56462306a36Sopenharmony_ci return -ENOMEM; 56562306a36Sopenharmony_ci } 56662306a36Sopenharmony_ci } 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci fc->fs_private = ctx; 56962306a36Sopenharmony_ci return 0; 57062306a36Sopenharmony_ci} 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci/* 57362306a36Sopenharmony_ci * Add a parameter to a legacy config. We build up a comma-separated list of 57462306a36Sopenharmony_ci * options. 57562306a36Sopenharmony_ci */ 57662306a36Sopenharmony_cistatic int legacy_parse_param(struct fs_context *fc, struct fs_parameter *param) 57762306a36Sopenharmony_ci{ 57862306a36Sopenharmony_ci struct legacy_fs_context *ctx = fc->fs_private; 57962306a36Sopenharmony_ci unsigned int size = ctx->data_size; 58062306a36Sopenharmony_ci size_t len = 0; 58162306a36Sopenharmony_ci int ret; 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci ret = vfs_parse_fs_param_source(fc, param); 58462306a36Sopenharmony_ci if (ret != -ENOPARAM) 58562306a36Sopenharmony_ci return ret; 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci if (ctx->param_type == LEGACY_FS_MONOLITHIC_PARAMS) 58862306a36Sopenharmony_ci return invalf(fc, "VFS: Legacy: Can't mix monolithic and individual options"); 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci switch (param->type) { 59162306a36Sopenharmony_ci case fs_value_is_string: 59262306a36Sopenharmony_ci len = 1 + param->size; 59362306a36Sopenharmony_ci fallthrough; 59462306a36Sopenharmony_ci case fs_value_is_flag: 59562306a36Sopenharmony_ci len += strlen(param->key); 59662306a36Sopenharmony_ci break; 59762306a36Sopenharmony_ci default: 59862306a36Sopenharmony_ci return invalf(fc, "VFS: Legacy: Parameter type for '%s' not supported", 59962306a36Sopenharmony_ci param->key); 60062306a36Sopenharmony_ci } 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci if (size + len + 2 > PAGE_SIZE) 60362306a36Sopenharmony_ci return invalf(fc, "VFS: Legacy: Cumulative options too large"); 60462306a36Sopenharmony_ci if (strchr(param->key, ',') || 60562306a36Sopenharmony_ci (param->type == fs_value_is_string && 60662306a36Sopenharmony_ci memchr(param->string, ',', param->size))) 60762306a36Sopenharmony_ci return invalf(fc, "VFS: Legacy: Option '%s' contained comma", 60862306a36Sopenharmony_ci param->key); 60962306a36Sopenharmony_ci if (!ctx->legacy_data) { 61062306a36Sopenharmony_ci ctx->legacy_data = kmalloc(PAGE_SIZE, GFP_KERNEL); 61162306a36Sopenharmony_ci if (!ctx->legacy_data) 61262306a36Sopenharmony_ci return -ENOMEM; 61362306a36Sopenharmony_ci } 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci if (size) 61662306a36Sopenharmony_ci ctx->legacy_data[size++] = ','; 61762306a36Sopenharmony_ci len = strlen(param->key); 61862306a36Sopenharmony_ci memcpy(ctx->legacy_data + size, param->key, len); 61962306a36Sopenharmony_ci size += len; 62062306a36Sopenharmony_ci if (param->type == fs_value_is_string) { 62162306a36Sopenharmony_ci ctx->legacy_data[size++] = '='; 62262306a36Sopenharmony_ci memcpy(ctx->legacy_data + size, param->string, param->size); 62362306a36Sopenharmony_ci size += param->size; 62462306a36Sopenharmony_ci } 62562306a36Sopenharmony_ci ctx->legacy_data[size] = '\0'; 62662306a36Sopenharmony_ci ctx->data_size = size; 62762306a36Sopenharmony_ci ctx->param_type = LEGACY_FS_INDIVIDUAL_PARAMS; 62862306a36Sopenharmony_ci return 0; 62962306a36Sopenharmony_ci} 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci/* 63262306a36Sopenharmony_ci * Add monolithic mount data. 63362306a36Sopenharmony_ci */ 63462306a36Sopenharmony_cistatic int legacy_parse_monolithic(struct fs_context *fc, void *data) 63562306a36Sopenharmony_ci{ 63662306a36Sopenharmony_ci struct legacy_fs_context *ctx = fc->fs_private; 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci if (ctx->param_type != LEGACY_FS_UNSET_PARAMS) { 63962306a36Sopenharmony_ci pr_warn("VFS: Can't mix monolithic and individual options\n"); 64062306a36Sopenharmony_ci return -EINVAL; 64162306a36Sopenharmony_ci } 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci ctx->legacy_data = data; 64462306a36Sopenharmony_ci ctx->param_type = LEGACY_FS_MONOLITHIC_PARAMS; 64562306a36Sopenharmony_ci if (!ctx->legacy_data) 64662306a36Sopenharmony_ci return 0; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci if (fc->fs_type->fs_flags & FS_BINARY_MOUNTDATA) 64962306a36Sopenharmony_ci return 0; 65062306a36Sopenharmony_ci return security_sb_eat_lsm_opts(ctx->legacy_data, &fc->security); 65162306a36Sopenharmony_ci} 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci/* 65462306a36Sopenharmony_ci * Get a mountable root with the legacy mount command. 65562306a36Sopenharmony_ci */ 65662306a36Sopenharmony_cistatic int legacy_get_tree(struct fs_context *fc) 65762306a36Sopenharmony_ci{ 65862306a36Sopenharmony_ci struct legacy_fs_context *ctx = fc->fs_private; 65962306a36Sopenharmony_ci struct super_block *sb; 66062306a36Sopenharmony_ci struct dentry *root; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci root = fc->fs_type->mount(fc->fs_type, fc->sb_flags, 66362306a36Sopenharmony_ci fc->source, ctx->legacy_data); 66462306a36Sopenharmony_ci if (IS_ERR(root)) 66562306a36Sopenharmony_ci return PTR_ERR(root); 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci sb = root->d_sb; 66862306a36Sopenharmony_ci BUG_ON(!sb); 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci fc->root = root; 67162306a36Sopenharmony_ci return 0; 67262306a36Sopenharmony_ci} 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci/* 67562306a36Sopenharmony_ci * Handle remount. 67662306a36Sopenharmony_ci */ 67762306a36Sopenharmony_cistatic int legacy_reconfigure(struct fs_context *fc) 67862306a36Sopenharmony_ci{ 67962306a36Sopenharmony_ci struct legacy_fs_context *ctx = fc->fs_private; 68062306a36Sopenharmony_ci struct super_block *sb = fc->root->d_sb; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci if (!sb->s_op->remount_fs) 68362306a36Sopenharmony_ci return 0; 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci return sb->s_op->remount_fs(sb, &fc->sb_flags, 68662306a36Sopenharmony_ci ctx ? ctx->legacy_data : NULL); 68762306a36Sopenharmony_ci} 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ciconst struct fs_context_operations legacy_fs_context_ops = { 69062306a36Sopenharmony_ci .free = legacy_fs_context_free, 69162306a36Sopenharmony_ci .dup = legacy_fs_context_dup, 69262306a36Sopenharmony_ci .parse_param = legacy_parse_param, 69362306a36Sopenharmony_ci .parse_monolithic = legacy_parse_monolithic, 69462306a36Sopenharmony_ci .get_tree = legacy_get_tree, 69562306a36Sopenharmony_ci .reconfigure = legacy_reconfigure, 69662306a36Sopenharmony_ci}; 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci/* 69962306a36Sopenharmony_ci * Initialise a legacy context for a filesystem that doesn't support 70062306a36Sopenharmony_ci * fs_context. 70162306a36Sopenharmony_ci */ 70262306a36Sopenharmony_cistatic int legacy_init_fs_context(struct fs_context *fc) 70362306a36Sopenharmony_ci{ 70462306a36Sopenharmony_ci fc->fs_private = kzalloc(sizeof(struct legacy_fs_context), GFP_KERNEL_ACCOUNT); 70562306a36Sopenharmony_ci if (!fc->fs_private) 70662306a36Sopenharmony_ci return -ENOMEM; 70762306a36Sopenharmony_ci fc->ops = &legacy_fs_context_ops; 70862306a36Sopenharmony_ci return 0; 70962306a36Sopenharmony_ci} 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ciint parse_monolithic_mount_data(struct fs_context *fc, void *data) 71262306a36Sopenharmony_ci{ 71362306a36Sopenharmony_ci int (*monolithic_mount_data)(struct fs_context *, void *); 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci monolithic_mount_data = fc->ops->parse_monolithic; 71662306a36Sopenharmony_ci if (!monolithic_mount_data) 71762306a36Sopenharmony_ci monolithic_mount_data = generic_parse_monolithic; 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci return monolithic_mount_data(fc, data); 72062306a36Sopenharmony_ci} 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci/* 72362306a36Sopenharmony_ci * Clean up a context after performing an action on it and put it into a state 72462306a36Sopenharmony_ci * from where it can be used to reconfigure a superblock. 72562306a36Sopenharmony_ci * 72662306a36Sopenharmony_ci * Note that here we do only the parts that can't fail; the rest is in 72762306a36Sopenharmony_ci * finish_clean_context() below and in between those fs_context is marked 72862306a36Sopenharmony_ci * FS_CONTEXT_AWAITING_RECONF. The reason for splitup is that after 72962306a36Sopenharmony_ci * successful mount or remount we need to report success to userland. 73062306a36Sopenharmony_ci * Trying to do full reinit (for the sake of possible subsequent remount) 73162306a36Sopenharmony_ci * and failing to allocate memory would've put us into a nasty situation. 73262306a36Sopenharmony_ci * So here we only discard the old state and reinitialization is left 73362306a36Sopenharmony_ci * until we actually try to reconfigure. 73462306a36Sopenharmony_ci */ 73562306a36Sopenharmony_civoid vfs_clean_context(struct fs_context *fc) 73662306a36Sopenharmony_ci{ 73762306a36Sopenharmony_ci if (fc->need_free && fc->ops && fc->ops->free) 73862306a36Sopenharmony_ci fc->ops->free(fc); 73962306a36Sopenharmony_ci fc->need_free = false; 74062306a36Sopenharmony_ci fc->fs_private = NULL; 74162306a36Sopenharmony_ci fc->s_fs_info = NULL; 74262306a36Sopenharmony_ci fc->sb_flags = 0; 74362306a36Sopenharmony_ci security_free_mnt_opts(&fc->security); 74462306a36Sopenharmony_ci kfree(fc->source); 74562306a36Sopenharmony_ci fc->source = NULL; 74662306a36Sopenharmony_ci fc->exclusive = false; 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci fc->purpose = FS_CONTEXT_FOR_RECONFIGURE; 74962306a36Sopenharmony_ci fc->phase = FS_CONTEXT_AWAITING_RECONF; 75062306a36Sopenharmony_ci} 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ciint finish_clean_context(struct fs_context *fc) 75362306a36Sopenharmony_ci{ 75462306a36Sopenharmony_ci int error; 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci if (fc->phase != FS_CONTEXT_AWAITING_RECONF) 75762306a36Sopenharmony_ci return 0; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci if (fc->fs_type->init_fs_context) 76062306a36Sopenharmony_ci error = fc->fs_type->init_fs_context(fc); 76162306a36Sopenharmony_ci else 76262306a36Sopenharmony_ci error = legacy_init_fs_context(fc); 76362306a36Sopenharmony_ci if (unlikely(error)) { 76462306a36Sopenharmony_ci fc->phase = FS_CONTEXT_FAILED; 76562306a36Sopenharmony_ci return error; 76662306a36Sopenharmony_ci } 76762306a36Sopenharmony_ci fc->need_free = true; 76862306a36Sopenharmony_ci fc->phase = FS_CONTEXT_RECONF_PARAMS; 76962306a36Sopenharmony_ci return 0; 77062306a36Sopenharmony_ci} 771