.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 inodes 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);
+}
+
+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, *result;
+       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_IRUGO;
+       inode->i_fop = &ns_file_operations;
+       ei->ns_ops = ns_ops;
+       ei->ns = ns;
+
+       d_set_d_op(dentry, &ns_dentry_operations);
+       result = d_instantiate_unique(dentry, inode);
+       if (result) {
+               dput(dentry);
+               dentry = result;
+       }
+
+       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;
+       void *ns;
+       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;
+
+       len = -ENOENT;
+       ns = ns_ops->get(task);
+       if (!ns)
+               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;
+
+       ns_ops->put(ns);
+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)
 {
        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;
 
        d_set_d_op(dentry, &pid_dentry_operations);
        d_add(dentry, inode);
                error = NULL;
 out:
        return error;
-out_iput:
-       iput(inode);
-       goto out;
 }
 
 static int proc_ns_fill_cache(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) {
        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)];
        for (entry = ns_entries; entry < last; entry++) {
                if (strlen((*entry)->name) != len)
                if (!memcmp(dentry->d_name.name, (*entry)->name, len))
                        break;
        }
-       error = ERR_PTR(-ENOENT);
        if (entry == last)
                goto out;