#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>
+#include <linux/seq_file.h>
#include "internal.h"
-struct proc_dir_entry *proc_net_create(struct net *net,
- const char *name, mode_t mode, get_info_t *get_info)
+static struct net *get_proc_net(const struct inode *inode)
{
- return create_proc_info_entry(name,mode, net->proc_net, get_info);
+ return maybe_get_net(PDE_NET(PDE(inode)));
}
-EXPORT_SYMBOL_GPL(proc_net_create);
-struct proc_dir_entry *proc_net_fops_create(struct net *net,
- const char *name, mode_t mode, const struct file_operations *fops)
+int seq_open_net(struct inode *ino, struct file *f,
+ const struct seq_operations *ops, int size)
{
- struct proc_dir_entry *res;
-
- res = create_proc_entry(name, mode, net->proc_net);
- if (res)
- res->proc_fops = fops;
- return res;
+ 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;
- shadow = proc_net_shadow_dentry(nd->dentry, net->proc_net);
- if (!shadow)
- return ERR_PTR(-ENOENT);
+ net = get_proc_task_net(inode);
- dput(nd->dentry);
- nd->dentry = shadow;
+ generic_fillattr(inode, stat);
- return shadow->d_inode->i_op->lookup(shadow->d_inode, dentry, nd);
+ if (net != NULL) {
+ stat->nlink = net->proc_net->nlink;
+ put_net(net);
+ }
+
+ 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 = {
+ .llseek = generic_file_llseek,
+ .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 __net_initdata proc_net_ns_ops = {
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);
}