diff options
Diffstat (limited to 'fs/proc/proc_net.c')
| -rw-r--r-- | fs/proc/proc_net.c | 170 |
1 files changed, 118 insertions, 52 deletions
diff --git a/fs/proc/proc_net.c b/fs/proc/proc_net.c index 4823c9677fa..4677bb7dc7c 100644 --- a/fs/proc/proc_net.c +++ b/fs/proc/proc_net.c @@ -14,11 +14,11 @@ #include <linux/time.h> #include <linux/proc_fs.h> #include <linux/stat.h> +#include <linux/slab.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> @@ -26,6 +26,15 @@ #include "internal.h" +static inline struct net *PDE_NET(struct proc_dir_entry *pde) +{ + return pde->parent->data; +} + +static struct net *get_proc_net(const struct inode *inode) +{ + return maybe_get_net(PDE_NET(PDE(inode))); +} int seq_open_net(struct inode *ino, struct file *f, const struct seq_operations *ops, int size) @@ -44,110 +53,168 @@ int seq_open_net(struct inode *ino, struct file *f, put_net(net); return -ENOMEM; } +#ifdef CONFIG_NET_NS p->net = net; +#endif return 0; } EXPORT_SYMBOL_GPL(seq_open_net); +int single_open_net(struct inode *inode, struct file *file, + int (*show)(struct seq_file *, void *)) +{ + int err; + struct net *net; + + err = -ENXIO; + net = get_proc_net(inode); + if (net == NULL) + goto err_net; + + err = single_open(file, show, net); + if (err < 0) + goto err_open; + + return 0; + +err_open: + put_net(net); +err_net: + return err; +} +EXPORT_SYMBOL_GPL(single_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); + put_net(seq_file_net(seq)); seq_release_private(ino, f); return 0; } EXPORT_SYMBOL_GPL(seq_release_net); +int single_release_net(struct inode *ino, struct file *f) +{ + struct seq_file *seq = f->private_data; + put_net(seq->private); + return single_release(ino, f); +} +EXPORT_SYMBOL_GPL(single_release_net); -struct proc_dir_entry *proc_net_fops_create(struct net *net, - const char *name, mode_t mode, const struct file_operations *fops) +static struct net *get_proc_task_net(struct inode *dir) { - struct proc_dir_entry *res; + 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(); - res = create_proc_entry(name, mode, net->proc_net); - if (res) - res->proc_fops = fops; - return res; + return net; } -EXPORT_SYMBOL_GPL(proc_net_fops_create); -void proc_net_remove(struct net *net, const char *name) +static struct dentry *proc_tgid_net_lookup(struct inode *dir, + struct dentry *dentry, unsigned int flags) { - remove_proc_entry(name, net->proc_net); + 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; } -EXPORT_SYMBOL_GPL(proc_net_remove); -struct net *get_proc_net(const struct inode *inode) +static int proc_tgid_net_getattr(struct vfsmount *mnt, struct dentry *dentry, + struct kstat *stat) { - return maybe_get_net(PDE_NET(PDE(inode))); -} -EXPORT_SYMBOL_GPL(get_proc_net); + struct inode *inode = dentry->d_inode; + struct net *net; -static struct proc_dir_entry *shadow_pde; + net = get_proc_task_net(inode); -static struct proc_dir_entry *proc_net_shadow(struct task_struct *task, - struct proc_dir_entry *de) -{ - return task->nsproxy->net_ns->proc_net; + generic_fillattr(inode, stat); + + if (net != NULL) { + stat->nlink = net->proc_net->nlink; + put_net(net); + } + + return 0; } -struct proc_dir_entry *proc_net_mkdir(struct net *net, const char *name, - struct proc_dir_entry *parent) +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 *file, struct dir_context *ctx) { - struct proc_dir_entry *pde; - pde = proc_mkdir_mode(name, S_IRUGO | S_IXUGO, parent); - if (pde != NULL) - pde->data = net; - return pde; + int ret; + struct net *net; + + ret = -EINVAL; + net = get_proc_task_net(file_inode(file)); + if (net != NULL) { + ret = proc_readdir_de(net->proc_net, file, ctx); + put_net(net); + } + return ret; } -EXPORT_SYMBOL_GPL(proc_net_mkdir); + +const struct file_operations proc_net_operations = { + .llseek = generic_file_llseek, + .read = generic_read_dir, + .iterate = proc_tgid_net_readdir, +}; static __net_init int proc_net_ns_init(struct net *net) { - struct proc_dir_entry *root, *netd, *net_statd; + struct proc_dir_entry *netd, *net_statd; int err; err = -ENOMEM; - root = kzalloc(sizeof(*root), GFP_KERNEL); - if (!root) + netd = kzalloc(sizeof(*netd) + 4, GFP_KERNEL); + if (!netd) goto out; - err = -EEXIST; - netd = proc_net_mkdir(net, "net", root); - if (!netd) - goto free_root; + netd->data = net; + netd->nlink = 2; + netd->namelen = 3; + netd->parent = &proc_root; + memcpy(netd->name, "net", 4); err = -EEXIST; net_statd = proc_net_mkdir(net, "stat", netd); if (!net_statd) goto free_net; - root->data = net; - - net->proc_net_root = root; net->proc_net = netd; net->proc_net_stat = net_statd; - err = 0; + return 0; +free_net: + kfree(netd); out: return err; -free_net: - remove_proc_entry("net", root); -free_root: - kfree(root); - goto out; } static __net_exit void proc_net_ns_exit(struct net *net) { remove_proc_entry("stat", net->proc_net); - remove_proc_entry("net", net->proc_net_root); - kfree(net->proc_net_root); + kfree(net->proc_net); } static struct pernet_operations __net_initdata proc_net_ns_ops = { @@ -157,8 +224,7 @@ static struct pernet_operations __net_initdata proc_net_ns_ops = { int __init proc_net_init(void) { - shadow_pde = proc_mkdir("net", NULL); - shadow_pde->shadow_proc = proc_net_shadow; + proc_symlink("net", NULL, "self/net"); return register_pernet_subsys(&proc_net_ns_ops); } |
