X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=fs%2Fproc%2Fproc_net.c;h=7bc296f424aebcb66cf89b2f0c9678f787c37a1d;hb=8ef2720397bb813d4985405a5ae7b8ad6474188b;hp=153554cf55752c9b0364a90847e57c07baa25192;hpb=487350e4434610e31b71eac5d6a9714b72fa32f6;p=~andy%2Flinux diff --git a/fs/proc/proc_net.c b/fs/proc/proc_net.c index 153554cf557..7bc296f424a 100644 --- a/fs/proc/proc_net.c +++ b/fs/proc/proc_net.c @@ -22,172 +22,219 @@ #include #include #include +#include #include "internal.h" -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_net(const struct inode *inode) { - struct proc_dir_entry *res; + return maybe_get_net(PDE_NET(PDE(inode))); +} - res = create_proc_entry(name, mode, net->proc_net); - if (res) - res->proc_fops = fops; - return res; +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; + } +#ifdef CONFIG_NET_NS + p->net = net; +#endif + return 0; } -EXPORT_SYMBOL_GPL(proc_net_fops_create); +EXPORT_SYMBOL_GPL(seq_open_net); -void proc_net_remove(struct net *net, const char *name) +int single_open_net(struct inode *inode, struct file *file, + int (*show)(struct seq_file *, void *)) { - remove_proc_entry(name, net->proc_net); + 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(proc_net_remove); +EXPORT_SYMBOL_GPL(single_open_net); -struct net *get_proc_net(const struct inode *inode) +int seq_release_net(struct inode *ino, struct file *f) { - return maybe_get_net(PDE_NET(PDE(inode))); + struct seq_file *seq; + + seq = f->private_data; + + put_net(seq_file_net(seq)); + seq_release_private(ino, f); + return 0; } -EXPORT_SYMBOL_GPL(get_proc_net); +EXPORT_SYMBOL_GPL(seq_release_net); -static struct proc_dir_entry *proc_net_shadow; +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); -static struct dentry *proc_net_shadow_dentry(struct dentry *parent, - struct proc_dir_entry *de) +static struct net *get_proc_task_net(struct inode *dir) { - struct dentry *shadow = NULL; - struct inode *inode; - if (!de) - goto out; - de_get(de); - inode = proc_get_inode(parent->d_inode->i_sb, de->low_ino, de); - if (!inode) - goto out_de_put; - shadow = d_alloc_name(parent, de->name); - if (!shadow) - goto out_iput; - shadow->d_op = parent->d_op; /* proc_dentry_operations */ - d_instantiate(shadow, inode); -out: - return shadow; -out_iput: - iput(inode); -out_de_put: - de_put(de); - goto out; + 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 void *proc_net_follow_link(struct dentry *parent, struct nameidata *nd) +static struct dentry *proc_tgid_net_lookup(struct inode *dir, + struct dentry *dentry, struct nameidata *nd) { - struct net *net = current->nsproxy->net_ns; - struct dentry *shadow; - shadow = proc_net_shadow_dentry(parent, net->proc_net); - if (!shadow) - return ERR_PTR(-ENOENT); - - dput(nd->dentry); - /* My dentry count is 1 and that should be enough as the - * shadow dentry is thrown away immediately. - */ - nd->dentry = shadow; - return NULL; + 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 struct dentry *proc_net_lookup(struct inode *dir, struct dentry *dentry, - struct nameidata *nd) +static int proc_tgid_net_getattr(struct vfsmount *mnt, struct dentry *dentry, + struct kstat *stat) { - struct net *net = current->nsproxy->net_ns; - struct dentry *shadow; + struct inode *inode = dentry->d_inode; + struct net *net; + + net = get_proc_task_net(inode); - shadow = proc_net_shadow_dentry(nd->dentry, net->proc_net); - if (!shadow) - return ERR_PTR(-ENOENT); + generic_fillattr(inode, stat); - dput(nd->dentry); - nd->dentry = shadow; + if (net != NULL) { + stat->nlink = net->proc_net->nlink; + put_net(net); + } - return shadow->d_inode->i_op->lookup(shadow->d_inode, dentry, nd); + return 0; } -static int proc_net_setattr(struct dentry *dentry, struct iattr *iattr) +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) { - struct net *net = current->nsproxy->net_ns; - struct dentry *shadow; int ret; - - shadow = proc_net_shadow_dentry(dentry->d_parent, net->proc_net); - if (!shadow) - return -ENOENT; - ret = shadow->d_inode->i_op->setattr(shadow, iattr); - dput(shadow); + 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; } -static const struct file_operations proc_net_dir_operations = { - .read = generic_read_dir, +const struct file_operations proc_net_operations = { + .read = generic_read_dir, + .readdir = proc_tgid_net_readdir, }; -static struct inode_operations proc_net_dir_inode_operations = { - .follow_link = proc_net_follow_link, - .lookup = proc_net_lookup, - .setattr = proc_net_setattr, -}; + +struct proc_dir_entry *proc_net_fops_create(struct net *net, + const char *name, mode_t mode, const struct file_operations *fops) +{ + return proc_create(name, mode, net->proc_net, fops); +} +EXPORT_SYMBOL_GPL(proc_net_fops_create); + +void proc_net_remove(struct net *net, const char *name) +{ + remove_proc_entry(name, net->proc_net); +} +EXPORT_SYMBOL_GPL(proc_net_remove); 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), GFP_KERNEL); + if (!netd) goto out; - err = -EEXIST; - netd = proc_mkdir("net", root); - if (!netd) - goto free_root; + netd->data = net; + netd->nlink = 2; + netd->name = "net"; + netd->namelen = 3; + netd->parent = &proc_root; err = -EEXIST; - net_statd = proc_mkdir("stat", netd); + net_statd = proc_net_mkdir(net, "stat", netd); if (!net_statd) goto free_net; - root->data = net; - netd->data = net; - net_statd->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 proc_net_ns_ops = { +static struct pernet_operations __net_initdata proc_net_ns_ops = { .init = proc_net_ns_init, .exit = proc_net_ns_exit, }; int __init proc_net_init(void) { - proc_net_shadow = proc_mkdir("net", NULL); - proc_net_shadow->proc_iops = &proc_net_dir_inode_operations; - proc_net_shadow->proc_fops = &proc_net_dir_operations; + proc_symlink("net", NULL, "self/net"); return register_pernet_subsys(&proc_net_ns_ops); }