diff options
Diffstat (limited to 'fs/proc')
-rw-r--r-- | fs/proc/base.c | 13 | ||||
-rw-r--r-- | fs/proc/proc_devtree.c | 103 | ||||
-rw-r--r-- | fs/proc/proc_misc.c | 163 |
3 files changed, 124 insertions, 155 deletions
diff --git a/fs/proc/base.c b/fs/proc/base.c index 8f1f49ceeb..a3a3eecef6 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c | |||
@@ -534,12 +534,15 @@ static int proc_oom_score(struct task_struct *task, char *buffer) | |||
534 | 534 | ||
535 | /* If the process being read is separated by chroot from the reading process, | 535 | /* If the process being read is separated by chroot from the reading process, |
536 | * don't let the reader access the threads. | 536 | * don't let the reader access the threads. |
537 | * | ||
538 | * note: this does dput(root) and mntput(vfsmnt) on exit. | ||
537 | */ | 539 | */ |
538 | static int proc_check_chroot(struct dentry *root, struct vfsmount *vfsmnt) | 540 | static int proc_check_chroot(struct dentry *root, struct vfsmount *vfsmnt) |
539 | { | 541 | { |
540 | struct dentry *de, *base; | 542 | struct dentry *de, *base; |
541 | struct vfsmount *our_vfsmnt, *mnt; | 543 | struct vfsmount *our_vfsmnt, *mnt; |
542 | int res = 0; | 544 | int res = 0; |
545 | |||
543 | read_lock(¤t->fs->lock); | 546 | read_lock(¤t->fs->lock); |
544 | our_vfsmnt = mntget(current->fs->rootmnt); | 547 | our_vfsmnt = mntget(current->fs->rootmnt); |
545 | base = dget(current->fs->root); | 548 | base = dget(current->fs->root); |
@@ -549,11 +552,11 @@ static int proc_check_chroot(struct dentry *root, struct vfsmount *vfsmnt) | |||
549 | de = root; | 552 | de = root; |
550 | mnt = vfsmnt; | 553 | mnt = vfsmnt; |
551 | 554 | ||
552 | while (vfsmnt != our_vfsmnt) { | 555 | while (mnt != our_vfsmnt) { |
553 | if (vfsmnt == vfsmnt->mnt_parent) | 556 | if (mnt == mnt->mnt_parent) |
554 | goto out; | 557 | goto out; |
555 | de = vfsmnt->mnt_mountpoint; | 558 | de = mnt->mnt_mountpoint; |
556 | vfsmnt = vfsmnt->mnt_parent; | 559 | mnt = mnt->mnt_parent; |
557 | } | 560 | } |
558 | 561 | ||
559 | if (!is_subdir(de, base)) | 562 | if (!is_subdir(de, base)) |
@@ -564,7 +567,7 @@ exit: | |||
564 | dput(base); | 567 | dput(base); |
565 | mntput(our_vfsmnt); | 568 | mntput(our_vfsmnt); |
566 | dput(root); | 569 | dput(root); |
567 | mntput(mnt); | 570 | mntput(vfsmnt); |
568 | return res; | 571 | return res; |
569 | out: | 572 | out: |
570 | spin_unlock(&vfsmount_lock); | 573 | spin_unlock(&vfsmount_lock); |
diff --git a/fs/proc/proc_devtree.c b/fs/proc/proc_devtree.c index 596b4b4f1c..abdf068bc2 100644 --- a/fs/proc/proc_devtree.c +++ b/fs/proc/proc_devtree.c | |||
@@ -52,7 +52,8 @@ static int property_read_proc(char *page, char **start, off_t off, | |||
52 | * Add a property to a node | 52 | * Add a property to a node |
53 | */ | 53 | */ |
54 | static struct proc_dir_entry * | 54 | static struct proc_dir_entry * |
55 | __proc_device_tree_add_prop(struct proc_dir_entry *de, struct property *pp) | 55 | __proc_device_tree_add_prop(struct proc_dir_entry *de, struct property *pp, |
56 | const char *name) | ||
56 | { | 57 | { |
57 | struct proc_dir_entry *ent; | 58 | struct proc_dir_entry *ent; |
58 | 59 | ||
@@ -60,14 +61,14 @@ __proc_device_tree_add_prop(struct proc_dir_entry *de, struct property *pp) | |||
60 | * Unfortunately proc_register puts each new entry | 61 | * Unfortunately proc_register puts each new entry |
61 | * at the beginning of the list. So we rearrange them. | 62 | * at the beginning of the list. So we rearrange them. |
62 | */ | 63 | */ |
63 | ent = create_proc_read_entry(pp->name, | 64 | ent = create_proc_read_entry(name, |
64 | strncmp(pp->name, "security-", 9) | 65 | strncmp(name, "security-", 9) |
65 | ? S_IRUGO : S_IRUSR, de, | 66 | ? S_IRUGO : S_IRUSR, de, |
66 | property_read_proc, pp); | 67 | property_read_proc, pp); |
67 | if (ent == NULL) | 68 | if (ent == NULL) |
68 | return NULL; | 69 | return NULL; |
69 | 70 | ||
70 | if (!strncmp(pp->name, "security-", 9)) | 71 | if (!strncmp(name, "security-", 9)) |
71 | ent->size = 0; /* don't leak number of password chars */ | 72 | ent->size = 0; /* don't leak number of password chars */ |
72 | else | 73 | else |
73 | ent->size = pp->length; | 74 | ent->size = pp->length; |
@@ -78,7 +79,7 @@ __proc_device_tree_add_prop(struct proc_dir_entry *de, struct property *pp) | |||
78 | 79 | ||
79 | void proc_device_tree_add_prop(struct proc_dir_entry *pde, struct property *prop) | 80 | void proc_device_tree_add_prop(struct proc_dir_entry *pde, struct property *prop) |
80 | { | 81 | { |
81 | __proc_device_tree_add_prop(pde, prop); | 82 | __proc_device_tree_add_prop(pde, prop, prop->name); |
82 | } | 83 | } |
83 | 84 | ||
84 | void proc_device_tree_remove_prop(struct proc_dir_entry *pde, | 85 | void proc_device_tree_remove_prop(struct proc_dir_entry *pde, |
@@ -106,6 +107,69 @@ void proc_device_tree_update_prop(struct proc_dir_entry *pde, | |||
106 | } | 107 | } |
107 | 108 | ||
108 | /* | 109 | /* |
110 | * Various dodgy firmware might give us nodes and/or properties with | ||
111 | * conflicting names. That's generally ok, except for exporting via /proc, | ||
112 | * so munge names here to ensure they're unique. | ||
113 | */ | ||
114 | |||
115 | static int duplicate_name(struct proc_dir_entry *de, const char *name) | ||
116 | { | ||
117 | struct proc_dir_entry *ent; | ||
118 | int found = 0; | ||
119 | |||
120 | spin_lock(&proc_subdir_lock); | ||
121 | |||
122 | for (ent = de->subdir; ent != NULL; ent = ent->next) { | ||
123 | if (strcmp(ent->name, name) == 0) { | ||
124 | found = 1; | ||
125 | break; | ||
126 | } | ||
127 | } | ||
128 | |||
129 | spin_unlock(&proc_subdir_lock); | ||
130 | |||
131 | return found; | ||
132 | } | ||
133 | |||
134 | static const char *fixup_name(struct device_node *np, struct proc_dir_entry *de, | ||
135 | const char *name) | ||
136 | { | ||
137 | char *fixed_name; | ||
138 | int fixup_len = strlen(name) + 2 + 1; /* name + #x + \0 */ | ||
139 | int i = 1, size; | ||
140 | |||
141 | realloc: | ||
142 | fixed_name = kmalloc(fixup_len, GFP_KERNEL); | ||
143 | if (fixed_name == NULL) { | ||
144 | printk(KERN_ERR "device-tree: Out of memory trying to fixup " | ||
145 | "name \"%s\"\n", name); | ||
146 | return name; | ||
147 | } | ||
148 | |||
149 | retry: | ||
150 | size = snprintf(fixed_name, fixup_len, "%s#%d", name, i); | ||
151 | size++; /* account for NULL */ | ||
152 | |||
153 | if (size > fixup_len) { | ||
154 | /* We ran out of space, free and reallocate. */ | ||
155 | kfree(fixed_name); | ||
156 | fixup_len = size; | ||
157 | goto realloc; | ||
158 | } | ||
159 | |||
160 | if (duplicate_name(de, fixed_name)) { | ||
161 | /* Multiple duplicates. Retry with a different offset. */ | ||
162 | i++; | ||
163 | goto retry; | ||
164 | } | ||
165 | |||
166 | printk(KERN_WARNING "device-tree: Duplicate name in %s, " | ||
167 | "renamed to \"%s\"\n", np->full_name, fixed_name); | ||
168 | |||
169 | return fixed_name; | ||
170 | } | ||
171 | |||
172 | /* | ||
109 | * Process a node, adding entries for its children and its properties. | 173 | * Process a node, adding entries for its children and its properties. |
110 | */ | 174 | */ |
111 | void proc_device_tree_add_node(struct device_node *np, | 175 | void proc_device_tree_add_node(struct device_node *np, |
@@ -118,37 +182,30 @@ void proc_device_tree_add_node(struct device_node *np, | |||
118 | 182 | ||
119 | set_node_proc_entry(np, de); | 183 | set_node_proc_entry(np, de); |
120 | for (child = NULL; (child = of_get_next_child(np, child));) { | 184 | for (child = NULL; (child = of_get_next_child(np, child));) { |
185 | /* Use everything after the last slash, or the full name */ | ||
121 | p = strrchr(child->full_name, '/'); | 186 | p = strrchr(child->full_name, '/'); |
122 | if (!p) | 187 | if (!p) |
123 | p = child->full_name; | 188 | p = child->full_name; |
124 | else | 189 | else |
125 | ++p; | 190 | ++p; |
191 | |||
192 | if (duplicate_name(de, p)) | ||
193 | p = fixup_name(np, de, p); | ||
194 | |||
126 | ent = proc_mkdir(p, de); | 195 | ent = proc_mkdir(p, de); |
127 | if (ent == 0) | 196 | if (ent == 0) |
128 | break; | 197 | break; |
129 | proc_device_tree_add_node(child, ent); | 198 | proc_device_tree_add_node(child, ent); |
130 | } | 199 | } |
131 | of_node_put(child); | 200 | of_node_put(child); |
201 | |||
132 | for (pp = np->properties; pp != 0; pp = pp->next) { | 202 | for (pp = np->properties; pp != 0; pp = pp->next) { |
133 | /* | 203 | p = pp->name; |
134 | * Yet another Apple device-tree bogosity: on some machines, | 204 | |
135 | * they have properties & nodes with the same name. Those | 205 | if (duplicate_name(de, p)) |
136 | * properties are quite unimportant for us though, thus we | 206 | p = fixup_name(np, de, p); |
137 | * simply "skip" them here, but we do have to check. | ||
138 | */ | ||
139 | spin_lock(&proc_subdir_lock); | ||
140 | for (ent = de->subdir; ent != NULL; ent = ent->next) | ||
141 | if (!strcmp(ent->name, pp->name)) | ||
142 | break; | ||
143 | spin_unlock(&proc_subdir_lock); | ||
144 | if (ent != NULL) { | ||
145 | printk(KERN_WARNING "device-tree: property \"%s\" name" | ||
146 | " conflicts with node in %s\n", pp->name, | ||
147 | np->full_name); | ||
148 | continue; | ||
149 | } | ||
150 | 207 | ||
151 | ent = __proc_device_tree_add_prop(de, pp); | 208 | ent = __proc_device_tree_add_prop(de, pp, p); |
152 | if (ent == 0) | 209 | if (ent == 0) |
153 | break; | 210 | break; |
154 | } | 211 | } |
diff --git a/fs/proc/proc_misc.c b/fs/proc/proc_misc.c index ef5a3323f4..5c10ea1574 100644 --- a/fs/proc/proc_misc.c +++ b/fs/proc/proc_misc.c | |||
@@ -249,144 +249,60 @@ static int cpuinfo_open(struct inode *inode, struct file *file) | |||
249 | return seq_open(file, &cpuinfo_op); | 249 | return seq_open(file, &cpuinfo_op); |
250 | } | 250 | } |
251 | 251 | ||
252 | enum devinfo_states { | 252 | static struct file_operations proc_cpuinfo_operations = { |
253 | CHR_HDR, | 253 | .open = cpuinfo_open, |
254 | CHR_LIST, | 254 | .read = seq_read, |
255 | BLK_HDR, | 255 | .llseek = seq_lseek, |
256 | BLK_LIST, | 256 | .release = seq_release, |
257 | DEVINFO_DONE | ||
258 | }; | ||
259 | |||
260 | struct devinfo_state { | ||
261 | void *chrdev; | ||
262 | void *blkdev; | ||
263 | unsigned int num_records; | ||
264 | unsigned int cur_record; | ||
265 | enum devinfo_states state; | ||
266 | }; | 257 | }; |
267 | 258 | ||
268 | static void *devinfo_start(struct seq_file *f, loff_t *pos) | 259 | static int devinfo_show(struct seq_file *f, void *v) |
269 | { | 260 | { |
270 | struct devinfo_state *info = f->private; | 261 | int i = *(loff_t *) v; |
271 | 262 | ||
272 | if (*pos) { | 263 | if (i < CHRDEV_MAJOR_HASH_SIZE) { |
273 | if ((info) && (*pos <= info->num_records)) | 264 | if (i == 0) |
274 | return info; | 265 | seq_printf(f, "Character devices:\n"); |
275 | return NULL; | 266 | chrdev_show(f, i); |
267 | } else { | ||
268 | i -= CHRDEV_MAJOR_HASH_SIZE; | ||
269 | if (i == 0) | ||
270 | seq_printf(f, "\nBlock devices:\n"); | ||
271 | blkdev_show(f, i); | ||
276 | } | 272 | } |
277 | info = kmalloc(sizeof(*info), GFP_KERNEL); | 273 | return 0; |
278 | f->private = info; | ||
279 | info->chrdev = acquire_chrdev_list(); | ||
280 | info->blkdev = acquire_blkdev_list(); | ||
281 | info->state = CHR_HDR; | ||
282 | info->num_records = count_chrdev_list(); | ||
283 | info->num_records += count_blkdev_list(); | ||
284 | info->num_records += 2; /* Character and Block headers */ | ||
285 | *pos = 1; | ||
286 | info->cur_record = *pos; | ||
287 | return info; | ||
288 | } | 274 | } |
289 | 275 | ||
290 | static void *devinfo_next(struct seq_file *f, void *v, loff_t *pos) | 276 | static void *devinfo_start(struct seq_file *f, loff_t *pos) |
291 | { | 277 | { |
292 | int idummy; | 278 | if (*pos < (BLKDEV_MAJOR_HASH_SIZE + CHRDEV_MAJOR_HASH_SIZE)) |
293 | char *ndummy; | 279 | return pos; |
294 | struct devinfo_state *info = f->private; | 280 | return NULL; |
295 | |||
296 | switch (info->state) { | ||
297 | case CHR_HDR: | ||
298 | info->state = CHR_LIST; | ||
299 | (*pos)++; | ||
300 | /*fallthrough*/ | ||
301 | case CHR_LIST: | ||
302 | if (get_chrdev_info(info->chrdev,&idummy,&ndummy)) { | ||
303 | /* | ||
304 | * The character dev list is complete | ||
305 | */ | ||
306 | info->state = BLK_HDR; | ||
307 | } else { | ||
308 | info->chrdev = get_next_chrdev(info->chrdev); | ||
309 | } | ||
310 | (*pos)++; | ||
311 | break; | ||
312 | case BLK_HDR: | ||
313 | info->state = BLK_LIST; | ||
314 | (*pos)++; | ||
315 | /*fallthrough*/ | ||
316 | case BLK_LIST: | ||
317 | if (get_blkdev_info(info->blkdev,&idummy,&ndummy)) { | ||
318 | /* | ||
319 | * The block dev list is complete | ||
320 | */ | ||
321 | info->state = DEVINFO_DONE; | ||
322 | } else { | ||
323 | info->blkdev = get_next_blkdev(info->blkdev); | ||
324 | } | ||
325 | (*pos)++; | ||
326 | break; | ||
327 | case DEVINFO_DONE: | ||
328 | (*pos)++; | ||
329 | info->cur_record = *pos; | ||
330 | info = NULL; | ||
331 | break; | ||
332 | default: | ||
333 | break; | ||
334 | } | ||
335 | if (info) | ||
336 | info->cur_record = *pos; | ||
337 | return info; | ||
338 | } | 281 | } |
339 | 282 | ||
340 | static void devinfo_stop(struct seq_file *f, void *v) | 283 | static void *devinfo_next(struct seq_file *f, void *v, loff_t *pos) |
341 | { | 284 | { |
342 | struct devinfo_state *info = f->private; | 285 | (*pos)++; |
343 | 286 | if (*pos >= (BLKDEV_MAJOR_HASH_SIZE + CHRDEV_MAJOR_HASH_SIZE)) | |
344 | if (info) { | 287 | return NULL; |
345 | release_chrdev_list(info->chrdev); | 288 | return pos; |
346 | release_blkdev_list(info->blkdev); | ||
347 | f->private = NULL; | ||
348 | kfree(info); | ||
349 | } | ||
350 | } | 289 | } |
351 | 290 | ||
352 | static int devinfo_show(struct seq_file *f, void *arg) | 291 | static void devinfo_stop(struct seq_file *f, void *v) |
353 | { | 292 | { |
354 | int major; | 293 | /* Nothing to do */ |
355 | char *name; | ||
356 | struct devinfo_state *info = f->private; | ||
357 | |||
358 | switch(info->state) { | ||
359 | case CHR_HDR: | ||
360 | seq_printf(f,"Character devices:\n"); | ||
361 | /* fallthrough */ | ||
362 | case CHR_LIST: | ||
363 | if (!get_chrdev_info(info->chrdev,&major,&name)) | ||
364 | seq_printf(f,"%3d %s\n",major,name); | ||
365 | break; | ||
366 | case BLK_HDR: | ||
367 | seq_printf(f,"\nBlock devices:\n"); | ||
368 | /* fallthrough */ | ||
369 | case BLK_LIST: | ||
370 | if (!get_blkdev_info(info->blkdev,&major,&name)) | ||
371 | seq_printf(f,"%3d %s\n",major,name); | ||
372 | break; | ||
373 | default: | ||
374 | break; | ||
375 | } | ||
376 | |||
377 | return 0; | ||
378 | } | 294 | } |
379 | 295 | ||
380 | static struct seq_operations devinfo_op = { | 296 | static struct seq_operations devinfo_ops = { |
381 | .start = devinfo_start, | 297 | .start = devinfo_start, |
382 | .next = devinfo_next, | 298 | .next = devinfo_next, |
383 | .stop = devinfo_stop, | 299 | .stop = devinfo_stop, |
384 | .show = devinfo_show, | 300 | .show = devinfo_show |
385 | }; | 301 | }; |
386 | 302 | ||
387 | static int devinfo_open(struct inode *inode, struct file *file) | 303 | static int devinfo_open(struct inode *inode, struct file *filp) |
388 | { | 304 | { |
389 | return seq_open(file, &devinfo_op); | 305 | return seq_open(filp, &devinfo_ops); |
390 | } | 306 | } |
391 | 307 | ||
392 | static struct file_operations proc_devinfo_operations = { | 308 | static struct file_operations proc_devinfo_operations = { |
@@ -396,13 +312,6 @@ static struct file_operations proc_devinfo_operations = { | |||
396 | .release = seq_release, | 312 | .release = seq_release, |
397 | }; | 313 | }; |
398 | 314 | ||
399 | static struct file_operations proc_cpuinfo_operations = { | ||
400 | .open = cpuinfo_open, | ||
401 | .read = seq_read, | ||
402 | .llseek = seq_lseek, | ||
403 | .release = seq_release, | ||
404 | }; | ||
405 | |||
406 | extern struct seq_operations vmstat_op; | 315 | extern struct seq_operations vmstat_op; |
407 | static int vmstat_open(struct inode *inode, struct file *file) | 316 | static int vmstat_open(struct inode *inode, struct file *file) |
408 | { | 317 | { |