]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
Merge remote-tracking branch 'namespace/master'
authorStephen Rothwell <sfr@canb.auug.org.au>
Mon, 15 Aug 2011 04:27:22 +0000 (14:27 +1000)
committerStephen Rothwell <sfr@canb.auug.org.au>
Mon, 15 Aug 2011 04:27:22 +0000 (14:27 +1000)
fs/proc/inode.c
fs/proc/namespaces.c

index 7ed72d6c1c6fc4d2c528e7c5578c2836a2875914..9c70229b6c35d24eba67b5fc48c64987b27910a3 100644 (file)
@@ -29,6 +29,7 @@ static void proc_evict_inode(struct inode *inode)
        struct proc_dir_entry *de;
        struct ctl_table_header *head;
        const struct proc_ns_operations *ns_ops;
+       void *ns;
 
        truncate_inode_pages(&inode->i_data, 0);
        end_writeback(inode);
@@ -47,8 +48,9 @@ static void proc_evict_inode(struct inode *inode)
        }
        /* Release any associated namespace */
        ns_ops = PROC_I(inode)->ns_ops;
-       if (ns_ops && ns_ops->put)
-               ns_ops->put(PROC_I(inode)->ns);
+       ns = PROC_I(inode)->ns;
+       if (ns_ops && ns)
+               ns_ops->put(ns);
 }
 
 static struct kmem_cache * proc_inode_cachep;
index be177f702acbc9bc352c0e13f5b37aeedbf6dd25..53d4cc890aa4939c44dfb6ce898800da49ee8a82 100644 (file)
@@ -31,6 +31,140 @@ static const struct file_operations ns_file_operations = {
        .llseek         = no_llseek,
 };
 
+static const struct inode_operations ns_inode_operations = {
+       .setattr        = proc_setattr,
+};
+
+static int ns_delete_dentry(const struct dentry *dentry)
+{
+       /* Don't cache namespace dentries when not in use */
+       return 1;
+}
+
+static char *ns_dname(struct dentry *dentry, char *buffer, int buflen)
+{
+       struct inode *inode = dentry->d_inode;
+       const struct proc_ns_operations *ns_ops = PROC_I(inode)->ns_ops;
+
+       return dynamic_dname(dentry, buffer, buflen, "%s:[%lu]",
+               ns_ops->name, inode->i_ino);
+}
+
+static const struct dentry_operations ns_dentry_operations =
+{
+       .d_delete       = ns_delete_dentry,
+       .d_dname        = ns_dname,
+};
+
+static struct dentry *proc_ns_get_dentry(struct super_block *sb,
+       struct task_struct *task, const struct proc_ns_operations *ns_ops)
+{
+       struct dentry *dentry;
+       struct inode *inode;
+       struct proc_inode *ei;
+       struct qstr qname = { .name = "", };
+       void *ns;
+
+       ns = ns_ops->get(task);
+       if (!ns)
+               return ERR_PTR(-ENOENT);
+
+       dentry = d_alloc_pseudo(sb, &qname);
+       if (!dentry) {
+               ns_ops->put(ns);
+               return ERR_PTR(-ENOMEM);
+       }
+
+       inode = new_inode(sb);
+       if (!inode) {
+               dput(dentry);
+               ns_ops->put(ns);
+               return ERR_PTR(-ENOMEM);
+       }
+               
+       ei = PROC_I(inode);
+       inode->i_ino = get_next_ino();
+       inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
+       inode->i_op = &ns_inode_operations;
+       inode->i_mode = S_IFREG|S_IRUSR;
+       inode->i_fop = &ns_file_operations;
+       ei->ns_ops = ns_ops;
+       ei->ns = ns;
+
+       d_set_d_op(dentry, &ns_dentry_operations);
+       d_instantiate(dentry, inode);
+
+       return dentry;
+}
+
+static void *proc_ns_follow_link(struct dentry *dentry, struct nameidata *nd)
+{
+       struct inode *inode = dentry->d_inode;
+       struct super_block *sb = inode->i_sb;
+       struct proc_inode *ei = PROC_I(inode);
+       struct task_struct *task;
+       struct dentry *ns_dentry;
+       void *error = ERR_PTR(-EACCES);
+       
+       task = get_proc_task(inode);
+       if (!task)
+               goto out;
+
+       if (!ptrace_may_access(task, PTRACE_MODE_READ))
+               goto out_put_task;
+
+       ns_dentry = proc_ns_get_dentry(sb, task, ei->ns_ops);
+       if (IS_ERR(ns_dentry)) {
+               error = ERR_CAST(ns_dentry);
+               goto out_put_task;
+       }
+
+       dput(nd->path.dentry);
+       nd->path.dentry = ns_dentry;
+       error = NULL;
+
+out_put_task:
+       put_task_struct(task);
+out:
+       return error;
+}
+
+static int proc_ns_readlink(struct dentry *dentry, char __user *buffer, int buflen)
+{
+       struct inode *inode = dentry->d_inode;
+       struct proc_inode *ei = PROC_I(inode);
+       const struct proc_ns_operations *ns_ops = ei->ns_ops;
+       struct task_struct *task;
+       char name[50];
+       int len = -EACCES;
+
+       task = get_proc_task(inode);
+       if (!task)
+               goto out;
+
+       if (!ptrace_may_access(task, PTRACE_MODE_READ))
+               goto out_put_task;
+
+       snprintf(name, sizeof(name), "%s", ns_ops->name);
+       len = strlen(name);
+
+       if (len > buflen)
+               len = buflen;
+       if (copy_to_user(buffer, ns_ops->name, len))
+               len = -EFAULT;
+
+out_put_task:
+       put_task_struct(task);
+out:
+       return len;
+}
+
+static const struct inode_operations proc_ns_link_inode_operations = {
+       .readlink       = proc_ns_readlink,
+       .follow_link    = proc_ns_follow_link,
+       .setattr        = proc_setattr,
+};
+
 static struct dentry *proc_ns_instantiate(struct inode *dir,
        struct dentry *dentry, struct task_struct *task, const void *ptr)
 {
@@ -38,32 +172,23 @@ static struct dentry *proc_ns_instantiate(struct inode *dir,
        struct inode *inode;
        struct proc_inode *ei;
        struct dentry *error = ERR_PTR(-ENOENT);
-       void *ns;
 
        inode = proc_pid_make_inode(dir->i_sb, task);
        if (!inode)
                goto out;
 
-       ns = ns_ops->get(task);
-       if (!ns)
-               goto out_iput;
-
        ei = PROC_I(inode);
-       inode->i_mode = S_IFREG|S_IRUSR;
-       inode->i_fop  = &ns_file_operations;
-       ei->ns_ops    = ns_ops;
-       ei->ns        = ns;
+       inode->i_mode = S_IFLNK|S_IRWXUGO;
+       inode->i_op = &proc_ns_link_inode_operations;
+       ei->ns_ops = ns_ops;
 
-       dentry->d_op = &pid_dentry_operations;
+       d_set_d_op(dentry, &pid_dentry_operations);
        d_add(dentry, inode);
        /* Close the race of the process dying before we return the dentry */
        if (pid_revalidate(dentry, NULL))
                error = NULL;
 out:
        return error;
-out_iput:
-       iput(inode);
-       goto out;
 }
 
 static int proc_ns_fill_cache(struct file *filp, void *dirent,
@@ -90,10 +215,6 @@ static int proc_ns_dir_readdir(struct file *filp, void *dirent,
        if (!task)
                goto out_no_task;
 
-       ret = -EPERM;
-       if (!ptrace_may_access(task, PTRACE_MODE_READ))
-               goto out;
-
        ret = 0;
        i = filp->f_pos;
        switch (i) {
@@ -153,10 +274,6 @@ static struct dentry *proc_ns_dir_lookup(struct inode *dir,
        if (!task)
                goto out_no_task;
 
-       error = ERR_PTR(-EPERM);
-       if (!ptrace_may_access(task, PTRACE_MODE_READ))
-               goto out;
-
        last = &ns_entries[ARRAY_SIZE(ns_entries) - 1];
        for (entry = ns_entries; entry <= last; entry++) {
                if (strlen((*entry)->name) != len)
@@ -164,7 +281,6 @@ static struct dentry *proc_ns_dir_lookup(struct inode *dir,
                if (!memcmp(dentry->d_name.name, (*entry)->name, len))
                        break;
        }
-       error = ERR_PTR(-ENOENT);
        if (entry > last)
                goto out;