Skip to content

Commit

Permalink
NFS: Add fs_context support.
Browse files Browse the repository at this point in the history
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: David Howells <dhowells@redhat.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Scott Mayhew <smayhew@redhat.com>
Signed-off-by: Anna Schumaker <Anna.Schumaker@Netapp.com>
  • Loading branch information
dhowells authored and amschuma-ntap committed Jan 15, 2020
1 parent e38bb23 commit f2aedb7
Show file tree
Hide file tree
Showing 12 changed files with 773 additions and 659 deletions.
470 changes: 316 additions & 154 deletions fs/nfs/fs_context.c

Large diffs are not rendered by default.

74 changes: 33 additions & 41 deletions fs/nfs/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand All @@ -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)
{
Expand All @@ -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;
};

/*
Expand Down Expand Up @@ -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;
Expand All @@ -98,8 +113,6 @@ struct nfs_fs_context {
char *fscache_uniq;
unsigned short protofamily;
unsigned short mountfamily;
bool need_mount;
bool sloppy;

struct {
union {
Expand All @@ -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;
Expand All @@ -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);

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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 *,
Expand All @@ -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,
Expand Down
136 changes: 84 additions & 52 deletions fs/nfs/namespace.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down Expand Up @@ -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);
2 changes: 1 addition & 1 deletion fs/nfs/nfs3proc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
9 changes: 5 additions & 4 deletions fs/nfs/nfs4_fs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -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;
Expand All @@ -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);
Expand Down
Loading

0 comments on commit f2aedb7

Please sign in to comment.