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 @@ ...@@ -4,7 +4,7 @@
*/ */
#include "nfs4_fs.h" #include "nfs4_fs.h"
#include <linux/mount.h> #include <linux/fs_context.h>
#include <linux/security.h> #include <linux/security.h>
#include <linux/crc32.h> #include <linux/crc32.h>
#include <linux/sunrpc/addr.h> #include <linux/sunrpc/addr.h>
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
extern const struct export_operations nfs_export_ops; extern const struct export_operations nfs_export_ops;
struct nfs_string; struct nfs_string;
struct nfs_pageio_descriptor;
static inline void nfs_attr_check_mountpoint(struct super_block *parent, struct nfs_fattr *fattr) 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) ...@@ -34,12 +35,13 @@ static inline int nfs_attr_use_mounted_on_fileid(struct nfs_fattr *fattr)
struct nfs_clone_mount { struct nfs_clone_mount {
const struct super_block *sb; const struct super_block *sb;
const struct dentry *dentry; struct dentry *dentry;
char *hostname; char *hostname;
char *mnt_path; char *mnt_path;
struct sockaddr *addr; struct sockaddr *addr;
size_t addrlen; size_t addrlen;
rpc_authflavor_t authflavor; rpc_authflavor_t authflavor;
struct nfs_fattr *fattr;
}; };
/* /*
...@@ -78,10 +80,23 @@ struct nfs_client_initdata { ...@@ -78,10 +80,23 @@ struct nfs_client_initdata {
const struct cred *cred; 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 * In-kernel mount arguments
*/ */
struct nfs_fs_context { 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 flags; /* NFS{,4}_MOUNT_* flags */
unsigned int rsize, wsize; unsigned int rsize, wsize;
unsigned int timeo, retrans; unsigned int timeo, retrans;
...@@ -98,8 +113,6 @@ struct nfs_fs_context { ...@@ -98,8 +113,6 @@ struct nfs_fs_context {
char *fscache_uniq; char *fscache_uniq;
unsigned short protofamily; unsigned short protofamily;
unsigned short mountfamily; unsigned short mountfamily;
bool need_mount;
bool sloppy;
struct { struct {
union { union {
...@@ -124,14 +137,23 @@ struct nfs_fs_context { ...@@ -124,14 +137,23 @@ struct nfs_fs_context {
int port; int port;
unsigned short protocol; unsigned short protocol;
unsigned short nconnect; unsigned short nconnect;
unsigned short export_path_len;
} nfs_server; } nfs_server;
void *lsm_opts; void *lsm_opts;
struct net *net; struct net *net;
char buf[32]; /* Parse buffer */ 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 */ /* mount_clnt.c */
struct nfs_mount_request { struct nfs_mount_request {
struct sockaddr *sap; struct sockaddr *sap;
...@@ -147,15 +169,6 @@ struct nfs_mount_request { ...@@ -147,15 +169,6 @@ struct nfs_mount_request {
struct net *net; 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 int nfs_mount(struct nfs_mount_request *info);
extern void nfs_umount(const 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) ...@@ -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_version1;
extern const struct svc_version nfs4_callback_version4; extern const struct svc_version nfs4_callback_version4;
struct nfs_pageio_descriptor; /* fs_context.c */
extern struct file_system_type nfs_fs_type;
/* 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);
/* pagelist.c */ /* pagelist.c */
extern int __init nfs_init_nfspagecache(void); extern int __init nfs_init_nfspagecache(void);
...@@ -411,14 +410,9 @@ extern int nfs_wait_atomic_killable(atomic_t *p, unsigned int mode); ...@@ -411,14 +410,9 @@ extern int nfs_wait_atomic_killable(atomic_t *p, unsigned int mode);
/* super.c */ /* super.c */
extern const struct super_operations nfs_sops; 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); 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 *); int nfs_try_get_tree(struct fs_context *);
struct dentry *nfs_fs_mount(struct file_system_type *, int, const char *, void *); int nfs_get_tree_common(struct fs_context *);
void nfs_kill_super(struct super_block *); void nfs_kill_super(struct super_block *);
extern struct rpc_stat nfs_rpcstat; extern struct rpc_stat nfs_rpcstat;
...@@ -446,10 +440,8 @@ static inline bool nfs_file_io_is_buffered(struct nfs_inode *nfsi) ...@@ -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, extern char *nfs_path(char **p, struct dentry *dentry,
char *buffer, ssize_t buflen, unsigned flags); char *buffer, ssize_t buflen, unsigned flags);
extern struct vfsmount *nfs_d_automount(struct path *path); extern struct vfsmount *nfs_d_automount(struct path *path);
struct vfsmount *nfs_submount(struct nfs_server *, struct dentry *, int nfs_submount(struct fs_context *, struct nfs_server *);
struct nfs_fh *, struct nfs_fattr *); int nfs_do_submount(struct fs_context *);
struct vfsmount *nfs_do_submount(struct dentry *, struct nfs_fh *,
struct nfs_fattr *, rpc_authflavor_t);
/* getroot.c */ /* getroot.c */
extern struct dentry *nfs_get_root(struct super_block *, struct nfs_fh *, 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 *); ...@@ -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_devname(struct seq_file *, struct dentry *);
int nfs_show_path(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_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 */ /* write.c */
extern void nfs_pageio_init_write(struct nfs_pageio_descriptor *pgio, extern void nfs_pageio_init_write(struct nfs_pageio_descriptor *pgio,
......
...@@ -140,34 +140,65 @@ EXPORT_SYMBOL_GPL(nfs_path); ...@@ -140,34 +140,65 @@ EXPORT_SYMBOL_GPL(nfs_path);
*/ */
struct vfsmount *nfs_d_automount(struct path *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_server *server = NFS_SERVER(d_inode(path->dentry));
struct nfs_fh *fh = NULL; struct nfs_client *client = server->nfs_client;
struct nfs_fattr *fattr = NULL; int ret;
if (IS_ROOT(path->dentry)) if (IS_ROOT(path->dentry))
return ERR_PTR(-ESTALE); return ERR_PTR(-ESTALE);
mnt = ERR_PTR(-ENOMEM); /* Open a new filesystem context, transferring parameters from the
fh = nfs_alloc_fhandle(); * parent superblock, including the network namespace.
fattr = nfs_alloc_fattr(); */
if (fh == NULL || fattr == NULL) fc = fs_context_for_submount(&nfs_fs_type, path->dentry);
goto out; 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)) if (IS_ERR(mnt))
goto out; goto out_fc;
if (nfs_mountpoint_expiry_timeout < 0) if (nfs_mountpoint_expiry_timeout < 0)
goto out; goto out_fc;
mntget(mnt); /* prevent immediate expiration */ mntget(mnt); /* prevent immediate expiration */
mnt_set_expiry(mnt, &nfs_automount_list); mnt_set_expiry(mnt, &nfs_automount_list);
schedule_delayed_work(&nfs_automount_task, nfs_mountpoint_expiry_timeout); schedule_delayed_work(&nfs_automount_task, nfs_mountpoint_expiry_timeout);
out: out_fc:
nfs_free_fattr(fattr); put_fs_context(fc);
nfs_free_fhandle(fh);
return mnt; return mnt;
} }
...@@ -222,61 +253,62 @@ void nfs_release_automount_timer(void) ...@@ -222,61 +253,62 @@ void nfs_release_automount_timer(void)
* @authflavor: security flavor to use when performing the mount * @authflavor: security flavor to use when performing the mount
* *
*/ */
struct vfsmount *nfs_do_submount(struct dentry *dentry, struct nfs_fh *fh, int nfs_do_submount(struct fs_context *fc)
struct nfs_fattr *fattr, rpc_authflavor_t authflavor)
{ {
struct super_block *sb = dentry->d_sb; struct nfs_fs_context *ctx = nfs_fc2context(fc);
struct nfs_clone_mount mountdata = { struct dentry *dentry = ctx->clone_data.dentry;
.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_server *server; struct nfs_server *server;
struct vfsmount *mnt; char *buffer, *p;
char *page = (char *) __get_free_page(GFP_USER); int ret;
char *devname;
if (page == NULL) /* create a new volume representation */
return ERR_PTR(-ENOMEM); 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)) 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); buffer = kmalloc(4096, GFP_USER);
if (IS_ERR(devname)) if (!buffer)
mnt = ERR_CAST(devname); return -ENOMEM;
else
mnt = vfs_submount(dentry, &nfs_prepared_fs_type, devname, &mount_info);
if (mount_info.server) ctx->internal = true;
nfs_free_server(mount_info.server); ctx->mount_info.inherited_bsize = ctx->clone_data.sb->s_blocksize_bits;
free_page((unsigned long)page);
return mnt; 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); EXPORT_SYMBOL_GPL(nfs_do_submount);
struct vfsmount *nfs_submount(struct nfs_server *server, struct dentry *dentry, int nfs_submount(struct fs_context *fc, struct nfs_server *server)
struct nfs_fh *fh, struct nfs_fattr *fattr)
{ {
int err; struct nfs_fs_context *ctx = nfs_fc2context(fc);
struct dentry *dentry = ctx->clone_data.dentry;
struct dentry *parent = dget_parent(dentry); struct dentry *parent = dget_parent(dentry);
int err;
/* Look it up again to get its attributes */ /* 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); dput(parent);
if (err != 0) 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); EXPORT_SYMBOL_GPL(nfs_submount);
...@@ -990,7 +990,7 @@ const struct nfs_rpc_ops nfs_v3_clientops = { ...@@ -990,7 +990,7 @@ const struct nfs_rpc_ops nfs_v3_clientops = {
.nlmclnt_ops = &nlmclnt_fl_close_lock_ops, .nlmclnt_ops = &nlmclnt_fl_close_lock_ops,
.getroot = nfs3_proc_get_root, .getroot = nfs3_proc_get_root,
.submount = nfs_submount, .submount = nfs_submount,
.try_mount = nfs_try_mount, .try_get_tree = nfs_try_get_tree,
.getattr = nfs3_proc_getattr, .getattr = nfs3_proc_getattr,
.setattr = nfs3_proc_setattr, .setattr = nfs3_proc_setattr,
.lookup = nfs3_proc_lookup, .lookup = nfs3_proc_lookup,
......
...@@ -268,14 +268,13 @@ extern const struct dentry_operations nfs4_dentry_operations; ...@@ -268,14 +268,13 @@ extern const struct dentry_operations nfs4_dentry_operations;
int nfs_atomic_open(struct inode *, struct dentry *, struct file *, int nfs_atomic_open(struct inode *, struct dentry *, struct file *,
unsigned, umode_t); unsigned, umode_t);
/* super.c */ /* fs_context.c */
extern struct file_system_type nfs4_fs_type; extern struct file_system_type nfs4_fs_type;
/* nfs4namespace.c */ /* nfs4namespace.c */
struct rpc_clnt *nfs4_negotiate_security(struct rpc_clnt *, struct inode *, struct rpc_clnt *nfs4_negotiate_security(struct rpc_clnt *, struct inode *,
const struct qstr *); const struct qstr *);
struct vfsmount *nfs4_submount(struct nfs_server *, struct dentry *, int nfs4_submount(struct fs_context *, struct nfs_server *);
struct nfs_fh *, struct nfs_fattr *);
int nfs4_replace_transport(struct nfs_server *server, int nfs4_replace_transport(struct nfs_server *server,
const struct nfs4_fs_locations *locations); const struct nfs4_fs_locations *locations);
...@@ -526,7 +525,6 @@ extern const nfs4_stateid invalid_stateid; ...@@ -526,7 +525,6 @@ extern const nfs4_stateid invalid_stateid;
/* nfs4super.c */ /* nfs4super.c */
struct nfs_mount_info; struct nfs_mount_info;
extern struct nfs_subversion nfs_v4; extern struct nfs_subversion nfs_v4;
struct dentry *nfs4_try_mount(int, const char *, struct nfs_mount_info *);
extern bool nfs4_disable_idmapping; extern bool nfs4_disable_idmapping;
extern unsigned short max_session_slots; extern unsigned short max_session_slots;
extern unsigned short max_session_cb_slots; extern unsigned short max_session_cb_slots;
...@@ -536,6 +534,9 @@ extern bool recover_lost_locks; ...@@ -536,6 +534,9 @@ extern bool recover_lost_locks;
#define NFS4_CLIENT_ID_UNIQ_LEN (64) #define NFS4_CLIENT_ID_UNIQ_LEN (64)
extern char nfs4_client_id_uniquifier[NFS4_CLIENT_ID_UNIQ_LEN]; 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 */ /* nfs4sysctl.c */
#ifdef CONFIG_SYSCTL #ifdef CONFIG_SYSCTL
int nfs4_register_sysctl(void); int nfs4_register_sysctl(void);
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/file.h> #include <linux/file.h>
#include <linux/falloc.h> #include <linux/falloc.h>
#include <linux/mount.h>
#include <linux/nfs_fs.h> #include <linux/nfs_fs.h>
#include "delegation.h" #include "delegation.h"
#include "internal.h" #include "internal.h"
......
This diff is collapsed.
...@@ -10001,7 +10001,7 @@ const struct nfs_rpc_ops nfs_v4_clientops = { ...@@ -10001,7 +10001,7 @@ const struct nfs_rpc_ops nfs_v4_clientops = {
.file_ops = &nfs4_file_operations, .file_ops = &nfs4_file_operations,
.getroot = nfs4_proc_get_root, .getroot = nfs4_proc_get_root,
.submount = nfs4_submount, .submount = nfs4_submount,
.try_mount = nfs4_try_mount, .try_get_tree = nfs4_try_get_tree,
.getattr = nfs4_proc_getattr, .getattr = nfs4_proc_getattr,
.setattr = nfs4_proc_setattr, .setattr = nfs4_proc_setattr,
.lookup = nfs4_proc_lookup, .lookup = nfs4_proc_lookup,
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
*/ */
#include <linux/init.h> #include <linux/init.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/mount.h>
#include <linux/nfs4_mount.h> #include <linux/nfs4_mount.h>
#include <linux/nfs_fs.h> #include <linux/nfs_fs.h>
#include "delegation.h" #include "delegation.h"
...@@ -18,16 +19,6 @@ ...@@ -18,16 +19,6 @@
static int nfs4_write_inode(struct inode *inode, struct writeback_control *wbc); static int nfs4_write_inode(struct inode *inode, struct writeback_control *wbc);
static void nfs4_evict_inode(struct inode *inode); 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 = { static const struct super_operations nfs4_sops = {
.alloc_inode = nfs_alloc_inode, .alloc_inode = nfs_alloc_inode,
...@@ -41,7 +32,6 @@ static const struct super_operations nfs4_sops = { ...@@ -41,7 +32,6 @@ static const struct super_operations nfs4_sops = {
.show_devname = nfs_show_devname, .show_devname = nfs_show_devname,
.show_path = nfs_show_path, .show_path = nfs_show_path,
.show_stats = nfs_show_stats, .show_stats = nfs_show_stats,
.remount_fs = nfs_remount,
}; };
struct nfs_subversion nfs_v4 = { struct nfs_subversion nfs_v4 = {
...@@ -147,102 +137,121 @@ static void nfs_referral_loop_unprotect(void) ...@@ -147,102 +137,121 @@ static void nfs_referral_loop_unprotect(void)
kfree(p); kfree(p);
} }
static struct dentry *do_nfs4_mount(struct nfs_server *server, int flags, static int do_nfs4_mount(struct nfs_server *server,
struct nfs_mount_info *info, struct fs_context *fc,
const char *hostname, const char *hostname,
const char *export_path) const char *export_path)
{ {
struct nfs_fs_context *root_ctx;
struct fs_context *root_fc;
struct vfsmount *root_mnt; struct vfsmount *root_mnt;
struct dentry *dentry; struct dentry *dentry;
char *root_devname;
int err;
size_t len; size_t len;
int ret;
struct fs_parameter param = {
.key = "source",
.type = fs_value_is_string,
.dirfd = -1,
};
if (IS_ERR(server)) if (IS_ERR(server))
return ERR_CAST(server); return PTR_ERR(server);
len = strlen(hostname) + 5; root_fc = vfs_dup_fs_context(fc);
root_devname = kmalloc(len, GFP_KERNEL); if (IS_ERR(root_fc)) {
if (root_devname == NULL) {
nfs_free_server(server); 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? */ /* Does hostname needs to be enclosed in brackets? */
if (strchr(hostname, ':')) if (strchr(hostname, ':'))
snprintf(root_devname, len, "[%s]:/", hostname); param.size = snprintf(param.string, len, "[%s]:/", hostname);
else else
snprintf(root_devname, len, "%s:/", hostname); param.size = snprintf(param.string, len, "%s:/", hostname);
info->server = server; ret = vfs_parse_fs_param(root_fc, &param);
root_mnt = vfs_kern_mount(&nfs_prepared_fs_type, flags, root_devname, info); kfree(param.string);
if (info->server) if (ret < 0) {
nfs_free_server(info->server); put_fs_context(root_fc);
info->server = NULL; return ret;
kfree(root_devname); }
root_mnt = fc_mount(root_fc);
put_fs_context(root_fc);
if (IS_ERR(root_mnt)) if (IS_ERR(root_mnt))
return ERR_CAST(root_mnt); return PTR_ERR(root_mnt);
err = nfs_referral_loop_protect(); ret = nfs_referral_loop_protect();
if (err) { if (ret) {
mntput(root_mnt); mntput(root_mnt);
return ERR_PTR(err); return ret;
} }
dentry = mount_subtree(root_mnt, export_path); dentry = mount_subtree(root_mnt, export_path);
nfs_referral_loop_unprotect(); 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, int nfs4_try_get_tree(struct fs_context *fc)
struct nfs_mount_info *mount_info)
{ {
struct nfs_fs_context *ctx = mount_info->ctx; struct nfs_fs_context *ctx = nfs_fc2context(fc);
struct dentry *res; int err;
dfprintk(MOUNT, "--> nfs4_try_mount()\n");
res = do_nfs4_mount(nfs4_create_server(mount_info), dfprintk(MOUNT, "--> nfs4_try_get_tree()\n");
flags, mount_info,
ctx->nfs_server.hostname,
ctx->nfs_server.export_path);
dfprintk(MOUNT, "<-- nfs4_try_mount() = %d%s\n", /* We create a mount for the server's root, walk to the requested
PTR_ERR_OR_ZERO(res), * location and then create another mount for that.
IS_ERR(res) ? " [error]" : ""); */
return res; 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 * Create an NFS4 server record on referral traversal
*/ */
static struct dentry *nfs4_referral_mount(struct file_system_type *fs_type, int nfs4_get_referral_tree(struct fs_context *fc)
int flags, const char *dev_name, void *raw_data)
{ {
struct nfs_clone_mount *data = raw_data; struct nfs_fs_context *ctx = nfs_fc2context(fc);
struct nfs_mount_info mount_info = { int err;
.cloned = data,
.nfs_mod = &nfs_v4,
};
struct dentry *res;
dprintk("--> nfs4_referral_mount()\n"); dprintk("--> nfs4_referral_mount()\n");
mount_info.mntfh = nfs_alloc_fhandle(); /* create a new volume representation */
if (!mount_info.mntfh) err = do_nfs4_mount(nfs4_create_referral_server(&ctx->clone_data, ctx->mount_info.mntfh),
return ERR_PTR(-ENOMEM); fc, ctx->nfs_server.hostname,
ctx->nfs_server.export_path);
res = do_nfs4_mount(nfs4_create_referral_server(mount_info.cloned, if (err) {
mount_info.mntfh), dfprintk(MOUNT, "<-- nfs4_get_referral_tree() = %d [error]\n", err);
flags, &mount_info, data->hostname, data->mnt_path); } else {
dfprintk(MOUNT, "<-- nfs4_get_referral_tree() = 0\n");
dprintk("<-- nfs4_referral_mount() = %d%s\n", }
PTR_ERR_OR_ZERO(res), return err;
IS_ERR(res) ? " [error]" : "");
nfs_free_fhandle(mount_info.mntfh);
return res;
} }
......
...@@ -710,7 +710,7 @@ const struct nfs_rpc_ops nfs_v2_clientops = { ...@@ -710,7 +710,7 @@ const struct nfs_rpc_ops nfs_v2_clientops = {
.file_ops = &nfs_file_operations, .file_ops = &nfs_file_operations,
.getroot = nfs_proc_get_root, .getroot = nfs_proc_get_root,
.submount = nfs_submount, .submount = nfs_submount,
.try_mount = nfs_try_mount, .try_get_tree = nfs_try_get_tree,
.getattr = nfs_proc_getattr, .getattr = nfs_proc_getattr,
.setattr = nfs_proc_setattr, .setattr = nfs_proc_setattr,
.lookup = nfs_proc_lookup, .lookup = nfs_proc_lookup,
......
This diff is collapsed.
...@@ -1639,6 +1639,7 @@ struct nfs_subversion; ...@@ -1639,6 +1639,7 @@ struct nfs_subversion;
struct nfs_mount_info; struct nfs_mount_info;
struct nfs_client_initdata; struct nfs_client_initdata;
struct nfs_pageio_descriptor; struct nfs_pageio_descriptor;
struct fs_context;
/* /*
* RPC procedure vector for NFSv2/NFSv3 demuxing * RPC procedure vector for NFSv2/NFSv3 demuxing
...@@ -1653,9 +1654,8 @@ struct nfs_rpc_ops { ...@@ -1653,9 +1654,8 @@ struct nfs_rpc_ops {
int (*getroot) (struct nfs_server *, struct nfs_fh *, int (*getroot) (struct nfs_server *, struct nfs_fh *,
struct nfs_fsinfo *); struct nfs_fsinfo *);
struct vfsmount *(*submount) (struct nfs_server *, struct dentry *, int (*submount) (struct fs_context *, struct nfs_server *);
struct nfs_fh *, struct nfs_fattr *); int (*try_get_tree) (struct fs_context *);
struct dentry *(*try_mount) (int, const char *, struct nfs_mount_info *);
int (*getattr) (struct nfs_server *, struct nfs_fh *, int (*getattr) (struct nfs_server *, struct nfs_fh *,
struct nfs_fattr *, struct nfs4_label *, struct nfs_fattr *, struct nfs4_label *,
struct inode *); 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