diff options
| -rw-r--r-- | fs/proc/inode.c | 6 | ||||
| -rw-r--r-- | fs/proc/namespaces.c | 169 |
2 files changed, 152 insertions, 23 deletions
diff --git a/fs/proc/inode.c b/fs/proc/inode.c index 3b22bbdee9ec..439ae6886507 100644 --- a/fs/proc/inode.c +++ b/fs/proc/inode.c | |||
| @@ -31,6 +31,7 @@ static void proc_evict_inode(struct inode *inode) | |||
| 31 | struct proc_dir_entry *de; | 31 | struct proc_dir_entry *de; |
| 32 | struct ctl_table_header *head; | 32 | struct ctl_table_header *head; |
| 33 | const struct proc_ns_operations *ns_ops; | 33 | const struct proc_ns_operations *ns_ops; |
| 34 | void *ns; | ||
| 34 | 35 | ||
| 35 | truncate_inode_pages(&inode->i_data, 0); | 36 | truncate_inode_pages(&inode->i_data, 0); |
| 36 | clear_inode(inode); | 37 | clear_inode(inode); |
| @@ -49,8 +50,9 @@ static void proc_evict_inode(struct inode *inode) | |||
| 49 | } | 50 | } |
| 50 | /* Release any associated namespace */ | 51 | /* Release any associated namespace */ |
| 51 | ns_ops = PROC_I(inode)->ns_ops; | 52 | ns_ops = PROC_I(inode)->ns_ops; |
| 52 | if (ns_ops && ns_ops->put) | 53 | ns = PROC_I(inode)->ns; |
| 53 | ns_ops->put(PROC_I(inode)->ns); | 54 | if (ns_ops && ns) |
| 55 | ns_ops->put(ns); | ||
| 54 | } | 56 | } |
| 55 | 57 | ||
| 56 | static struct kmem_cache * proc_inode_cachep; | 58 | static struct kmem_cache * proc_inode_cachep; |
diff --git a/fs/proc/namespaces.c b/fs/proc/namespaces.c index 030250c27d70..7a6d8d69cdb8 100644 --- a/fs/proc/namespaces.c +++ b/fs/proc/namespaces.c | |||
| @@ -38,6 +38,151 @@ static const struct file_operations ns_file_operations = { | |||
| 38 | .llseek = no_llseek, | 38 | .llseek = no_llseek, |
| 39 | }; | 39 | }; |
| 40 | 40 | ||
| 41 | static const struct inode_operations ns_inode_operations = { | ||
| 42 | .setattr = proc_setattr, | ||
| 43 | }; | ||
| 44 | |||
| 45 | static int ns_delete_dentry(const struct dentry *dentry) | ||
| 46 | { | ||
| 47 | /* Don't cache namespace inodes when not in use */ | ||
| 48 | return 1; | ||
| 49 | } | ||
| 50 | |||
| 51 | static char *ns_dname(struct dentry *dentry, char *buffer, int buflen) | ||
| 52 | { | ||
| 53 | struct inode *inode = dentry->d_inode; | ||
| 54 | const struct proc_ns_operations *ns_ops = PROC_I(inode)->ns_ops; | ||
| 55 | |||
| 56 | return dynamic_dname(dentry, buffer, buflen, "%s:[%lu]", | ||
| 57 | ns_ops->name, inode->i_ino); | ||
| 58 | } | ||
| 59 | |||
| 60 | const struct dentry_operations ns_dentry_operations = | ||
| 61 | { | ||
| 62 | .d_delete = ns_delete_dentry, | ||
| 63 | .d_dname = ns_dname, | ||
| 64 | }; | ||
| 65 | |||
| 66 | static struct dentry *proc_ns_get_dentry(struct super_block *sb, | ||
| 67 | struct task_struct *task, const struct proc_ns_operations *ns_ops) | ||
| 68 | { | ||
| 69 | struct dentry *dentry, *result; | ||
| 70 | struct inode *inode; | ||
| 71 | struct proc_inode *ei; | ||
| 72 | struct qstr qname = { .name = "", }; | ||
| 73 | void *ns; | ||
| 74 | |||
| 75 | ns = ns_ops->get(task); | ||
| 76 | if (!ns) | ||
| 77 | return ERR_PTR(-ENOENT); | ||
| 78 | |||
| 79 | dentry = d_alloc_pseudo(sb, &qname); | ||
| 80 | if (!dentry) { | ||
| 81 | ns_ops->put(ns); | ||
| 82 | return ERR_PTR(-ENOMEM); | ||
| 83 | } | ||
| 84 | |||
| 85 | inode = new_inode(sb); | ||
| 86 | if (!inode) { | ||
| 87 | dput(dentry); | ||
| 88 | ns_ops->put(ns); | ||
| 89 | return ERR_PTR(-ENOMEM); | ||
| 90 | } | ||
| 91 | |||
| 92 | ei = PROC_I(inode); | ||
| 93 | inode->i_ino = get_next_ino(); | ||
| 94 | inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; | ||
| 95 | inode->i_op = &ns_inode_operations; | ||
| 96 | inode->i_mode = S_IFREG | S_IRUGO; | ||
| 97 | inode->i_fop = &ns_file_operations; | ||
| 98 | ei->ns_ops = ns_ops; | ||
| 99 | ei->ns = ns; | ||
| 100 | |||
| 101 | d_set_d_op(dentry, &ns_dentry_operations); | ||
| 102 | result = d_instantiate_unique(dentry, inode); | ||
| 103 | if (result) { | ||
| 104 | dput(dentry); | ||
| 105 | dentry = result; | ||
| 106 | } | ||
| 107 | |||
| 108 | return dentry; | ||
| 109 | } | ||
| 110 | |||
| 111 | static void *proc_ns_follow_link(struct dentry *dentry, struct nameidata *nd) | ||
| 112 | { | ||
| 113 | struct inode *inode = dentry->d_inode; | ||
| 114 | struct super_block *sb = inode->i_sb; | ||
| 115 | struct proc_inode *ei = PROC_I(inode); | ||
| 116 | struct task_struct *task; | ||
| 117 | struct dentry *ns_dentry; | ||
| 118 | void *error = ERR_PTR(-EACCES); | ||
| 119 | |||
| 120 | task = get_proc_task(inode); | ||
| 121 | if (!task) | ||
| 122 | goto out; | ||
| 123 | |||
| 124 | if (!ptrace_may_access(task, PTRACE_MODE_READ)) | ||
| 125 | goto out_put_task; | ||
| 126 | |||
| 127 | ns_dentry = proc_ns_get_dentry(sb, task, ei->ns_ops); | ||
| 128 | if (IS_ERR(ns_dentry)) { | ||
| 129 | error = ERR_CAST(ns_dentry); | ||
| 130 | goto out_put_task; | ||
| 131 | } | ||
| 132 | |||
| 133 | dput(nd->path.dentry); | ||
| 134 | nd->path.dentry = ns_dentry; | ||
| 135 | error = NULL; | ||
| 136 | |||
| 137 | out_put_task: | ||
| 138 | put_task_struct(task); | ||
| 139 | out: | ||
| 140 | return error; | ||
| 141 | } | ||
| 142 | |||
| 143 | static int proc_ns_readlink(struct dentry *dentry, char __user *buffer, int buflen) | ||
| 144 | { | ||
| 145 | struct inode *inode = dentry->d_inode; | ||
| 146 | struct proc_inode *ei = PROC_I(inode); | ||
| 147 | const struct proc_ns_operations *ns_ops = ei->ns_ops; | ||
| 148 | struct task_struct *task; | ||
| 149 | void *ns; | ||
| 150 | char name[50]; | ||
| 151 | int len = -EACCES; | ||
| 152 | |||
| 153 | task = get_proc_task(inode); | ||
| 154 | if (!task) | ||
| 155 | goto out; | ||
| 156 | |||
| 157 | if (!ptrace_may_access(task, PTRACE_MODE_READ)) | ||
| 158 | goto out_put_task; | ||
| 159 | |||
| 160 | len = -ENOENT; | ||
| 161 | ns = ns_ops->get(task); | ||
| 162 | if (!ns) | ||
| 163 | goto out_put_task; | ||
| 164 | |||
| 165 | snprintf(name, sizeof(name), "%s", ns_ops->name); | ||
| 166 | len = strlen(name); | ||
| 167 | |||
| 168 | if (len > buflen) | ||
| 169 | len = buflen; | ||
| 170 | if (copy_to_user(buffer, ns_ops->name, len)) | ||
| 171 | len = -EFAULT; | ||
| 172 | |||
| 173 | ns_ops->put(ns); | ||
| 174 | out_put_task: | ||
| 175 | put_task_struct(task); | ||
| 176 | out: | ||
| 177 | return len; | ||
| 178 | } | ||
| 179 | |||
| 180 | static const struct inode_operations proc_ns_link_inode_operations = { | ||
| 181 | .readlink = proc_ns_readlink, | ||
| 182 | .follow_link = proc_ns_follow_link, | ||
| 183 | .setattr = proc_setattr, | ||
| 184 | }; | ||
| 185 | |||
| 41 | static struct dentry *proc_ns_instantiate(struct inode *dir, | 186 | static struct dentry *proc_ns_instantiate(struct inode *dir, |
| 42 | struct dentry *dentry, struct task_struct *task, const void *ptr) | 187 | struct dentry *dentry, struct task_struct *task, const void *ptr) |
| 43 | { | 188 | { |
| @@ -45,21 +190,15 @@ static struct dentry *proc_ns_instantiate(struct inode *dir, | |||
| 45 | struct inode *inode; | 190 | struct inode *inode; |
| 46 | struct proc_inode *ei; | 191 | struct proc_inode *ei; |
| 47 | struct dentry *error = ERR_PTR(-ENOENT); | 192 | struct dentry *error = ERR_PTR(-ENOENT); |
| 48 | void *ns; | ||
| 49 | 193 | ||
| 50 | inode = proc_pid_make_inode(dir->i_sb, task); | 194 | inode = proc_pid_make_inode(dir->i_sb, task); |
| 51 | if (!inode) | 195 | if (!inode) |
| 52 | goto out; | 196 | goto out; |
| 53 | 197 | ||
| 54 | ns = ns_ops->get(task); | ||
| 55 | if (!ns) | ||
| 56 | goto out_iput; | ||
| 57 | |||
| 58 | ei = PROC_I(inode); | 198 | ei = PROC_I(inode); |
| 59 | inode->i_mode = S_IFREG|S_IRUSR; | 199 | inode->i_mode = S_IFLNK|S_IRWXUGO; |
| 60 | inode->i_fop = &ns_file_operations; | 200 | inode->i_op = &proc_ns_link_inode_operations; |
| 61 | ei->ns_ops = ns_ops; | 201 | ei->ns_ops = ns_ops; |
| 62 | ei->ns = ns; | ||
| 63 | 202 | ||
| 64 | d_set_d_op(dentry, &pid_dentry_operations); | 203 | d_set_d_op(dentry, &pid_dentry_operations); |
| 65 | d_add(dentry, inode); | 204 | d_add(dentry, inode); |
| @@ -68,9 +207,6 @@ static struct dentry *proc_ns_instantiate(struct inode *dir, | |||
| 68 | error = NULL; | 207 | error = NULL; |
| 69 | out: | 208 | out: |
| 70 | return error; | 209 | return error; |
| 71 | out_iput: | ||
| 72 | iput(inode); | ||
| 73 | goto out; | ||
| 74 | } | 210 | } |
| 75 | 211 | ||
| 76 | static int proc_ns_fill_cache(struct file *filp, void *dirent, | 212 | static int proc_ns_fill_cache(struct file *filp, void *dirent, |
| @@ -97,10 +233,6 @@ static int proc_ns_dir_readdir(struct file *filp, void *dirent, | |||
| 97 | if (!task) | 233 | if (!task) |
| 98 | goto out_no_task; | 234 | goto out_no_task; |
| 99 | 235 | ||
| 100 | ret = -EPERM; | ||
| 101 | if (!ptrace_may_access(task, PTRACE_MODE_READ)) | ||
| 102 | goto out; | ||
| 103 | |||
| 104 | ret = 0; | 236 | ret = 0; |
| 105 | i = filp->f_pos; | 237 | i = filp->f_pos; |
| 106 | switch (i) { | 238 | switch (i) { |
| @@ -160,10 +292,6 @@ static struct dentry *proc_ns_dir_lookup(struct inode *dir, | |||
| 160 | if (!task) | 292 | if (!task) |
| 161 | goto out_no_task; | 293 | goto out_no_task; |
| 162 | 294 | ||
| 163 | error = ERR_PTR(-EPERM); | ||
| 164 | if (!ptrace_may_access(task, PTRACE_MODE_READ)) | ||
| 165 | goto out; | ||
| 166 | |||
| 167 | last = &ns_entries[ARRAY_SIZE(ns_entries)]; | 295 | last = &ns_entries[ARRAY_SIZE(ns_entries)]; |
| 168 | for (entry = ns_entries; entry < last; entry++) { | 296 | for (entry = ns_entries; entry < last; entry++) { |
| 169 | if (strlen((*entry)->name) != len) | 297 | if (strlen((*entry)->name) != len) |
| @@ -171,7 +299,6 @@ static struct dentry *proc_ns_dir_lookup(struct inode *dir, | |||
| 171 | if (!memcmp(dentry->d_name.name, (*entry)->name, len)) | 299 | if (!memcmp(dentry->d_name.name, (*entry)->name, len)) |
| 172 | break; | 300 | break; |
| 173 | } | 301 | } |
| 174 | error = ERR_PTR(-ENOENT); | ||
| 175 | if (entry == last) | 302 | if (entry == last) |
| 176 | goto out; | 303 | goto out; |
| 177 | 304 | ||
