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 | ||