patch-2.1.128 linux/kernel/acct.c

Next file: linux/kernel/capability.c
Previous file: linux/init/main.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.127/linux/kernel/acct.c linux/kernel/acct.c
@@ -23,6 +23,21 @@
  *
  *  Now we silently close acct_file on attempt to reopen. Cleaned sys_acct().
  *  XTerms and EMACS are manifestations of pure evil. 21/10/98, AV.
+ *
+ *  Fixed a nasty interaction with with sys_umount(). If the accointing
+ *  was suspeneded we failed to stop it on umount(). Messy.
+ *  Another one: remount to readonly didn't stop accounting.
+ *	Question: what should we do if we have CAP_SYS_ADMIN but not
+ *  CAP_SYS_PACCT? Current code does the following: umount returns -EBUSY
+ *  unless we are messing with the root. In that case we are getting a
+ *  real mess with do_remount_sb(). 9/11/98, AV.
+ *
+ *  Fixed a bunch of races (and pair of leaks). Probably not the best way,
+ *  but this one obviously doesn't introduce deadlocks. Later. BTW, found
+ *  one race (and leak) in BSD implementation.
+ *  OK, that's better. ANOTHER race and leak in BSD variant. There always
+ *  is one more bug... TODO: move filp_open into open.c and make
+ *  parameters sysctl-controllable. 10/11/98, AV.
  */
 
 #include <linux/config.h>
@@ -30,18 +45,13 @@
 #include <linux/kernel.h>
 
 #ifdef CONFIG_BSD_PROCESS_ACCT
-#include <linux/fs.h>
+#include <linux/sched.h>
 #include <linux/vfs.h>
-#include <linux/types.h>
 #include <linux/fcntl.h>
 #include <linux/stat.h>
-#include <linux/sched.h>
 #include <linux/mm.h>
-#include <linux/timer.h>
-#include <linux/tty.h>
 #include <linux/acct.h>
 #include <linux/major.h>
-#include <linux/smp.h>
 #include <linux/smp_lock.h>
 #include <linux/file.h>
 
@@ -51,11 +61,13 @@
  * These constants control the amount of freespace that suspend and
  * resume the process accounting system, and the time delay between
  * each check.
+ * Turned into sysctl-controllable parameters. AV, 12/11/98
  */
 
-#define RESUME		(4)       /* More than 4% free space will resume */
-#define SUSPEND		(2)       /* Less than 2% free space will suspend */
-#define ACCT_TIMEOUT	(30 * HZ) /* 30 second timeout between checks */
+int acct_parm[3] = {4, 2, 30};
+#define RESUME		(acct_parm[0])	/* >foo% free space - resume */
+#define SUSPEND		(acct_parm[1])	/* <foo% free space - suspend */
+#define ACCT_TIMEOUT	(acct_parm[2])	/* foo second timeout between checks */
 
 /*
  * External references and all of the globals.
@@ -66,6 +78,7 @@
 static volatile int acct_needcheck = 0;
 static struct file *acct_file = NULL;
 static struct timer_list acct_timer = { NULL, NULL, 0, 0, acct_timeout };
+static int do_acct_process(long, struct file *);
 
 /*
  * Called whenever the timer says to check the free space.
@@ -78,39 +91,58 @@
 /*
  * Check the amount of free space and suspend/resume accordingly.
  */
-static void check_free_space(void)
+static int check_free_space(struct file *file)
 {
 	mm_segment_t fs;
 	struct statfs sbuf;
 	struct super_block *sb;
+	int res = acct_active;
+	int act;
 
-	if (!acct_file || !acct_needcheck)
-		return;
+	if (!file || !acct_needcheck)
+		return res;
 
-	sb = acct_file->f_dentry->d_inode->i_sb;
+	sb = file->f_dentry->d_inode->i_sb;
 	if (!sb->s_op || !sb->s_op->statfs)
-		return;
+		return res;
 
 	fs = get_fs();
 	set_fs(KERNEL_DS);
+	/* May block */
 	sb->s_op->statfs(sb, &sbuf, sizeof(struct statfs));
 	set_fs(fs);
 
+	if (sbuf.f_bavail <= SUSPEND * sbuf.f_blocks / 100)
+		act = -1;
+	else if (sbuf.f_bavail >= RESUME * sbuf.f_blocks / 100)
+		act = 1;
+	else
+		act = 0;
+
+	/*
+	 * If some joker switched acct_file under us we'ld better be
+	 * silent and _not_ touch anything.
+	 */
+	if (file != acct_file)
+		return act ? (act>0) : res;
+
 	if (acct_active) {
-		if (sbuf.f_bavail <= SUSPEND * sbuf.f_blocks / 100) {
+		if (act < 0) {
 			acct_active = 0;
 			printk(KERN_INFO "Process accounting paused\r\n");
 		}
 	} else {
-		if (sbuf.f_bavail >= RESUME * sbuf.f_blocks / 100) {
+		if (act > 0) {
 			acct_active = 1;
 			printk(KERN_INFO "Process accounting resumed\r\n");
 		}
 	}
+
 	del_timer(&acct_timer);
 	acct_needcheck = 0;
-	acct_timer.expires = jiffies + ACCT_TIMEOUT;
+	acct_timer.expires = jiffies + ACCT_TIMEOUT*HZ;
 	add_timer(&acct_timer);
+	return acct_active;
 }
 
 /*
@@ -121,9 +153,7 @@
  */
 asmlinkage int sys_acct(const char *name)
 {
-	struct dentry *dentry;
-	struct inode *inode;
-	struct file_operations *ops;
+	struct file *file = NULL, *old_acct = NULL;
 	char *tmp;
 	int error = -EPERM;
 
@@ -131,82 +161,57 @@
 	if (!capable(CAP_SYS_PACCT))
 		goto out;
 
+	if (name) {
+		tmp = getname(name);
+		error = PTR_ERR(tmp);
+		if (IS_ERR(tmp))
+			goto out;
+		/* Difference from BSD - they don't do O_APPEND */
+		file = filp_open(tmp, O_WRONLY|O_APPEND, 0);
+		putname(tmp);
+		if (IS_ERR(file)) {
+			error = PTR_ERR(file);
+			goto out;
+		}
+		error = -EACCES;
+		if (!S_ISREG(file->f_dentry->d_inode->i_mode)) 
+			goto out_err;
+
+		error = -EIO;
+		if (!file->f_op->write) 
+			goto out_err;
+	}
+
+	error = 0;
 	if (acct_file) {
-		/* fput() may block, so just in case... */
-		struct file *tmp = acct_file;
-		if (acct_active)
-			acct_process(0); 
+		old_acct = acct_file;
 		del_timer(&acct_timer);
 		acct_active = 0;
 		acct_needcheck = 0;
 		acct_file = NULL;
-		fput(tmp);
 	}
-	error = 0;
-	if (!name)		/* We are done */
-		goto out;
-
-	tmp = getname(name);
-	error = PTR_ERR(tmp);
-	if (IS_ERR(tmp))
-		goto out;
-
-	dentry = open_namei(tmp, O_RDWR, 0600);
-	putname(tmp);
-
-	error = PTR_ERR(dentry);
-	if (IS_ERR(dentry))
-		goto out;
-
-	inode = dentry->d_inode;
-
-	error = -EACCES;
-	if (!S_ISREG(inode->i_mode)) 
-		goto out_d;
-
-	error = -EIO;
-	if (!inode->i_op || !(ops = inode->i_op->default_file_ops) || 
-	    !ops->write) 
-		goto out_d;
-
-	error = -EUSERS;
-	if (!(acct_file = get_empty_filp()))
-		goto out_d;
-
-	acct_file->f_mode = (O_WRONLY + 1) & O_ACCMODE;
-	acct_file->f_flags = O_WRONLY;
-	acct_file->f_dentry = dentry;
-	acct_file->f_pos = inode->i_size;
-	acct_file->f_reada = 0;
-	acct_file->f_op = ops;
-	error = get_write_access(inode);
-	if (error)
-		goto out_f;
-	if (ops->open)
-		error = ops->open(inode, acct_file);
-	if (error) {
-		put_write_access(inode);
-		goto out_f;
+	if (name) {
+		acct_file = file;
+		acct_needcheck = 0;
+		acct_active = 1;
+		acct_timer.expires = jiffies + ACCT_TIMEOUT*HZ;
+		add_timer(&acct_timer);
+	}
+	if (old_acct) {
+		do_acct_process(0,old_acct);
+		fput(old_acct);
 	}
-	acct_needcheck = 0;
-	acct_active = 1;
-	acct_timer.expires = jiffies + ACCT_TIMEOUT;
-	add_timer(&acct_timer);
-	goto out;
-out_f:
-	/* decrementing f_count is _not_ enough */
-	put_filp(acct_file);
-	acct_file = NULL;
-out_d:
-	dput(dentry);
 out:
 	unlock_kernel();
 	return error;
+out_err:
+	fput(file);
+	goto out;
 }
 
 void acct_auto_close(kdev_t dev)
 {
-	if (acct_active && acct_file && acct_file->f_dentry->d_inode->i_dev == dev)
+	if (acct_file && acct_file->f_dentry->d_inode->i_dev == dev)
 		sys_acct((char *)NULL);
 }
 
@@ -260,7 +265,10 @@
 #define KSTK_EIP(stack) (((unsigned long *)(stack))[1019])
 #define KSTK_ESP(stack) (((unsigned long *)(stack))[1022])
 
-int acct_process(long exitcode)
+/*
+ *  do_acct_process does all actual work.
+ */
+static int do_acct_process(long exitcode, struct file *file)
 {
 	struct acct ac;
 	mm_segment_t fs;
@@ -268,14 +276,15 @@
 
 	/*
 	 * First check to see if there is enough free_space to continue
-	 * the process accounting system. Check_free_space toggles the
-	 * acct_active flag so we need to check that after check_free_space.
+	 * the process accounting system.
 	 */
-	check_free_space();
-
-	if (!acct_active)
+	if (!file)
 		return 0;
-
+	file->f_count++;
+	if (!check_free_space(file)) {
+		fput(file);
+		return 0;
+	}
 
 	/*
 	 * Fill the accounting struct with the needed info as recorded
@@ -327,10 +336,19 @@
          */
 	fs = get_fs();
 	set_fs(KERNEL_DS);
-	acct_file->f_op->write(acct_file, (char *)&ac,
-			       sizeof(struct acct), &acct_file->f_pos);
+	file->f_op->write(file, (char *)&ac,
+			       sizeof(struct acct), &file->f_pos);
 	set_fs(fs);
+	fput(file);
 	return 0;
+}
+
+/*
+ * acct_process - now just a wrapper around do_acct_process
+ */
+int acct_process(long exitcode)
+{
+	return do_acct_process(exitcode, acct_file);
 }
 
 #else

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov