aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2005-11-07 17:13:39 -0500
committerLinus Torvalds <torvalds@g5.osdl.org>2005-11-07 21:18:09 -0500
commit7b7b1ace2d9d06d76bce7481a045c22ed75e35dd (patch)
tree458f9f16b855ed0347013048c13d3a29031f00ee /kernel
parent254ce8dc882f8d69e5d49ed4807c94a61976fb15 (diff)
[PATCH] saner handling of auto_acct_off() and DQUOT_OFF() in umount
The way we currently deal with quota and process accounting that might keep vfsmount busy at umount time is inherently broken; we try to turn them off just in case (not quite correctly, at that) and a) pray umount doesn't fail (otherwise they'll stay turned off) b) pray nobody doesn anything funny just as we turn quota off Moreover, LSM provides hooks for doing the same sort of broken logics. The proper way to deal with that is to introduce the second kind of reference to vfsmount. Semantics: - when the last normal reference is dropped, all special ones are converted to normal ones and if there had been any, cleanup is done. - normal reference can be cloned into a special one - special reference can be converted to normal one; that's a no-op if we'd already passed the point of no return (i.e. mntput() had converted special references to normal and started cleanup). The way it works: e.g. starting process accounting converts the vfsmount reference pinned by the opened file into special one and turns it back to normal when it gets shut down; acct_auto_close() is done when no normal references are left. That way it does *not* obstruct umount(2) and it silently gets turned off when the last normal reference to vfsmount is gone. Which is exactly what we want... The same should be done by LSM module that holds some internal references to vfsmount and wants to shut them down on umount - it should make them special and security_sb_umount_close() will be called exactly when the last normal reference to vfsmount is gone. quota handling is even simpler - we don't use normal file IO anymore, so there's no need to hold vfsmounts at all. DQUOT_OFF() is done from deactivate_super(), where it really belongs. Signed-off-by: Al Viro <viro@zeniv.linux.org.uk> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'kernel')
-rw-r--r--kernel/acct.c92
1 files changed, 62 insertions, 30 deletions
diff --git a/kernel/acct.c b/kernel/acct.c
index 2e3f4a47e7d0..6312d6bd43e3 100644
--- a/kernel/acct.c
+++ b/kernel/acct.c
@@ -54,6 +54,7 @@
54#include <linux/jiffies.h> 54#include <linux/jiffies.h>
55#include <linux/times.h> 55#include <linux/times.h>
56#include <linux/syscalls.h> 56#include <linux/syscalls.h>
57#include <linux/mount.h>
57#include <asm/uaccess.h> 58#include <asm/uaccess.h>
58#include <asm/div64.h> 59#include <asm/div64.h>
59#include <linux/blkdev.h> /* sector_div */ 60#include <linux/blkdev.h> /* sector_div */
@@ -192,6 +193,7 @@ static void acct_file_reopen(struct file *file)
192 add_timer(&acct_globals.timer); 193 add_timer(&acct_globals.timer);
193 } 194 }
194 if (old_acct) { 195 if (old_acct) {
196 mnt_unpin(old_acct->f_vfsmnt);
195 spin_unlock(&acct_globals.lock); 197 spin_unlock(&acct_globals.lock);
196 do_acct_process(0, old_acct); 198 do_acct_process(0, old_acct);
197 filp_close(old_acct, NULL); 199 filp_close(old_acct, NULL);
@@ -199,6 +201,42 @@ static void acct_file_reopen(struct file *file)
199 } 201 }
200} 202}
201 203
204static int acct_on(char *name)
205{
206 struct file *file;
207 int error;
208
209 /* Difference from BSD - they don't do O_APPEND */
210 file = filp_open(name, O_WRONLY|O_APPEND|O_LARGEFILE, 0);
211 if (IS_ERR(file))
212 return PTR_ERR(file);
213
214 if (!S_ISREG(file->f_dentry->d_inode->i_mode)) {
215 filp_close(file, NULL);
216 return -EACCES;
217 }
218
219 if (!file->f_op->write) {
220 filp_close(file, NULL);
221 return -EIO;
222 }
223
224 error = security_acct(file);
225 if (error) {
226 filp_close(file, NULL);
227 return error;
228 }
229
230 spin_lock(&acct_globals.lock);
231 mnt_pin(file->f_vfsmnt);
232 acct_file_reopen(file);
233 spin_unlock(&acct_globals.lock);
234
235 mntput(file->f_vfsmnt); /* it's pinned, now give up active reference */
236
237 return 0;
238}
239
202/** 240/**
203 * sys_acct - enable/disable process accounting 241 * sys_acct - enable/disable process accounting
204 * @name: file name for accounting records or NULL to shutdown accounting 242 * @name: file name for accounting records or NULL to shutdown accounting
@@ -212,47 +250,41 @@ static void acct_file_reopen(struct file *file)
212 */ 250 */
213asmlinkage long sys_acct(const char __user *name) 251asmlinkage long sys_acct(const char __user *name)
214{ 252{
215 struct file *file = NULL;
216 char *tmp;
217 int error; 253 int error;
218 254
219 if (!capable(CAP_SYS_PACCT)) 255 if (!capable(CAP_SYS_PACCT))
220 return -EPERM; 256 return -EPERM;
221 257
222 if (name) { 258 if (name) {
223 tmp = getname(name); 259 char *tmp = getname(name);
224 if (IS_ERR(tmp)) { 260 if (IS_ERR(tmp))
225 return (PTR_ERR(tmp)); 261 return (PTR_ERR(tmp));
226 } 262 error = acct_on(tmp);
227 /* Difference from BSD - they don't do O_APPEND */
228 file = filp_open(tmp, O_WRONLY|O_APPEND|O_LARGEFILE, 0);
229 putname(tmp); 263 putname(tmp);
230 if (IS_ERR(file)) { 264 } else {
231 return (PTR_ERR(file)); 265 error = security_acct(NULL);
232 } 266 if (!error) {
233 if (!S_ISREG(file->f_dentry->d_inode->i_mode)) { 267 spin_lock(&acct_globals.lock);
234 filp_close(file, NULL); 268 acct_file_reopen(NULL);
235 return (-EACCES); 269 spin_unlock(&acct_globals.lock);
236 }
237
238 if (!file->f_op->write) {
239 filp_close(file, NULL);
240 return (-EIO);
241 } 270 }
242 } 271 }
272 return error;
273}
243 274
244 error = security_acct(file); 275/**
245 if (error) { 276 * acct_auto_close - turn off a filesystem's accounting if it is on
246 if (file) 277 * @m: vfsmount being shut down
247 filp_close(file, NULL); 278 *
248 return error; 279 * If the accounting is turned on for a file in the subtree pointed to
249 } 280 * to by m, turn accounting off. Done when m is about to die.
250 281 */
282void acct_auto_close_mnt(struct vfsmount *m)
283{
251 spin_lock(&acct_globals.lock); 284 spin_lock(&acct_globals.lock);
252 acct_file_reopen(file); 285 if (acct_globals.file && acct_globals.file->f_vfsmnt == m)
286 acct_file_reopen(NULL);
253 spin_unlock(&acct_globals.lock); 287 spin_unlock(&acct_globals.lock);
254
255 return (0);
256} 288}
257 289
258/** 290/**
@@ -266,8 +298,8 @@ void acct_auto_close(struct super_block *sb)
266{ 298{
267 spin_lock(&acct_globals.lock); 299 spin_lock(&acct_globals.lock);
268 if (acct_globals.file && 300 if (acct_globals.file &&
269 acct_globals.file->f_dentry->d_inode->i_sb == sb) { 301 acct_globals.file->f_vfsmnt->mnt_sb == sb) {
270 acct_file_reopen((struct file *)NULL); 302 acct_file_reopen(NULL);
271 } 303 }
272 spin_unlock(&acct_globals.lock); 304 spin_unlock(&acct_globals.lock);
273} 305}