NFS: Fix /proc/fs/nfsfs/servers and /proc/fs/nfsfs/volumes

The usage of pid_ns->child_reaper->nsproxy->net_ns in
nfs_server_list_open and nfs_client_list_open is not safe.

/proc for a pid namespace can remain mounted after the all of the
process in that pid namespace have exited.  There are also times
before the initial process in a pid namespace has started or after the
initial process in a pid namespace has exited where
pid_ns->child_reaper can be NULL or stale.  Making the idiom
pid_ns->child_reaper->nsproxy a double whammy of problems.

Luckily all that needs to happen is to move /proc/fs/nfsfs/servers and
/proc/fs/nfsfs/volumes under /proc/net to /proc/net/nfsfs/servers and
/proc/net/nfsfs/volumes and add a symlink from the original location,
and to use seq_open_net as it has been designed.

Cc: stable@vger.kernel.org
Cc: Trond Myklebust <trond.myklebust@primarydata.com>
Cc: Stanislav Kinsbursky <skinsbursky@parallels.com>
Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com>
This commit is contained in:
Eric W. Biederman 2014-07-31 04:35:20 -07:00
parent db181ce011
commit 65b38851a1
4 changed files with 69 additions and 41 deletions

View file

@ -1205,7 +1205,7 @@ static const struct file_operations nfs_server_list_fops = {
.open = nfs_server_list_open, .open = nfs_server_list_open,
.read = seq_read, .read = seq_read,
.llseek = seq_lseek, .llseek = seq_lseek,
.release = seq_release, .release = seq_release_net,
.owner = THIS_MODULE, .owner = THIS_MODULE,
}; };
@ -1226,7 +1226,7 @@ static const struct file_operations nfs_volume_list_fops = {
.open = nfs_volume_list_open, .open = nfs_volume_list_open,
.read = seq_read, .read = seq_read,
.llseek = seq_lseek, .llseek = seq_lseek,
.release = seq_release, .release = seq_release_net,
.owner = THIS_MODULE, .owner = THIS_MODULE,
}; };
@ -1236,19 +1236,8 @@ static const struct file_operations nfs_volume_list_fops = {
*/ */
static int nfs_server_list_open(struct inode *inode, struct file *file) static int nfs_server_list_open(struct inode *inode, struct file *file)
{ {
struct seq_file *m; return seq_open_net(inode, file, &nfs_server_list_ops,
int ret; sizeof(struct seq_net_private));
struct pid_namespace *pid_ns = file->f_dentry->d_sb->s_fs_info;
struct net *net = pid_ns->child_reaper->nsproxy->net_ns;
ret = seq_open(file, &nfs_server_list_ops);
if (ret < 0)
return ret;
m = file->private_data;
m->private = net;
return 0;
} }
/* /*
@ -1256,7 +1245,7 @@ static int nfs_server_list_open(struct inode *inode, struct file *file)
*/ */
static void *nfs_server_list_start(struct seq_file *m, loff_t *_pos) static void *nfs_server_list_start(struct seq_file *m, loff_t *_pos)
{ {
struct nfs_net *nn = net_generic(m->private, nfs_net_id); struct nfs_net *nn = net_generic(seq_file_net(m), nfs_net_id);
/* lock the list against modification */ /* lock the list against modification */
spin_lock(&nn->nfs_client_lock); spin_lock(&nn->nfs_client_lock);
@ -1268,7 +1257,7 @@ static void *nfs_server_list_start(struct seq_file *m, loff_t *_pos)
*/ */
static void *nfs_server_list_next(struct seq_file *p, void *v, loff_t *pos) static void *nfs_server_list_next(struct seq_file *p, void *v, loff_t *pos)
{ {
struct nfs_net *nn = net_generic(p->private, nfs_net_id); struct nfs_net *nn = net_generic(seq_file_net(p), nfs_net_id);
return seq_list_next(v, &nn->nfs_client_list, pos); return seq_list_next(v, &nn->nfs_client_list, pos);
} }
@ -1278,7 +1267,7 @@ static void *nfs_server_list_next(struct seq_file *p, void *v, loff_t *pos)
*/ */
static void nfs_server_list_stop(struct seq_file *p, void *v) static void nfs_server_list_stop(struct seq_file *p, void *v)
{ {
struct nfs_net *nn = net_generic(p->private, nfs_net_id); struct nfs_net *nn = net_generic(seq_file_net(p), nfs_net_id);
spin_unlock(&nn->nfs_client_lock); spin_unlock(&nn->nfs_client_lock);
} }
@ -1289,7 +1278,7 @@ static void nfs_server_list_stop(struct seq_file *p, void *v)
static int nfs_server_list_show(struct seq_file *m, void *v) static int nfs_server_list_show(struct seq_file *m, void *v)
{ {
struct nfs_client *clp; struct nfs_client *clp;
struct nfs_net *nn = net_generic(m->private, nfs_net_id); struct nfs_net *nn = net_generic(seq_file_net(m), nfs_net_id);
/* display header on line 1 */ /* display header on line 1 */
if (v == &nn->nfs_client_list) { if (v == &nn->nfs_client_list) {
@ -1321,19 +1310,8 @@ static int nfs_server_list_show(struct seq_file *m, void *v)
*/ */
static int nfs_volume_list_open(struct inode *inode, struct file *file) static int nfs_volume_list_open(struct inode *inode, struct file *file)
{ {
struct seq_file *m; return seq_open_net(inode, file, &nfs_server_list_ops,
int ret; sizeof(struct seq_net_private));
struct pid_namespace *pid_ns = file->f_dentry->d_sb->s_fs_info;
struct net *net = pid_ns->child_reaper->nsproxy->net_ns;
ret = seq_open(file, &nfs_volume_list_ops);
if (ret < 0)
return ret;
m = file->private_data;
m->private = net;
return 0;
} }
/* /*
@ -1341,7 +1319,7 @@ static int nfs_volume_list_open(struct inode *inode, struct file *file)
*/ */
static void *nfs_volume_list_start(struct seq_file *m, loff_t *_pos) static void *nfs_volume_list_start(struct seq_file *m, loff_t *_pos)
{ {
struct nfs_net *nn = net_generic(m->private, nfs_net_id); struct nfs_net *nn = net_generic(seq_file_net(m), nfs_net_id);
/* lock the list against modification */ /* lock the list against modification */
spin_lock(&nn->nfs_client_lock); spin_lock(&nn->nfs_client_lock);
@ -1353,7 +1331,7 @@ static void *nfs_volume_list_start(struct seq_file *m, loff_t *_pos)
*/ */
static void *nfs_volume_list_next(struct seq_file *p, void *v, loff_t *pos) static void *nfs_volume_list_next(struct seq_file *p, void *v, loff_t *pos)
{ {
struct nfs_net *nn = net_generic(p->private, nfs_net_id); struct nfs_net *nn = net_generic(seq_file_net(p), nfs_net_id);
return seq_list_next(v, &nn->nfs_volume_list, pos); return seq_list_next(v, &nn->nfs_volume_list, pos);
} }
@ -1363,7 +1341,7 @@ static void *nfs_volume_list_next(struct seq_file *p, void *v, loff_t *pos)
*/ */
static void nfs_volume_list_stop(struct seq_file *p, void *v) static void nfs_volume_list_stop(struct seq_file *p, void *v)
{ {
struct nfs_net *nn = net_generic(p->private, nfs_net_id); struct nfs_net *nn = net_generic(seq_file_net(p), nfs_net_id);
spin_unlock(&nn->nfs_client_lock); spin_unlock(&nn->nfs_client_lock);
} }
@ -1376,7 +1354,7 @@ static int nfs_volume_list_show(struct seq_file *m, void *v)
struct nfs_server *server; struct nfs_server *server;
struct nfs_client *clp; struct nfs_client *clp;
char dev[8], fsid[17]; char dev[8], fsid[17];
struct nfs_net *nn = net_generic(m->private, nfs_net_id); struct nfs_net *nn = net_generic(seq_file_net(m), nfs_net_id);
/* display header on line 1 */ /* display header on line 1 */
if (v == &nn->nfs_volume_list) { if (v == &nn->nfs_volume_list) {
@ -1407,6 +1385,45 @@ static int nfs_volume_list_show(struct seq_file *m, void *v)
return 0; return 0;
} }
int nfs_fs_proc_net_init(struct net *net)
{
struct nfs_net *nn = net_generic(net, nfs_net_id);
struct proc_dir_entry *p;
nn->proc_nfsfs = proc_net_mkdir(net, "nfsfs", net->proc_net);
if (!nn->proc_nfsfs)
goto error_0;
/* a file of servers with which we're dealing */
p = proc_create("servers", S_IFREG|S_IRUGO,
nn->proc_nfsfs, &nfs_server_list_fops);
if (!p)
goto error_1;
/* a file of volumes that we have mounted */
p = proc_create("volumes", S_IFREG|S_IRUGO,
nn->proc_nfsfs, &nfs_volume_list_fops);
if (!p)
goto error_2;
return 0;
error_2:
remove_proc_entry("servers", nn->proc_nfsfs);
error_1:
remove_proc_entry("fs/nfsfs", NULL);
error_0:
return -ENOMEM;
}
void nfs_fs_proc_net_exit(struct net *net)
{
struct nfs_net *nn = net_generic(net, nfs_net_id);
remove_proc_entry("volumes", nn->proc_nfsfs);
remove_proc_entry("servers", nn->proc_nfsfs);
remove_proc_entry("fs/nfsfs", NULL);
}
/* /*
* initialise the /proc/fs/nfsfs/ directory * initialise the /proc/fs/nfsfs/ directory
*/ */
@ -1419,14 +1436,12 @@ int __init nfs_fs_proc_init(void)
goto error_0; goto error_0;
/* a file of servers with which we're dealing */ /* a file of servers with which we're dealing */
p = proc_create("servers", S_IFREG|S_IRUGO, p = proc_symlink("servers", proc_fs_nfs, "../../net/nfsfs/servers");
proc_fs_nfs, &nfs_server_list_fops);
if (!p) if (!p)
goto error_1; goto error_1;
/* a file of volumes that we have mounted */ /* a file of volumes that we have mounted */
p = proc_create("volumes", S_IFREG|S_IRUGO, p = proc_symlink("volumes", proc_fs_nfs, "../../net/nfsfs/volumes");
proc_fs_nfs, &nfs_volume_list_fops);
if (!p) if (!p)
goto error_2; goto error_2;
return 0; return 0;

View file

@ -1840,11 +1840,12 @@ EXPORT_SYMBOL_GPL(nfs_net_id);
static int nfs_net_init(struct net *net) static int nfs_net_init(struct net *net)
{ {
nfs_clients_init(net); nfs_clients_init(net);
return 0; return nfs_fs_proc_net_init(net);
} }
static void nfs_net_exit(struct net *net) static void nfs_net_exit(struct net *net)
{ {
nfs_fs_proc_net_exit(net);
nfs_cleanup_cb_ident_idr(net); nfs_cleanup_cb_ident_idr(net);
} }

View file

@ -195,7 +195,16 @@ extern struct rpc_clnt *nfs4_find_or_create_ds_client(struct nfs_client *,
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
extern int __init nfs_fs_proc_init(void); extern int __init nfs_fs_proc_init(void);
extern void nfs_fs_proc_exit(void); extern void nfs_fs_proc_exit(void);
extern int nfs_fs_proc_net_init(struct net *net);
extern void nfs_fs_proc_net_exit(struct net *net);
#else #else
static inline int nfs_fs_proc_net_init(struct net *net)
{
return 0;
}
static inline void nfs_fs_proc_net_exit(struct net *net)
{
}
static inline int nfs_fs_proc_init(void) static inline int nfs_fs_proc_init(void)
{ {
return 0; return 0;

View file

@ -29,6 +29,9 @@ struct nfs_net {
#endif #endif
spinlock_t nfs_client_lock; spinlock_t nfs_client_lock;
struct timespec boot_time; struct timespec boot_time;
#ifdef CONFIG_PROC_FS
struct proc_dir_entry *proc_nfsfs;
#endif
}; };
extern int nfs_net_id; extern int nfs_net_id;