proc_net.c 4.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
/*
 *  linux/fs/proc/net.c
 *
 *  Copyright (C) 2007
 *
 *  Author: Eric Biederman <ebiederm@xmission.com>
 *
 *  proc net directory handling functions
 */

#include <asm/uaccess.h>

#include <linux/errno.h>
#include <linux/time.h>
#include <linux/proc_fs.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/module.h>
#include <linux/bitops.h>
#include <linux/smp_lock.h>
#include <linux/mount.h>
#include <linux/nsproxy.h>
#include <net/net_namespace.h>
25
#include <linux/seq_file.h>
26 27 28 29

#include "internal.h"


30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
int seq_open_net(struct inode *ino, struct file *f,
		 const struct seq_operations *ops, int size)
{
	struct net *net;
	struct seq_net_private *p;

	BUG_ON(size < sizeof(*p));

	net = get_proc_net(ino);
	if (net == NULL)
		return -ENXIO;

	p = __seq_open_private(f, ops, size);
	if (p == NULL) {
		put_net(net);
		return -ENOMEM;
	}
	p->net = net;
	return 0;
}
EXPORT_SYMBOL_GPL(seq_open_net);

int seq_release_net(struct inode *ino, struct file *f)
{
	struct seq_file *seq;
	struct seq_net_private *p;

	seq = f->private_data;
	p = seq->private;

	put_net(p->net);
	seq_release_private(ino, f);
	return 0;
}
EXPORT_SYMBOL_GPL(seq_release_net);

66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
static struct net *get_proc_task_net(struct inode *dir)
{
	struct task_struct *task;
	struct nsproxy *ns;
	struct net *net = NULL;

	rcu_read_lock();
	task = pid_task(proc_pid(dir), PIDTYPE_PID);
	if (task != NULL) {
		ns = task_nsproxy(task);
		if (ns != NULL)
			net = get_net(ns->net_ns);
	}
	rcu_read_unlock();

	return net;
}

static struct dentry *proc_tgid_net_lookup(struct inode *dir,
		struct dentry *dentry, struct nameidata *nd)
{
	struct dentry *de;
	struct net *net;

	de = ERR_PTR(-ENOENT);
	net = get_proc_task_net(dir);
	if (net != NULL) {
		de = proc_lookup_de(net->proc_net, dir, dentry);
		put_net(net);
	}
	return de;
}

static int proc_tgid_net_getattr(struct vfsmount *mnt, struct dentry *dentry,
		struct kstat *stat)
{
	struct inode *inode = dentry->d_inode;
	struct net *net;

	net = get_proc_task_net(inode);

	generic_fillattr(inode, stat);

	if (net != NULL) {
		stat->nlink = net->proc_net->nlink;
		put_net(net);
	}

	return 0;
}

const struct inode_operations proc_net_inode_operations = {
	.lookup		= proc_tgid_net_lookup,
	.getattr	= proc_tgid_net_getattr,
};

static int proc_tgid_net_readdir(struct file *filp, void *dirent,
		filldir_t filldir)
{
	int ret;
	struct net *net;

	ret = -EINVAL;
	net = get_proc_task_net(filp->f_path.dentry->d_inode);
	if (net != NULL) {
		ret = proc_readdir_de(net->proc_net, filp, dirent, filldir);
		put_net(net);
	}
	return ret;
}

const struct file_operations proc_net_operations = {
	.read		= generic_read_dir,
	.readdir	= proc_tgid_net_readdir,
};

142

143 144 145
struct proc_dir_entry *proc_net_fops_create(struct net *net,
	const char *name, mode_t mode, const struct file_operations *fops)
{
146
	return proc_create(name, mode, net->proc_net, fops);
147
}
D
Daniel Lezcano 已提交
148
EXPORT_SYMBOL_GPL(proc_net_fops_create);
149 150 151 152 153

void proc_net_remove(struct net *net, const char *name)
{
	remove_proc_entry(name, net->proc_net);
}
D
Daniel Lezcano 已提交
154
EXPORT_SYMBOL_GPL(proc_net_remove);
155

156 157 158 159 160 161
struct net *get_proc_net(const struct inode *inode)
{
	return maybe_get_net(PDE_NET(PDE(inode)));
}
EXPORT_SYMBOL_GPL(get_proc_net);

D
Denis V. Lunev 已提交
162 163 164 165 166 167 168 169 170 171 172
struct proc_dir_entry *proc_net_mkdir(struct net *net, const char *name,
		struct proc_dir_entry *parent)
{
	struct proc_dir_entry *pde;
	pde = proc_mkdir_mode(name, S_IRUGO | S_IXUGO, parent);
	if (pde != NULL)
		pde->data = net;
	return pde;
}
EXPORT_SYMBOL_GPL(proc_net_mkdir);

173
static __net_init int proc_net_ns_init(struct net *net)
174
{
175
	struct proc_dir_entry *netd, *net_statd;
176 177 178
	int err;

	err = -ENOMEM;
179 180
	netd = kzalloc(sizeof(*netd), GFP_KERNEL);
	if (!netd)
181 182
		goto out;

183 184 185 186 187
	netd->data = net;
	netd->nlink = 2;
	netd->name = "net";
	netd->namelen = 3;
	netd->parent = &proc_root;
188 189

	err = -EEXIST;
D
Denis V. Lunev 已提交
190
	net_statd = proc_net_mkdir(net, "stat", netd);
191 192 193 194 195
	if (!net_statd)
		goto free_net;

	net->proc_net = netd;
	net->proc_net_stat = net_statd;
196
	return 0;
197

198 199
free_net:
	kfree(netd);
200 201 202 203
out:
	return err;
}

204
static __net_exit void proc_net_ns_exit(struct net *net)
205 206
{
	remove_proc_entry("stat", net->proc_net);
207
	kfree(net->proc_net);
208 209
}

210
static struct pernet_operations __net_initdata proc_net_ns_ops = {
211 212 213 214
	.init = proc_net_ns_init,
	.exit = proc_net_ns_exit,
};

215
int __init proc_net_init(void)
216
{
217
	proc_symlink("net", NULL, "self/net");
218 219 220

	return register_pernet_subsys(&proc_net_ns_ops);
}