Blame view
fs/proc/inode.c
12.2 KB
1da177e4c Linux-2.6.12-rc2 |
1 2 3 4 5 6 7 8 9 |
/* * linux/fs/proc/inode.c * * Copyright (C) 1991, 1992 Linus Torvalds */ #include <linux/time.h> #include <linux/proc_fs.h> #include <linux/kernel.h> |
97412950b procfs: parse mou... |
10 |
#include <linux/pid_namespace.h> |
1da177e4c Linux-2.6.12-rc2 |
11 12 13 |
#include <linux/mm.h> #include <linux/string.h> #include <linux/stat.h> |
786d7e161 Fix rmmod/read/wr... |
14 |
#include <linux/completion.h> |
dd23aae4f Fix select on /pr... |
15 |
#include <linux/poll.h> |
1da177e4c Linux-2.6.12-rc2 |
16 17 18 19 |
#include <linux/file.h> #include <linux/limits.h> #include <linux/init.h> #include <linux/module.h> |
9043476f7 [PATCH] sanitize ... |
20 |
#include <linux/sysctl.h> |
97412950b procfs: parse mou... |
21 |
#include <linux/seq_file.h> |
5a0e3ad6a include cleanup: ... |
22 |
#include <linux/slab.h> |
97412950b procfs: parse mou... |
23 |
#include <linux/mount.h> |
1da177e4c Linux-2.6.12-rc2 |
24 25 26 |
#include <asm/system.h> #include <asm/uaccess.h> |
fee781e6c [PATCH] fs/proc/:... |
27 |
#include "internal.h" |
1da177e4c Linux-2.6.12-rc2 |
28 |
|
8267952b3 switch procfs to ... |
29 |
static void proc_evict_inode(struct inode *inode) |
1da177e4c Linux-2.6.12-rc2 |
30 31 |
{ struct proc_dir_entry *de; |
dfef6dcd3 unfuck proc_sysct... |
32 |
struct ctl_table_header *head; |
6b4e306aa ns: proc files fo... |
33 |
const struct proc_ns_operations *ns_ops; |
1da177e4c Linux-2.6.12-rc2 |
34 |
|
fef266580 [PATCH] update fi... |
35 |
truncate_inode_pages(&inode->i_data, 0); |
8267952b3 switch procfs to ... |
36 |
end_writeback(inode); |
fef266580 [PATCH] update fi... |
37 |
|
99f895518 [PATCH] proc: don... |
38 |
/* Stop tracking associated processes */ |
13b41b094 [PATCH] proc: Use... |
39 |
put_pid(PROC_I(inode)->pid); |
1da177e4c Linux-2.6.12-rc2 |
40 41 42 |
/* Let go of any associated proc directory entry */ de = PROC_I(inode)->pde; |
99b762338 proc 2/2: remove ... |
43 |
if (de) |
135d5655d proc: rename de_g... |
44 |
pde_put(de); |
dfef6dcd3 unfuck proc_sysct... |
45 46 47 48 49 |
head = PROC_I(inode)->sysctl; if (head) { rcu_assign_pointer(PROC_I(inode)->sysctl, NULL); sysctl_head_put(head); } |
6b4e306aa ns: proc files fo... |
50 51 52 53 |
/* Release any associated namespace */ ns_ops = PROC_I(inode)->ns_ops; if (ns_ops && ns_ops->put) ns_ops->put(PROC_I(inode)->ns); |
1da177e4c Linux-2.6.12-rc2 |
54 |
} |
e18b890bb [PATCH] slab: rem... |
55 |
static struct kmem_cache * proc_inode_cachep; |
1da177e4c Linux-2.6.12-rc2 |
56 57 58 59 60 |
static struct inode *proc_alloc_inode(struct super_block *sb) { struct proc_inode *ei; struct inode *inode; |
e94b17660 [PATCH] slab: rem... |
61 |
ei = (struct proc_inode *)kmem_cache_alloc(proc_inode_cachep, GFP_KERNEL); |
1da177e4c Linux-2.6.12-rc2 |
62 63 |
if (!ei) return NULL; |
13b41b094 [PATCH] proc: Use... |
64 |
ei->pid = NULL; |
aed7a6c47 [PATCH] proc: Rep... |
65 |
ei->fd = 0; |
1da177e4c Linux-2.6.12-rc2 |
66 67 |
ei->op.proc_get_link = NULL; ei->pde = NULL; |
9043476f7 [PATCH] sanitize ... |
68 69 |
ei->sysctl = NULL; ei->sysctl_entry = NULL; |
6b4e306aa ns: proc files fo... |
70 71 |
ei->ns = NULL; ei->ns_ops = NULL; |
1da177e4c Linux-2.6.12-rc2 |
72 73 74 75 |
inode = &ei->vfs_inode; inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; return inode; } |
fa0d7e3de fs: icache RCU fr... |
76 |
static void proc_i_callback(struct rcu_head *head) |
1da177e4c Linux-2.6.12-rc2 |
77 |
{ |
fa0d7e3de fs: icache RCU fr... |
78 |
struct inode *inode = container_of(head, struct inode, i_rcu); |
1da177e4c Linux-2.6.12-rc2 |
79 80 |
kmem_cache_free(proc_inode_cachep, PROC_I(inode)); } |
fa0d7e3de fs: icache RCU fr... |
81 82 83 84 |
static void proc_destroy_inode(struct inode *inode) { call_rcu(&inode->i_rcu, proc_i_callback); } |
51cc50685 SL*B: drop kmem c... |
85 |
static void init_once(void *foo) |
1da177e4c Linux-2.6.12-rc2 |
86 87 |
{ struct proc_inode *ei = (struct proc_inode *) foo; |
a35afb830 Remove SLAB_CTOR_... |
88 |
inode_init_once(&ei->vfs_inode); |
1da177e4c Linux-2.6.12-rc2 |
89 |
} |
20c2df83d mm: Remove slab d... |
90 |
|
5bcd7ff9e proc: proc_init_i... |
91 |
void __init proc_init_inodecache(void) |
1da177e4c Linux-2.6.12-rc2 |
92 93 94 |
{ proc_inode_cachep = kmem_cache_create("proc_inode_cache", sizeof(struct proc_inode), |
fffb60f93 [PATCH] cpuset me... |
95 |
0, (SLAB_RECLAIM_ACCOUNT| |
040b5c6f9 SLAB_PANIC more (... |
96 |
SLAB_MEM_SPREAD|SLAB_PANIC), |
20c2df83d mm: Remove slab d... |
97 |
init_once); |
1da177e4c Linux-2.6.12-rc2 |
98 |
} |
97412950b procfs: parse mou... |
99 100 |
static int proc_show_options(struct seq_file *seq, struct dentry *root) { |
0499680a4 procfs: add hidep... |
101 102 103 104 105 106 107 |
struct super_block *sb = root->d_sb; struct pid_namespace *pid = sb->s_fs_info; if (pid->pid_gid) seq_printf(seq, ",gid=%lu", (unsigned long)pid->pid_gid); if (pid->hide_pid != 0) seq_printf(seq, ",hidepid=%u", pid->hide_pid); |
97412950b procfs: parse mou... |
108 109 |
return 0; } |
ee9b6d61a [PATCH] Mark stru... |
110 |
static const struct super_operations proc_sops = { |
1da177e4c Linux-2.6.12-rc2 |
111 112 |
.alloc_inode = proc_alloc_inode, .destroy_inode = proc_destroy_inode, |
1da177e4c Linux-2.6.12-rc2 |
113 |
.drop_inode = generic_delete_inode, |
8267952b3 switch procfs to ... |
114 |
.evict_inode = proc_evict_inode, |
1da177e4c Linux-2.6.12-rc2 |
115 |
.statfs = simple_statfs, |
97412950b procfs: parse mou... |
116 117 |
.remount_fs = proc_remount, .show_options = proc_show_options, |
1da177e4c Linux-2.6.12-rc2 |
118 |
}; |
881adb853 proc: always do -... |
119 |
static void __pde_users_dec(struct proc_dir_entry *pde) |
786d7e161 Fix rmmod/read/wr... |
120 |
{ |
786d7e161 Fix rmmod/read/wr... |
121 122 123 |
pde->pde_users--; if (pde->pde_unload_completion && pde->pde_users == 0) complete(pde->pde_unload_completion); |
881adb853 proc: always do -... |
124 |
} |
3dec7f59c proc 1/2: do PDE ... |
125 |
void pde_users_dec(struct proc_dir_entry *pde) |
881adb853 proc: always do -... |
126 127 128 |
{ spin_lock(&pde->pde_unload_lock); __pde_users_dec(pde); |
786d7e161 Fix rmmod/read/wr... |
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 |
spin_unlock(&pde->pde_unload_lock); } static loff_t proc_reg_llseek(struct file *file, loff_t offset, int whence) { struct proc_dir_entry *pde = PDE(file->f_path.dentry->d_inode); loff_t rv = -EINVAL; loff_t (*llseek)(struct file *, loff_t, int); spin_lock(&pde->pde_unload_lock); /* * remove_proc_entry() is going to delete PDE (as part of module * cleanup sequence). No new callers into module allowed. */ if (!pde->proc_fops) { spin_unlock(&pde->pde_unload_lock); return rv; } /* * Bump refcount so that remove_proc_entry will wail for ->llseek to * complete. */ pde->pde_users++; /* * Save function pointer under lock, to protect against ->proc_fops * NULL'ifying right after ->pde_unload_lock is dropped. */ llseek = pde->proc_fops->llseek; spin_unlock(&pde->pde_unload_lock); if (!llseek) llseek = default_llseek; rv = llseek(file, offset, whence); pde_users_dec(pde); return rv; } static ssize_t proc_reg_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { struct proc_dir_entry *pde = PDE(file->f_path.dentry->d_inode); ssize_t rv = -EIO; ssize_t (*read)(struct file *, char __user *, size_t, loff_t *); spin_lock(&pde->pde_unload_lock); if (!pde->proc_fops) { spin_unlock(&pde->pde_unload_lock); return rv; } pde->pde_users++; read = pde->proc_fops->read; spin_unlock(&pde->pde_unload_lock); if (read) rv = read(file, buf, count, ppos); pde_users_dec(pde); return rv; } static ssize_t proc_reg_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { struct proc_dir_entry *pde = PDE(file->f_path.dentry->d_inode); ssize_t rv = -EIO; ssize_t (*write)(struct file *, const char __user *, size_t, loff_t *); spin_lock(&pde->pde_unload_lock); if (!pde->proc_fops) { spin_unlock(&pde->pde_unload_lock); return rv; } pde->pde_users++; write = pde->proc_fops->write; spin_unlock(&pde->pde_unload_lock); if (write) rv = write(file, buf, count, ppos); pde_users_dec(pde); return rv; } static unsigned int proc_reg_poll(struct file *file, struct poll_table_struct *pts) { struct proc_dir_entry *pde = PDE(file->f_path.dentry->d_inode); |
dd23aae4f Fix select on /pr... |
214 |
unsigned int rv = DEFAULT_POLLMASK; |
786d7e161 Fix rmmod/read/wr... |
215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 |
unsigned int (*poll)(struct file *, struct poll_table_struct *); spin_lock(&pde->pde_unload_lock); if (!pde->proc_fops) { spin_unlock(&pde->pde_unload_lock); return rv; } pde->pde_users++; poll = pde->proc_fops->poll; spin_unlock(&pde->pde_unload_lock); if (poll) rv = poll(file, pts); pde_users_dec(pde); return rv; } static long proc_reg_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct proc_dir_entry *pde = PDE(file->f_path.dentry->d_inode); long rv = -ENOTTY; |
b19dd42fa bkl: Remove locke... |
237 |
long (*ioctl)(struct file *, unsigned int, unsigned long); |
786d7e161 Fix rmmod/read/wr... |
238 239 240 241 242 243 244 |
spin_lock(&pde->pde_unload_lock); if (!pde->proc_fops) { spin_unlock(&pde->pde_unload_lock); return rv; } pde->pde_users++; |
b19dd42fa bkl: Remove locke... |
245 |
ioctl = pde->proc_fops->unlocked_ioctl; |
786d7e161 Fix rmmod/read/wr... |
246 |
spin_unlock(&pde->pde_unload_lock); |
b19dd42fa bkl: Remove locke... |
247 248 |
if (ioctl) rv = ioctl(file, cmd, arg); |
786d7e161 Fix rmmod/read/wr... |
249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 |
pde_users_dec(pde); return rv; } #ifdef CONFIG_COMPAT static long proc_reg_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct proc_dir_entry *pde = PDE(file->f_path.dentry->d_inode); long rv = -ENOTTY; long (*compat_ioctl)(struct file *, unsigned int, unsigned long); spin_lock(&pde->pde_unload_lock); if (!pde->proc_fops) { spin_unlock(&pde->pde_unload_lock); return rv; } pde->pde_users++; compat_ioctl = pde->proc_fops->compat_ioctl; spin_unlock(&pde->pde_unload_lock); if (compat_ioctl) rv = compat_ioctl(file, cmd, arg); pde_users_dec(pde); return rv; } #endif static int proc_reg_mmap(struct file *file, struct vm_area_struct *vma) { struct proc_dir_entry *pde = PDE(file->f_path.dentry->d_inode); int rv = -EIO; int (*mmap)(struct file *, struct vm_area_struct *); spin_lock(&pde->pde_unload_lock); if (!pde->proc_fops) { spin_unlock(&pde->pde_unload_lock); return rv; } pde->pde_users++; mmap = pde->proc_fops->mmap; spin_unlock(&pde->pde_unload_lock); if (mmap) rv = mmap(file, vma); pde_users_dec(pde); return rv; } static int proc_reg_open(struct inode *inode, struct file *file) { struct proc_dir_entry *pde = PDE(inode); int rv = 0; int (*open)(struct inode *, struct file *); |
881adb853 proc: always do -... |
305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 |
int (*release)(struct inode *, struct file *); struct pde_opener *pdeo; /* * What for, you ask? Well, we can have open, rmmod, remove_proc_entry * sequence. ->release won't be called because ->proc_fops will be * cleared. Depending on complexity of ->release, consequences vary. * * We can't wait for mercy when close will be done for real, it's * deadlockable: rmmod foo </proc/foo . So, we're going to do ->release * by hand in remove_proc_entry(). For this, save opener's credentials * for later. */ pdeo = kmalloc(sizeof(struct pde_opener), GFP_KERNEL); if (!pdeo) return -ENOMEM; |
786d7e161 Fix rmmod/read/wr... |
321 322 323 324 |
spin_lock(&pde->pde_unload_lock); if (!pde->proc_fops) { spin_unlock(&pde->pde_unload_lock); |
881adb853 proc: always do -... |
325 |
kfree(pdeo); |
d2857e79a procfs: return EN... |
326 |
return -ENOENT; |
786d7e161 Fix rmmod/read/wr... |
327 328 329 |
} pde->pde_users++; open = pde->proc_fops->open; |
881adb853 proc: always do -... |
330 |
release = pde->proc_fops->release; |
786d7e161 Fix rmmod/read/wr... |
331 332 333 334 |
spin_unlock(&pde->pde_unload_lock); if (open) rv = open(inode, file); |
881adb853 proc: always do -... |
335 336 337 338 339 340 341 342 343 344 345 346 |
spin_lock(&pde->pde_unload_lock); if (rv == 0 && release) { /* To know what to release. */ pdeo->inode = inode; pdeo->file = file; /* Strictly for "too late" ->release in proc_reg_release(). */ pdeo->release = release; list_add(&pdeo->lh, &pde->pde_openers); } else kfree(pdeo); __pde_users_dec(pde); spin_unlock(&pde->pde_unload_lock); |
786d7e161 Fix rmmod/read/wr... |
347 348 |
return rv; } |
881adb853 proc: always do -... |
349 350 351 352 353 354 355 356 357 358 359 |
static struct pde_opener *find_pde_opener(struct proc_dir_entry *pde, struct inode *inode, struct file *file) { struct pde_opener *pdeo; list_for_each_entry(pdeo, &pde->pde_openers, lh) { if (pdeo->inode == inode && pdeo->file == file) return pdeo; } return NULL; } |
786d7e161 Fix rmmod/read/wr... |
360 361 362 363 364 |
static int proc_reg_release(struct inode *inode, struct file *file) { struct proc_dir_entry *pde = PDE(inode); int rv = 0; int (*release)(struct inode *, struct file *); |
881adb853 proc: always do -... |
365 |
struct pde_opener *pdeo; |
786d7e161 Fix rmmod/read/wr... |
366 367 |
spin_lock(&pde->pde_unload_lock); |
881adb853 proc: always do -... |
368 |
pdeo = find_pde_opener(pde, inode, file); |
786d7e161 Fix rmmod/read/wr... |
369 |
if (!pde->proc_fops) { |
881adb853 proc: always do -... |
370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 |
/* * Can't simply exit, __fput() will think that everything is OK, * and move on to freeing struct file. remove_proc_entry() will * find slacker in opener's list and will try to do non-trivial * things with struct file. Therefore, remove opener from list. * * But if opener is removed from list, who will ->release it? */ if (pdeo) { list_del(&pdeo->lh); spin_unlock(&pde->pde_unload_lock); rv = pdeo->release(inode, file); kfree(pdeo); } else spin_unlock(&pde->pde_unload_lock); |
786d7e161 Fix rmmod/read/wr... |
385 386 387 388 |
return rv; } pde->pde_users++; release = pde->proc_fops->release; |
881adb853 proc: always do -... |
389 390 391 392 |
if (pdeo) { list_del(&pdeo->lh); kfree(pdeo); } |
786d7e161 Fix rmmod/read/wr... |
393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 |
spin_unlock(&pde->pde_unload_lock); if (release) rv = release(inode, file); pde_users_dec(pde); return rv; } static const struct file_operations proc_reg_file_ops = { .llseek = proc_reg_llseek, .read = proc_reg_read, .write = proc_reg_write, .poll = proc_reg_poll, .unlocked_ioctl = proc_reg_unlocked_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = proc_reg_compat_ioctl, #endif .mmap = proc_reg_mmap, .open = proc_reg_open, .release = proc_reg_release, }; |
778f3dd5a Fix procfs compat... |
415 416 417 418 419 420 421 422 423 424 425 426 |
#ifdef CONFIG_COMPAT static const struct file_operations proc_reg_file_ops_no_compat = { .llseek = proc_reg_llseek, .read = proc_reg_read, .write = proc_reg_write, .poll = proc_reg_poll, .unlocked_ioctl = proc_reg_unlocked_ioctl, .mmap = proc_reg_mmap, .open = proc_reg_open, .release = proc_reg_release, }; #endif |
6d1b6e4ef proc: ->low_ino c... |
427 |
struct inode *proc_get_inode(struct super_block *sb, struct proc_dir_entry *de) |
1da177e4c Linux-2.6.12-rc2 |
428 429 |
{ struct inode * inode; |
6d1b6e4ef proc: ->low_ino c... |
430 |
inode = iget_locked(sb, de->low_ino); |
1da177e4c Linux-2.6.12-rc2 |
431 |
if (!inode) |
99b762338 proc 2/2: remove ... |
432 |
return NULL; |
a1d4aebbf iget: stop PROCFS... |
433 434 435 436 |
if (inode->i_state & I_NEW) { inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; PROC_I(inode)->fd = 0; PROC_I(inode)->pde = de; |
5e971dce0 proc: drop severa... |
437 438 439 440 441 442 443 444 445 |
if (de->mode) { inode->i_mode = de->mode; inode->i_uid = de->uid; inode->i_gid = de->gid; } if (de->size) inode->i_size = de->size; if (de->nlink) |
bfe868486 filesystems: add ... |
446 |
set_nlink(inode, de->nlink); |
5e971dce0 proc: drop severa... |
447 448 449 450 |
if (de->proc_iops) inode->i_op = de->proc_iops; if (de->proc_fops) { if (S_ISREG(inode->i_mode)) { |
778f3dd5a Fix procfs compat... |
451 |
#ifdef CONFIG_COMPAT |
5e971dce0 proc: drop severa... |
452 453 454 455 |
if (!de->proc_fops->compat_ioctl) inode->i_fop = &proc_reg_file_ops_no_compat; else |
778f3dd5a Fix procfs compat... |
456 |
#endif |
5e971dce0 proc: drop severa... |
457 458 459 |
inode->i_fop = &proc_reg_file_ops; } else { inode->i_fop = de->proc_fops; |
778f3dd5a Fix procfs compat... |
460 |
} |
786d7e161 Fix rmmod/read/wr... |
461 |
} |
a1d4aebbf iget: stop PROCFS... |
462 |
unlock_new_inode(inode); |
99b762338 proc 2/2: remove ... |
463 |
} else |
135d5655d proc: rename de_g... |
464 |
pde_put(de); |
1da177e4c Linux-2.6.12-rc2 |
465 |
return inode; |
1da177e4c Linux-2.6.12-rc2 |
466 |
} |
07543f5c7 pid namespaces: m... |
467 |
int proc_fill_super(struct super_block *s) |
1da177e4c Linux-2.6.12-rc2 |
468 469 |
{ struct inode * root_inode; |
92d032855 Mark /proc MS_NOS... |
470 |
s->s_flags |= MS_NODIRATIME | MS_NOSUID | MS_NOEXEC; |
1da177e4c Linux-2.6.12-rc2 |
471 472 473 474 475 476 |
s->s_blocksize = 1024; s->s_blocksize_bits = 10; s->s_magic = PROC_SUPER_MAGIC; s->s_op = &proc_sops; s->s_time_gran = 1; |
135d5655d proc: rename de_g... |
477 |
pde_get(&proc_root); |
6d1b6e4ef proc: ->low_ino c... |
478 |
root_inode = proc_get_inode(s, &proc_root); |
1da177e4c Linux-2.6.12-rc2 |
479 480 |
if (!root_inode) goto out_no_root; |
1da177e4c Linux-2.6.12-rc2 |
481 482 483 484 485 486 487 488 489 490 491 |
root_inode->i_uid = 0; root_inode->i_gid = 0; s->s_root = d_alloc_root(root_inode); if (!s->s_root) goto out_no_root; return 0; out_no_root: printk("proc_read_super: get root inode failed "); iput(root_inode); |
135d5655d proc: rename de_g... |
492 |
pde_put(&proc_root); |
1da177e4c Linux-2.6.12-rc2 |
493 494 |
return -ENOMEM; } |