Commit f2aedb71 authored by David Howells's avatar David Howells Committed by Anna Schumaker

NFS: Add fs_context support.

Add filesystem context support to NFS, parsing the options in advance and
attaching the information to struct nfs_fs_context.  The highlights are:

 (*) Merge nfs_mount_info and nfs_clone_mount into nfs_fs_context.  This
     structure represents NFS's superblock config.

 (*) Make use of the VFS's parsing support to split comma-separated lists

 (*) Pin the NFS protocol module in the nfs_fs_context.

 (*) Attach supplementary error information to fs_context.  This has the
     downside that these strings must be static and can't be formatted.

 (*) Remove the auxiliary file_system_type structs since the information
     necessary can be conveyed in the nfs_fs_context struct instead.

 (*) Root mounts are made by duplicating the config for the requested mount
     so as to have the same parameters.  Submounts pick up their parameters
     from the parent superblock.

[AV -- retrans is u32, not string]
[SM -- Renamed cfg to ctx in a few functions in an earlier patch]
[SM -- Moved fs_context mount option parsing to an earlier patch]
[SM -- Moved fs_context error logging to a later patch]
[SM -- Fixed printks in nfs4_try_get_tree() and nfs4_get_referral_tree()]
[SM -- Added is_remount_fc() helper]
[SM -- Deferred some refactoring to a later patch]
[SM -- Fixed referral mounts, which were broken in the original patch]
[SM -- Fixed leak of nfs_fattr when fs_context is freed]
Signed-off-by: default avatarDavid Howells <dhowells@redhat.com>
Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
Signed-off-by: default avatarScott Mayhew <smayhew@redhat.com>
Signed-off-by: default avatarAnna Schumaker <Anna.Schumaker@Netapp.com>
parent e38bb238
This diff is collapsed.
......@@ -4,7 +4,7 @@
*/
#include "nfs4_fs.h"
#include <linux/mount.h>
#include <linux/fs_context.h>
#include <linux/security.h>
#include <linux/crc32.h>
#include <linux/sunrpc/addr.h>
......@@ -16,6 +16,7 @@
extern const struct export_operations nfs_export_ops;
struct nfs_string;
struct nfs_pageio_descriptor;
static inline void nfs_attr_check_mountpoint(struct super_block *parent, struct nfs_fattr *fattr)
{
......@@ -34,12 +35,13 @@ static inline int nfs_attr_use_mounted_on_fileid(struct nfs_fattr *fattr)
struct nfs_clone_mount {
const struct super_block *sb;
const struct dentry *dentry;
struct dentry *dentry;
char *hostname;
char *mnt_path;
struct sockaddr *addr;
size_t addrlen;
rpc_authflavor_t authflavor;
struct nfs_fattr *fattr;
};
/*
......@@ -78,10 +80,23 @@ struct nfs_client_initdata {
const struct cred *cred;
};
struct nfs_mount_info {
unsigned int inherited_bsize;
struct nfs_fs_context *ctx;
struct nfs_clone_mount *cloned;
struct nfs_server *server;
struct nfs_fh *mntfh;
struct nfs_subversion *nfs_mod;
};
/*
* In-kernel mount arguments
*/
struct nfs_fs_context {
bool internal;
bool skip_reconfig_option_check;
bool need_mount;
bool sloppy;
unsigned int flags; /* NFS{,4}_MOUNT_* flags */
unsigned int rsize, wsize;
unsigned int timeo, retrans;
......@@ -98,8 +113,6 @@ struct nfs_fs_context {
char *fscache_uniq;
unsigned short protofamily;
unsigned short mountfamily;
bool need_mount;
bool sloppy;
struct {
union {
......@@ -124,14 +137,23 @@ struct nfs_fs_context {
int port;
unsigned short protocol;
unsigned short nconnect;
unsigned short export_path_len;
} nfs_server;
void *lsm_opts;
struct net *net;
char buf[32]; /* Parse buffer */
struct nfs_mount_info mount_info;
struct nfs_clone_mount clone_data;
};
static inline struct nfs_fs_context *nfs_fc2context(const struct fs_context *fc)
{
return fc->fs_private;
}
/* mount_clnt.c */
struct nfs_mount_request {
struct sockaddr *sap;
......@@ -147,15 +169,6 @@ struct nfs_mount_request {
struct net *net;
};
struct nfs_mount_info {
unsigned int inherited_bsize;
struct nfs_fs_context *ctx;
struct nfs_clone_mount *cloned;
struct nfs_server *server;
struct nfs_fh *mntfh;
struct nfs_subversion *nfs_mod;
};
extern int nfs_mount(struct nfs_mount_request *info);
extern void nfs_umount(const struct nfs_mount_request *info);
......@@ -235,22 +248,8 @@ static inline void nfs_fs_proc_exit(void)
extern const struct svc_version nfs4_callback_version1;
extern const struct svc_version nfs4_callback_version4;
struct nfs_pageio_descriptor;
/* mount.c */
#define NFS_TEXT_DATA 1
extern struct nfs_fs_context *nfs_alloc_parsed_mount_data(void);
extern void nfs_free_parsed_mount_data(struct nfs_fs_context *ctx);
extern int nfs_parse_mount_options(char *raw, struct nfs_fs_context *ctx);
extern int nfs_validate_mount_data(struct file_system_type *fs_type,
void *options,
struct nfs_fs_context *ctx,
struct nfs_fh *mntfh,
const char *dev_name);
extern int nfs_validate_text_mount_data(void *options,
struct nfs_fs_context *ctx,
const char *dev_name);
/* fs_context.c */
extern struct file_system_type nfs_fs_type;
/* pagelist.c */
extern int __init nfs_init_nfspagecache(void);
......@@ -411,14 +410,9 @@ extern int nfs_wait_atomic_killable(atomic_t *p, unsigned int mode);
/* super.c */
extern const struct super_operations nfs_sops;
extern struct file_system_type nfs_fs_type;
extern struct file_system_type nfs_prepared_fs_type;
#if IS_ENABLED(CONFIG_NFS_V4)
extern struct file_system_type nfs4_referral_fs_type;
#endif
bool nfs_auth_info_match(const struct nfs_auth_info *, rpc_authflavor_t);
struct dentry *nfs_try_mount(int, const char *, struct nfs_mount_info *);
struct dentry *nfs_fs_mount(struct file_system_type *, int, const char *, void *);
int nfs_try_get_tree(struct fs_context *);
int nfs_get_tree_common(struct fs_context *);
void nfs_kill_super(struct super_block *);
extern struct rpc_stat nfs_rpcstat;
......@@ -446,10 +440,8 @@ static inline bool nfs_file_io_is_buffered(struct nfs_inode *nfsi)
extern char *nfs_path(char **p, struct dentry *dentry,
char *buffer, ssize_t buflen, unsigned flags);
extern struct vfsmount *nfs_d_automount(struct path *path);
struct vfsmount *nfs_submount(struct nfs_server *, struct dentry *,
struct nfs_fh *, struct nfs_fattr *);
struct vfsmount *nfs_do_submount(struct dentry *, struct nfs_fh *,
struct nfs_fattr *, rpc_authflavor_t);
int nfs_submount(struct fs_context *, struct nfs_server *);
int nfs_do_submount(struct fs_context *);
/* getroot.c */
extern struct dentry *nfs_get_root(struct super_block *, struct nfs_fh *,
......@@ -476,7 +468,7 @@ int nfs_show_options(struct seq_file *, struct dentry *);
int nfs_show_devname(struct seq_file *, struct dentry *);
int nfs_show_path(struct seq_file *, struct dentry *);
int nfs_show_stats(struct seq_file *, struct dentry *);
int nfs_remount(struct super_block *sb, int *flags, char *raw_data);
int nfs_reconfigure(struct fs_context *);
/* write.c */
extern void nfs_pageio_init_write(struct nfs_pageio_descriptor *pgio,
......
......@@ -140,34 +140,65 @@ EXPORT_SYMBOL_GPL(nfs_path);
*/
struct vfsmount *nfs_d_automount(struct path *path)
{
struct vfsmount *mnt;
struct nfs_fs_context *ctx;
struct fs_context *fc;
struct vfsmount *mnt = ERR_PTR(-ENOMEM);
struct nfs_server *server = NFS_SERVER(d_inode(path->dentry));
struct nfs_fh *fh = NULL;
struct nfs_fattr *fattr = NULL;
struct nfs_client *client = server->nfs_client;
int ret;
if (IS_ROOT(path->dentry))
return ERR_PTR(-ESTALE);
mnt = ERR_PTR(-ENOMEM);
fh = nfs_alloc_fhandle();
fattr = nfs_alloc_fattr();
if (fh == NULL || fattr == NULL)
goto out;
/* Open a new filesystem context, transferring parameters from the
* parent superblock, including the network namespace.
*/
fc = fs_context_for_submount(&nfs_fs_type, path->dentry);
if (IS_ERR(fc))
return ERR_CAST(fc);
mnt = server->nfs_client->rpc_ops->submount(server, path->dentry, fh, fattr);
ctx = nfs_fc2context(fc);
ctx->clone_data.dentry = path->dentry;
ctx->clone_data.sb = path->dentry->d_sb;
ctx->clone_data.fattr = nfs_alloc_fattr();
if (!ctx->clone_data.fattr)
goto out_fc;
if (fc->net_ns != client->cl_net) {
put_net(fc->net_ns);
fc->net_ns = get_net(client->cl_net);
}
/* for submounts we want the same server; referrals will reassign */
memcpy(&ctx->nfs_server.address, &client->cl_addr, client->cl_addrlen);
ctx->nfs_server.addrlen = client->cl_addrlen;
ctx->nfs_server.port = server->port;
ctx->version = client->rpc_ops->version;
ctx->minorversion = client->cl_minorversion;
ctx->mount_info.nfs_mod = client->cl_nfs_mod;
__module_get(ctx->mount_info.nfs_mod->owner);
ret = client->rpc_ops->submount(fc, server);
if (ret < 0) {
mnt = ERR_PTR(ret);
goto out_fc;
}
up_write(&fc->root->d_sb->s_umount);
mnt = vfs_create_mount(fc);
if (IS_ERR(mnt))
goto out;
goto out_fc;
if (nfs_mountpoint_expiry_timeout < 0)
goto out;
goto out_fc;
mntget(mnt); /* prevent immediate expiration */
mnt_set_expiry(mnt, &nfs_automount_list);
schedule_delayed_work(&nfs_automount_task, nfs_mountpoint_expiry_timeout);
out:
nfs_free_fattr(fattr);
nfs_free_fhandle(fh);
out_fc:
put_fs_context(fc);
return mnt;
}
......@@ -222,61 +253,62 @@ void nfs_release_automount_timer(void)
* @authflavor: security flavor to use when performing the mount
*
*/
struct vfsmount *nfs_do_submount(struct dentry *dentry, struct nfs_fh *fh,
struct nfs_fattr *fattr, rpc_authflavor_t authflavor)
int nfs_do_submount(struct fs_context *fc)
{
struct super_block *sb = dentry->d_sb;
struct nfs_clone_mount mountdata = {
.sb = sb,
.dentry = dentry,
.authflavor = authflavor,
};
struct nfs_mount_info mount_info = {
.inherited_bsize = sb->s_blocksize_bits,
.cloned = &mountdata,
.mntfh = fh,
.nfs_mod = NFS_SB(sb)->nfs_client->cl_nfs_mod,
};
struct nfs_fs_context *ctx = nfs_fc2context(fc);
struct dentry *dentry = ctx->clone_data.dentry;
struct nfs_server *server;
struct vfsmount *mnt;
char *page = (char *) __get_free_page(GFP_USER);
char *devname;
char *buffer, *p;
int ret;
if (page == NULL)
return ERR_PTR(-ENOMEM);
/* create a new volume representation */
server = ctx->mount_info.nfs_mod->rpc_ops->clone_server(NFS_SB(ctx->clone_data.sb),
ctx->mount_info.mntfh,
ctx->clone_data.fattr,
ctx->selected_flavor);
server = mount_info.nfs_mod->rpc_ops->clone_server(NFS_SB(sb), fh,
fattr, authflavor);
if (IS_ERR(server))
return ERR_CAST(server);
return PTR_ERR(server);
mount_info.server = server;
ctx->mount_info.server = server;
devname = nfs_devname(dentry, page, PAGE_SIZE);
if (IS_ERR(devname))
mnt = ERR_CAST(devname);
else
mnt = vfs_submount(dentry, &nfs_prepared_fs_type, devname, &mount_info);
buffer = kmalloc(4096, GFP_USER);
if (!buffer)
return -ENOMEM;
if (mount_info.server)
nfs_free_server(mount_info.server);
free_page((unsigned long)page);
return mnt;
ctx->internal = true;
ctx->mount_info.inherited_bsize = ctx->clone_data.sb->s_blocksize_bits;
p = nfs_devname(dentry, buffer, 4096);
if (IS_ERR(p)) {
dprintk("NFS: Couldn't determine submount pathname\n");
ret = PTR_ERR(p);
} else {
ret = vfs_parse_fs_string(fc, "source", p, buffer + 4096 - p);
if (!ret)
ret = vfs_get_tree(fc);
}
kfree(buffer);
return ret;
}
EXPORT_SYMBOL_GPL(nfs_do_submount);
struct vfsmount *nfs_submount(struct nfs_server *server, struct dentry *dentry,
struct nfs_fh *fh, struct nfs_fattr *fattr)
int nfs_submount(struct fs_context *fc, struct nfs_server *server)
{
int err;
struct nfs_fs_context *ctx = nfs_fc2context(fc);
struct dentry *dentry = ctx->clone_data.dentry;
struct dentry *parent = dget_parent(dentry);
int err;
/* Look it up again to get its attributes */
err = server->nfs_client->rpc_ops->lookup(d_inode(parent), &dentry->d_name, fh, fattr, NULL);
err = server->nfs_client->rpc_ops->lookup(d_inode(parent), &dentry->d_name,
ctx->mount_info.mntfh, ctx->clone_data.fattr,
NULL);
dput(parent);
if (err != 0)
return ERR_PTR(err);
return err;
return nfs_do_submount(dentry, fh, fattr, server->client->cl_auth->au_flavor);
ctx->selected_flavor = server->client->cl_auth->au_flavor;
return nfs_do_submount(fc);
}
EXPORT_SYMBOL_GPL(nfs_submount);
......@@ -990,7 +990,7 @@ const struct nfs_rpc_ops nfs_v3_clientops = {
.nlmclnt_ops = &nlmclnt_fl_close_lock_ops,
.getroot = nfs3_proc_get_root,
.submount = nfs_submount,
.try_mount = nfs_try_mount,
.try_get_tree = nfs_try_get_tree,
.getattr = nfs3_proc_getattr,
.setattr = nfs3_proc_setattr,
.lookup = nfs3_proc_lookup,
......
......@@ -268,14 +268,13 @@ extern const struct dentry_operations nfs4_dentry_operations;
int nfs_atomic_open(struct inode *, struct dentry *, struct file *,
unsigned, umode_t);
/* super.c */
/* fs_context.c */
extern struct file_system_type nfs4_fs_type;
/* nfs4namespace.c */
struct rpc_clnt *nfs4_negotiate_security(struct rpc_clnt *, struct inode *,
const struct qstr *);
struct vfsmount *nfs4_submount(struct nfs_server *, struct dentry *,
struct nfs_fh *, struct nfs_fattr *);
int nfs4_submount(struct fs_context *, struct nfs_server *);
int nfs4_replace_transport(struct nfs_server *server,
const struct nfs4_fs_locations *locations);
......@@ -526,7 +525,6 @@ extern const nfs4_stateid invalid_stateid;
/* nfs4super.c */
struct nfs_mount_info;
extern struct nfs_subversion nfs_v4;
struct dentry *nfs4_try_mount(int, const char *, struct nfs_mount_info *);
extern bool nfs4_disable_idmapping;
extern unsigned short max_session_slots;
extern unsigned short max_session_cb_slots;
......@@ -536,6 +534,9 @@ extern bool recover_lost_locks;
#define NFS4_CLIENT_ID_UNIQ_LEN (64)
extern char nfs4_client_id_uniquifier[NFS4_CLIENT_ID_UNIQ_LEN];
extern int nfs4_try_get_tree(struct fs_context *);
extern int nfs4_get_referral_tree(struct fs_context *);
/* nfs4sysctl.c */
#ifdef CONFIG_SYSCTL
int nfs4_register_sysctl(void);
......
......@@ -7,6 +7,7 @@
#include <linux/fs.h>
#include <linux/file.h>
#include <linux/falloc.h>
#include <linux/mount.h>
#include <linux/nfs_fs.h>
#include "delegation.h"
#include "internal.h"
......
This diff is collapsed.
......@@ -10001,7 +10001,7 @@ const struct nfs_rpc_ops nfs_v4_clientops = {
.file_ops = &nfs4_file_operations,
.getroot = nfs4_proc_get_root,
.submount = nfs4_submount,
.try_mount = nfs4_try_mount,
.try_get_tree = nfs4_try_get_tree,
.getattr = nfs4_proc_getattr,
.setattr = nfs4_proc_setattr,
.lookup = nfs4_proc_lookup,
......
......@@ -4,6 +4,7 @@
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/mount.h>
#include <linux/nfs4_mount.h>
#include <linux/nfs_fs.h>
#include "delegation.h"
......@@ -18,16 +19,6 @@
static int nfs4_write_inode(struct inode *inode, struct writeback_control *wbc);
static void nfs4_evict_inode(struct inode *inode);
static struct dentry *nfs4_referral_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, void *raw_data);
struct file_system_type nfs4_referral_fs_type = {
.owner = THIS_MODULE,
.name = "nfs4",
.mount = nfs4_referral_mount,
.kill_sb = nfs_kill_super,
.fs_flags = FS_RENAME_DOES_D_MOVE|FS_BINARY_MOUNTDATA,
};
static const struct super_operations nfs4_sops = {
.alloc_inode = nfs_alloc_inode,
......@@ -41,7 +32,6 @@ static const struct super_operations nfs4_sops = {
.show_devname = nfs_show_devname,
.show_path = nfs_show_path,
.show_stats = nfs_show_stats,
.remount_fs = nfs_remount,
};
struct nfs_subversion nfs_v4 = {
......@@ -147,102 +137,121 @@ static void nfs_referral_loop_unprotect(void)
kfree(p);
}
static struct dentry *do_nfs4_mount(struct nfs_server *server, int flags,
struct nfs_mount_info *info,
const char *hostname,
const char *export_path)
static int do_nfs4_mount(struct nfs_server *server,
struct fs_context *fc,
const char *hostname,
const char *export_path)
{
struct nfs_fs_context *root_ctx;
struct fs_context *root_fc;
struct vfsmount *root_mnt;
struct dentry *dentry;
char *root_devname;
int err;
size_t len;
int ret;
struct fs_parameter param = {
.key = "source",
.type = fs_value_is_string,
.dirfd = -1,
};
if (IS_ERR(server))
return ERR_CAST(server);
return PTR_ERR(server);
len = strlen(hostname) + 5;
root_devname = kmalloc(len, GFP_KERNEL);
if (root_devname == NULL) {
root_fc = vfs_dup_fs_context(fc);
if (IS_ERR(root_fc)) {
nfs_free_server(server);
return ERR_PTR(-ENOMEM);
return PTR_ERR(root_fc);
}
kfree(root_fc->source);
root_fc->source = NULL;
root_ctx = nfs_fc2context(root_fc);
root_ctx->internal = true;
root_ctx->mount_info.server = server;
/* We leave export_path unset as it's not used to find the root. */
len = strlen(hostname) + 5;
param.string = kmalloc(len, GFP_KERNEL);
if (param.string == NULL) {
put_fs_context(root_fc);
return -ENOMEM;
}
/* Does hostname needs to be enclosed in brackets? */
if (strchr(hostname, ':'))
snprintf(root_devname, len, "[%s]:/", hostname);
param.size = snprintf(param.string, len, "[%s]:/", hostname);
else
snprintf(root_devname, len, "%s:/", hostname);
info->server = server;
root_mnt = vfs_kern_mount(&nfs_prepared_fs_type, flags, root_devname, info);
if (info->server)
nfs_free_server(info->server);
info->server = NULL;
kfree(root_devname);
param.size = snprintf(param.string, len, "%s:/", hostname);
ret = vfs_parse_fs_param(root_fc, &param);
kfree(param.string);
if (ret < 0) {
put_fs_context(root_fc);
return ret;
}
root_mnt = fc_mount(root_fc);
put_fs_context(root_fc);
if (IS_ERR(root_mnt))
return ERR_CAST(root_mnt);
return PTR_ERR(root_mnt);
err = nfs_referral_loop_protect();
if (err) {
ret = nfs_referral_loop_protect();
if (ret) {
mntput(root_mnt);
return ERR_PTR(err);
return ret;
}
dentry = mount_subtree(root_mnt, export_path);
nfs_referral_loop_unprotect();
return dentry;
if (IS_ERR(dentry))
return PTR_ERR(dentry);
fc->root = dentry;
return 0;
}
struct dentry *nfs4_try_mount(int flags, const char *dev_name,
struct nfs_mount_info *mount_info)
int nfs4_try_get_tree(struct fs_context *fc)
{
struct nfs_fs_context *ctx = mount_info->ctx;
struct dentry *res;
dfprintk(MOUNT, "--> nfs4_try_mount()\n");
struct nfs_fs_context *ctx = nfs_fc2context(fc);
int err;
res = do_nfs4_mount(nfs4_create_server(mount_info),
flags, mount_info,
ctx->nfs_server.hostname,
ctx->nfs_server.export_path);
dfprintk(MOUNT, "--> nfs4_try_get_tree()\n");
dfprintk(MOUNT, "<-- nfs4_try_mount() = %d%s\n",
PTR_ERR_OR_ZERO(res),
IS_ERR(res) ? " [error]" : "");
return res;
/* We create a mount for the server's root, walk to the requested
* location and then create another mount for that.
*/
err= do_nfs4_mount(nfs4_create_server(&ctx->mount_info),
fc, ctx->nfs_server.hostname,
ctx->nfs_server.export_path);
if (err) {
dfprintk(MOUNT, "<-- nfs4_try_get_tree() = %d [error]\n", err);
} else {
dfprintk(MOUNT, "<-- nfs4_try_get_tree() = 0\n");
}
return err;
}
/*
* Create an NFS4 server record on referral traversal
*/
static struct dentry *nfs4_referral_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, void *raw_data)
int nfs4_get_referral_tree(struct fs_context *fc)
{
struct nfs_clone_mount *data = raw_data;
struct nfs_mount_info mount_info = {
.cloned = data,
.nfs_mod = &nfs_v4,
};
struct dentry *res;
struct nfs_fs_context *ctx = nfs_fc2context(fc);
int err;
dprintk("--> nfs4_referral_mount()\n");
mount_info.mntfh = nfs_alloc_fhandle();
if (!mount_info.mntfh)
return ERR_PTR(-ENOMEM);
res = do_nfs4_mount(nfs4_create_referral_server(mount_info.cloned,
mount_info.mntfh),
flags, &mount_info, data->hostname, data->mnt_path);
dprintk("<-- nfs4_referral_mount() = %d%s\n",
PTR_ERR_OR_ZERO(res),
IS_ERR(res) ? " [error]" : "");
nfs_free_fhandle(mount_info.mntfh);
return res;
/* create a new volume representation */
err = do_nfs4_mount(nfs4_create_referral_server(&ctx->clone_data, ctx->mount_info.mntfh),
fc, ctx->nfs_server.hostname,
ctx->nfs_server.export_path);
if (err) {
dfprintk(MOUNT, "<-- nfs4_get_referral_tree() = %d [error]\n", err);
} else {
dfprintk(MOUNT, "<-- nfs4_get_referral_tree() = 0\n");
}
return err;
}
......
......@@ -710,7 +710,7 @@ const struct nfs_rpc_ops nfs_v2_clientops = {
.file_ops = &nfs_file_operations,
.getroot = nfs_proc_get_root,
.submount = nfs_submount,
.try_mount = nfs_try_mount,
.try_get_tree = nfs_try_get_tree,
.getattr = nfs_proc_getattr,
.setattr = nfs_proc_setattr,
.lookup = nfs_proc_lookup,
......
This diff is collapsed.
......@@ -1639,6 +1639,7 @@ struct nfs_subversion;
struct nfs_mount_info;
struct nfs_client_initdata;
struct nfs_pageio_descriptor;
struct fs_context;
/*
* RPC procedure vector for NFSv2/NFSv3 demuxing
......@@ -1653,9 +1654,8 @@ struct nfs_rpc_ops {
int (*getroot) (struct nfs_server *, struct nfs_fh *,
struct nfs_fsinfo *);
struct vfsmount *(*submount) (struct nfs_server *, struct dentry *,
struct nfs_fh *, struct nfs_fattr *);
struct dentry *(*try_mount) (int, const char *, struct nfs_mount_info *);
int (*submount) (struct fs_context *, struct nfs_server *);
int (*try_get_tree) (struct fs_context *);
int (*getattr) (struct nfs_server *, struct nfs_fh *,
struct nfs_fattr *, struct nfs4_label *,
struct inode *);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment