diff options
Diffstat (limited to 'fs/proc/inode.c')
-rw-r--r-- | fs/proc/inode.c | 81 |
1 files changed, 70 insertions, 11 deletions
diff --git a/fs/proc/inode.c b/fs/proc/inode.c index b08d10017911..02eca2ed9dd7 100644 --- a/fs/proc/inode.c +++ b/fs/proc/inode.c | |||
@@ -111,27 +111,25 @@ int __init proc_init_inodecache(void) | |||
111 | return 0; | 111 | return 0; |
112 | } | 112 | } |
113 | 113 | ||
114 | static int proc_remount(struct super_block *sb, int *flags, char *data) | ||
115 | { | ||
116 | *flags |= MS_NODIRATIME; | ||
117 | return 0; | ||
118 | } | ||
119 | |||
120 | static const struct super_operations proc_sops = { | 114 | static const struct super_operations proc_sops = { |
121 | .alloc_inode = proc_alloc_inode, | 115 | .alloc_inode = proc_alloc_inode, |
122 | .destroy_inode = proc_destroy_inode, | 116 | .destroy_inode = proc_destroy_inode, |
123 | .drop_inode = generic_delete_inode, | 117 | .drop_inode = generic_delete_inode, |
124 | .delete_inode = proc_delete_inode, | 118 | .delete_inode = proc_delete_inode, |
125 | .statfs = simple_statfs, | 119 | .statfs = simple_statfs, |
126 | .remount_fs = proc_remount, | ||
127 | }; | 120 | }; |
128 | 121 | ||
129 | static void pde_users_dec(struct proc_dir_entry *pde) | 122 | static void __pde_users_dec(struct proc_dir_entry *pde) |
130 | { | 123 | { |
131 | spin_lock(&pde->pde_unload_lock); | ||
132 | pde->pde_users--; | 124 | pde->pde_users--; |
133 | if (pde->pde_unload_completion && pde->pde_users == 0) | 125 | if (pde->pde_unload_completion && pde->pde_users == 0) |
134 | complete(pde->pde_unload_completion); | 126 | complete(pde->pde_unload_completion); |
127 | } | ||
128 | |||
129 | static void pde_users_dec(struct proc_dir_entry *pde) | ||
130 | { | ||
131 | spin_lock(&pde->pde_unload_lock); | ||
132 | __pde_users_dec(pde); | ||
135 | spin_unlock(&pde->pde_unload_lock); | 133 | spin_unlock(&pde->pde_unload_lock); |
136 | } | 134 | } |
137 | 135 | ||
@@ -318,36 +316,97 @@ static int proc_reg_open(struct inode *inode, struct file *file) | |||
318 | struct proc_dir_entry *pde = PDE(inode); | 316 | struct proc_dir_entry *pde = PDE(inode); |
319 | int rv = 0; | 317 | int rv = 0; |
320 | int (*open)(struct inode *, struct file *); | 318 | int (*open)(struct inode *, struct file *); |
319 | int (*release)(struct inode *, struct file *); | ||
320 | struct pde_opener *pdeo; | ||
321 | |||
322 | /* | ||
323 | * What for, you ask? Well, we can have open, rmmod, remove_proc_entry | ||
324 | * sequence. ->release won't be called because ->proc_fops will be | ||
325 | * cleared. Depending on complexity of ->release, consequences vary. | ||
326 | * | ||
327 | * We can't wait for mercy when close will be done for real, it's | ||
328 | * deadlockable: rmmod foo </proc/foo . So, we're going to do ->release | ||
329 | * by hand in remove_proc_entry(). For this, save opener's credentials | ||
330 | * for later. | ||
331 | */ | ||
332 | pdeo = kmalloc(sizeof(struct pde_opener), GFP_KERNEL); | ||
333 | if (!pdeo) | ||
334 | return -ENOMEM; | ||
321 | 335 | ||
322 | spin_lock(&pde->pde_unload_lock); | 336 | spin_lock(&pde->pde_unload_lock); |
323 | if (!pde->proc_fops) { | 337 | if (!pde->proc_fops) { |
324 | spin_unlock(&pde->pde_unload_lock); | 338 | spin_unlock(&pde->pde_unload_lock); |
339 | kfree(pdeo); | ||
325 | return rv; | 340 | return rv; |
326 | } | 341 | } |
327 | pde->pde_users++; | 342 | pde->pde_users++; |
328 | open = pde->proc_fops->open; | 343 | open = pde->proc_fops->open; |
344 | release = pde->proc_fops->release; | ||
329 | spin_unlock(&pde->pde_unload_lock); | 345 | spin_unlock(&pde->pde_unload_lock); |
330 | 346 | ||
331 | if (open) | 347 | if (open) |
332 | rv = open(inode, file); | 348 | rv = open(inode, file); |
333 | 349 | ||
334 | pde_users_dec(pde); | 350 | spin_lock(&pde->pde_unload_lock); |
351 | if (rv == 0 && release) { | ||
352 | /* To know what to release. */ | ||
353 | pdeo->inode = inode; | ||
354 | pdeo->file = file; | ||
355 | /* Strictly for "too late" ->release in proc_reg_release(). */ | ||
356 | pdeo->release = release; | ||
357 | list_add(&pdeo->lh, &pde->pde_openers); | ||
358 | } else | ||
359 | kfree(pdeo); | ||
360 | __pde_users_dec(pde); | ||
361 | spin_unlock(&pde->pde_unload_lock); | ||
335 | return rv; | 362 | return rv; |
336 | } | 363 | } |
337 | 364 | ||
365 | static struct pde_opener *find_pde_opener(struct proc_dir_entry *pde, | ||
366 | struct inode *inode, struct file *file) | ||
367 | { | ||
368 | struct pde_opener *pdeo; | ||
369 | |||
370 | list_for_each_entry(pdeo, &pde->pde_openers, lh) { | ||
371 | if (pdeo->inode == inode && pdeo->file == file) | ||
372 | return pdeo; | ||
373 | } | ||
374 | return NULL; | ||
375 | } | ||
376 | |||
338 | static int proc_reg_release(struct inode *inode, struct file *file) | 377 | static int proc_reg_release(struct inode *inode, struct file *file) |
339 | { | 378 | { |
340 | struct proc_dir_entry *pde = PDE(inode); | 379 | struct proc_dir_entry *pde = PDE(inode); |
341 | int rv = 0; | 380 | int rv = 0; |
342 | int (*release)(struct inode *, struct file *); | 381 | int (*release)(struct inode *, struct file *); |
382 | struct pde_opener *pdeo; | ||
343 | 383 | ||
344 | spin_lock(&pde->pde_unload_lock); | 384 | spin_lock(&pde->pde_unload_lock); |
385 | pdeo = find_pde_opener(pde, inode, file); | ||
345 | if (!pde->proc_fops) { | 386 | if (!pde->proc_fops) { |
346 | spin_unlock(&pde->pde_unload_lock); | 387 | /* |
388 | * Can't simply exit, __fput() will think that everything is OK, | ||
389 | * and move on to freeing struct file. remove_proc_entry() will | ||
390 | * find slacker in opener's list and will try to do non-trivial | ||
391 | * things with struct file. Therefore, remove opener from list. | ||
392 | * | ||
393 | * But if opener is removed from list, who will ->release it? | ||
394 | */ | ||
395 | if (pdeo) { | ||
396 | list_del(&pdeo->lh); | ||
397 | spin_unlock(&pde->pde_unload_lock); | ||
398 | rv = pdeo->release(inode, file); | ||
399 | kfree(pdeo); | ||
400 | } else | ||
401 | spin_unlock(&pde->pde_unload_lock); | ||
347 | return rv; | 402 | return rv; |
348 | } | 403 | } |
349 | pde->pde_users++; | 404 | pde->pde_users++; |
350 | release = pde->proc_fops->release; | 405 | release = pde->proc_fops->release; |
406 | if (pdeo) { | ||
407 | list_del(&pdeo->lh); | ||
408 | kfree(pdeo); | ||
409 | } | ||
351 | spin_unlock(&pde->pde_unload_lock); | 410 | spin_unlock(&pde->pde_unload_lock); |
352 | 411 | ||
353 | if (release) | 412 | if (release) |