diff -Nrub linux-2.6.11.7-orig/Makefile linux-2.6.11.7-snare/Makefile
--- linux-2.6.11.7-orig/Makefile	2005-04-07 14:57:50.000000000 -0400
+++ linux-2.6.11.7-snare/Makefile	2005-04-22 11:22:37.030846744 -0400
@@ -1,7 +1,7 @@
 VERSION = 2
 PATCHLEVEL = 6
 SUBLEVEL = 11
-EXTRAVERSION = .7
+EXTRAVERSION = .7-snare
 NAME=Woozy Numbat
 
 # *DOCUMENTATION*
diff -Nrub linux-2.6.11.7-orig/fs/exec.c linux-2.6.11.7-snare/fs/exec.c
--- linux-2.6.11.7-orig/fs/exec.c	2005-04-07 14:57:31.000000000 -0400
+++ linux-2.6.11.7-snare/fs/exec.c	2005-04-22 11:19:47.188666680 -0400
@@ -37,6 +37,7 @@
 #include <linux/key.h>
 #include <linux/personality.h>
 #include <linux/binfmts.h>
+#include <linux/saudit.h>
 #include <linux/swap.h>
 #include <linux/utsname.h>
 #include <linux/module.h>
@@ -1129,6 +1130,13 @@
 	int retval;
 	int i;
 
+#if defined(CONFIG_C2_AUDIT)
+	char *SNARE_arguments;
+	SNARE_arguments=saudit_copy_argv(argv);
+#else
+	#define SNARE_arguments (char *)NULL
+#endif
+
 	retval = -ENOMEM;
 	bprm = kmalloc(sizeof(*bprm), GFP_KERNEL);
 	if (!bprm)
@@ -1189,6 +1197,7 @@
 	if (retval >= 0) {
 		free_arg_pages(bprm);
 
+		saudit_execve((char *)NULL, filename, SNARE_arguments, retval);
 		/* execve success */
 		security_bprm_free(bprm);
 		acct_update_integrals();
@@ -1222,6 +1231,7 @@
 	kfree(bprm);
 
 out_ret:
+	saudit_execve((char *) NULL, filename, SNARE_arguments, retval);
 	return retval;
 }
 
diff -Nrub linux-2.6.11.7-orig/fs/exec.c.orig linux-2.6.11.7-snare/fs/exec.c.orig
--- linux-2.6.11.7-orig/fs/exec.c.orig	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.11.7-snare/fs/exec.c.orig	2005-04-07 14:57:31.000000000 -0400
@@ -0,0 +1,1496 @@
+/*
+ *  linux/fs/exec.c
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ */
+
+/*
+ * #!-checking implemented by tytso.
+ */
+/*
+ * Demand-loading implemented 01.12.91 - no need to read anything but
+ * the header into memory. The inode of the executable is put into
+ * "current->executable", and page faults do the actual loading. Clean.
+ *
+ * Once more I can proudly say that linux stood up to being changed: it
+ * was less than 2 hours work to get demand-loading completely implemented.
+ *
+ * Demand loading changed July 1993 by Eric Youngdale.   Use mmap instead,
+ * current->executable is only used by the procfs.  This allows a dispatch
+ * table to check for several different types  of binary formats.  We keep
+ * trying until we recognize the file or we run out of supported binary
+ * formats. 
+ */
+
+#include <linux/config.h>
+#include <linux/slab.h>
+#include <linux/file.h>
+#include <linux/mman.h>
+#include <linux/a.out.h>
+#include <linux/stat.h>
+#include <linux/fcntl.h>
+#include <linux/smp_lock.h>
+#include <linux/init.h>
+#include <linux/pagemap.h>
+#include <linux/highmem.h>
+#include <linux/spinlock.h>
+#include <linux/key.h>
+#include <linux/personality.h>
+#include <linux/binfmts.h>
+#include <linux/swap.h>
+#include <linux/utsname.h>
+#include <linux/module.h>
+#include <linux/namei.h>
+#include <linux/proc_fs.h>
+#include <linux/ptrace.h>
+#include <linux/mount.h>
+#include <linux/security.h>
+#include <linux/syscalls.h>
+#include <linux/rmap.h>
+#include <linux/acct.h>
+
+#include <asm/uaccess.h>
+#include <asm/mmu_context.h>
+
+#ifdef CONFIG_KMOD
+#include <linux/kmod.h>
+#endif
+
+int core_uses_pid;
+char core_pattern[65] = "core";
+/* The maximal length of core_pattern is also specified in sysctl.c */
+
+static struct linux_binfmt *formats;
+static DEFINE_RWLOCK(binfmt_lock);
+
+int register_binfmt(struct linux_binfmt * fmt)
+{
+	struct linux_binfmt ** tmp = &formats;
+
+	if (!fmt)
+		return -EINVAL;
+	if (fmt->next)
+		return -EBUSY;
+	write_lock(&binfmt_lock);
+	while (*tmp) {
+		if (fmt == *tmp) {
+			write_unlock(&binfmt_lock);
+			return -EBUSY;
+		}
+		tmp = &(*tmp)->next;
+	}
+	fmt->next = formats;
+	formats = fmt;
+	write_unlock(&binfmt_lock);
+	return 0;	
+}
+
+EXPORT_SYMBOL(register_binfmt);
+
+int unregister_binfmt(struct linux_binfmt * fmt)
+{
+	struct linux_binfmt ** tmp = &formats;
+
+	write_lock(&binfmt_lock);
+	while (*tmp) {
+		if (fmt == *tmp) {
+			*tmp = fmt->next;
+			write_unlock(&binfmt_lock);
+			return 0;
+		}
+		tmp = &(*tmp)->next;
+	}
+	write_unlock(&binfmt_lock);
+	return -EINVAL;
+}
+
+EXPORT_SYMBOL(unregister_binfmt);
+
+static inline void put_binfmt(struct linux_binfmt * fmt)
+{
+	module_put(fmt->module);
+}
+
+/*
+ * Note that a shared library must be both readable and executable due to
+ * security reasons.
+ *
+ * Also note that we take the address to load from from the file itself.
+ */
+asmlinkage long sys_uselib(const char __user * library)
+{
+	struct file * file;
+	struct nameidata nd;
+	int error;
+
+	nd.intent.open.flags = FMODE_READ;
+	error = __user_walk(library, LOOKUP_FOLLOW|LOOKUP_OPEN, &nd);
+	if (error)
+		goto out;
+
+	error = -EINVAL;
+	if (!S_ISREG(nd.dentry->d_inode->i_mode))
+		goto exit;
+
+	error = permission(nd.dentry->d_inode, MAY_READ | MAY_EXEC, &nd);
+	if (error)
+		goto exit;
+
+	file = dentry_open(nd.dentry, nd.mnt, O_RDONLY);
+	error = PTR_ERR(file);
+	if (IS_ERR(file))
+		goto out;
+
+	error = -ENOEXEC;
+	if(file->f_op) {
+		struct linux_binfmt * fmt;
+
+		read_lock(&binfmt_lock);
+		for (fmt = formats ; fmt ; fmt = fmt->next) {
+			if (!fmt->load_shlib)
+				continue;
+			if (!try_module_get(fmt->module))
+				continue;
+			read_unlock(&binfmt_lock);
+			error = fmt->load_shlib(file);
+			read_lock(&binfmt_lock);
+			put_binfmt(fmt);
+			if (error != -ENOEXEC)
+				break;
+		}
+		read_unlock(&binfmt_lock);
+	}
+	fput(file);
+out:
+  	return error;
+exit:
+	path_release(&nd);
+	goto out;
+}
+
+/*
+ * count() counts the number of strings in array ARGV.
+ */
+static int count(char __user * __user * argv, int max)
+{
+	int i = 0;
+
+	if (argv != NULL) {
+		for (;;) {
+			char __user * p;
+
+			if (get_user(p, argv))
+				return -EFAULT;
+			if (!p)
+				break;
+			argv++;
+			if(++i > max)
+				return -E2BIG;
+			cond_resched();
+		}
+	}
+	return i;
+}
+
+/*
+ * 'copy_strings()' copies argument/environment strings from user
+ * memory to free pages in kernel mem. These are in a format ready
+ * to be put directly into the top of new user memory.
+ */
+int copy_strings(int argc,char __user * __user * argv, struct linux_binprm *bprm)
+{
+	struct page *kmapped_page = NULL;
+	char *kaddr = NULL;
+	int ret;
+
+	while (argc-- > 0) {
+		char __user *str;
+		int len;
+		unsigned long pos;
+
+		if (get_user(str, argv+argc) ||
+				!(len = strnlen_user(str, bprm->p))) {
+			ret = -EFAULT;
+			goto out;
+		}
+
+		if (bprm->p < len)  {
+			ret = -E2BIG;
+			goto out;
+		}
+
+		bprm->p -= len;
+		/* XXX: add architecture specific overflow check here. */
+		pos = bprm->p;
+
+		while (len > 0) {
+			int i, new, err;
+			int offset, bytes_to_copy;
+			struct page *page;
+
+			offset = pos % PAGE_SIZE;
+			i = pos/PAGE_SIZE;
+			page = bprm->page[i];
+			new = 0;
+			if (!page) {
+				page = alloc_page(GFP_HIGHUSER);
+				bprm->page[i] = page;
+				if (!page) {
+					ret = -ENOMEM;
+					goto out;
+				}
+				new = 1;
+			}
+
+			if (page != kmapped_page) {
+				if (kmapped_page)
+					kunmap(kmapped_page);
+				kmapped_page = page;
+				kaddr = kmap(kmapped_page);
+			}
+			if (new && offset)
+				memset(kaddr, 0, offset);
+			bytes_to_copy = PAGE_SIZE - offset;
+			if (bytes_to_copy > len) {
+				bytes_to_copy = len;
+				if (new)
+					memset(kaddr+offset+len, 0,
+						PAGE_SIZE-offset-len);
+			}
+			err = copy_from_user(kaddr+offset, str, bytes_to_copy);
+			if (err) {
+				ret = -EFAULT;
+				goto out;
+			}
+
+			pos += bytes_to_copy;
+			str += bytes_to_copy;
+			len -= bytes_to_copy;
+		}
+	}
+	ret = 0;
+out:
+	if (kmapped_page)
+		kunmap(kmapped_page);
+	return ret;
+}
+
+/*
+ * Like copy_strings, but get argv and its values from kernel memory.
+ */
+int copy_strings_kernel(int argc,char ** argv, struct linux_binprm *bprm)
+{
+	int r;
+	mm_segment_t oldfs = get_fs();
+	set_fs(KERNEL_DS);
+	r = copy_strings(argc, (char __user * __user *)argv, bprm);
+	set_fs(oldfs);
+	return r;
+}
+
+EXPORT_SYMBOL(copy_strings_kernel);
+
+#ifdef CONFIG_MMU
+/*
+ * This routine is used to map in a page into an address space: needed by
+ * execve() for the initial stack and environment pages.
+ *
+ * vma->vm_mm->mmap_sem is held for writing.
+ */
+void install_arg_page(struct vm_area_struct *vma,
+			struct page *page, unsigned long address)
+{
+	struct mm_struct *mm = vma->vm_mm;
+	pgd_t * pgd;
+	pud_t * pud;
+	pmd_t * pmd;
+	pte_t * pte;
+
+	if (unlikely(anon_vma_prepare(vma)))
+		goto out_sig;
+
+	flush_dcache_page(page);
+	pgd = pgd_offset(mm, address);
+
+	spin_lock(&mm->page_table_lock);
+	pud = pud_alloc(mm, pgd, address);
+	if (!pud)
+		goto out;
+	pmd = pmd_alloc(mm, pud, address);
+	if (!pmd)
+		goto out;
+	pte = pte_alloc_map(mm, pmd, address);
+	if (!pte)
+		goto out;
+	if (!pte_none(*pte)) {
+		pte_unmap(pte);
+		goto out;
+	}
+	mm->rss++;
+	lru_cache_add_active(page);
+	set_pte(pte, pte_mkdirty(pte_mkwrite(mk_pte(
+					page, vma->vm_page_prot))));
+	page_add_anon_rmap(page, vma, address);
+	pte_unmap(pte);
+	spin_unlock(&mm->page_table_lock);
+
+	/* no need for flush_tlb */
+	return;
+out:
+	spin_unlock(&mm->page_table_lock);
+out_sig:
+	__free_page(page);
+	force_sig(SIGKILL, current);
+}
+
+#define EXTRA_STACK_VM_PAGES	20	/* random */
+
+int setup_arg_pages(struct linux_binprm *bprm,
+		    unsigned long stack_top,
+		    int executable_stack)
+{
+	unsigned long stack_base;
+	struct vm_area_struct *mpnt;
+	struct mm_struct *mm = current->mm;
+	int i, ret;
+	long arg_size;
+
+#ifdef CONFIG_STACK_GROWSUP
+	/* Move the argument and environment strings to the bottom of the
+	 * stack space.
+	 */
+	int offset, j;
+	char *to, *from;
+
+	/* Start by shifting all the pages down */
+	i = 0;
+	for (j = 0; j < MAX_ARG_PAGES; j++) {
+		struct page *page = bprm->page[j];
+		if (!page)
+			continue;
+		bprm->page[i++] = page;
+	}
+
+	/* Now move them within their pages */
+	offset = bprm->p % PAGE_SIZE;
+	to = kmap(bprm->page[0]);
+	for (j = 1; j < i; j++) {
+		memmove(to, to + offset, PAGE_SIZE - offset);
+		from = kmap(bprm->page[j]);
+		memcpy(to + PAGE_SIZE - offset, from, offset);
+		kunmap(bprm->page[j - 1]);
+		to = from;
+	}
+	memmove(to, to + offset, PAGE_SIZE - offset);
+	kunmap(bprm->page[j - 1]);
+
+	/* Limit stack size to 1GB */
+	stack_base = current->signal->rlim[RLIMIT_STACK].rlim_max;
+	if (stack_base > (1 << 30))
+		stack_base = 1 << 30;
+	stack_base = PAGE_ALIGN(stack_top - stack_base);
+
+	/* Adjust bprm->p to point to the end of the strings. */
+	bprm->p = stack_base + PAGE_SIZE * i - offset;
+
+	mm->arg_start = stack_base;
+	arg_size = i << PAGE_SHIFT;
+
+	/* zero pages that were copied above */
+	while (i < MAX_ARG_PAGES)
+		bprm->page[i++] = NULL;
+#else
+	stack_base = stack_top - MAX_ARG_PAGES * PAGE_SIZE;
+	bprm->p += stack_base;
+	mm->arg_start = bprm->p;
+	arg_size = stack_top - (PAGE_MASK & (unsigned long) mm->arg_start);
+#endif
+
+	arg_size += EXTRA_STACK_VM_PAGES * PAGE_SIZE;
+
+	if (bprm->loader)
+		bprm->loader += stack_base;
+	bprm->exec += stack_base;
+
+	mpnt = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
+	if (!mpnt)
+		return -ENOMEM;
+
+	if (security_vm_enough_memory(arg_size >> PAGE_SHIFT)) {
+		kmem_cache_free(vm_area_cachep, mpnt);
+		return -ENOMEM;
+	}
+
+	memset(mpnt, 0, sizeof(*mpnt));
+
+	down_write(&mm->mmap_sem);
+	{
+		mpnt->vm_mm = mm;
+#ifdef CONFIG_STACK_GROWSUP
+		mpnt->vm_start = stack_base;
+		mpnt->vm_end = stack_base + arg_size;
+#else
+		mpnt->vm_end = stack_top;
+		mpnt->vm_start = mpnt->vm_end - arg_size;
+#endif
+		/* Adjust stack execute permissions; explicitly enable
+		 * for EXSTACK_ENABLE_X, disable for EXSTACK_DISABLE_X
+		 * and leave alone (arch default) otherwise. */
+		if (unlikely(executable_stack == EXSTACK_ENABLE_X))
+			mpnt->vm_flags = VM_STACK_FLAGS |  VM_EXEC;
+		else if (executable_stack == EXSTACK_DISABLE_X)
+			mpnt->vm_flags = VM_STACK_FLAGS & ~VM_EXEC;
+		else
+			mpnt->vm_flags = VM_STACK_FLAGS;
+		mpnt->vm_flags |= mm->def_flags;
+		mpnt->vm_page_prot = protection_map[mpnt->vm_flags & 0x7];
+		if ((ret = insert_vm_struct(mm, mpnt))) {
+			up_write(&mm->mmap_sem);
+			kmem_cache_free(vm_area_cachep, mpnt);
+			return ret;
+		}
+		mm->stack_vm = mm->total_vm = vma_pages(mpnt);
+	}
+
+	for (i = 0 ; i < MAX_ARG_PAGES ; i++) {
+		struct page *page = bprm->page[i];
+		if (page) {
+			bprm->page[i] = NULL;
+			install_arg_page(mpnt, page, stack_base);
+		}
+		stack_base += PAGE_SIZE;
+	}
+	up_write(&mm->mmap_sem);
+	
+	return 0;
+}
+
+EXPORT_SYMBOL(setup_arg_pages);
+
+#define free_arg_pages(bprm) do { } while (0)
+
+#else
+
+static inline void free_arg_pages(struct linux_binprm *bprm)
+{
+	int i;
+
+	for (i = 0; i < MAX_ARG_PAGES; i++) {
+		if (bprm->page[i])
+			__free_page(bprm->page[i]);
+		bprm->page[i] = NULL;
+	}
+}
+
+#endif /* CONFIG_MMU */
+
+struct file *open_exec(const char *name)
+{
+	struct nameidata nd;
+	int err;
+	struct file *file;
+
+	nd.intent.open.flags = FMODE_READ;
+	err = path_lookup(name, LOOKUP_FOLLOW|LOOKUP_OPEN, &nd);
+	file = ERR_PTR(err);
+
+	if (!err) {
+		struct inode *inode = nd.dentry->d_inode;
+		file = ERR_PTR(-EACCES);
+		if (!(nd.mnt->mnt_flags & MNT_NOEXEC) &&
+		    S_ISREG(inode->i_mode)) {
+			int err = permission(inode, MAY_EXEC, &nd);
+			if (!err && !(inode->i_mode & 0111))
+				err = -EACCES;
+			file = ERR_PTR(err);
+			if (!err) {
+				file = dentry_open(nd.dentry, nd.mnt, O_RDONLY);
+				if (!IS_ERR(file)) {
+					err = deny_write_access(file);
+					if (err) {
+						fput(file);
+						file = ERR_PTR(err);
+					}
+				}
+out:
+				return file;
+			}
+		}
+		path_release(&nd);
+	}
+	goto out;
+}
+
+EXPORT_SYMBOL(open_exec);
+
+int kernel_read(struct file *file, unsigned long offset,
+	char *addr, unsigned long count)
+{
+	mm_segment_t old_fs;
+	loff_t pos = offset;
+	int result;
+
+	old_fs = get_fs();
+	set_fs(get_ds());
+	/* The cast to a user pointer is valid due to the set_fs() */
+	result = vfs_read(file, (void __user *)addr, count, &pos);
+	set_fs(old_fs);
+	return result;
+}
+
+EXPORT_SYMBOL(kernel_read);
+
+static int exec_mmap(struct mm_struct *mm)
+{
+	struct task_struct *tsk;
+	struct mm_struct * old_mm, *active_mm;
+
+	/* Notify parent that we're no longer interested in the old VM */
+	tsk = current;
+	old_mm = current->mm;
+	mm_release(tsk, old_mm);
+
+	if (old_mm) {
+		/*
+		 * Make sure that if there is a core dump in progress
+		 * for the old mm, we get out and die instead of going
+		 * through with the exec.  We must hold mmap_sem around
+		 * checking core_waiters and changing tsk->mm.  The
+		 * core-inducing thread will increment core_waiters for
+		 * each thread whose ->mm == old_mm.
+		 */
+		down_read(&old_mm->mmap_sem);
+		if (unlikely(old_mm->core_waiters)) {
+			up_read(&old_mm->mmap_sem);
+			return -EINTR;
+		}
+	}
+	task_lock(tsk);
+	active_mm = tsk->active_mm;
+	tsk->mm = mm;
+	tsk->active_mm = mm;
+	activate_mm(active_mm, mm);
+	task_unlock(tsk);
+	arch_pick_mmap_layout(mm);
+	if (old_mm) {
+		up_read(&old_mm->mmap_sem);
+		if (active_mm != old_mm) BUG();
+		mmput(old_mm);
+		return 0;
+	}
+	mmdrop(active_mm);
+	return 0;
+}
+
+/*
+ * This function makes sure the current process has its own signal table,
+ * so that flush_signal_handlers can later reset the handlers without
+ * disturbing other processes.  (Other processes might share the signal
+ * table via the CLONE_SIGHAND option to clone().)
+ */
+static inline int de_thread(struct task_struct *tsk)
+{
+	struct signal_struct *sig = tsk->signal;
+	struct sighand_struct *newsighand, *oldsighand = tsk->sighand;
+	spinlock_t *lock = &oldsighand->siglock;
+	int count;
+
+	/*
+	 * If we don't share sighandlers, then we aren't sharing anything
+	 * and we can just re-use it all.
+	 */
+	if (atomic_read(&oldsighand->count) <= 1) {
+		BUG_ON(atomic_read(&sig->count) != 1);
+		exit_itimers(sig);
+		return 0;
+	}
+
+	newsighand = kmem_cache_alloc(sighand_cachep, GFP_KERNEL);
+	if (!newsighand)
+		return -ENOMEM;
+
+	if (thread_group_empty(current))
+		goto no_thread_group;
+
+	/*
+	 * Kill all other threads in the thread group.
+	 * We must hold tasklist_lock to call zap_other_threads.
+	 */
+	read_lock(&tasklist_lock);
+	spin_lock_irq(lock);
+	if (sig->flags & SIGNAL_GROUP_EXIT) {
+		/*
+		 * Another group action in progress, just
+		 * return so that the signal is processed.
+		 */
+		spin_unlock_irq(lock);
+		read_unlock(&tasklist_lock);
+		kmem_cache_free(sighand_cachep, newsighand);
+		return -EAGAIN;
+	}
+	zap_other_threads(current);
+	read_unlock(&tasklist_lock);
+
+	/*
+	 * Account for the thread group leader hanging around:
+	 */
+	count = 2;
+	if (thread_group_leader(current))
+		count = 1;
+	while (atomic_read(&sig->count) > count) {
+		sig->group_exit_task = current;
+		sig->notify_count = count;
+		__set_current_state(TASK_UNINTERRUPTIBLE);
+		spin_unlock_irq(lock);
+		schedule();
+		spin_lock_irq(lock);
+	}
+	sig->group_exit_task = NULL;
+	sig->notify_count = 0;
+	spin_unlock_irq(lock);
+
+	/*
+	 * At this point all other threads have exited, all we have to
+	 * do is to wait for the thread group leader to become inactive,
+	 * and to assume its PID:
+	 */
+	if (!thread_group_leader(current)) {
+		struct task_struct *leader = current->group_leader, *parent;
+		struct dentry *proc_dentry1, *proc_dentry2;
+		unsigned long exit_state, ptrace;
+
+		/*
+		 * Wait for the thread group leader to be a zombie.
+		 * It should already be zombie at this point, most
+		 * of the time.
+		 */
+		while (leader->exit_state != EXIT_ZOMBIE)
+			yield();
+
+		spin_lock(&leader->proc_lock);
+		spin_lock(&current->proc_lock);
+		proc_dentry1 = proc_pid_unhash(current);
+		proc_dentry2 = proc_pid_unhash(leader);
+		write_lock_irq(&tasklist_lock);
+
+		if (leader->tgid != current->tgid)
+			BUG();
+		if (current->pid == current->tgid)
+			BUG();
+		/*
+		 * An exec() starts a new thread group with the
+		 * TGID of the previous thread group. Rehash the
+		 * two threads with a switched PID, and release
+		 * the former thread group leader:
+		 */
+		ptrace = leader->ptrace;
+		parent = leader->parent;
+		if (unlikely(ptrace) && unlikely(parent == current)) {
+			/*
+			 * Joker was ptracing his own group leader,
+			 * and now he wants to be his own parent!
+			 * We can't have that.
+			 */
+			ptrace = 0;
+		}
+
+		ptrace_unlink(current);
+		ptrace_unlink(leader);
+		remove_parent(current);
+		remove_parent(leader);
+
+		switch_exec_pids(leader, current);
+
+		current->parent = current->real_parent = leader->real_parent;
+		leader->parent = leader->real_parent = child_reaper;
+		current->group_leader = current;
+		leader->group_leader = leader;
+
+		add_parent(current, current->parent);
+		add_parent(leader, leader->parent);
+		if (ptrace) {
+			current->ptrace = ptrace;
+			__ptrace_link(current, parent);
+		}
+
+		list_del(&current->tasks);
+		list_add_tail(&current->tasks, &init_task.tasks);
+		current->exit_signal = SIGCHLD;
+		exit_state = leader->exit_state;
+
+		write_unlock_irq(&tasklist_lock);
+		spin_unlock(&leader->proc_lock);
+		spin_unlock(&current->proc_lock);
+		proc_pid_flush(proc_dentry1);
+		proc_pid_flush(proc_dentry2);
+
+		if (exit_state != EXIT_ZOMBIE)
+			BUG();
+		release_task(leader);
+        }
+
+	/*
+	 * Now there are really no other threads at all,
+	 * so it's safe to stop telling them to kill themselves.
+	 */
+	sig->flags = 0;
+
+no_thread_group:
+	BUG_ON(atomic_read(&sig->count) != 1);
+	exit_itimers(sig);
+
+	if (atomic_read(&oldsighand->count) == 1) {
+		/*
+		 * Now that we nuked the rest of the thread group,
+		 * it turns out we are not sharing sighand any more either.
+		 * So we can just keep it.
+		 */
+		kmem_cache_free(sighand_cachep, newsighand);
+	} else {
+		/*
+		 * Move our state over to newsighand and switch it in.
+		 */
+		spin_lock_init(&newsighand->siglock);
+		atomic_set(&newsighand->count, 1);
+		memcpy(newsighand->action, oldsighand->action,
+		       sizeof(newsighand->action));
+
+		write_lock_irq(&tasklist_lock);
+		spin_lock(&oldsighand->siglock);
+		spin_lock(&newsighand->siglock);
+
+		current->sighand = newsighand;
+		recalc_sigpending();
+
+		spin_unlock(&newsighand->siglock);
+		spin_unlock(&oldsighand->siglock);
+		write_unlock_irq(&tasklist_lock);
+
+		if (atomic_dec_and_test(&oldsighand->count))
+			kmem_cache_free(sighand_cachep, oldsighand);
+	}
+
+	if (!thread_group_empty(current))
+		BUG();
+	if (!thread_group_leader(current))
+		BUG();
+	return 0;
+}
+	
+/*
+ * These functions flushes out all traces of the currently running executable
+ * so that a new one can be started
+ */
+
+static inline void flush_old_files(struct files_struct * files)
+{
+	long j = -1;
+
+	spin_lock(&files->file_lock);
+	for (;;) {
+		unsigned long set, i;
+
+		j++;
+		i = j * __NFDBITS;
+		if (i >= files->max_fds || i >= files->max_fdset)
+			break;
+		set = files->close_on_exec->fds_bits[j];
+		if (!set)
+			continue;
+		files->close_on_exec->fds_bits[j] = 0;
+		spin_unlock(&files->file_lock);
+		for ( ; set ; i++,set >>= 1) {
+			if (set & 1) {
+				sys_close(i);
+			}
+		}
+		spin_lock(&files->file_lock);
+
+	}
+	spin_unlock(&files->file_lock);
+}
+
+void get_task_comm(char *buf, struct task_struct *tsk)
+{
+	/* buf must be at least sizeof(tsk->comm) in size */
+	task_lock(tsk);
+	strncpy(buf, tsk->comm, sizeof(tsk->comm));
+	task_unlock(tsk);
+}
+
+void set_task_comm(struct task_struct *tsk, char *buf)
+{
+	task_lock(tsk);
+	strlcpy(tsk->comm, buf, sizeof(tsk->comm));
+	task_unlock(tsk);
+}
+
+int flush_old_exec(struct linux_binprm * bprm)
+{
+	char * name;
+	int i, ch, retval;
+	struct files_struct *files;
+	char tcomm[sizeof(current->comm)];
+
+	/*
+	 * Make sure we have a private signal table and that
+	 * we are unassociated from the previous thread group.
+	 */
+	retval = de_thread(current);
+	if (retval)
+		goto out;
+
+	/*
+	 * Make sure we have private file handles. Ask the
+	 * fork helper to do the work for us and the exit
+	 * helper to do the cleanup of the old one.
+	 */
+	files = current->files;		/* refcounted so safe to hold */
+	retval = unshare_files();
+	if (retval)
+		goto out;
+	/*
+	 * Release all of the old mmap stuff
+	 */
+	retval = exec_mmap(bprm->mm);
+	if (retval)
+		goto mmap_failed;
+
+	bprm->mm = NULL;		/* We're using it now */
+
+	/* This is the point of no return */
+	steal_locks(files);
+	put_files_struct(files);
+
+	current->sas_ss_sp = current->sas_ss_size = 0;
+
+	if (current->euid == current->uid && current->egid == current->gid)
+		current->mm->dumpable = 1;
+	name = bprm->filename;
+	for (i=0; (ch = *(name++)) != '\0';) {
+		if (ch == '/')
+			i = 0;
+		else
+			if (i < (sizeof(tcomm) - 1))
+				tcomm[i++] = ch;
+	}
+	tcomm[i] = '\0';
+	set_task_comm(current, tcomm);
+
+	flush_thread();
+
+	if (bprm->e_uid != current->euid || bprm->e_gid != current->egid || 
+	    permission(bprm->file->f_dentry->d_inode,MAY_READ, NULL) ||
+	    (bprm->interp_flags & BINPRM_FLAGS_ENFORCE_NONDUMP)) {
+		suid_keys(current);
+		current->mm->dumpable = 0;
+	}
+
+	/* An exec changes our domain. We are no longer part of the thread
+	   group */
+
+	current->self_exec_id++;
+			
+	flush_signal_handlers(current, 0);
+	flush_old_files(current->files);
+
+	return 0;
+
+mmap_failed:
+	put_files_struct(current->files);
+	current->files = files;
+out:
+	return retval;
+}
+
+EXPORT_SYMBOL(flush_old_exec);
+
+/* 
+ * Fill the binprm structure from the inode. 
+ * Check permissions, then read the first 128 (BINPRM_BUF_SIZE) bytes
+ */
+int prepare_binprm(struct linux_binprm *bprm)
+{
+	int mode;
+	struct inode * inode = bprm->file->f_dentry->d_inode;
+	int retval;
+
+	mode = inode->i_mode;
+	/*
+	 * Check execute perms again - if the caller has CAP_DAC_OVERRIDE,
+	 * generic_permission lets a non-executable through
+	 */
+	if (!(mode & 0111))	/* with at least _one_ execute bit set */
+		return -EACCES;
+	if (bprm->file->f_op == NULL)
+		return -EACCES;
+
+	bprm->e_uid = current->euid;
+	bprm->e_gid = current->egid;
+
+	if(!(bprm->file->f_vfsmnt->mnt_flags & MNT_NOSUID)) {
+		/* Set-uid? */
+		if (mode & S_ISUID) {
+			current->personality &= ~PER_CLEAR_ON_SETID;
+			bprm->e_uid = inode->i_uid;
+		}
+
+		/* Set-gid? */
+		/*
+		 * If setgid is set but no group execute bit then this
+		 * is a candidate for mandatory locking, not a setgid
+		 * executable.
+		 */
+		if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) {
+			current->personality &= ~PER_CLEAR_ON_SETID;
+			bprm->e_gid = inode->i_gid;
+		}
+	}
+
+	/* fill in binprm security blob */
+	retval = security_bprm_set(bprm);
+	if (retval)
+		return retval;
+
+	memset(bprm->buf,0,BINPRM_BUF_SIZE);
+	return kernel_read(bprm->file,0,bprm->buf,BINPRM_BUF_SIZE);
+}
+
+EXPORT_SYMBOL(prepare_binprm);
+
+static inline int unsafe_exec(struct task_struct *p)
+{
+	int unsafe = 0;
+	if (p->ptrace & PT_PTRACED) {
+		if (p->ptrace & PT_PTRACE_CAP)
+			unsafe |= LSM_UNSAFE_PTRACE_CAP;
+		else
+			unsafe |= LSM_UNSAFE_PTRACE;
+	}
+	if (atomic_read(&p->fs->count) > 1 ||
+	    atomic_read(&p->files->count) > 1 ||
+	    atomic_read(&p->sighand->count) > 1)
+		unsafe |= LSM_UNSAFE_SHARE;
+
+	return unsafe;
+}
+
+void compute_creds(struct linux_binprm *bprm)
+{
+	int unsafe;
+
+	if (bprm->e_uid != current->uid)
+		suid_keys(current);
+	exec_keys(current);
+
+	task_lock(current);
+	unsafe = unsafe_exec(current);
+	security_bprm_apply_creds(bprm, unsafe);
+	task_unlock(current);
+	security_bprm_post_apply_creds(bprm);
+}
+
+EXPORT_SYMBOL(compute_creds);
+
+void remove_arg_zero(struct linux_binprm *bprm)
+{
+	if (bprm->argc) {
+		unsigned long offset;
+		char * kaddr;
+		struct page *page;
+
+		offset = bprm->p % PAGE_SIZE;
+		goto inside;
+
+		while (bprm->p++, *(kaddr+offset++)) {
+			if (offset != PAGE_SIZE)
+				continue;
+			offset = 0;
+			kunmap_atomic(kaddr, KM_USER0);
+inside:
+			page = bprm->page[bprm->p/PAGE_SIZE];
+			kaddr = kmap_atomic(page, KM_USER0);
+		}
+		kunmap_atomic(kaddr, KM_USER0);
+		bprm->argc--;
+	}
+}
+
+EXPORT_SYMBOL(remove_arg_zero);
+
+/*
+ * cycle the list of binary formats handler, until one recognizes the image
+ */
+int search_binary_handler(struct linux_binprm *bprm,struct pt_regs *regs)
+{
+	int try,retval;
+	struct linux_binfmt *fmt;
+#ifdef __alpha__
+	/* handle /sbin/loader.. */
+	{
+	    struct exec * eh = (struct exec *) bprm->buf;
+
+	    if (!bprm->loader && eh->fh.f_magic == 0x183 &&
+		(eh->fh.f_flags & 0x3000) == 0x3000)
+	    {
+		struct file * file;
+		unsigned long loader;
+
+		allow_write_access(bprm->file);
+		fput(bprm->file);
+		bprm->file = NULL;
+
+	        loader = PAGE_SIZE*MAX_ARG_PAGES-sizeof(void *);
+
+		file = open_exec("/sbin/loader");
+		retval = PTR_ERR(file);
+		if (IS_ERR(file))
+			return retval;
+
+		/* Remember if the application is TASO.  */
+		bprm->sh_bang = eh->ah.entry < 0x100000000UL;
+
+		bprm->file = file;
+		bprm->loader = loader;
+		retval = prepare_binprm(bprm);
+		if (retval<0)
+			return retval;
+		/* should call search_binary_handler recursively here,
+		   but it does not matter */
+	    }
+	}
+#endif
+	retval = security_bprm_check(bprm);
+	if (retval)
+		return retval;
+
+	/* kernel module loader fixup */
+	/* so we don't try to load run modprobe in kernel space. */
+	set_fs(USER_DS);
+	retval = -ENOENT;
+	for (try=0; try<2; try++) {
+		read_lock(&binfmt_lock);
+		for (fmt = formats ; fmt ; fmt = fmt->next) {
+			int (*fn)(struct linux_binprm *, struct pt_regs *) = fmt->load_binary;
+			if (!fn)
+				continue;
+			if (!try_module_get(fmt->module))
+				continue;
+			read_unlock(&binfmt_lock);
+			retval = fn(bprm, regs);
+			if (retval >= 0) {
+				put_binfmt(fmt);
+				allow_write_access(bprm->file);
+				if (bprm->file)
+					fput(bprm->file);
+				bprm->file = NULL;
+				current->did_exec = 1;
+				return retval;
+			}
+			read_lock(&binfmt_lock);
+			put_binfmt(fmt);
+			if (retval != -ENOEXEC || bprm->mm == NULL)
+				break;
+			if (!bprm->file) {
+				read_unlock(&binfmt_lock);
+				return retval;
+			}
+		}
+		read_unlock(&binfmt_lock);
+		if (retval != -ENOEXEC || bprm->mm == NULL) {
+			break;
+#ifdef CONFIG_KMOD
+		}else{
+#define printable(c) (((c)=='\t') || ((c)=='\n') || (0x20<=(c) && (c)<=0x7e))
+			if (printable(bprm->buf[0]) &&
+			    printable(bprm->buf[1]) &&
+			    printable(bprm->buf[2]) &&
+			    printable(bprm->buf[3]))
+				break; /* -ENOEXEC */
+			request_module("binfmt-%04x", *(unsigned short *)(&bprm->buf[2]));
+#endif
+		}
+	}
+	return retval;
+}
+
+EXPORT_SYMBOL(search_binary_handler);
+
+/*
+ * sys_execve() executes a new program.
+ */
+int do_execve(char * filename,
+	char __user *__user *argv,
+	char __user *__user *envp,
+	struct pt_regs * regs)
+{
+	struct linux_binprm *bprm;
+	struct file *file;
+	int retval;
+	int i;
+
+	retval = -ENOMEM;
+	bprm = kmalloc(sizeof(*bprm), GFP_KERNEL);
+	if (!bprm)
+		goto out_ret;
+	memset(bprm, 0, sizeof(*bprm));
+
+	file = open_exec(filename);
+	retval = PTR_ERR(file);
+	if (IS_ERR(file))
+		goto out_kfree;
+
+	sched_exec();
+
+	bprm->p = PAGE_SIZE*MAX_ARG_PAGES-sizeof(void *);
+
+	bprm->file = file;
+	bprm->filename = filename;
+	bprm->interp = filename;
+	bprm->mm = mm_alloc();
+	retval = -ENOMEM;
+	if (!bprm->mm)
+		goto out_file;
+
+	retval = init_new_context(current, bprm->mm);
+	if (retval < 0)
+		goto out_mm;
+
+	bprm->argc = count(argv, bprm->p / sizeof(void *));
+	if ((retval = bprm->argc) < 0)
+		goto out_mm;
+
+	bprm->envc = count(envp, bprm->p / sizeof(void *));
+	if ((retval = bprm->envc) < 0)
+		goto out_mm;
+
+	retval = security_bprm_alloc(bprm);
+	if (retval)
+		goto out;
+
+	retval = prepare_binprm(bprm);
+	if (retval < 0)
+		goto out;
+
+	retval = copy_strings_kernel(1, &bprm->filename, bprm);
+	if (retval < 0)
+		goto out;
+
+	bprm->exec = bprm->p;
+	retval = copy_strings(bprm->envc, envp, bprm);
+	if (retval < 0)
+		goto out;
+
+	retval = copy_strings(bprm->argc, argv, bprm);
+	if (retval < 0)
+		goto out;
+
+	retval = search_binary_handler(bprm,regs);
+	if (retval >= 0) {
+		free_arg_pages(bprm);
+
+		/* execve success */
+		security_bprm_free(bprm);
+		acct_update_integrals();
+		update_mem_hiwater();
+		kfree(bprm);
+		return retval;
+	}
+
+out:
+	/* Something went wrong, return the inode and free the argument pages*/
+	for (i = 0 ; i < MAX_ARG_PAGES ; i++) {
+		struct page * page = bprm->page[i];
+		if (page)
+			__free_page(page);
+	}
+
+	if (bprm->security)
+		security_bprm_free(bprm);
+
+out_mm:
+	if (bprm->mm)
+		mmdrop(bprm->mm);
+
+out_file:
+	if (bprm->file) {
+		allow_write_access(bprm->file);
+		fput(bprm->file);
+	}
+
+out_kfree:
+	kfree(bprm);
+
+out_ret:
+	return retval;
+}
+
+int set_binfmt(struct linux_binfmt *new)
+{
+	struct linux_binfmt *old = current->binfmt;
+
+	if (new) {
+		if (!try_module_get(new->module))
+			return -1;
+	}
+	current->binfmt = new;
+	if (old)
+		module_put(old->module);
+	return 0;
+}
+
+EXPORT_SYMBOL(set_binfmt);
+
+#define CORENAME_MAX_SIZE 64
+
+/* format_corename will inspect the pattern parameter, and output a
+ * name into corename, which must have space for at least
+ * CORENAME_MAX_SIZE bytes plus one byte for the zero terminator.
+ */
+static void format_corename(char *corename, const char *pattern, long signr)
+{
+	const char *pat_ptr = pattern;
+	char *out_ptr = corename;
+	char *const out_end = corename + CORENAME_MAX_SIZE;
+	int rc;
+	int pid_in_pattern = 0;
+
+	/* Repeat as long as we have more pattern to process and more output
+	   space */
+	while (*pat_ptr) {
+		if (*pat_ptr != '%') {
+			if (out_ptr == out_end)
+				goto out;
+			*out_ptr++ = *pat_ptr++;
+		} else {
+			switch (*++pat_ptr) {
+			case 0:
+				goto out;
+			/* Double percent, output one percent */
+			case '%':
+				if (out_ptr == out_end)
+					goto out;
+				*out_ptr++ = '%';
+				break;
+			/* pid */
+			case 'p':
+				pid_in_pattern = 1;
+				rc = snprintf(out_ptr, out_end - out_ptr,
+					      "%d", current->tgid);
+				if (rc > out_end - out_ptr)
+					goto out;
+				out_ptr += rc;
+				break;
+			/* uid */
+			case 'u':
+				rc = snprintf(out_ptr, out_end - out_ptr,
+					      "%d", current->uid);
+				if (rc > out_end - out_ptr)
+					goto out;
+				out_ptr += rc;
+				break;
+			/* gid */
+			case 'g':
+				rc = snprintf(out_ptr, out_end - out_ptr,
+					      "%d", current->gid);
+				if (rc > out_end - out_ptr)
+					goto out;
+				out_ptr += rc;
+				break;
+			/* signal that caused the coredump */
+			case 's':
+				rc = snprintf(out_ptr, out_end - out_ptr,
+					      "%ld", signr);
+				if (rc > out_end - out_ptr)
+					goto out;
+				out_ptr += rc;
+				break;
+			/* UNIX time of coredump */
+			case 't': {
+				struct timeval tv;
+				do_gettimeofday(&tv);
+				rc = snprintf(out_ptr, out_end - out_ptr,
+					      "%lu", tv.tv_sec);
+				if (rc > out_end - out_ptr)
+					goto out;
+				out_ptr += rc;
+				break;
+			}
+			/* hostname */
+			case 'h':
+				down_read(&uts_sem);
+				rc = snprintf(out_ptr, out_end - out_ptr,
+					      "%s", system_utsname.nodename);
+				up_read(&uts_sem);
+				if (rc > out_end - out_ptr)
+					goto out;
+				out_ptr += rc;
+				break;
+			/* executable */
+			case 'e':
+				rc = snprintf(out_ptr, out_end - out_ptr,
+					      "%s", current->comm);
+				if (rc > out_end - out_ptr)
+					goto out;
+				out_ptr += rc;
+				break;
+			default:
+				break;
+			}
+			++pat_ptr;
+		}
+	}
+	/* Backward compatibility with core_uses_pid:
+	 *
+	 * If core_pattern does not include a %p (as is the default)
+	 * and core_uses_pid is set, then .%pid will be appended to
+	 * the filename */
+	if (!pid_in_pattern
+            && (core_uses_pid || atomic_read(&current->mm->mm_users) != 1)) {
+		rc = snprintf(out_ptr, out_end - out_ptr,
+			      ".%d", current->tgid);
+		if (rc > out_end - out_ptr)
+			goto out;
+		out_ptr += rc;
+	}
+      out:
+	*out_ptr = 0;
+}
+
+static void zap_threads (struct mm_struct *mm)
+{
+	struct task_struct *g, *p;
+	struct task_struct *tsk = current;
+	struct completion *vfork_done = tsk->vfork_done;
+	int traced = 0;
+
+	/*
+	 * Make sure nobody is waiting for us to release the VM,
+	 * otherwise we can deadlock when we wait on each other
+	 */
+	if (vfork_done) {
+		tsk->vfork_done = NULL;
+		complete(vfork_done);
+	}
+
+	read_lock(&tasklist_lock);
+	do_each_thread(g,p)
+		if (mm == p->mm && p != tsk) {
+			force_sig_specific(SIGKILL, p);
+			mm->core_waiters++;
+			if (unlikely(p->ptrace) &&
+			    unlikely(p->parent->mm == mm))
+				traced = 1;
+		}
+	while_each_thread(g,p);
+
+	read_unlock(&tasklist_lock);
+
+	if (unlikely(traced)) {
+		/*
+		 * We are zapping a thread and the thread it ptraces.
+		 * If the tracee went into a ptrace stop for exit tracing,
+		 * we could deadlock since the tracer is waiting for this
+		 * coredump to finish.  Detach them so they can both die.
+		 */
+		write_lock_irq(&tasklist_lock);
+		do_each_thread(g,p) {
+			if (mm == p->mm && p != tsk &&
+			    p->ptrace && p->parent->mm == mm) {
+				__ptrace_unlink(p);
+			}
+		} while_each_thread(g,p);
+		write_unlock_irq(&tasklist_lock);
+	}
+}
+
+static void coredump_wait(struct mm_struct *mm)
+{
+	DECLARE_COMPLETION(startup_done);
+
+	mm->core_waiters++; /* let other threads block */
+	mm->core_startup_done = &startup_done;
+
+	/* give other threads a chance to run: */
+	yield();
+
+	zap_threads(mm);
+	if (--mm->core_waiters) {
+		up_write(&mm->mmap_sem);
+		wait_for_completion(&startup_done);
+	} else
+		up_write(&mm->mmap_sem);
+	BUG_ON(mm->core_waiters);
+}
+
+int do_coredump(long signr, int exit_code, struct pt_regs * regs)
+{
+	char corename[CORENAME_MAX_SIZE + 1];
+	struct mm_struct *mm = current->mm;
+	struct linux_binfmt * binfmt;
+	struct inode * inode;
+	struct file * file;
+	int retval = 0;
+
+	binfmt = current->binfmt;
+	if (!binfmt || !binfmt->core_dump)
+		goto fail;
+	down_write(&mm->mmap_sem);
+	if (!mm->dumpable) {
+		up_write(&mm->mmap_sem);
+		goto fail;
+	}
+	mm->dumpable = 0;
+	init_completion(&mm->core_done);
+	spin_lock_irq(&current->sighand->siglock);
+	current->signal->flags = SIGNAL_GROUP_EXIT;
+	current->signal->group_exit_code = exit_code;
+	spin_unlock_irq(&current->sighand->siglock);
+	coredump_wait(mm);
+
+	/*
+	 * Clear any false indication of pending signals that might
+	 * be seen by the filesystem code called to write the core file.
+	 */
+	current->signal->group_stop_count = 0;
+	clear_thread_flag(TIF_SIGPENDING);
+
+	if (current->signal->rlim[RLIMIT_CORE].rlim_cur < binfmt->min_coredump)
+		goto fail_unlock;
+
+	/*
+	 * lock_kernel() because format_corename() is controlled by sysctl, which
+	 * uses lock_kernel()
+	 */
+ 	lock_kernel();
+	format_corename(corename, core_pattern, signr);
+	unlock_kernel();
+	file = filp_open(corename, O_CREAT | 2 | O_NOFOLLOW | O_LARGEFILE, 0600);
+	if (IS_ERR(file))
+		goto fail_unlock;
+	inode = file->f_dentry->d_inode;
+	if (inode->i_nlink > 1)
+		goto close_fail;	/* multiple links - don't dump */
+	if (d_unhashed(file->f_dentry))
+		goto close_fail;
+
+	if (!S_ISREG(inode->i_mode))
+		goto close_fail;
+	if (!file->f_op)
+		goto close_fail;
+	if (!file->f_op->write)
+		goto close_fail;
+	if (do_truncate(file->f_dentry, 0) != 0)
+		goto close_fail;
+
+	retval = binfmt->core_dump(signr, regs, file);
+
+	if (retval)
+		current->signal->group_exit_code |= 0x80;
+close_fail:
+	filp_close(file, NULL);
+fail_unlock:
+	complete_all(&mm->core_done);
+fail:
+	return retval;
+}
diff -Nrub linux-2.6.11.7-orig/fs/namei.c linux-2.6.11.7-snare/fs/namei.c
--- linux-2.6.11.7-orig/fs/namei.c	2005-04-07 14:57:30.000000000 -0400
+++ linux-2.6.11.7-snare/fs/namei.c	2005-04-22 11:19:47.191666224 -0400
@@ -24,6 +24,7 @@
 #include <linux/dnotify.h>
 #include <linux/smp_lock.h>
 #include <linux/personality.h>
+#include <linux/saudit.h>
 #include <linux/security.h>
 #include <linux/syscalls.h>
 #include <linux/mount.h>
@@ -1570,11 +1571,15 @@
 	struct dentry * dentry;
 	struct nameidata nd;
 
-	if (S_ISDIR(mode))
+	if (S_ISDIR(mode)) {
+		saudit_mknod((char *)NULL,filename,mode,dev,-EPERM);
 		return -EPERM;
+	}
 	tmp = getname(filename);
-	if (IS_ERR(tmp))
+	if (IS_ERR(tmp)) {
+		saudit_mknod((char *)NULL,(char *)NULL,mode,dev,PTR_ERR(tmp));
 		return PTR_ERR(tmp);
+	}
 
 	error = path_lookup(tmp, LOOKUP_PARENT, &nd);
 	if (error)
@@ -1607,6 +1612,7 @@
 	up(&nd.dentry->d_inode->i_sem);
 	path_release(&nd);
 out:
+	saudit_mknod(tmp,(char *)NULL,mode,dev,error);
 	putname(tmp);
 
 	return error;
@@ -1643,7 +1649,9 @@
 
 	tmp = getname(pathname);
 	error = PTR_ERR(tmp);
-	if (!IS_ERR(tmp)) {
+	if (IS_ERR(tmp)) {
+		saudit_mkdir((char *)NULL,(char *)NULL,mode & ~current->fs->umask,error);
+	} else {
 		struct dentry *dentry;
 		struct nameidata nd;
 
@@ -1661,6 +1669,7 @@
 		up(&nd.dentry->d_inode->i_sem);
 		path_release(&nd);
 out:
+		saudit_mkdir(tmp,(char *)NULL,mode & ~current->fs->umask,error);
 		putname(tmp);
 	}
 
@@ -1741,8 +1750,10 @@
 	struct nameidata nd;
 
 	name = getname(pathname);
-	if(IS_ERR(name))
+	if(IS_ERR(name)) {
+		saudit_rmdir((char *)NULL,(char *)NULL,PTR_ERR(name));
 		return PTR_ERR(name);
+	}
 
 	error = path_lookup(name, LOOKUP_PARENT, &nd);
 	if (error)
@@ -1770,6 +1781,7 @@
 exit1:
 	path_release(&nd);
 exit:
+	saudit_rmdir(name,(char *)NULL,error);
 	putname(name);
 	return error;
 }
@@ -1819,8 +1831,10 @@
 	struct inode *inode = NULL;
 
 	name = getname(pathname);
-	if(IS_ERR(name))
+	if(IS_ERR(name)) {
+		saudit_unlink((char *)NULL,pathname,PTR_ERR(name));
 		return PTR_ERR(name);
+	}
 
 	error = path_lookup(name, LOOKUP_PARENT, &nd);
 	if (error)
@@ -1848,6 +1862,7 @@
 exit1:
 	path_release(&nd);
 exit:
+	saudit_unlink(name,(char *)NULL,error);
 	putname(name);
 	return error;
 
@@ -1887,11 +1902,15 @@
 	char * to;
 
 	from = getname(oldname);
-	if(IS_ERR(from))
+	if(IS_ERR(from)) {
+		saudit_symlink((char *)NULL,(char *)NULL,(char *)NULL,newname,PTR_ERR(from));
 		return PTR_ERR(from);
+	}
 	to = getname(newname);
 	error = PTR_ERR(to);
-	if (!IS_ERR(to)) {
+	if(IS_ERR(to)) {
+		saudit_symlink(from,(char *)NULL,(char *)NULL,(char *)NULL,PTR_ERR(to));
+	} else {
 		struct dentry *dentry;
 		struct nameidata nd;
 
@@ -1907,6 +1926,7 @@
 		up(&nd.dentry->d_inode->i_sem);
 		path_release(&nd);
 out:
+		saudit_symlink(from,(char *)NULL,to,(char *)NULL,error);
 		putname(to);
 	}
 	putname(from);
@@ -1970,8 +1990,10 @@
 	char * to;
 
 	to = getname(newname);
-	if (IS_ERR(to))
+	if (IS_ERR(to)) {
+		saudit_link((char *)NULL,oldname,(char *)NULL,(char *)NULL,PTR_ERR(to));
 		return PTR_ERR(to);
+	}
 
 	error = __user_walk(oldname, 0, &old_nd);
 	if (error)
@@ -1994,6 +2016,7 @@
 out:
 	path_release(&old_nd);
 exit:
+	saudit_link((char *)NULL,oldname,to,(char *)NULL,error);
 	putname(to);
 
 	return error;
@@ -2230,13 +2253,18 @@
 	char * to;
 
 	from = getname(oldname);
-	if(IS_ERR(from))
+	if(IS_ERR(from)) {
+		saudit_rename((char *)NULL,(char *)NULL,(char *)NULL,newname,PTR_ERR(from));
 		return PTR_ERR(from);
+	}
 	to = getname(newname);
 	error = PTR_ERR(to);
 	if (!IS_ERR(to)) {
 		error = do_rename(from,to);
+		saudit_rename(from,(char *)NULL,to,(char *)NULL,error);
 		putname(to);
+	} else {
+		saudit_rename(from,(char *)NULL,(char *)NULL,(char *)NULL,error);
 	}
 	putname(from);
 	return error;
diff -Nrub linux-2.6.11.7-orig/fs/namei.c.orig linux-2.6.11.7-snare/fs/namei.c.orig
--- linux-2.6.11.7-orig/fs/namei.c.orig	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.11.7-snare/fs/namei.c.orig	2005-04-07 14:57:30.000000000 -0400
@@ -0,0 +1,2418 @@
+/*
+ *  linux/fs/namei.c
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ */
+
+/*
+ * Some corrections by tytso.
+ */
+
+/* [Feb 1997 T. Schoebel-Theuer] Complete rewrite of the pathname
+ * lookup logic.
+ */
+/* [Feb-Apr 2000, AV] Rewrite to the new namespace architecture.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/namei.h>
+#include <linux/quotaops.h>
+#include <linux/pagemap.h>
+#include <linux/dnotify.h>
+#include <linux/smp_lock.h>
+#include <linux/personality.h>
+#include <linux/security.h>
+#include <linux/syscalls.h>
+#include <linux/mount.h>
+#include <linux/audit.h>
+#include <asm/namei.h>
+#include <asm/uaccess.h>
+
+#define ACC_MODE(x) ("\000\004\002\006"[(x)&O_ACCMODE])
+
+/* [Feb-1997 T. Schoebel-Theuer]
+ * Fundamental changes in the pathname lookup mechanisms (namei)
+ * were necessary because of omirr.  The reason is that omirr needs
+ * to know the _real_ pathname, not the user-supplied one, in case
+ * of symlinks (and also when transname replacements occur).
+ *
+ * The new code replaces the old recursive symlink resolution with
+ * an iterative one (in case of non-nested symlink chains).  It does
+ * this with calls to <fs>_follow_link().
+ * As a side effect, dir_namei(), _namei() and follow_link() are now 
+ * replaced with a single function lookup_dentry() that can handle all 
+ * the special cases of the former code.
+ *
+ * With the new dcache, the pathname is stored at each inode, at least as
+ * long as the refcount of the inode is positive.  As a side effect, the
+ * size of the dcache depends on the inode cache and thus is dynamic.
+ *
+ * [29-Apr-1998 C. Scott Ananian] Updated above description of symlink
+ * resolution to correspond with current state of the code.
+ *
+ * Note that the symlink resolution is not *completely* iterative.
+ * There is still a significant amount of tail- and mid- recursion in
+ * the algorithm.  Also, note that <fs>_readlink() is not used in
+ * lookup_dentry(): lookup_dentry() on the result of <fs>_readlink()
+ * may return different results than <fs>_follow_link().  Many virtual
+ * filesystems (including /proc) exhibit this behavior.
+ */
+
+/* [24-Feb-97 T. Schoebel-Theuer] Side effects caused by new implementation:
+ * New symlink semantics: when open() is called with flags O_CREAT | O_EXCL
+ * and the name already exists in form of a symlink, try to create the new
+ * name indicated by the symlink. The old code always complained that the
+ * name already exists, due to not following the symlink even if its target
+ * is nonexistent.  The new semantics affects also mknod() and link() when
+ * the name is a symlink pointing to a non-existant name.
+ *
+ * I don't know which semantics is the right one, since I have no access
+ * to standards. But I found by trial that HP-UX 9.0 has the full "new"
+ * semantics implemented, while SunOS 4.1.1 and Solaris (SunOS 5.4) have the
+ * "old" one. Personally, I think the new semantics is much more logical.
+ * Note that "ln old new" where "new" is a symlink pointing to a non-existing
+ * file does succeed in both HP-UX and SunOs, but not in Solaris
+ * and in the old Linux semantics.
+ */
+
+/* [16-Dec-97 Kevin Buhr] For security reasons, we change some symlink
+ * semantics.  See the comments in "open_namei" and "do_link" below.
+ *
+ * [10-Sep-98 Alan Modra] Another symlink change.
+ */
+
+/* [Feb-Apr 2000 AV] Complete rewrite. Rules for symlinks:
+ *	inside the path - always follow.
+ *	in the last component in creation/removal/renaming - never follow.
+ *	if LOOKUP_FOLLOW passed - follow.
+ *	if the pathname has trailing slashes - follow.
+ *	otherwise - don't follow.
+ * (applied in that order).
+ *
+ * [Jun 2000 AV] Inconsistent behaviour of open() in case if flags==O_CREAT
+ * restored for 2.4. This is the last surviving part of old 4.2BSD bug.
+ * During the 2.4 we need to fix the userland stuff depending on it -
+ * hopefully we will be able to get rid of that wart in 2.5. So far only
+ * XEmacs seems to be relying on it...
+ */
+/*
+ * [Sep 2001 AV] Single-semaphore locking scheme (kudos to David Holland)
+ * implemented.  Let's see if raised priority of ->s_vfs_rename_sem gives
+ * any extra contention...
+ */
+
+/* In order to reduce some races, while at the same time doing additional
+ * checking and hopefully speeding things up, we copy filenames to the
+ * kernel data space before using them..
+ *
+ * POSIX.1 2.4: an empty pathname is invalid (ENOENT).
+ * PATH_MAX includes the nul terminator --RR.
+ */
+static inline int do_getname(const char __user *filename, char *page)
+{
+	int retval;
+	unsigned long len = PATH_MAX;
+
+	if (!segment_eq(get_fs(), KERNEL_DS)) {
+		if ((unsigned long) filename >= TASK_SIZE)
+			return -EFAULT;
+		if (TASK_SIZE - (unsigned long) filename < PATH_MAX)
+			len = TASK_SIZE - (unsigned long) filename;
+	}
+
+	retval = strncpy_from_user(page, filename, len);
+	if (retval > 0) {
+		if (retval < len)
+			return 0;
+		return -ENAMETOOLONG;
+	} else if (!retval)
+		retval = -ENOENT;
+	return retval;
+}
+
+char * getname(const char __user * filename)
+{
+	char *tmp, *result;
+
+	result = ERR_PTR(-ENOMEM);
+	tmp = __getname();
+	if (tmp)  {
+		int retval = do_getname(filename, tmp);
+
+		result = tmp;
+		if (retval < 0) {
+			__putname(tmp);
+			result = ERR_PTR(retval);
+		}
+	}
+	if (unlikely(current->audit_context) && !IS_ERR(result) && result)
+		audit_getname(result);
+	return result;
+}
+
+/**
+ * generic_permission  -  check for access rights on a Posix-like filesystem
+ * @inode:	inode to check access rights for
+ * @mask:	right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC)
+ * @check_acl:	optional callback to check for Posix ACLs
+ *
+ * Used to check for read/write/execute permissions on a file.
+ * We use "fsuid" for this, letting us set arbitrary permissions
+ * for filesystem access without changing the "normal" uids which
+ * are used for other things..
+ */
+int generic_permission(struct inode *inode, int mask,
+		int (*check_acl)(struct inode *inode, int mask))
+{
+	umode_t			mode = inode->i_mode;
+
+	if (current->fsuid == inode->i_uid)
+		mode >>= 6;
+	else {
+		if (IS_POSIXACL(inode) && (mode & S_IRWXG) && check_acl) {
+			int error = check_acl(inode, mask);
+			if (error == -EACCES)
+				goto check_capabilities;
+			else if (error != -EAGAIN)
+				return error;
+		}
+
+		if (in_group_p(inode->i_gid))
+			mode >>= 3;
+	}
+
+	/*
+	 * If the DACs are ok we don't need any capability check.
+	 */
+	if (((mode & mask & (MAY_READ|MAY_WRITE|MAY_EXEC)) == mask))
+		return 0;
+
+ check_capabilities:
+	/*
+	 * Read/write DACs are always overridable.
+	 * Executable DACs are overridable if at least one exec bit is set.
+	 */
+	if (!(mask & MAY_EXEC) ||
+	    (inode->i_mode & S_IXUGO) || S_ISDIR(inode->i_mode))
+		if (capable(CAP_DAC_OVERRIDE))
+			return 0;
+
+	/*
+	 * Searching includes executable on directories, else just read.
+	 */
+	if (mask == MAY_READ || (S_ISDIR(inode->i_mode) && !(mask & MAY_WRITE)))
+		if (capable(CAP_DAC_READ_SEARCH))
+			return 0;
+
+	return -EACCES;
+}
+
+int permission(struct inode *inode, int mask, struct nameidata *nd)
+{
+	int retval, submask;
+
+	if (mask & MAY_WRITE) {
+		umode_t mode = inode->i_mode;
+
+		/*
+		 * Nobody gets write access to a read-only fs.
+		 */
+		if (IS_RDONLY(inode) &&
+		    (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)))
+			return -EROFS;
+
+		/*
+		 * Nobody gets write access to an immutable file.
+		 */
+		if (IS_IMMUTABLE(inode))
+			return -EACCES;
+	}
+
+
+	/* Ordinary permission routines do not understand MAY_APPEND. */
+	submask = mask & ~MAY_APPEND;
+	if (inode->i_op && inode->i_op->permission)
+		retval = inode->i_op->permission(inode, submask, nd);
+	else
+		retval = generic_permission(inode, submask, NULL);
+	if (retval)
+		return retval;
+
+	return security_inode_permission(inode, mask, nd);
+}
+
+/*
+ * get_write_access() gets write permission for a file.
+ * put_write_access() releases this write permission.
+ * This is used for regular files.
+ * We cannot support write (and maybe mmap read-write shared) accesses and
+ * MAP_DENYWRITE mmappings simultaneously. The i_writecount field of an inode
+ * can have the following values:
+ * 0: no writers, no VM_DENYWRITE mappings
+ * < 0: (-i_writecount) vm_area_structs with VM_DENYWRITE set exist
+ * > 0: (i_writecount) users are writing to the file.
+ *
+ * Normally we operate on that counter with atomic_{inc,dec} and it's safe
+ * except for the cases where we don't hold i_writecount yet. Then we need to
+ * use {get,deny}_write_access() - these functions check the sign and refuse
+ * to do the change if sign is wrong. Exclusion between them is provided by
+ * the inode->i_lock spinlock.
+ */
+
+int get_write_access(struct inode * inode)
+{
+	spin_lock(&inode->i_lock);
+	if (atomic_read(&inode->i_writecount) < 0) {
+		spin_unlock(&inode->i_lock);
+		return -ETXTBSY;
+	}
+	atomic_inc(&inode->i_writecount);
+	spin_unlock(&inode->i_lock);
+
+	return 0;
+}
+
+int deny_write_access(struct file * file)
+{
+	struct inode *inode = file->f_dentry->d_inode;
+
+	spin_lock(&inode->i_lock);
+	if (atomic_read(&inode->i_writecount) > 0) {
+		spin_unlock(&inode->i_lock);
+		return -ETXTBSY;
+	}
+	atomic_dec(&inode->i_writecount);
+	spin_unlock(&inode->i_lock);
+
+	return 0;
+}
+
+void path_release(struct nameidata *nd)
+{
+	dput(nd->dentry);
+	mntput(nd->mnt);
+}
+
+/*
+ * umount() mustn't call path_release()/mntput() as that would clear
+ * mnt_expiry_mark
+ */
+void path_release_on_umount(struct nameidata *nd)
+{
+	dput(nd->dentry);
+	_mntput(nd->mnt);
+}
+
+/*
+ * Internal lookup() using the new generic dcache.
+ * SMP-safe
+ */
+static struct dentry * cached_lookup(struct dentry * parent, struct qstr * name, struct nameidata *nd)
+{
+	struct dentry * dentry = __d_lookup(parent, name);
+
+	/* lockess __d_lookup may fail due to concurrent d_move() 
+	 * in some unrelated directory, so try with d_lookup
+	 */
+	if (!dentry)
+		dentry = d_lookup(parent, name);
+
+	if (dentry && dentry->d_op && dentry->d_op->d_revalidate) {
+		if (!dentry->d_op->d_revalidate(dentry, nd) && !d_invalidate(dentry)) {
+			dput(dentry);
+			dentry = NULL;
+		}
+	}
+	return dentry;
+}
+
+/*
+ * Short-cut version of permission(), for calling by
+ * path_walk(), when dcache lock is held.  Combines parts
+ * of permission() and generic_permission(), and tests ONLY for
+ * MAY_EXEC permission.
+ *
+ * If appropriate, check DAC only.  If not appropriate, or
+ * short-cut DAC fails, then call permission() to do more
+ * complete permission check.
+ */
+static inline int exec_permission_lite(struct inode *inode,
+				       struct nameidata *nd)
+{
+	umode_t	mode = inode->i_mode;
+
+	if (inode->i_op && inode->i_op->permission)
+		return -EAGAIN;
+
+	if (current->fsuid == inode->i_uid)
+		mode >>= 6;
+	else if (in_group_p(inode->i_gid))
+		mode >>= 3;
+
+	if (mode & MAY_EXEC)
+		goto ok;
+
+	if ((inode->i_mode & S_IXUGO) && capable(CAP_DAC_OVERRIDE))
+		goto ok;
+
+	if (S_ISDIR(inode->i_mode) && capable(CAP_DAC_OVERRIDE))
+		goto ok;
+
+	if (S_ISDIR(inode->i_mode) && capable(CAP_DAC_READ_SEARCH))
+		goto ok;
+
+	return -EACCES;
+ok:
+	return security_inode_permission(inode, MAY_EXEC, nd);
+}
+
+/*
+ * This is called when everything else fails, and we actually have
+ * to go to the low-level filesystem to find out what we should do..
+ *
+ * We get the directory semaphore, and after getting that we also
+ * make sure that nobody added the entry to the dcache in the meantime..
+ * SMP-safe
+ */
+static struct dentry * real_lookup(struct dentry * parent, struct qstr * name, struct nameidata *nd)
+{
+	struct dentry * result;
+	struct inode *dir = parent->d_inode;
+
+	down(&dir->i_sem);
+	/*
+	 * First re-do the cached lookup just in case it was created
+	 * while we waited for the directory semaphore..
+	 *
+	 * FIXME! This could use version numbering or similar to
+	 * avoid unnecessary cache lookups.
+	 *
+	 * The "dcache_lock" is purely to protect the RCU list walker
+	 * from concurrent renames at this point (we mustn't get false
+	 * negatives from the RCU list walk here, unlike the optimistic
+	 * fast walk).
+	 *
+	 * so doing d_lookup() (with seqlock), instead of lockfree __d_lookup
+	 */
+	result = d_lookup(parent, name);
+	if (!result) {
+		struct dentry * dentry = d_alloc(parent, name);
+		result = ERR_PTR(-ENOMEM);
+		if (dentry) {
+			result = dir->i_op->lookup(dir, dentry, nd);
+			if (result)
+				dput(dentry);
+			else
+				result = dentry;
+		}
+		up(&dir->i_sem);
+		return result;
+	}
+
+	/*
+	 * Uhhuh! Nasty case: the cache was re-populated while
+	 * we waited on the semaphore. Need to revalidate.
+	 */
+	up(&dir->i_sem);
+	if (result->d_op && result->d_op->d_revalidate) {
+		if (!result->d_op->d_revalidate(result, nd) && !d_invalidate(result)) {
+			dput(result);
+			result = ERR_PTR(-ENOENT);
+		}
+	}
+	return result;
+}
+
+static int __emul_lookup_dentry(const char *, struct nameidata *);
+
+/* SMP-safe */
+static inline int
+walk_init_root(const char *name, struct nameidata *nd)
+{
+	read_lock(&current->fs->lock);
+	if (current->fs->altroot && !(nd->flags & LOOKUP_NOALT)) {
+		nd->mnt = mntget(current->fs->altrootmnt);
+		nd->dentry = dget(current->fs->altroot);
+		read_unlock(&current->fs->lock);
+		if (__emul_lookup_dentry(name,nd))
+			return 0;
+		read_lock(&current->fs->lock);
+	}
+	nd->mnt = mntget(current->fs->rootmnt);
+	nd->dentry = dget(current->fs->root);
+	read_unlock(&current->fs->lock);
+	return 1;
+}
+
+static inline int __vfs_follow_link(struct nameidata *nd, const char *link)
+{
+	int res = 0;
+	char *name;
+	if (IS_ERR(link))
+		goto fail;
+
+	if (*link == '/') {
+		path_release(nd);
+		if (!walk_init_root(link, nd))
+			/* weird __emul_prefix() stuff did it */
+			goto out;
+	}
+	res = link_path_walk(link, nd);
+out:
+	if (nd->depth || res || nd->last_type!=LAST_NORM)
+		return res;
+	/*
+	 * If it is an iterative symlinks resolution in open_namei() we
+	 * have to copy the last component. And all that crap because of
+	 * bloody create() on broken symlinks. Furrfu...
+	 */
+	name = __getname();
+	if (unlikely(!name)) {
+		path_release(nd);
+		return -ENOMEM;
+	}
+	strcpy(name, nd->last.name);
+	nd->last.name = name;
+	return 0;
+fail:
+	path_release(nd);
+	return PTR_ERR(link);
+}
+
+static inline int __do_follow_link(struct dentry *dentry, struct nameidata *nd)
+{
+	int error;
+
+	touch_atime(nd->mnt, dentry);
+	nd_set_link(nd, NULL);
+	error = dentry->d_inode->i_op->follow_link(dentry, nd);
+	if (!error) {
+		char *s = nd_get_link(nd);
+		if (s)
+			error = __vfs_follow_link(nd, s);
+		if (dentry->d_inode->i_op->put_link)
+			dentry->d_inode->i_op->put_link(dentry, nd);
+	}
+
+	return error;
+}
+
+/*
+ * This limits recursive symlink follows to 8, while
+ * limiting consecutive symlinks to 40.
+ *
+ * Without that kind of total limit, nasty chains of consecutive
+ * symlinks can cause almost arbitrarily long lookups. 
+ */
+static inline int do_follow_link(struct dentry *dentry, struct nameidata *nd)
+{
+	int err = -ELOOP;
+	if (current->link_count >= MAX_NESTED_LINKS)
+		goto loop;
+	if (current->total_link_count >= 40)
+		goto loop;
+	BUG_ON(nd->depth >= MAX_NESTED_LINKS);
+	cond_resched();
+	err = security_inode_follow_link(dentry, nd);
+	if (err)
+		goto loop;
+	current->link_count++;
+	current->total_link_count++;
+	nd->depth++;
+	err = __do_follow_link(dentry, nd);
+	current->link_count--;
+	nd->depth--;
+	return err;
+loop:
+	path_release(nd);
+	return err;
+}
+
+int follow_up(struct vfsmount **mnt, struct dentry **dentry)
+{
+	struct vfsmount *parent;
+	struct dentry *mountpoint;
+	spin_lock(&vfsmount_lock);
+	parent=(*mnt)->mnt_parent;
+	if (parent == *mnt) {
+		spin_unlock(&vfsmount_lock);
+		return 0;
+	}
+	mntget(parent);
+	mountpoint=dget((*mnt)->mnt_mountpoint);
+	spin_unlock(&vfsmount_lock);
+	dput(*dentry);
+	*dentry = mountpoint;
+	mntput(*mnt);
+	*mnt = parent;
+	return 1;
+}
+
+/* no need for dcache_lock, as serialization is taken care in
+ * namespace.c
+ */
+static int follow_mount(struct vfsmount **mnt, struct dentry **dentry)
+{
+	int res = 0;
+	while (d_mountpoint(*dentry)) {
+		struct vfsmount *mounted = lookup_mnt(*mnt, *dentry);
+		if (!mounted)
+			break;
+		mntput(*mnt);
+		*mnt = mounted;
+		dput(*dentry);
+		*dentry = dget(mounted->mnt_root);
+		res = 1;
+	}
+	return res;
+}
+
+/* no need for dcache_lock, as serialization is taken care in
+ * namespace.c
+ */
+static inline int __follow_down(struct vfsmount **mnt, struct dentry **dentry)
+{
+	struct vfsmount *mounted;
+
+	mounted = lookup_mnt(*mnt, *dentry);
+	if (mounted) {
+		mntput(*mnt);
+		*mnt = mounted;
+		dput(*dentry);
+		*dentry = dget(mounted->mnt_root);
+		return 1;
+	}
+	return 0;
+}
+
+int follow_down(struct vfsmount **mnt, struct dentry **dentry)
+{
+	return __follow_down(mnt,dentry);
+}
+ 
+static inline void follow_dotdot(struct vfsmount **mnt, struct dentry **dentry)
+{
+	while(1) {
+		struct vfsmount *parent;
+		struct dentry *old = *dentry;
+
+                read_lock(&current->fs->lock);
+		if (*dentry == current->fs->root &&
+		    *mnt == current->fs->rootmnt) {
+                        read_unlock(&current->fs->lock);
+			break;
+		}
+                read_unlock(&current->fs->lock);
+		spin_lock(&dcache_lock);
+		if (*dentry != (*mnt)->mnt_root) {
+			*dentry = dget((*dentry)->d_parent);
+			spin_unlock(&dcache_lock);
+			dput(old);
+			break;
+		}
+		spin_unlock(&dcache_lock);
+		spin_lock(&vfsmount_lock);
+		parent = (*mnt)->mnt_parent;
+		if (parent == *mnt) {
+			spin_unlock(&vfsmount_lock);
+			break;
+		}
+		mntget(parent);
+		*dentry = dget((*mnt)->mnt_mountpoint);
+		spin_unlock(&vfsmount_lock);
+		dput(old);
+		mntput(*mnt);
+		*mnt = parent;
+	}
+	follow_mount(mnt, dentry);
+}
+
+struct path {
+	struct vfsmount *mnt;
+	struct dentry *dentry;
+};
+
+/*
+ *  It's more convoluted than I'd like it to be, but... it's still fairly
+ *  small and for now I'd prefer to have fast path as straight as possible.
+ *  It _is_ time-critical.
+ */
+static int do_lookup(struct nameidata *nd, struct qstr *name,
+		     struct path *path)
+{
+	struct vfsmount *mnt = nd->mnt;
+	struct dentry *dentry = __d_lookup(nd->dentry, name);
+
+	if (!dentry)
+		goto need_lookup;
+	if (dentry->d_op && dentry->d_op->d_revalidate)
+		goto need_revalidate;
+done:
+	path->mnt = mnt;
+	path->dentry = dentry;
+	return 0;
+
+need_lookup:
+	dentry = real_lookup(nd->dentry, name, nd);
+	if (IS_ERR(dentry))
+		goto fail;
+	goto done;
+
+need_revalidate:
+	if (dentry->d_op->d_revalidate(dentry, nd))
+		goto done;
+	if (d_invalidate(dentry))
+		goto done;
+	dput(dentry);
+	goto need_lookup;
+
+fail:
+	return PTR_ERR(dentry);
+}
+
+/*
+ * Name resolution.
+ *
+ * This is the basic name resolution function, turning a pathname
+ * into the final dentry.
+ *
+ * We expect 'base' to be positive and a directory.
+ */
+int fastcall link_path_walk(const char * name, struct nameidata *nd)
+{
+	struct path next;
+	struct inode *inode;
+	int err;
+	unsigned int lookup_flags = nd->flags;
+	
+	while (*name=='/')
+		name++;
+	if (!*name)
+		goto return_reval;
+
+	inode = nd->dentry->d_inode;
+	if (nd->depth)
+		lookup_flags = LOOKUP_FOLLOW;
+
+	/* At this point we know we have a real path component. */
+	for(;;) {
+		unsigned long hash;
+		struct qstr this;
+		unsigned int c;
+
+		err = exec_permission_lite(inode, nd);
+		if (err == -EAGAIN) { 
+			err = permission(inode, MAY_EXEC, nd);
+		}
+ 		if (err)
+			break;
+
+		this.name = name;
+		c = *(const unsigned char *)name;
+
+		hash = init_name_hash();
+		do {
+			name++;
+			hash = partial_name_hash(c, hash);
+			c = *(const unsigned char *)name;
+		} while (c && (c != '/'));
+		this.len = name - (const char *) this.name;
+		this.hash = end_name_hash(hash);
+
+		/* remove trailing slashes? */
+		if (!c)
+			goto last_component;
+		while (*++name == '/');
+		if (!*name)
+			goto last_with_slashes;
+
+		/*
+		 * "." and ".." are special - ".." especially so because it has
+		 * to be able to know about the current root directory and
+		 * parent relationships.
+		 */
+		if (this.name[0] == '.') switch (this.len) {
+			default:
+				break;
+			case 2:	
+				if (this.name[1] != '.')
+					break;
+				follow_dotdot(&nd->mnt, &nd->dentry);
+				inode = nd->dentry->d_inode;
+				/* fallthrough */
+			case 1:
+				continue;
+		}
+		/*
+		 * See if the low-level filesystem might want
+		 * to use its own hash..
+		 */
+		if (nd->dentry->d_op && nd->dentry->d_op->d_hash) {
+			err = nd->dentry->d_op->d_hash(nd->dentry, &this);
+			if (err < 0)
+				break;
+		}
+		nd->flags |= LOOKUP_CONTINUE;
+		/* This does the actual lookups.. */
+		err = do_lookup(nd, &this, &next);
+		if (err)
+			break;
+		/* Check mountpoints.. */
+		follow_mount(&next.mnt, &next.dentry);
+
+		err = -ENOENT;
+		inode = next.dentry->d_inode;
+		if (!inode)
+			goto out_dput;
+		err = -ENOTDIR; 
+		if (!inode->i_op)
+			goto out_dput;
+
+		if (inode->i_op->follow_link) {
+			mntget(next.mnt);
+			err = do_follow_link(next.dentry, nd);
+			dput(next.dentry);
+			mntput(next.mnt);
+			if (err)
+				goto return_err;
+			err = -ENOENT;
+			inode = nd->dentry->d_inode;
+			if (!inode)
+				break;
+			err = -ENOTDIR; 
+			if (!inode->i_op)
+				break;
+		} else {
+			dput(nd->dentry);
+			nd->mnt = next.mnt;
+			nd->dentry = next.dentry;
+		}
+		err = -ENOTDIR; 
+		if (!inode->i_op->lookup)
+			break;
+		continue;
+		/* here ends the main loop */
+
+last_with_slashes:
+		lookup_flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY;
+last_component:
+		nd->flags &= ~LOOKUP_CONTINUE;
+		if (lookup_flags & LOOKUP_PARENT)
+			goto lookup_parent;
+		if (this.name[0] == '.') switch (this.len) {
+			default:
+				break;
+			case 2:	
+				if (this.name[1] != '.')
+					break;
+				follow_dotdot(&nd->mnt, &nd->dentry);
+				inode = nd->dentry->d_inode;
+				/* fallthrough */
+			case 1:
+				goto return_reval;
+		}
+		if (nd->dentry->d_op && nd->dentry->d_op->d_hash) {
+			err = nd->dentry->d_op->d_hash(nd->dentry, &this);
+			if (err < 0)
+				break;
+		}
+		err = do_lookup(nd, &this, &next);
+		if (err)
+			break;
+		follow_mount(&next.mnt, &next.dentry);
+		inode = next.dentry->d_inode;
+		if ((lookup_flags & LOOKUP_FOLLOW)
+		    && inode && inode->i_op && inode->i_op->follow_link) {
+			mntget(next.mnt);
+			err = do_follow_link(next.dentry, nd);
+			dput(next.dentry);
+			mntput(next.mnt);
+			if (err)
+				goto return_err;
+			inode = nd->dentry->d_inode;
+		} else {
+			dput(nd->dentry);
+			nd->mnt = next.mnt;
+			nd->dentry = next.dentry;
+		}
+		err = -ENOENT;
+		if (!inode)
+			break;
+		if (lookup_flags & LOOKUP_DIRECTORY) {
+			err = -ENOTDIR; 
+			if (!inode->i_op || !inode->i_op->lookup)
+				break;
+		}
+		goto return_base;
+lookup_parent:
+		nd->last = this;
+		nd->last_type = LAST_NORM;
+		if (this.name[0] != '.')
+			goto return_base;
+		if (this.len == 1)
+			nd->last_type = LAST_DOT;
+		else if (this.len == 2 && this.name[1] == '.')
+			nd->last_type = LAST_DOTDOT;
+		else
+			goto return_base;
+return_reval:
+		/*
+		 * We bypassed the ordinary revalidation routines.
+		 * We may need to check the cached dentry for staleness.
+		 */
+		if (nd->dentry && nd->dentry->d_sb &&
+		    (nd->dentry->d_sb->s_type->fs_flags & FS_REVAL_DOT)) {
+			err = -ESTALE;
+			/* Note: we do not d_invalidate() */
+			if (!nd->dentry->d_op->d_revalidate(nd->dentry, nd))
+				break;
+		}
+return_base:
+		return 0;
+out_dput:
+		dput(next.dentry);
+		break;
+	}
+	path_release(nd);
+return_err:
+	return err;
+}
+
+int fastcall path_walk(const char * name, struct nameidata *nd)
+{
+	current->total_link_count = 0;
+	return link_path_walk(name, nd);
+}
+
+/* SMP-safe */
+/* returns 1 if everything is done */
+static int __emul_lookup_dentry(const char *name, struct nameidata *nd)
+{
+	if (path_walk(name, nd))
+		return 0;		/* something went wrong... */
+
+	if (!nd->dentry->d_inode || S_ISDIR(nd->dentry->d_inode->i_mode)) {
+		struct dentry *old_dentry = nd->dentry;
+		struct vfsmount *old_mnt = nd->mnt;
+		struct qstr last = nd->last;
+		int last_type = nd->last_type;
+		/*
+		 * NAME was not found in alternate root or it's a directory.  Try to find
+		 * it in the normal root:
+		 */
+		nd->last_type = LAST_ROOT;
+		read_lock(&current->fs->lock);
+		nd->mnt = mntget(current->fs->rootmnt);
+		nd->dentry = dget(current->fs->root);
+		read_unlock(&current->fs->lock);
+		if (path_walk(name, nd) == 0) {
+			if (nd->dentry->d_inode) {
+				dput(old_dentry);
+				mntput(old_mnt);
+				return 1;
+			}
+			path_release(nd);
+		}
+		nd->dentry = old_dentry;
+		nd->mnt = old_mnt;
+		nd->last = last;
+		nd->last_type = last_type;
+	}
+	return 1;
+}
+
+void set_fs_altroot(void)
+{
+	char *emul = __emul_prefix();
+	struct nameidata nd;
+	struct vfsmount *mnt = NULL, *oldmnt;
+	struct dentry *dentry = NULL, *olddentry;
+	int err;
+
+	if (!emul)
+		goto set_it;
+	err = path_lookup(emul, LOOKUP_FOLLOW|LOOKUP_DIRECTORY|LOOKUP_NOALT, &nd);
+	if (!err) {
+		mnt = nd.mnt;
+		dentry = nd.dentry;
+	}
+set_it:
+	write_lock(&current->fs->lock);
+	oldmnt = current->fs->altrootmnt;
+	olddentry = current->fs->altroot;
+	current->fs->altrootmnt = mnt;
+	current->fs->altroot = dentry;
+	write_unlock(&current->fs->lock);
+	if (olddentry) {
+		dput(olddentry);
+		mntput(oldmnt);
+	}
+}
+
+int fastcall path_lookup(const char *name, unsigned int flags, struct nameidata *nd)
+{
+	int retval;
+
+	nd->last_type = LAST_ROOT; /* if there are only slashes... */
+	nd->flags = flags;
+	nd->depth = 0;
+
+	read_lock(&current->fs->lock);
+	if (*name=='/') {
+		if (current->fs->altroot && !(nd->flags & LOOKUP_NOALT)) {
+			nd->mnt = mntget(current->fs->altrootmnt);
+			nd->dentry = dget(current->fs->altroot);
+			read_unlock(&current->fs->lock);
+			if (__emul_lookup_dentry(name,nd))
+				return 0;
+			read_lock(&current->fs->lock);
+		}
+		nd->mnt = mntget(current->fs->rootmnt);
+		nd->dentry = dget(current->fs->root);
+	} else {
+		nd->mnt = mntget(current->fs->pwdmnt);
+		nd->dentry = dget(current->fs->pwd);
+	}
+	read_unlock(&current->fs->lock);
+	current->total_link_count = 0;
+	retval = link_path_walk(name, nd);
+	if (unlikely(current->audit_context
+		     && nd && nd->dentry && nd->dentry->d_inode))
+		audit_inode(name,
+			    nd->dentry->d_inode->i_ino,
+			    nd->dentry->d_inode->i_rdev);
+	return retval;
+}
+
+/*
+ * Restricted form of lookup. Doesn't follow links, single-component only,
+ * needs parent already locked. Doesn't follow mounts.
+ * SMP-safe.
+ */
+static struct dentry * __lookup_hash(struct qstr *name, struct dentry * base, struct nameidata *nd)
+{
+	struct dentry * dentry;
+	struct inode *inode;
+	int err;
+
+	inode = base->d_inode;
+	err = permission(inode, MAY_EXEC, nd);
+	dentry = ERR_PTR(err);
+	if (err)
+		goto out;
+
+	/*
+	 * See if the low-level filesystem might want
+	 * to use its own hash..
+	 */
+	if (base->d_op && base->d_op->d_hash) {
+		err = base->d_op->d_hash(base, name);
+		dentry = ERR_PTR(err);
+		if (err < 0)
+			goto out;
+	}
+
+	dentry = cached_lookup(base, name, nd);
+	if (!dentry) {
+		struct dentry *new = d_alloc(base, name);
+		dentry = ERR_PTR(-ENOMEM);
+		if (!new)
+			goto out;
+		dentry = inode->i_op->lookup(inode, new, nd);
+		if (!dentry)
+			dentry = new;
+		else
+			dput(new);
+	}
+out:
+	return dentry;
+}
+
+struct dentry * lookup_hash(struct qstr *name, struct dentry * base)
+{
+	return __lookup_hash(name, base, NULL);
+}
+
+/* SMP-safe */
+struct dentry * lookup_one_len(const char * name, struct dentry * base, int len)
+{
+	unsigned long hash;
+	struct qstr this;
+	unsigned int c;
+
+	this.name = name;
+	this.len = len;
+	if (!len)
+		goto access;
+
+	hash = init_name_hash();
+	while (len--) {
+		c = *(const unsigned char *)name++;
+		if (c == '/' || c == '\0')
+			goto access;
+		hash = partial_name_hash(c, hash);
+	}
+	this.hash = end_name_hash(hash);
+
+	return lookup_hash(&this, base);
+access:
+	return ERR_PTR(-EACCES);
+}
+
+/*
+ *	namei()
+ *
+ * is used by most simple commands to get the inode of a specified name.
+ * Open, link etc use their own routines, but this is enough for things
+ * like 'chmod' etc.
+ *
+ * namei exists in two versions: namei/lnamei. The only difference is
+ * that namei follows links, while lnamei does not.
+ * SMP-safe
+ */
+int fastcall __user_walk(const char __user *name, unsigned flags, struct nameidata *nd)
+{
+	char *tmp = getname(name);
+	int err = PTR_ERR(tmp);
+
+	if (!IS_ERR(tmp)) {
+		err = path_lookup(tmp, flags, nd);
+		putname(tmp);
+	}
+	return err;
+}
+
+/*
+ * It's inline, so penalty for filesystems that don't use sticky bit is
+ * minimal.
+ */
+static inline int check_sticky(struct inode *dir, struct inode *inode)
+{
+	if (!(dir->i_mode & S_ISVTX))
+		return 0;
+	if (inode->i_uid == current->fsuid)
+		return 0;
+	if (dir->i_uid == current->fsuid)
+		return 0;
+	return !capable(CAP_FOWNER);
+}
+
+/*
+ *	Check whether we can remove a link victim from directory dir, check
+ *  whether the type of victim is right.
+ *  1. We can't do it if dir is read-only (done in permission())
+ *  2. We should have write and exec permissions on dir
+ *  3. We can't remove anything from append-only dir
+ *  4. We can't do anything with immutable dir (done in permission())
+ *  5. If the sticky bit on dir is set we should either
+ *	a. be owner of dir, or
+ *	b. be owner of victim, or
+ *	c. have CAP_FOWNER capability
+ *  6. If the victim is append-only or immutable we can't do antyhing with
+ *     links pointing to it.
+ *  7. If we were asked to remove a directory and victim isn't one - ENOTDIR.
+ *  8. If we were asked to remove a non-directory and victim isn't one - EISDIR.
+ *  9. We can't remove a root or mountpoint.
+ * 10. We don't allow removal of NFS sillyrenamed files; it's handled by
+ *     nfs_async_unlink().
+ */
+static inline int may_delete(struct inode *dir,struct dentry *victim,int isdir)
+{
+	int error;
+
+	if (!victim->d_inode)
+		return -ENOENT;
+
+	BUG_ON(victim->d_parent->d_inode != dir);
+
+	error = permission(dir,MAY_WRITE | MAY_EXEC, NULL);
+	if (error)
+		return error;
+	if (IS_APPEND(dir))
+		return -EPERM;
+	if (check_sticky(dir, victim->d_inode)||IS_APPEND(victim->d_inode)||
+	    IS_IMMUTABLE(victim->d_inode))
+		return -EPERM;
+	if (isdir) {
+		if (!S_ISDIR(victim->d_inode->i_mode))
+			return -ENOTDIR;
+		if (IS_ROOT(victim))
+			return -EBUSY;
+	} else if (S_ISDIR(victim->d_inode->i_mode))
+		return -EISDIR;
+	if (IS_DEADDIR(dir))
+		return -ENOENT;
+	if (victim->d_flags & DCACHE_NFSFS_RENAMED)
+		return -EBUSY;
+	return 0;
+}
+
+/*	Check whether we can create an object with dentry child in directory
+ *  dir.
+ *  1. We can't do it if child already exists (open has special treatment for
+ *     this case, but since we are inlined it's OK)
+ *  2. We can't do it if dir is read-only (done in permission())
+ *  3. We should have write and exec permissions on dir
+ *  4. We can't do it if dir is immutable (done in permission())
+ */
+static inline int may_create(struct inode *dir, struct dentry *child,
+			     struct nameidata *nd)
+{
+	if (child->d_inode)
+		return -EEXIST;
+	if (IS_DEADDIR(dir))
+		return -ENOENT;
+	return permission(dir,MAY_WRITE | MAY_EXEC, nd);
+}
+
+/* 
+ * Special case: O_CREAT|O_EXCL implies O_NOFOLLOW for security
+ * reasons.
+ *
+ * O_DIRECTORY translates into forcing a directory lookup.
+ */
+static inline int lookup_flags(unsigned int f)
+{
+	unsigned long retval = LOOKUP_FOLLOW;
+
+	if (f & O_NOFOLLOW)
+		retval &= ~LOOKUP_FOLLOW;
+	
+	if ((f & (O_CREAT|O_EXCL)) == (O_CREAT|O_EXCL))
+		retval &= ~LOOKUP_FOLLOW;
+	
+	if (f & O_DIRECTORY)
+		retval |= LOOKUP_DIRECTORY;
+
+	return retval;
+}
+
+/*
+ * p1 and p2 should be directories on the same fs.
+ */
+struct dentry *lock_rename(struct dentry *p1, struct dentry *p2)
+{
+	struct dentry *p;
+
+	if (p1 == p2) {
+		down(&p1->d_inode->i_sem);
+		return NULL;
+	}
+
+	down(&p1->d_inode->i_sb->s_vfs_rename_sem);
+
+	for (p = p1; p->d_parent != p; p = p->d_parent) {
+		if (p->d_parent == p2) {
+			down(&p2->d_inode->i_sem);
+			down(&p1->d_inode->i_sem);
+			return p;
+		}
+	}
+
+	for (p = p2; p->d_parent != p; p = p->d_parent) {
+		if (p->d_parent == p1) {
+			down(&p1->d_inode->i_sem);
+			down(&p2->d_inode->i_sem);
+			return p;
+		}
+	}
+
+	down(&p1->d_inode->i_sem);
+	down(&p2->d_inode->i_sem);
+	return NULL;
+}
+
+void unlock_rename(struct dentry *p1, struct dentry *p2)
+{
+	up(&p1->d_inode->i_sem);
+	if (p1 != p2) {
+		up(&p2->d_inode->i_sem);
+		up(&p1->d_inode->i_sb->s_vfs_rename_sem);
+	}
+}
+
+int vfs_create(struct inode *dir, struct dentry *dentry, int mode,
+		struct nameidata *nd)
+{
+	int error = may_create(dir, dentry, nd);
+
+	if (error)
+		return error;
+
+	if (!dir->i_op || !dir->i_op->create)
+		return -EACCES;	/* shouldn't it be ENOSYS? */
+	mode &= S_IALLUGO;
+	mode |= S_IFREG;
+	error = security_inode_create(dir, dentry, mode);
+	if (error)
+		return error;
+	DQUOT_INIT(dir);
+	error = dir->i_op->create(dir, dentry, mode, nd);
+	if (!error) {
+		inode_dir_notify(dir, DN_CREATE);
+		security_inode_post_create(dir, dentry, mode);
+	}
+	return error;
+}
+
+int may_open(struct nameidata *nd, int acc_mode, int flag)
+{
+	struct dentry *dentry = nd->dentry;
+	struct inode *inode = dentry->d_inode;
+	int error;
+
+	if (!inode)
+		return -ENOENT;
+
+	if (S_ISLNK(inode->i_mode))
+		return -ELOOP;
+	
+	if (S_ISDIR(inode->i_mode) && (flag & FMODE_WRITE))
+		return -EISDIR;
+
+	error = permission(inode, acc_mode, nd);
+	if (error)
+		return error;
+
+	/*
+	 * FIFO's, sockets and device files are special: they don't
+	 * actually live on the filesystem itself, and as such you
+	 * can write to them even if the filesystem is read-only.
+	 */
+	if (S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) {
+	    	flag &= ~O_TRUNC;
+	} else if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode)) {
+		if (nd->mnt->mnt_flags & MNT_NODEV)
+			return -EACCES;
+
+		flag &= ~O_TRUNC;
+	} else if (IS_RDONLY(inode) && (flag & FMODE_WRITE))
+		return -EROFS;
+	/*
+	 * An append-only file must be opened in append mode for writing.
+	 */
+	if (IS_APPEND(inode)) {
+		if  ((flag & FMODE_WRITE) && !(flag & O_APPEND))
+			return -EPERM;
+		if (flag & O_TRUNC)
+			return -EPERM;
+	}
+
+	/* O_NOATIME can only be set by the owner or superuser */
+	if (flag & O_NOATIME)
+		if (current->fsuid != inode->i_uid && !capable(CAP_FOWNER))
+			return -EPERM;
+
+	/*
+	 * Ensure there are no outstanding leases on the file.
+	 */
+	error = break_lease(inode, flag);
+	if (error)
+		return error;
+
+	if (flag & O_TRUNC) {
+		error = get_write_access(inode);
+		if (error)
+			return error;
+
+		/*
+		 * Refuse to truncate files with mandatory locks held on them.
+		 */
+		error = locks_verify_locked(inode);
+		if (!error) {
+			DQUOT_INIT(inode);
+			
+			error = do_truncate(dentry, 0);
+		}
+		put_write_access(inode);
+		if (error)
+			return error;
+	} else
+		if (flag & FMODE_WRITE)
+			DQUOT_INIT(inode);
+
+	return 0;
+}
+
+/*
+ *	open_namei()
+ *
+ * namei for open - this is in fact almost the whole open-routine.
+ *
+ * Note that the low bits of "flag" aren't the same as in the open
+ * system call - they are 00 - no permissions needed
+ *			  01 - read permission needed
+ *			  10 - write permission needed
+ *			  11 - read/write permissions needed
+ * which is a lot more logical, and also allows the "no perm" needed
+ * for symlinks (where the permissions are checked later).
+ * SMP-safe
+ */
+int open_namei(const char * pathname, int flag, int mode, struct nameidata *nd)
+{
+	int acc_mode, error = 0;
+	struct dentry *dentry;
+	struct dentry *dir;
+	int count = 0;
+
+	acc_mode = ACC_MODE(flag);
+
+	/* Allow the LSM permission hook to distinguish append 
+	   access from general write access. */
+	if (flag & O_APPEND)
+		acc_mode |= MAY_APPEND;
+
+	/* Fill in the open() intent data */
+	nd->intent.open.flags = flag;
+	nd->intent.open.create_mode = mode;
+
+	/*
+	 * The simplest case - just a plain lookup.
+	 */
+	if (!(flag & O_CREAT)) {
+		error = path_lookup(pathname, lookup_flags(flag)|LOOKUP_OPEN, nd);
+		if (error)
+			return error;
+		goto ok;
+	}
+
+	/*
+	 * Create - we need to know the parent.
+	 */
+	error = path_lookup(pathname, LOOKUP_PARENT|LOOKUP_OPEN|LOOKUP_CREATE, nd);
+	if (error)
+		return error;
+
+	/*
+	 * We have the parent and last component. First of all, check
+	 * that we are not asked to creat(2) an obvious directory - that
+	 * will not do.
+	 */
+	error = -EISDIR;
+	if (nd->last_type != LAST_NORM || nd->last.name[nd->last.len])
+		goto exit;
+
+	dir = nd->dentry;
+	nd->flags &= ~LOOKUP_PARENT;
+	down(&dir->d_inode->i_sem);
+	dentry = __lookup_hash(&nd->last, nd->dentry, nd);
+
+do_last:
+	error = PTR_ERR(dentry);
+	if (IS_ERR(dentry)) {
+		up(&dir->d_inode->i_sem);
+		goto exit;
+	}
+
+	/* Negative dentry, just create the file */
+	if (!dentry->d_inode) {
+		if (!IS_POSIXACL(dir->d_inode))
+			mode &= ~current->fs->umask;
+		error = vfs_create(dir->d_inode, dentry, mode, nd);
+		up(&dir->d_inode->i_sem);
+		dput(nd->dentry);
+		nd->dentry = dentry;
+		if (error)
+			goto exit;
+		/* Don't check for write permission, don't truncate */
+		acc_mode = 0;
+		flag &= ~O_TRUNC;
+		goto ok;
+	}
+
+	/*
+	 * It already exists.
+	 */
+	up(&dir->d_inode->i_sem);
+
+	error = -EEXIST;
+	if (flag & O_EXCL)
+		goto exit_dput;
+
+	if (d_mountpoint(dentry)) {
+		error = -ELOOP;
+		if (flag & O_NOFOLLOW)
+			goto exit_dput;
+		while (__follow_down(&nd->mnt,&dentry) && d_mountpoint(dentry));
+	}
+	error = -ENOENT;
+	if (!dentry->d_inode)
+		goto exit_dput;
+	if (dentry->d_inode->i_op && dentry->d_inode->i_op->follow_link)
+		goto do_link;
+
+	dput(nd->dentry);
+	nd->dentry = dentry;
+	error = -EISDIR;
+	if (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode))
+		goto exit;
+ok:
+	error = may_open(nd, acc_mode, flag);
+	if (error)
+		goto exit;
+	return 0;
+
+exit_dput:
+	dput(dentry);
+exit:
+	path_release(nd);
+	return error;
+
+do_link:
+	error = -ELOOP;
+	if (flag & O_NOFOLLOW)
+		goto exit_dput;
+	/*
+	 * This is subtle. Instead of calling do_follow_link() we do the
+	 * thing by hands. The reason is that this way we have zero link_count
+	 * and path_walk() (called from ->follow_link) honoring LOOKUP_PARENT.
+	 * After that we have the parent and last component, i.e.
+	 * we are in the same situation as after the first path_walk().
+	 * Well, almost - if the last component is normal we get its copy
+	 * stored in nd->last.name and we will have to putname() it when we
+	 * are done. Procfs-like symlinks just set LAST_BIND.
+	 */
+	nd->flags |= LOOKUP_PARENT;
+	error = security_inode_follow_link(dentry, nd);
+	if (error)
+		goto exit_dput;
+	error = __do_follow_link(dentry, nd);
+	dput(dentry);
+	if (error)
+		return error;
+	nd->flags &= ~LOOKUP_PARENT;
+	if (nd->last_type == LAST_BIND) {
+		dentry = nd->dentry;
+		goto ok;
+	}
+	error = -EISDIR;
+	if (nd->last_type != LAST_NORM)
+		goto exit;
+	if (nd->last.name[nd->last.len]) {
+		putname(nd->last.name);
+		goto exit;
+	}
+	error = -ELOOP;
+	if (count++==32) {
+		putname(nd->last.name);
+		goto exit;
+	}
+	dir = nd->dentry;
+	down(&dir->d_inode->i_sem);
+	dentry = __lookup_hash(&nd->last, nd->dentry, nd);
+	putname(nd->last.name);
+	goto do_last;
+}
+
+/**
+ * lookup_create - lookup a dentry, creating it if it doesn't exist
+ * @nd: nameidata info
+ * @is_dir: directory flag
+ *
+ * Simple function to lookup and return a dentry and create it
+ * if it doesn't exist.  Is SMP-safe.
+ */
+struct dentry *lookup_create(struct nameidata *nd, int is_dir)
+{
+	struct dentry *dentry;
+
+	down(&nd->dentry->d_inode->i_sem);
+	dentry = ERR_PTR(-EEXIST);
+	if (nd->last_type != LAST_NORM)
+		goto fail;
+	nd->flags &= ~LOOKUP_PARENT;
+	dentry = lookup_hash(&nd->last, nd->dentry);
+	if (IS_ERR(dentry))
+		goto fail;
+	if (!is_dir && nd->last.name[nd->last.len] && !dentry->d_inode)
+		goto enoent;
+	return dentry;
+enoent:
+	dput(dentry);
+	dentry = ERR_PTR(-ENOENT);
+fail:
+	return dentry;
+}
+
+int vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
+{
+	int error = may_create(dir, dentry, NULL);
+
+	if (error)
+		return error;
+
+	if ((S_ISCHR(mode) || S_ISBLK(mode)) && !capable(CAP_MKNOD))
+		return -EPERM;
+
+	if (!dir->i_op || !dir->i_op->mknod)
+		return -EPERM;
+
+	error = security_inode_mknod(dir, dentry, mode, dev);
+	if (error)
+		return error;
+
+	DQUOT_INIT(dir);
+	error = dir->i_op->mknod(dir, dentry, mode, dev);
+	if (!error) {
+		inode_dir_notify(dir, DN_CREATE);
+		security_inode_post_mknod(dir, dentry, mode, dev);
+	}
+	return error;
+}
+
+asmlinkage long sys_mknod(const char __user * filename, int mode, unsigned dev)
+{
+	int error = 0;
+	char * tmp;
+	struct dentry * dentry;
+	struct nameidata nd;
+
+	if (S_ISDIR(mode))
+		return -EPERM;
+	tmp = getname(filename);
+	if (IS_ERR(tmp))
+		return PTR_ERR(tmp);
+
+	error = path_lookup(tmp, LOOKUP_PARENT, &nd);
+	if (error)
+		goto out;
+	dentry = lookup_create(&nd, 0);
+	error = PTR_ERR(dentry);
+
+	if (!IS_POSIXACL(nd.dentry->d_inode))
+		mode &= ~current->fs->umask;
+	if (!IS_ERR(dentry)) {
+		switch (mode & S_IFMT) {
+		case 0: case S_IFREG:
+			error = vfs_create(nd.dentry->d_inode,dentry,mode,&nd);
+			break;
+		case S_IFCHR: case S_IFBLK:
+			error = vfs_mknod(nd.dentry->d_inode,dentry,mode,
+					new_decode_dev(dev));
+			break;
+		case S_IFIFO: case S_IFSOCK:
+			error = vfs_mknod(nd.dentry->d_inode,dentry,mode,0);
+			break;
+		case S_IFDIR:
+			error = -EPERM;
+			break;
+		default:
+			error = -EINVAL;
+		}
+		dput(dentry);
+	}
+	up(&nd.dentry->d_inode->i_sem);
+	path_release(&nd);
+out:
+	putname(tmp);
+
+	return error;
+}
+
+int vfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+{
+	int error = may_create(dir, dentry, NULL);
+
+	if (error)
+		return error;
+
+	if (!dir->i_op || !dir->i_op->mkdir)
+		return -EPERM;
+
+	mode &= (S_IRWXUGO|S_ISVTX);
+	error = security_inode_mkdir(dir, dentry, mode);
+	if (error)
+		return error;
+
+	DQUOT_INIT(dir);
+	error = dir->i_op->mkdir(dir, dentry, mode);
+	if (!error) {
+		inode_dir_notify(dir, DN_CREATE);
+		security_inode_post_mkdir(dir,dentry, mode);
+	}
+	return error;
+}
+
+asmlinkage long sys_mkdir(const char __user * pathname, int mode)
+{
+	int error = 0;
+	char * tmp;
+
+	tmp = getname(pathname);
+	error = PTR_ERR(tmp);
+	if (!IS_ERR(tmp)) {
+		struct dentry *dentry;
+		struct nameidata nd;
+
+		error = path_lookup(tmp, LOOKUP_PARENT, &nd);
+		if (error)
+			goto out;
+		dentry = lookup_create(&nd, 1);
+		error = PTR_ERR(dentry);
+		if (!IS_ERR(dentry)) {
+			if (!IS_POSIXACL(nd.dentry->d_inode))
+				mode &= ~current->fs->umask;
+			error = vfs_mkdir(nd.dentry->d_inode, dentry, mode);
+			dput(dentry);
+		}
+		up(&nd.dentry->d_inode->i_sem);
+		path_release(&nd);
+out:
+		putname(tmp);
+	}
+
+	return error;
+}
+
+/*
+ * We try to drop the dentry early: we should have
+ * a usage count of 2 if we're the only user of this
+ * dentry, and if that is true (possibly after pruning
+ * the dcache), then we drop the dentry now.
+ *
+ * A low-level filesystem can, if it choses, legally
+ * do a
+ *
+ *	if (!d_unhashed(dentry))
+ *		return -EBUSY;
+ *
+ * if it cannot handle the case of removing a directory
+ * that is still in use by something else..
+ */
+void dentry_unhash(struct dentry *dentry)
+{
+	dget(dentry);
+	spin_lock(&dcache_lock);
+	switch (atomic_read(&dentry->d_count)) {
+	default:
+		spin_unlock(&dcache_lock);
+		shrink_dcache_parent(dentry);
+		spin_lock(&dcache_lock);
+		if (atomic_read(&dentry->d_count) != 2)
+			break;
+	case 2:
+		__d_drop(dentry);
+	}
+	spin_unlock(&dcache_lock);
+}
+
+int vfs_rmdir(struct inode *dir, struct dentry *dentry)
+{
+	int error = may_delete(dir, dentry, 1);
+
+	if (error)
+		return error;
+
+	if (!dir->i_op || !dir->i_op->rmdir)
+		return -EPERM;
+
+	DQUOT_INIT(dir);
+
+	down(&dentry->d_inode->i_sem);
+	dentry_unhash(dentry);
+	if (d_mountpoint(dentry))
+		error = -EBUSY;
+	else {
+		error = security_inode_rmdir(dir, dentry);
+		if (!error) {
+			error = dir->i_op->rmdir(dir, dentry);
+			if (!error)
+				dentry->d_inode->i_flags |= S_DEAD;
+		}
+	}
+	up(&dentry->d_inode->i_sem);
+	if (!error) {
+		inode_dir_notify(dir, DN_DELETE);
+		d_delete(dentry);
+	}
+	dput(dentry);
+
+	return error;
+}
+
+asmlinkage long sys_rmdir(const char __user * pathname)
+{
+	int error = 0;
+	char * name;
+	struct dentry *dentry;
+	struct nameidata nd;
+
+	name = getname(pathname);
+	if(IS_ERR(name))
+		return PTR_ERR(name);
+
+	error = path_lookup(name, LOOKUP_PARENT, &nd);
+	if (error)
+		goto exit;
+
+	switch(nd.last_type) {
+		case LAST_DOTDOT:
+			error = -ENOTEMPTY;
+			goto exit1;
+		case LAST_DOT:
+			error = -EINVAL;
+			goto exit1;
+		case LAST_ROOT:
+			error = -EBUSY;
+			goto exit1;
+	}
+	down(&nd.dentry->d_inode->i_sem);
+	dentry = lookup_hash(&nd.last, nd.dentry);
+	error = PTR_ERR(dentry);
+	if (!IS_ERR(dentry)) {
+		error = vfs_rmdir(nd.dentry->d_inode, dentry);
+		dput(dentry);
+	}
+	up(&nd.dentry->d_inode->i_sem);
+exit1:
+	path_release(&nd);
+exit:
+	putname(name);
+	return error;
+}
+
+int vfs_unlink(struct inode *dir, struct dentry *dentry)
+{
+	int error = may_delete(dir, dentry, 0);
+
+	if (error)
+		return error;
+
+	if (!dir->i_op || !dir->i_op->unlink)
+		return -EPERM;
+
+	DQUOT_INIT(dir);
+
+	down(&dentry->d_inode->i_sem);
+	if (d_mountpoint(dentry))
+		error = -EBUSY;
+	else {
+		error = security_inode_unlink(dir, dentry);
+		if (!error)
+			error = dir->i_op->unlink(dir, dentry);
+	}
+	up(&dentry->d_inode->i_sem);
+
+	/* We don't d_delete() NFS sillyrenamed files--they still exist. */
+	if (!error && !(dentry->d_flags & DCACHE_NFSFS_RENAMED)) {
+		d_delete(dentry);
+		inode_dir_notify(dir, DN_DELETE);
+	}
+	return error;
+}
+
+/*
+ * Make sure that the actual truncation of the file will occur outside its
+ * directory's i_sem.  Truncate can take a long time if there is a lot of
+ * writeout happening, and we don't want to prevent access to the directory
+ * while waiting on the I/O.
+ */
+asmlinkage long sys_unlink(const char __user * pathname)
+{
+	int error = 0;
+	char * name;
+	struct dentry *dentry;
+	struct nameidata nd;
+	struct inode *inode = NULL;
+
+	name = getname(pathname);
+	if(IS_ERR(name))
+		return PTR_ERR(name);
+
+	error = path_lookup(name, LOOKUP_PARENT, &nd);
+	if (error)
+		goto exit;
+	error = -EISDIR;
+	if (nd.last_type != LAST_NORM)
+		goto exit1;
+	down(&nd.dentry->d_inode->i_sem);
+	dentry = lookup_hash(&nd.last, nd.dentry);
+	error = PTR_ERR(dentry);
+	if (!IS_ERR(dentry)) {
+		/* Why not before? Because we want correct error value */
+		if (nd.last.name[nd.last.len])
+			goto slashes;
+		inode = dentry->d_inode;
+		if (inode)
+			atomic_inc(&inode->i_count);
+		error = vfs_unlink(nd.dentry->d_inode, dentry);
+	exit2:
+		dput(dentry);
+	}
+	up(&nd.dentry->d_inode->i_sem);
+	if (inode)
+		iput(inode);	/* truncate the inode here */
+exit1:
+	path_release(&nd);
+exit:
+	putname(name);
+	return error;
+
+slashes:
+	error = !dentry->d_inode ? -ENOENT :
+		S_ISDIR(dentry->d_inode->i_mode) ? -EISDIR : -ENOTDIR;
+	goto exit2;
+}
+
+int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname, int mode)
+{
+	int error = may_create(dir, dentry, NULL);
+
+	if (error)
+		return error;
+
+	if (!dir->i_op || !dir->i_op->symlink)
+		return -EPERM;
+
+	error = security_inode_symlink(dir, dentry, oldname);
+	if (error)
+		return error;
+
+	DQUOT_INIT(dir);
+	error = dir->i_op->symlink(dir, dentry, oldname);
+	if (!error) {
+		inode_dir_notify(dir, DN_CREATE);
+		security_inode_post_symlink(dir, dentry, oldname);
+	}
+	return error;
+}
+
+asmlinkage long sys_symlink(const char __user * oldname, const char __user * newname)
+{
+	int error = 0;
+	char * from;
+	char * to;
+
+	from = getname(oldname);
+	if(IS_ERR(from))
+		return PTR_ERR(from);
+	to = getname(newname);
+	error = PTR_ERR(to);
+	if (!IS_ERR(to)) {
+		struct dentry *dentry;
+		struct nameidata nd;
+
+		error = path_lookup(to, LOOKUP_PARENT, &nd);
+		if (error)
+			goto out;
+		dentry = lookup_create(&nd, 0);
+		error = PTR_ERR(dentry);
+		if (!IS_ERR(dentry)) {
+			error = vfs_symlink(nd.dentry->d_inode, dentry, from, S_IALLUGO);
+			dput(dentry);
+		}
+		up(&nd.dentry->d_inode->i_sem);
+		path_release(&nd);
+out:
+		putname(to);
+	}
+	putname(from);
+	return error;
+}
+
+int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry)
+{
+	struct inode *inode = old_dentry->d_inode;
+	int error;
+
+	if (!inode)
+		return -ENOENT;
+
+	error = may_create(dir, new_dentry, NULL);
+	if (error)
+		return error;
+
+	if (dir->i_sb != inode->i_sb)
+		return -EXDEV;
+
+	/*
+	 * A link to an append-only or immutable file cannot be created.
+	 */
+	if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
+		return -EPERM;
+	if (!dir->i_op || !dir->i_op->link)
+		return -EPERM;
+	if (S_ISDIR(old_dentry->d_inode->i_mode))
+		return -EPERM;
+
+	error = security_inode_link(old_dentry, dir, new_dentry);
+	if (error)
+		return error;
+
+	down(&old_dentry->d_inode->i_sem);
+	DQUOT_INIT(dir);
+	error = dir->i_op->link(old_dentry, dir, new_dentry);
+	up(&old_dentry->d_inode->i_sem);
+	if (!error) {
+		inode_dir_notify(dir, DN_CREATE);
+		security_inode_post_link(old_dentry, dir, new_dentry);
+	}
+	return error;
+}
+
+/*
+ * Hardlinks are often used in delicate situations.  We avoid
+ * security-related surprises by not following symlinks on the
+ * newname.  --KAB
+ *
+ * We don't follow them on the oldname either to be compatible
+ * with linux 2.0, and to avoid hard-linking to directories
+ * and other special files.  --ADM
+ */
+asmlinkage long sys_link(const char __user * oldname, const char __user * newname)
+{
+	struct dentry *new_dentry;
+	struct nameidata nd, old_nd;
+	int error;
+	char * to;
+
+	to = getname(newname);
+	if (IS_ERR(to))
+		return PTR_ERR(to);
+
+	error = __user_walk(oldname, 0, &old_nd);
+	if (error)
+		goto exit;
+	error = path_lookup(to, LOOKUP_PARENT, &nd);
+	if (error)
+		goto out;
+	error = -EXDEV;
+	if (old_nd.mnt != nd.mnt)
+		goto out_release;
+	new_dentry = lookup_create(&nd, 0);
+	error = PTR_ERR(new_dentry);
+	if (!IS_ERR(new_dentry)) {
+		error = vfs_link(old_nd.dentry, nd.dentry->d_inode, new_dentry);
+		dput(new_dentry);
+	}
+	up(&nd.dentry->d_inode->i_sem);
+out_release:
+	path_release(&nd);
+out:
+	path_release(&old_nd);
+exit:
+	putname(to);
+
+	return error;
+}
+
+/*
+ * The worst of all namespace operations - renaming directory. "Perverted"
+ * doesn't even start to describe it. Somebody in UCB had a heck of a trip...
+ * Problems:
+ *	a) we can get into loop creation. Check is done in is_subdir().
+ *	b) race potential - two innocent renames can create a loop together.
+ *	   That's where 4.4 screws up. Current fix: serialization on
+ *	   sb->s_vfs_rename_sem. We might be more accurate, but that's another
+ *	   story.
+ *	c) we have to lock _three_ objects - parents and victim (if it exists).
+ *	   And that - after we got ->i_sem on parents (until then we don't know
+ *	   whether the target exists).  Solution: try to be smart with locking
+ *	   order for inodes.  We rely on the fact that tree topology may change
+ *	   only under ->s_vfs_rename_sem _and_ that parent of the object we
+ *	   move will be locked.  Thus we can rank directories by the tree
+ *	   (ancestors first) and rank all non-directories after them.
+ *	   That works since everybody except rename does "lock parent, lookup,
+ *	   lock child" and rename is under ->s_vfs_rename_sem.
+ *	   HOWEVER, it relies on the assumption that any object with ->lookup()
+ *	   has no more than 1 dentry.  If "hybrid" objects will ever appear,
+ *	   we'd better make sure that there's no link(2) for them.
+ *	d) some filesystems don't support opened-but-unlinked directories,
+ *	   either because of layout or because they are not ready to deal with
+ *	   all cases correctly. The latter will be fixed (taking this sort of
+ *	   stuff into VFS), but the former is not going away. Solution: the same
+ *	   trick as in rmdir().
+ *	e) conversion from fhandle to dentry may come in the wrong moment - when
+ *	   we are removing the target. Solution: we will have to grab ->i_sem
+ *	   in the fhandle_to_dentry code. [FIXME - current nfsfh.c relies on
+ *	   ->i_sem on parents, which works but leads to some truely excessive
+ *	   locking].
+ */
+int vfs_rename_dir(struct inode *old_dir, struct dentry *old_dentry,
+	       struct inode *new_dir, struct dentry *new_dentry)
+{
+	int error = 0;
+	struct inode *target;
+
+	/*
+	 * If we are going to change the parent - check write permissions,
+	 * we'll need to flip '..'.
+	 */
+	if (new_dir != old_dir) {
+		error = permission(old_dentry->d_inode, MAY_WRITE, NULL);
+		if (error)
+			return error;
+	}
+
+	error = security_inode_rename(old_dir, old_dentry, new_dir, new_dentry);
+	if (error)
+		return error;
+
+	target = new_dentry->d_inode;
+	if (target) {
+		down(&target->i_sem);
+		dentry_unhash(new_dentry);
+	}
+	if (d_mountpoint(old_dentry)||d_mountpoint(new_dentry))
+		error = -EBUSY;
+	else 
+		error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry);
+	if (target) {
+		if (!error)
+			target->i_flags |= S_DEAD;
+		up(&target->i_sem);
+		if (d_unhashed(new_dentry))
+			d_rehash(new_dentry);
+		dput(new_dentry);
+	}
+	if (!error) {
+		d_move(old_dentry,new_dentry);
+		security_inode_post_rename(old_dir, old_dentry,
+					   new_dir, new_dentry);
+	}
+	return error;
+}
+
+int vfs_rename_other(struct inode *old_dir, struct dentry *old_dentry,
+	       struct inode *new_dir, struct dentry *new_dentry)
+{
+	struct inode *target;
+	int error;
+
+	error = security_inode_rename(old_dir, old_dentry, new_dir, new_dentry);
+	if (error)
+		return error;
+
+	dget(new_dentry);
+	target = new_dentry->d_inode;
+	if (target)
+		down(&target->i_sem);
+	if (d_mountpoint(old_dentry)||d_mountpoint(new_dentry))
+		error = -EBUSY;
+	else
+		error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry);
+	if (!error) {
+		/* The following d_move() should become unconditional */
+		if (!(old_dir->i_sb->s_type->fs_flags & FS_ODD_RENAME))
+			d_move(old_dentry, new_dentry);
+		security_inode_post_rename(old_dir, old_dentry, new_dir, new_dentry);
+	}
+	if (target)
+		up(&target->i_sem);
+	dput(new_dentry);
+	return error;
+}
+
+int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
+	       struct inode *new_dir, struct dentry *new_dentry)
+{
+	int error;
+	int is_dir = S_ISDIR(old_dentry->d_inode->i_mode);
+
+	if (old_dentry->d_inode == new_dentry->d_inode)
+ 		return 0;
+ 
+	error = may_delete(old_dir, old_dentry, is_dir);
+	if (error)
+		return error;
+
+	if (!new_dentry->d_inode)
+		error = may_create(new_dir, new_dentry, NULL);
+	else
+		error = may_delete(new_dir, new_dentry, is_dir);
+	if (error)
+		return error;
+
+	if (!old_dir->i_op || !old_dir->i_op->rename)
+		return -EPERM;
+
+	DQUOT_INIT(old_dir);
+	DQUOT_INIT(new_dir);
+
+	if (is_dir)
+		error = vfs_rename_dir(old_dir,old_dentry,new_dir,new_dentry);
+	else
+		error = vfs_rename_other(old_dir,old_dentry,new_dir,new_dentry);
+	if (!error) {
+		if (old_dir == new_dir)
+			inode_dir_notify(old_dir, DN_RENAME);
+		else {
+			inode_dir_notify(old_dir, DN_DELETE);
+			inode_dir_notify(new_dir, DN_CREATE);
+		}
+	}
+	return error;
+}
+
+static inline int do_rename(const char * oldname, const char * newname)
+{
+	int error = 0;
+	struct dentry * old_dir, * new_dir;
+	struct dentry * old_dentry, *new_dentry;
+	struct dentry * trap;
+	struct nameidata oldnd, newnd;
+
+	error = path_lookup(oldname, LOOKUP_PARENT, &oldnd);
+	if (error)
+		goto exit;
+
+	error = path_lookup(newname, LOOKUP_PARENT, &newnd);
+	if (error)
+		goto exit1;
+
+	error = -EXDEV;
+	if (oldnd.mnt != newnd.mnt)
+		goto exit2;
+
+	old_dir = oldnd.dentry;
+	error = -EBUSY;
+	if (oldnd.last_type != LAST_NORM)
+		goto exit2;
+
+	new_dir = newnd.dentry;
+	if (newnd.last_type != LAST_NORM)
+		goto exit2;
+
+	trap = lock_rename(new_dir, old_dir);
+
+	old_dentry = lookup_hash(&oldnd.last, old_dir);
+	error = PTR_ERR(old_dentry);
+	if (IS_ERR(old_dentry))
+		goto exit3;
+	/* source must exist */
+	error = -ENOENT;
+	if (!old_dentry->d_inode)
+		goto exit4;
+	/* unless the source is a directory trailing slashes give -ENOTDIR */
+	if (!S_ISDIR(old_dentry->d_inode->i_mode)) {
+		error = -ENOTDIR;
+		if (oldnd.last.name[oldnd.last.len])
+			goto exit4;
+		if (newnd.last.name[newnd.last.len])
+			goto exit4;
+	}
+	/* source should not be ancestor of target */
+	error = -EINVAL;
+	if (old_dentry == trap)
+		goto exit4;
+	new_dentry = lookup_hash(&newnd.last, new_dir);
+	error = PTR_ERR(new_dentry);
+	if (IS_ERR(new_dentry))
+		goto exit4;
+	/* target should not be an ancestor of source */
+	error = -ENOTEMPTY;
+	if (new_dentry == trap)
+		goto exit5;
+
+	error = vfs_rename(old_dir->d_inode, old_dentry,
+				   new_dir->d_inode, new_dentry);
+exit5:
+	dput(new_dentry);
+exit4:
+	dput(old_dentry);
+exit3:
+	unlock_rename(new_dir, old_dir);
+exit2:
+	path_release(&newnd);
+exit1:
+	path_release(&oldnd);
+exit:
+	return error;
+}
+
+asmlinkage long sys_rename(const char __user * oldname, const char __user * newname)
+{
+	int error;
+	char * from;
+	char * to;
+
+	from = getname(oldname);
+	if(IS_ERR(from))
+		return PTR_ERR(from);
+	to = getname(newname);
+	error = PTR_ERR(to);
+	if (!IS_ERR(to)) {
+		error = do_rename(from,to);
+		putname(to);
+	}
+	putname(from);
+	return error;
+}
+
+int vfs_readlink(struct dentry *dentry, char __user *buffer, int buflen, const char *link)
+{
+	int len;
+
+	len = PTR_ERR(link);
+	if (IS_ERR(link))
+		goto out;
+
+	len = strlen(link);
+	if (len > (unsigned) buflen)
+		len = buflen;
+	if (copy_to_user(buffer, link, len))
+		len = -EFAULT;
+out:
+	return len;
+}
+
+/*
+ * A helper for ->readlink().  This should be used *ONLY* for symlinks that
+ * have ->follow_link() touching nd only in nd_set_link().  Using (or not
+ * using) it for any given inode is up to filesystem.
+ */
+int generic_readlink(struct dentry *dentry, char __user *buffer, int buflen)
+{
+	struct nameidata nd;
+	int res;
+	nd.depth = 0;
+	res = dentry->d_inode->i_op->follow_link(dentry, &nd);
+	if (!res) {
+		res = vfs_readlink(dentry, buffer, buflen, nd_get_link(&nd));
+		if (dentry->d_inode->i_op->put_link)
+			dentry->d_inode->i_op->put_link(dentry, &nd);
+	}
+	return res;
+}
+
+int vfs_follow_link(struct nameidata *nd, const char *link)
+{
+	return __vfs_follow_link(nd, link);
+}
+
+/* get the link contents into pagecache */
+static char *page_getlink(struct dentry * dentry, struct page **ppage)
+{
+	struct page * page;
+	struct address_space *mapping = dentry->d_inode->i_mapping;
+	page = read_cache_page(mapping, 0, (filler_t *)mapping->a_ops->readpage,
+				NULL);
+	if (IS_ERR(page))
+		goto sync_fail;
+	wait_on_page_locked(page);
+	if (!PageUptodate(page))
+		goto async_fail;
+	*ppage = page;
+	return kmap(page);
+
+async_fail:
+	page_cache_release(page);
+	return ERR_PTR(-EIO);
+
+sync_fail:
+	return (char*)page;
+}
+
+int page_readlink(struct dentry *dentry, char __user *buffer, int buflen)
+{
+	struct page *page = NULL;
+	char *s = page_getlink(dentry, &page);
+	int res = vfs_readlink(dentry,buffer,buflen,s);
+	if (page) {
+		kunmap(page);
+		page_cache_release(page);
+	}
+	return res;
+}
+
+int page_follow_link_light(struct dentry *dentry, struct nameidata *nd)
+{
+	struct page *page;
+	nd_set_link(nd, page_getlink(dentry, &page));
+	return 0;
+}
+
+void page_put_link(struct dentry *dentry, struct nameidata *nd)
+{
+	if (!IS_ERR(nd_get_link(nd))) {
+		struct page *page;
+		page = find_get_page(dentry->d_inode->i_mapping, 0);
+		if (!page)
+			BUG();
+		kunmap(page);
+		page_cache_release(page);
+		page_cache_release(page);
+	}
+}
+
+int page_symlink(struct inode *inode, const char *symname, int len)
+{
+	struct address_space *mapping = inode->i_mapping;
+	struct page *page = grab_cache_page(mapping, 0);
+	int err = -ENOMEM;
+	char *kaddr;
+
+	if (!page)
+		goto fail;
+	err = mapping->a_ops->prepare_write(NULL, page, 0, len-1);
+	if (err)
+		goto fail_map;
+	kaddr = kmap_atomic(page, KM_USER0);
+	memcpy(kaddr, symname, len-1);
+	kunmap_atomic(kaddr, KM_USER0);
+	mapping->a_ops->commit_write(NULL, page, 0, len-1);
+	/*
+	 * Notice that we are _not_ going to block here - end of page is
+	 * unmapped, so this will only try to map the rest of page, see
+	 * that it is unmapped (typically even will not look into inode -
+	 * ->i_size will be enough for everything) and zero it out.
+	 * OTOH it's obviously correct and should make the page up-to-date.
+	 */
+	if (!PageUptodate(page)) {
+		err = mapping->a_ops->readpage(NULL, page);
+		wait_on_page_locked(page);
+	} else {
+		unlock_page(page);
+	}
+	page_cache_release(page);
+	if (err < 0)
+		goto fail;
+	mark_inode_dirty(inode);
+	return 0;
+fail_map:
+	unlock_page(page);
+	page_cache_release(page);
+fail:
+	return err;
+}
+
+struct inode_operations page_symlink_inode_operations = {
+	.readlink	= generic_readlink,
+	.follow_link	= page_follow_link_light,
+	.put_link	= page_put_link,
+};
+
+EXPORT_SYMBOL(__user_walk);
+EXPORT_SYMBOL(follow_down);
+EXPORT_SYMBOL(follow_up);
+EXPORT_SYMBOL(get_write_access); /* binfmt_aout */
+EXPORT_SYMBOL(getname);
+EXPORT_SYMBOL(lock_rename);
+EXPORT_SYMBOL(lookup_hash);
+EXPORT_SYMBOL(lookup_one_len);
+EXPORT_SYMBOL(page_follow_link_light);
+EXPORT_SYMBOL(page_put_link);
+EXPORT_SYMBOL(page_readlink);
+EXPORT_SYMBOL(page_symlink);
+EXPORT_SYMBOL(page_symlink_inode_operations);
+EXPORT_SYMBOL(path_lookup);
+EXPORT_SYMBOL(path_release);
+EXPORT_SYMBOL(path_walk);
+EXPORT_SYMBOL(permission);
+EXPORT_SYMBOL(unlock_rename);
+EXPORT_SYMBOL(vfs_create);
+EXPORT_SYMBOL(vfs_follow_link);
+EXPORT_SYMBOL(vfs_link);
+EXPORT_SYMBOL(vfs_mkdir);
+EXPORT_SYMBOL(vfs_mknod);
+EXPORT_SYMBOL(generic_permission);
+EXPORT_SYMBOL(vfs_readlink);
+EXPORT_SYMBOL(vfs_rename);
+EXPORT_SYMBOL(vfs_rmdir);
+EXPORT_SYMBOL(vfs_symlink);
+EXPORT_SYMBOL(vfs_unlink);
+EXPORT_SYMBOL(dentry_unhash);
+EXPORT_SYMBOL(generic_readlink);
diff -Nrub linux-2.6.11.7-orig/fs/namespace.c linux-2.6.11.7-snare/fs/namespace.c
--- linux-2.6.11.7-orig/fs/namespace.c	2005-04-07 14:57:46.000000000 -0400
+++ linux-2.6.11.7-snare/fs/namespace.c	2005-04-22 11:19:47.192666072 -0400
@@ -19,6 +19,7 @@
 #include <linux/module.h>
 #include <linux/seq_file.h>
 #include <linux/namespace.h>
+#include <linux/saudit.h>
 #include <linux/namei.h>
 #include <linux/security.h>
 #include <linux/mount.h>
@@ -487,6 +488,7 @@
 dput_and_out:
 	path_release_on_umount(&nd);
 out:
+	saudit_umount((char *)NULL,name,flags,retval);
 	return retval;
 }
 
@@ -1156,17 +1158,23 @@
 	char *dir_page;
 
 	retval = copy_mount_options (type, &type_page);
-	if (retval < 0)
+	if (retval < 0) {
+		saudit_mount((char *)NULL,dev_name,(char *)NULL,dir_name,flags,retval);
 		return retval;
+	}
 
 	dir_page = getname(dir_name);
 	retval = PTR_ERR(dir_page);
-	if (IS_ERR(dir_page))
+	if (IS_ERR(dir_page)) {
+		saudit_mount((char *)NULL,dev_name,(char *)NULL,dir_name,flags,retval);
 		goto out1;
+	}
 
 	retval = copy_mount_options (dev_name, &dev_page);
-	if (retval < 0)
+	if (retval < 0) {
+		saudit_mount((char *)NULL,dev_name,dir_page,(char *)NULL,flags,retval);
 		goto out2;
+	}
 
 	retval = copy_mount_options (data, &data_page);
 	if (retval < 0)
@@ -1179,6 +1187,7 @@
 	free_page(data_page);
 
 out3:
+	saudit_mount((char *)dev_page,(char *)NULL,dir_page,(char *)NULL,flags,retval);
 	free_page(dev_page);
 out2:
 	putname(dir_page);
diff -Nrub linux-2.6.11.7-orig/fs/open.c linux-2.6.11.7-snare/fs/open.c
--- linux-2.6.11.7-orig/fs/open.c	2005-04-07 14:57:09.000000000 -0400
+++ linux-2.6.11.7-snare/fs/open.c	2005-04-22 11:19:47.193665920 -0400
@@ -14,6 +14,7 @@
 #include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/tty.h>
+#include <linux/saudit.h>
 #include <linux/namei.h>
 #include <linux/backing-dev.h>
 #include <linux/security.h>
@@ -217,12 +218,16 @@
 	int error;
 
 	error = -EINVAL;
-	if (length < 0)	/* sorry, but loff_t says... */
+	if (length < 0) {	/* sorry, but loff_t says... */
+		saudit_truncate((char *)NULL,path,length,error);
 		goto out;
+	}
 
 	error = user_path_walk(path, &nd);
-	if (error)
+	if (error) {
+		saudit_truncate((char *)NULL,path,length,error);
 		goto out;
+	}
 	inode = nd.dentry->d_inode;
 
 	/* For directories it's -EISDIR, for other non-regulars - -EINVAL */
@@ -267,6 +272,7 @@
 dput_and_out:
 	path_release(&nd);
 out:
+	saudit_truncate((char *)NULL,path,length,error);
 	return error;
 }
 
@@ -572,8 +578,10 @@
 	int error;
 
 	error = __user_walk(filename, LOOKUP_FOLLOW | LOOKUP_DIRECTORY | LOOKUP_NOALT, &nd);
-	if (error)
+	if (error) {
+		saudit_chroot((char *)NULL,filename,error);
 		goto out;
+	}
 
 	error = permission(nd.dentry->d_inode,MAY_EXEC,&nd);
 	if (error)
@@ -589,6 +597,7 @@
 dput_and_out:
 	path_release(&nd);
 out:
+	saudit_chroot((char *)NULL,filename,error);
 	return error;
 }
 
@@ -635,8 +644,10 @@
 	struct iattr newattrs;
 
 	error = user_path_walk(filename, &nd);
-	if (error)
+	if (error) {
+		saudit_chmod((char *)NULL,filename,mode,error);
 		goto out;
+	}
 	inode = nd.dentry->d_inode;
 
 	error = -EROFS;
@@ -658,6 +669,7 @@
 dput_and_out:
 	path_release(&nd);
 out:
+	saudit_chmod((char *)NULL,filename,mode,error);
 	return error;
 }
 
@@ -706,6 +718,7 @@
 		error = chown_common(nd.dentry, user, group);
 		path_release(&nd);
 	}
+	saudit_chown((char *)NULL,filename,user,group,error);
 	return error;
 }
 
@@ -719,6 +732,7 @@
 		error = chown_common(nd.dentry, user, group);
 		path_release(&nd);
 	}
+	saudit_chown((char *)NULL,filename,user,group,error);
 	return error;
 }
 
@@ -940,16 +954,21 @@
 #endif
 	tmp = getname(filename);
 	fd = PTR_ERR(tmp);
-	if (!IS_ERR(tmp)) {
+	if (IS_ERR(tmp)) {
+		saudit_open((char *)NULL,(char *)NULL,flags,mode,fd);
+	} else {
 		fd = get_unused_fd();
 		if (fd >= 0) {
 			struct file *f = filp_open(tmp, flags, mode);
 			error = PTR_ERR(f);
-			if (IS_ERR(f))
+			if (IS_ERR(f)) {
+				saudit_open(tmp,(char *)NULL,flags,mode,fd);
 				goto out_error;
+			}
 			fd_install(fd, f);
 		}
 out:
+		saudit_open(tmp,(char *)NULL,flags,mode,fd);
 		putname(tmp);
 	}
 	return fd;
diff -Nrub linux-2.6.11.7-orig/fs/open.c.orig linux-2.6.11.7-snare/fs/open.c.orig
--- linux-2.6.11.7-orig/fs/open.c.orig	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.11.7-snare/fs/open.c.orig	2005-04-07 14:57:09.000000000 -0400
@@ -0,0 +1,1076 @@
+/*
+ *  linux/fs/open.c
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ */
+
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/utime.h>
+#include <linux/file.h>
+#include <linux/smp_lock.h>
+#include <linux/quotaops.h>
+#include <linux/dnotify.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/namei.h>
+#include <linux/backing-dev.h>
+#include <linux/security.h>
+#include <linux/mount.h>
+#include <linux/vfs.h>
+#include <asm/uaccess.h>
+#include <linux/fs.h>
+#include <linux/pagemap.h>
+#include <linux/syscalls.h>
+
+#include <asm/unistd.h>
+
+int vfs_statfs(struct super_block *sb, struct kstatfs *buf)
+{
+	int retval = -ENODEV;
+
+	if (sb) {
+		retval = -ENOSYS;
+		if (sb->s_op->statfs) {
+			memset(buf, 0, sizeof(*buf));
+			retval = security_sb_statfs(sb);
+			if (retval)
+				return retval;
+			retval = sb->s_op->statfs(sb, buf);
+			if (retval == 0 && buf->f_frsize == 0)
+				buf->f_frsize = buf->f_bsize;
+		}
+	}
+	return retval;
+}
+
+EXPORT_SYMBOL(vfs_statfs);
+
+static int vfs_statfs_native(struct super_block *sb, struct statfs *buf)
+{
+	struct kstatfs st;
+	int retval;
+
+	retval = vfs_statfs(sb, &st);
+	if (retval)
+		return retval;
+
+	if (sizeof(*buf) == sizeof(st))
+		memcpy(buf, &st, sizeof(st));
+	else {
+		if (sizeof buf->f_blocks == 4) {
+			if ((st.f_blocks | st.f_bfree | st.f_bavail) &
+			    0xffffffff00000000ULL)
+				return -EOVERFLOW;
+			/*
+			 * f_files and f_ffree may be -1; it's okay to stuff
+			 * that into 32 bits
+			 */
+			if (st.f_files != -1 &&
+			    (st.f_files & 0xffffffff00000000ULL))
+				return -EOVERFLOW;
+			if (st.f_ffree != -1 &&
+			    (st.f_ffree & 0xffffffff00000000ULL))
+				return -EOVERFLOW;
+		}
+
+		buf->f_type = st.f_type;
+		buf->f_bsize = st.f_bsize;
+		buf->f_blocks = st.f_blocks;
+		buf->f_bfree = st.f_bfree;
+		buf->f_bavail = st.f_bavail;
+		buf->f_files = st.f_files;
+		buf->f_ffree = st.f_ffree;
+		buf->f_fsid = st.f_fsid;
+		buf->f_namelen = st.f_namelen;
+		buf->f_frsize = st.f_frsize;
+		memset(buf->f_spare, 0, sizeof(buf->f_spare));
+	}
+	return 0;
+}
+
+static int vfs_statfs64(struct super_block *sb, struct statfs64 *buf)
+{
+	struct kstatfs st;
+	int retval;
+
+	retval = vfs_statfs(sb, &st);
+	if (retval)
+		return retval;
+
+	if (sizeof(*buf) == sizeof(st))
+		memcpy(buf, &st, sizeof(st));
+	else {
+		buf->f_type = st.f_type;
+		buf->f_bsize = st.f_bsize;
+		buf->f_blocks = st.f_blocks;
+		buf->f_bfree = st.f_bfree;
+		buf->f_bavail = st.f_bavail;
+		buf->f_files = st.f_files;
+		buf->f_ffree = st.f_ffree;
+		buf->f_fsid = st.f_fsid;
+		buf->f_namelen = st.f_namelen;
+		buf->f_frsize = st.f_frsize;
+		memset(buf->f_spare, 0, sizeof(buf->f_spare));
+	}
+	return 0;
+}
+
+asmlinkage long sys_statfs(const char __user * path, struct statfs __user * buf)
+{
+	struct nameidata nd;
+	int error;
+
+	error = user_path_walk(path, &nd);
+	if (!error) {
+		struct statfs tmp;
+		error = vfs_statfs_native(nd.dentry->d_inode->i_sb, &tmp);
+		if (!error && copy_to_user(buf, &tmp, sizeof(tmp)))
+			error = -EFAULT;
+		path_release(&nd);
+	}
+	return error;
+}
+
+
+asmlinkage long sys_statfs64(const char __user *path, size_t sz, struct statfs64 __user *buf)
+{
+	struct nameidata nd;
+	long error;
+
+	if (sz != sizeof(*buf))
+		return -EINVAL;
+	error = user_path_walk(path, &nd);
+	if (!error) {
+		struct statfs64 tmp;
+		error = vfs_statfs64(nd.dentry->d_inode->i_sb, &tmp);
+		if (!error && copy_to_user(buf, &tmp, sizeof(tmp)))
+			error = -EFAULT;
+		path_release(&nd);
+	}
+	return error;
+}
+
+
+asmlinkage long sys_fstatfs(unsigned int fd, struct statfs __user * buf)
+{
+	struct file * file;
+	struct statfs tmp;
+	int error;
+
+	error = -EBADF;
+	file = fget(fd);
+	if (!file)
+		goto out;
+	error = vfs_statfs_native(file->f_dentry->d_inode->i_sb, &tmp);
+	if (!error && copy_to_user(buf, &tmp, sizeof(tmp)))
+		error = -EFAULT;
+	fput(file);
+out:
+	return error;
+}
+
+asmlinkage long sys_fstatfs64(unsigned int fd, size_t sz, struct statfs64 __user *buf)
+{
+	struct file * file;
+	struct statfs64 tmp;
+	int error;
+
+	if (sz != sizeof(*buf))
+		return -EINVAL;
+
+	error = -EBADF;
+	file = fget(fd);
+	if (!file)
+		goto out;
+	error = vfs_statfs64(file->f_dentry->d_inode->i_sb, &tmp);
+	if (!error && copy_to_user(buf, &tmp, sizeof(tmp)))
+		error = -EFAULT;
+	fput(file);
+out:
+	return error;
+}
+
+int do_truncate(struct dentry *dentry, loff_t length)
+{
+	int err;
+	struct iattr newattrs;
+
+	/* Not pretty: "inode->i_size" shouldn't really be signed. But it is. */
+	if (length < 0)
+		return -EINVAL;
+
+	newattrs.ia_size = length;
+	newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME;
+
+	down(&dentry->d_inode->i_sem);
+	err = notify_change(dentry, &newattrs);
+	up(&dentry->d_inode->i_sem);
+	return err;
+}
+
+static inline long do_sys_truncate(const char __user * path, loff_t length)
+{
+	struct nameidata nd;
+	struct inode * inode;
+	int error;
+
+	error = -EINVAL;
+	if (length < 0)	/* sorry, but loff_t says... */
+		goto out;
+
+	error = user_path_walk(path, &nd);
+	if (error)
+		goto out;
+	inode = nd.dentry->d_inode;
+
+	/* For directories it's -EISDIR, for other non-regulars - -EINVAL */
+	error = -EISDIR;
+	if (S_ISDIR(inode->i_mode))
+		goto dput_and_out;
+
+	error = -EINVAL;
+	if (!S_ISREG(inode->i_mode))
+		goto dput_and_out;
+
+	error = permission(inode,MAY_WRITE,&nd);
+	if (error)
+		goto dput_and_out;
+
+	error = -EROFS;
+	if (IS_RDONLY(inode))
+		goto dput_and_out;
+
+	error = -EPERM;
+	if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
+		goto dput_and_out;
+
+	/*
+	 * Make sure that there are no leases.
+	 */
+	error = break_lease(inode, FMODE_WRITE);
+	if (error)
+		goto dput_and_out;
+
+	error = get_write_access(inode);
+	if (error)
+		goto dput_and_out;
+
+	error = locks_verify_truncate(inode, NULL, length);
+	if (!error) {
+		DQUOT_INIT(inode);
+		error = do_truncate(nd.dentry, length);
+	}
+	put_write_access(inode);
+
+dput_and_out:
+	path_release(&nd);
+out:
+	return error;
+}
+
+asmlinkage long sys_truncate(const char __user * path, unsigned long length)
+{
+	/* on 32-bit boxen it will cut the range 2^31--2^32-1 off */
+	return do_sys_truncate(path, (long)length);
+}
+
+static inline long do_sys_ftruncate(unsigned int fd, loff_t length, int small)
+{
+	struct inode * inode;
+	struct dentry *dentry;
+	struct file * file;
+	int error;
+
+	error = -EINVAL;
+	if (length < 0)
+		goto out;
+	error = -EBADF;
+	file = fget(fd);
+	if (!file)
+		goto out;
+
+	/* explicitly opened as large or we are on 64-bit box */
+	if (file->f_flags & O_LARGEFILE)
+		small = 0;
+
+	dentry = file->f_dentry;
+	inode = dentry->d_inode;
+	error = -EINVAL;
+	if (!S_ISREG(inode->i_mode) || !(file->f_mode & FMODE_WRITE))
+		goto out_putf;
+
+	error = -EINVAL;
+	/* Cannot ftruncate over 2^31 bytes without large file support */
+	if (small && length > MAX_NON_LFS)
+		goto out_putf;
+
+	error = -EPERM;
+	if (IS_APPEND(inode))
+		goto out_putf;
+
+	error = locks_verify_truncate(inode, file, length);
+	if (!error)
+		error = do_truncate(dentry, length);
+out_putf:
+	fput(file);
+out:
+	return error;
+}
+
+asmlinkage long sys_ftruncate(unsigned int fd, unsigned long length)
+{
+	return do_sys_ftruncate(fd, length, 1);
+}
+
+/* LFS versions of truncate are only needed on 32 bit machines */
+#if BITS_PER_LONG == 32
+asmlinkage long sys_truncate64(const char __user * path, loff_t length)
+{
+	return do_sys_truncate(path, length);
+}
+
+asmlinkage long sys_ftruncate64(unsigned int fd, loff_t length)
+{
+	return do_sys_ftruncate(fd, length, 0);
+}
+#endif
+
+#ifdef __ARCH_WANT_SYS_UTIME
+
+/*
+ * sys_utime() can be implemented in user-level using sys_utimes().
+ * Is this for backwards compatibility?  If so, why not move it
+ * into the appropriate arch directory (for those architectures that
+ * need it).
+ */
+
+/* If times==NULL, set access and modification to current time,
+ * must be owner or have write permission.
+ * Else, update from *times, must be owner or super user.
+ */
+asmlinkage long sys_utime(char __user * filename, struct utimbuf __user * times)
+{
+	int error;
+	struct nameidata nd;
+	struct inode * inode;
+	struct iattr newattrs;
+
+	error = user_path_walk(filename, &nd);
+	if (error)
+		goto out;
+	inode = nd.dentry->d_inode;
+
+	error = -EROFS;
+	if (IS_RDONLY(inode))
+		goto dput_and_out;
+
+	/* Don't worry, the checks are done in inode_change_ok() */
+	newattrs.ia_valid = ATTR_CTIME | ATTR_MTIME | ATTR_ATIME;
+	if (times) {
+		error = -EPERM;
+		if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
+			goto dput_and_out;
+
+		error = get_user(newattrs.ia_atime.tv_sec, &times->actime);
+		newattrs.ia_atime.tv_nsec = 0;
+		if (!error) 
+			error = get_user(newattrs.ia_mtime.tv_sec, &times->modtime);
+		newattrs.ia_mtime.tv_nsec = 0;
+		if (error)
+			goto dput_and_out;
+
+		newattrs.ia_valid |= ATTR_ATIME_SET | ATTR_MTIME_SET;
+	} else {
+                error = -EACCES;
+                if (IS_IMMUTABLE(inode))
+                        goto dput_and_out;
+
+		if (current->fsuid != inode->i_uid &&
+		    (error = permission(inode,MAY_WRITE,&nd)) != 0)
+			goto dput_and_out;
+	}
+	down(&inode->i_sem);
+	error = notify_change(nd.dentry, &newattrs);
+	up(&inode->i_sem);
+dput_and_out:
+	path_release(&nd);
+out:
+	return error;
+}
+
+#endif
+
+/* If times==NULL, set access and modification to current time,
+ * must be owner or have write permission.
+ * Else, update from *times, must be owner or super user.
+ */
+long do_utimes(char __user * filename, struct timeval * times)
+{
+	int error;
+	struct nameidata nd;
+	struct inode * inode;
+	struct iattr newattrs;
+
+	error = user_path_walk(filename, &nd);
+
+	if (error)
+		goto out;
+	inode = nd.dentry->d_inode;
+
+	error = -EROFS;
+	if (IS_RDONLY(inode))
+		goto dput_and_out;
+
+	/* Don't worry, the checks are done in inode_change_ok() */
+	newattrs.ia_valid = ATTR_CTIME | ATTR_MTIME | ATTR_ATIME;
+	if (times) {
+		error = -EPERM;
+                if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
+                        goto dput_and_out;
+
+		newattrs.ia_atime.tv_sec = times[0].tv_sec;
+		newattrs.ia_atime.tv_nsec = times[0].tv_usec * 1000;
+		newattrs.ia_mtime.tv_sec = times[1].tv_sec;
+		newattrs.ia_mtime.tv_nsec = times[1].tv_usec * 1000;
+		newattrs.ia_valid |= ATTR_ATIME_SET | ATTR_MTIME_SET;
+	} else {
+		error = -EACCES;
+                if (IS_IMMUTABLE(inode))
+                        goto dput_and_out;
+
+		if (current->fsuid != inode->i_uid &&
+		    (error = permission(inode,MAY_WRITE,&nd)) != 0)
+			goto dput_and_out;
+	}
+	down(&inode->i_sem);
+	error = notify_change(nd.dentry, &newattrs);
+	up(&inode->i_sem);
+dput_and_out:
+	path_release(&nd);
+out:
+	return error;
+}
+
+asmlinkage long sys_utimes(char __user * filename, struct timeval __user * utimes)
+{
+	struct timeval times[2];
+
+	if (utimes && copy_from_user(&times, utimes, sizeof(times)))
+		return -EFAULT;
+	return do_utimes(filename, utimes ? times : NULL);
+}
+
+
+/*
+ * access() needs to use the real uid/gid, not the effective uid/gid.
+ * We do this by temporarily clearing all FS-related capabilities and
+ * switching the fsuid/fsgid around to the real ones.
+ */
+asmlinkage long sys_access(const char __user * filename, int mode)
+{
+	struct nameidata nd;
+	int old_fsuid, old_fsgid;
+	kernel_cap_t old_cap;
+	int res;
+
+	if (mode & ~S_IRWXO)	/* where's F_OK, X_OK, W_OK, R_OK? */
+		return -EINVAL;
+
+	old_fsuid = current->fsuid;
+	old_fsgid = current->fsgid;
+	old_cap = current->cap_effective;
+
+	current->fsuid = current->uid;
+	current->fsgid = current->gid;
+
+	/*
+	 * Clear the capabilities if we switch to a non-root user
+	 *
+	 * FIXME: There is a race here against sys_capset.  The
+	 * capabilities can change yet we will restore the old
+	 * value below.  We should hold task_capabilities_lock,
+	 * but we cannot because user_path_walk can sleep.
+	 */
+	if (current->uid)
+		cap_clear(current->cap_effective);
+	else
+		current->cap_effective = current->cap_permitted;
+
+	res = __user_walk(filename, LOOKUP_FOLLOW|LOOKUP_ACCESS, &nd);
+	if (!res) {
+		res = permission(nd.dentry->d_inode, mode, &nd);
+		/* SuS v2 requires we report a read only fs too */
+		if(!res && (mode & S_IWOTH) && IS_RDONLY(nd.dentry->d_inode)
+		   && !special_file(nd.dentry->d_inode->i_mode))
+			res = -EROFS;
+		path_release(&nd);
+	}
+
+	current->fsuid = old_fsuid;
+	current->fsgid = old_fsgid;
+	current->cap_effective = old_cap;
+
+	return res;
+}
+
+asmlinkage long sys_chdir(const char __user * filename)
+{
+	struct nameidata nd;
+	int error;
+
+	error = __user_walk(filename, LOOKUP_FOLLOW|LOOKUP_DIRECTORY, &nd);
+	if (error)
+		goto out;
+
+	error = permission(nd.dentry->d_inode,MAY_EXEC,&nd);
+	if (error)
+		goto dput_and_out;
+
+	set_fs_pwd(current->fs, nd.mnt, nd.dentry);
+
+dput_and_out:
+	path_release(&nd);
+out:
+	return error;
+}
+
+asmlinkage long sys_fchdir(unsigned int fd)
+{
+	struct file *file;
+	struct dentry *dentry;
+	struct inode *inode;
+	struct vfsmount *mnt;
+	int error;
+
+	error = -EBADF;
+	file = fget(fd);
+	if (!file)
+		goto out;
+
+	dentry = file->f_dentry;
+	mnt = file->f_vfsmnt;
+	inode = dentry->d_inode;
+
+	error = -ENOTDIR;
+	if (!S_ISDIR(inode->i_mode))
+		goto out_putf;
+
+	error = permission(inode, MAY_EXEC, NULL);
+	if (!error)
+		set_fs_pwd(current->fs, mnt, dentry);
+out_putf:
+	fput(file);
+out:
+	return error;
+}
+
+asmlinkage long sys_chroot(const char __user * filename)
+{
+	struct nameidata nd;
+	int error;
+
+	error = __user_walk(filename, LOOKUP_FOLLOW | LOOKUP_DIRECTORY | LOOKUP_NOALT, &nd);
+	if (error)
+		goto out;
+
+	error = permission(nd.dentry->d_inode,MAY_EXEC,&nd);
+	if (error)
+		goto dput_and_out;
+
+	error = -EPERM;
+	if (!capable(CAP_SYS_CHROOT))
+		goto dput_and_out;
+
+	set_fs_root(current->fs, nd.mnt, nd.dentry);
+	set_fs_altroot();
+	error = 0;
+dput_and_out:
+	path_release(&nd);
+out:
+	return error;
+}
+
+asmlinkage long sys_fchmod(unsigned int fd, mode_t mode)
+{
+	struct inode * inode;
+	struct dentry * dentry;
+	struct file * file;
+	int err = -EBADF;
+	struct iattr newattrs;
+
+	file = fget(fd);
+	if (!file)
+		goto out;
+
+	dentry = file->f_dentry;
+	inode = dentry->d_inode;
+
+	err = -EROFS;
+	if (IS_RDONLY(inode))
+		goto out_putf;
+	err = -EPERM;
+	if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
+		goto out_putf;
+	down(&inode->i_sem);
+	if (mode == (mode_t) -1)
+		mode = inode->i_mode;
+	newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
+	newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
+	err = notify_change(dentry, &newattrs);
+	up(&inode->i_sem);
+
+out_putf:
+	fput(file);
+out:
+	return err;
+}
+
+asmlinkage long sys_chmod(const char __user * filename, mode_t mode)
+{
+	struct nameidata nd;
+	struct inode * inode;
+	int error;
+	struct iattr newattrs;
+
+	error = user_path_walk(filename, &nd);
+	if (error)
+		goto out;
+	inode = nd.dentry->d_inode;
+
+	error = -EROFS;
+	if (IS_RDONLY(inode))
+		goto dput_and_out;
+
+	error = -EPERM;
+	if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
+		goto dput_and_out;
+
+	down(&inode->i_sem);
+	if (mode == (mode_t) -1)
+		mode = inode->i_mode;
+	newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
+	newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
+	error = notify_change(nd.dentry, &newattrs);
+	up(&inode->i_sem);
+
+dput_and_out:
+	path_release(&nd);
+out:
+	return error;
+}
+
+static int chown_common(struct dentry * dentry, uid_t user, gid_t group)
+{
+	struct inode * inode;
+	int error;
+	struct iattr newattrs;
+
+	error = -ENOENT;
+	if (!(inode = dentry->d_inode)) {
+		printk(KERN_ERR "chown_common: NULL inode\n");
+		goto out;
+	}
+	error = -EROFS;
+	if (IS_RDONLY(inode))
+		goto out;
+	error = -EPERM;
+	if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
+		goto out;
+	newattrs.ia_valid =  ATTR_CTIME;
+	if (user != (uid_t) -1) {
+		newattrs.ia_valid |= ATTR_UID;
+		newattrs.ia_uid = user;
+	}
+	if (group != (gid_t) -1) {
+		newattrs.ia_valid |= ATTR_GID;
+		newattrs.ia_gid = group;
+	}
+	if (!S_ISDIR(inode->i_mode))
+		newattrs.ia_valid |= ATTR_KILL_SUID|ATTR_KILL_SGID;
+	down(&inode->i_sem);
+	error = notify_change(dentry, &newattrs);
+	up(&inode->i_sem);
+out:
+	return error;
+}
+
+asmlinkage long sys_chown(const char __user * filename, uid_t user, gid_t group)
+{
+	struct nameidata nd;
+	int error;
+
+	error = user_path_walk(filename, &nd);
+	if (!error) {
+		error = chown_common(nd.dentry, user, group);
+		path_release(&nd);
+	}
+	return error;
+}
+
+asmlinkage long sys_lchown(const char __user * filename, uid_t user, gid_t group)
+{
+	struct nameidata nd;
+	int error;
+
+	error = user_path_walk_link(filename, &nd);
+	if (!error) {
+		error = chown_common(nd.dentry, user, group);
+		path_release(&nd);
+	}
+	return error;
+}
+
+
+asmlinkage long sys_fchown(unsigned int fd, uid_t user, gid_t group)
+{
+	struct file * file;
+	int error = -EBADF;
+
+	file = fget(fd);
+	if (file) {
+		error = chown_common(file->f_dentry, user, group);
+		fput(file);
+	}
+	return error;
+}
+
+/*
+ * Note that while the flag value (low two bits) for sys_open means:
+ *	00 - read-only
+ *	01 - write-only
+ *	10 - read-write
+ *	11 - special
+ * it is changed into
+ *	00 - no permissions needed
+ *	01 - read-permission
+ *	10 - write-permission
+ *	11 - read-write
+ * for the internal routines (ie open_namei()/follow_link() etc). 00 is
+ * used by symlinks.
+ */
+struct file *filp_open(const char * filename, int flags, int mode)
+{
+	int namei_flags, error;
+	struct nameidata nd;
+
+	namei_flags = flags;
+	if ((namei_flags+1) & O_ACCMODE)
+		namei_flags++;
+	if (namei_flags & O_TRUNC)
+		namei_flags |= 2;
+
+	error = open_namei(filename, namei_flags, mode, &nd);
+	if (!error)
+		return dentry_open(nd.dentry, nd.mnt, flags);
+
+	return ERR_PTR(error);
+}
+
+EXPORT_SYMBOL(filp_open);
+
+struct file *dentry_open(struct dentry *dentry, struct vfsmount *mnt, int flags)
+{
+	struct file * f;
+	struct inode *inode;
+	int error;
+
+	error = -ENFILE;
+	f = get_empty_filp();
+	if (!f)
+		goto cleanup_dentry;
+	f->f_flags = flags;
+	f->f_mode = ((flags+1) & O_ACCMODE) | FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE;
+	inode = dentry->d_inode;
+	if (f->f_mode & FMODE_WRITE) {
+		error = get_write_access(inode);
+		if (error)
+			goto cleanup_file;
+	}
+
+	f->f_mapping = inode->i_mapping;
+	f->f_dentry = dentry;
+	f->f_vfsmnt = mnt;
+	f->f_pos = 0;
+	f->f_op = fops_get(inode->i_fop);
+	file_move(f, &inode->i_sb->s_files);
+
+	if (f->f_op && f->f_op->open) {
+		error = f->f_op->open(inode,f);
+		if (error)
+			goto cleanup_all;
+	}
+	f->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC);
+
+	file_ra_state_init(&f->f_ra, f->f_mapping->host->i_mapping);
+
+	/* NB: we're sure to have correct a_ops only after f_op->open */
+	if (f->f_flags & O_DIRECT) {
+		if (!f->f_mapping->a_ops || !f->f_mapping->a_ops->direct_IO) {
+			fput(f);
+			f = ERR_PTR(-EINVAL);
+		}
+	}
+
+	return f;
+
+cleanup_all:
+	fops_put(f->f_op);
+	if (f->f_mode & FMODE_WRITE)
+		put_write_access(inode);
+	file_kill(f);
+	f->f_dentry = NULL;
+	f->f_vfsmnt = NULL;
+cleanup_file:
+	put_filp(f);
+cleanup_dentry:
+	dput(dentry);
+	mntput(mnt);
+	return ERR_PTR(error);
+}
+
+EXPORT_SYMBOL(dentry_open);
+
+/*
+ * Find an empty file descriptor entry, and mark it busy.
+ */
+int get_unused_fd(void)
+{
+	struct files_struct * files = current->files;
+	int fd, error;
+
+  	error = -EMFILE;
+	spin_lock(&files->file_lock);
+
+repeat:
+ 	fd = find_next_zero_bit(files->open_fds->fds_bits, 
+				files->max_fdset, 
+				files->next_fd);
+
+	/*
+	 * N.B. For clone tasks sharing a files structure, this test
+	 * will limit the total number of files that can be opened.
+	 */
+	if (fd >= current->signal->rlim[RLIMIT_NOFILE].rlim_cur)
+		goto out;
+
+	/* Do we need to expand the fd array or fd set?  */
+	error = expand_files(files, fd);
+	if (error < 0)
+		goto out;
+
+	if (error) {
+		/*
+	 	 * If we needed to expand the fs array we
+		 * might have blocked - try again.
+		 */
+		error = -EMFILE;
+		goto repeat;
+	}
+
+	FD_SET(fd, files->open_fds);
+	FD_CLR(fd, files->close_on_exec);
+	files->next_fd = fd + 1;
+#if 1
+	/* Sanity check */
+	if (files->fd[fd] != NULL) {
+		printk(KERN_WARNING "get_unused_fd: slot %d not NULL!\n", fd);
+		files->fd[fd] = NULL;
+	}
+#endif
+	error = fd;
+
+out:
+	spin_unlock(&files->file_lock);
+	return error;
+}
+
+EXPORT_SYMBOL(get_unused_fd);
+
+static inline void __put_unused_fd(struct files_struct *files, unsigned int fd)
+{
+	__FD_CLR(fd, files->open_fds);
+	if (fd < files->next_fd)
+		files->next_fd = fd;
+}
+
+void fastcall put_unused_fd(unsigned int fd)
+{
+	struct files_struct *files = current->files;
+	spin_lock(&files->file_lock);
+	__put_unused_fd(files, fd);
+	spin_unlock(&files->file_lock);
+}
+
+EXPORT_SYMBOL(put_unused_fd);
+
+/*
+ * Install a file pointer in the fd array.  
+ *
+ * The VFS is full of places where we drop the files lock between
+ * setting the open_fds bitmap and installing the file in the file
+ * array.  At any such point, we are vulnerable to a dup2() race
+ * installing a file in the array before us.  We need to detect this and
+ * fput() the struct file we are about to overwrite in this case.
+ *
+ * It should never happen - if we allow dup2() do it, _really_ bad things
+ * will follow.
+ */
+
+void fastcall fd_install(unsigned int fd, struct file * file)
+{
+	struct files_struct *files = current->files;
+	spin_lock(&files->file_lock);
+	if (unlikely(files->fd[fd] != NULL))
+		BUG();
+	files->fd[fd] = file;
+	spin_unlock(&files->file_lock);
+}
+
+EXPORT_SYMBOL(fd_install);
+
+asmlinkage long sys_open(const char __user * filename, int flags, int mode)
+{
+	char * tmp;
+	int fd, error;
+
+#if BITS_PER_LONG != 32
+	flags |= O_LARGEFILE;
+#endif
+	tmp = getname(filename);
+	fd = PTR_ERR(tmp);
+	if (!IS_ERR(tmp)) {
+		fd = get_unused_fd();
+		if (fd >= 0) {
+			struct file *f = filp_open(tmp, flags, mode);
+			error = PTR_ERR(f);
+			if (IS_ERR(f))
+				goto out_error;
+			fd_install(fd, f);
+		}
+out:
+		putname(tmp);
+	}
+	return fd;
+
+out_error:
+	put_unused_fd(fd);
+	fd = error;
+	goto out;
+}
+EXPORT_SYMBOL_GPL(sys_open);
+
+#ifndef __alpha__
+
+/*
+ * For backward compatibility?  Maybe this should be moved
+ * into arch/i386 instead?
+ */
+asmlinkage long sys_creat(const char __user * pathname, int mode)
+{
+	return sys_open(pathname, O_CREAT | O_WRONLY | O_TRUNC, mode);
+}
+
+#endif
+
+/*
+ * "id" is the POSIX thread ID. We use the
+ * files pointer for this..
+ */
+int filp_close(struct file *filp, fl_owner_t id)
+{
+	int retval;
+
+	/* Report and clear outstanding errors */
+	retval = filp->f_error;
+	if (retval)
+		filp->f_error = 0;
+
+	if (!file_count(filp)) {
+		printk(KERN_ERR "VFS: Close: file count is 0\n");
+		return retval;
+	}
+
+	if (filp->f_op && filp->f_op->flush) {
+		int err = filp->f_op->flush(filp);
+		if (!retval)
+			retval = err;
+	}
+
+	dnotify_flush(filp, id);
+	locks_remove_posix(filp, id);
+	fput(filp);
+	return retval;
+}
+
+EXPORT_SYMBOL(filp_close);
+
+/*
+ * Careful here! We test whether the file pointer is NULL before
+ * releasing the fd. This ensures that one clone task can't release
+ * an fd while another clone is opening it.
+ */
+asmlinkage long sys_close(unsigned int fd)
+{
+	struct file * filp;
+	struct files_struct *files = current->files;
+
+	spin_lock(&files->file_lock);
+	if (fd >= files->max_fds)
+		goto out_unlock;
+	filp = files->fd[fd];
+	if (!filp)
+		goto out_unlock;
+	files->fd[fd] = NULL;
+	FD_CLR(fd, files->close_on_exec);
+	__put_unused_fd(files, fd);
+	spin_unlock(&files->file_lock);
+	return filp_close(filp, files);
+
+out_unlock:
+	spin_unlock(&files->file_lock);
+	return -EBADF;
+}
+
+EXPORT_SYMBOL(sys_close);
+
+/*
+ * This routine simulates a hangup on the tty, to arrange that users
+ * are given clean terminals at login time.
+ */
+asmlinkage long sys_vhangup(void)
+{
+	if (capable(CAP_SYS_TTY_CONFIG)) {
+		tty_vhangup(current->signal->tty);
+		return 0;
+	}
+	return -EPERM;
+}
+
+/*
+ * Called when an inode is about to be open.
+ * We use this to disallow opening large files on 32bit systems if
+ * the caller didn't specify O_LARGEFILE.  On 64bit systems we force
+ * on this flag in sys_open.
+ */
+int generic_file_open(struct inode * inode, struct file * filp)
+{
+	if (!(filp->f_flags & O_LARGEFILE) && i_size_read(inode) > MAX_NON_LFS)
+		return -EFBIG;
+	return 0;
+}
+
+EXPORT_SYMBOL(generic_file_open);
+
+/*
+ * This is used by subsystems that don't want seekable
+ * file descriptors
+ */
+int nonseekable_open(struct inode *inode, struct file *filp)
+{
+	filp->f_mode &= ~(FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE);
+	return 0;
+}
+
+EXPORT_SYMBOL(nonseekable_open);
diff -Nrub linux-2.6.11.7-orig/include/linux/saudit.h linux-2.6.11.7-snare/include/linux/saudit.h
--- linux-2.6.11.7-orig/include/linux/saudit.h	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.11.7-snare/include/linux/saudit.h	2005-04-22 11:19:47.194665768 -0400
@@ -0,0 +1,506 @@
+/*
+ * linux/saudit.h
+ *
+ * Original:
+ * Copyright (c) 1999-2004 InterSect Alliance Pty Ltd 
+ *                         - http://www.intersectalliance.com/
+ * Additions:
+ * Copyright (c) 2004 Silicon Graphics, Inc. All rights reserved.
+ */
+
+#ifndef __C2_AUDIT_H
+#define __C2_AUDIT_H
+
+#include <linux/compiler.h>
+#include <linux/net.h>
+#include <linux/in.h>
+#include <linux/mm.h>
+#include <net/sock.h>
+
+// If the user has not turned on C2 Auditing, define all 'public' routines out.
+
+#if defined(CONFIG_C2_AUDIT)
+
+// I was going to use __NR_xxx, but we need audit events for events that
+// may NOT be system calls specifically (eg: login, connect/accept)
+#define AUDIT_open		1
+#define AUDIT_mkdir		2
+#define AUDIT_unlink		3
+#define AUDIT_rmdir		4
+#define AUDIT_chown		5
+#define AUDIT_chmod		6
+#define AUDIT_symlink		7
+#define AUDIT_link		8
+#define AUDIT_rename		9
+#define AUDIT_mknod		10
+#define AUDIT_truncate		11
+#define AUDIT_ftruncate		12
+#define AUDIT_chroot		13
+#define AUDIT_execve		14
+#define AUDIT_exit		15
+#define AUDIT_setuid		16
+#define AUDIT_setreuid		17
+#define AUDIT_setresuid		18
+#define AUDIT_setgid		19
+#define AUDIT_setregid		20
+#define AUDIT_setresgid		21
+#define AUDIT_create_module	22
+#define AUDIT_delete_module	23
+#define AUDIT_reboot		24
+#define AUDIT_connect		25
+#define AUDIT_accept		26
+#define AUDIT_mount		27
+#define AUDIT_umount		28
+#define AUDIT_fork		29
+
+// Size of the bitmask array that we need to store the information
+// associated with whether an audit event is currently turned on.
+#define MAXAUDIT			AUDIT_fork
+
+// ioctl modes - note that '1' doesnt seem to work. Added 10.
+#define AUDIT_STOP			10  // stop auditing
+#define AUDIT_START			11  // start auditing
+#define AUDIT_INFO			12  // Give me a list of events
+					    // currently active, and other
+					    // info such as the process ID
+#define AUDIT_LOSTEVENTS		13
+#define AUDIT_FLUSH			14  // Stop all events.
+#define AUDIT_EVENT_ON			15  // Turn on a selected event
+#define AUDIT_EVENT_OFF			16  // Turn off a selected event
+#define AUDIT_DELIVERY 			17  // Do we want to guarantee audit event delivery
+#define AUDIT_TOTALEVENTS		18  // How many events have been received this session?
+
+#define AUDIT_HIGHWATERMARK_MEM		19  // Set the High Water Mark memory usage
+#define AUDIT_HIGHWATERMARK_PER		20  // Set the High Water Mark memory as a percentage mem.
+#define AUDIT_HIGHWATERMARK_PAUSE	21  // Set the High Water Mark pause percetage 
+#define AUDIT_LOWWATERMARK_PAUSE        22  // Set the Low Water Mark to resume
+#define AUDIT_HIGHWATERMARK_NICE        23  // Set the High Water Mark to change nice value
+#define AUDIT_LOWWATERMARK_NICE         24  // Set the Low Water Mark to restore nice value
+#define AUDIT_HIGHWATER_NICE_VAL	25  // The nice value to change the audit daemon to
+
+
+
+
+
+#define AUDIT_CLASS_NONE	0
+#define AUDIT_CLASS_IO		1	// Input/output (file opens)
+#define AUDIT_CLASS_PC		2	// Process Control
+#define AUDIT_CLASS_EXEC	3	// Execution
+#define AUDIT_CLASS_NET		4	// Network related
+#define AUDIT_CLASS_ADMIN	5	// Administrative events
+#define AUDIT_CLASS_CH		6	// CHMOD event. Might not use this for anything else.
+#define AUDIT_CLASS_CP		7	// Where more than one pathname is required
+#define AUDIT_CLASS_SU		8	// SetUID
+#define AUDIT_CLASS_AD		9	// Admin such as create/delete module
+
+#define SNAREAUDIT_MAJOR_VERSION 0
+#define SNAREAUDIT_MINOR_VERSION 9
+#define SNAREAUDIT_PATCH_VERSION 7
+
+
+// /proc entry
+#define AUDITDEV_NAME  "snare"			// device name in /dev and /proc/devices
+#define AUDITINFO_NAME	"snareinfo"		// Information about the process
+#define AUDITDEV_FILE  "/proc/snare"		// full file name
+#define AUDITINFO_FILE "/proc/snareinfo"	// Information about the process.
+#define MAX_PATH   512				// NOTE: will migrgate this to PATH_MAX eventually
+#define MAXCOMMAND 25
+
+
+// This contains the details that are common to ALL audit events.
+typedef struct
+{
+	unsigned short event_class;		// event class. Each class has a predictable format for tokens.
+	unsigned short event_id;		// number of the event
+	unsigned short event_size;		// size of the event struct - don't include header
+						// since it's always the same
+	struct timeval time;			// time
+
+	int user_id;				// User ID
+	int euser_id;				// Effective User ID
+	int group_id;				// Group ID
+	int egroup_id;				// Effective Group ID
+
+	int returncode;				// Make sure that this is big enough to contain the largest returncode.
+
+	pid_t pid;				// process ID.
+	pid_t ppid;				// Parent process ID.
+	char processname[MAXCOMMAND];	// Same as in /usr/include/linux/sched.h for current->comm
+} header_token;
+
+typedef struct
+{
+	char path[MAX_PATH];
+} path_token;
+
+typedef struct
+{
+	int mode;			// How the file was attempted to be opened or created
+	unsigned long	createmode;	// Flags associated with the file creation. Ulong for mknod.
+} attributes_token;
+
+typedef struct
+{
+	int owner;			// new owner of a file - was uid_t, but these are different between kernel and user.
+	int group;			// new group of a file
+} owner_token;
+
+typedef struct
+{
+	char args[MAX_PATH];		// Should really allocate more here. Whats is the max command line size?
+} execargs_token;
+
+// System calls like setuid
+typedef struct
+{
+	int id;		// uid/gid/euid depending on the call
+	int rid;	// ruid/rgid
+	int sid;	// suid/sgid
+} target_token;
+
+// Network connections
+typedef struct
+{
+	char src_ip[40];	// String containing source dotted ip address - 40 bytes, for IPv6
+	int src_port;		// Source port
+	char dst_ip[40];	// String containing destination dotted ip address - 40 bytes, for IPv6
+	int dst_port;		// Destination port
+	int protocol;		// Protocol type - IPPROTO_UDP or IPPROTO_TCP
+} connection_token;
+
+
+
+// Now for the audit event classes
+
+// Just a bare class with the minimal data
+// note that this will mean that EVERY class must start with header_token
+typedef struct
+{
+	header_token	t_header;
+} null_class;
+	
+
+// NOTE: ANY CLASS STRUCTURE SHOULD HAVE THE RETURN TOKEN AS THE SECOND ELEMENT.
+// SEE AUDITD FOR MORE INFO.
+// io - reads/writes
+typedef struct
+{
+	header_token		t_header;
+	path_token		t_path;
+	path_token		t_pwd;	// Working directory
+	attributes_token	t_attributes;
+} io_class;
+
+typedef struct
+{
+	header_token		t_header;
+	path_token		t_path;
+	path_token		t_pwd;	// Working directory
+	owner_token		t_owner;
+} ch_class;
+
+typedef struct
+{
+	header_token		t_header;
+	path_token		t_path;
+	path_token		t_pwd;	// Working directory
+	execargs_token		t_execargs;
+	// environment variables too?
+} ex_class;
+
+typedef struct
+{
+	header_token		t_header;
+} pc_class;
+
+// copy one file to another (amongst others - eg: symlink)
+typedef struct
+{
+	header_token		t_header;
+	path_token		t_sourcepath;
+	path_token		t_pwd;	// Working directory
+	path_token		t_destpath;
+} cp_class;
+
+typedef struct
+{
+	header_token		t_header;
+	target_token		t_target;	// target UID or GID.. I really only need a single value here.
+} su_class;
+
+typedef struct
+{
+	header_token		t_header;
+	connection_token	t_connection;	
+} nt_class;	// Network
+
+typedef struct
+{
+	header_token		t_header;
+	path_token		t_name;		// Name of the module loaded / removed
+} ad_class;	// General Administrative
+
+struct _auditnode
+{
+	void * location;	// Location in RAM of the allocated chunk
+	int size;		// Size of the chunk
+	struct _auditnode *next;	// Next node in the series.
+};
+
+typedef struct _auditnode AuditNode;
+
+
+#ifdef __KERNEL__
+#include <linux/sched.h>
+
+int _audit_mknod(const char * kfile, const char __user * ufile, int mode, dev_t dev, int retval);
+int _audit_execve(const char * kfilename, const char __user * ufilename, char *arguments, int retval);
+// Add this back in..
+// _char *audit_copy_exec_strings(char **argv);
+int _audit_exit(int retval);
+int _audit_fork(int retval);
+int _audit_open(const char * kfile,const char __user *ufile, int flags, int mode, int retval);
+int _audit_mkdir(const char * kfile, const char __user * ufile, int mode, int retval);
+int _audit_unlink(const char * kfile,const char __user * ufile, int retval);
+int _audit_rmdir(const char * kfile, const char __user * ufile, int retval);
+int _audit_chown(const char * kfile,const char __user * ufile, uid_t user, gid_t group, int retval);
+int _audit_chmod(const char * kfile, const char __user * ufile, mode_t mode, int retval);
+int _audit_symlink(const char * kfrom,const char __user * ufrom, const char * kto,const char __user * uto, int retval);
+int _audit_link(const char * kfrom,const char __user * ufrom, const char * kto,const char __user * uto, int retval);
+int _audit_rename(const char * kfrom,const char __user * ufrom, const char * kto, const char __user * uto, int retval);
+int _audit_truncate(const char * kfile,const char __user * ufile, loff_t length, int retval);
+int _audit_ftruncate(const char * file, loff_t length, int retval);
+int _audit_chroot(const char * kfile, const char * ufile, int retval);
+int _audit_setuid(uid_t uid, int retval);
+int _audit_setreuid(uid_t ruid, uid_t euid, int retval);
+int _audit_setresuid(uid_t ruid, uid_t euid, uid_t suid, int retval);
+int _audit_setgid(gid_t gid, int retval);
+int _audit_setregid(gid_t rgid, gid_t egid, int retval);
+int _audit_setresgid(gid_t rgid, gid_t egid, gid_t sgid, int retval);
+int _audit_create_module(const char * kname, const char __user * uname, int retval);
+int _audit_delete_module(const char * kname, const char __user * uname, int retval);
+int _audit_reboot(int magic1, int magic2, unsigned int cmd, void __user * arg, int retval);
+int _audit_connect(int sockfd, struct sockaddr __user *serv_addr, int addrlen, int retval);
+int _audit_accept(int sockfd, struct sockaddr __user *serv_addr, int *addrlen, int retval);
+int _audit_mount(const char *kdev_name, const char __user *udev_name, const char *kdir_name, const char __user *udir_name, unsigned long flags, int retval);
+int _audit_umount(const char *kname, const char __user *uname, int flags, int retval);
+
+void saudit_init(void);
+char * saudit_copy_argv(char __user *__user *argv);
+
+extern int AUDIT_IS_RUNNING;
+
+// Inline routines in order to speed things up a little.
+static inline int saudit_mknod(const char * kfile, const char * ufile, int mode, dev_t dev, int retval) {
+	if (unlikely(AUDIT_IS_RUNNING))
+		return (_audit_mknod(kfile,ufile,mode,dev,retval));
+	else
+		return(0);
+}
+static inline int saudit_execve(const char * kfilename, const char * ufilename, char *arguments, int retval) {
+	if (unlikely(AUDIT_IS_RUNNING))
+		return (_audit_execve(kfilename,ufilename,arguments,retval));
+	else
+		return(0);
+}
+static inline int saudit_exit(int retval) {
+	if (unlikely(AUDIT_IS_RUNNING))
+		return (_audit_exit(retval));
+	else
+		return(0);
+}
+static inline int saudit_fork(int retval) {
+	if (unlikely(AUDIT_IS_RUNNING))
+		return (_audit_fork(retval));
+	else
+		return(0);
+}
+static inline int saudit_open(const char * kfile,const char *ufile, int flags, int mode, int retval) {
+	if (unlikely(AUDIT_IS_RUNNING))
+		return (_audit_open(kfile,ufile,flags,mode,retval));
+	else
+		return(0);
+}
+static inline int saudit_mkdir(const char * kfile, const char * ufile, int mode, int retval) {
+	if (unlikely(AUDIT_IS_RUNNING))
+		return (_audit_mkdir(kfile,ufile,mode,retval));
+	else
+		return(0);
+}
+static inline int saudit_unlink(const char * kfile,const char * ufile, int retval) {
+	if (unlikely(AUDIT_IS_RUNNING))
+		return (_audit_unlink(kfile,ufile,retval));
+	else
+		return(0);
+}
+static inline int saudit_rmdir(const char * kfile, const char * ufile, int retval) {
+	if (unlikely(AUDIT_IS_RUNNING))
+		return (_audit_rmdir(kfile,ufile,retval));
+	else
+		return(0);
+}
+static inline int saudit_chown(const char * kfile,const char * ufile, uid_t user, gid_t group, int retval) {
+	if (unlikely(AUDIT_IS_RUNNING))
+		return (_audit_chown(kfile,ufile,user,group,retval));
+	else
+		return(0);
+}
+static inline int saudit_chmod(const char * kfile, const char * ufile, mode_t mode, int retval) {
+	if (unlikely(AUDIT_IS_RUNNING))
+		return (_audit_chmod(kfile,ufile,mode,retval));
+	else
+		return(0);
+}
+static inline int saudit_symlink(const char * kfrom,const char * ufrom, const char * kto,const char * uto, int retval) {
+	if (unlikely(AUDIT_IS_RUNNING))
+		return (_audit_symlink(kfrom,ufrom,kto,uto,retval));
+	else
+		return(0);
+}
+static inline int saudit_link(const char * kfrom,const char * ufrom, const char * kto,const char * uto, int retval) {
+	if (unlikely(AUDIT_IS_RUNNING))
+		return (_audit_link(kfrom,ufrom,kto,uto,retval));
+	else
+		return(0);
+}
+static inline int saudit_rename(const char * kfrom,const char * ufrom, const char * kto, const char * uto, int retval) {
+	if (unlikely(AUDIT_IS_RUNNING))
+		return (_audit_rename(kfrom,ufrom,kto,uto,retval));
+	else
+		return(0);
+}
+static inline int saudit_truncate(const char * kfile,const char * ufile, loff_t length, int retval) {
+	if (unlikely(AUDIT_IS_RUNNING))
+		return (_audit_truncate(kfile,ufile,length,retval));
+	else
+		return(0);
+}
+static inline int saudit_ftruncate(const char * file, loff_t length, int retval) {
+	if (unlikely(AUDIT_IS_RUNNING))
+		return (_audit_ftruncate(file,length,retval));
+	else
+		return(0);
+}
+static inline int saudit_chroot(const char * kfile, const char * ufile, int retval) {
+	if (unlikely(AUDIT_IS_RUNNING))
+		return (_audit_chroot(kfile,ufile,retval));
+	else
+		return(0);
+}
+static inline int saudit_setuid(uid_t uid, int retval) {
+	if (unlikely(AUDIT_IS_RUNNING))
+		return (_audit_setuid(uid,retval));
+	else
+		return(0);
+}
+static inline int saudit_setreuid(uid_t ruid, uid_t euid, int retval) {
+	if (unlikely(AUDIT_IS_RUNNING))
+		return (_audit_setreuid(ruid,euid,retval));
+	else
+		return(0);
+}
+static inline int saudit_setresuid(uid_t ruid, uid_t euid, uid_t suid, int retval) {
+	if (unlikely(AUDIT_IS_RUNNING))
+		return (_audit_setresuid(ruid,euid,suid,retval));
+	else
+		return(0);
+}
+static inline int saudit_setgid(gid_t gid, int retval) {
+	if (unlikely(AUDIT_IS_RUNNING))
+		return (_audit_setgid(gid,retval));
+	else
+		return(0);
+}
+static inline int saudit_setregid(gid_t rgid, gid_t egid, int retval) {
+	if (unlikely(AUDIT_IS_RUNNING))
+		return (_audit_setregid(rgid,egid,retval));
+	else
+		return(0);
+}
+static inline int saudit_setresgid(gid_t rgid, gid_t egid, gid_t sgid, int retval) {
+	if (unlikely(AUDIT_IS_RUNNING))
+		return (_audit_setresgid(rgid,egid,sgid,retval));
+	else
+		return(0);
+}
+static inline int saudit_create_module(const char * kname, const char * uname, int retval) {
+	if (unlikely(AUDIT_IS_RUNNING))
+		return (_audit_create_module(kname,uname,retval));
+	else
+		return(0);
+}
+static inline int saudit_delete_module(const char * kname, const char * uname, int retval) {
+	if (unlikely(AUDIT_IS_RUNNING))
+		return (_audit_delete_module(kname,uname,retval));
+	else
+		return(0);
+}
+static inline int saudit_reboot(int magic1, int magic2, unsigned int cmd, void __user * arg, int retval) {
+	if (unlikely(AUDIT_IS_RUNNING))
+		return (_audit_reboot(magic1,magic2,cmd,arg,retval));
+	else
+		return(0);
+}
+static inline int saudit_connect(int sockfd, void __user *serv_addr, int addrlen, int retval) {
+	if (unlikely(AUDIT_IS_RUNNING))
+		return (_audit_connect(sockfd,serv_addr,addrlen,retval));
+	else
+		return(0);
+}
+static inline int saudit_accept(int sockfd, void __user *serv_addr, int *addrlen, int retval) {
+	if (unlikely(AUDIT_IS_RUNNING))
+		return (_audit_accept(sockfd,serv_addr,addrlen,retval));
+	else
+		return(0);
+}
+static inline int saudit_mount(const char *kdev_name, const char *udev_name, const char *kdir_name, const char *udir_name, unsigned long flags, int retval) {
+	if (unlikely(AUDIT_IS_RUNNING))
+		return (_audit_mount(kdev_name,udev_name,kdir_name,udir_name,flags,retval));
+	else
+		return(0);
+}
+static inline int saudit_umount(const char *kname, const char *uname, int flags, int retval) {
+	if (unlikely(AUDIT_IS_RUNNING))
+		return (_audit_umount(kname,uname,flags,retval));
+	else
+		return(0);
+}
+
+#endif /* __KERNEL__ */
+
+#else	/* CONFIG_C2_AUDIT */
+
+#ifdef __KERNEL__
+static inline int saudit_mknod(const char * kfile, const char * ufile, int mode, dev_t dev, int retval) {return(0);}
+static inline char * saudit_copy_argv(char **argv) {return(0);}
+static inline int saudit_execve(const char * kfilename, const char * ufilename, char *arguments, int retval) {return(0);}
+static inline int saudit_exit(int retval) {return(0);}
+static inline int saudit_fork(int retval) {return(0);}
+static inline int saudit_open(const char * kfile,const char *ufile, int flags, int mode, int retval) {return(0);}
+static inline int saudit_mkdir(const char * kfile, const char * ufile, int mode, int retval) {return(0);}
+static inline int saudit_unlink(const char * kfile,const char * ufile, int retval) {return(0);}
+static inline int saudit_rmdir(const char * kfile, const char * ufile, int retval) {return(0);}
+static inline int saudit_chown(const char * kfile,const char * ufile, uid_t user, gid_t group, int retval) {return(0);}
+static inline int saudit_chmod(const char * kfile, const char * ufile, mode_t mode, int retval) {return(0);}
+static inline int saudit_symlink(const char * kfrom,const char * ufrom, const char * kto,const char * uto, int retval) {return(0);}
+static inline int saudit_link(const char * kfrom,const char * ufrom, const char * kto,const char * uto, int retval) {return(0);}
+static inline int saudit_rename(const char * kfrom,const char * ufrom, const char * kto, const char * uto, int retval) {return(0);}
+static inline int saudit_truncate(const char * kfile,const char * ufile, loff_t length, int retval) {return(0);}
+static inline int saudit_ftruncate(const char * file, loff_t length, int retval) {return(0);}
+static inline int saudit_chroot(const char * kfile, const char * ufile, int retval) {return(0);}
+static inline int saudit_setuid(uid_t uid, int retval) {return(0);}
+static inline int saudit_setreuid(uid_t ruid, uid_t euid, int retval) {return(0);}
+static inline int saudit_setresuid(uid_t ruid, uid_t euid, uid_t suid, int retval) {return(0);}
+static inline int saudit_setgid(gid_t gid, int retval) {return(0);}
+static inline int saudit_setregid(gid_t rgid, gid_t egid, int retval) {return(0);}
+static inline int saudit_setresgid(gid_t rgid, gid_t egid, gid_t sgid, int retval) {return(0);}
+static inline int saudit_create_module(const char * kname, const char * uname, int retval) {return(0);}
+static inline int saudit_delete_module(const char * kname, const char * uname, int retval) {return(0);}
+static inline int saudit_reboot(int magic1, int magic2, unsigned int cmd, void __user * arg, int retval) {return(0);}
+static inline int saudit_connect(int sockfd, void __user *serv_addr, int addrlen, int retval) {return(0);}
+static inline int saudit_accept(int sockfd, void __user *serv_addr, int *addrlen, int retval) {return(0);}
+static inline int saudit_mount(const char *kdev_name, const char *udev_name, const char *kdir_name, const char *udir_name, unsigned long flags, int retval) {return(0);}
+static inline int saudit_umount(const char *kname, const char *uname, int flags, int retval) {return(0);}
+#endif /* __KERNEL__ */
+#endif /* CONFIG_C2_AUDIT */
+#endif /* __C2_AUDIT_H */
diff -Nrub linux-2.6.11.7-orig/include/linux/sched.h linux-2.6.11.7-snare/include/linux/sched.h
--- linux-2.6.11.7-orig/include/linux/sched.h	2005-04-07 14:57:12.000000000 -0400
+++ linux-2.6.11.7-snare/include/linux/sched.h	2005-04-22 11:19:47.195665616 -0400
@@ -657,6 +657,11 @@
 /* journalling filesystem info */
 	void *journal_info;
 
+#ifdef CONFIG_C2_AUDIT
+  /*Audit record pointer*/
+     void *saudit_record;
+#endif
+
 /* VM state */
 	struct reclaim_state *reclaim_state;
 
diff -Nrub linux-2.6.11.7-orig/include/linux/sched.h.orig linux-2.6.11.7-snare/include/linux/sched.h.orig
--- linux-2.6.11.7-orig/include/linux/sched.h.orig	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.11.7-snare/include/linux/sched.h.orig	2005-04-07 14:57:12.000000000 -0400
@@ -0,0 +1,1219 @@
+#ifndef _LINUX_SCHED_H
+#define _LINUX_SCHED_H
+
+#include <asm/param.h>	/* for HZ */
+
+#include <linux/config.h>
+#include <linux/capability.h>
+#include <linux/threads.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/timex.h>
+#include <linux/jiffies.h>
+#include <linux/rbtree.h>
+#include <linux/thread_info.h>
+#include <linux/cpumask.h>
+#include <linux/errno.h>
+
+#include <asm/system.h>
+#include <asm/semaphore.h>
+#include <asm/page.h>
+#include <asm/ptrace.h>
+#include <asm/mmu.h>
+#include <asm/cputime.h>
+
+#include <linux/smp.h>
+#include <linux/sem.h>
+#include <linux/signal.h>
+#include <linux/securebits.h>
+#include <linux/fs_struct.h>
+#include <linux/compiler.h>
+#include <linux/completion.h>
+#include <linux/pid.h>
+#include <linux/percpu.h>
+#include <linux/topology.h>
+
+struct exec_domain;
+
+/*
+ * cloning flags:
+ */
+#define CSIGNAL		0x000000ff	/* signal mask to be sent at exit */
+#define CLONE_VM	0x00000100	/* set if VM shared between processes */
+#define CLONE_FS	0x00000200	/* set if fs info shared between processes */
+#define CLONE_FILES	0x00000400	/* set if open files shared between processes */
+#define CLONE_SIGHAND	0x00000800	/* set if signal handlers and blocked signals shared */
+#define CLONE_PTRACE	0x00002000	/* set if we want to let tracing continue on the child too */
+#define CLONE_VFORK	0x00004000	/* set if the parent wants the child to wake it up on mm_release */
+#define CLONE_PARENT	0x00008000	/* set if we want to have the same parent as the cloner */
+#define CLONE_THREAD	0x00010000	/* Same thread group? */
+#define CLONE_NEWNS	0x00020000	/* New namespace group? */
+#define CLONE_SYSVSEM	0x00040000	/* share system V SEM_UNDO semantics */
+#define CLONE_SETTLS	0x00080000	/* create a new TLS for the child */
+#define CLONE_PARENT_SETTID	0x00100000	/* set the TID in the parent */
+#define CLONE_CHILD_CLEARTID	0x00200000	/* clear the TID in the child */
+#define CLONE_DETACHED		0x00400000	/* Unused, ignored */
+#define CLONE_UNTRACED		0x00800000	/* set if the tracing process can't force CLONE_PTRACE on this clone */
+#define CLONE_CHILD_SETTID	0x01000000	/* set the TID in the child */
+#define CLONE_STOPPED		0x02000000	/* Start in stopped state */
+
+/*
+ * List of flags we want to share for kernel threads,
+ * if only because they are not used by them anyway.
+ */
+#define CLONE_KERNEL	(CLONE_FS | CLONE_FILES | CLONE_SIGHAND)
+
+/*
+ * These are the constant used to fake the fixed-point load-average
+ * counting. Some notes:
+ *  - 11 bit fractions expand to 22 bits by the multiplies: this gives
+ *    a load-average precision of 10 bits integer + 11 bits fractional
+ *  - if you want to count load-averages more often, you need more
+ *    precision, or rounding will get you. With 2-second counting freq,
+ *    the EXP_n values would be 1981, 2034 and 2043 if still using only
+ *    11 bit fractions.
+ */
+extern unsigned long avenrun[];		/* Load averages */
+
+#define FSHIFT		11		/* nr of bits of precision */
+#define FIXED_1		(1<<FSHIFT)	/* 1.0 as fixed-point */
+#define LOAD_FREQ	(5*HZ)		/* 5 sec intervals */
+#define EXP_1		1884		/* 1/exp(5sec/1min) as fixed-point */
+#define EXP_5		2014		/* 1/exp(5sec/5min) */
+#define EXP_15		2037		/* 1/exp(5sec/15min) */
+
+#define CALC_LOAD(load,exp,n) \
+	load *= exp; \
+	load += n*(FIXED_1-exp); \
+	load >>= FSHIFT;
+
+extern unsigned long total_forks;
+extern int nr_threads;
+extern int last_pid;
+DECLARE_PER_CPU(unsigned long, process_counts);
+extern int nr_processes(void);
+extern unsigned long nr_running(void);
+extern unsigned long nr_uninterruptible(void);
+extern unsigned long nr_iowait(void);
+
+#include <linux/time.h>
+#include <linux/param.h>
+#include <linux/resource.h>
+#include <linux/timer.h>
+
+#include <asm/processor.h>
+
+#define TASK_RUNNING		0
+#define TASK_INTERRUPTIBLE	1
+#define TASK_UNINTERRUPTIBLE	2
+#define TASK_STOPPED		4
+#define TASK_TRACED		8
+#define EXIT_ZOMBIE		16
+#define EXIT_DEAD		32
+
+#define __set_task_state(tsk, state_value)		\
+	do { (tsk)->state = (state_value); } while (0)
+#define set_task_state(tsk, state_value)		\
+	set_mb((tsk)->state, (state_value))
+
+#define __set_current_state(state_value)			\
+	do { current->state = (state_value); } while (0)
+#define set_current_state(state_value)		\
+	set_mb(current->state, (state_value))
+
+/* Task command name length */
+#define TASK_COMM_LEN 16
+
+/*
+ * Scheduling policies
+ */
+#define SCHED_NORMAL		0
+#define SCHED_FIFO		1
+#define SCHED_RR		2
+
+struct sched_param {
+	int sched_priority;
+};
+
+#ifdef __KERNEL__
+
+#include <linux/spinlock.h>
+
+/*
+ * This serializes "schedule()" and also protects
+ * the run-queue from deletions/modifications (but
+ * _adding_ to the beginning of the run-queue has
+ * a separate lock).
+ */
+extern rwlock_t tasklist_lock;
+extern spinlock_t mmlist_lock;
+
+typedef struct task_struct task_t;
+
+extern void sched_init(void);
+extern void sched_init_smp(void);
+extern void init_idle(task_t *idle, int cpu);
+
+extern cpumask_t nohz_cpu_mask;
+
+extern void show_state(void);
+extern void show_regs(struct pt_regs *);
+
+/*
+ * TASK is a pointer to the task whose backtrace we want to see (or NULL for current
+ * task), SP is the stack pointer of the first frame that should be shown in the back
+ * trace (or NULL if the entire call-chain of the task should be shown).
+ */
+extern void show_stack(struct task_struct *task, unsigned long *sp);
+
+void io_schedule(void);
+long io_schedule_timeout(long timeout);
+
+extern void cpu_init (void);
+extern void trap_init(void);
+extern void update_process_times(int user);
+extern void scheduler_tick(void);
+extern unsigned long cache_decay_ticks;
+
+/* Attach to any functions which should be ignored in wchan output. */
+#define __sched		__attribute__((__section__(".sched.text")))
+/* Is this address in the __sched functions? */
+extern int in_sched_functions(unsigned long addr);
+
+#define	MAX_SCHEDULE_TIMEOUT	LONG_MAX
+extern signed long FASTCALL(schedule_timeout(signed long timeout));
+asmlinkage void schedule(void);
+
+struct namespace;
+
+/* Maximum number of active map areas.. This is a random (large) number */
+#define DEFAULT_MAX_MAP_COUNT	65536
+
+extern int sysctl_max_map_count;
+
+#include <linux/aio.h>
+
+extern unsigned long
+arch_get_unmapped_area(struct file *, unsigned long, unsigned long,
+		       unsigned long, unsigned long);
+extern unsigned long
+arch_get_unmapped_area_topdown(struct file *filp, unsigned long addr,
+			  unsigned long len, unsigned long pgoff,
+			  unsigned long flags);
+extern void arch_unmap_area(struct vm_area_struct *area);
+extern void arch_unmap_area_topdown(struct vm_area_struct *area);
+
+
+struct mm_struct {
+	struct vm_area_struct * mmap;		/* list of VMAs */
+	struct rb_root mm_rb;
+	struct vm_area_struct * mmap_cache;	/* last find_vma result */
+	unsigned long (*get_unmapped_area) (struct file *filp,
+				unsigned long addr, unsigned long len,
+				unsigned long pgoff, unsigned long flags);
+	void (*unmap_area) (struct vm_area_struct *area);
+	unsigned long mmap_base;		/* base of mmap area */
+	unsigned long free_area_cache;		/* first hole */
+	pgd_t * pgd;
+	atomic_t mm_users;			/* How many users with user space? */
+	atomic_t mm_count;			/* How many references to "struct mm_struct" (users count as 1) */
+	int map_count;				/* number of VMAs */
+	struct rw_semaphore mmap_sem;
+	spinlock_t page_table_lock;		/* Protects page tables, mm->rss, mm->anon_rss */
+
+	struct list_head mmlist;		/* List of maybe swapped mm's.  These are globally strung
+						 * together off init_mm.mmlist, and are protected
+						 * by mmlist_lock
+						 */
+
+	unsigned long start_code, end_code, start_data, end_data;
+	unsigned long start_brk, brk, start_stack;
+	unsigned long arg_start, arg_end, env_start, env_end;
+	unsigned long rss, anon_rss, total_vm, locked_vm, shared_vm;
+	unsigned long exec_vm, stack_vm, reserved_vm, def_flags, nr_ptes;
+
+	unsigned long saved_auxv[42]; /* for /proc/PID/auxv */
+
+	unsigned dumpable:1;
+	cpumask_t cpu_vm_mask;
+
+	/* Architecture-specific MM context */
+	mm_context_t context;
+
+	/* Token based thrashing protection. */
+	unsigned long swap_token_time;
+	char recent_pagein;
+
+	/* coredumping support */
+	int core_waiters;
+	struct completion *core_startup_done, core_done;
+
+	/* aio bits */
+	rwlock_t		ioctx_list_lock;
+	struct kioctx		*ioctx_list;
+
+	struct kioctx		default_kioctx;
+
+	unsigned long hiwater_rss;	/* High-water RSS usage */
+	unsigned long hiwater_vm;	/* High-water virtual memory usage */
+};
+
+struct sighand_struct {
+	atomic_t		count;
+	struct k_sigaction	action[_NSIG];
+	spinlock_t		siglock;
+};
+
+/*
+ * NOTE! "signal_struct" does not have it's own
+ * locking, because a shared signal_struct always
+ * implies a shared sighand_struct, so locking
+ * sighand_struct is always a proper superset of
+ * the locking of signal_struct.
+ */
+struct signal_struct {
+	atomic_t		count;
+	atomic_t		live;
+
+	wait_queue_head_t	wait_chldexit;	/* for wait4() */
+
+	/* current thread group signal load-balancing target: */
+	task_t			*curr_target;
+
+	/* shared signal handling: */
+	struct sigpending	shared_pending;
+
+	/* thread group exit support */
+	int			group_exit_code;
+	/* overloaded:
+	 * - notify group_exit_task when ->count is equal to notify_count
+	 * - everyone except group_exit_task is stopped during signal delivery
+	 *   of fatal signals, group_exit_task processes the signal.
+	 */
+	struct task_struct	*group_exit_task;
+	int			notify_count;
+
+	/* thread group stop support, overloads group_exit_code too */
+	int			group_stop_count;
+	unsigned int		flags; /* see SIGNAL_* flags below */
+
+	/* POSIX.1b Interval Timers */
+	struct list_head posix_timers;
+
+	/* job control IDs */
+	pid_t pgrp;
+	pid_t tty_old_pgrp;
+	pid_t session;
+	/* boolean value for session group leader */
+	int leader;
+
+	struct tty_struct *tty; /* NULL if no tty */
+
+	/*
+	 * Cumulative resource counters for dead threads in the group,
+	 * and for reaped dead child processes forked by this group.
+	 * Live threads maintain their own counters and add to these
+	 * in __exit_signal, except for the group leader.
+	 */
+	cputime_t utime, stime, cutime, cstime;
+	unsigned long nvcsw, nivcsw, cnvcsw, cnivcsw;
+	unsigned long min_flt, maj_flt, cmin_flt, cmaj_flt;
+
+	/*
+	 * We don't bother to synchronize most readers of this at all,
+	 * because there is no reader checking a limit that actually needs
+	 * to get both rlim_cur and rlim_max atomically, and either one
+	 * alone is a single word that can safely be read normally.
+	 * getrlimit/setrlimit use task_lock(current->group_leader) to
+	 * protect this instead of the siglock, because they really
+	 * have no need to disable irqs.
+	 */
+	struct rlimit rlim[RLIM_NLIMITS];
+};
+
+/*
+ * Bits in flags field of signal_struct.
+ */
+#define SIGNAL_STOP_STOPPED	0x00000001 /* job control stop in effect */
+#define SIGNAL_STOP_DEQUEUED	0x00000002 /* stop signal dequeued */
+#define SIGNAL_STOP_CONTINUED	0x00000004 /* SIGCONT since WCONTINUED reap */
+#define SIGNAL_GROUP_EXIT	0x00000008 /* group exit in progress */
+
+
+/*
+ * Priority of a process goes from 0..MAX_PRIO-1, valid RT
+ * priority is 0..MAX_RT_PRIO-1, and SCHED_NORMAL tasks are
+ * in the range MAX_RT_PRIO..MAX_PRIO-1. Priority values
+ * are inverted: lower p->prio value means higher priority.
+ *
+ * The MAX_USER_RT_PRIO value allows the actual maximum
+ * RT priority to be separate from the value exported to
+ * user-space.  This allows kernel threads to set their
+ * priority to a value higher than any user task. Note:
+ * MAX_RT_PRIO must not be smaller than MAX_USER_RT_PRIO.
+ */
+
+#define MAX_USER_RT_PRIO	100
+#define MAX_RT_PRIO		MAX_USER_RT_PRIO
+
+#define MAX_PRIO		(MAX_RT_PRIO + 40)
+
+#define rt_task(p)		(unlikely((p)->prio < MAX_RT_PRIO))
+
+/*
+ * Some day this will be a full-fledged user tracking system..
+ */
+struct user_struct {
+	atomic_t __count;	/* reference count */
+	atomic_t processes;	/* How many processes does this user have? */
+	atomic_t files;		/* How many open files does this user have? */
+	atomic_t sigpending;	/* How many pending signals does this user have? */
+	/* protected by mq_lock	*/
+	unsigned long mq_bytes;	/* How many bytes can be allocated to mqueue? */
+	unsigned long locked_shm; /* How many pages of mlocked shm ? */
+
+#ifdef CONFIG_KEYS
+	struct key *uid_keyring;	/* UID specific keyring */
+	struct key *session_keyring;	/* UID's default session keyring */
+#endif
+
+	/* Hash table maintenance information */
+	struct list_head uidhash_list;
+	uid_t uid;
+};
+
+extern struct user_struct *find_user(uid_t);
+
+extern struct user_struct root_user;
+#define INIT_USER (&root_user)
+
+typedef struct prio_array prio_array_t;
+struct backing_dev_info;
+struct reclaim_state;
+
+#ifdef CONFIG_SCHEDSTATS
+struct sched_info {
+	/* cumulative counters */
+	unsigned long	cpu_time,	/* time spent on the cpu */
+			run_delay,	/* time spent waiting on a runqueue */
+			pcnt;		/* # of timeslices run on this cpu */
+
+	/* timestamps */
+	unsigned long	last_arrival,	/* when we last ran on a cpu */
+			last_queued;	/* when we were last queued to run */
+};
+
+extern struct file_operations proc_schedstat_operations;
+#endif
+
+enum idle_type
+{
+	SCHED_IDLE,
+	NOT_IDLE,
+	NEWLY_IDLE,
+	MAX_IDLE_TYPES
+};
+
+/*
+ * sched-domains (multiprocessor balancing) declarations:
+ */
+#ifdef CONFIG_SMP
+#define SCHED_LOAD_SCALE	128UL	/* increase resolution of load */
+
+#define SD_LOAD_BALANCE		1	/* Do load balancing on this domain. */
+#define SD_BALANCE_NEWIDLE	2	/* Balance when about to become idle */
+#define SD_BALANCE_EXEC		4	/* Balance on exec */
+#define SD_WAKE_IDLE		8	/* Wake to idle CPU on task wakeup */
+#define SD_WAKE_AFFINE		16	/* Wake task to waking CPU */
+#define SD_WAKE_BALANCE		32	/* Perform balancing at task wakeup */
+#define SD_SHARE_CPUPOWER	64	/* Domain members share cpu power */
+
+struct sched_group {
+	struct sched_group *next;	/* Must be a circular list */
+	cpumask_t cpumask;
+
+	/*
+	 * CPU power of this group, SCHED_LOAD_SCALE being max power for a
+	 * single CPU. This is read only (except for setup, hotplug CPU).
+	 */
+	unsigned long cpu_power;
+};
+
+struct sched_domain {
+	/* These fields must be setup */
+	struct sched_domain *parent;	/* top domain must be null terminated */
+	struct sched_group *groups;	/* the balancing groups of the domain */
+	cpumask_t span;			/* span of all CPUs in this domain */
+	unsigned long min_interval;	/* Minimum balance interval ms */
+	unsigned long max_interval;	/* Maximum balance interval ms */
+	unsigned int busy_factor;	/* less balancing by factor if busy */
+	unsigned int imbalance_pct;	/* No balance until over watermark */
+	unsigned long long cache_hot_time; /* Task considered cache hot (ns) */
+	unsigned int cache_nice_tries;	/* Leave cache hot tasks for # tries */
+	unsigned int per_cpu_gain;	/* CPU % gained by adding domain cpus */
+	int flags;			/* See SD_* */
+
+	/* Runtime fields. */
+	unsigned long last_balance;	/* init to jiffies. units in jiffies */
+	unsigned int balance_interval;	/* initialise to 1. units in ms. */
+	unsigned int nr_balance_failed; /* initialise to 0 */
+
+#ifdef CONFIG_SCHEDSTATS
+	/* load_balance() stats */
+	unsigned long lb_cnt[MAX_IDLE_TYPES];
+	unsigned long lb_failed[MAX_IDLE_TYPES];
+	unsigned long lb_imbalance[MAX_IDLE_TYPES];
+	unsigned long lb_nobusyg[MAX_IDLE_TYPES];
+	unsigned long lb_nobusyq[MAX_IDLE_TYPES];
+
+	/* sched_balance_exec() stats */
+	unsigned long sbe_attempts;
+	unsigned long sbe_pushed;
+
+	/* try_to_wake_up() stats */
+	unsigned long ttwu_wake_affine;
+	unsigned long ttwu_wake_balance;
+#endif
+};
+
+#ifdef ARCH_HAS_SCHED_DOMAIN
+/* Useful helpers that arch setup code may use. Defined in kernel/sched.c */
+extern cpumask_t cpu_isolated_map;
+extern void init_sched_build_groups(struct sched_group groups[],
+	                        cpumask_t span, int (*group_fn)(int cpu));
+extern void cpu_attach_domain(struct sched_domain *sd, int cpu);
+#endif /* ARCH_HAS_SCHED_DOMAIN */
+#endif /* CONFIG_SMP */
+
+
+struct io_context;			/* See blkdev.h */
+void exit_io_context(void);
+
+#define NGROUPS_SMALL		32
+#define NGROUPS_PER_BLOCK	((int)(PAGE_SIZE / sizeof(gid_t)))
+struct group_info {
+	int ngroups;
+	atomic_t usage;
+	gid_t small_block[NGROUPS_SMALL];
+	int nblocks;
+	gid_t *blocks[0];
+};
+
+/*
+ * get_group_info() must be called with the owning task locked (via task_lock())
+ * when task != current.  The reason being that the vast majority of callers are
+ * looking at current->group_info, which can not be changed except by the
+ * current task.  Changing current->group_info requires the task lock, too.
+ */
+#define get_group_info(group_info) do { \
+	atomic_inc(&(group_info)->usage); \
+} while (0)
+
+#define put_group_info(group_info) do { \
+	if (atomic_dec_and_test(&(group_info)->usage)) \
+		groups_free(group_info); \
+} while (0)
+
+struct group_info *groups_alloc(int gidsetsize);
+void groups_free(struct group_info *group_info);
+int set_current_groups(struct group_info *group_info);
+/* access the groups "array" with this macro */
+#define GROUP_AT(gi, i) \
+    ((gi)->blocks[(i)/NGROUPS_PER_BLOCK][(i)%NGROUPS_PER_BLOCK])
+
+
+struct audit_context;		/* See audit.c */
+struct mempolicy;
+
+struct task_struct {
+	volatile long state;	/* -1 unrunnable, 0 runnable, >0 stopped */
+	struct thread_info *thread_info;
+	atomic_t usage;
+	unsigned long flags;	/* per process flags, defined below */
+	unsigned long ptrace;
+
+	int lock_depth;		/* Lock depth */
+
+	int prio, static_prio;
+	struct list_head run_list;
+	prio_array_t *array;
+
+	unsigned long sleep_avg;
+	unsigned long long timestamp, last_ran;
+	int activated;
+
+	unsigned long policy;
+	cpumask_t cpus_allowed;
+	unsigned int time_slice, first_time_slice;
+
+#ifdef CONFIG_SCHEDSTATS
+	struct sched_info sched_info;
+#endif
+
+	struct list_head tasks;
+	/*
+	 * ptrace_list/ptrace_children forms the list of my children
+	 * that were stolen by a ptracer.
+	 */
+	struct list_head ptrace_children;
+	struct list_head ptrace_list;
+
+	struct mm_struct *mm, *active_mm;
+
+/* task state */
+	struct linux_binfmt *binfmt;
+	long exit_state;
+	int exit_code, exit_signal;
+	int pdeath_signal;  /*  The signal sent when the parent dies  */
+	/* ??? */
+	unsigned long personality;
+	unsigned did_exec:1;
+	pid_t pid;
+	pid_t tgid;
+	/* 
+	 * pointers to (original) parent process, youngest child, younger sibling,
+	 * older sibling, respectively.  (p->father can be replaced with 
+	 * p->parent->pid)
+	 */
+	struct task_struct *real_parent; /* real parent process (when being debugged) */
+	struct task_struct *parent;	/* parent process */
+	/*
+	 * children/sibling forms the list of my children plus the
+	 * tasks I'm ptracing.
+	 */
+	struct list_head children;	/* list of my children */
+	struct list_head sibling;	/* linkage in my parent's children list */
+	struct task_struct *group_leader;	/* threadgroup leader */
+
+	/* PID/PID hash table linkage. */
+	struct pid pids[PIDTYPE_MAX];
+
+	struct completion *vfork_done;		/* for vfork() */
+	int __user *set_child_tid;		/* CLONE_CHILD_SETTID */
+	int __user *clear_child_tid;		/* CLONE_CHILD_CLEARTID */
+
+	unsigned long rt_priority;
+	unsigned long it_real_value, it_real_incr;
+	cputime_t it_virt_value, it_virt_incr;
+	cputime_t it_prof_value, it_prof_incr;
+	struct timer_list real_timer;
+	cputime_t utime, stime;
+	unsigned long nvcsw, nivcsw; /* context switch counts */
+	struct timespec start_time;
+/* mm fault and swap info: this can arguably be seen as either mm-specific or thread-specific */
+	unsigned long min_flt, maj_flt;
+/* process credentials */
+	uid_t uid,euid,suid,fsuid;
+	gid_t gid,egid,sgid,fsgid;
+	struct group_info *group_info;
+	kernel_cap_t   cap_effective, cap_inheritable, cap_permitted;
+	unsigned keep_capabilities:1;
+	struct user_struct *user;
+#ifdef CONFIG_KEYS
+	struct key *session_keyring;	/* keyring inherited over fork */
+	struct key *process_keyring;	/* keyring private to this process (CLONE_THREAD) */
+	struct key *thread_keyring;	/* keyring private to this thread */
+#endif
+	int oomkilladj; /* OOM kill score adjustment (bit shift). */
+	char comm[TASK_COMM_LEN];
+/* file system info */
+	int link_count, total_link_count;
+/* ipc stuff */
+	struct sysv_sem sysvsem;
+/* CPU-specific state of this task */
+	struct thread_struct thread;
+/* filesystem information */
+	struct fs_struct *fs;
+/* open file information */
+	struct files_struct *files;
+/* namespace */
+	struct namespace *namespace;
+/* signal handlers */
+	struct signal_struct *signal;
+	struct sighand_struct *sighand;
+
+	sigset_t blocked, real_blocked;
+	struct sigpending pending;
+
+	unsigned long sas_ss_sp;
+	size_t sas_ss_size;
+	int (*notifier)(void *priv);
+	void *notifier_data;
+	sigset_t *notifier_mask;
+	
+	void *security;
+	struct audit_context *audit_context;
+
+/* Thread group tracking */
+   	u32 parent_exec_id;
+   	u32 self_exec_id;
+/* Protection of (de-)allocation: mm, files, fs, tty, keyrings */
+	spinlock_t alloc_lock;
+/* Protection of proc_dentry: nesting proc_lock, dcache_lock, write_lock_irq(&tasklist_lock); */
+	spinlock_t proc_lock;
+/* context-switch lock */
+	spinlock_t switch_lock;
+
+/* journalling filesystem info */
+	void *journal_info;
+
+/* VM state */
+	struct reclaim_state *reclaim_state;
+
+	struct dentry *proc_dentry;
+	struct backing_dev_info *backing_dev_info;
+
+	struct io_context *io_context;
+
+	unsigned long ptrace_message;
+	siginfo_t *last_siginfo; /* For ptrace use.  */
+/*
+ * current io wait handle: wait queue entry to use for io waits
+ * If this thread is processing aio, this points at the waitqueue
+ * inside the currently handled kiocb. It may be NULL (i.e. default
+ * to a stack based synchronous wait) if its doing sync IO.
+ */
+	wait_queue_t *io_wait;
+/* i/o counters(bytes read/written, #syscalls */
+	u64 rchar, wchar, syscr, syscw;
+#if defined(CONFIG_BSD_PROCESS_ACCT)
+	u64 acct_rss_mem1;	/* accumulated rss usage */
+	u64 acct_vm_mem1;	/* accumulated virtual memory usage */
+	clock_t acct_stimexpd;	/* clock_t-converted stime since last update */
+#endif
+#ifdef CONFIG_NUMA
+  	struct mempolicy *mempolicy;
+	short il_next;
+#endif
+};
+
+static inline pid_t process_group(struct task_struct *tsk)
+{
+	return tsk->signal->pgrp;
+}
+
+/**
+ * pid_alive - check that a task structure is not stale
+ * @p: Task structure to be checked.
+ *
+ * Test if a process is not yet dead (at most zombie state)
+ * If pid_alive fails, then pointers within the task structure
+ * can be stale and must not be dereferenced.
+ */
+static inline int pid_alive(struct task_struct *p)
+{
+	return p->pids[PIDTYPE_PID].nr != 0;
+}
+
+extern void free_task(struct task_struct *tsk);
+extern void __put_task_struct(struct task_struct *tsk);
+#define get_task_struct(tsk) do { atomic_inc(&(tsk)->usage); } while(0)
+#define put_task_struct(tsk) \
+do { if (atomic_dec_and_test(&(tsk)->usage)) __put_task_struct(tsk); } while(0)
+
+/*
+ * Per process flags
+ */
+#define PF_ALIGNWARN	0x00000001	/* Print alignment warning msgs */
+					/* Not implemented yet, only for 486*/
+#define PF_STARTING	0x00000002	/* being created */
+#define PF_EXITING	0x00000004	/* getting shut down */
+#define PF_DEAD		0x00000008	/* Dead */
+#define PF_FORKNOEXEC	0x00000040	/* forked but didn't exec */
+#define PF_SUPERPRIV	0x00000100	/* used super-user privileges */
+#define PF_DUMPCORE	0x00000200	/* dumped core */
+#define PF_SIGNALED	0x00000400	/* killed by a signal */
+#define PF_MEMALLOC	0x00000800	/* Allocating memory */
+#define PF_FLUSHER	0x00001000	/* responsible for disk writeback */
+#define PF_USED_MATH	0x00002000	/* if unset the fpu must be initialized before use */
+#define PF_FREEZE	0x00004000	/* this task is being frozen for suspend now */
+#define PF_NOFREEZE	0x00008000	/* this thread should not be frozen */
+#define PF_FROZEN	0x00010000	/* frozen for system suspend */
+#define PF_FSTRANS	0x00020000	/* inside a filesystem transaction */
+#define PF_KSWAPD	0x00040000	/* I am kswapd */
+#define PF_SWAPOFF	0x00080000	/* I am in swapoff */
+#define PF_LESS_THROTTLE 0x00100000	/* Throttle me less: I clean memory */
+#define PF_SYNCWRITE	0x00200000	/* I am doing a sync write */
+#define PF_BORROWED_MM	0x00400000	/* I am a kthread doing use_mm */
+
+/*
+ * Only the _current_ task can read/write to tsk->flags, but other
+ * tasks can access tsk->flags in readonly mode for example
+ * with tsk_used_math (like during threaded core dumping).
+ * There is however an exception to this rule during ptrace
+ * or during fork: the ptracer task is allowed to write to the
+ * child->flags of its traced child (same goes for fork, the parent
+ * can write to the child->flags), because we're guaranteed the
+ * child is not running and in turn not changing child->flags
+ * at the same time the parent does it.
+ */
+#define clear_stopped_child_used_math(child) do { (child)->flags &= ~PF_USED_MATH; } while (0)
+#define set_stopped_child_used_math(child) do { (child)->flags |= PF_USED_MATH; } while (0)
+#define clear_used_math() clear_stopped_child_used_math(current)
+#define set_used_math() set_stopped_child_used_math(current)
+#define conditional_stopped_child_used_math(condition, child) \
+	do { (child)->flags &= ~PF_USED_MATH, (child)->flags |= (condition) ? PF_USED_MATH : 0; } while (0)
+#define conditional_used_math(condition) \
+	conditional_stopped_child_used_math(condition, current)
+#define copy_to_stopped_child_used_math(child) \
+	do { (child)->flags &= ~PF_USED_MATH, (child)->flags |= current->flags & PF_USED_MATH; } while (0)
+/* NOTE: this will return 0 or PF_USED_MATH, it will never return 1 */
+#define tsk_used_math(p) ((p)->flags & PF_USED_MATH)
+#define used_math() tsk_used_math(current)
+
+#ifdef CONFIG_SMP
+extern int set_cpus_allowed(task_t *p, cpumask_t new_mask);
+#else
+static inline int set_cpus_allowed(task_t *p, cpumask_t new_mask)
+{
+	if (!cpus_intersects(new_mask, cpu_online_map))
+		return -EINVAL;
+	return 0;
+}
+#endif
+
+extern unsigned long long sched_clock(void);
+
+/* sched_exec is called by processes performing an exec */
+#ifdef CONFIG_SMP
+extern void sched_exec(void);
+#else
+#define sched_exec()   {}
+#endif
+
+#ifdef CONFIG_HOTPLUG_CPU
+extern void idle_task_exit(void);
+#else
+static inline void idle_task_exit(void) {}
+#endif
+
+extern void sched_idle_next(void);
+extern void set_user_nice(task_t *p, long nice);
+extern int task_prio(const task_t *p);
+extern int task_nice(const task_t *p);
+extern int task_curr(const task_t *p);
+extern int idle_cpu(int cpu);
+extern int sched_setscheduler(struct task_struct *, int, struct sched_param *);
+extern task_t *idle_task(int cpu);
+
+void yield(void);
+
+/*
+ * The default (Linux) execution domain.
+ */
+extern struct exec_domain	default_exec_domain;
+
+union thread_union {
+	struct thread_info thread_info;
+	unsigned long stack[THREAD_SIZE/sizeof(long)];
+};
+
+#ifndef __HAVE_ARCH_KSTACK_END
+static inline int kstack_end(void *addr)
+{
+	/* Reliable end of stack detection:
+	 * Some APM bios versions misalign the stack
+	 */
+	return !(((unsigned long)addr+sizeof(void*)-1) & (THREAD_SIZE-sizeof(void*)));
+}
+#endif
+
+extern union thread_union init_thread_union;
+extern struct task_struct init_task;
+
+extern struct   mm_struct init_mm;
+
+#define find_task_by_pid(nr)	find_task_by_pid_type(PIDTYPE_PID, nr)
+extern struct task_struct *find_task_by_pid_type(int type, int pid);
+extern void set_special_pids(pid_t session, pid_t pgrp);
+extern void __set_special_pids(pid_t session, pid_t pgrp);
+
+/* per-UID process charging. */
+extern struct user_struct * alloc_uid(uid_t);
+static inline struct user_struct *get_uid(struct user_struct *u)
+{
+	atomic_inc(&u->__count);
+	return u;
+}
+extern void free_uid(struct user_struct *);
+extern void switch_uid(struct user_struct *);
+
+#include <asm/current.h>
+
+extern void do_timer(struct pt_regs *);
+
+extern int FASTCALL(wake_up_state(struct task_struct * tsk, unsigned int state));
+extern int FASTCALL(wake_up_process(struct task_struct * tsk));
+extern void FASTCALL(wake_up_new_task(struct task_struct * tsk,
+						unsigned long clone_flags));
+#ifdef CONFIG_SMP
+ extern void kick_process(struct task_struct *tsk);
+#else
+ static inline void kick_process(struct task_struct *tsk) { }
+#endif
+extern void FASTCALL(sched_fork(task_t * p));
+extern void FASTCALL(sched_exit(task_t * p));
+
+extern int in_group_p(gid_t);
+extern int in_egroup_p(gid_t);
+
+extern void proc_caches_init(void);
+extern void flush_signals(struct task_struct *);
+extern void flush_signal_handlers(struct task_struct *, int force_default);
+extern int dequeue_signal(struct task_struct *tsk, sigset_t *mask, siginfo_t *info);
+
+static inline int dequeue_signal_lock(struct task_struct *tsk, sigset_t *mask, siginfo_t *info)
+{
+	unsigned long flags;
+	int ret;
+
+	spin_lock_irqsave(&tsk->sighand->siglock, flags);
+	ret = dequeue_signal(tsk, mask, info);
+	spin_unlock_irqrestore(&tsk->sighand->siglock, flags);
+
+	return ret;
+}	
+
+extern void block_all_signals(int (*notifier)(void *priv), void *priv,
+			      sigset_t *mask);
+extern void unblock_all_signals(void);
+extern void release_task(struct task_struct * p);
+extern int send_sig_info(int, struct siginfo *, struct task_struct *);
+extern int send_group_sig_info(int, struct siginfo *, struct task_struct *);
+extern int force_sigsegv(int, struct task_struct *);
+extern int force_sig_info(int, struct siginfo *, struct task_struct *);
+extern int __kill_pg_info(int sig, struct siginfo *info, pid_t pgrp);
+extern int kill_pg_info(int, struct siginfo *, pid_t);
+extern int kill_proc_info(int, struct siginfo *, pid_t);
+extern void do_notify_parent(struct task_struct *, int);
+extern void force_sig(int, struct task_struct *);
+extern void force_sig_specific(int, struct task_struct *);
+extern int send_sig(int, struct task_struct *, int);
+extern void zap_other_threads(struct task_struct *p);
+extern int kill_pg(pid_t, int, int);
+extern int kill_sl(pid_t, int, int);
+extern int kill_proc(pid_t, int, int);
+extern struct sigqueue *sigqueue_alloc(void);
+extern void sigqueue_free(struct sigqueue *);
+extern int send_sigqueue(int, struct sigqueue *,  struct task_struct *);
+extern int send_group_sigqueue(int, struct sigqueue *,  struct task_struct *);
+extern int do_sigaction(int, const struct k_sigaction *, struct k_sigaction *);
+extern int do_sigaltstack(const stack_t __user *, stack_t __user *, unsigned long);
+
+/* These can be the second arg to send_sig_info/send_group_sig_info.  */
+#define SEND_SIG_NOINFO ((struct siginfo *) 0)
+#define SEND_SIG_PRIV	((struct siginfo *) 1)
+#define SEND_SIG_FORCED	((struct siginfo *) 2)
+
+/* True if we are on the alternate signal stack.  */
+
+static inline int on_sig_stack(unsigned long sp)
+{
+	return (sp - current->sas_ss_sp < current->sas_ss_size);
+}
+
+static inline int sas_ss_flags(unsigned long sp)
+{
+	return (current->sas_ss_size == 0 ? SS_DISABLE
+		: on_sig_stack(sp) ? SS_ONSTACK : 0);
+}
+
+
+#ifdef CONFIG_SECURITY
+/* code is in security.c */
+extern int capable(int cap);
+#else
+static inline int capable(int cap)
+{
+	if (cap_raised(current->cap_effective, cap)) {
+		current->flags |= PF_SUPERPRIV;
+		return 1;
+	}
+	return 0;
+}
+#endif
+
+/*
+ * Routines for handling mm_structs
+ */
+extern struct mm_struct * mm_alloc(void);
+
+/* mmdrop drops the mm and the page tables */
+extern void FASTCALL(__mmdrop(struct mm_struct *));
+static inline void mmdrop(struct mm_struct * mm)
+{
+	if (atomic_dec_and_test(&mm->mm_count))
+		__mmdrop(mm);
+}
+
+/* mmput gets rid of the mappings and all user-space */
+extern void mmput(struct mm_struct *);
+/* Grab a reference to a task's mm, if it is not already going away */
+extern struct mm_struct *get_task_mm(struct task_struct *task);
+/* Remove the current tasks stale references to the old mm_struct */
+extern void mm_release(struct task_struct *, struct mm_struct *);
+
+extern int  copy_thread(int, unsigned long, unsigned long, unsigned long, struct task_struct *, struct pt_regs *);
+extern void flush_thread(void);
+extern void exit_thread(void);
+
+extern void exit_mm(struct task_struct *);
+extern void exit_files(struct task_struct *);
+extern void exit_signal(struct task_struct *);
+extern void __exit_signal(struct task_struct *);
+extern void exit_sighand(struct task_struct *);
+extern void __exit_sighand(struct task_struct *);
+extern void exit_itimers(struct signal_struct *);
+
+extern NORET_TYPE void do_group_exit(int);
+
+extern void reparent_to_init(void);
+extern void daemonize(const char *, ...);
+extern int allow_signal(int);
+extern int disallow_signal(int);
+extern task_t *child_reaper;
+
+extern int do_execve(char *, char __user * __user *, char __user * __user *, struct pt_regs *);
+extern long do_fork(unsigned long, unsigned long, struct pt_regs *, unsigned long, int __user *, int __user *);
+task_t *fork_idle(int);
+
+extern void set_task_comm(struct task_struct *tsk, char *from);
+extern void get_task_comm(char *to, struct task_struct *tsk);
+
+#ifdef CONFIG_SMP
+extern void wait_task_inactive(task_t * p);
+#else
+#define wait_task_inactive(p)	do { } while (0)
+#endif
+
+#define remove_parent(p)	list_del_init(&(p)->sibling)
+#define add_parent(p, parent)	list_add_tail(&(p)->sibling,&(parent)->children)
+
+#define REMOVE_LINKS(p) do {					\
+	if (thread_group_leader(p))				\
+		list_del_init(&(p)->tasks);			\
+	remove_parent(p);					\
+	} while (0)
+
+#define SET_LINKS(p) do {					\
+	if (thread_group_leader(p))				\
+		list_add_tail(&(p)->tasks,&init_task.tasks);	\
+	add_parent(p, (p)->parent);				\
+	} while (0)
+
+#define next_task(p)	list_entry((p)->tasks.next, struct task_struct, tasks)
+#define prev_task(p)	list_entry((p)->tasks.prev, struct task_struct, tasks)
+
+#define for_each_process(p) \
+	for (p = &init_task ; (p = next_task(p)) != &init_task ; )
+
+/*
+ * Careful: do_each_thread/while_each_thread is a double loop so
+ *          'break' will not work as expected - use goto instead.
+ */
+#define do_each_thread(g, t) \
+	for (g = t = &init_task ; (g = t = next_task(g)) != &init_task ; ) do
+
+#define while_each_thread(g, t) \
+	while ((t = next_thread(t)) != g)
+
+extern task_t * FASTCALL(next_thread(const task_t *p));
+
+#define thread_group_leader(p)	(p->pid == p->tgid)
+
+static inline int thread_group_empty(task_t *p)
+{
+	return list_empty(&p->pids[PIDTYPE_TGID].pid_list);
+}
+
+#define delay_group_leader(p) \
+		(thread_group_leader(p) && !thread_group_empty(p))
+
+extern void unhash_process(struct task_struct *p);
+
+/*
+ * Protects ->fs, ->files, ->mm, ->ptrace, ->group_info, ->comm, keyring
+ * subscriptions and synchronises with wait4().  Also used in procfs.
+ *
+ * Nests both inside and outside of read_lock(&tasklist_lock).
+ * It must not be nested with write_lock_irq(&tasklist_lock),
+ * neither inside nor outside.
+ */
+static inline void task_lock(struct task_struct *p)
+{
+	spin_lock(&p->alloc_lock);
+}
+
+static inline void task_unlock(struct task_struct *p)
+{
+	spin_unlock(&p->alloc_lock);
+}
+
+/* set thread flags in other task's structures
+ * - see asm/thread_info.h for TIF_xxxx flags available
+ */
+static inline void set_tsk_thread_flag(struct task_struct *tsk, int flag)
+{
+	set_ti_thread_flag(tsk->thread_info,flag);
+}
+
+static inline void clear_tsk_thread_flag(struct task_struct *tsk, int flag)
+{
+	clear_ti_thread_flag(tsk->thread_info,flag);
+}
+
+static inline int test_and_set_tsk_thread_flag(struct task_struct *tsk, int flag)
+{
+	return test_and_set_ti_thread_flag(tsk->thread_info,flag);
+}
+
+static inline int test_and_clear_tsk_thread_flag(struct task_struct *tsk, int flag)
+{
+	return test_and_clear_ti_thread_flag(tsk->thread_info,flag);
+}
+
+static inline int test_tsk_thread_flag(struct task_struct *tsk, int flag)
+{
+	return test_ti_thread_flag(tsk->thread_info,flag);
+}
+
+static inline void set_tsk_need_resched(struct task_struct *tsk)
+{
+	set_tsk_thread_flag(tsk,TIF_NEED_RESCHED);
+}
+
+static inline void clear_tsk_need_resched(struct task_struct *tsk)
+{
+	clear_tsk_thread_flag(tsk,TIF_NEED_RESCHED);
+}
+
+static inline int signal_pending(struct task_struct *p)
+{
+	return unlikely(test_tsk_thread_flag(p,TIF_SIGPENDING));
+}
+  
+static inline int need_resched(void)
+{
+	return unlikely(test_thread_flag(TIF_NEED_RESCHED));
+}
+
+/*
+ * cond_resched() and cond_resched_lock(): latency reduction via
+ * explicit rescheduling in places that are safe. The return
+ * value indicates whether a reschedule was done in fact.
+ * cond_resched_lock() will drop the spinlock before scheduling,
+ * cond_resched_softirq() will enable bhs before scheduling.
+ */
+extern int cond_resched(void);
+extern int cond_resched_lock(spinlock_t * lock);
+extern int cond_resched_softirq(void);
+
+/*
+ * Does a critical section need to be broken due to another
+ * task waiting?:
+ */
+#if defined(CONFIG_PREEMPT) && defined(CONFIG_SMP)
+# define need_lockbreak(lock) ((lock)->break_lock)
+#else
+# define need_lockbreak(lock) 0
+#endif
+
+/*
+ * Does a critical section need to be broken due to another
+ * task waiting or preemption being signalled:
+ */
+static inline int lock_need_resched(spinlock_t *lock)
+{
+	if (need_lockbreak(lock) || need_resched())
+		return 1;
+	return 0;
+}
+
+/* Reevaluate whether the task has signals pending delivery.
+   This is required every time the blocked sigset_t changes.
+   callers must hold sighand->siglock.  */
+
+extern FASTCALL(void recalc_sigpending_tsk(struct task_struct *t));
+extern void recalc_sigpending(void);
+
+extern void signal_wake_up(struct task_struct *t, int resume_stopped);
+
+/*
+ * Wrappers for p->thread_info->cpu access. No-op on UP.
+ */
+#ifdef CONFIG_SMP
+
+static inline unsigned int task_cpu(const struct task_struct *p)
+{
+	return p->thread_info->cpu;
+}
+
+static inline void set_task_cpu(struct task_struct *p, unsigned int cpu)
+{
+	p->thread_info->cpu = cpu;
+}
+
+#else
+
+static inline unsigned int task_cpu(const struct task_struct *p)
+{
+	return 0;
+}
+
+static inline void set_task_cpu(struct task_struct *p, unsigned int cpu)
+{
+}
+
+#endif /* CONFIG_SMP */
+
+#ifdef HAVE_ARCH_PICK_MMAP_LAYOUT
+extern void arch_pick_mmap_layout(struct mm_struct *mm);
+#else
+static inline void arch_pick_mmap_layout(struct mm_struct *mm)
+{
+	mm->mmap_base = TASK_UNMAPPED_BASE;
+	mm->get_unmapped_area = arch_get_unmapped_area;
+	mm->unmap_area = arch_unmap_area;
+}
+#endif
+
+extern long sched_setaffinity(pid_t pid, cpumask_t new_mask);
+extern long sched_getaffinity(pid_t pid, cpumask_t *mask);
+
+#ifdef CONFIG_MAGIC_SYSRQ
+
+extern void normalize_rt_tasks(void);
+
+#endif
+
+/* try_to_freeze
+ *
+ * Checks whether we need to enter the refrigerator
+ * and returns 1 if we did so.
+ */
+#ifdef CONFIG_PM
+extern void refrigerator(unsigned long);
+extern int freeze_processes(void);
+extern void thaw_processes(void);
+
+static inline int try_to_freeze(unsigned long refrigerator_flags)
+{
+	if (unlikely(current->flags & PF_FREEZE)) {
+		refrigerator(refrigerator_flags);
+		return 1;
+	} else
+		return 0;
+}
+#else
+static inline void refrigerator(unsigned long flag) {}
+static inline int freeze_processes(void) { BUG(); return 0; }
+static inline void thaw_processes(void) {}
+
+static inline int try_to_freeze(unsigned long refrigerator_flags)
+{
+	return 0;
+}
+#endif /* CONFIG_PM */
+#endif /* __KERNEL__ */
+
+#endif
diff -Nrub linux-2.6.11.7-orig/init/main.c linux-2.6.11.7-snare/init/main.c
--- linux-2.6.11.7-orig/init/main.c	2005-04-07 14:57:21.000000000 -0400
+++ linux-2.6.11.7-snare/init/main.c	2005-04-22 11:19:47.196665464 -0400
@@ -29,6 +29,7 @@
 #include <linux/hdreg.h>
 #include <linux/bootmem.h>
 #include <linux/tty.h>
+#include <linux/saudit.h>
 #include <linux/gfp.h>
 #include <linux/percpu.h>
 #include <linux/kmod.h>
@@ -506,6 +507,9 @@
 #ifdef CONFIG_PROC_FS
 	proc_root_init();
 #endif
+#if defined(CONFIG_C2_AUDIT)
+	saudit_init();
+#endif
 	check_bugs();
 
 	acpi_early_init(); /* before LAPIC and SMP init */
diff -Nrub linux-2.6.11.7-orig/init/main.c.orig linux-2.6.11.7-snare/init/main.c.orig
--- linux-2.6.11.7-orig/init/main.c.orig	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.11.7-snare/init/main.c.orig	2005-04-07 14:57:21.000000000 -0400
@@ -0,0 +1,696 @@
+/*
+ *  linux/init/main.c
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ *
+ *  GK 2/5/95  -  Changed to support mounting root fs via NFS
+ *  Added initrd & change_root: Werner Almesberger & Hans Lermen, Feb '96
+ *  Moan early if gcc is old, avoiding bogus kernels - Paul Gortmaker, May '96
+ *  Simplified starting of init:  Michael A. Griffith <grif@acm.org> 
+ */
+
+#define __KERNEL_SYSCALLS__
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/proc_fs.h>
+#include <linux/devfs_fs_kernel.h>
+#include <linux/kernel.h>
+#include <linux/syscalls.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/delay.h>
+#include <linux/utsname.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/smp_lock.h>
+#include <linux/initrd.h>
+#include <linux/hdreg.h>
+#include <linux/bootmem.h>
+#include <linux/tty.h>
+#include <linux/gfp.h>
+#include <linux/percpu.h>
+#include <linux/kmod.h>
+#include <linux/kernel_stat.h>
+#include <linux/security.h>
+#include <linux/workqueue.h>
+#include <linux/profile.h>
+#include <linux/rcupdate.h>
+#include <linux/moduleparam.h>
+#include <linux/kallsyms.h>
+#include <linux/writeback.h>
+#include <linux/cpu.h>
+#include <linux/efi.h>
+#include <linux/unistd.h>
+#include <linux/rmap.h>
+#include <linux/mempolicy.h>
+#include <linux/key.h>
+
+#include <asm/io.h>
+#include <asm/bugs.h>
+#include <asm/setup.h>
+
+/*
+ * This is one of the first .c files built. Error out early
+ * if we have compiler trouble..
+ */
+#if __GNUC__ == 2 && __GNUC_MINOR__ == 96
+#ifdef CONFIG_FRAME_POINTER
+#error This compiler cannot compile correctly with frame pointers enabled
+#endif
+#endif
+
+#ifdef CONFIG_X86_LOCAL_APIC
+#include <asm/smp.h>
+#endif
+
+/*
+ * Versions of gcc older than that listed below may actually compile
+ * and link okay, but the end product can have subtle run time bugs.
+ * To avoid associated bogus bug reports, we flatly refuse to compile
+ * with a gcc that is known to be too old from the very beginning.
+ */
+#if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 95)
+#error Sorry, your GCC is too old. It builds incorrect kernels.
+#endif
+
+static int init(void *);
+
+extern void init_IRQ(void);
+extern void sock_init(void);
+extern void fork_init(unsigned long);
+extern void mca_init(void);
+extern void sbus_init(void);
+extern void sysctl_init(void);
+extern void signals_init(void);
+extern void buffer_init(void);
+extern void pidhash_init(void);
+extern void pidmap_init(void);
+extern void prio_tree_init(void);
+extern void radix_tree_init(void);
+extern void free_initmem(void);
+extern void populate_rootfs(void);
+extern void driver_init(void);
+extern void prepare_namespace(void);
+#ifdef	CONFIG_ACPI
+extern void acpi_early_init(void);
+#else
+static inline void acpi_early_init(void) { }
+#endif
+
+#ifdef CONFIG_TC
+extern void tc_init(void);
+#endif
+
+enum system_states system_state;
+EXPORT_SYMBOL(system_state);
+
+/*
+ * Boot command-line arguments
+ */
+#define MAX_INIT_ARGS 32
+#define MAX_INIT_ENVS 32
+
+extern void time_init(void);
+/* Default late time init is NULL. archs can override this later. */
+void (*late_time_init)(void);
+extern void softirq_init(void);
+
+/* Untouched command line (eg. for /proc) saved by arch-specific code. */
+char saved_command_line[COMMAND_LINE_SIZE];
+
+static char *execute_command;
+
+/* Setup configured maximum number of CPUs to activate */
+static unsigned int max_cpus = NR_CPUS;
+
+/*
+ * Setup routine for controlling SMP activation
+ *
+ * Command-line option of "nosmp" or "maxcpus=0" will disable SMP
+ * activation entirely (the MPS table probe still happens, though).
+ *
+ * Command-line option of "maxcpus=<NUM>", where <NUM> is an integer
+ * greater than 0, limits the maximum number of CPUs activated in
+ * SMP mode to <NUM>.
+ */
+static int __init nosmp(char *str)
+{
+	max_cpus = 0;
+	return 1;
+}
+
+__setup("nosmp", nosmp);
+
+static int __init maxcpus(char *str)
+{
+	get_option(&str, &max_cpus);
+	return 1;
+}
+
+__setup("maxcpus=", maxcpus);
+
+static char * argv_init[MAX_INIT_ARGS+2] = { "init", NULL, };
+char * envp_init[MAX_INIT_ENVS+2] = { "HOME=/", "TERM=linux", NULL, };
+static const char *panic_later, *panic_param;
+
+extern struct obs_kernel_param __setup_start[], __setup_end[];
+
+static int __init obsolete_checksetup(char *line)
+{
+	struct obs_kernel_param *p;
+
+	p = __setup_start;
+	do {
+		int n = strlen(p->str);
+		if (!strncmp(line, p->str, n)) {
+			if (p->early) {
+				/* Already done in parse_early_param?  (Needs
+				 * exact match on param part) */
+				if (line[n] == '\0' || line[n] == '=')
+					return 1;
+			} else if (!p->setup_func) {
+				printk(KERN_WARNING "Parameter %s is obsolete,"
+				       " ignored\n", p->str);
+				return 1;
+			} else if (p->setup_func(line + n))
+				return 1;
+		}
+		p++;
+	} while (p < __setup_end);
+	return 0;
+}
+
+/*
+ * This should be approx 2 Bo*oMips to start (note initial shift), and will
+ * still work even if initially too large, it will just take slightly longer
+ */
+unsigned long loops_per_jiffy = (1<<12);
+
+EXPORT_SYMBOL(loops_per_jiffy);
+
+static int __init debug_kernel(char *str)
+{
+	if (*str)
+		return 0;
+	console_loglevel = 10;
+	return 1;
+}
+
+static int __init quiet_kernel(char *str)
+{
+	if (*str)
+		return 0;
+	console_loglevel = 4;
+	return 1;
+}
+
+__setup("debug", debug_kernel);
+__setup("quiet", quiet_kernel);
+
+/*
+ * Unknown boot options get handed to init, unless they look like
+ * failed parameters
+ */
+static int __init unknown_bootoption(char *param, char *val)
+{
+	/* Change NUL term back to "=", to make "param" the whole string. */
+	if (val) {
+		/* param=val or param="val"? */
+		if (val == param+strlen(param)+1)
+			val[-1] = '=';
+		else if (val == param+strlen(param)+2) {
+			val[-2] = '=';
+			memmove(val-1, val, strlen(val)+1);
+			val--;
+		} else
+			BUG();
+	}
+
+	/* Handle obsolete-style parameters */
+	if (obsolete_checksetup(param))
+		return 0;
+
+	/*
+	 * Preemptive maintenance for "why didn't my mispelled command
+	 * line work?"
+	 */
+	if (strchr(param, '.') && (!val || strchr(param, '.') < val)) {
+		printk(KERN_ERR "Unknown boot option `%s': ignoring\n", param);
+		return 0;
+	}
+
+	if (panic_later)
+		return 0;
+
+	if (val) {
+		/* Environment option */
+		unsigned int i;
+		for (i = 0; envp_init[i]; i++) {
+			if (i == MAX_INIT_ENVS) {
+				panic_later = "Too many boot env vars at `%s'";
+				panic_param = param;
+			}
+			if (!strncmp(param, envp_init[i], val - param))
+				break;
+		}
+		envp_init[i] = param;
+	} else {
+		/* Command line option */
+		unsigned int i;
+		for (i = 0; argv_init[i]; i++) {
+			if (i == MAX_INIT_ARGS) {
+				panic_later = "Too many boot init vars at `%s'";
+				panic_param = param;
+			}
+		}
+		argv_init[i] = param;
+	}
+	return 0;
+}
+
+static int __init init_setup(char *str)
+{
+	unsigned int i;
+
+	execute_command = str;
+	/*
+	 * In case LILO is going to boot us with default command line,
+	 * it prepends "auto" before the whole cmdline which makes
+	 * the shell think it should execute a script with such name.
+	 * So we ignore all arguments entered _before_ init=... [MJ]
+	 */
+	for (i = 1; i < MAX_INIT_ARGS; i++)
+		argv_init[i] = NULL;
+	return 1;
+}
+__setup("init=", init_setup);
+
+extern void setup_arch(char **);
+
+#ifndef CONFIG_SMP
+
+#ifdef CONFIG_X86_LOCAL_APIC
+static void __init smp_init(void)
+{
+	APIC_init_uniprocessor();
+}
+#else
+#define smp_init()	do { } while (0)
+#endif
+
+static inline void setup_per_cpu_areas(void) { }
+static inline void smp_prepare_cpus(unsigned int maxcpus) { }
+
+#else
+
+#ifdef __GENERIC_PER_CPU
+unsigned long __per_cpu_offset[NR_CPUS];
+
+EXPORT_SYMBOL(__per_cpu_offset);
+
+static void __init setup_per_cpu_areas(void)
+{
+	unsigned long size, i;
+	char *ptr;
+	/* Created by linker magic */
+	extern char __per_cpu_start[], __per_cpu_end[];
+
+	/* Copy section for each CPU (we discard the original) */
+	size = ALIGN(__per_cpu_end - __per_cpu_start, SMP_CACHE_BYTES);
+#ifdef CONFIG_MODULES
+	if (size < PERCPU_ENOUGH_ROOM)
+		size = PERCPU_ENOUGH_ROOM;
+#endif
+
+	ptr = alloc_bootmem(size * NR_CPUS);
+
+	for (i = 0; i < NR_CPUS; i++, ptr += size) {
+		__per_cpu_offset[i] = ptr - __per_cpu_start;
+		memcpy(ptr, __per_cpu_start, __per_cpu_end - __per_cpu_start);
+	}
+}
+#endif /* !__GENERIC_PER_CPU */
+
+/* Called by boot processor to activate the rest. */
+static void __init smp_init(void)
+{
+	unsigned int i;
+
+	/* FIXME: This should be done in userspace --RR */
+	for_each_present_cpu(i) {
+		if (num_online_cpus() >= max_cpus)
+			break;
+		if (!cpu_online(i))
+			cpu_up(i);
+	}
+
+	/* Any cleanup work */
+	printk("Brought up %ld CPUs\n", (long)num_online_cpus());
+	smp_cpus_done(max_cpus);
+#if 0
+	/* Get other processors into their bootup holding patterns. */
+
+	smp_threads_ready=1;
+	smp_commence();
+#endif
+}
+
+#endif
+
+/*
+ * We need to finalize in a non-__init function or else race conditions
+ * between the root thread and the init thread may cause start_kernel to
+ * be reaped by free_initmem before the root thread has proceeded to
+ * cpu_idle.
+ *
+ * gcc-3.4 accidentally inlines this function, so use noinline.
+ */
+
+static void noinline rest_init(void)
+	__releases(kernel_lock)
+{
+	kernel_thread(init, NULL, CLONE_FS | CLONE_SIGHAND);
+	numa_default_policy();
+	unlock_kernel();
+	preempt_enable_no_resched();
+	cpu_idle();
+} 
+
+/* Check for early params. */
+static int __init do_early_param(char *param, char *val)
+{
+	struct obs_kernel_param *p;
+
+	for (p = __setup_start; p < __setup_end; p++) {
+		if (p->early && strcmp(param, p->str) == 0) {
+			if (p->setup_func(val) != 0)
+				printk(KERN_WARNING
+				       "Malformed early option '%s'\n", param);
+		}
+	}
+	/* We accept everything at this stage. */
+	return 0;
+}
+
+/* Arch code calls this early on, or if not, just before other parsing. */
+void __init parse_early_param(void)
+{
+	static __initdata int done = 0;
+	static __initdata char tmp_cmdline[COMMAND_LINE_SIZE];
+
+	if (done)
+		return;
+
+	/* All fall through to do_early_param. */
+	strlcpy(tmp_cmdline, saved_command_line, COMMAND_LINE_SIZE);
+	parse_args("early options", tmp_cmdline, NULL, 0, do_early_param);
+	done = 1;
+}
+
+/*
+ *	Activate the first processor.
+ */
+
+asmlinkage void __init start_kernel(void)
+{
+	char * command_line;
+	extern struct kernel_param __start___param[], __stop___param[];
+/*
+ * Interrupts are still disabled. Do necessary setups, then
+ * enable them
+ */
+	lock_kernel();
+	page_address_init();
+	printk(linux_banner);
+	setup_arch(&command_line);
+	setup_per_cpu_areas();
+
+	/*
+	 * Mark the boot cpu "online" so that it can call console drivers in
+	 * printk() and can access its per-cpu storage.
+	 */
+	smp_prepare_boot_cpu();
+
+	/*
+	 * Set up the scheduler prior starting any interrupts (such as the
+	 * timer interrupt). Full topology setup happens at smp_init()
+	 * time - but meanwhile we still have a functioning scheduler.
+	 */
+	sched_init();
+	/*
+	 * Disable preemption - early bootup scheduling is extremely
+	 * fragile until we cpu_idle() for the first time.
+	 */
+	preempt_disable();
+	build_all_zonelists();
+	page_alloc_init();
+	printk("Kernel command line: %s\n", saved_command_line);
+	parse_early_param();
+	parse_args("Booting kernel", command_line, __start___param,
+		   __stop___param - __start___param,
+		   &unknown_bootoption);
+	sort_main_extable();
+	trap_init();
+	rcu_init();
+	init_IRQ();
+	pidhash_init();
+	init_timers();
+	softirq_init();
+	time_init();
+
+	/*
+	 * HACK ALERT! This is early. We're enabling the console before
+	 * we've done PCI setups etc, and console_init() must be aware of
+	 * this. But we do want output early, in case something goes wrong.
+	 */
+	console_init();
+	if (panic_later)
+		panic(panic_later, panic_param);
+	profile_init();
+	local_irq_enable();
+#ifdef CONFIG_BLK_DEV_INITRD
+	if (initrd_start && !initrd_below_start_ok &&
+			initrd_start < min_low_pfn << PAGE_SHIFT) {
+		printk(KERN_CRIT "initrd overwritten (0x%08lx < 0x%08lx) - "
+		    "disabling it.\n",initrd_start,min_low_pfn << PAGE_SHIFT);
+		initrd_start = 0;
+	}
+#endif
+	vfs_caches_init_early();
+	mem_init();
+	kmem_cache_init();
+	numa_policy_init();
+	if (late_time_init)
+		late_time_init();
+	calibrate_delay();
+	pidmap_init();
+	pgtable_cache_init();
+	prio_tree_init();
+	anon_vma_init();
+#ifdef CONFIG_X86
+	if (efi_enabled)
+		efi_enter_virtual_mode();
+#endif
+	fork_init(num_physpages);
+	proc_caches_init();
+	buffer_init();
+	unnamed_dev_init();
+	security_init();
+	vfs_caches_init(num_physpages);
+	radix_tree_init();
+	signals_init();
+	/* rootfs populating might need page-writeback */
+	page_writeback_init();
+#ifdef CONFIG_PROC_FS
+	proc_root_init();
+#endif
+	check_bugs();
+
+	acpi_early_init(); /* before LAPIC and SMP init */
+
+	/* Do the rest non-__init'ed, we're now alive */
+	rest_init();
+}
+
+static int __initdata initcall_debug;
+
+static int __init initcall_debug_setup(char *str)
+{
+	initcall_debug = 1;
+	return 1;
+}
+__setup("initcall_debug", initcall_debug_setup);
+
+struct task_struct *child_reaper = &init_task;
+
+extern initcall_t __initcall_start[], __initcall_end[];
+
+static void __init do_initcalls(void)
+{
+	initcall_t *call;
+	int count = preempt_count();
+
+	for (call = __initcall_start; call < __initcall_end; call++) {
+		char *msg;
+
+		if (initcall_debug) {
+			printk(KERN_DEBUG "Calling initcall 0x%p", *call);
+			print_fn_descriptor_symbol(": %s()", (unsigned long) *call);
+			printk("\n");
+		}
+
+		(*call)();
+
+		msg = NULL;
+		if (preempt_count() != count) {
+			msg = "preemption imbalance";
+			preempt_count() = count;
+		}
+		if (irqs_disabled()) {
+			msg = "disabled interrupts";
+			local_irq_enable();
+		}
+		if (msg) {
+			printk("error in initcall at 0x%p: "
+				"returned with %s\n", *call, msg);
+		}
+	}
+
+	/* Make sure there is no pending stuff from the initcall sequence */
+	flush_scheduled_work();
+}
+
+/*
+ * Ok, the machine is now initialized. None of the devices
+ * have been touched yet, but the CPU subsystem is up and
+ * running, and memory and process management works.
+ *
+ * Now we can finally start doing some real work..
+ */
+static void __init do_basic_setup(void)
+{
+	/* drivers will send hotplug events */
+	init_workqueues();
+	usermodehelper_init();
+	key_init();
+	driver_init();
+
+#ifdef CONFIG_SYSCTL
+	sysctl_init();
+#endif
+
+	/* Networking initialization needs a process context */ 
+	sock_init();
+
+	do_initcalls();
+}
+
+static void do_pre_smp_initcalls(void)
+{
+	extern int spawn_ksoftirqd(void);
+#ifdef CONFIG_SMP
+	extern int migration_init(void);
+
+	migration_init();
+#endif
+	spawn_ksoftirqd();
+}
+
+static void run_init_process(char *init_filename)
+{
+	argv_init[0] = init_filename;
+	execve(init_filename, argv_init, envp_init);
+}
+
+static inline void fixup_cpu_present_map(void)
+{
+#ifdef CONFIG_SMP
+	int i;
+
+	/*
+	 * If arch is not hotplug ready and did not populate
+	 * cpu_present_map, just make cpu_present_map same as cpu_possible_map
+	 * for other cpu bringup code to function as normal. e.g smp_init() etc.
+	 */
+	if (cpus_empty(cpu_present_map)) {
+		for_each_cpu(i) {
+			cpu_set(i, cpu_present_map);
+		}
+	}
+#endif
+}
+
+static int init(void * unused)
+{
+	lock_kernel();
+	/*
+	 * Tell the world that we're going to be the grim
+	 * reaper of innocent orphaned children.
+	 *
+	 * We don't want people to have to make incorrect
+	 * assumptions about where in the task array this
+	 * can be found.
+	 */
+	child_reaper = current;
+
+	/* Sets up cpus_possible() */
+	smp_prepare_cpus(max_cpus);
+
+	do_pre_smp_initcalls();
+
+	fixup_cpu_present_map();
+	smp_init();
+	sched_init_smp();
+
+	/*
+	 * Do this before initcalls, because some drivers want to access
+	 * firmware files.
+	 */
+	populate_rootfs();
+
+	do_basic_setup();
+
+	/*
+	 * check if there is an early userspace init.  If yes, let it do all
+	 * the work
+	 */
+	if (sys_access((const char __user *) "/init", 0) == 0)
+		execute_command = "/init";
+	else
+		prepare_namespace();
+
+	/*
+	 * Ok, we have completed the initial bootup, and
+	 * we're essentially up and running. Get rid of the
+	 * initmem segments and start the user-mode stuff..
+	 */
+	free_initmem();
+	unlock_kernel();
+	system_state = SYSTEM_RUNNING;
+	numa_default_policy();
+
+	if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)
+		printk("Warning: unable to open an initial console.\n");
+
+	(void) sys_dup(0);
+	(void) sys_dup(0);
+	
+	/*
+	 * We try each of these until one succeeds.
+	 *
+	 * The Bourne shell can be used instead of init if we are 
+	 * trying to recover a really broken machine.
+	 */
+
+	if (execute_command)
+		run_init_process(execute_command);
+
+	run_init_process("/sbin/init");
+	run_init_process("/etc/init");
+	run_init_process("/bin/init");
+	run_init_process("/bin/sh");
+
+	panic("No init found.  Try passing init= option to kernel.");
+}
diff -Nrub linux-2.6.11.7-orig/kernel/exit.c linux-2.6.11.7-snare/kernel/exit.c
--- linux-2.6.11.7-orig/kernel/exit.c	2005-04-07 14:58:18.000000000 -0400
+++ linux-2.6.11.7-snare/kernel/exit.c	2005-04-22 11:19:47.197665312 -0400
@@ -14,6 +14,7 @@
 #include <linux/personality.h>
 #include <linux/tty.h>
 #include <linux/namespace.h>
+#include <linux/saudit.h>
 #include <linux/key.h>
 #include <linux/security.h>
 #include <linux/cpu.h>
@@ -853,6 +854,7 @@
 
 asmlinkage long sys_exit(int error_code)
 {
+	saudit_exit(error_code);
 	do_exit((error_code&0xff)<<8);
 }
 
diff -Nrub linux-2.6.11.7-orig/kernel/exit.c.orig linux-2.6.11.7-snare/kernel/exit.c.orig
--- linux-2.6.11.7-orig/kernel/exit.c.orig	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.11.7-snare/kernel/exit.c.orig	2005-04-07 14:58:18.000000000 -0400
@@ -0,0 +1,1523 @@
+/*
+ *  linux/kernel/exit.c
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ */
+
+#include <linux/config.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/smp_lock.h>
+#include <linux/module.h>
+#include <linux/completion.h>
+#include <linux/personality.h>
+#include <linux/tty.h>
+#include <linux/namespace.h>
+#include <linux/key.h>
+#include <linux/security.h>
+#include <linux/cpu.h>
+#include <linux/acct.h>
+#include <linux/file.h>
+#include <linux/binfmts.h>
+#include <linux/ptrace.h>
+#include <linux/profile.h>
+#include <linux/mount.h>
+#include <linux/proc_fs.h>
+#include <linux/mempolicy.h>
+#include <linux/syscalls.h>
+
+#include <asm/uaccess.h>
+#include <asm/unistd.h>
+#include <asm/pgtable.h>
+#include <asm/mmu_context.h>
+
+extern void sem_exit (void);
+extern struct task_struct *child_reaper;
+
+int getrusage(struct task_struct *, int, struct rusage __user *);
+
+static void __unhash_process(struct task_struct *p)
+{
+	nr_threads--;
+	detach_pid(p, PIDTYPE_PID);
+	detach_pid(p, PIDTYPE_TGID);
+	if (thread_group_leader(p)) {
+		detach_pid(p, PIDTYPE_PGID);
+		detach_pid(p, PIDTYPE_SID);
+		if (p->pid)
+			__get_cpu_var(process_counts)--;
+	}
+
+	REMOVE_LINKS(p);
+}
+
+void release_task(struct task_struct * p)
+{
+	int zap_leader;
+	task_t *leader;
+	struct dentry *proc_dentry;
+
+repeat: 
+	atomic_dec(&p->user->processes);
+	spin_lock(&p->proc_lock);
+	proc_dentry = proc_pid_unhash(p);
+	write_lock_irq(&tasklist_lock);
+	if (unlikely(p->ptrace))
+		__ptrace_unlink(p);
+	BUG_ON(!list_empty(&p->ptrace_list) || !list_empty(&p->ptrace_children));
+	__exit_signal(p);
+	__exit_sighand(p);
+	__unhash_process(p);
+
+	/*
+	 * If we are the last non-leader member of the thread
+	 * group, and the leader is zombie, then notify the
+	 * group leader's parent process. (if it wants notification.)
+	 */
+	zap_leader = 0;
+	leader = p->group_leader;
+	if (leader != p && thread_group_empty(leader) && leader->exit_state == EXIT_ZOMBIE) {
+		BUG_ON(leader->exit_signal == -1);
+		do_notify_parent(leader, leader->exit_signal);
+		/*
+		 * If we were the last child thread and the leader has
+		 * exited already, and the leader's parent ignores SIGCHLD,
+		 * then we are the one who should release the leader.
+		 *
+		 * do_notify_parent() will have marked it self-reaping in
+		 * that case.
+		 */
+		zap_leader = (leader->exit_signal == -1);
+	}
+
+	sched_exit(p);
+	write_unlock_irq(&tasklist_lock);
+	spin_unlock(&p->proc_lock);
+	proc_pid_flush(proc_dentry);
+	release_thread(p);
+	put_task_struct(p);
+
+	p = leader;
+	if (unlikely(zap_leader))
+		goto repeat;
+}
+
+/* we are using it only for SMP init */
+
+void unhash_process(struct task_struct *p)
+{
+	struct dentry *proc_dentry;
+
+	spin_lock(&p->proc_lock);
+	proc_dentry = proc_pid_unhash(p);
+	write_lock_irq(&tasklist_lock);
+	__unhash_process(p);
+	write_unlock_irq(&tasklist_lock);
+	spin_unlock(&p->proc_lock);
+	proc_pid_flush(proc_dentry);
+}
+
+/*
+ * This checks not only the pgrp, but falls back on the pid if no
+ * satisfactory pgrp is found. I dunno - gdb doesn't work correctly
+ * without this...
+ */
+int session_of_pgrp(int pgrp)
+{
+	struct task_struct *p;
+	int sid = -1;
+
+	read_lock(&tasklist_lock);
+	do_each_task_pid(pgrp, PIDTYPE_PGID, p) {
+		if (p->signal->session > 0) {
+			sid = p->signal->session;
+			goto out;
+		}
+	} while_each_task_pid(pgrp, PIDTYPE_PGID, p);
+	p = find_task_by_pid(pgrp);
+	if (p)
+		sid = p->signal->session;
+out:
+	read_unlock(&tasklist_lock);
+	
+	return sid;
+}
+
+/*
+ * Determine if a process group is "orphaned", according to the POSIX
+ * definition in 2.2.2.52.  Orphaned process groups are not to be affected
+ * by terminal-generated stop signals.  Newly orphaned process groups are
+ * to receive a SIGHUP and a SIGCONT.
+ *
+ * "I ask you, have you ever known what it is to be an orphan?"
+ */
+static int will_become_orphaned_pgrp(int pgrp, task_t *ignored_task)
+{
+	struct task_struct *p;
+	int ret = 1;
+
+	do_each_task_pid(pgrp, PIDTYPE_PGID, p) {
+		if (p == ignored_task
+				|| p->exit_state
+				|| p->real_parent->pid == 1)
+			continue;
+		if (process_group(p->real_parent) != pgrp
+			    && p->real_parent->signal->session == p->signal->session) {
+			ret = 0;
+			break;
+		}
+	} while_each_task_pid(pgrp, PIDTYPE_PGID, p);
+	return ret;	/* (sighing) "Often!" */
+}
+
+int is_orphaned_pgrp(int pgrp)
+{
+	int retval;
+
+	read_lock(&tasklist_lock);
+	retval = will_become_orphaned_pgrp(pgrp, NULL);
+	read_unlock(&tasklist_lock);
+
+	return retval;
+}
+
+static inline int has_stopped_jobs(int pgrp)
+{
+	int retval = 0;
+	struct task_struct *p;
+
+	do_each_task_pid(pgrp, PIDTYPE_PGID, p) {
+		if (p->state != TASK_STOPPED)
+			continue;
+
+		/* If p is stopped by a debugger on a signal that won't
+		   stop it, then don't count p as stopped.  This isn't
+		   perfect but it's a good approximation.  */
+		if (unlikely (p->ptrace)
+		    && p->exit_code != SIGSTOP
+		    && p->exit_code != SIGTSTP
+		    && p->exit_code != SIGTTOU
+		    && p->exit_code != SIGTTIN)
+			continue;
+
+		retval = 1;
+		break;
+	} while_each_task_pid(pgrp, PIDTYPE_PGID, p);
+	return retval;
+}
+
+/**
+ * reparent_to_init() - Reparent the calling kernel thread to the init task.
+ *
+ * If a kernel thread is launched as a result of a system call, or if
+ * it ever exits, it should generally reparent itself to init so that
+ * it is correctly cleaned up on exit.
+ *
+ * The various task state such as scheduling policy and priority may have
+ * been inherited from a user process, so we reset them to sane values here.
+ *
+ * NOTE that reparent_to_init() gives the caller full capabilities.
+ */
+void reparent_to_init(void)
+{
+	write_lock_irq(&tasklist_lock);
+
+	ptrace_unlink(current);
+	/* Reparent to init */
+	REMOVE_LINKS(current);
+	current->parent = child_reaper;
+	current->real_parent = child_reaper;
+	SET_LINKS(current);
+
+	/* Set the exit signal to SIGCHLD so we signal init on exit */
+	current->exit_signal = SIGCHLD;
+
+	if ((current->policy == SCHED_NORMAL) && (task_nice(current) < 0))
+		set_user_nice(current, 0);
+	/* cpus_allowed? */
+	/* rt_priority? */
+	/* signals? */
+	security_task_reparent_to_init(current);
+	memcpy(current->signal->rlim, init_task.signal->rlim,
+	       sizeof(current->signal->rlim));
+	atomic_inc(&(INIT_USER->__count));
+	write_unlock_irq(&tasklist_lock);
+	switch_uid(INIT_USER);
+}
+
+void __set_special_pids(pid_t session, pid_t pgrp)
+{
+	struct task_struct *curr = current;
+
+	if (curr->signal->session != session) {
+		detach_pid(curr, PIDTYPE_SID);
+		curr->signal->session = session;
+		attach_pid(curr, PIDTYPE_SID, session);
+	}
+	if (process_group(curr) != pgrp) {
+		detach_pid(curr, PIDTYPE_PGID);
+		curr->signal->pgrp = pgrp;
+		attach_pid(curr, PIDTYPE_PGID, pgrp);
+	}
+}
+
+void set_special_pids(pid_t session, pid_t pgrp)
+{
+	write_lock_irq(&tasklist_lock);
+	__set_special_pids(session, pgrp);
+	write_unlock_irq(&tasklist_lock);
+}
+
+/*
+ * Let kernel threads use this to say that they
+ * allow a certain signal (since daemonize() will
+ * have disabled all of them by default).
+ */
+int allow_signal(int sig)
+{
+	if (sig < 1 || sig > _NSIG)
+		return -EINVAL;
+
+	spin_lock_irq(&current->sighand->siglock);
+	sigdelset(&current->blocked, sig);
+	if (!current->mm) {
+		/* Kernel threads handle their own signals.
+		   Let the signal code know it'll be handled, so
+		   that they don't get converted to SIGKILL or
+		   just silently dropped */
+		current->sighand->action[(sig)-1].sa.sa_handler = (void __user *)2;
+	}
+	recalc_sigpending();
+	spin_unlock_irq(&current->sighand->siglock);
+	return 0;
+}
+
+EXPORT_SYMBOL(allow_signal);
+
+int disallow_signal(int sig)
+{
+	if (sig < 1 || sig > _NSIG)
+		return -EINVAL;
+
+	spin_lock_irq(&current->sighand->siglock);
+	sigaddset(&current->blocked, sig);
+	recalc_sigpending();
+	spin_unlock_irq(&current->sighand->siglock);
+	return 0;
+}
+
+EXPORT_SYMBOL(disallow_signal);
+
+/*
+ *	Put all the gunge required to become a kernel thread without
+ *	attached user resources in one place where it belongs.
+ */
+
+void daemonize(const char *name, ...)
+{
+	va_list args;
+	struct fs_struct *fs;
+	sigset_t blocked;
+
+	va_start(args, name);
+	vsnprintf(current->comm, sizeof(current->comm), name, args);
+	va_end(args);
+
+	/*
+	 * If we were started as result of loading a module, close all of the
+	 * user space pages.  We don't need them, and if we didn't close them
+	 * they would be locked into memory.
+	 */
+	exit_mm(current);
+
+	set_special_pids(1, 1);
+	down(&tty_sem);
+	current->signal->tty = NULL;
+	up(&tty_sem);
+
+	/* Block and flush all signals */
+	sigfillset(&blocked);
+	sigprocmask(SIG_BLOCK, &blocked, NULL);
+	flush_signals(current);
+
+	/* Become as one with the init task */
+
+	exit_fs(current);	/* current->fs->count--; */
+	fs = init_task.fs;
+	current->fs = fs;
+	atomic_inc(&fs->count);
+ 	exit_files(current);
+	current->files = init_task.files;
+	atomic_inc(&current->files->count);
+
+	reparent_to_init();
+}
+
+EXPORT_SYMBOL(daemonize);
+
+static inline void close_files(struct files_struct * files)
+{
+	int i, j;
+
+	j = 0;
+	for (;;) {
+		unsigned long set;
+		i = j * __NFDBITS;
+		if (i >= files->max_fdset || i >= files->max_fds)
+			break;
+		set = files->open_fds->fds_bits[j++];
+		while (set) {
+			if (set & 1) {
+				struct file * file = xchg(&files->fd[i], NULL);
+				if (file)
+					filp_close(file, files);
+			}
+			i++;
+			set >>= 1;
+		}
+	}
+}
+
+struct files_struct *get_files_struct(struct task_struct *task)
+{
+	struct files_struct *files;
+
+	task_lock(task);
+	files = task->files;
+	if (files)
+		atomic_inc(&files->count);
+	task_unlock(task);
+
+	return files;
+}
+
+void fastcall put_files_struct(struct files_struct *files)
+{
+	if (atomic_dec_and_test(&files->count)) {
+		close_files(files);
+		/*
+		 * Free the fd and fdset arrays if we expanded them.
+		 */
+		if (files->fd != &files->fd_array[0])
+			free_fd_array(files->fd, files->max_fds);
+		if (files->max_fdset > __FD_SETSIZE) {
+			free_fdset(files->open_fds, files->max_fdset);
+			free_fdset(files->close_on_exec, files->max_fdset);
+		}
+		kmem_cache_free(files_cachep, files);
+	}
+}
+
+EXPORT_SYMBOL(put_files_struct);
+
+static inline void __exit_files(struct task_struct *tsk)
+{
+	struct files_struct * files = tsk->files;
+
+	if (files) {
+		task_lock(tsk);
+		tsk->files = NULL;
+		task_unlock(tsk);
+		put_files_struct(files);
+	}
+}
+
+void exit_files(struct task_struct *tsk)
+{
+	__exit_files(tsk);
+}
+
+static inline void __put_fs_struct(struct fs_struct *fs)
+{
+	/* No need to hold fs->lock if we are killing it */
+	if (atomic_dec_and_test(&fs->count)) {
+		dput(fs->root);
+		mntput(fs->rootmnt);
+		dput(fs->pwd);
+		mntput(fs->pwdmnt);
+		if (fs->altroot) {
+			dput(fs->altroot);
+			mntput(fs->altrootmnt);
+		}
+		kmem_cache_free(fs_cachep, fs);
+	}
+}
+
+void put_fs_struct(struct fs_struct *fs)
+{
+	__put_fs_struct(fs);
+}
+
+static inline void __exit_fs(struct task_struct *tsk)
+{
+	struct fs_struct * fs = tsk->fs;
+
+	if (fs) {
+		task_lock(tsk);
+		tsk->fs = NULL;
+		task_unlock(tsk);
+		__put_fs_struct(fs);
+	}
+}
+
+void exit_fs(struct task_struct *tsk)
+{
+	__exit_fs(tsk);
+}
+
+EXPORT_SYMBOL_GPL(exit_fs);
+
+/*
+ * Turn us into a lazy TLB process if we
+ * aren't already..
+ */
+void exit_mm(struct task_struct * tsk)
+{
+	struct mm_struct *mm = tsk->mm;
+
+	mm_release(tsk, mm);
+	if (!mm)
+		return;
+	/*
+	 * Serialize with any possible pending coredump.
+	 * We must hold mmap_sem around checking core_waiters
+	 * and clearing tsk->mm.  The core-inducing thread
+	 * will increment core_waiters for each thread in the
+	 * group with ->mm != NULL.
+	 */
+	down_read(&mm->mmap_sem);
+	if (mm->core_waiters) {
+		up_read(&mm->mmap_sem);
+		down_write(&mm->mmap_sem);
+		if (!--mm->core_waiters)
+			complete(mm->core_startup_done);
+		up_write(&mm->mmap_sem);
+
+		wait_for_completion(&mm->core_done);
+		down_read(&mm->mmap_sem);
+	}
+	atomic_inc(&mm->mm_count);
+	if (mm != tsk->active_mm) BUG();
+	/* more a memory barrier than a real lock */
+	task_lock(tsk);
+	tsk->mm = NULL;
+	up_read(&mm->mmap_sem);
+	enter_lazy_tlb(mm, current);
+	task_unlock(tsk);
+	mmput(mm);
+}
+
+static inline void choose_new_parent(task_t *p, task_t *reaper, task_t *child_reaper)
+{
+	/*
+	 * Make sure we're not reparenting to ourselves and that
+	 * the parent is not a zombie.
+	 */
+	BUG_ON(p == reaper || reaper->exit_state >= EXIT_ZOMBIE);
+	p->real_parent = reaper;
+	if (p->parent == p->real_parent)
+		BUG();
+}
+
+static inline void reparent_thread(task_t *p, task_t *father, int traced)
+{
+	/* We don't want people slaying init.  */
+	if (p->exit_signal != -1)
+		p->exit_signal = SIGCHLD;
+
+	if (p->pdeath_signal)
+		/* We already hold the tasklist_lock here.  */
+		group_send_sig_info(p->pdeath_signal, (void *) 0, p);
+
+	/* Move the child from its dying parent to the new one.  */
+	if (unlikely(traced)) {
+		/* Preserve ptrace links if someone else is tracing this child.  */
+		list_del_init(&p->ptrace_list);
+		if (p->parent != p->real_parent)
+			list_add(&p->ptrace_list, &p->real_parent->ptrace_children);
+	} else {
+		/* If this child is being traced, then we're the one tracing it
+		 * anyway, so let go of it.
+		 */
+		p->ptrace = 0;
+		list_del_init(&p->sibling);
+		p->parent = p->real_parent;
+		list_add_tail(&p->sibling, &p->parent->children);
+
+		/* If we'd notified the old parent about this child's death,
+		 * also notify the new parent.
+		 */
+		if (p->exit_state == EXIT_ZOMBIE && p->exit_signal != -1 &&
+		    thread_group_empty(p))
+			do_notify_parent(p, p->exit_signal);
+		else if (p->state == TASK_TRACED) {
+			/*
+			 * If it was at a trace stop, turn it into
+			 * a normal stop since it's no longer being
+			 * traced.
+			 */
+			ptrace_untrace(p);
+		}
+	}
+
+	/*
+	 * process group orphan check
+	 * Case ii: Our child is in a different pgrp
+	 * than we are, and it was the only connection
+	 * outside, so the child pgrp is now orphaned.
+	 */
+	if ((process_group(p) != process_group(father)) &&
+	    (p->signal->session == father->signal->session)) {
+		int pgrp = process_group(p);
+
+		if (will_become_orphaned_pgrp(pgrp, NULL) && has_stopped_jobs(pgrp)) {
+			__kill_pg_info(SIGHUP, (void *)1, pgrp);
+			__kill_pg_info(SIGCONT, (void *)1, pgrp);
+		}
+	}
+}
+
+/*
+ * When we die, we re-parent all our children.
+ * Try to give them to another thread in our thread
+ * group, and if no such member exists, give it to
+ * the global child reaper process (ie "init")
+ */
+static inline void forget_original_parent(struct task_struct * father,
+					  struct list_head *to_release)
+{
+	struct task_struct *p, *reaper = father;
+	struct list_head *_p, *_n;
+
+	do {
+		reaper = next_thread(reaper);
+		if (reaper == father) {
+			reaper = child_reaper;
+			break;
+		}
+	} while (reaper->exit_state);
+
+	/*
+	 * There are only two places where our children can be:
+	 *
+	 * - in our child list
+	 * - in our ptraced child list
+	 *
+	 * Search them and reparent children.
+	 */
+	list_for_each_safe(_p, _n, &father->children) {
+		int ptrace;
+		p = list_entry(_p,struct task_struct,sibling);
+
+		ptrace = p->ptrace;
+
+		/* if father isn't the real parent, then ptrace must be enabled */
+		BUG_ON(father != p->real_parent && !ptrace);
+
+		if (father == p->real_parent) {
+			/* reparent with a reaper, real father it's us */
+			choose_new_parent(p, reaper, child_reaper);
+			reparent_thread(p, father, 0);
+		} else {
+			/* reparent ptraced task to its real parent */
+			__ptrace_unlink (p);
+			if (p->exit_state == EXIT_ZOMBIE && p->exit_signal != -1 &&
+			    thread_group_empty(p))
+				do_notify_parent(p, p->exit_signal);
+		}
+
+		/*
+		 * if the ptraced child is a zombie with exit_signal == -1
+		 * we must collect it before we exit, or it will remain
+		 * zombie forever since we prevented it from self-reap itself
+		 * while it was being traced by us, to be able to see it in wait4.
+		 */
+		if (unlikely(ptrace && p->exit_state == EXIT_ZOMBIE && p->exit_signal == -1))
+			list_add(&p->ptrace_list, to_release);
+	}
+	list_for_each_safe(_p, _n, &father->ptrace_children) {
+		p = list_entry(_p,struct task_struct,ptrace_list);
+		choose_new_parent(p, reaper, child_reaper);
+		reparent_thread(p, father, 1);
+	}
+}
+
+/*
+ * Send signals to all our closest relatives so that they know
+ * to properly mourn us..
+ */
+static void exit_notify(struct task_struct *tsk)
+{
+	int state;
+	struct task_struct *t;
+	struct list_head ptrace_dead, *_p, *_n;
+
+	if (signal_pending(tsk) && !(tsk->signal->flags & SIGNAL_GROUP_EXIT)
+	    && !thread_group_empty(tsk)) {
+		/*
+		 * This occurs when there was a race between our exit
+		 * syscall and a group signal choosing us as the one to
+		 * wake up.  It could be that we are the only thread
+		 * alerted to check for pending signals, but another thread
+		 * should be woken now to take the signal since we will not.
+		 * Now we'll wake all the threads in the group just to make
+		 * sure someone gets all the pending signals.
+		 */
+		read_lock(&tasklist_lock);
+		spin_lock_irq(&tsk->sighand->siglock);
+		for (t = next_thread(tsk); t != tsk; t = next_thread(t))
+			if (!signal_pending(t) && !(t->flags & PF_EXITING)) {
+				recalc_sigpending_tsk(t);
+				if (signal_pending(t))
+					signal_wake_up(t, 0);
+			}
+		spin_unlock_irq(&tsk->sighand->siglock);
+		read_unlock(&tasklist_lock);
+	}
+
+	write_lock_irq(&tasklist_lock);
+
+	/*
+	 * This does two things:
+	 *
+  	 * A.  Make init inherit all the child processes
+	 * B.  Check to see if any process groups have become orphaned
+	 *	as a result of our exiting, and if they have any stopped
+	 *	jobs, send them a SIGHUP and then a SIGCONT.  (POSIX 3.2.2.2)
+	 */
+
+	INIT_LIST_HEAD(&ptrace_dead);
+	forget_original_parent(tsk, &ptrace_dead);
+	BUG_ON(!list_empty(&tsk->children));
+	BUG_ON(!list_empty(&tsk->ptrace_children));
+
+	/*
+	 * Check to see if any process groups have become orphaned
+	 * as a result of our exiting, and if they have any stopped
+	 * jobs, send them a SIGHUP and then a SIGCONT.  (POSIX 3.2.2.2)
+	 *
+	 * Case i: Our father is in a different pgrp than we are
+	 * and we were the only connection outside, so our pgrp
+	 * is about to become orphaned.
+	 */
+	 
+	t = tsk->real_parent;
+	
+	if ((process_group(t) != process_group(tsk)) &&
+	    (t->signal->session == tsk->signal->session) &&
+	    will_become_orphaned_pgrp(process_group(tsk), tsk) &&
+	    has_stopped_jobs(process_group(tsk))) {
+		__kill_pg_info(SIGHUP, (void *)1, process_group(tsk));
+		__kill_pg_info(SIGCONT, (void *)1, process_group(tsk));
+	}
+
+	/* Let father know we died 
+	 *
+	 * Thread signals are configurable, but you aren't going to use
+	 * that to send signals to arbitary processes. 
+	 * That stops right now.
+	 *
+	 * If the parent exec id doesn't match the exec id we saved
+	 * when we started then we know the parent has changed security
+	 * domain.
+	 *
+	 * If our self_exec id doesn't match our parent_exec_id then
+	 * we have changed execution domain as these two values started
+	 * the same after a fork.
+	 *	
+	 */
+	
+	if (tsk->exit_signal != SIGCHLD && tsk->exit_signal != -1 &&
+	    ( tsk->parent_exec_id != t->self_exec_id  ||
+	      tsk->self_exec_id != tsk->parent_exec_id)
+	    && !capable(CAP_KILL))
+		tsk->exit_signal = SIGCHLD;
+
+
+	/* If something other than our normal parent is ptracing us, then
+	 * send it a SIGCHLD instead of honoring exit_signal.  exit_signal
+	 * only has special meaning to our real parent.
+	 */
+	if (tsk->exit_signal != -1 && thread_group_empty(tsk)) {
+		int signal = tsk->parent == tsk->real_parent ? tsk->exit_signal : SIGCHLD;
+		do_notify_parent(tsk, signal);
+	} else if (tsk->ptrace) {
+		do_notify_parent(tsk, SIGCHLD);
+	}
+
+	state = EXIT_ZOMBIE;
+	if (tsk->exit_signal == -1 &&
+	    (likely(tsk->ptrace == 0) ||
+	     unlikely(tsk->parent->signal->flags & SIGNAL_GROUP_EXIT)))
+		state = EXIT_DEAD;
+	tsk->exit_state = state;
+
+	/*
+	 * Clear these here so that update_process_times() won't try to deliver
+	 * itimer, profile or rlimit signals to this task while it is in late exit.
+	 */
+	tsk->it_virt_value = cputime_zero;
+	tsk->it_prof_value = cputime_zero;
+
+	write_unlock_irq(&tasklist_lock);
+
+	list_for_each_safe(_p, _n, &ptrace_dead) {
+		list_del_init(_p);
+		t = list_entry(_p,struct task_struct,ptrace_list);
+		release_task(t);
+	}
+
+	/* If the process is dead, release it - nobody will wait for it */
+	if (state == EXIT_DEAD)
+		release_task(tsk);
+
+	/* PF_DEAD causes final put_task_struct after we schedule. */
+	preempt_disable();
+	tsk->flags |= PF_DEAD;
+}
+
+fastcall NORET_TYPE void do_exit(long code)
+{
+	struct task_struct *tsk = current;
+	int group_dead;
+
+	profile_task_exit(tsk);
+
+	if (unlikely(in_interrupt()))
+		panic("Aiee, killing interrupt handler!");
+	if (unlikely(!tsk->pid))
+		panic("Attempted to kill the idle task!");
+	if (unlikely(tsk->pid == 1))
+		panic("Attempted to kill init!");
+	if (tsk->io_context)
+		exit_io_context();
+
+	if (unlikely(current->ptrace & PT_TRACE_EXIT)) {
+		current->ptrace_message = code;
+		ptrace_notify((PTRACE_EVENT_EXIT << 8) | SIGTRAP);
+	}
+
+	tsk->flags |= PF_EXITING;
+	del_timer_sync(&tsk->real_timer);
+
+	if (unlikely(in_atomic()))
+		printk(KERN_INFO "note: %s[%d] exited with preempt_count %d\n",
+				current->comm, current->pid,
+				preempt_count());
+
+	acct_update_integrals();
+	update_mem_hiwater();
+	group_dead = atomic_dec_and_test(&tsk->signal->live);
+	if (group_dead)
+		acct_process(code);
+	exit_mm(tsk);
+
+	exit_sem(tsk);
+	__exit_files(tsk);
+	__exit_fs(tsk);
+	exit_namespace(tsk);
+	exit_thread();
+	exit_keys(tsk);
+
+	if (group_dead && tsk->signal->leader)
+		disassociate_ctty(1);
+
+	module_put(tsk->thread_info->exec_domain->module);
+	if (tsk->binfmt)
+		module_put(tsk->binfmt->module);
+
+	tsk->exit_code = code;
+	exit_notify(tsk);
+#ifdef CONFIG_NUMA
+	mpol_free(tsk->mempolicy);
+	tsk->mempolicy = NULL;
+#endif
+
+	BUG_ON(!(current->flags & PF_DEAD));
+	schedule();
+	BUG();
+	/* Avoid "noreturn function does return".  */
+	for (;;) ;
+}
+
+NORET_TYPE void complete_and_exit(struct completion *comp, long code)
+{
+	if (comp)
+		complete(comp);
+	
+	do_exit(code);
+}
+
+EXPORT_SYMBOL(complete_and_exit);
+
+asmlinkage long sys_exit(int error_code)
+{
+	do_exit((error_code&0xff)<<8);
+}
+
+task_t fastcall *next_thread(const task_t *p)
+{
+	return pid_task(p->pids[PIDTYPE_TGID].pid_list.next, PIDTYPE_TGID);
+}
+
+EXPORT_SYMBOL(next_thread);
+
+/*
+ * Take down every thread in the group.  This is called by fatal signals
+ * as well as by sys_exit_group (below).
+ */
+NORET_TYPE void
+do_group_exit(int exit_code)
+{
+	BUG_ON(exit_code & 0x80); /* core dumps don't get here */
+
+	if (current->signal->flags & SIGNAL_GROUP_EXIT)
+		exit_code = current->signal->group_exit_code;
+	else if (!thread_group_empty(current)) {
+		struct signal_struct *const sig = current->signal;
+		struct sighand_struct *const sighand = current->sighand;
+		read_lock(&tasklist_lock);
+		spin_lock_irq(&sighand->siglock);
+		if (sig->flags & SIGNAL_GROUP_EXIT)
+			/* Another thread got here before we took the lock.  */
+			exit_code = sig->group_exit_code;
+		else {
+			sig->flags = SIGNAL_GROUP_EXIT;
+			sig->group_exit_code = exit_code;
+			zap_other_threads(current);
+		}
+		spin_unlock_irq(&sighand->siglock);
+		read_unlock(&tasklist_lock);
+	}
+
+	do_exit(exit_code);
+	/* NOTREACHED */
+}
+
+/*
+ * this kills every thread in the thread group. Note that any externally
+ * wait4()-ing process will get the correct exit code - even if this
+ * thread is not the thread group leader.
+ */
+asmlinkage void sys_exit_group(int error_code)
+{
+	do_group_exit((error_code & 0xff) << 8);
+}
+
+static int eligible_child(pid_t pid, int options, task_t *p)
+{
+	if (pid > 0) {
+		if (p->pid != pid)
+			return 0;
+	} else if (!pid) {
+		if (process_group(p) != process_group(current))
+			return 0;
+	} else if (pid != -1) {
+		if (process_group(p) != -pid)
+			return 0;
+	}
+
+	/*
+	 * Do not consider detached threads that are
+	 * not ptraced:
+	 */
+	if (p->exit_signal == -1 && !p->ptrace)
+		return 0;
+
+	/* Wait for all children (clone and not) if __WALL is set;
+	 * otherwise, wait for clone children *only* if __WCLONE is
+	 * set; otherwise, wait for non-clone children *only*.  (Note:
+	 * A "clone" child here is one that reports to its parent
+	 * using a signal other than SIGCHLD.) */
+	if (((p->exit_signal != SIGCHLD) ^ ((options & __WCLONE) != 0))
+	    && !(options & __WALL))
+		return 0;
+	/*
+	 * Do not consider thread group leaders that are
+	 * in a non-empty thread group:
+	 */
+	if (current->tgid != p->tgid && delay_group_leader(p))
+		return 2;
+
+	if (security_task_wait(p))
+		return 0;
+
+	return 1;
+}
+
+static int wait_noreap_copyout(task_t *p, pid_t pid, uid_t uid,
+			       int why, int status,
+			       struct siginfo __user *infop,
+			       struct rusage __user *rusagep)
+{
+	int retval = rusagep ? getrusage(p, RUSAGE_BOTH, rusagep) : 0;
+	put_task_struct(p);
+	if (!retval)
+		retval = put_user(SIGCHLD, &infop->si_signo);
+	if (!retval)
+		retval = put_user(0, &infop->si_errno);
+	if (!retval)
+		retval = put_user((short)why, &infop->si_code);
+	if (!retval)
+		retval = put_user(pid, &infop->si_pid);
+	if (!retval)
+		retval = put_user(uid, &infop->si_uid);
+	if (!retval)
+		retval = put_user(status, &infop->si_status);
+	if (!retval)
+		retval = pid;
+	return retval;
+}
+
+/*
+ * Handle sys_wait4 work for one task in state EXIT_ZOMBIE.  We hold
+ * read_lock(&tasklist_lock) on entry.  If we return zero, we still hold
+ * the lock and this task is uninteresting.  If we return nonzero, we have
+ * released the lock and the system call should return.
+ */
+static int wait_task_zombie(task_t *p, int noreap,
+			    struct siginfo __user *infop,
+			    int __user *stat_addr, struct rusage __user *ru)
+{
+	unsigned long state;
+	int retval;
+	int status;
+
+	if (unlikely(noreap)) {
+		pid_t pid = p->pid;
+		uid_t uid = p->uid;
+		int exit_code = p->exit_code;
+		int why, status;
+
+		if (unlikely(p->exit_state != EXIT_ZOMBIE))
+			return 0;
+		if (unlikely(p->exit_signal == -1 && p->ptrace == 0))
+			return 0;
+		get_task_struct(p);
+		read_unlock(&tasklist_lock);
+		if ((exit_code & 0x7f) == 0) {
+			why = CLD_EXITED;
+			status = exit_code >> 8;
+		} else {
+			why = (exit_code & 0x80) ? CLD_DUMPED : CLD_KILLED;
+			status = exit_code & 0x7f;
+		}
+		return wait_noreap_copyout(p, pid, uid, why,
+					   status, infop, ru);
+	}
+
+	/*
+	 * Try to move the task's state to DEAD
+	 * only one thread is allowed to do this:
+	 */
+	state = xchg(&p->exit_state, EXIT_DEAD);
+	if (state != EXIT_ZOMBIE) {
+		BUG_ON(state != EXIT_DEAD);
+		return 0;
+	}
+	if (unlikely(p->exit_signal == -1 && p->ptrace == 0)) {
+		/*
+		 * This can only happen in a race with a ptraced thread
+		 * dying on another processor.
+		 */
+		return 0;
+	}
+
+	if (likely(p->real_parent == p->parent) && likely(p->signal)) {
+		/*
+		 * The resource counters for the group leader are in its
+		 * own task_struct.  Those for dead threads in the group
+		 * are in its signal_struct, as are those for the child
+		 * processes it has previously reaped.  All these
+		 * accumulate in the parent's signal_struct c* fields.
+		 *
+		 * We don't bother to take a lock here to protect these
+		 * p->signal fields, because they are only touched by
+		 * __exit_signal, which runs with tasklist_lock
+		 * write-locked anyway, and so is excluded here.  We do
+		 * need to protect the access to p->parent->signal fields,
+		 * as other threads in the parent group can be right
+		 * here reaping other children at the same time.
+		 */
+		spin_lock_irq(&p->parent->sighand->siglock);
+		p->parent->signal->cutime =
+			cputime_add(p->parent->signal->cutime,
+			cputime_add(p->utime,
+			cputime_add(p->signal->utime,
+				    p->signal->cutime)));
+		p->parent->signal->cstime =
+			cputime_add(p->parent->signal->cstime,
+			cputime_add(p->stime,
+			cputime_add(p->signal->stime,
+				    p->signal->cstime)));
+		p->parent->signal->cmin_flt +=
+			p->min_flt + p->signal->min_flt + p->signal->cmin_flt;
+		p->parent->signal->cmaj_flt +=
+			p->maj_flt + p->signal->maj_flt + p->signal->cmaj_flt;
+		p->parent->signal->cnvcsw +=
+			p->nvcsw + p->signal->nvcsw + p->signal->cnvcsw;
+		p->parent->signal->cnivcsw +=
+			p->nivcsw + p->signal->nivcsw + p->signal->cnivcsw;
+		spin_unlock_irq(&p->parent->sighand->siglock);
+	}
+
+	/*
+	 * Now we are sure this task is interesting, and no other
+	 * thread can reap it because we set its state to EXIT_DEAD.
+	 */
+	read_unlock(&tasklist_lock);
+
+	retval = ru ? getrusage(p, RUSAGE_BOTH, ru) : 0;
+	status = (p->signal->flags & SIGNAL_GROUP_EXIT)
+		? p->signal->group_exit_code : p->exit_code;
+	if (!retval && stat_addr)
+		retval = put_user(status, stat_addr);
+	if (!retval && infop)
+		retval = put_user(SIGCHLD, &infop->si_signo);
+	if (!retval && infop)
+		retval = put_user(0, &infop->si_errno);
+	if (!retval && infop) {
+		int why;
+
+		if ((status & 0x7f) == 0) {
+			why = CLD_EXITED;
+			status >>= 8;
+		} else {
+			why = (status & 0x80) ? CLD_DUMPED : CLD_KILLED;
+			status &= 0x7f;
+		}
+		retval = put_user((short)why, &infop->si_code);
+		if (!retval)
+			retval = put_user(status, &infop->si_status);
+	}
+	if (!retval && infop)
+		retval = put_user(p->pid, &infop->si_pid);
+	if (!retval && infop)
+		retval = put_user(p->uid, &infop->si_uid);
+	if (retval) {
+		// TODO: is this safe?
+		p->exit_state = EXIT_ZOMBIE;
+		return retval;
+	}
+	retval = p->pid;
+	if (p->real_parent != p->parent) {
+		write_lock_irq(&tasklist_lock);
+		/* Double-check with lock held.  */
+		if (p->real_parent != p->parent) {
+			__ptrace_unlink(p);
+			// TODO: is this safe?
+			p->exit_state = EXIT_ZOMBIE;
+			/*
+			 * If this is not a detached task, notify the parent.
+			 * If it's still not detached after that, don't release
+			 * it now.
+			 */
+			if (p->exit_signal != -1) {
+				do_notify_parent(p, p->exit_signal);
+				if (p->exit_signal != -1)
+					p = NULL;
+			}
+		}
+		write_unlock_irq(&tasklist_lock);
+	}
+	if (p != NULL)
+		release_task(p);
+	BUG_ON(!retval);
+	return retval;
+}
+
+/*
+ * Handle sys_wait4 work for one task in state TASK_STOPPED.  We hold
+ * read_lock(&tasklist_lock) on entry.  If we return zero, we still hold
+ * the lock and this task is uninteresting.  If we return nonzero, we have
+ * released the lock and the system call should return.
+ */
+static int wait_task_stopped(task_t *p, int delayed_group_leader, int noreap,
+			     struct siginfo __user *infop,
+			     int __user *stat_addr, struct rusage __user *ru)
+{
+	int retval, exit_code;
+
+	if (!p->exit_code)
+		return 0;
+	if (delayed_group_leader && !(p->ptrace & PT_PTRACED) &&
+	    p->signal && p->signal->group_stop_count > 0)
+		/*
+		 * A group stop is in progress and this is the group leader.
+		 * We won't report until all threads have stopped.
+		 */
+		return 0;
+
+	/*
+	 * Now we are pretty sure this task is interesting.
+	 * Make sure it doesn't get reaped out from under us while we
+	 * give up the lock and then examine it below.  We don't want to
+	 * keep holding onto the tasklist_lock while we call getrusage and
+	 * possibly take page faults for user memory.
+	 */
+	get_task_struct(p);
+	read_unlock(&tasklist_lock);
+
+	if (unlikely(noreap)) {
+		pid_t pid = p->pid;
+		uid_t uid = p->uid;
+		int why = (p->ptrace & PT_PTRACED) ? CLD_TRAPPED : CLD_STOPPED;
+
+		exit_code = p->exit_code;
+		if (unlikely(!exit_code) ||
+		    unlikely(p->state > TASK_STOPPED))
+			goto bail_ref;
+		return wait_noreap_copyout(p, pid, uid,
+					   why, (exit_code << 8) | 0x7f,
+					   infop, ru);
+	}
+
+	write_lock_irq(&tasklist_lock);
+
+	/*
+	 * This uses xchg to be atomic with the thread resuming and setting
+	 * it.  It must also be done with the write lock held to prevent a
+	 * race with the EXIT_ZOMBIE case.
+	 */
+	exit_code = xchg(&p->exit_code, 0);
+	if (unlikely(p->exit_state)) {
+		/*
+		 * The task resumed and then died.  Let the next iteration
+		 * catch it in EXIT_ZOMBIE.  Note that exit_code might
+		 * already be zero here if it resumed and did _exit(0).
+		 * The task itself is dead and won't touch exit_code again;
+		 * other processors in this function are locked out.
+		 */
+		p->exit_code = exit_code;
+		exit_code = 0;
+	}
+	if (unlikely(exit_code == 0)) {
+		/*
+		 * Another thread in this function got to it first, or it
+		 * resumed, or it resumed and then died.
+		 */
+		write_unlock_irq(&tasklist_lock);
+bail_ref:
+		put_task_struct(p);
+		/*
+		 * We are returning to the wait loop without having successfully
+		 * removed the process and having released the lock. We cannot
+		 * continue, since the "p" task pointer is potentially stale.
+		 *
+		 * Return -EAGAIN, and do_wait() will restart the loop from the
+		 * beginning. Do _not_ re-acquire the lock.
+		 */
+		return -EAGAIN;
+	}
+
+	/* move to end of parent's list to avoid starvation */
+	remove_parent(p);
+	add_parent(p, p->parent);
+
+	write_unlock_irq(&tasklist_lock);
+
+	retval = ru ? getrusage(p, RUSAGE_BOTH, ru) : 0;
+	if (!retval && stat_addr)
+		retval = put_user((exit_code << 8) | 0x7f, stat_addr);
+	if (!retval && infop)
+		retval = put_user(SIGCHLD, &infop->si_signo);
+	if (!retval && infop)
+		retval = put_user(0, &infop->si_errno);
+	if (!retval && infop)
+		retval = put_user((short)((p->ptrace & PT_PTRACED)
+					  ? CLD_TRAPPED : CLD_STOPPED),
+				  &infop->si_code);
+	if (!retval && infop)
+		retval = put_user(exit_code, &infop->si_status);
+	if (!retval && infop)
+		retval = put_user(p->pid, &infop->si_pid);
+	if (!retval && infop)
+		retval = put_user(p->uid, &infop->si_uid);
+	if (!retval)
+		retval = p->pid;
+	put_task_struct(p);
+
+	BUG_ON(!retval);
+	return retval;
+}
+
+/*
+ * Handle do_wait work for one task in a live, non-stopped state.
+ * read_lock(&tasklist_lock) on entry.  If we return zero, we still hold
+ * the lock and this task is uninteresting.  If we return nonzero, we have
+ * released the lock and the system call should return.
+ */
+static int wait_task_continued(task_t *p, int noreap,
+			       struct siginfo __user *infop,
+			       int __user *stat_addr, struct rusage __user *ru)
+{
+	int retval;
+	pid_t pid;
+	uid_t uid;
+
+	if (unlikely(!p->signal))
+		return 0;
+
+	if (!(p->signal->flags & SIGNAL_STOP_CONTINUED))
+		return 0;
+
+	spin_lock_irq(&p->sighand->siglock);
+	/* Re-check with the lock held.  */
+	if (!(p->signal->flags & SIGNAL_STOP_CONTINUED)) {
+		spin_unlock_irq(&p->sighand->siglock);
+		return 0;
+	}
+	if (!noreap)
+		p->signal->flags &= ~SIGNAL_STOP_CONTINUED;
+	spin_unlock_irq(&p->sighand->siglock);
+
+	pid = p->pid;
+	uid = p->uid;
+	get_task_struct(p);
+	read_unlock(&tasklist_lock);
+
+	if (!infop) {
+		retval = ru ? getrusage(p, RUSAGE_BOTH, ru) : 0;
+		put_task_struct(p);
+		if (!retval && stat_addr)
+			retval = put_user(0xffff, stat_addr);
+		if (!retval)
+			retval = p->pid;
+	} else {
+		retval = wait_noreap_copyout(p, pid, uid,
+					     CLD_CONTINUED, SIGCONT,
+					     infop, ru);
+		BUG_ON(retval == 0);
+	}
+
+	return retval;
+}
+
+
+static inline int my_ptrace_child(struct task_struct *p)
+{
+	if (!(p->ptrace & PT_PTRACED))
+		return 0;
+	if (!(p->ptrace & PT_ATTACHED))
+		return 1;
+	/*
+	 * This child was PTRACE_ATTACH'd.  We should be seeing it only if
+	 * we are the attacher.  If we are the real parent, this is a race
+	 * inside ptrace_attach.  It is waiting for the tasklist_lock,
+	 * which we have to switch the parent links, but has already set
+	 * the flags in p->ptrace.
+	 */
+	return (p->parent != p->real_parent);
+}
+
+static long do_wait(pid_t pid, int options, struct siginfo __user *infop,
+		    int __user *stat_addr, struct rusage __user *ru)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	struct task_struct *tsk;
+	int flag, retval;
+
+	add_wait_queue(&current->signal->wait_chldexit,&wait);
+repeat:
+	/*
+	 * We will set this flag if we see any child that might later
+	 * match our criteria, even if we are not able to reap it yet.
+	 */
+	flag = 0;
+	current->state = TASK_INTERRUPTIBLE;
+	read_lock(&tasklist_lock);
+	tsk = current;
+	do {
+		struct task_struct *p;
+		struct list_head *_p;
+		int ret;
+
+		list_for_each(_p,&tsk->children) {
+			p = list_entry(_p,struct task_struct,sibling);
+
+			ret = eligible_child(pid, options, p);
+			if (!ret)
+				continue;
+
+			switch (p->state) {
+			case TASK_TRACED:
+				if (!my_ptrace_child(p))
+					continue;
+				/*FALLTHROUGH*/
+			case TASK_STOPPED:
+				/*
+				 * It's stopped now, so it might later
+				 * continue, exit, or stop again.
+				 */
+				flag = 1;
+				if (!(options & WUNTRACED) &&
+				    !my_ptrace_child(p))
+					continue;
+				retval = wait_task_stopped(p, ret == 2,
+							   (options & WNOWAIT),
+							   infop,
+							   stat_addr, ru);
+				if (retval == -EAGAIN)
+					goto repeat;
+				if (retval != 0) /* He released the lock.  */
+					goto end;
+				break;
+			default:
+			// case EXIT_DEAD:
+				if (p->exit_state == EXIT_DEAD)
+					continue;
+			// case EXIT_ZOMBIE:
+				if (p->exit_state == EXIT_ZOMBIE) {
+					/*
+					 * Eligible but we cannot release
+					 * it yet:
+					 */
+					if (ret == 2)
+						goto check_continued;
+					if (!likely(options & WEXITED))
+						continue;
+					retval = wait_task_zombie(
+						p, (options & WNOWAIT),
+						infop, stat_addr, ru);
+					/* He released the lock.  */
+					if (retval != 0)
+						goto end;
+					break;
+				}
+check_continued:
+				/*
+				 * It's running now, so it might later
+				 * exit, stop, or stop and then continue.
+				 */
+				flag = 1;
+				if (!unlikely(options & WCONTINUED))
+					continue;
+				retval = wait_task_continued(
+					p, (options & WNOWAIT),
+					infop, stat_addr, ru);
+				if (retval != 0) /* He released the lock.  */
+					goto end;
+				break;
+			}
+		}
+		if (!flag) {
+			list_for_each(_p, &tsk->ptrace_children) {
+				p = list_entry(_p, struct task_struct,
+						ptrace_list);
+				if (!eligible_child(pid, options, p))
+					continue;
+				flag = 1;
+				break;
+			}
+		}
+		if (options & __WNOTHREAD)
+			break;
+		tsk = next_thread(tsk);
+		if (tsk->signal != current->signal)
+			BUG();
+	} while (tsk != current);
+
+	read_unlock(&tasklist_lock);
+	if (flag) {
+		retval = 0;
+		if (options & WNOHANG)
+			goto end;
+		retval = -ERESTARTSYS;
+		if (signal_pending(current))
+			goto end;
+		schedule();
+		goto repeat;
+	}
+	retval = -ECHILD;
+end:
+	current->state = TASK_RUNNING;
+	remove_wait_queue(&current->signal->wait_chldexit,&wait);
+	if (infop) {
+		if (retval > 0)
+		retval = 0;
+		else {
+			/*
+			 * For a WNOHANG return, clear out all the fields
+			 * we would set so the user can easily tell the
+			 * difference.
+			 */
+			if (!retval)
+				retval = put_user(0, &infop->si_signo);
+			if (!retval)
+				retval = put_user(0, &infop->si_errno);
+			if (!retval)
+				retval = put_user(0, &infop->si_code);
+			if (!retval)
+				retval = put_user(0, &infop->si_pid);
+			if (!retval)
+				retval = put_user(0, &infop->si_uid);
+			if (!retval)
+				retval = put_user(0, &infop->si_status);
+		}
+	}
+	return retval;
+}
+
+asmlinkage long sys_waitid(int which, pid_t pid,
+			   struct siginfo __user *infop, int options,
+			   struct rusage __user *ru)
+{
+	long ret;
+
+	if (options & ~(WNOHANG|WNOWAIT|WEXITED|WSTOPPED|WCONTINUED))
+		return -EINVAL;
+	if (!(options & (WEXITED|WSTOPPED|WCONTINUED)))
+		return -EINVAL;
+
+	switch (which) {
+	case P_ALL:
+		pid = -1;
+		break;
+	case P_PID:
+		if (pid <= 0)
+			return -EINVAL;
+		break;
+	case P_PGID:
+		if (pid <= 0)
+			return -EINVAL;
+		pid = -pid;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	ret = do_wait(pid, options, infop, NULL, ru);
+
+	/* avoid REGPARM breakage on x86: */
+	prevent_tail_call(ret);
+	return ret;
+}
+
+asmlinkage long sys_wait4(pid_t pid, int __user *stat_addr,
+			  int options, struct rusage __user *ru)
+{
+	long ret;
+
+	if (options & ~(WNOHANG|WUNTRACED|WCONTINUED|
+			__WNOTHREAD|__WCLONE|__WALL))
+		return -EINVAL;
+	ret = do_wait(pid, options | WEXITED, NULL, stat_addr, ru);
+
+	/* avoid REGPARM breakage on x86: */
+	prevent_tail_call(ret);
+	return ret;
+}
+
+#ifdef __ARCH_WANT_SYS_WAITPID
+
+/*
+ * sys_waitpid() remains for compatibility. waitpid() should be
+ * implemented by calling sys_wait4() from libc.a.
+ */
+asmlinkage long sys_waitpid(pid_t pid, int __user *stat_addr, int options)
+{
+	return sys_wait4(pid, stat_addr, options, NULL);
+}
+
+#endif
diff -Nrub linux-2.6.11.7-orig/kernel/fork.c linux-2.6.11.7-snare/kernel/fork.c
--- linux-2.6.11.7-orig/kernel/fork.c	2005-04-07 14:57:12.000000000 -0400
+++ linux-2.6.11.7-snare/kernel/fork.c	2005-04-22 11:19:47.198665160 -0400
@@ -23,6 +23,7 @@
 #include <linux/personality.h>
 #include <linux/mempolicy.h>
 #include <linux/sem.h>
+#include <linux/saudit.h>
 #include <linux/file.h>
 #include <linux/key.h>
 #include <linux/binfmts.h>
@@ -792,25 +793,34 @@
 				 int pid)
 {
 	int retval;
+#ifdef CONFIG_C2_AUDIT
+	int auditretval=0;
+#endif
 	struct task_struct *p = NULL;
 
-	if ((clone_flags & (CLONE_NEWNS|CLONE_FS)) == (CLONE_NEWNS|CLONE_FS))
+	if ((clone_flags & (CLONE_NEWNS|CLONE_FS)) == (CLONE_NEWNS|CLONE_FS)) {
+		saudit_fork(-1);
 		return ERR_PTR(-EINVAL);
+	}
 
 	/*
 	 * Thread groups must share signals as well, and detached threads
 	 * can only be started up within the thread group.
 	 */
-	if ((clone_flags & CLONE_THREAD) && !(clone_flags & CLONE_SIGHAND))
+	if ((clone_flags & CLONE_THREAD) && !(clone_flags & CLONE_SIGHAND)) {
+		saudit_fork(-1);
 		return ERR_PTR(-EINVAL);
+	}
 
 	/*
 	 * Shared signal handlers imply shared VM. By way of the above,
 	 * thread groups also imply shared VM. Blocking this case allows
 	 * for various simplifications in other code.
 	 */
-	if ((clone_flags & CLONE_SIGHAND) && !(clone_flags & CLONE_VM))
+	if ((clone_flags & CLONE_SIGHAND) && !(clone_flags & CLONE_VM)) {
+		saudit_fork(-1);
 		return ERR_PTR(-EINVAL);
+	}
 
 	retval = security_task_create(clone_flags);
 	if (retval)
@@ -821,6 +831,11 @@
 	if (!p)
 		goto fork_out;
 
+#ifdef CONFIG_C2_AUDIT
+	p->saudit_record = NULL;
+#endif
+
+
 	retval = -EAGAIN;
 	if (atomic_read(&p->user->processes) >=
 			p->signal->rlim[RLIMIT_NPROC].rlim_cur) {
@@ -1037,6 +1052,13 @@
 	retval = 0;
 
 fork_out:
+#ifdef CONFIG_C2_AUDIT
+	if (auditretval == -1)
+	  saudit_fork(-1);
+	else
+	  saudit_fork(p->pid);
+#endif
+
 	if (retval)
 		return ERR_PTR(retval);
 	return p;
@@ -1077,6 +1099,9 @@
 	free_uid(p->user);
 bad_fork_free:
 	free_task(p);
+#ifdef CONFIG_C2_AUDIT
+	auditretval = -1;
+#endif
 	goto fork_out;
 }
 
diff -Nrub linux-2.6.11.7-orig/kernel/fork.c.orig linux-2.6.11.7-snare/kernel/fork.c.orig
--- linux-2.6.11.7-orig/kernel/fork.c.orig	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.11.7-snare/kernel/fork.c.orig	2005-04-07 14:57:12.000000000 -0400
@@ -0,0 +1,1224 @@
+/*
+ *  linux/kernel/fork.c
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ */
+
+/*
+ *  'fork.c' contains the help-routines for the 'fork' system call
+ * (see also entry.S and others).
+ * Fork is rather simple, once you get the hang of it, but the memory
+ * management can be a bitch. See 'mm/memory.c': 'copy_page_range()'
+ */
+
+#include <linux/config.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/unistd.h>
+#include <linux/smp_lock.h>
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+#include <linux/completion.h>
+#include <linux/namespace.h>
+#include <linux/personality.h>
+#include <linux/mempolicy.h>
+#include <linux/sem.h>
+#include <linux/file.h>
+#include <linux/key.h>
+#include <linux/binfmts.h>
+#include <linux/mman.h>
+#include <linux/fs.h>
+#include <linux/cpu.h>
+#include <linux/security.h>
+#include <linux/swap.h>
+#include <linux/syscalls.h>
+#include <linux/jiffies.h>
+#include <linux/futex.h>
+#include <linux/ptrace.h>
+#include <linux/mount.h>
+#include <linux/audit.h>
+#include <linux/profile.h>
+#include <linux/rmap.h>
+#include <linux/acct.h>
+
+#include <asm/pgtable.h>
+#include <asm/pgalloc.h>
+#include <asm/uaccess.h>
+#include <asm/mmu_context.h>
+#include <asm/cacheflush.h>
+#include <asm/tlbflush.h>
+
+/*
+ * Protected counters by write_lock_irq(&tasklist_lock)
+ */
+unsigned long total_forks;	/* Handle normal Linux uptimes. */
+int nr_threads; 		/* The idle threads do not count.. */
+
+int max_threads;		/* tunable limit on nr_threads */
+
+DEFINE_PER_CPU(unsigned long, process_counts) = 0;
+
+ __cacheline_aligned DEFINE_RWLOCK(tasklist_lock);  /* outer */
+
+EXPORT_SYMBOL(tasklist_lock);
+
+int nr_processes(void)
+{
+	int cpu;
+	int total = 0;
+
+	for_each_online_cpu(cpu)
+		total += per_cpu(process_counts, cpu);
+
+	return total;
+}
+
+#ifndef __HAVE_ARCH_TASK_STRUCT_ALLOCATOR
+# define alloc_task_struct()	kmem_cache_alloc(task_struct_cachep, GFP_KERNEL)
+# define free_task_struct(tsk)	kmem_cache_free(task_struct_cachep, (tsk))
+static kmem_cache_t *task_struct_cachep;
+#endif
+
+void free_task(struct task_struct *tsk)
+{
+	free_thread_info(tsk->thread_info);
+	free_task_struct(tsk);
+}
+EXPORT_SYMBOL(free_task);
+
+void __put_task_struct(struct task_struct *tsk)
+{
+	WARN_ON(!(tsk->exit_state & (EXIT_DEAD | EXIT_ZOMBIE)));
+	WARN_ON(atomic_read(&tsk->usage));
+	WARN_ON(tsk == current);
+
+	if (unlikely(tsk->audit_context))
+		audit_free(tsk);
+	security_task_free(tsk);
+	free_uid(tsk->user);
+	put_group_info(tsk->group_info);
+
+	if (!profile_handoff_task(tsk))
+		free_task(tsk);
+}
+
+void __init fork_init(unsigned long mempages)
+{
+#ifndef __HAVE_ARCH_TASK_STRUCT_ALLOCATOR
+#ifndef ARCH_MIN_TASKALIGN
+#define ARCH_MIN_TASKALIGN	L1_CACHE_BYTES
+#endif
+	/* create a slab on which task_structs can be allocated */
+	task_struct_cachep =
+		kmem_cache_create("task_struct", sizeof(struct task_struct),
+			ARCH_MIN_TASKALIGN, SLAB_PANIC, NULL, NULL);
+#endif
+
+	/*
+	 * The default maximum number of threads is set to a safe
+	 * value: the thread structures can take up at most half
+	 * of memory.
+	 */
+	max_threads = mempages / (8 * THREAD_SIZE / PAGE_SIZE);
+
+	/*
+	 * we need to allow at least 20 threads to boot a system
+	 */
+	if(max_threads < 20)
+		max_threads = 20;
+
+	init_task.signal->rlim[RLIMIT_NPROC].rlim_cur = max_threads/2;
+	init_task.signal->rlim[RLIMIT_NPROC].rlim_max = max_threads/2;
+}
+
+static struct task_struct *dup_task_struct(struct task_struct *orig)
+{
+	struct task_struct *tsk;
+	struct thread_info *ti;
+
+	prepare_to_copy(orig);
+
+	tsk = alloc_task_struct();
+	if (!tsk)
+		return NULL;
+
+	ti = alloc_thread_info(tsk);
+	if (!ti) {
+		free_task_struct(tsk);
+		return NULL;
+	}
+
+	*ti = *orig->thread_info;
+	*tsk = *orig;
+	tsk->thread_info = ti;
+	ti->task = tsk;
+
+	/* One for us, one for whoever does the "release_task()" (usually parent) */
+	atomic_set(&tsk->usage,2);
+	return tsk;
+}
+
+#ifdef CONFIG_MMU
+static inline int dup_mmap(struct mm_struct * mm, struct mm_struct * oldmm)
+{
+	struct vm_area_struct * mpnt, *tmp, **pprev;
+	struct rb_node **rb_link, *rb_parent;
+	int retval;
+	unsigned long charge;
+	struct mempolicy *pol;
+
+	down_write(&oldmm->mmap_sem);
+	flush_cache_mm(current->mm);
+	mm->locked_vm = 0;
+	mm->mmap = NULL;
+	mm->mmap_cache = NULL;
+	mm->free_area_cache = oldmm->mmap_base;
+	mm->map_count = 0;
+	mm->rss = 0;
+	mm->anon_rss = 0;
+	cpus_clear(mm->cpu_vm_mask);
+	mm->mm_rb = RB_ROOT;
+	rb_link = &mm->mm_rb.rb_node;
+	rb_parent = NULL;
+	pprev = &mm->mmap;
+
+	for (mpnt = current->mm->mmap ; mpnt ; mpnt = mpnt->vm_next) {
+		struct file *file;
+
+		if (mpnt->vm_flags & VM_DONTCOPY) {
+			__vm_stat_account(mm, mpnt->vm_flags, mpnt->vm_file,
+							-vma_pages(mpnt));
+			continue;
+		}
+		charge = 0;
+		if (mpnt->vm_flags & VM_ACCOUNT) {
+			unsigned int len = (mpnt->vm_end - mpnt->vm_start) >> PAGE_SHIFT;
+			if (security_vm_enough_memory(len))
+				goto fail_nomem;
+			charge = len;
+		}
+		tmp = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
+		if (!tmp)
+			goto fail_nomem;
+		*tmp = *mpnt;
+		pol = mpol_copy(vma_policy(mpnt));
+		retval = PTR_ERR(pol);
+		if (IS_ERR(pol))
+			goto fail_nomem_policy;
+		vma_set_policy(tmp, pol);
+		tmp->vm_flags &= ~VM_LOCKED;
+		tmp->vm_mm = mm;
+		tmp->vm_next = NULL;
+		anon_vma_link(tmp);
+		file = tmp->vm_file;
+		if (file) {
+			struct inode *inode = file->f_dentry->d_inode;
+			get_file(file);
+			if (tmp->vm_flags & VM_DENYWRITE)
+				atomic_dec(&inode->i_writecount);
+      
+			/* insert tmp into the share list, just after mpnt */
+			spin_lock(&file->f_mapping->i_mmap_lock);
+			tmp->vm_truncate_count = mpnt->vm_truncate_count;
+			flush_dcache_mmap_lock(file->f_mapping);
+			vma_prio_tree_add(tmp, mpnt);
+			flush_dcache_mmap_unlock(file->f_mapping);
+			spin_unlock(&file->f_mapping->i_mmap_lock);
+		}
+
+		/*
+		 * Link in the new vma and copy the page table entries:
+		 * link in first so that swapoff can see swap entries,
+		 * and try_to_unmap_one's find_vma find the new vma.
+		 */
+		spin_lock(&mm->page_table_lock);
+		*pprev = tmp;
+		pprev = &tmp->vm_next;
+
+		__vma_link_rb(mm, tmp, rb_link, rb_parent);
+		rb_link = &tmp->vm_rb.rb_right;
+		rb_parent = &tmp->vm_rb;
+
+		mm->map_count++;
+		retval = copy_page_range(mm, current->mm, tmp);
+		spin_unlock(&mm->page_table_lock);
+
+		if (tmp->vm_ops && tmp->vm_ops->open)
+			tmp->vm_ops->open(tmp);
+
+		if (retval)
+			goto out;
+	}
+	retval = 0;
+
+out:
+	flush_tlb_mm(current->mm);
+	up_write(&oldmm->mmap_sem);
+	return retval;
+fail_nomem_policy:
+	kmem_cache_free(vm_area_cachep, tmp);
+fail_nomem:
+	retval = -ENOMEM;
+	vm_unacct_memory(charge);
+	goto out;
+}
+
+static inline int mm_alloc_pgd(struct mm_struct * mm)
+{
+	mm->pgd = pgd_alloc(mm);
+	if (unlikely(!mm->pgd))
+		return -ENOMEM;
+	return 0;
+}
+
+static inline void mm_free_pgd(struct mm_struct * mm)
+{
+	pgd_free(mm->pgd);
+}
+#else
+#define dup_mmap(mm, oldmm)	(0)
+#define mm_alloc_pgd(mm)	(0)
+#define mm_free_pgd(mm)
+#endif /* CONFIG_MMU */
+
+ __cacheline_aligned_in_smp DEFINE_SPINLOCK(mmlist_lock);
+
+#define allocate_mm()	(kmem_cache_alloc(mm_cachep, SLAB_KERNEL))
+#define free_mm(mm)	(kmem_cache_free(mm_cachep, (mm)))
+
+#include <linux/init_task.h>
+
+static struct mm_struct * mm_init(struct mm_struct * mm)
+{
+	atomic_set(&mm->mm_users, 1);
+	atomic_set(&mm->mm_count, 1);
+	init_rwsem(&mm->mmap_sem);
+	INIT_LIST_HEAD(&mm->mmlist);
+	mm->core_waiters = 0;
+	mm->nr_ptes = 0;
+	spin_lock_init(&mm->page_table_lock);
+	rwlock_init(&mm->ioctx_list_lock);
+	mm->ioctx_list = NULL;
+	mm->default_kioctx = (struct kioctx)INIT_KIOCTX(mm->default_kioctx, *mm);
+	mm->free_area_cache = TASK_UNMAPPED_BASE;
+
+	if (likely(!mm_alloc_pgd(mm))) {
+		mm->def_flags = 0;
+		return mm;
+	}
+	free_mm(mm);
+	return NULL;
+}
+
+/*
+ * Allocate and initialize an mm_struct.
+ */
+struct mm_struct * mm_alloc(void)
+{
+	struct mm_struct * mm;
+
+	mm = allocate_mm();
+	if (mm) {
+		memset(mm, 0, sizeof(*mm));
+		mm = mm_init(mm);
+	}
+	return mm;
+}
+
+/*
+ * Called when the last reference to the mm
+ * is dropped: either by a lazy thread or by
+ * mmput. Free the page directory and the mm.
+ */
+void fastcall __mmdrop(struct mm_struct *mm)
+{
+	BUG_ON(mm == &init_mm);
+	mm_free_pgd(mm);
+	destroy_context(mm);
+	free_mm(mm);
+}
+
+/*
+ * Decrement the use count and release all resources for an mm.
+ */
+void mmput(struct mm_struct *mm)
+{
+	if (atomic_dec_and_test(&mm->mm_users)) {
+		exit_aio(mm);
+		exit_mmap(mm);
+		if (!list_empty(&mm->mmlist)) {
+			spin_lock(&mmlist_lock);
+			list_del(&mm->mmlist);
+			spin_unlock(&mmlist_lock);
+		}
+		put_swap_token(mm);
+		mmdrop(mm);
+	}
+}
+EXPORT_SYMBOL_GPL(mmput);
+
+/**
+ * get_task_mm - acquire a reference to the task's mm
+ *
+ * Returns %NULL if the task has no mm.  Checks PF_BORROWED_MM (meaning
+ * this kernel workthread has transiently adopted a user mm with use_mm,
+ * to do its AIO) is not set and if so returns a reference to it, after
+ * bumping up the use count.  User must release the mm via mmput()
+ * after use.  Typically used by /proc and ptrace.
+ */
+struct mm_struct *get_task_mm(struct task_struct *task)
+{
+	struct mm_struct *mm;
+
+	task_lock(task);
+	mm = task->mm;
+	if (mm) {
+		if (task->flags & PF_BORROWED_MM)
+			mm = NULL;
+		else
+			atomic_inc(&mm->mm_users);
+	}
+	task_unlock(task);
+	return mm;
+}
+EXPORT_SYMBOL_GPL(get_task_mm);
+
+/* Please note the differences between mmput and mm_release.
+ * mmput is called whenever we stop holding onto a mm_struct,
+ * error success whatever.
+ *
+ * mm_release is called after a mm_struct has been removed
+ * from the current process.
+ *
+ * This difference is important for error handling, when we
+ * only half set up a mm_struct for a new process and need to restore
+ * the old one.  Because we mmput the new mm_struct before
+ * restoring the old one. . .
+ * Eric Biederman 10 January 1998
+ */
+void mm_release(struct task_struct *tsk, struct mm_struct *mm)
+{
+	struct completion *vfork_done = tsk->vfork_done;
+
+	/* Get rid of any cached register state */
+	deactivate_mm(tsk, mm);
+
+	/* notify parent sleeping on vfork() */
+	if (vfork_done) {
+		tsk->vfork_done = NULL;
+		complete(vfork_done);
+	}
+	if (tsk->clear_child_tid && atomic_read(&mm->mm_users) > 1) {
+		u32 __user * tidptr = tsk->clear_child_tid;
+		tsk->clear_child_tid = NULL;
+
+		/*
+		 * We don't check the error code - if userspace has
+		 * not set up a proper pointer then tough luck.
+		 */
+		put_user(0, tidptr);
+		sys_futex(tidptr, FUTEX_WAKE, 1, NULL, NULL, 0);
+	}
+}
+
+static int copy_mm(unsigned long clone_flags, struct task_struct * tsk)
+{
+	struct mm_struct * mm, *oldmm;
+	int retval;
+
+	tsk->min_flt = tsk->maj_flt = 0;
+	tsk->nvcsw = tsk->nivcsw = 0;
+
+	tsk->mm = NULL;
+	tsk->active_mm = NULL;
+
+	/*
+	 * Are we cloning a kernel thread?
+	 *
+	 * We need to steal a active VM for that..
+	 */
+	oldmm = current->mm;
+	if (!oldmm)
+		return 0;
+
+	if (clone_flags & CLONE_VM) {
+		atomic_inc(&oldmm->mm_users);
+		mm = oldmm;
+		/*
+		 * There are cases where the PTL is held to ensure no
+		 * new threads start up in user mode using an mm, which
+		 * allows optimizing out ipis; the tlb_gather_mmu code
+		 * is an example.
+		 */
+		spin_unlock_wait(&oldmm->page_table_lock);
+		goto good_mm;
+	}
+
+	retval = -ENOMEM;
+	mm = allocate_mm();
+	if (!mm)
+		goto fail_nomem;
+
+	/* Copy the current MM stuff.. */
+	memcpy(mm, oldmm, sizeof(*mm));
+	if (!mm_init(mm))
+		goto fail_nomem;
+
+	if (init_new_context(tsk,mm))
+		goto fail_nocontext;
+
+	retval = dup_mmap(mm, oldmm);
+	if (retval)
+		goto free_pt;
+
+	mm->hiwater_rss = mm->rss;
+	mm->hiwater_vm = mm->total_vm;
+
+good_mm:
+	tsk->mm = mm;
+	tsk->active_mm = mm;
+	return 0;
+
+free_pt:
+	mmput(mm);
+fail_nomem:
+	return retval;
+
+fail_nocontext:
+	/*
+	 * If init_new_context() failed, we cannot use mmput() to free the mm
+	 * because it calls destroy_context()
+	 */
+	mm_free_pgd(mm);
+	free_mm(mm);
+	return retval;
+}
+
+static inline struct fs_struct *__copy_fs_struct(struct fs_struct *old)
+{
+	struct fs_struct *fs = kmem_cache_alloc(fs_cachep, GFP_KERNEL);
+	/* We don't need to lock fs - think why ;-) */
+	if (fs) {
+		atomic_set(&fs->count, 1);
+		rwlock_init(&fs->lock);
+		fs->umask = old->umask;
+		read_lock(&old->lock);
+		fs->rootmnt = mntget(old->rootmnt);
+		fs->root = dget(old->root);
+		fs->pwdmnt = mntget(old->pwdmnt);
+		fs->pwd = dget(old->pwd);
+		if (old->altroot) {
+			fs->altrootmnt = mntget(old->altrootmnt);
+			fs->altroot = dget(old->altroot);
+		} else {
+			fs->altrootmnt = NULL;
+			fs->altroot = NULL;
+		}
+		read_unlock(&old->lock);
+	}
+	return fs;
+}
+
+struct fs_struct *copy_fs_struct(struct fs_struct *old)
+{
+	return __copy_fs_struct(old);
+}
+
+EXPORT_SYMBOL_GPL(copy_fs_struct);
+
+static inline int copy_fs(unsigned long clone_flags, struct task_struct * tsk)
+{
+	if (clone_flags & CLONE_FS) {
+		atomic_inc(&current->fs->count);
+		return 0;
+	}
+	tsk->fs = __copy_fs_struct(current->fs);
+	if (!tsk->fs)
+		return -ENOMEM;
+	return 0;
+}
+
+static int count_open_files(struct files_struct *files, int size)
+{
+	int i;
+
+	/* Find the last open fd */
+	for (i = size/(8*sizeof(long)); i > 0; ) {
+		if (files->open_fds->fds_bits[--i])
+			break;
+	}
+	i = (i+1) * 8 * sizeof(long);
+	return i;
+}
+
+static int copy_files(unsigned long clone_flags, struct task_struct * tsk)
+{
+	struct files_struct *oldf, *newf;
+	struct file **old_fds, **new_fds;
+	int open_files, size, i, error = 0, expand;
+
+	/*
+	 * A background process may not have any files ...
+	 */
+	oldf = current->files;
+	if (!oldf)
+		goto out;
+
+	if (clone_flags & CLONE_FILES) {
+		atomic_inc(&oldf->count);
+		goto out;
+	}
+
+	/*
+	 * Note: we may be using current for both targets (See exec.c)
+	 * This works because we cache current->files (old) as oldf. Don't
+	 * break this.
+	 */
+	tsk->files = NULL;
+	error = -ENOMEM;
+	newf = kmem_cache_alloc(files_cachep, SLAB_KERNEL);
+	if (!newf) 
+		goto out;
+
+	atomic_set(&newf->count, 1);
+
+	spin_lock_init(&newf->file_lock);
+	newf->next_fd	    = 0;
+	newf->max_fds	    = NR_OPEN_DEFAULT;
+	newf->max_fdset	    = __FD_SETSIZE;
+	newf->close_on_exec = &newf->close_on_exec_init;
+	newf->open_fds	    = &newf->open_fds_init;
+	newf->fd	    = &newf->fd_array[0];
+
+	spin_lock(&oldf->file_lock);
+
+	open_files = count_open_files(oldf, oldf->max_fdset);
+	expand = 0;
+
+	/*
+	 * Check whether we need to allocate a larger fd array or fd set.
+	 * Note: we're not a clone task, so the open count won't  change.
+	 */
+	if (open_files > newf->max_fdset) {
+		newf->max_fdset = 0;
+		expand = 1;
+	}
+	if (open_files > newf->max_fds) {
+		newf->max_fds = 0;
+		expand = 1;
+	}
+
+	/* if the old fdset gets grown now, we'll only copy up to "size" fds */
+	if (expand) {
+		spin_unlock(&oldf->file_lock);
+		spin_lock(&newf->file_lock);
+		error = expand_files(newf, open_files-1);
+		spin_unlock(&newf->file_lock);
+		if (error < 0)
+			goto out_release;
+		spin_lock(&oldf->file_lock);
+	}
+
+	old_fds = oldf->fd;
+	new_fds = newf->fd;
+
+	memcpy(newf->open_fds->fds_bits, oldf->open_fds->fds_bits, open_files/8);
+	memcpy(newf->close_on_exec->fds_bits, oldf->close_on_exec->fds_bits, open_files/8);
+
+	for (i = open_files; i != 0; i--) {
+		struct file *f = *old_fds++;
+		if (f) {
+			get_file(f);
+		} else {
+			/*
+			 * The fd may be claimed in the fd bitmap but not yet
+			 * instantiated in the files array if a sibling thread
+			 * is partway through open().  So make sure that this
+			 * fd is available to the new process.
+			 */
+			FD_CLR(open_files - i, newf->open_fds);
+		}
+		*new_fds++ = f;
+	}
+	spin_unlock(&oldf->file_lock);
+
+	/* compute the remainder to be cleared */
+	size = (newf->max_fds - open_files) * sizeof(struct file *);
+
+	/* This is long word aligned thus could use a optimized version */ 
+	memset(new_fds, 0, size); 
+
+	if (newf->max_fdset > open_files) {
+		int left = (newf->max_fdset-open_files)/8;
+		int start = open_files / (8 * sizeof(unsigned long));
+
+		memset(&newf->open_fds->fds_bits[start], 0, left);
+		memset(&newf->close_on_exec->fds_bits[start], 0, left);
+	}
+
+	tsk->files = newf;
+	error = 0;
+out:
+	return error;
+
+out_release:
+	free_fdset (newf->close_on_exec, newf->max_fdset);
+	free_fdset (newf->open_fds, newf->max_fdset);
+	free_fd_array(newf->fd, newf->max_fds);
+	kmem_cache_free(files_cachep, newf);
+	goto out;
+}
+
+/*
+ *	Helper to unshare the files of the current task.
+ *	We don't want to expose copy_files internals to
+ *	the exec layer of the kernel.
+ */
+
+int unshare_files(void)
+{
+	struct files_struct *files  = current->files;
+	int rc;
+
+	if(!files)
+		BUG();
+
+	/* This can race but the race causes us to copy when we don't
+	   need to and drop the copy */
+	if(atomic_read(&files->count) == 1)
+	{
+		atomic_inc(&files->count);
+		return 0;
+	}
+	rc = copy_files(0, current);
+	if(rc)
+		current->files = files;
+	return rc;
+}
+
+EXPORT_SYMBOL(unshare_files);
+
+static inline int copy_sighand(unsigned long clone_flags, struct task_struct * tsk)
+{
+	struct sighand_struct *sig;
+
+	if (clone_flags & (CLONE_SIGHAND | CLONE_THREAD)) {
+		atomic_inc(&current->sighand->count);
+		return 0;
+	}
+	sig = kmem_cache_alloc(sighand_cachep, GFP_KERNEL);
+	tsk->sighand = sig;
+	if (!sig)
+		return -ENOMEM;
+	spin_lock_init(&sig->siglock);
+	atomic_set(&sig->count, 1);
+	memcpy(sig->action, current->sighand->action, sizeof(sig->action));
+	return 0;
+}
+
+static inline int copy_signal(unsigned long clone_flags, struct task_struct * tsk)
+{
+	struct signal_struct *sig;
+
+	if (clone_flags & CLONE_THREAD) {
+		atomic_inc(&current->signal->count);
+		atomic_inc(&current->signal->live);
+		return 0;
+	}
+	sig = kmem_cache_alloc(signal_cachep, GFP_KERNEL);
+	tsk->signal = sig;
+	if (!sig)
+		return -ENOMEM;
+	atomic_set(&sig->count, 1);
+	atomic_set(&sig->live, 1);
+	init_waitqueue_head(&sig->wait_chldexit);
+	sig->flags = 0;
+	sig->group_exit_code = 0;
+	sig->group_exit_task = NULL;
+	sig->group_stop_count = 0;
+	sig->curr_target = NULL;
+	init_sigpending(&sig->shared_pending);
+	INIT_LIST_HEAD(&sig->posix_timers);
+
+	sig->tty = current->signal->tty;
+	sig->pgrp = process_group(current);
+	sig->session = current->signal->session;
+	sig->leader = 0;	/* session leadership doesn't inherit */
+	sig->tty_old_pgrp = 0;
+
+	sig->utime = sig->stime = sig->cutime = sig->cstime = cputime_zero;
+	sig->nvcsw = sig->nivcsw = sig->cnvcsw = sig->cnivcsw = 0;
+	sig->min_flt = sig->maj_flt = sig->cmin_flt = sig->cmaj_flt = 0;
+
+	task_lock(current->group_leader);
+	memcpy(sig->rlim, current->signal->rlim, sizeof sig->rlim);
+	task_unlock(current->group_leader);
+
+	return 0;
+}
+
+static inline void copy_flags(unsigned long clone_flags, struct task_struct *p)
+{
+	unsigned long new_flags = p->flags;
+
+	new_flags &= ~PF_SUPERPRIV;
+	new_flags |= PF_FORKNOEXEC;
+	if (!(clone_flags & CLONE_PTRACE))
+		p->ptrace = 0;
+	p->flags = new_flags;
+}
+
+asmlinkage long sys_set_tid_address(int __user *tidptr)
+{
+	current->clear_child_tid = tidptr;
+
+	return current->pid;
+}
+
+/*
+ * This creates a new process as a copy of the old one,
+ * but does not actually start it yet.
+ *
+ * It copies the registers, and all the appropriate
+ * parts of the process environment (as per the clone
+ * flags). The actual kick-off is left to the caller.
+ */
+static task_t *copy_process(unsigned long clone_flags,
+				 unsigned long stack_start,
+				 struct pt_regs *regs,
+				 unsigned long stack_size,
+				 int __user *parent_tidptr,
+				 int __user *child_tidptr,
+				 int pid)
+{
+	int retval;
+	struct task_struct *p = NULL;
+
+	if ((clone_flags & (CLONE_NEWNS|CLONE_FS)) == (CLONE_NEWNS|CLONE_FS))
+		return ERR_PTR(-EINVAL);
+
+	/*
+	 * Thread groups must share signals as well, and detached threads
+	 * can only be started up within the thread group.
+	 */
+	if ((clone_flags & CLONE_THREAD) && !(clone_flags & CLONE_SIGHAND))
+		return ERR_PTR(-EINVAL);
+
+	/*
+	 * Shared signal handlers imply shared VM. By way of the above,
+	 * thread groups also imply shared VM. Blocking this case allows
+	 * for various simplifications in other code.
+	 */
+	if ((clone_flags & CLONE_SIGHAND) && !(clone_flags & CLONE_VM))
+		return ERR_PTR(-EINVAL);
+
+	retval = security_task_create(clone_flags);
+	if (retval)
+		goto fork_out;
+
+	retval = -ENOMEM;
+	p = dup_task_struct(current);
+	if (!p)
+		goto fork_out;
+
+	retval = -EAGAIN;
+	if (atomic_read(&p->user->processes) >=
+			p->signal->rlim[RLIMIT_NPROC].rlim_cur) {
+		if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RESOURCE) &&
+				p->user != &root_user)
+			goto bad_fork_free;
+	}
+
+	atomic_inc(&p->user->__count);
+	atomic_inc(&p->user->processes);
+	get_group_info(p->group_info);
+
+	/*
+	 * If multiple threads are within copy_process(), then this check
+	 * triggers too late. This doesn't hurt, the check is only there
+	 * to stop root fork bombs.
+	 */
+	if (nr_threads >= max_threads)
+		goto bad_fork_cleanup_count;
+
+	if (!try_module_get(p->thread_info->exec_domain->module))
+		goto bad_fork_cleanup_count;
+
+	if (p->binfmt && !try_module_get(p->binfmt->module))
+		goto bad_fork_cleanup_put_domain;
+
+	p->did_exec = 0;
+	copy_flags(clone_flags, p);
+	p->pid = pid;
+	retval = -EFAULT;
+	if (clone_flags & CLONE_PARENT_SETTID)
+		if (put_user(p->pid, parent_tidptr))
+			goto bad_fork_cleanup;
+
+	p->proc_dentry = NULL;
+
+	INIT_LIST_HEAD(&p->children);
+	INIT_LIST_HEAD(&p->sibling);
+	p->vfork_done = NULL;
+	spin_lock_init(&p->alloc_lock);
+	spin_lock_init(&p->proc_lock);
+
+	clear_tsk_thread_flag(p, TIF_SIGPENDING);
+	init_sigpending(&p->pending);
+
+	p->it_real_value = 0;
+	p->it_real_incr = 0;
+	p->it_virt_value = cputime_zero;
+	p->it_virt_incr = cputime_zero;
+	p->it_prof_value = cputime_zero;
+	p->it_prof_incr = cputime_zero;
+	init_timer(&p->real_timer);
+	p->real_timer.data = (unsigned long) p;
+
+	p->utime = cputime_zero;
+	p->stime = cputime_zero;
+	p->rchar = 0;		/* I/O counter: bytes read */
+	p->wchar = 0;		/* I/O counter: bytes written */
+	p->syscr = 0;		/* I/O counter: read syscalls */
+	p->syscw = 0;		/* I/O counter: write syscalls */
+	acct_clear_integrals(p);
+
+	p->lock_depth = -1;		/* -1 = no lock */
+	do_posix_clock_monotonic_gettime(&p->start_time);
+	p->security = NULL;
+	p->io_context = NULL;
+	p->io_wait = NULL;
+	p->audit_context = NULL;
+#ifdef CONFIG_NUMA
+ 	p->mempolicy = mpol_copy(p->mempolicy);
+ 	if (IS_ERR(p->mempolicy)) {
+ 		retval = PTR_ERR(p->mempolicy);
+ 		p->mempolicy = NULL;
+ 		goto bad_fork_cleanup;
+ 	}
+#endif
+
+	p->tgid = p->pid;
+	if (clone_flags & CLONE_THREAD)
+		p->tgid = current->tgid;
+
+	if ((retval = security_task_alloc(p)))
+		goto bad_fork_cleanup_policy;
+	if ((retval = audit_alloc(p)))
+		goto bad_fork_cleanup_security;
+	/* copy all the process information */
+	if ((retval = copy_semundo(clone_flags, p)))
+		goto bad_fork_cleanup_audit;
+	if ((retval = copy_files(clone_flags, p)))
+		goto bad_fork_cleanup_semundo;
+	if ((retval = copy_fs(clone_flags, p)))
+		goto bad_fork_cleanup_files;
+	if ((retval = copy_sighand(clone_flags, p)))
+		goto bad_fork_cleanup_fs;
+	if ((retval = copy_signal(clone_flags, p)))
+		goto bad_fork_cleanup_sighand;
+	if ((retval = copy_mm(clone_flags, p)))
+		goto bad_fork_cleanup_signal;
+	if ((retval = copy_keys(clone_flags, p)))
+		goto bad_fork_cleanup_mm;
+	if ((retval = copy_namespace(clone_flags, p)))
+		goto bad_fork_cleanup_keys;
+	retval = copy_thread(0, clone_flags, stack_start, stack_size, p, regs);
+	if (retval)
+		goto bad_fork_cleanup_namespace;
+
+	p->set_child_tid = (clone_flags & CLONE_CHILD_SETTID) ? child_tidptr : NULL;
+	/*
+	 * Clear TID on mm_release()?
+	 */
+	p->clear_child_tid = (clone_flags & CLONE_CHILD_CLEARTID) ? child_tidptr: NULL;
+
+	/*
+	 * Syscall tracing should be turned off in the child regardless
+	 * of CLONE_PTRACE.
+	 */
+	clear_tsk_thread_flag(p, TIF_SYSCALL_TRACE);
+
+	/* Our parent execution domain becomes current domain
+	   These must match for thread signalling to apply */
+	   
+	p->parent_exec_id = p->self_exec_id;
+
+	/* ok, now we should be set up.. */
+	p->exit_signal = (clone_flags & CLONE_THREAD) ? -1 : (clone_flags & CSIGNAL);
+	p->pdeath_signal = 0;
+	p->exit_state = 0;
+
+	/* Perform scheduler related setup */
+	sched_fork(p);
+
+	/*
+	 * Ok, make it visible to the rest of the system.
+	 * We dont wake it up yet.
+	 */
+	p->group_leader = p;
+	INIT_LIST_HEAD(&p->ptrace_children);
+	INIT_LIST_HEAD(&p->ptrace_list);
+
+	/* Need tasklist lock for parent etc handling! */
+	write_lock_irq(&tasklist_lock);
+
+	/*
+	 * The task hasn't been attached yet, so cpus_allowed mask cannot
+	 * have changed. The cpus_allowed mask of the parent may have
+	 * changed after it was copied first time, and it may then move to
+	 * another CPU - so we re-copy it here and set the child's CPU to
+	 * the parent's CPU. This avoids alot of nasty races.
+	 */
+	p->cpus_allowed = current->cpus_allowed;
+	set_task_cpu(p, smp_processor_id());
+
+	/*
+	 * Check for pending SIGKILL! The new thread should not be allowed
+	 * to slip out of an OOM kill. (or normal SIGKILL.)
+	 */
+	if (sigismember(&current->pending.signal, SIGKILL)) {
+		write_unlock_irq(&tasklist_lock);
+		retval = -EINTR;
+		goto bad_fork_cleanup_namespace;
+	}
+
+	/* CLONE_PARENT re-uses the old parent */
+	if (clone_flags & (CLONE_PARENT|CLONE_THREAD))
+		p->real_parent = current->real_parent;
+	else
+		p->real_parent = current;
+	p->parent = p->real_parent;
+
+	if (clone_flags & CLONE_THREAD) {
+		spin_lock(&current->sighand->siglock);
+		/*
+		 * Important: if an exit-all has been started then
+		 * do not create this new thread - the whole thread
+		 * group is supposed to exit anyway.
+		 */
+		if (current->signal->flags & SIGNAL_GROUP_EXIT) {
+			spin_unlock(&current->sighand->siglock);
+			write_unlock_irq(&tasklist_lock);
+			retval = -EAGAIN;
+			goto bad_fork_cleanup_namespace;
+		}
+		p->group_leader = current->group_leader;
+
+		if (current->signal->group_stop_count > 0) {
+			/*
+			 * There is an all-stop in progress for the group.
+			 * We ourselves will stop as soon as we check signals.
+			 * Make the new thread part of that group stop too.
+			 */
+			current->signal->group_stop_count++;
+			set_tsk_thread_flag(p, TIF_SIGPENDING);
+		}
+
+		spin_unlock(&current->sighand->siglock);
+	}
+
+	SET_LINKS(p);
+	if (unlikely(p->ptrace & PT_PTRACED))
+		__ptrace_link(p, current->parent);
+
+	attach_pid(p, PIDTYPE_PID, p->pid);
+	attach_pid(p, PIDTYPE_TGID, p->tgid);
+	if (thread_group_leader(p)) {
+		attach_pid(p, PIDTYPE_PGID, process_group(p));
+		attach_pid(p, PIDTYPE_SID, p->signal->session);
+		if (p->pid)
+			__get_cpu_var(process_counts)++;
+	}
+
+	nr_threads++;
+	total_forks++;
+	write_unlock_irq(&tasklist_lock);
+	retval = 0;
+
+fork_out:
+	if (retval)
+		return ERR_PTR(retval);
+	return p;
+
+bad_fork_cleanup_namespace:
+	exit_namespace(p);
+bad_fork_cleanup_keys:
+	exit_keys(p);
+bad_fork_cleanup_mm:
+	if (p->mm)
+		mmput(p->mm);
+bad_fork_cleanup_signal:
+	exit_signal(p);
+bad_fork_cleanup_sighand:
+	exit_sighand(p);
+bad_fork_cleanup_fs:
+	exit_fs(p); /* blocking */
+bad_fork_cleanup_files:
+	exit_files(p); /* blocking */
+bad_fork_cleanup_semundo:
+	exit_sem(p);
+bad_fork_cleanup_audit:
+	audit_free(p);
+bad_fork_cleanup_security:
+	security_task_free(p);
+bad_fork_cleanup_policy:
+#ifdef CONFIG_NUMA
+	mpol_free(p->mempolicy);
+#endif
+bad_fork_cleanup:
+	if (p->binfmt)
+		module_put(p->binfmt->module);
+bad_fork_cleanup_put_domain:
+	module_put(p->thread_info->exec_domain->module);
+bad_fork_cleanup_count:
+	put_group_info(p->group_info);
+	atomic_dec(&p->user->processes);
+	free_uid(p->user);
+bad_fork_free:
+	free_task(p);
+	goto fork_out;
+}
+
+struct pt_regs * __devinit __attribute__((weak)) idle_regs(struct pt_regs *regs)
+{
+	memset(regs, 0, sizeof(struct pt_regs));
+	return regs;
+}
+
+task_t * __devinit fork_idle(int cpu)
+{
+	task_t *task;
+	struct pt_regs regs;
+
+	task = copy_process(CLONE_VM, 0, idle_regs(&regs), 0, NULL, NULL, 0);
+	if (!task)
+		return ERR_PTR(-ENOMEM);
+	init_idle(task, cpu);
+	unhash_process(task);
+	return task;
+}
+
+static inline int fork_traceflag (unsigned clone_flags)
+{
+	if (clone_flags & CLONE_UNTRACED)
+		return 0;
+	else if (clone_flags & CLONE_VFORK) {
+		if (current->ptrace & PT_TRACE_VFORK)
+			return PTRACE_EVENT_VFORK;
+	} else if ((clone_flags & CSIGNAL) != SIGCHLD) {
+		if (current->ptrace & PT_TRACE_CLONE)
+			return PTRACE_EVENT_CLONE;
+	} else if (current->ptrace & PT_TRACE_FORK)
+		return PTRACE_EVENT_FORK;
+
+	return 0;
+}
+
+/*
+ *  Ok, this is the main fork-routine.
+ *
+ * It copies the process, and if successful kick-starts
+ * it and waits for it to finish using the VM if required.
+ */
+long do_fork(unsigned long clone_flags,
+	      unsigned long stack_start,
+	      struct pt_regs *regs,
+	      unsigned long stack_size,
+	      int __user *parent_tidptr,
+	      int __user *child_tidptr)
+{
+	struct task_struct *p;
+	int trace = 0;
+	long pid = alloc_pidmap();
+
+	if (pid < 0)
+		return -EAGAIN;
+	if (unlikely(current->ptrace)) {
+		trace = fork_traceflag (clone_flags);
+		if (trace)
+			clone_flags |= CLONE_PTRACE;
+	}
+
+	p = copy_process(clone_flags, stack_start, regs, stack_size, parent_tidptr, child_tidptr, pid);
+	/*
+	 * Do this prior waking up the new thread - the thread pointer
+	 * might get invalid after that point, if the thread exits quickly.
+	 */
+	if (!IS_ERR(p)) {
+		struct completion vfork;
+
+		if (clone_flags & CLONE_VFORK) {
+			p->vfork_done = &vfork;
+			init_completion(&vfork);
+		}
+
+		if ((p->ptrace & PT_PTRACED) || (clone_flags & CLONE_STOPPED)) {
+			/*
+			 * We'll start up with an immediate SIGSTOP.
+			 */
+			sigaddset(&p->pending.signal, SIGSTOP);
+			set_tsk_thread_flag(p, TIF_SIGPENDING);
+		}
+
+		if (!(clone_flags & CLONE_STOPPED))
+			wake_up_new_task(p, clone_flags);
+		else
+			p->state = TASK_STOPPED;
+
+		if (unlikely (trace)) {
+			current->ptrace_message = pid;
+			ptrace_notify ((trace << 8) | SIGTRAP);
+		}
+
+		if (clone_flags & CLONE_VFORK) {
+			wait_for_completion(&vfork);
+			if (unlikely (current->ptrace & PT_TRACE_VFORK_DONE))
+				ptrace_notify ((PTRACE_EVENT_VFORK_DONE << 8) | SIGTRAP);
+		}
+	} else {
+		free_pidmap(pid);
+		pid = PTR_ERR(p);
+	}
+	return pid;
+}
+
+/* SLAB cache for signal_struct structures (tsk->signal) */
+kmem_cache_t *signal_cachep;
+
+/* SLAB cache for sighand_struct structures (tsk->sighand) */
+kmem_cache_t *sighand_cachep;
+
+/* SLAB cache for files_struct structures (tsk->files) */
+kmem_cache_t *files_cachep;
+
+/* SLAB cache for fs_struct structures (tsk->fs) */
+kmem_cache_t *fs_cachep;
+
+/* SLAB cache for vm_area_struct structures */
+kmem_cache_t *vm_area_cachep;
+
+/* SLAB cache for mm_struct structures (tsk->mm) */
+kmem_cache_t *mm_cachep;
+
+void __init proc_caches_init(void)
+{
+	sighand_cachep = kmem_cache_create("sighand_cache",
+			sizeof(struct sighand_struct), 0,
+			SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL, NULL);
+	signal_cachep = kmem_cache_create("signal_cache",
+			sizeof(struct signal_struct), 0,
+			SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL, NULL);
+	files_cachep = kmem_cache_create("files_cache", 
+			sizeof(struct files_struct), 0,
+			SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL, NULL);
+	fs_cachep = kmem_cache_create("fs_cache", 
+			sizeof(struct fs_struct), 0,
+			SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL, NULL);
+	vm_area_cachep = kmem_cache_create("vm_area_struct",
+			sizeof(struct vm_area_struct), 0,
+			SLAB_PANIC, NULL, NULL);
+	mm_cachep = kmem_cache_create("mm_struct",
+			sizeof(struct mm_struct), 0,
+			SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL, NULL);
+}
diff -Nrub linux-2.6.11.7-orig/kernel/module.c linux-2.6.11.7-snare/kernel/module.c
--- linux-2.6.11.7-orig/kernel/module.c	2005-04-07 14:57:54.000000000 -0400
+++ linux-2.6.11.7-snare/kernel/module.c	2005-04-22 11:19:47.200664856 -0400
@@ -24,6 +24,7 @@
 #include <linux/vmalloc.h>
 #include <linux/elf.h>
 #include <linux/seq_file.h>
+#include <linux/saudit.h>
 #include <linux/syscalls.h>
 #include <linux/fcntl.h>
 #include <linux/rcupdate.h>
@@ -529,8 +530,10 @@
 	char name[MODULE_NAME_LEN];
 	int ret, forced = 0;
 
-	if (!capable(CAP_SYS_MODULE))
+	if (!capable(CAP_SYS_MODULE)) {
+		saudit_delete_module((char *)NULL, name_user, -EPERM);
 		return -EPERM;
+		}
 
 	if (strncpy_from_user(name, name_user, MODULE_NAME_LEN-1) < 0)
 		return -EFAULT;
@@ -593,6 +596,7 @@
 
  out:
 	up(&module_mutex);
+	saudit_delete_module((char *)NULL, name_user, ret);
 	return ret;
 }
 
@@ -1742,17 +1746,22 @@
 	int ret = 0;
 
 	/* Must have permission */
-	if (!capable(CAP_SYS_MODULE))
+	if (!capable(CAP_SYS_MODULE)) {
+		saudit_create_module((char *)NULL, uargs, -EPERM);
 		return -EPERM;
+	}
 
 	/* Only one module load at a time, please */
-	if (down_interruptible(&module_mutex) != 0)
+	if (down_interruptible(&module_mutex) != 0) {
+		saudit_create_module((char *)NULL, uargs, -EINTR);
 		return -EINTR;
+	}
 
 	/* Do all the hard work */
 	mod = load_module(umod, len, uargs);
 	if (IS_ERR(mod)) {
 		up(&module_mutex);
+		saudit_create_module((char *)NULL, uargs, PTR_ERR(mod));
 		return PTR_ERR(mod);
 	}
 
@@ -1794,6 +1803,7 @@
 			free_module(mod);
 			up(&module_mutex);
 		}
+		saudit_create_module(mod->name, (char *)NULL, ret);
 		return ret;
 	}
 
@@ -1808,6 +1818,7 @@
 	mod->init_text_size = 0;
 	up(&module_mutex);
 
+	saudit_create_module(mod->name, (char *)NULL, 0);
 	return 0;
 }
 
diff -Nrub linux-2.6.11.7-orig/kernel/module.c.orig linux-2.6.11.7-snare/kernel/module.c.orig
--- linux-2.6.11.7-orig/kernel/module.c.orig	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.11.7-snare/kernel/module.c.orig	2005-04-07 14:57:54.000000000 -0400
@@ -0,0 +1,2091 @@
+/* Rewritten by Rusty Russell, on the backs of many others...
+   Copyright (C) 2002 Richard Henderson
+   Copyright (C) 2001 Rusty Russell, 2002 Rusty Russell IBM.
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/moduleloader.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/elf.h>
+#include <linux/seq_file.h>
+#include <linux/syscalls.h>
+#include <linux/fcntl.h>
+#include <linux/rcupdate.h>
+#include <linux/cpu.h>
+#include <linux/moduleparam.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/vermagic.h>
+#include <linux/notifier.h>
+#include <linux/stop_machine.h>
+#include <linux/device.h>
+#include <asm/uaccess.h>
+#include <asm/semaphore.h>
+#include <asm/cacheflush.h>
+
+#if 0
+#define DEBUGP printk
+#else
+#define DEBUGP(fmt , a...)
+#endif
+
+#ifndef ARCH_SHF_SMALL
+#define ARCH_SHF_SMALL 0
+#endif
+
+/* If this is set, the section belongs in the init part of the module */
+#define INIT_OFFSET_MASK (1UL << (BITS_PER_LONG-1))
+
+/* Protects module list */
+static DEFINE_SPINLOCK(modlist_lock);
+
+/* List of modules, protected by module_mutex AND modlist_lock */
+static DECLARE_MUTEX(module_mutex);
+static LIST_HEAD(modules);
+
+static DECLARE_MUTEX(notify_mutex);
+static struct notifier_block * module_notify_list;
+
+int register_module_notifier(struct notifier_block * nb)
+{
+	int err;
+	down(&notify_mutex);
+	err = notifier_chain_register(&module_notify_list, nb);
+	up(&notify_mutex);
+	return err;
+}
+EXPORT_SYMBOL(register_module_notifier);
+
+int unregister_module_notifier(struct notifier_block * nb)
+{
+	int err;
+	down(&notify_mutex);
+	err = notifier_chain_unregister(&module_notify_list, nb);
+	up(&notify_mutex);
+	return err;
+}
+EXPORT_SYMBOL(unregister_module_notifier);
+
+/* We require a truly strong try_module_get() */
+static inline int strong_try_module_get(struct module *mod)
+{
+	if (mod && mod->state == MODULE_STATE_COMING)
+		return 0;
+	return try_module_get(mod);
+}
+
+/* A thread that wants to hold a reference to a module only while it
+ * is running can call ths to safely exit.
+ * nfsd and lockd use this.
+ */
+void __module_put_and_exit(struct module *mod, long code)
+{
+	module_put(mod);
+	do_exit(code);
+}
+EXPORT_SYMBOL(__module_put_and_exit);
+	
+/* Find a module section: 0 means not found. */
+static unsigned int find_sec(Elf_Ehdr *hdr,
+			     Elf_Shdr *sechdrs,
+			     const char *secstrings,
+			     const char *name)
+{
+	unsigned int i;
+
+	for (i = 1; i < hdr->e_shnum; i++)
+		/* Alloc bit cleared means "ignore it." */
+		if ((sechdrs[i].sh_flags & SHF_ALLOC)
+		    && strcmp(secstrings+sechdrs[i].sh_name, name) == 0)
+			return i;
+	return 0;
+}
+
+/* Provided by the linker */
+extern const struct kernel_symbol __start___ksymtab[];
+extern const struct kernel_symbol __stop___ksymtab[];
+extern const struct kernel_symbol __start___ksymtab_gpl[];
+extern const struct kernel_symbol __stop___ksymtab_gpl[];
+extern const unsigned long __start___kcrctab[];
+extern const unsigned long __start___kcrctab_gpl[];
+
+#ifndef CONFIG_MODVERSIONS
+#define symversion(base, idx) NULL
+#else
+#define symversion(base, idx) ((base) ? ((base) + (idx)) : NULL)
+#endif
+
+/* Find a symbol, return value, crc and module which owns it */
+static unsigned long __find_symbol(const char *name,
+				   struct module **owner,
+				   const unsigned long **crc,
+				   int gplok)
+{
+	struct module *mod;
+	unsigned int i;
+
+	/* Core kernel first. */ 
+	*owner = NULL;
+	for (i = 0; __start___ksymtab+i < __stop___ksymtab; i++) {
+		if (strcmp(__start___ksymtab[i].name, name) == 0) {
+			*crc = symversion(__start___kcrctab, i);
+			return __start___ksymtab[i].value;
+		}
+	}
+	if (gplok) {
+		for (i = 0; __start___ksymtab_gpl+i<__stop___ksymtab_gpl; i++)
+			if (strcmp(__start___ksymtab_gpl[i].name, name) == 0) {
+				*crc = symversion(__start___kcrctab_gpl, i);
+				return __start___ksymtab_gpl[i].value;
+			}
+	}
+
+	/* Now try modules. */ 
+	list_for_each_entry(mod, &modules, list) {
+		*owner = mod;
+		for (i = 0; i < mod->num_syms; i++)
+			if (strcmp(mod->syms[i].name, name) == 0) {
+				*crc = symversion(mod->crcs, i);
+				return mod->syms[i].value;
+			}
+
+		if (gplok) {
+			for (i = 0; i < mod->num_gpl_syms; i++) {
+				if (strcmp(mod->gpl_syms[i].name, name) == 0) {
+					*crc = symversion(mod->gpl_crcs, i);
+					return mod->gpl_syms[i].value;
+				}
+			}
+		}
+	}
+	DEBUGP("Failed to find symbol %s\n", name);
+ 	return 0;
+}
+
+/* Find a symbol in this elf symbol table */
+static unsigned long find_local_symbol(Elf_Shdr *sechdrs,
+				       unsigned int symindex,
+				       const char *strtab,
+				       const char *name)
+{
+	unsigned int i;
+	Elf_Sym *sym = (void *)sechdrs[symindex].sh_addr;
+
+	/* Search (defined) internal symbols first. */
+	for (i = 1; i < sechdrs[symindex].sh_size/sizeof(*sym); i++) {
+		if (sym[i].st_shndx != SHN_UNDEF
+		    && strcmp(name, strtab + sym[i].st_name) == 0)
+			return sym[i].st_value;
+	}
+	return 0;
+}
+
+/* Search for module by name: must hold module_mutex. */
+static struct module *find_module(const char *name)
+{
+	struct module *mod;
+
+	list_for_each_entry(mod, &modules, list) {
+		if (strcmp(mod->name, name) == 0)
+			return mod;
+	}
+	return NULL;
+}
+
+#ifdef CONFIG_SMP
+/* Number of blocks used and allocated. */
+static unsigned int pcpu_num_used, pcpu_num_allocated;
+/* Size of each block.  -ve means used. */
+static int *pcpu_size;
+
+static int split_block(unsigned int i, unsigned short size)
+{
+	/* Reallocation required? */
+	if (pcpu_num_used + 1 > pcpu_num_allocated) {
+		int *new = kmalloc(sizeof(new[0]) * pcpu_num_allocated*2,
+				   GFP_KERNEL);
+		if (!new)
+			return 0;
+
+		memcpy(new, pcpu_size, sizeof(new[0])*pcpu_num_allocated);
+		pcpu_num_allocated *= 2;
+		kfree(pcpu_size);
+		pcpu_size = new;
+	}
+
+	/* Insert a new subblock */
+	memmove(&pcpu_size[i+1], &pcpu_size[i],
+		sizeof(pcpu_size[0]) * (pcpu_num_used - i));
+	pcpu_num_used++;
+
+	pcpu_size[i+1] -= size;
+	pcpu_size[i] = size;
+	return 1;
+}
+
+static inline unsigned int block_size(int val)
+{
+	if (val < 0)
+		return -val;
+	return val;
+}
+
+/* Created by linker magic */
+extern char __per_cpu_start[], __per_cpu_end[];
+
+static void *percpu_modalloc(unsigned long size, unsigned long align)
+{
+	unsigned long extra;
+	unsigned int i;
+	void *ptr;
+
+	BUG_ON(align > SMP_CACHE_BYTES);
+
+	ptr = __per_cpu_start;
+	for (i = 0; i < pcpu_num_used; ptr += block_size(pcpu_size[i]), i++) {
+		/* Extra for alignment requirement. */
+		extra = ALIGN((unsigned long)ptr, align) - (unsigned long)ptr;
+		BUG_ON(i == 0 && extra != 0);
+
+		if (pcpu_size[i] < 0 || pcpu_size[i] < extra + size)
+			continue;
+
+		/* Transfer extra to previous block. */
+		if (pcpu_size[i-1] < 0)
+			pcpu_size[i-1] -= extra;
+		else
+			pcpu_size[i-1] += extra;
+		pcpu_size[i] -= extra;
+		ptr += extra;
+
+		/* Split block if warranted */
+		if (pcpu_size[i] - size > sizeof(unsigned long))
+			if (!split_block(i, size))
+				return NULL;
+
+		/* Mark allocated */
+		pcpu_size[i] = -pcpu_size[i];
+		return ptr;
+	}
+
+	printk(KERN_WARNING "Could not allocate %lu bytes percpu data\n",
+	       size);
+	return NULL;
+}
+
+static void percpu_modfree(void *freeme)
+{
+	unsigned int i;
+	void *ptr = __per_cpu_start + block_size(pcpu_size[0]);
+
+	/* First entry is core kernel percpu data. */
+	for (i = 1; i < pcpu_num_used; ptr += block_size(pcpu_size[i]), i++) {
+		if (ptr == freeme) {
+			pcpu_size[i] = -pcpu_size[i];
+			goto free;
+		}
+	}
+	BUG();
+
+ free:
+	/* Merge with previous? */
+	if (pcpu_size[i-1] >= 0) {
+		pcpu_size[i-1] += pcpu_size[i];
+		pcpu_num_used--;
+		memmove(&pcpu_size[i], &pcpu_size[i+1],
+			(pcpu_num_used - i) * sizeof(pcpu_size[0]));
+		i--;
+	}
+	/* Merge with next? */
+	if (i+1 < pcpu_num_used && pcpu_size[i+1] >= 0) {
+		pcpu_size[i] += pcpu_size[i+1];
+		pcpu_num_used--;
+		memmove(&pcpu_size[i+1], &pcpu_size[i+2],
+			(pcpu_num_used - (i+1)) * sizeof(pcpu_size[0]));
+	}
+}
+
+static unsigned int find_pcpusec(Elf_Ehdr *hdr,
+				 Elf_Shdr *sechdrs,
+				 const char *secstrings)
+{
+	return find_sec(hdr, sechdrs, secstrings, ".data.percpu");
+}
+
+static int percpu_modinit(void)
+{
+	pcpu_num_used = 2;
+	pcpu_num_allocated = 2;
+	pcpu_size = kmalloc(sizeof(pcpu_size[0]) * pcpu_num_allocated,
+			    GFP_KERNEL);
+	/* Static in-kernel percpu data (used). */
+	pcpu_size[0] = -ALIGN(__per_cpu_end-__per_cpu_start, SMP_CACHE_BYTES);
+	/* Free room. */
+	pcpu_size[1] = PERCPU_ENOUGH_ROOM + pcpu_size[0];
+	if (pcpu_size[1] < 0) {
+		printk(KERN_ERR "No per-cpu room for modules.\n");
+		pcpu_num_used = 1;
+	}
+
+	return 0;
+}	
+__initcall(percpu_modinit);
+#else /* ... !CONFIG_SMP */
+static inline void *percpu_modalloc(unsigned long size, unsigned long align)
+{
+	return NULL;
+}
+static inline void percpu_modfree(void *pcpuptr)
+{
+	BUG();
+}
+static inline unsigned int find_pcpusec(Elf_Ehdr *hdr,
+					Elf_Shdr *sechdrs,
+					const char *secstrings)
+{
+	return 0;
+}
+static inline void percpu_modcopy(void *pcpudst, const void *src,
+				  unsigned long size)
+{
+	/* pcpusec should be 0, and size of that section should be 0. */
+	BUG_ON(size != 0);
+}
+#endif /* CONFIG_SMP */
+
+#ifdef CONFIG_MODULE_UNLOAD
+/* Init the unload section of the module. */
+static void module_unload_init(struct module *mod)
+{
+	unsigned int i;
+
+	INIT_LIST_HEAD(&mod->modules_which_use_me);
+	for (i = 0; i < NR_CPUS; i++)
+		local_set(&mod->ref[i].count, 0);
+	/* Hold reference count during initialization. */
+	local_set(&mod->ref[_smp_processor_id()].count, 1);
+	/* Backwards compatibility macros put refcount during init. */
+	mod->waiter = current;
+}
+
+/* modules using other modules */
+struct module_use
+{
+	struct list_head list;
+	struct module *module_which_uses;
+};
+
+/* Does a already use b? */
+static int already_uses(struct module *a, struct module *b)
+{
+	struct module_use *use;
+
+	list_for_each_entry(use, &b->modules_which_use_me, list) {
+		if (use->module_which_uses == a) {
+			DEBUGP("%s uses %s!\n", a->name, b->name);
+			return 1;
+		}
+	}
+	DEBUGP("%s does not use %s!\n", a->name, b->name);
+	return 0;
+}
+
+/* Module a uses b */
+static int use_module(struct module *a, struct module *b)
+{
+	struct module_use *use;
+	if (b == NULL || already_uses(a, b)) return 1;
+
+	if (!strong_try_module_get(b))
+		return 0;
+
+	DEBUGP("Allocating new usage for %s.\n", a->name);
+	use = kmalloc(sizeof(*use), GFP_ATOMIC);
+	if (!use) {
+		printk("%s: out of memory loading\n", a->name);
+		module_put(b);
+		return 0;
+	}
+
+	use->module_which_uses = a;
+	list_add(&use->list, &b->modules_which_use_me);
+	return 1;
+}
+
+/* Clear the unload stuff of the module. */
+static void module_unload_free(struct module *mod)
+{
+	struct module *i;
+
+	list_for_each_entry(i, &modules, list) {
+		struct module_use *use;
+
+		list_for_each_entry(use, &i->modules_which_use_me, list) {
+			if (use->module_which_uses == mod) {
+				DEBUGP("%s unusing %s\n", mod->name, i->name);
+				module_put(i);
+				list_del(&use->list);
+				kfree(use);
+				/* There can be at most one match. */
+				break;
+			}
+		}
+	}
+}
+
+#ifdef CONFIG_MODULE_FORCE_UNLOAD
+static inline int try_force(unsigned int flags)
+{
+	int ret = (flags & O_TRUNC);
+	if (ret)
+		tainted |= TAINT_FORCED_MODULE;
+	return ret;
+}
+#else
+static inline int try_force(unsigned int flags)
+{
+	return 0;
+}
+#endif /* CONFIG_MODULE_FORCE_UNLOAD */
+
+struct stopref
+{
+	struct module *mod;
+	int flags;
+	int *forced;
+};
+
+/* Whole machine is stopped with interrupts off when this runs. */
+static inline int __try_stop_module(void *_sref)
+{
+	struct stopref *sref = _sref;
+
+	/* If it's not unused, quit unless we are told to block. */
+	if ((sref->flags & O_NONBLOCK) && module_refcount(sref->mod) != 0) {
+		if (!(*sref->forced = try_force(sref->flags)))
+			return -EWOULDBLOCK;
+	}
+
+	/* Mark it as dying. */
+	sref->mod->state = MODULE_STATE_GOING;
+	return 0;
+}
+
+static int try_stop_module(struct module *mod, int flags, int *forced)
+{
+	struct stopref sref = { mod, flags, forced };
+
+	return stop_machine_run(__try_stop_module, &sref, NR_CPUS);
+}
+
+unsigned int module_refcount(struct module *mod)
+{
+	unsigned int i, total = 0;
+
+	for (i = 0; i < NR_CPUS; i++)
+		total += local_read(&mod->ref[i].count);
+	return total;
+}
+EXPORT_SYMBOL(module_refcount);
+
+/* This exists whether we can unload or not */
+static void free_module(struct module *mod);
+
+static void wait_for_zero_refcount(struct module *mod)
+{
+	/* Since we might sleep for some time, drop the semaphore first */
+	up(&module_mutex);
+	for (;;) {
+		DEBUGP("Looking at refcount...\n");
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		if (module_refcount(mod) == 0)
+			break;
+		schedule();
+	}
+	current->state = TASK_RUNNING;
+	down(&module_mutex);
+}
+
+asmlinkage long
+sys_delete_module(const char __user *name_user, unsigned int flags)
+{
+	struct module *mod;
+	char name[MODULE_NAME_LEN];
+	int ret, forced = 0;
+
+	if (!capable(CAP_SYS_MODULE))
+		return -EPERM;
+
+	if (strncpy_from_user(name, name_user, MODULE_NAME_LEN-1) < 0)
+		return -EFAULT;
+	name[MODULE_NAME_LEN-1] = '\0';
+
+	if (down_interruptible(&module_mutex) != 0)
+		return -EINTR;
+
+	mod = find_module(name);
+	if (!mod) {
+		ret = -ENOENT;
+		goto out;
+	}
+
+	if (!list_empty(&mod->modules_which_use_me)) {
+		/* Other modules depend on us: get rid of them first. */
+		ret = -EWOULDBLOCK;
+		goto out;
+	}
+
+	/* Doing init or already dying? */
+	if (mod->state != MODULE_STATE_LIVE) {
+		/* FIXME: if (force), slam module count and wake up
+                   waiter --RR */
+		DEBUGP("%s already dying\n", mod->name);
+		ret = -EBUSY;
+		goto out;
+	}
+
+	/* If it has an init func, it must have an exit func to unload */
+	if ((mod->init != NULL && mod->exit == NULL)
+	    || mod->unsafe) {
+		forced = try_force(flags);
+		if (!forced) {
+			/* This module can't be removed */
+			ret = -EBUSY;
+			goto out;
+		}
+	}
+
+	/* Set this up before setting mod->state */
+	mod->waiter = current;
+
+	/* Stop the machine so refcounts can't move and disable module. */
+	ret = try_stop_module(mod, flags, &forced);
+	if (ret != 0)
+		goto out;
+
+	/* Never wait if forced. */
+	if (!forced && module_refcount(mod) != 0)
+		wait_for_zero_refcount(mod);
+
+	/* Final destruction now noone is using it. */
+	if (mod->exit != NULL) {
+		up(&module_mutex);
+		mod->exit();
+		down(&module_mutex);
+	}
+	free_module(mod);
+
+ out:
+	up(&module_mutex);
+	return ret;
+}
+
+static void print_unload_info(struct seq_file *m, struct module *mod)
+{
+	struct module_use *use;
+	int printed_something = 0;
+
+	seq_printf(m, " %u ", module_refcount(mod));
+
+	/* Always include a trailing , so userspace can differentiate
+           between this and the old multi-field proc format. */
+	list_for_each_entry(use, &mod->modules_which_use_me, list) {
+		printed_something = 1;
+		seq_printf(m, "%s,", use->module_which_uses->name);
+	}
+
+	if (mod->unsafe) {
+		printed_something = 1;
+		seq_printf(m, "[unsafe],");
+	}
+
+	if (mod->init != NULL && mod->exit == NULL) {
+		printed_something = 1;
+		seq_printf(m, "[permanent],");
+	}
+
+	if (!printed_something)
+		seq_printf(m, "-");
+}
+
+void __symbol_put(const char *symbol)
+{
+	struct module *owner;
+	unsigned long flags;
+	const unsigned long *crc;
+
+	spin_lock_irqsave(&modlist_lock, flags);
+	if (!__find_symbol(symbol, &owner, &crc, 1))
+		BUG();
+	module_put(owner);
+	spin_unlock_irqrestore(&modlist_lock, flags);
+}
+EXPORT_SYMBOL(__symbol_put);
+
+void symbol_put_addr(void *addr)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&modlist_lock, flags);
+	if (!kernel_text_address((unsigned long)addr))
+		BUG();
+
+	module_put(module_text_address((unsigned long)addr));
+	spin_unlock_irqrestore(&modlist_lock, flags);
+}
+EXPORT_SYMBOL_GPL(symbol_put_addr);
+
+static ssize_t show_refcnt(struct module_attribute *mattr,
+			   struct module *mod, char *buffer)
+{
+	/* sysfs holds a reference */
+	return sprintf(buffer, "%u\n", module_refcount(mod)-1);
+}
+
+static struct module_attribute refcnt = {
+	.attr = { .name = "refcnt", .mode = 0444, .owner = THIS_MODULE },
+	.show = show_refcnt,
+};
+
+#else /* !CONFIG_MODULE_UNLOAD */
+static void print_unload_info(struct seq_file *m, struct module *mod)
+{
+	/* We don't know the usage count, or what modules are using. */
+	seq_printf(m, " - -");
+}
+
+static inline void module_unload_free(struct module *mod)
+{
+}
+
+static inline int use_module(struct module *a, struct module *b)
+{
+	return strong_try_module_get(b);
+}
+
+static inline void module_unload_init(struct module *mod)
+{
+}
+#endif /* CONFIG_MODULE_UNLOAD */
+
+#ifdef CONFIG_OBSOLETE_MODPARM
+/* Bounds checking done below */
+static int obsparm_copy_string(const char *val, struct kernel_param *kp)
+{
+	strcpy(kp->arg, val);
+	return 0;
+}
+
+int set_obsolete(const char *val, struct kernel_param *kp)
+{
+	unsigned int min, max;
+	unsigned int size, maxsize;
+	int dummy;
+	char *endp;
+	const char *p;
+	struct obsolete_modparm *obsparm = kp->arg;
+
+	if (!val) {
+		printk(KERN_ERR "Parameter %s needs an argument\n", kp->name);
+		return -EINVAL;
+	}
+
+	/* type is: [min[-max]]{b,h,i,l,s} */
+	p = obsparm->type;
+	min = simple_strtol(p, &endp, 10);
+	if (endp == obsparm->type)
+		min = max = 1;
+	else if (*endp == '-') {
+		p = endp+1;
+		max = simple_strtol(p, &endp, 10);
+	} else
+		max = min;
+	switch (*endp) {
+	case 'b':
+		return param_array(kp->name, val, min, max, obsparm->addr,
+				   1, param_set_byte, &dummy);
+	case 'h':
+		return param_array(kp->name, val, min, max, obsparm->addr,
+				   sizeof(short), param_set_short, &dummy);
+	case 'i':
+		return param_array(kp->name, val, min, max, obsparm->addr,
+				   sizeof(int), param_set_int, &dummy);
+	case 'l':
+		return param_array(kp->name, val, min, max, obsparm->addr,
+				   sizeof(long), param_set_long, &dummy);
+	case 's':
+		return param_array(kp->name, val, min, max, obsparm->addr,
+				   sizeof(char *), param_set_charp, &dummy);
+
+	case 'c':
+		/* Undocumented: 1-5c50 means 1-5 strings of up to 49 chars,
+		   and the decl is "char xxx[5][50];" */
+		p = endp+1;
+		maxsize = simple_strtol(p, &endp, 10);
+		/* We check lengths here (yes, this is a hack). */
+		p = val;
+		while (p[size = strcspn(p, ",")]) {
+			if (size >= maxsize) 
+				goto oversize;
+			p += size+1;
+		}
+		if (size >= maxsize) 
+			goto oversize;
+		return param_array(kp->name, val, min, max, obsparm->addr,
+				   maxsize, obsparm_copy_string, &dummy);
+	}
+	printk(KERN_ERR "Unknown obsolete parameter type %s\n", obsparm->type);
+	return -EINVAL;
+ oversize:
+	printk(KERN_ERR
+	       "Parameter %s doesn't fit in %u chars.\n", kp->name, maxsize);
+	return -EINVAL;
+}
+
+static int obsolete_params(const char *name,
+			   char *args,
+			   struct obsolete_modparm obsparm[],
+			   unsigned int num,
+			   Elf_Shdr *sechdrs,
+			   unsigned int symindex,
+			   const char *strtab)
+{
+	struct kernel_param *kp;
+	unsigned int i;
+	int ret;
+
+	kp = kmalloc(sizeof(kp[0]) * num, GFP_KERNEL);
+	if (!kp)
+		return -ENOMEM;
+
+	for (i = 0; i < num; i++) {
+		char sym_name[128 + sizeof(MODULE_SYMBOL_PREFIX)];
+
+		snprintf(sym_name, sizeof(sym_name), "%s%s",
+			 MODULE_SYMBOL_PREFIX, obsparm[i].name);
+
+		kp[i].name = obsparm[i].name;
+		kp[i].perm = 000;
+		kp[i].set = set_obsolete;
+		kp[i].get = NULL;
+		obsparm[i].addr
+			= (void *)find_local_symbol(sechdrs, symindex, strtab,
+						    sym_name);
+		if (!obsparm[i].addr) {
+			printk("%s: falsely claims to have parameter %s\n",
+			       name, obsparm[i].name);
+			ret = -EINVAL;
+			goto out;
+		}
+		kp[i].arg = &obsparm[i];
+	}
+
+	ret = parse_args(name, args, kp, num, NULL);
+ out:
+	kfree(kp);
+	return ret;
+}
+#else
+static int obsolete_params(const char *name,
+			   char *args,
+			   struct obsolete_modparm obsparm[],
+			   unsigned int num,
+			   Elf_Shdr *sechdrs,
+			   unsigned int symindex,
+			   const char *strtab)
+{
+	if (num != 0)
+		printk(KERN_WARNING "%s: Ignoring obsolete parameters\n",
+		       name);
+	return 0;
+}
+#endif /* CONFIG_OBSOLETE_MODPARM */
+
+static const char vermagic[] = VERMAGIC_STRING;
+
+#ifdef CONFIG_MODVERSIONS
+static int check_version(Elf_Shdr *sechdrs,
+			 unsigned int versindex,
+			 const char *symname,
+			 struct module *mod, 
+			 const unsigned long *crc)
+{
+	unsigned int i, num_versions;
+	struct modversion_info *versions;
+
+	/* Exporting module didn't supply crcs?  OK, we're already tainted. */
+	if (!crc)
+		return 1;
+
+	versions = (void *) sechdrs[versindex].sh_addr;
+	num_versions = sechdrs[versindex].sh_size
+		/ sizeof(struct modversion_info);
+
+	for (i = 0; i < num_versions; i++) {
+		if (strcmp(versions[i].name, symname) != 0)
+			continue;
+
+		if (versions[i].crc == *crc)
+			return 1;
+		printk("%s: disagrees about version of symbol %s\n",
+		       mod->name, symname);
+		DEBUGP("Found checksum %lX vs module %lX\n",
+		       *crc, versions[i].crc);
+		return 0;
+	}
+	/* Not in module's version table.  OK, but that taints the kernel. */
+	if (!(tainted & TAINT_FORCED_MODULE)) {
+		printk("%s: no version for \"%s\" found: kernel tainted.\n",
+		       mod->name, symname);
+		tainted |= TAINT_FORCED_MODULE;
+	}
+	return 1;
+}
+
+static inline int check_modstruct_version(Elf_Shdr *sechdrs,
+					  unsigned int versindex,
+					  struct module *mod)
+{
+	const unsigned long *crc;
+	struct module *owner;
+
+	if (!__find_symbol("struct_module", &owner, &crc, 1))
+		BUG();
+	return check_version(sechdrs, versindex, "struct_module", mod,
+			     crc);
+}
+
+/* First part is kernel version, which we ignore. */
+static inline int same_magic(const char *amagic, const char *bmagic)
+{
+	amagic += strcspn(amagic, " ");
+	bmagic += strcspn(bmagic, " ");
+	return strcmp(amagic, bmagic) == 0;
+}
+#else
+static inline int check_version(Elf_Shdr *sechdrs,
+				unsigned int versindex,
+				const char *symname,
+				struct module *mod, 
+				const unsigned long *crc)
+{
+	return 1;
+}
+
+static inline int check_modstruct_version(Elf_Shdr *sechdrs,
+					  unsigned int versindex,
+					  struct module *mod)
+{
+	return 1;
+}
+
+static inline int same_magic(const char *amagic, const char *bmagic)
+{
+	return strcmp(amagic, bmagic) == 0;
+}
+#endif /* CONFIG_MODVERSIONS */
+
+/* Resolve a symbol for this module.  I.e. if we find one, record usage.
+   Must be holding module_mutex. */
+static unsigned long resolve_symbol(Elf_Shdr *sechdrs,
+				    unsigned int versindex,
+				    const char *name,
+				    struct module *mod)
+{
+	struct module *owner;
+	unsigned long ret;
+	const unsigned long *crc;
+
+	spin_lock_irq(&modlist_lock);
+	ret = __find_symbol(name, &owner, &crc, mod->license_gplok);
+	if (ret) {
+		/* use_module can fail due to OOM, or module unloading */
+		if (!check_version(sechdrs, versindex, name, mod, crc) ||
+		    !use_module(mod, owner))
+			ret = 0;
+	}
+	spin_unlock_irq(&modlist_lock);
+	return ret;
+}
+
+
+/*
+ * /sys/module/foo/sections stuff
+ * J. Corbet <corbet@lwn.net>
+ */
+#ifdef CONFIG_KALLSYMS
+static ssize_t module_sect_show(struct module_attribute *mattr,
+				struct module *mod, char *buf)
+{
+	struct module_sect_attr *sattr =
+		container_of(mattr, struct module_sect_attr, mattr);
+	return sprintf(buf, "0x%lx\n", sattr->address);
+}
+
+static void add_sect_attrs(struct module *mod, unsigned int nsect,
+		char *secstrings, Elf_Shdr *sechdrs)
+{
+	unsigned int nloaded = 0, i, size[2];
+	struct module_sect_attrs *sect_attrs;
+	struct module_sect_attr *sattr;
+	struct attribute **gattr;
+	
+	/* Count loaded sections and allocate structures */
+	for (i = 0; i < nsect; i++)
+		if (sechdrs[i].sh_flags & SHF_ALLOC)
+			nloaded++;
+	size[0] = ALIGN(sizeof(*sect_attrs)
+			+ nloaded * sizeof(sect_attrs->attrs[0]),
+			sizeof(sect_attrs->grp.attrs[0]));
+	size[1] = (nloaded + 1) * sizeof(sect_attrs->grp.attrs[0]);
+	if (! (sect_attrs = kmalloc(size[0] + size[1], GFP_KERNEL)))
+		return;
+
+	/* Setup section attributes. */
+	sect_attrs->grp.name = "sections";
+	sect_attrs->grp.attrs = (void *)sect_attrs + size[0];
+
+	sattr = &sect_attrs->attrs[0];
+	gattr = &sect_attrs->grp.attrs[0];
+	for (i = 0; i < nsect; i++) {
+		if (! (sechdrs[i].sh_flags & SHF_ALLOC))
+			continue;
+		sattr->address = sechdrs[i].sh_addr;
+		strlcpy(sattr->name, secstrings + sechdrs[i].sh_name,
+			MODULE_SECT_NAME_LEN);
+		sattr->mattr.show = module_sect_show;
+		sattr->mattr.store = NULL;
+		sattr->mattr.attr.name = sattr->name;
+		sattr->mattr.attr.owner = mod;
+		sattr->mattr.attr.mode = S_IRUGO;
+		*(gattr++) = &(sattr++)->mattr.attr;
+	}
+	*gattr = NULL;
+
+	if (sysfs_create_group(&mod->mkobj.kobj, &sect_attrs->grp))
+		goto out;
+
+	mod->sect_attrs = sect_attrs;
+	return;
+  out:
+	kfree(sect_attrs);
+}
+
+static void remove_sect_attrs(struct module *mod)
+{
+	if (mod->sect_attrs) {
+		sysfs_remove_group(&mod->mkobj.kobj,
+				   &mod->sect_attrs->grp);
+		/* We are positive that no one is using any sect attrs
+		 * at this point.  Deallocate immediately. */
+		kfree(mod->sect_attrs);
+		mod->sect_attrs = NULL;
+	}
+}
+
+
+#else
+static inline void add_sect_attrs(struct module *mod, unsigned int nsect,
+		char *sectstrings, Elf_Shdr *sechdrs)
+{
+}
+
+static inline void remove_sect_attrs(struct module *mod)
+{
+}
+#endif /* CONFIG_KALLSYMS */
+
+
+#ifdef CONFIG_MODULE_UNLOAD
+static inline int module_add_refcnt_attr(struct module *mod)
+{
+	return sysfs_create_file(&mod->mkobj.kobj, &refcnt.attr);
+}
+static void module_remove_refcnt_attr(struct module *mod)
+{
+	return sysfs_remove_file(&mod->mkobj.kobj, &refcnt.attr);
+}
+#else
+static inline int module_add_refcnt_attr(struct module *mod)
+{
+	return 0;
+}
+static void module_remove_refcnt_attr(struct module *mod)
+{
+}
+#endif
+
+
+static int mod_sysfs_setup(struct module *mod,
+			   struct kernel_param *kparam,
+			   unsigned int num_params)
+{
+	int err;
+
+	memset(&mod->mkobj.kobj, 0, sizeof(mod->mkobj.kobj));
+	err = kobject_set_name(&mod->mkobj.kobj, "%s", mod->name);
+	if (err)
+		goto out;
+	kobj_set_kset_s(&mod->mkobj, module_subsys);
+	mod->mkobj.mod = mod;
+	err = kobject_register(&mod->mkobj.kobj);
+	if (err)
+		goto out;
+
+	err = module_add_refcnt_attr(mod);
+	if (err)
+		goto out_unreg;
+
+	err = module_param_sysfs_setup(mod, kparam, num_params);
+	if (err)
+		goto out_unreg;
+
+	return 0;
+
+out_unreg:
+	kobject_unregister(&mod->mkobj.kobj);
+out:
+	return err;
+}
+
+static void mod_kobject_remove(struct module *mod)
+{
+	module_remove_refcnt_attr(mod);
+	module_param_sysfs_remove(mod);
+
+	kobject_unregister(&mod->mkobj.kobj);
+}
+
+/* Free a module, remove from lists, etc (must hold module mutex). */
+static void free_module(struct module *mod)
+{
+	/* Delete from various lists */
+	spin_lock_irq(&modlist_lock);
+	list_del(&mod->list);
+	spin_unlock_irq(&modlist_lock);
+
+	remove_sect_attrs(mod);
+	mod_kobject_remove(mod);
+
+	/* Arch-specific cleanup. */
+	module_arch_cleanup(mod);
+
+	/* Module unload stuff */
+	module_unload_free(mod);
+
+	/* This may be NULL, but that's OK */
+	module_free(mod, mod->module_init);
+	kfree(mod->args);
+	if (mod->percpu)
+		percpu_modfree(mod->percpu);
+
+	/* Finally, free the core (containing the module structure) */
+	module_free(mod, mod->module_core);
+}
+
+void *__symbol_get(const char *symbol)
+{
+	struct module *owner;
+	unsigned long value, flags;
+	const unsigned long *crc;
+
+	spin_lock_irqsave(&modlist_lock, flags);
+	value = __find_symbol(symbol, &owner, &crc, 1);
+	if (value && !strong_try_module_get(owner))
+		value = 0;
+	spin_unlock_irqrestore(&modlist_lock, flags);
+
+	return (void *)value;
+}
+EXPORT_SYMBOL_GPL(__symbol_get);
+
+/* Change all symbols so that sh_value encodes the pointer directly. */
+static int simplify_symbols(Elf_Shdr *sechdrs,
+			    unsigned int symindex,
+			    const char *strtab,
+			    unsigned int versindex,
+			    unsigned int pcpuindex,
+			    struct module *mod)
+{
+	Elf_Sym *sym = (void *)sechdrs[symindex].sh_addr;
+	unsigned long secbase;
+	unsigned int i, n = sechdrs[symindex].sh_size / sizeof(Elf_Sym);
+	int ret = 0;
+
+	for (i = 1; i < n; i++) {
+		switch (sym[i].st_shndx) {
+		case SHN_COMMON:
+			/* We compiled with -fno-common.  These are not
+			   supposed to happen.  */
+			DEBUGP("Common symbol: %s\n", strtab + sym[i].st_name);
+			printk("%s: please compile with -fno-common\n",
+			       mod->name);
+			ret = -ENOEXEC;
+			break;
+
+		case SHN_ABS:
+			/* Don't need to do anything */
+			DEBUGP("Absolute symbol: 0x%08lx\n",
+			       (long)sym[i].st_value);
+			break;
+
+		case SHN_UNDEF:
+			sym[i].st_value
+			  = resolve_symbol(sechdrs, versindex,
+					   strtab + sym[i].st_name, mod);
+
+			/* Ok if resolved.  */
+			if (sym[i].st_value != 0)
+				break;
+			/* Ok if weak.  */
+			if (ELF_ST_BIND(sym[i].st_info) == STB_WEAK)
+				break;
+
+			printk(KERN_WARNING "%s: Unknown symbol %s\n",
+			       mod->name, strtab + sym[i].st_name);
+			ret = -ENOENT;
+			break;
+
+		default:
+			/* Divert to percpu allocation if a percpu var. */
+			if (sym[i].st_shndx == pcpuindex)
+				secbase = (unsigned long)mod->percpu;
+			else
+				secbase = sechdrs[sym[i].st_shndx].sh_addr;
+			sym[i].st_value += secbase;
+			break;
+		}
+	}
+
+	return ret;
+}
+
+/* Update size with this section: return offset. */
+static long get_offset(unsigned long *size, Elf_Shdr *sechdr)
+{
+	long ret;
+
+	ret = ALIGN(*size, sechdr->sh_addralign ?: 1);
+	*size = ret + sechdr->sh_size;
+	return ret;
+}
+
+/* Lay out the SHF_ALLOC sections in a way not dissimilar to how ld
+   might -- code, read-only data, read-write data, small data.  Tally
+   sizes, and place the offsets into sh_entsize fields: high bit means it
+   belongs in init. */
+static void layout_sections(struct module *mod,
+			    const Elf_Ehdr *hdr,
+			    Elf_Shdr *sechdrs,
+			    const char *secstrings)
+{
+	static unsigned long const masks[][2] = {
+		/* NOTE: all executable code must be the first section
+		 * in this array; otherwise modify the text_size
+		 * finder in the two loops below */
+		{ SHF_EXECINSTR | SHF_ALLOC, ARCH_SHF_SMALL },
+		{ SHF_ALLOC, SHF_WRITE | ARCH_SHF_SMALL },
+		{ SHF_WRITE | SHF_ALLOC, ARCH_SHF_SMALL },
+		{ ARCH_SHF_SMALL | SHF_ALLOC, 0 }
+	};
+	unsigned int m, i;
+
+	for (i = 0; i < hdr->e_shnum; i++)
+		sechdrs[i].sh_entsize = ~0UL;
+
+	DEBUGP("Core section allocation order:\n");
+	for (m = 0; m < ARRAY_SIZE(masks); ++m) {
+		for (i = 0; i < hdr->e_shnum; ++i) {
+			Elf_Shdr *s = &sechdrs[i];
+
+			if ((s->sh_flags & masks[m][0]) != masks[m][0]
+			    || (s->sh_flags & masks[m][1])
+			    || s->sh_entsize != ~0UL
+			    || strncmp(secstrings + s->sh_name,
+				       ".init", 5) == 0)
+				continue;
+			s->sh_entsize = get_offset(&mod->core_size, s);
+			DEBUGP("\t%s\n", secstrings + s->sh_name);
+		}
+		if (m == 0)
+			mod->core_text_size = mod->core_size;
+	}
+
+	DEBUGP("Init section allocation order:\n");
+	for (m = 0; m < ARRAY_SIZE(masks); ++m) {
+		for (i = 0; i < hdr->e_shnum; ++i) {
+			Elf_Shdr *s = &sechdrs[i];
+
+			if ((s->sh_flags & masks[m][0]) != masks[m][0]
+			    || (s->sh_flags & masks[m][1])
+			    || s->sh_entsize != ~0UL
+			    || strncmp(secstrings + s->sh_name,
+				       ".init", 5) != 0)
+				continue;
+			s->sh_entsize = (get_offset(&mod->init_size, s)
+					 | INIT_OFFSET_MASK);
+			DEBUGP("\t%s\n", secstrings + s->sh_name);
+		}
+		if (m == 0)
+			mod->init_text_size = mod->init_size;
+	}
+}
+
+static inline int license_is_gpl_compatible(const char *license)
+{
+	return (strcmp(license, "GPL") == 0
+		|| strcmp(license, "GPL v2") == 0
+		|| strcmp(license, "GPL and additional rights") == 0
+		|| strcmp(license, "Dual BSD/GPL") == 0
+		|| strcmp(license, "Dual MPL/GPL") == 0);
+}
+
+static void set_license(struct module *mod, const char *license)
+{
+	if (!license)
+		license = "unspecified";
+
+	mod->license_gplok = license_is_gpl_compatible(license);
+	if (!mod->license_gplok && !(tainted & TAINT_PROPRIETARY_MODULE)) {
+		printk(KERN_WARNING "%s: module license '%s' taints kernel.\n",
+		       mod->name, license);
+		tainted |= TAINT_PROPRIETARY_MODULE;
+	}
+}
+
+/* Parse tag=value strings from .modinfo section */
+static char *next_string(char *string, unsigned long *secsize)
+{
+	/* Skip non-zero chars */
+	while (string[0]) {
+		string++;
+		if ((*secsize)-- <= 1)
+			return NULL;
+	}
+
+	/* Skip any zero padding. */
+	while (!string[0]) {
+		string++;
+		if ((*secsize)-- <= 1)
+			return NULL;
+	}
+	return string;
+}
+
+static char *get_modinfo(Elf_Shdr *sechdrs,
+			 unsigned int info,
+			 const char *tag)
+{
+	char *p;
+	unsigned int taglen = strlen(tag);
+	unsigned long size = sechdrs[info].sh_size;
+
+	for (p = (char *)sechdrs[info].sh_addr; p; p = next_string(p, &size)) {
+		if (strncmp(p, tag, taglen) == 0 && p[taglen] == '=')
+			return p + taglen + 1;
+	}
+	return NULL;
+}
+
+#ifdef CONFIG_KALLSYMS
+int is_exported(const char *name, const struct module *mod)
+{
+	unsigned int i;
+
+	if (!mod) {
+		for (i = 0; __start___ksymtab+i < __stop___ksymtab; i++)
+			if (strcmp(__start___ksymtab[i].name, name) == 0)
+				return 1;
+		return 0;
+	}
+	for (i = 0; i < mod->num_syms; i++)
+		if (strcmp(mod->syms[i].name, name) == 0)
+			return 1;
+	return 0;
+}
+
+/* As per nm */
+static char elf_type(const Elf_Sym *sym,
+		     Elf_Shdr *sechdrs,
+		     const char *secstrings,
+		     struct module *mod)
+{
+	if (ELF_ST_BIND(sym->st_info) == STB_WEAK) {
+		if (ELF_ST_TYPE(sym->st_info) == STT_OBJECT)
+			return 'v';
+		else
+			return 'w';
+	}
+	if (sym->st_shndx == SHN_UNDEF)
+		return 'U';
+	if (sym->st_shndx == SHN_ABS)
+		return 'a';
+	if (sym->st_shndx >= SHN_LORESERVE)
+		return '?';
+	if (sechdrs[sym->st_shndx].sh_flags & SHF_EXECINSTR)
+		return 't';
+	if (sechdrs[sym->st_shndx].sh_flags & SHF_ALLOC
+	    && sechdrs[sym->st_shndx].sh_type != SHT_NOBITS) {
+		if (!(sechdrs[sym->st_shndx].sh_flags & SHF_WRITE))
+			return 'r';
+		else if (sechdrs[sym->st_shndx].sh_flags & ARCH_SHF_SMALL)
+			return 'g';
+		else
+			return 'd';
+	}
+	if (sechdrs[sym->st_shndx].sh_type == SHT_NOBITS) {
+		if (sechdrs[sym->st_shndx].sh_flags & ARCH_SHF_SMALL)
+			return 's';
+		else
+			return 'b';
+	}
+	if (strncmp(secstrings + sechdrs[sym->st_shndx].sh_name,
+		    ".debug", strlen(".debug")) == 0)
+		return 'n';
+	return '?';
+}
+
+static void add_kallsyms(struct module *mod,
+			 Elf_Shdr *sechdrs,
+			 unsigned int symindex,
+			 unsigned int strindex,
+			 const char *secstrings)
+{
+	unsigned int i;
+
+	mod->symtab = (void *)sechdrs[symindex].sh_addr;
+	mod->num_symtab = sechdrs[symindex].sh_size / sizeof(Elf_Sym);
+	mod->strtab = (void *)sechdrs[strindex].sh_addr;
+
+	/* Set types up while we still have access to sections. */
+	for (i = 0; i < mod->num_symtab; i++)
+		mod->symtab[i].st_info
+			= elf_type(&mod->symtab[i], sechdrs, secstrings, mod);
+}
+#else
+static inline void add_kallsyms(struct module *mod,
+				Elf_Shdr *sechdrs,
+				unsigned int symindex,
+				unsigned int strindex,
+				const char *secstrings)
+{
+}
+#endif /* CONFIG_KALLSYMS */
+
+/* Allocate and load the module: note that size of section 0 is always
+   zero, and we rely on this for optional sections. */
+static struct module *load_module(void __user *umod,
+				  unsigned long len,
+				  const char __user *uargs)
+{
+	Elf_Ehdr *hdr;
+	Elf_Shdr *sechdrs;
+	char *secstrings, *args, *modmagic, *strtab = NULL;
+	unsigned int i, symindex = 0, strindex = 0, setupindex, exindex,
+		exportindex, modindex, obsparmindex, infoindex, gplindex,
+		crcindex, gplcrcindex, versindex, pcpuindex;
+	long arglen;
+	struct module *mod;
+	long err = 0;
+	void *percpu = NULL, *ptr = NULL; /* Stops spurious gcc warning */
+	struct exception_table_entry *extable;
+
+	DEBUGP("load_module: umod=%p, len=%lu, uargs=%p\n",
+	       umod, len, uargs);
+	if (len < sizeof(*hdr))
+		return ERR_PTR(-ENOEXEC);
+
+	/* Suck in entire file: we'll want most of it. */
+	/* vmalloc barfs on "unusual" numbers.  Check here */
+	if (len > 64 * 1024 * 1024 || (hdr = vmalloc(len)) == NULL)
+		return ERR_PTR(-ENOMEM);
+	if (copy_from_user(hdr, umod, len) != 0) {
+		err = -EFAULT;
+		goto free_hdr;
+	}
+
+	/* Sanity checks against insmoding binaries or wrong arch,
+           weird elf version */
+	if (memcmp(hdr->e_ident, ELFMAG, 4) != 0
+	    || hdr->e_type != ET_REL
+	    || !elf_check_arch(hdr)
+	    || hdr->e_shentsize != sizeof(*sechdrs)) {
+		err = -ENOEXEC;
+		goto free_hdr;
+	}
+
+	if (len < hdr->e_shoff + hdr->e_shnum * sizeof(Elf_Shdr))
+		goto truncated;
+
+	/* Convenience variables */
+	sechdrs = (void *)hdr + hdr->e_shoff;
+	secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
+	sechdrs[0].sh_addr = 0;
+
+	for (i = 1; i < hdr->e_shnum; i++) {
+		if (sechdrs[i].sh_type != SHT_NOBITS
+		    && len < sechdrs[i].sh_offset + sechdrs[i].sh_size)
+			goto truncated;
+
+		/* Mark all sections sh_addr with their address in the
+		   temporary image. */
+		sechdrs[i].sh_addr = (size_t)hdr + sechdrs[i].sh_offset;
+
+		/* Internal symbols and strings. */
+		if (sechdrs[i].sh_type == SHT_SYMTAB) {
+			symindex = i;
+			strindex = sechdrs[i].sh_link;
+			strtab = (char *)hdr + sechdrs[strindex].sh_offset;
+		}
+#ifndef CONFIG_MODULE_UNLOAD
+		/* Don't load .exit sections */
+		if (strncmp(secstrings+sechdrs[i].sh_name, ".exit", 5) == 0)
+			sechdrs[i].sh_flags &= ~(unsigned long)SHF_ALLOC;
+#endif
+	}
+
+	modindex = find_sec(hdr, sechdrs, secstrings,
+			    ".gnu.linkonce.this_module");
+	if (!modindex) {
+		printk(KERN_WARNING "No module found in object\n");
+		err = -ENOEXEC;
+		goto free_hdr;
+	}
+	mod = (void *)sechdrs[modindex].sh_addr;
+
+	if (symindex == 0) {
+		printk(KERN_WARNING "%s: module has no symbols (stripped?)\n",
+		       mod->name);
+		err = -ENOEXEC;
+		goto free_hdr;
+	}
+
+	/* Optional sections */
+	exportindex = find_sec(hdr, sechdrs, secstrings, "__ksymtab");
+	gplindex = find_sec(hdr, sechdrs, secstrings, "__ksymtab_gpl");
+	crcindex = find_sec(hdr, sechdrs, secstrings, "__kcrctab");
+	gplcrcindex = find_sec(hdr, sechdrs, secstrings, "__kcrctab_gpl");
+	setupindex = find_sec(hdr, sechdrs, secstrings, "__param");
+	exindex = find_sec(hdr, sechdrs, secstrings, "__ex_table");
+	obsparmindex = find_sec(hdr, sechdrs, secstrings, "__obsparm");
+	versindex = find_sec(hdr, sechdrs, secstrings, "__versions");
+	infoindex = find_sec(hdr, sechdrs, secstrings, ".modinfo");
+	pcpuindex = find_pcpusec(hdr, sechdrs, secstrings);
+
+	/* Don't keep modinfo section */
+	sechdrs[infoindex].sh_flags &= ~(unsigned long)SHF_ALLOC;
+#ifdef CONFIG_KALLSYMS
+	/* Keep symbol and string tables for decoding later. */
+	sechdrs[symindex].sh_flags |= SHF_ALLOC;
+	sechdrs[strindex].sh_flags |= SHF_ALLOC;
+#endif
+
+	/* Check module struct version now, before we try to use module. */
+	if (!check_modstruct_version(sechdrs, versindex, mod)) {
+		err = -ENOEXEC;
+		goto free_hdr;
+	}
+
+	modmagic = get_modinfo(sechdrs, infoindex, "vermagic");
+	/* This is allowed: modprobe --force will invalidate it. */
+	if (!modmagic) {
+		tainted |= TAINT_FORCED_MODULE;
+		printk(KERN_WARNING "%s: no version magic, tainting kernel.\n",
+		       mod->name);
+	} else if (!same_magic(modmagic, vermagic)) {
+		printk(KERN_ERR "%s: version magic '%s' should be '%s'\n",
+		       mod->name, modmagic, vermagic);
+		err = -ENOEXEC;
+		goto free_hdr;
+	}
+
+	/* Now copy in args */
+	arglen = strlen_user(uargs);
+	if (!arglen) {
+		err = -EFAULT;
+		goto free_hdr;
+	}
+	args = kmalloc(arglen, GFP_KERNEL);
+	if (!args) {
+		err = -ENOMEM;
+		goto free_hdr;
+	}
+	if (copy_from_user(args, uargs, arglen) != 0) {
+		err = -EFAULT;
+		goto free_mod;
+	}
+
+	if (find_module(mod->name)) {
+		err = -EEXIST;
+		goto free_mod;
+	}
+
+	mod->state = MODULE_STATE_COMING;
+
+	/* Allow arches to frob section contents and sizes.  */
+	err = module_frob_arch_sections(hdr, sechdrs, secstrings, mod);
+	if (err < 0)
+		goto free_mod;
+
+	if (pcpuindex) {
+		/* We have a special allocation for this section. */
+		percpu = percpu_modalloc(sechdrs[pcpuindex].sh_size,
+					 sechdrs[pcpuindex].sh_addralign);
+		if (!percpu) {
+			err = -ENOMEM;
+			goto free_mod;
+		}
+		sechdrs[pcpuindex].sh_flags &= ~(unsigned long)SHF_ALLOC;
+		mod->percpu = percpu;
+	}
+
+	/* Determine total sizes, and put offsets in sh_entsize.  For now
+	   this is done generically; there doesn't appear to be any
+	   special cases for the architectures. */
+	layout_sections(mod, hdr, sechdrs, secstrings);
+
+	/* Do the allocs. */
+	ptr = module_alloc(mod->core_size);
+	if (!ptr) {
+		err = -ENOMEM;
+		goto free_percpu;
+	}
+	memset(ptr, 0, mod->core_size);
+	mod->module_core = ptr;
+
+	ptr = module_alloc(mod->init_size);
+	if (!ptr && mod->init_size) {
+		err = -ENOMEM;
+		goto free_core;
+	}
+	memset(ptr, 0, mod->init_size);
+	mod->module_init = ptr;
+
+	/* Transfer each section which specifies SHF_ALLOC */
+	DEBUGP("final section addresses:\n");
+	for (i = 0; i < hdr->e_shnum; i++) {
+		void *dest;
+
+		if (!(sechdrs[i].sh_flags & SHF_ALLOC))
+			continue;
+
+		if (sechdrs[i].sh_entsize & INIT_OFFSET_MASK)
+			dest = mod->module_init
+				+ (sechdrs[i].sh_entsize & ~INIT_OFFSET_MASK);
+		else
+			dest = mod->module_core + sechdrs[i].sh_entsize;
+
+		if (sechdrs[i].sh_type != SHT_NOBITS)
+			memcpy(dest, (void *)sechdrs[i].sh_addr,
+			       sechdrs[i].sh_size);
+		/* Update sh_addr to point to copy in image. */
+		sechdrs[i].sh_addr = (unsigned long)dest;
+		DEBUGP("\t0x%lx %s\n", sechdrs[i].sh_addr, secstrings + sechdrs[i].sh_name);
+	}
+	/* Module has been moved. */
+	mod = (void *)sechdrs[modindex].sh_addr;
+
+	/* Now we've moved module, initialize linked lists, etc. */
+	module_unload_init(mod);
+
+	/* Set up license info based on the info section */
+	set_license(mod, get_modinfo(sechdrs, infoindex, "license"));
+
+	/* Fix up syms, so that st_value is a pointer to location. */
+	err = simplify_symbols(sechdrs, symindex, strtab, versindex, pcpuindex,
+			       mod);
+	if (err < 0)
+		goto cleanup;
+
+	/* Set up EXPORTed & EXPORT_GPLed symbols (section 0 is 0 length) */
+	mod->num_syms = sechdrs[exportindex].sh_size / sizeof(*mod->syms);
+	mod->syms = (void *)sechdrs[exportindex].sh_addr;
+	if (crcindex)
+		mod->crcs = (void *)sechdrs[crcindex].sh_addr;
+	mod->num_gpl_syms = sechdrs[gplindex].sh_size / sizeof(*mod->gpl_syms);
+	mod->gpl_syms = (void *)sechdrs[gplindex].sh_addr;
+	if (gplcrcindex)
+		mod->gpl_crcs = (void *)sechdrs[gplcrcindex].sh_addr;
+
+#ifdef CONFIG_MODVERSIONS
+	if ((mod->num_syms && !crcindex) || 
+	    (mod->num_gpl_syms && !gplcrcindex)) {
+		printk(KERN_WARNING "%s: No versions for exported symbols."
+		       " Tainting kernel.\n", mod->name);
+		tainted |= TAINT_FORCED_MODULE;
+	}
+#endif
+
+	/* Now do relocations. */
+	for (i = 1; i < hdr->e_shnum; i++) {
+		const char *strtab = (char *)sechdrs[strindex].sh_addr;
+		unsigned int info = sechdrs[i].sh_info;
+
+		/* Not a valid relocation section? */
+		if (info >= hdr->e_shnum)
+			continue;
+
+		/* Don't bother with non-allocated sections */
+		if (!(sechdrs[info].sh_flags & SHF_ALLOC))
+			continue;
+
+		if (sechdrs[i].sh_type == SHT_REL)
+			err = apply_relocate(sechdrs, strtab, symindex, i,mod);
+		else if (sechdrs[i].sh_type == SHT_RELA)
+			err = apply_relocate_add(sechdrs, strtab, symindex, i,
+						 mod);
+		if (err < 0)
+			goto cleanup;
+	}
+
+  	/* Set up and sort exception table */
+	mod->num_exentries = sechdrs[exindex].sh_size / sizeof(*mod->extable);
+	mod->extable = extable = (void *)sechdrs[exindex].sh_addr;
+	sort_extable(extable, extable + mod->num_exentries);
+
+	/* Finally, copy percpu area over. */
+	percpu_modcopy(mod->percpu, (void *)sechdrs[pcpuindex].sh_addr,
+		       sechdrs[pcpuindex].sh_size);
+
+	add_kallsyms(mod, sechdrs, symindex, strindex, secstrings);
+
+	err = module_finalize(hdr, sechdrs, mod);
+	if (err < 0)
+		goto cleanup;
+
+	mod->args = args;
+	if (obsparmindex) {
+		err = obsolete_params(mod->name, mod->args,
+				      (struct obsolete_modparm *)
+				      sechdrs[obsparmindex].sh_addr,
+				      sechdrs[obsparmindex].sh_size
+				      / sizeof(struct obsolete_modparm),
+				      sechdrs, symindex,
+				      (char *)sechdrs[strindex].sh_addr);
+		if (setupindex)
+			printk(KERN_WARNING "%s: Ignoring new-style "
+			       "parameters in presence of obsolete ones\n",
+			       mod->name);
+	} else {
+		/* Size of section 0 is 0, so this works well if no params */
+		err = parse_args(mod->name, mod->args,
+				 (struct kernel_param *)
+				 sechdrs[setupindex].sh_addr,
+				 sechdrs[setupindex].sh_size
+				 / sizeof(struct kernel_param),
+				 NULL);
+	}
+	if (err < 0)
+		goto arch_cleanup;
+
+	err = mod_sysfs_setup(mod, 
+			      (struct kernel_param *)
+			      sechdrs[setupindex].sh_addr,
+			      sechdrs[setupindex].sh_size
+			      / sizeof(struct kernel_param));
+	if (err < 0)
+		goto arch_cleanup;
+	add_sect_attrs(mod, hdr->e_shnum, secstrings, sechdrs);
+
+	/* Get rid of temporary copy */
+	vfree(hdr);
+
+	/* Done! */
+	return mod;
+
+ arch_cleanup:
+	module_arch_cleanup(mod);
+ cleanup:
+	module_unload_free(mod);
+	module_free(mod, mod->module_init);
+ free_core:
+	module_free(mod, mod->module_core);
+ free_percpu:
+	if (percpu)
+		percpu_modfree(percpu);
+ free_mod:
+	kfree(args);
+ free_hdr:
+	vfree(hdr);
+	if (err < 0) return ERR_PTR(err);
+	else return ptr;
+
+ truncated:
+	printk(KERN_ERR "Module len %lu truncated\n", len);
+	err = -ENOEXEC;
+	goto free_hdr;
+}
+
+/* This is where the real work happens */
+asmlinkage long
+sys_init_module(void __user *umod,
+		unsigned long len,
+		const char __user *uargs)
+{
+	struct module *mod;
+	int ret = 0;
+
+	/* Must have permission */
+	if (!capable(CAP_SYS_MODULE))
+		return -EPERM;
+
+	/* Only one module load at a time, please */
+	if (down_interruptible(&module_mutex) != 0)
+		return -EINTR;
+
+	/* Do all the hard work */
+	mod = load_module(umod, len, uargs);
+	if (IS_ERR(mod)) {
+		up(&module_mutex);
+		return PTR_ERR(mod);
+	}
+
+	/* Flush the instruction cache, since we've played with text */
+	if (mod->module_init)
+		flush_icache_range((unsigned long)mod->module_init,
+				   (unsigned long)mod->module_init
+				   + mod->init_size);
+	flush_icache_range((unsigned long)mod->module_core,
+			   (unsigned long)mod->module_core + mod->core_size);
+
+	/* Now sew it into the lists.  They won't access us, since
+           strong_try_module_get() will fail. */
+	spin_lock_irq(&modlist_lock);
+	list_add(&mod->list, &modules);
+	spin_unlock_irq(&modlist_lock);
+
+	/* Drop lock so they can recurse */
+	up(&module_mutex);
+
+	down(&notify_mutex);
+	notifier_call_chain(&module_notify_list, MODULE_STATE_COMING, mod);
+	up(&notify_mutex);
+
+	/* Start the module */
+	if (mod->init != NULL)
+		ret = mod->init();
+	if (ret < 0) {
+		/* Init routine failed: abort.  Try to protect us from
+                   buggy refcounters. */
+		mod->state = MODULE_STATE_GOING;
+		synchronize_kernel();
+		if (mod->unsafe)
+			printk(KERN_ERR "%s: module is now stuck!\n",
+			       mod->name);
+		else {
+			module_put(mod);
+			down(&module_mutex);
+			free_module(mod);
+			up(&module_mutex);
+		}
+		return ret;
+	}
+
+	/* Now it's a first class citizen! */
+	down(&module_mutex);
+	mod->state = MODULE_STATE_LIVE;
+	/* Drop initial reference. */
+	module_put(mod);
+	module_free(mod, mod->module_init);
+	mod->module_init = NULL;
+	mod->init_size = 0;
+	mod->init_text_size = 0;
+	up(&module_mutex);
+
+	return 0;
+}
+
+static inline int within(unsigned long addr, void *start, unsigned long size)
+{
+	return ((void *)addr >= start && (void *)addr < start + size);
+}
+
+#ifdef CONFIG_KALLSYMS
+/*
+ * This ignores the intensely annoying "mapping symbols" found
+ * in ARM ELF files: $a, $t and $d.
+ */
+static inline int is_arm_mapping_symbol(const char *str)
+{
+	return str[0] == '$' && strchr("atd", str[1]) 
+	       && (str[2] == '\0' || str[2] == '.');
+}
+
+static const char *get_ksymbol(struct module *mod,
+			       unsigned long addr,
+			       unsigned long *size,
+			       unsigned long *offset)
+{
+	unsigned int i, best = 0;
+	unsigned long nextval;
+
+	/* At worse, next value is at end of module */
+	if (within(addr, mod->module_init, mod->init_size))
+		nextval = (unsigned long)mod->module_init+mod->init_text_size;
+	else 
+		nextval = (unsigned long)mod->module_core+mod->core_text_size;
+
+	/* Scan for closest preceeding symbol, and next symbol. (ELF
+           starts real symbols at 1). */
+	for (i = 1; i < mod->num_symtab; i++) {
+		if (mod->symtab[i].st_shndx == SHN_UNDEF)
+			continue;
+
+		/* We ignore unnamed symbols: they're uninformative
+		 * and inserted at a whim. */
+		if (mod->symtab[i].st_value <= addr
+		    && mod->symtab[i].st_value > mod->symtab[best].st_value
+		    && *(mod->strtab + mod->symtab[i].st_name) != '\0'
+		    && !is_arm_mapping_symbol(mod->strtab + mod->symtab[i].st_name))
+			best = i;
+		if (mod->symtab[i].st_value > addr
+		    && mod->symtab[i].st_value < nextval
+		    && *(mod->strtab + mod->symtab[i].st_name) != '\0'
+		    && !is_arm_mapping_symbol(mod->strtab + mod->symtab[i].st_name))
+			nextval = mod->symtab[i].st_value;
+	}
+
+	if (!best)
+		return NULL;
+
+	*size = nextval - mod->symtab[best].st_value;
+	*offset = addr - mod->symtab[best].st_value;
+	return mod->strtab + mod->symtab[best].st_name;
+}
+
+/* For kallsyms to ask for address resolution.  NULL means not found.
+   We don't lock, as this is used for oops resolution and races are a
+   lesser concern. */
+const char *module_address_lookup(unsigned long addr,
+				  unsigned long *size,
+				  unsigned long *offset,
+				  char **modname)
+{
+	struct module *mod;
+
+	list_for_each_entry(mod, &modules, list) {
+		if (within(addr, mod->module_init, mod->init_size)
+		    || within(addr, mod->module_core, mod->core_size)) {
+			*modname = mod->name;
+			return get_ksymbol(mod, addr, size, offset);
+		}
+	}
+	return NULL;
+}
+
+struct module *module_get_kallsym(unsigned int symnum,
+				  unsigned long *value,
+				  char *type,
+				  char namebuf[128])
+{
+	struct module *mod;
+
+	down(&module_mutex);
+	list_for_each_entry(mod, &modules, list) {
+		if (symnum < mod->num_symtab) {
+			*value = mod->symtab[symnum].st_value;
+			*type = mod->symtab[symnum].st_info;
+			strncpy(namebuf,
+				mod->strtab + mod->symtab[symnum].st_name,
+				127);
+			up(&module_mutex);
+			return mod;
+		}
+		symnum -= mod->num_symtab;
+	}
+	up(&module_mutex);
+	return NULL;
+}
+
+static unsigned long mod_find_symname(struct module *mod, const char *name)
+{
+	unsigned int i;
+
+	for (i = 0; i < mod->num_symtab; i++)
+		if (strcmp(name, mod->strtab+mod->symtab[i].st_name) == 0)
+			return mod->symtab[i].st_value;
+	return 0;
+}
+
+/* Look for this name: can be of form module:name. */
+unsigned long module_kallsyms_lookup_name(const char *name)
+{
+	struct module *mod;
+	char *colon;
+	unsigned long ret = 0;
+
+	/* Don't lock: we're in enough trouble already. */
+	if ((colon = strchr(name, ':')) != NULL) {
+		*colon = '\0';
+		if ((mod = find_module(name)) != NULL)
+			ret = mod_find_symname(mod, colon+1);
+		*colon = ':';
+	} else {
+		list_for_each_entry(mod, &modules, list)
+			if ((ret = mod_find_symname(mod, name)) != 0)
+				break;
+	}
+	return ret;
+}
+#endif /* CONFIG_KALLSYMS */
+
+/* Called by the /proc file system to return a list of modules. */
+static void *m_start(struct seq_file *m, loff_t *pos)
+{
+	struct list_head *i;
+	loff_t n = 0;
+
+	down(&module_mutex);
+	list_for_each(i, &modules) {
+		if (n++ == *pos)
+			break;
+	}
+	if (i == &modules)
+		return NULL;
+	return i;
+}
+
+static void *m_next(struct seq_file *m, void *p, loff_t *pos)
+{
+	struct list_head *i = p;
+	(*pos)++;
+	if (i->next == &modules)
+		return NULL;
+	return i->next;
+}
+
+static void m_stop(struct seq_file *m, void *p)
+{
+	up(&module_mutex);
+}
+
+static int m_show(struct seq_file *m, void *p)
+{
+	struct module *mod = list_entry(p, struct module, list);
+	seq_printf(m, "%s %lu",
+		   mod->name, mod->init_size + mod->core_size);
+	print_unload_info(m, mod);
+
+	/* Informative for users. */
+	seq_printf(m, " %s",
+		   mod->state == MODULE_STATE_GOING ? "Unloading":
+		   mod->state == MODULE_STATE_COMING ? "Loading":
+		   "Live");
+	/* Used by oprofile and other similar tools. */
+	seq_printf(m, " 0x%p", mod->module_core);
+
+	seq_printf(m, "\n");
+	return 0;
+}
+
+/* Format: modulename size refcount deps address
+
+   Where refcount is a number or -, and deps is a comma-separated list
+   of depends or -.
+*/
+struct seq_operations modules_op = {
+	.start	= m_start,
+	.next	= m_next,
+	.stop	= m_stop,
+	.show	= m_show
+};
+
+/* Given an address, look for it in the module exception tables. */
+const struct exception_table_entry *search_module_extables(unsigned long addr)
+{
+	unsigned long flags;
+	const struct exception_table_entry *e = NULL;
+	struct module *mod;
+
+	spin_lock_irqsave(&modlist_lock, flags);
+	list_for_each_entry(mod, &modules, list) {
+		if (mod->num_exentries == 0)
+			continue;
+				
+		e = search_extable(mod->extable,
+				   mod->extable + mod->num_exentries - 1,
+				   addr);
+		if (e)
+			break;
+	}
+	spin_unlock_irqrestore(&modlist_lock, flags);
+
+	/* Now, if we found one, we are running inside it now, hence
+           we cannot unload the module, hence no refcnt needed. */
+	return e;
+}
+
+/* Is this a valid kernel address?  We don't grab the lock: we are oopsing. */
+struct module *__module_text_address(unsigned long addr)
+{
+	struct module *mod;
+
+	list_for_each_entry(mod, &modules, list)
+		if (within(addr, mod->module_init, mod->init_text_size)
+		    || within(addr, mod->module_core, mod->core_text_size))
+			return mod;
+	return NULL;
+}
+
+struct module *module_text_address(unsigned long addr)
+{
+	struct module *mod;
+	unsigned long flags;
+
+	spin_lock_irqsave(&modlist_lock, flags);
+	mod = __module_text_address(addr);
+	spin_unlock_irqrestore(&modlist_lock, flags);
+
+	return mod;
+}
+
+/* Don't grab lock, we're oopsing. */
+void print_modules(void)
+{
+	struct module *mod;
+
+	printk("Modules linked in:");
+	list_for_each_entry(mod, &modules, list)
+		printk(" %s", mod->name);
+	printk("\n");
+}
+
+void module_add_driver(struct module *mod, struct device_driver *drv)
+{
+	if (!mod || !drv)
+		return;
+
+	/* Don't check return code; this call is idempotent */
+	sysfs_create_link(&drv->kobj, &mod->mkobj.kobj, "module");
+}
+EXPORT_SYMBOL(module_add_driver);
+
+void module_remove_driver(struct device_driver *drv)
+{
+	if (!drv)
+		return;
+	sysfs_remove_link(&drv->kobj, "module");
+}
+EXPORT_SYMBOL(module_remove_driver);
+
+#ifdef CONFIG_MODVERSIONS
+/* Generate the signature for struct module here, too, for modversions. */
+void struct_module(struct module *mod) { return; }
+EXPORT_SYMBOL(struct_module);
+#endif
diff -Nrub linux-2.6.11.7-orig/kernel/sys.c linux-2.6.11.7-snare/kernel/sys.c
--- linux-2.6.11.7-orig/kernel/sys.c	2005-04-07 14:57:13.000000000 -0400
+++ linux-2.6.11.7-snare/kernel/sys.c	2005-04-22 11:19:47.202664552 -0400
@@ -15,6 +15,7 @@
 #include <linux/prctl.h>
 #include <linux/init.h>
 #include <linux/highuid.h>
+#include <linux/saudit.h>
 #include <linux/fs.h>
 #include <linux/workqueue.h>
 #include <linux/device.h>
@@ -370,16 +371,23 @@
 	char buffer[256];
 
 	/* We only trust the superuser with rebooting the system. */
-	if (!capable(CAP_SYS_BOOT))
+	if (!capable(CAP_SYS_BOOT)) {
+		saudit_reboot(magic1,magic2,cmd,arg,-EPERM);
 		return -EPERM;
+	}
 
 	/* For safety, we require "magic" arguments. */
 	if (magic1 != LINUX_REBOOT_MAGIC1 ||
 	    (magic2 != LINUX_REBOOT_MAGIC2 &&
 	                magic2 != LINUX_REBOOT_MAGIC2A &&
 			magic2 != LINUX_REBOOT_MAGIC2B &&
-	                magic2 != LINUX_REBOOT_MAGIC2C))
+	                magic2 != LINUX_REBOOT_MAGIC2C)) {
+		saudit_reboot(magic1,magic2,cmd,arg,-EINVAL);
 		return -EINVAL;
+	}
+
+	// Try and audit before we go down.
+	saudit_reboot(magic1,magic2,cmd,arg,0);
 
 	lock_kernel();
 	switch (cmd) {
@@ -507,9 +515,11 @@
 		    (current->egid==rgid) ||
 		    capable(CAP_SETGID))
 			new_rgid = rgid;
-		else
+		else {
+			saudit_setregid(rgid,egid,-EPERM);
 			return -EPERM;
 	}
+	}
 	if (egid != (gid_t) -1) {
 		if ((old_rgid == egid) ||
 		    (current->egid == egid) ||
@@ -517,6 +527,7 @@
 		    capable(CAP_SETGID))
 			new_egid = egid;
 		else {
+			saudit_setregid(rgid,egid,-EPERM);
 			return -EPERM;
 		}
 	}
@@ -532,6 +543,7 @@
 	current->egid = new_egid;
 	current->gid = new_rgid;
 	key_fsgid_changed(current);
+	saudit_setregid(rgid,egid,0);
 	return 0;
 }
 
@@ -551,6 +563,7 @@
 
 	if (capable(CAP_SETGID))
 	{
+		saudit_setgid(gid,0);
 		if(old_egid != gid)
 		{
 			current->mm->dumpable=0;
@@ -560,6 +573,7 @@
 	}
 	else if ((gid == current->gid) || (gid == current->sgid))
 	{
+		saudit_setgid(gid,0);
 		if(old_egid != gid)
 		{
 			current->mm->dumpable=0;
@@ -568,9 +582,13 @@
 		current->egid = current->fsgid = gid;
 	}
 	else
+	{
+		saudit_setgid(gid,-EPERM);
 		return -EPERM;
+	}
 
 	key_fsgid_changed(current);
+	saudit_setgid(gid,0);
 	return 0;
 }
   
@@ -621,8 +639,10 @@
 	int retval;
 
 	retval = security_task_setuid(ruid, euid, (uid_t)-1, LSM_SETID_RE);
-	if (retval)
+	if (retval) {
+		saudit_setreuid(ruid, euid, retval);
 		return retval;
+	}
 
 	new_ruid = old_ruid = current->uid;
 	new_euid = old_euid = current->euid;
@@ -632,21 +652,29 @@
 		new_ruid = ruid;
 		if ((old_ruid != ruid) &&
 		    (current->euid != ruid) &&
-		    !capable(CAP_SETUID))
+		    !capable(CAP_SETUID)) {
+			saudit_setreuid(ruid,euid,-EPERM);
 			return -EPERM;
 	}
+	}
 
 	if (euid != (uid_t) -1) {
 		new_euid = euid;
 		if ((old_ruid != euid) &&
 		    (current->euid != euid) &&
 		    (current->suid != euid) &&
-		    !capable(CAP_SETUID))
+		    !capable(CAP_SETUID)) {
+			saudit_setreuid(ruid,euid,-EPERM);
 			return -EPERM;
 	}
+	}
 
-	if (new_ruid != old_ruid && set_user(new_ruid, new_euid != old_euid) < 0)
+	if (new_ruid != old_ruid && set_user(new_ruid, new_euid != old_euid) < 0) {
+		saudit_setreuid(ruid,euid,-EAGAIN);
 		return -EAGAIN;
+	}
+
+	saudit_setreuid(ruid,euid,0);	// Before current->*id is changed
 
 	if (new_euid != old_euid)
 	{
@@ -692,11 +720,17 @@
 	new_suid = old_suid;
 	
 	if (capable(CAP_SETUID)) {
-		if (uid != old_ruid && set_user(uid, old_euid != uid) < 0)
+		if (uid != old_ruid && set_user(uid, old_euid != uid) < 0) {
+			saudit_setuid(uid,-EAGAIN);
 			return -EAGAIN;
+		}
 		new_suid = uid;
-	} else if ((uid != current->uid) && (uid != new_suid))
+	} else if ((uid != current->uid) && (uid != new_suid)) {
+		saudit_setuid(uid,-EPERM);
 		return -EPERM;
+	}
+	
+	saudit_setuid(uid,0);        // Before current->*id is changed
 
 	if (old_euid != uid)
 	{
@@ -729,19 +763,30 @@
 
 	if (!capable(CAP_SETUID)) {
 		if ((ruid != (uid_t) -1) && (ruid != current->uid) &&
-		    (ruid != current->euid) && (ruid != current->suid))
+		    (ruid != current->euid) && (ruid != current->suid)) {
+			saudit_setresuid(ruid,euid,suid,-EPERM);
 			return -EPERM;
+		}
 		if ((euid != (uid_t) -1) && (euid != current->uid) &&
-		    (euid != current->euid) && (euid != current->suid))
+		    (euid != current->euid) && (euid != current->suid)) {
+			saudit_setresuid(ruid,euid,suid,-EPERM);
 			return -EPERM;
+		}
 		if ((suid != (uid_t) -1) && (suid != current->uid) &&
-		    (suid != current->euid) && (suid != current->suid))
+		    (suid != current->euid) && (suid != current->suid)) {
+			saudit_setresuid(ruid,euid,suid,-EPERM);
 			return -EPERM;
 	}
+	}
 	if (ruid != (uid_t) -1) {
-		if (ruid != current->uid && set_user(ruid, euid != current->euid) < 0)
+		if (ruid != current->uid && set_user(ruid, euid != current->euid) < 0) {
+			saudit_setresuid(ruid,euid,suid,-EAGAIN);
 			return -EAGAIN;
 	}
+	}
+	
+	saudit_setresuid(ruid,euid,suid,0);        // Before current->uids change
+
 	if (euid != (uid_t) -1) {
 		if (euid != current->euid)
 		{
@@ -783,15 +828,24 @@
 
 	if (!capable(CAP_SETGID)) {
 		if ((rgid != (gid_t) -1) && (rgid != current->gid) &&
-		    (rgid != current->egid) && (rgid != current->sgid))
+		    (rgid != current->egid) && (rgid != current->sgid)) {
+			saudit_setresgid(rgid,egid,sgid,-EPERM);
 			return -EPERM;
+		}
 		if ((egid != (gid_t) -1) && (egid != current->gid) &&
-		    (egid != current->egid) && (egid != current->sgid))
+		    (egid != current->egid) && (egid != current->sgid)) {
+			saudit_setresgid(rgid,egid,sgid,-EPERM);
 			return -EPERM;
+		}
 		if ((sgid != (gid_t) -1) && (sgid != current->gid) &&
-		    (sgid != current->egid) && (sgid != current->sgid))
+		    (sgid != current->egid) && (sgid != current->sgid)) {
+			saudit_setresgid(rgid,egid,sgid,-EPERM);
 			return -EPERM;
 	}
+	}
+	
+	saudit_setresgid(rgid,egid,sgid,0);        // Before our current process UIDs change
+	
 	if (egid != (gid_t) -1) {
 		if (egid != current->egid)
 		{
diff -Nrub linux-2.6.11.7-orig/kernel/sys.c.orig linux-2.6.11.7-snare/kernel/sys.c.orig
--- linux-2.6.11.7-orig/kernel/sys.c.orig	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.11.7-snare/kernel/sys.c.orig	2005-04-07 14:57:13.000000000 -0400
@@ -0,0 +1,1709 @@
+/*
+ *  linux/kernel/sys.c
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/utsname.h>
+#include <linux/mman.h>
+#include <linux/smp_lock.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/prctl.h>
+#include <linux/init.h>
+#include <linux/highuid.h>
+#include <linux/fs.h>
+#include <linux/workqueue.h>
+#include <linux/device.h>
+#include <linux/key.h>
+#include <linux/times.h>
+#include <linux/security.h>
+#include <linux/dcookies.h>
+#include <linux/suspend.h>
+#include <linux/tty.h>
+
+#include <linux/compat.h>
+#include <linux/syscalls.h>
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/unistd.h>
+
+#ifndef SET_UNALIGN_CTL
+# define SET_UNALIGN_CTL(a,b)	(-EINVAL)
+#endif
+#ifndef GET_UNALIGN_CTL
+# define GET_UNALIGN_CTL(a,b)	(-EINVAL)
+#endif
+#ifndef SET_FPEMU_CTL
+# define SET_FPEMU_CTL(a,b)	(-EINVAL)
+#endif
+#ifndef GET_FPEMU_CTL
+# define GET_FPEMU_CTL(a,b)	(-EINVAL)
+#endif
+#ifndef SET_FPEXC_CTL
+# define SET_FPEXC_CTL(a,b)	(-EINVAL)
+#endif
+#ifndef GET_FPEXC_CTL
+# define GET_FPEXC_CTL(a,b)	(-EINVAL)
+#endif
+
+/*
+ * this is where the system-wide overflow UID and GID are defined, for
+ * architectures that now have 32-bit UID/GID but didn't in the past
+ */
+
+int overflowuid = DEFAULT_OVERFLOWUID;
+int overflowgid = DEFAULT_OVERFLOWGID;
+
+#ifdef CONFIG_UID16
+EXPORT_SYMBOL(overflowuid);
+EXPORT_SYMBOL(overflowgid);
+#endif
+
+/*
+ * the same as above, but for filesystems which can only store a 16-bit
+ * UID and GID. as such, this is needed on all architectures
+ */
+
+int fs_overflowuid = DEFAULT_FS_OVERFLOWUID;
+int fs_overflowgid = DEFAULT_FS_OVERFLOWUID;
+
+EXPORT_SYMBOL(fs_overflowuid);
+EXPORT_SYMBOL(fs_overflowgid);
+
+/*
+ * this indicates whether you can reboot with ctrl-alt-del: the default is yes
+ */
+
+int C_A_D = 1;
+int cad_pid = 1;
+
+/*
+ *	Notifier list for kernel code which wants to be called
+ *	at shutdown. This is used to stop any idling DMA operations
+ *	and the like. 
+ */
+
+static struct notifier_block *reboot_notifier_list;
+DEFINE_RWLOCK(notifier_lock);
+
+/**
+ *	notifier_chain_register	- Add notifier to a notifier chain
+ *	@list: Pointer to root list pointer
+ *	@n: New entry in notifier chain
+ *
+ *	Adds a notifier to a notifier chain.
+ *
+ *	Currently always returns zero.
+ */
+ 
+int notifier_chain_register(struct notifier_block **list, struct notifier_block *n)
+{
+	write_lock(&notifier_lock);
+	while(*list)
+	{
+		if(n->priority > (*list)->priority)
+			break;
+		list= &((*list)->next);
+	}
+	n->next = *list;
+	*list=n;
+	write_unlock(&notifier_lock);
+	return 0;
+}
+
+EXPORT_SYMBOL(notifier_chain_register);
+
+/**
+ *	notifier_chain_unregister - Remove notifier from a notifier chain
+ *	@nl: Pointer to root list pointer
+ *	@n: New entry in notifier chain
+ *
+ *	Removes a notifier from a notifier chain.
+ *
+ *	Returns zero on success, or %-ENOENT on failure.
+ */
+ 
+int notifier_chain_unregister(struct notifier_block **nl, struct notifier_block *n)
+{
+	write_lock(&notifier_lock);
+	while((*nl)!=NULL)
+	{
+		if((*nl)==n)
+		{
+			*nl=n->next;
+			write_unlock(&notifier_lock);
+			return 0;
+		}
+		nl=&((*nl)->next);
+	}
+	write_unlock(&notifier_lock);
+	return -ENOENT;
+}
+
+EXPORT_SYMBOL(notifier_chain_unregister);
+
+/**
+ *	notifier_call_chain - Call functions in a notifier chain
+ *	@n: Pointer to root pointer of notifier chain
+ *	@val: Value passed unmodified to notifier function
+ *	@v: Pointer passed unmodified to notifier function
+ *
+ *	Calls each function in a notifier chain in turn.
+ *
+ *	If the return value of the notifier can be and'd
+ *	with %NOTIFY_STOP_MASK, then notifier_call_chain
+ *	will return immediately, with the return value of
+ *	the notifier function which halted execution.
+ *	Otherwise, the return value is the return value
+ *	of the last notifier function called.
+ */
+ 
+int notifier_call_chain(struct notifier_block **n, unsigned long val, void *v)
+{
+	int ret=NOTIFY_DONE;
+	struct notifier_block *nb = *n;
+
+	while(nb)
+	{
+		ret=nb->notifier_call(nb,val,v);
+		if(ret&NOTIFY_STOP_MASK)
+		{
+			return ret;
+		}
+		nb=nb->next;
+	}
+	return ret;
+}
+
+EXPORT_SYMBOL(notifier_call_chain);
+
+/**
+ *	register_reboot_notifier - Register function to be called at reboot time
+ *	@nb: Info about notifier function to be called
+ *
+ *	Registers a function with the list of functions
+ *	to be called at reboot time.
+ *
+ *	Currently always returns zero, as notifier_chain_register
+ *	always returns zero.
+ */
+ 
+int register_reboot_notifier(struct notifier_block * nb)
+{
+	return notifier_chain_register(&reboot_notifier_list, nb);
+}
+
+EXPORT_SYMBOL(register_reboot_notifier);
+
+/**
+ *	unregister_reboot_notifier - Unregister previously registered reboot notifier
+ *	@nb: Hook to be unregistered
+ *
+ *	Unregisters a previously registered reboot
+ *	notifier function.
+ *
+ *	Returns zero on success, or %-ENOENT on failure.
+ */
+ 
+int unregister_reboot_notifier(struct notifier_block * nb)
+{
+	return notifier_chain_unregister(&reboot_notifier_list, nb);
+}
+
+EXPORT_SYMBOL(unregister_reboot_notifier);
+static int set_one_prio(struct task_struct *p, int niceval, int error)
+{
+	int no_nice;
+
+	if (p->uid != current->euid &&
+		p->uid != current->uid && !capable(CAP_SYS_NICE)) {
+		error = -EPERM;
+		goto out;
+	}
+	if (niceval < task_nice(p) && !capable(CAP_SYS_NICE)) {
+		error = -EACCES;
+		goto out;
+	}
+	no_nice = security_task_setnice(p, niceval);
+	if (no_nice) {
+		error = no_nice;
+		goto out;
+	}
+	if (error == -ESRCH)
+		error = 0;
+	set_user_nice(p, niceval);
+out:
+	return error;
+}
+
+asmlinkage long sys_setpriority(int which, int who, int niceval)
+{
+	struct task_struct *g, *p;
+	struct user_struct *user;
+	int error = -EINVAL;
+
+	if (which > 2 || which < 0)
+		goto out;
+
+	/* normalize: avoid signed division (rounding problems) */
+	error = -ESRCH;
+	if (niceval < -20)
+		niceval = -20;
+	if (niceval > 19)
+		niceval = 19;
+
+	read_lock(&tasklist_lock);
+	switch (which) {
+		case PRIO_PROCESS:
+			if (!who)
+				who = current->pid;
+			p = find_task_by_pid(who);
+			if (p)
+				error = set_one_prio(p, niceval, error);
+			break;
+		case PRIO_PGRP:
+			if (!who)
+				who = process_group(current);
+			do_each_task_pid(who, PIDTYPE_PGID, p) {
+				error = set_one_prio(p, niceval, error);
+			} while_each_task_pid(who, PIDTYPE_PGID, p);
+			break;
+		case PRIO_USER:
+			user = current->user;
+			if (!who)
+				who = current->uid;
+			else
+				if ((who != current->uid) && !(user = find_user(who)))
+					goto out_unlock;	/* No processes for this user */
+
+			do_each_thread(g, p)
+				if (p->uid == who)
+					error = set_one_prio(p, niceval, error);
+			while_each_thread(g, p);
+			if (who != current->uid)
+				free_uid(user);		/* For find_user() */
+			break;
+	}
+out_unlock:
+	read_unlock(&tasklist_lock);
+out:
+	return error;
+}
+
+/*
+ * Ugh. To avoid negative return values, "getpriority()" will
+ * not return the normal nice-value, but a negated value that
+ * has been offset by 20 (ie it returns 40..1 instead of -20..19)
+ * to stay compatible.
+ */
+asmlinkage long sys_getpriority(int which, int who)
+{
+	struct task_struct *g, *p;
+	struct user_struct *user;
+	long niceval, retval = -ESRCH;
+
+	if (which > 2 || which < 0)
+		return -EINVAL;
+
+	read_lock(&tasklist_lock);
+	switch (which) {
+		case PRIO_PROCESS:
+			if (!who)
+				who = current->pid;
+			p = find_task_by_pid(who);
+			if (p) {
+				niceval = 20 - task_nice(p);
+				if (niceval > retval)
+					retval = niceval;
+			}
+			break;
+		case PRIO_PGRP:
+			if (!who)
+				who = process_group(current);
+			do_each_task_pid(who, PIDTYPE_PGID, p) {
+				niceval = 20 - task_nice(p);
+				if (niceval > retval)
+					retval = niceval;
+			} while_each_task_pid(who, PIDTYPE_PGID, p);
+			break;
+		case PRIO_USER:
+			user = current->user;
+			if (!who)
+				who = current->uid;
+			else
+				if ((who != current->uid) && !(user = find_user(who)))
+					goto out_unlock;	/* No processes for this user */
+
+			do_each_thread(g, p)
+				if (p->uid == who) {
+					niceval = 20 - task_nice(p);
+					if (niceval > retval)
+						retval = niceval;
+				}
+			while_each_thread(g, p);
+			if (who != current->uid)
+				free_uid(user);		/* for find_user() */
+			break;
+	}
+out_unlock:
+	read_unlock(&tasklist_lock);
+
+	return retval;
+}
+
+
+/*
+ * Reboot system call: for obvious reasons only root may call it,
+ * and even root needs to set up some magic numbers in the registers
+ * so that some mistake won't make this reboot the whole machine.
+ * You can also set the meaning of the ctrl-alt-del-key here.
+ *
+ * reboot doesn't sync: do that yourself before calling this.
+ */
+asmlinkage long sys_reboot(int magic1, int magic2, unsigned int cmd, void __user * arg)
+{
+	char buffer[256];
+
+	/* We only trust the superuser with rebooting the system. */
+	if (!capable(CAP_SYS_BOOT))
+		return -EPERM;
+
+	/* For safety, we require "magic" arguments. */
+	if (magic1 != LINUX_REBOOT_MAGIC1 ||
+	    (magic2 != LINUX_REBOOT_MAGIC2 &&
+	                magic2 != LINUX_REBOOT_MAGIC2A &&
+			magic2 != LINUX_REBOOT_MAGIC2B &&
+	                magic2 != LINUX_REBOOT_MAGIC2C))
+		return -EINVAL;
+
+	lock_kernel();
+	switch (cmd) {
+	case LINUX_REBOOT_CMD_RESTART:
+		notifier_call_chain(&reboot_notifier_list, SYS_RESTART, NULL);
+		system_state = SYSTEM_RESTART;
+		device_shutdown();
+		printk(KERN_EMERG "Restarting system.\n");
+		machine_restart(NULL);
+		break;
+
+	case LINUX_REBOOT_CMD_CAD_ON:
+		C_A_D = 1;
+		break;
+
+	case LINUX_REBOOT_CMD_CAD_OFF:
+		C_A_D = 0;
+		break;
+
+	case LINUX_REBOOT_CMD_HALT:
+		notifier_call_chain(&reboot_notifier_list, SYS_HALT, NULL);
+		system_state = SYSTEM_HALT;
+		device_shutdown();
+		printk(KERN_EMERG "System halted.\n");
+		machine_halt();
+		unlock_kernel();
+		do_exit(0);
+		break;
+
+	case LINUX_REBOOT_CMD_POWER_OFF:
+		notifier_call_chain(&reboot_notifier_list, SYS_POWER_OFF, NULL);
+		system_state = SYSTEM_POWER_OFF;
+		device_shutdown();
+		printk(KERN_EMERG "Power down.\n");
+		machine_power_off();
+		unlock_kernel();
+		do_exit(0);
+		break;
+
+	case LINUX_REBOOT_CMD_RESTART2:
+		if (strncpy_from_user(&buffer[0], arg, sizeof(buffer) - 1) < 0) {
+			unlock_kernel();
+			return -EFAULT;
+		}
+		buffer[sizeof(buffer) - 1] = '\0';
+
+		notifier_call_chain(&reboot_notifier_list, SYS_RESTART, buffer);
+		system_state = SYSTEM_RESTART;
+		device_shutdown();
+		printk(KERN_EMERG "Restarting system with command '%s'.\n", buffer);
+		machine_restart(buffer);
+		break;
+
+#ifdef CONFIG_SOFTWARE_SUSPEND
+	case LINUX_REBOOT_CMD_SW_SUSPEND:
+		{
+			int ret = software_suspend();
+			unlock_kernel();
+			return ret;
+		}
+#endif
+
+	default:
+		unlock_kernel();
+		return -EINVAL;
+	}
+	unlock_kernel();
+	return 0;
+}
+
+static void deferred_cad(void *dummy)
+{
+	notifier_call_chain(&reboot_notifier_list, SYS_RESTART, NULL);
+	machine_restart(NULL);
+}
+
+/*
+ * This function gets called by ctrl-alt-del - ie the keyboard interrupt.
+ * As it's called within an interrupt, it may NOT sync: the only choice
+ * is whether to reboot at once, or just ignore the ctrl-alt-del.
+ */
+void ctrl_alt_del(void)
+{
+	static DECLARE_WORK(cad_work, deferred_cad, NULL);
+
+	if (C_A_D)
+		schedule_work(&cad_work);
+	else
+		kill_proc(cad_pid, SIGINT, 1);
+}
+	
+
+/*
+ * Unprivileged users may change the real gid to the effective gid
+ * or vice versa.  (BSD-style)
+ *
+ * If you set the real gid at all, or set the effective gid to a value not
+ * equal to the real gid, then the saved gid is set to the new effective gid.
+ *
+ * This makes it possible for a setgid program to completely drop its
+ * privileges, which is often a useful assertion to make when you are doing
+ * a security audit over a program.
+ *
+ * The general idea is that a program which uses just setregid() will be
+ * 100% compatible with BSD.  A program which uses just setgid() will be
+ * 100% compatible with POSIX with saved IDs. 
+ *
+ * SMP: There are not races, the GIDs are checked only by filesystem
+ *      operations (as far as semantic preservation is concerned).
+ */
+asmlinkage long sys_setregid(gid_t rgid, gid_t egid)
+{
+	int old_rgid = current->gid;
+	int old_egid = current->egid;
+	int new_rgid = old_rgid;
+	int new_egid = old_egid;
+	int retval;
+
+	retval = security_task_setgid(rgid, egid, (gid_t)-1, LSM_SETID_RE);
+	if (retval)
+		return retval;
+
+	if (rgid != (gid_t) -1) {
+		if ((old_rgid == rgid) ||
+		    (current->egid==rgid) ||
+		    capable(CAP_SETGID))
+			new_rgid = rgid;
+		else
+			return -EPERM;
+	}
+	if (egid != (gid_t) -1) {
+		if ((old_rgid == egid) ||
+		    (current->egid == egid) ||
+		    (current->sgid == egid) ||
+		    capable(CAP_SETGID))
+			new_egid = egid;
+		else {
+			return -EPERM;
+		}
+	}
+	if (new_egid != old_egid)
+	{
+		current->mm->dumpable = 0;
+		wmb();
+	}
+	if (rgid != (gid_t) -1 ||
+	    (egid != (gid_t) -1 && egid != old_rgid))
+		current->sgid = new_egid;
+	current->fsgid = new_egid;
+	current->egid = new_egid;
+	current->gid = new_rgid;
+	key_fsgid_changed(current);
+	return 0;
+}
+
+/*
+ * setgid() is implemented like SysV w/ SAVED_IDS 
+ *
+ * SMP: Same implicit races as above.
+ */
+asmlinkage long sys_setgid(gid_t gid)
+{
+	int old_egid = current->egid;
+	int retval;
+
+	retval = security_task_setgid(gid, (gid_t)-1, (gid_t)-1, LSM_SETID_ID);
+	if (retval)
+		return retval;
+
+	if (capable(CAP_SETGID))
+	{
+		if(old_egid != gid)
+		{
+			current->mm->dumpable=0;
+			wmb();
+		}
+		current->gid = current->egid = current->sgid = current->fsgid = gid;
+	}
+	else if ((gid == current->gid) || (gid == current->sgid))
+	{
+		if(old_egid != gid)
+		{
+			current->mm->dumpable=0;
+			wmb();
+		}
+		current->egid = current->fsgid = gid;
+	}
+	else
+		return -EPERM;
+
+	key_fsgid_changed(current);
+	return 0;
+}
+  
+static int set_user(uid_t new_ruid, int dumpclear)
+{
+	struct user_struct *new_user;
+
+	new_user = alloc_uid(new_ruid);
+	if (!new_user)
+		return -EAGAIN;
+
+	if (atomic_read(&new_user->processes) >=
+				current->signal->rlim[RLIMIT_NPROC].rlim_cur &&
+			new_user != &root_user) {
+		free_uid(new_user);
+		return -EAGAIN;
+	}
+
+	switch_uid(new_user);
+
+	if(dumpclear)
+	{
+		current->mm->dumpable = 0;
+		wmb();
+	}
+	current->uid = new_ruid;
+	return 0;
+}
+
+/*
+ * Unprivileged users may change the real uid to the effective uid
+ * or vice versa.  (BSD-style)
+ *
+ * If you set the real uid at all, or set the effective uid to a value not
+ * equal to the real uid, then the saved uid is set to the new effective uid.
+ *
+ * This makes it possible for a setuid program to completely drop its
+ * privileges, which is often a useful assertion to make when you are doing
+ * a security audit over a program.
+ *
+ * The general idea is that a program which uses just setreuid() will be
+ * 100% compatible with BSD.  A program which uses just setuid() will be
+ * 100% compatible with POSIX with saved IDs. 
+ */
+asmlinkage long sys_setreuid(uid_t ruid, uid_t euid)
+{
+	int old_ruid, old_euid, old_suid, new_ruid, new_euid;
+	int retval;
+
+	retval = security_task_setuid(ruid, euid, (uid_t)-1, LSM_SETID_RE);
+	if (retval)
+		return retval;
+
+	new_ruid = old_ruid = current->uid;
+	new_euid = old_euid = current->euid;
+	old_suid = current->suid;
+
+	if (ruid != (uid_t) -1) {
+		new_ruid = ruid;
+		if ((old_ruid != ruid) &&
+		    (current->euid != ruid) &&
+		    !capable(CAP_SETUID))
+			return -EPERM;
+	}
+
+	if (euid != (uid_t) -1) {
+		new_euid = euid;
+		if ((old_ruid != euid) &&
+		    (current->euid != euid) &&
+		    (current->suid != euid) &&
+		    !capable(CAP_SETUID))
+			return -EPERM;
+	}
+
+	if (new_ruid != old_ruid && set_user(new_ruid, new_euid != old_euid) < 0)
+		return -EAGAIN;
+
+	if (new_euid != old_euid)
+	{
+		current->mm->dumpable=0;
+		wmb();
+	}
+	current->fsuid = current->euid = new_euid;
+	if (ruid != (uid_t) -1 ||
+	    (euid != (uid_t) -1 && euid != old_ruid))
+		current->suid = current->euid;
+	current->fsuid = current->euid;
+
+	key_fsuid_changed(current);
+
+	return security_task_post_setuid(old_ruid, old_euid, old_suid, LSM_SETID_RE);
+}
+
+
+		
+/*
+ * setuid() is implemented like SysV with SAVED_IDS 
+ * 
+ * Note that SAVED_ID's is deficient in that a setuid root program
+ * like sendmail, for example, cannot set its uid to be a normal 
+ * user and then switch back, because if you're root, setuid() sets
+ * the saved uid too.  If you don't like this, blame the bright people
+ * in the POSIX committee and/or USG.  Note that the BSD-style setreuid()
+ * will allow a root program to temporarily drop privileges and be able to
+ * regain them by swapping the real and effective uid.  
+ */
+asmlinkage long sys_setuid(uid_t uid)
+{
+	int old_euid = current->euid;
+	int old_ruid, old_suid, new_ruid, new_suid;
+	int retval;
+
+	retval = security_task_setuid(uid, (uid_t)-1, (uid_t)-1, LSM_SETID_ID);
+	if (retval)
+		return retval;
+
+	old_ruid = new_ruid = current->uid;
+	old_suid = current->suid;
+	new_suid = old_suid;
+	
+	if (capable(CAP_SETUID)) {
+		if (uid != old_ruid && set_user(uid, old_euid != uid) < 0)
+			return -EAGAIN;
+		new_suid = uid;
+	} else if ((uid != current->uid) && (uid != new_suid))
+		return -EPERM;
+
+	if (old_euid != uid)
+	{
+		current->mm->dumpable = 0;
+		wmb();
+	}
+	current->fsuid = current->euid = uid;
+	current->suid = new_suid;
+
+	key_fsuid_changed(current);
+
+	return security_task_post_setuid(old_ruid, old_euid, old_suid, LSM_SETID_ID);
+}
+
+
+/*
+ * This function implements a generic ability to update ruid, euid,
+ * and suid.  This allows you to implement the 4.4 compatible seteuid().
+ */
+asmlinkage long sys_setresuid(uid_t ruid, uid_t euid, uid_t suid)
+{
+	int old_ruid = current->uid;
+	int old_euid = current->euid;
+	int old_suid = current->suid;
+	int retval;
+
+	retval = security_task_setuid(ruid, euid, suid, LSM_SETID_RES);
+	if (retval)
+		return retval;
+
+	if (!capable(CAP_SETUID)) {
+		if ((ruid != (uid_t) -1) && (ruid != current->uid) &&
+		    (ruid != current->euid) && (ruid != current->suid))
+			return -EPERM;
+		if ((euid != (uid_t) -1) && (euid != current->uid) &&
+		    (euid != current->euid) && (euid != current->suid))
+			return -EPERM;
+		if ((suid != (uid_t) -1) && (suid != current->uid) &&
+		    (suid != current->euid) && (suid != current->suid))
+			return -EPERM;
+	}
+	if (ruid != (uid_t) -1) {
+		if (ruid != current->uid && set_user(ruid, euid != current->euid) < 0)
+			return -EAGAIN;
+	}
+	if (euid != (uid_t) -1) {
+		if (euid != current->euid)
+		{
+			current->mm->dumpable = 0;
+			wmb();
+		}
+		current->euid = euid;
+	}
+	current->fsuid = current->euid;
+	if (suid != (uid_t) -1)
+		current->suid = suid;
+
+	key_fsuid_changed(current);
+
+	return security_task_post_setuid(old_ruid, old_euid, old_suid, LSM_SETID_RES);
+}
+
+asmlinkage long sys_getresuid(uid_t __user *ruid, uid_t __user *euid, uid_t __user *suid)
+{
+	int retval;
+
+	if (!(retval = put_user(current->uid, ruid)) &&
+	    !(retval = put_user(current->euid, euid)))
+		retval = put_user(current->suid, suid);
+
+	return retval;
+}
+
+/*
+ * Same as above, but for rgid, egid, sgid.
+ */
+asmlinkage long sys_setresgid(gid_t rgid, gid_t egid, gid_t sgid)
+{
+	int retval;
+
+	retval = security_task_setgid(rgid, egid, sgid, LSM_SETID_RES);
+	if (retval)
+		return retval;
+
+	if (!capable(CAP_SETGID)) {
+		if ((rgid != (gid_t) -1) && (rgid != current->gid) &&
+		    (rgid != current->egid) && (rgid != current->sgid))
+			return -EPERM;
+		if ((egid != (gid_t) -1) && (egid != current->gid) &&
+		    (egid != current->egid) && (egid != current->sgid))
+			return -EPERM;
+		if ((sgid != (gid_t) -1) && (sgid != current->gid) &&
+		    (sgid != current->egid) && (sgid != current->sgid))
+			return -EPERM;
+	}
+	if (egid != (gid_t) -1) {
+		if (egid != current->egid)
+		{
+			current->mm->dumpable = 0;
+			wmb();
+		}
+		current->egid = egid;
+	}
+	current->fsgid = current->egid;
+	if (rgid != (gid_t) -1)
+		current->gid = rgid;
+	if (sgid != (gid_t) -1)
+		current->sgid = sgid;
+
+	key_fsgid_changed(current);
+	return 0;
+}
+
+asmlinkage long sys_getresgid(gid_t __user *rgid, gid_t __user *egid, gid_t __user *sgid)
+{
+	int retval;
+
+	if (!(retval = put_user(current->gid, rgid)) &&
+	    !(retval = put_user(current->egid, egid)))
+		retval = put_user(current->sgid, sgid);
+
+	return retval;
+}
+
+
+/*
+ * "setfsuid()" sets the fsuid - the uid used for filesystem checks. This
+ * is used for "access()" and for the NFS daemon (letting nfsd stay at
+ * whatever uid it wants to). It normally shadows "euid", except when
+ * explicitly set by setfsuid() or for access..
+ */
+asmlinkage long sys_setfsuid(uid_t uid)
+{
+	int old_fsuid;
+
+	old_fsuid = current->fsuid;
+	if (security_task_setuid(uid, (uid_t)-1, (uid_t)-1, LSM_SETID_FS))
+		return old_fsuid;
+
+	if (uid == current->uid || uid == current->euid ||
+	    uid == current->suid || uid == current->fsuid || 
+	    capable(CAP_SETUID))
+	{
+		if (uid != old_fsuid)
+		{
+			current->mm->dumpable = 0;
+			wmb();
+		}
+		current->fsuid = uid;
+	}
+
+	key_fsuid_changed(current);
+
+	security_task_post_setuid(old_fsuid, (uid_t)-1, (uid_t)-1, LSM_SETID_FS);
+
+	return old_fsuid;
+}
+
+/*
+ * Samma på svenska..
+ */
+asmlinkage long sys_setfsgid(gid_t gid)
+{
+	int old_fsgid;
+
+	old_fsgid = current->fsgid;
+	if (security_task_setgid(gid, (gid_t)-1, (gid_t)-1, LSM_SETID_FS))
+		return old_fsgid;
+
+	if (gid == current->gid || gid == current->egid ||
+	    gid == current->sgid || gid == current->fsgid || 
+	    capable(CAP_SETGID))
+	{
+		if (gid != old_fsgid)
+		{
+			current->mm->dumpable = 0;
+			wmb();
+		}
+		current->fsgid = gid;
+		key_fsgid_changed(current);
+	}
+	return old_fsgid;
+}
+
+asmlinkage long sys_times(struct tms __user * tbuf)
+{
+	/*
+	 *	In the SMP world we might just be unlucky and have one of
+	 *	the times increment as we use it. Since the value is an
+	 *	atomically safe type this is just fine. Conceptually its
+	 *	as if the syscall took an instant longer to occur.
+	 */
+	if (tbuf) {
+		struct tms tmp;
+		struct task_struct *tsk = current;
+		struct task_struct *t;
+		cputime_t utime, stime, cutime, cstime;
+
+		read_lock(&tasklist_lock);
+		utime = tsk->signal->utime;
+		stime = tsk->signal->stime;
+		t = tsk;
+		do {
+			utime = cputime_add(utime, t->utime);
+			stime = cputime_add(stime, t->stime);
+			t = next_thread(t);
+		} while (t != tsk);
+
+		/*
+		 * While we have tasklist_lock read-locked, no dying thread
+		 * can be updating current->signal->[us]time.  Instead,
+		 * we got their counts included in the live thread loop.
+		 * However, another thread can come in right now and
+		 * do a wait call that updates current->signal->c[us]time.
+		 * To make sure we always see that pair updated atomically,
+		 * we take the siglock around fetching them.
+		 */
+		spin_lock_irq(&tsk->sighand->siglock);
+		cutime = tsk->signal->cutime;
+		cstime = tsk->signal->cstime;
+		spin_unlock_irq(&tsk->sighand->siglock);
+		read_unlock(&tasklist_lock);
+
+		tmp.tms_utime = cputime_to_clock_t(utime);
+		tmp.tms_stime = cputime_to_clock_t(stime);
+		tmp.tms_cutime = cputime_to_clock_t(cutime);
+		tmp.tms_cstime = cputime_to_clock_t(cstime);
+		if (copy_to_user(tbuf, &tmp, sizeof(struct tms)))
+			return -EFAULT;
+	}
+	return (long) jiffies_64_to_clock_t(get_jiffies_64());
+}
+
+/*
+ * This needs some heavy checking ...
+ * I just haven't the stomach for it. I also don't fully
+ * understand sessions/pgrp etc. Let somebody who does explain it.
+ *
+ * OK, I think I have the protection semantics right.... this is really
+ * only important on a multi-user system anyway, to make sure one user
+ * can't send a signal to a process owned by another.  -TYT, 12/12/91
+ *
+ * Auch. Had to add the 'did_exec' flag to conform completely to POSIX.
+ * LBT 04.03.94
+ */
+
+asmlinkage long sys_setpgid(pid_t pid, pid_t pgid)
+{
+	struct task_struct *p;
+	int err = -EINVAL;
+
+	if (!pid)
+		pid = current->pid;
+	if (!pgid)
+		pgid = pid;
+	if (pgid < 0)
+		return -EINVAL;
+
+	/* From this point forward we keep holding onto the tasklist lock
+	 * so that our parent does not change from under us. -DaveM
+	 */
+	write_lock_irq(&tasklist_lock);
+
+	err = -ESRCH;
+	p = find_task_by_pid(pid);
+	if (!p)
+		goto out;
+
+	err = -EINVAL;
+	if (!thread_group_leader(p))
+		goto out;
+
+	if (p->parent == current || p->real_parent == current) {
+		err = -EPERM;
+		if (p->signal->session != current->signal->session)
+			goto out;
+		err = -EACCES;
+		if (p->did_exec)
+			goto out;
+	} else {
+		err = -ESRCH;
+		if (p != current)
+			goto out;
+	}
+
+	err = -EPERM;
+	if (p->signal->leader)
+		goto out;
+
+	if (pgid != pid) {
+		struct task_struct *p;
+
+		do_each_task_pid(pgid, PIDTYPE_PGID, p) {
+			if (p->signal->session == current->signal->session)
+				goto ok_pgid;
+		} while_each_task_pid(pgid, PIDTYPE_PGID, p);
+		goto out;
+	}
+
+ok_pgid:
+	err = security_task_setpgid(p, pgid);
+	if (err)
+		goto out;
+
+	if (process_group(p) != pgid) {
+		detach_pid(p, PIDTYPE_PGID);
+		p->signal->pgrp = pgid;
+		attach_pid(p, PIDTYPE_PGID, pgid);
+	}
+
+	err = 0;
+out:
+	/* All paths lead to here, thus we are safe. -DaveM */
+	write_unlock_irq(&tasklist_lock);
+	return err;
+}
+
+asmlinkage long sys_getpgid(pid_t pid)
+{
+	if (!pid) {
+		return process_group(current);
+	} else {
+		int retval;
+		struct task_struct *p;
+
+		read_lock(&tasklist_lock);
+		p = find_task_by_pid(pid);
+
+		retval = -ESRCH;
+		if (p) {
+			retval = security_task_getpgid(p);
+			if (!retval)
+				retval = process_group(p);
+		}
+		read_unlock(&tasklist_lock);
+		return retval;
+	}
+}
+
+#ifdef __ARCH_WANT_SYS_GETPGRP
+
+asmlinkage long sys_getpgrp(void)
+{
+	/* SMP - assuming writes are word atomic this is fine */
+	return process_group(current);
+}
+
+#endif
+
+asmlinkage long sys_getsid(pid_t pid)
+{
+	if (!pid) {
+		return current->signal->session;
+	} else {
+		int retval;
+		struct task_struct *p;
+
+		read_lock(&tasklist_lock);
+		p = find_task_by_pid(pid);
+
+		retval = -ESRCH;
+		if(p) {
+			retval = security_task_getsid(p);
+			if (!retval)
+				retval = p->signal->session;
+		}
+		read_unlock(&tasklist_lock);
+		return retval;
+	}
+}
+
+asmlinkage long sys_setsid(void)
+{
+	struct pid *pid;
+	int err = -EPERM;
+
+	if (!thread_group_leader(current))
+		return -EINVAL;
+
+	down(&tty_sem);
+	write_lock_irq(&tasklist_lock);
+
+	pid = find_pid(PIDTYPE_PGID, current->pid);
+	if (pid)
+		goto out;
+
+	current->signal->leader = 1;
+	__set_special_pids(current->pid, current->pid);
+	current->signal->tty = NULL;
+	current->signal->tty_old_pgrp = 0;
+	err = process_group(current);
+out:
+	write_unlock_irq(&tasklist_lock);
+	up(&tty_sem);
+	return err;
+}
+
+/*
+ * Supplementary group IDs
+ */
+
+/* init to 2 - one for init_task, one to ensure it is never freed */
+struct group_info init_groups = { .usage = ATOMIC_INIT(2) };
+
+struct group_info *groups_alloc(int gidsetsize)
+{
+	struct group_info *group_info;
+	int nblocks;
+	int i;
+
+	nblocks = (gidsetsize + NGROUPS_PER_BLOCK - 1) / NGROUPS_PER_BLOCK;
+	/* Make sure we always allocate at least one indirect block pointer */
+	nblocks = nblocks ? : 1;
+	group_info = kmalloc(sizeof(*group_info) + nblocks*sizeof(gid_t *), GFP_USER);
+	if (!group_info)
+		return NULL;
+	group_info->ngroups = gidsetsize;
+	group_info->nblocks = nblocks;
+	atomic_set(&group_info->usage, 1);
+
+	if (gidsetsize <= NGROUPS_SMALL) {
+		group_info->blocks[0] = group_info->small_block;
+	} else {
+		for (i = 0; i < nblocks; i++) {
+			gid_t *b;
+			b = (void *)__get_free_page(GFP_USER);
+			if (!b)
+				goto out_undo_partial_alloc;
+			group_info->blocks[i] = b;
+		}
+	}
+	return group_info;
+
+out_undo_partial_alloc:
+	while (--i >= 0) {
+		free_page((unsigned long)group_info->blocks[i]);
+	}
+	kfree(group_info);
+	return NULL;
+}
+
+EXPORT_SYMBOL(groups_alloc);
+
+void groups_free(struct group_info *group_info)
+{
+	if (group_info->blocks[0] != group_info->small_block) {
+		int i;
+		for (i = 0; i < group_info->nblocks; i++)
+			free_page((unsigned long)group_info->blocks[i]);
+	}
+	kfree(group_info);
+}
+
+EXPORT_SYMBOL(groups_free);
+
+/* export the group_info to a user-space array */
+static int groups_to_user(gid_t __user *grouplist,
+    struct group_info *group_info)
+{
+	int i;
+	int count = group_info->ngroups;
+
+	for (i = 0; i < group_info->nblocks; i++) {
+		int cp_count = min(NGROUPS_PER_BLOCK, count);
+		int off = i * NGROUPS_PER_BLOCK;
+		int len = cp_count * sizeof(*grouplist);
+
+		if (copy_to_user(grouplist+off, group_info->blocks[i], len))
+			return -EFAULT;
+
+		count -= cp_count;
+	}
+	return 0;
+}
+
+/* fill a group_info from a user-space array - it must be allocated already */
+static int groups_from_user(struct group_info *group_info,
+    gid_t __user *grouplist)
+ {
+	int i;
+	int count = group_info->ngroups;
+
+	for (i = 0; i < group_info->nblocks; i++) {
+		int cp_count = min(NGROUPS_PER_BLOCK, count);
+		int off = i * NGROUPS_PER_BLOCK;
+		int len = cp_count * sizeof(*grouplist);
+
+		if (copy_from_user(group_info->blocks[i], grouplist+off, len))
+			return -EFAULT;
+
+		count -= cp_count;
+	}
+	return 0;
+}
+
+/* a simple shell-metzner sort */
+static void groups_sort(struct group_info *group_info)
+{
+	int base, max, stride;
+	int gidsetsize = group_info->ngroups;
+
+	for (stride = 1; stride < gidsetsize; stride = 3 * stride + 1)
+		; /* nothing */
+	stride /= 3;
+
+	while (stride) {
+		max = gidsetsize - stride;
+		for (base = 0; base < max; base++) {
+			int left = base;
+			int right = left + stride;
+			gid_t tmp = GROUP_AT(group_info, right);
+
+			while (left >= 0 && GROUP_AT(group_info, left) > tmp) {
+				GROUP_AT(group_info, right) =
+				    GROUP_AT(group_info, left);
+				right = left;
+				left -= stride;
+			}
+			GROUP_AT(group_info, right) = tmp;
+		}
+		stride /= 3;
+	}
+}
+
+/* a simple bsearch */
+static int groups_search(struct group_info *group_info, gid_t grp)
+{
+	int left, right;
+
+	if (!group_info)
+		return 0;
+
+	left = 0;
+	right = group_info->ngroups;
+	while (left < right) {
+		int mid = (left+right)/2;
+		int cmp = grp - GROUP_AT(group_info, mid);
+		if (cmp > 0)
+			left = mid + 1;
+		else if (cmp < 0)
+			right = mid;
+		else
+			return 1;
+	}
+	return 0;
+}
+
+/* validate and set current->group_info */
+int set_current_groups(struct group_info *group_info)
+{
+	int retval;
+	struct group_info *old_info;
+
+	retval = security_task_setgroups(group_info);
+	if (retval)
+		return retval;
+
+	groups_sort(group_info);
+	get_group_info(group_info);
+
+	task_lock(current);
+	old_info = current->group_info;
+	current->group_info = group_info;
+	task_unlock(current);
+
+	put_group_info(old_info);
+
+	return 0;
+}
+
+EXPORT_SYMBOL(set_current_groups);
+
+asmlinkage long sys_getgroups(int gidsetsize, gid_t __user *grouplist)
+{
+	int i = 0;
+
+	/*
+	 *	SMP: Nobody else can change our grouplist. Thus we are
+	 *	safe.
+	 */
+
+	if (gidsetsize < 0)
+		return -EINVAL;
+
+	/* no need to grab task_lock here; it cannot change */
+	get_group_info(current->group_info);
+	i = current->group_info->ngroups;
+	if (gidsetsize) {
+		if (i > gidsetsize) {
+			i = -EINVAL;
+			goto out;
+		}
+		if (groups_to_user(grouplist, current->group_info)) {
+			i = -EFAULT;
+			goto out;
+		}
+	}
+out:
+	put_group_info(current->group_info);
+	return i;
+}
+
+/*
+ *	SMP: Our groups are copy-on-write. We can set them safely
+ *	without another task interfering.
+ */
+ 
+asmlinkage long sys_setgroups(int gidsetsize, gid_t __user *grouplist)
+{
+	struct group_info *group_info;
+	int retval;
+
+	if (!capable(CAP_SETGID))
+		return -EPERM;
+	if ((unsigned)gidsetsize > NGROUPS_MAX)
+		return -EINVAL;
+
+	group_info = groups_alloc(gidsetsize);
+	if (!group_info)
+		return -ENOMEM;
+	retval = groups_from_user(group_info, grouplist);
+	if (retval) {
+		put_group_info(group_info);
+		return retval;
+	}
+
+	retval = set_current_groups(group_info);
+	put_group_info(group_info);
+
+	return retval;
+}
+
+/*
+ * Check whether we're fsgid/egid or in the supplemental group..
+ */
+int in_group_p(gid_t grp)
+{
+	int retval = 1;
+	if (grp != current->fsgid) {
+		get_group_info(current->group_info);
+		retval = groups_search(current->group_info, grp);
+		put_group_info(current->group_info);
+	}
+	return retval;
+}
+
+EXPORT_SYMBOL(in_group_p);
+
+int in_egroup_p(gid_t grp)
+{
+	int retval = 1;
+	if (grp != current->egid) {
+		get_group_info(current->group_info);
+		retval = groups_search(current->group_info, grp);
+		put_group_info(current->group_info);
+	}
+	return retval;
+}
+
+EXPORT_SYMBOL(in_egroup_p);
+
+DECLARE_RWSEM(uts_sem);
+
+EXPORT_SYMBOL(uts_sem);
+
+asmlinkage long sys_newuname(struct new_utsname __user * name)
+{
+	int errno = 0;
+
+	down_read(&uts_sem);
+	if (copy_to_user(name,&system_utsname,sizeof *name))
+		errno = -EFAULT;
+	up_read(&uts_sem);
+	return errno;
+}
+
+asmlinkage long sys_sethostname(char __user *name, int len)
+{
+	int errno;
+	char tmp[__NEW_UTS_LEN];
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+	if (len < 0 || len > __NEW_UTS_LEN)
+		return -EINVAL;
+	down_write(&uts_sem);
+	errno = -EFAULT;
+	if (!copy_from_user(tmp, name, len)) {
+		memcpy(system_utsname.nodename, tmp, len);
+		system_utsname.nodename[len] = 0;
+		errno = 0;
+	}
+	up_write(&uts_sem);
+	return errno;
+}
+
+#ifdef __ARCH_WANT_SYS_GETHOSTNAME
+
+asmlinkage long sys_gethostname(char __user *name, int len)
+{
+	int i, errno;
+
+	if (len < 0)
+		return -EINVAL;
+	down_read(&uts_sem);
+	i = 1 + strlen(system_utsname.nodename);
+	if (i > len)
+		i = len;
+	errno = 0;
+	if (copy_to_user(name, system_utsname.nodename, i))
+		errno = -EFAULT;
+	up_read(&uts_sem);
+	return errno;
+}
+
+#endif
+
+/*
+ * Only setdomainname; getdomainname can be implemented by calling
+ * uname()
+ */
+asmlinkage long sys_setdomainname(char __user *name, int len)
+{
+	int errno;
+	char tmp[__NEW_UTS_LEN];
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+	if (len < 0 || len > __NEW_UTS_LEN)
+		return -EINVAL;
+
+	down_write(&uts_sem);
+	errno = -EFAULT;
+	if (!copy_from_user(tmp, name, len)) {
+		memcpy(system_utsname.domainname, tmp, len);
+		system_utsname.domainname[len] = 0;
+		errno = 0;
+	}
+	up_write(&uts_sem);
+	return errno;
+}
+
+asmlinkage long sys_getrlimit(unsigned int resource, struct rlimit __user *rlim)
+{
+	if (resource >= RLIM_NLIMITS)
+		return -EINVAL;
+	else {
+		struct rlimit value;
+		task_lock(current->group_leader);
+		value = current->signal->rlim[resource];
+		task_unlock(current->group_leader);
+		return copy_to_user(rlim, &value, sizeof(*rlim)) ? -EFAULT : 0;
+	}
+}
+
+#ifdef __ARCH_WANT_SYS_OLD_GETRLIMIT
+
+/*
+ *	Back compatibility for getrlimit. Needed for some apps.
+ */
+ 
+asmlinkage long sys_old_getrlimit(unsigned int resource, struct rlimit __user *rlim)
+{
+	struct rlimit x;
+	if (resource >= RLIM_NLIMITS)
+		return -EINVAL;
+
+	task_lock(current->group_leader);
+	x = current->signal->rlim[resource];
+	task_unlock(current->group_leader);
+	if(x.rlim_cur > 0x7FFFFFFF)
+		x.rlim_cur = 0x7FFFFFFF;
+	if(x.rlim_max > 0x7FFFFFFF)
+		x.rlim_max = 0x7FFFFFFF;
+	return copy_to_user(rlim, &x, sizeof(x))?-EFAULT:0;
+}
+
+#endif
+
+asmlinkage long sys_setrlimit(unsigned int resource, struct rlimit __user *rlim)
+{
+	struct rlimit new_rlim, *old_rlim;
+	int retval;
+
+	if (resource >= RLIM_NLIMITS)
+		return -EINVAL;
+	if(copy_from_user(&new_rlim, rlim, sizeof(*rlim)))
+		return -EFAULT;
+       if (new_rlim.rlim_cur > new_rlim.rlim_max)
+               return -EINVAL;
+	old_rlim = current->signal->rlim + resource;
+	if ((new_rlim.rlim_max > old_rlim->rlim_max) &&
+	    !capable(CAP_SYS_RESOURCE))
+		return -EPERM;
+	if (resource == RLIMIT_NOFILE && new_rlim.rlim_max > NR_OPEN)
+			return -EPERM;
+
+	retval = security_task_setrlimit(resource, &new_rlim);
+	if (retval)
+		return retval;
+
+	task_lock(current->group_leader);
+	*old_rlim = new_rlim;
+	task_unlock(current->group_leader);
+	return 0;
+}
+
+/*
+ * It would make sense to put struct rusage in the task_struct,
+ * except that would make the task_struct be *really big*.  After
+ * task_struct gets moved into malloc'ed memory, it would
+ * make sense to do this.  It will make moving the rest of the information
+ * a lot simpler!  (Which we're not doing right now because we're not
+ * measuring them yet).
+ *
+ * This expects to be called with tasklist_lock read-locked or better,
+ * and the siglock not locked.  It may momentarily take the siglock.
+ *
+ * When sampling multiple threads for RUSAGE_SELF, under SMP we might have
+ * races with threads incrementing their own counters.  But since word
+ * reads are atomic, we either get new values or old values and we don't
+ * care which for the sums.  We always take the siglock to protect reading
+ * the c* fields from p->signal from races with exit.c updating those
+ * fields when reaping, so a sample either gets all the additions of a
+ * given child after it's reaped, or none so this sample is before reaping.
+ */
+
+void k_getrusage(struct task_struct *p, int who, struct rusage *r)
+{
+	struct task_struct *t;
+	unsigned long flags;
+	cputime_t utime, stime;
+
+	memset((char *) r, 0, sizeof *r);
+
+	if (unlikely(!p->signal))
+		return;
+
+	switch (who) {
+		case RUSAGE_CHILDREN:
+			spin_lock_irqsave(&p->sighand->siglock, flags);
+			utime = p->signal->cutime;
+			stime = p->signal->cstime;
+			r->ru_nvcsw = p->signal->cnvcsw;
+			r->ru_nivcsw = p->signal->cnivcsw;
+			r->ru_minflt = p->signal->cmin_flt;
+			r->ru_majflt = p->signal->cmaj_flt;
+			spin_unlock_irqrestore(&p->sighand->siglock, flags);
+			cputime_to_timeval(utime, &r->ru_utime);
+			cputime_to_timeval(stime, &r->ru_stime);
+			break;
+		case RUSAGE_SELF:
+			spin_lock_irqsave(&p->sighand->siglock, flags);
+			utime = stime = cputime_zero;
+			goto sum_group;
+		case RUSAGE_BOTH:
+			spin_lock_irqsave(&p->sighand->siglock, flags);
+			utime = p->signal->cutime;
+			stime = p->signal->cstime;
+			r->ru_nvcsw = p->signal->cnvcsw;
+			r->ru_nivcsw = p->signal->cnivcsw;
+			r->ru_minflt = p->signal->cmin_flt;
+			r->ru_majflt = p->signal->cmaj_flt;
+		sum_group:
+			utime = cputime_add(utime, p->signal->utime);
+			stime = cputime_add(stime, p->signal->stime);
+			r->ru_nvcsw += p->signal->nvcsw;
+			r->ru_nivcsw += p->signal->nivcsw;
+			r->ru_minflt += p->signal->min_flt;
+			r->ru_majflt += p->signal->maj_flt;
+			t = p;
+			do {
+				utime = cputime_add(utime, t->utime);
+				stime = cputime_add(stime, t->stime);
+				r->ru_nvcsw += t->nvcsw;
+				r->ru_nivcsw += t->nivcsw;
+				r->ru_minflt += t->min_flt;
+				r->ru_majflt += t->maj_flt;
+				t = next_thread(t);
+			} while (t != p);
+			spin_unlock_irqrestore(&p->sighand->siglock, flags);
+			cputime_to_timeval(utime, &r->ru_utime);
+			cputime_to_timeval(stime, &r->ru_stime);
+			break;
+		default:
+			BUG();
+	}
+}
+
+int getrusage(struct task_struct *p, int who, struct rusage __user *ru)
+{
+	struct rusage r;
+	read_lock(&tasklist_lock);
+	k_getrusage(p, who, &r);
+	read_unlock(&tasklist_lock);
+	return copy_to_user(ru, &r, sizeof(r)) ? -EFAULT : 0;
+}
+
+asmlinkage long sys_getrusage(int who, struct rusage __user *ru)
+{
+	if (who != RUSAGE_SELF && who != RUSAGE_CHILDREN)
+		return -EINVAL;
+	return getrusage(current, who, ru);
+}
+
+asmlinkage long sys_umask(int mask)
+{
+	mask = xchg(&current->fs->umask, mask & S_IRWXUGO);
+	return mask;
+}
+    
+asmlinkage long sys_prctl(int option, unsigned long arg2, unsigned long arg3,
+			  unsigned long arg4, unsigned long arg5)
+{
+	long error;
+	int sig;
+
+	error = security_task_prctl(option, arg2, arg3, arg4, arg5);
+	if (error)
+		return error;
+
+	switch (option) {
+		case PR_SET_PDEATHSIG:
+			sig = arg2;
+			if (sig < 0 || sig > _NSIG) {
+				error = -EINVAL;
+				break;
+			}
+			current->pdeath_signal = sig;
+			break;
+		case PR_GET_PDEATHSIG:
+			error = put_user(current->pdeath_signal, (int __user *)arg2);
+			break;
+		case PR_GET_DUMPABLE:
+			if (current->mm->dumpable)
+				error = 1;
+			break;
+		case PR_SET_DUMPABLE:
+			if (arg2 != 0 && arg2 != 1) {
+				error = -EINVAL;
+				break;
+			}
+			current->mm->dumpable = arg2;
+			break;
+
+		case PR_SET_UNALIGN:
+			error = SET_UNALIGN_CTL(current, arg2);
+			break;
+		case PR_GET_UNALIGN:
+			error = GET_UNALIGN_CTL(current, arg2);
+			break;
+		case PR_SET_FPEMU:
+			error = SET_FPEMU_CTL(current, arg2);
+			break;
+		case PR_GET_FPEMU:
+			error = GET_FPEMU_CTL(current, arg2);
+			break;
+		case PR_SET_FPEXC:
+			error = SET_FPEXC_CTL(current, arg2);
+			break;
+		case PR_GET_FPEXC:
+			error = GET_FPEXC_CTL(current, arg2);
+			break;
+		case PR_GET_TIMING:
+			error = PR_TIMING_STATISTICAL;
+			break;
+		case PR_SET_TIMING:
+			if (arg2 == PR_TIMING_STATISTICAL)
+				error = 0;
+			else
+				error = -EINVAL;
+			break;
+
+		case PR_GET_KEEPCAPS:
+			if (current->keep_capabilities)
+				error = 1;
+			break;
+		case PR_SET_KEEPCAPS:
+			if (arg2 != 0 && arg2 != 1) {
+				error = -EINVAL;
+				break;
+			}
+			current->keep_capabilities = arg2;
+			break;
+		case PR_SET_NAME: {
+			struct task_struct *me = current;
+			unsigned char ncomm[sizeof(me->comm)];
+
+			ncomm[sizeof(me->comm)-1] = 0;
+			if (strncpy_from_user(ncomm, (char __user *)arg2,
+						sizeof(me->comm)-1) < 0)
+				return -EFAULT;
+			set_task_comm(me, ncomm);
+			return 0;
+		}
+		case PR_GET_NAME: {
+			struct task_struct *me = current;
+			unsigned char tcomm[sizeof(me->comm)];
+
+			get_task_comm(tcomm, me);
+			if (copy_to_user((char __user *)arg2, tcomm, sizeof(tcomm)))
+				return -EFAULT;
+			return 0;
+		}
+		default:
+			error = -EINVAL;
+			break;
+	}
+	return error;
+}
diff -Nrub linux-2.6.11.7-orig/net/socket.c linux-2.6.11.7-snare/net/socket.c
--- linux-2.6.11.7-orig/net/socket.c	2005-04-07 14:57:30.000000000 -0400
+++ linux-2.6.11.7-snare/net/socket.c	2005-04-22 11:19:47.205664096 -0400
@@ -44,6 +44,7 @@
  *		Tigran Aivazian	:	sys_send(args) calls sys_sendto(args, NULL, 0)
  *		Tigran Aivazian	:	Made listen(2) backlog sanity checks 
  *					protocol-independent
+ *              Leigh Purdie    :       Socketcall auditing.
  *
  *
  *		This program is free software; you can redistribute it and/or
@@ -75,6 +76,7 @@
 #include <linux/cache.h>
 #include <linux/module.h>
 #include <linux/highmem.h>
+#include <linux/saudit.h>
 #include <linux/divert.h>
 #include <linux/mount.h>
 #include <linux/security.h>
@@ -1922,12 +1924,14 @@
 			break;
 		case SYS_CONNECT:
 			err = sys_connect(a0, (struct sockaddr __user *)a1, a[2]);
+			saudit_connect(a0, (struct sockaddr __user *)a1, a[2],err);
 			break;
 		case SYS_LISTEN:
 			err = sys_listen(a0,a1);
 			break;
 		case SYS_ACCEPT:
 			err = sys_accept(a0,(struct sockaddr __user *)a1, (int __user *)a[2]);
+			saudit_accept(a0, (struct sockaddr __user*)a1, (int __user *)a[2],err);
 			break;
 		case SYS_GETSOCKNAME:
 			err = sys_getsockname(a0,(struct sockaddr __user *)a1, (int __user *)a[2]);
diff -Nrub linux-2.6.11.7-orig/net/socket.c.orig linux-2.6.11.7-snare/net/socket.c.orig
--- linux-2.6.11.7-orig/net/socket.c.orig	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.11.7-snare/net/socket.c.orig	2005-04-07 14:57:30.000000000 -0400
@@ -0,0 +1,2090 @@
+/*
+ * NET		An implementation of the SOCKET network access protocol.
+ *
+ * Version:	@(#)socket.c	1.1.93	18/02/95
+ *
+ * Authors:	Orest Zborowski, <obz@Kodak.COM>
+ *		Ross Biro, <bir7@leland.Stanford.Edu>
+ *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * Fixes:
+ *		Anonymous	:	NOTSOCK/BADF cleanup. Error fix in
+ *					shutdown()
+ *		Alan Cox	:	verify_area() fixes
+ *		Alan Cox	:	Removed DDI
+ *		Jonathan Kamens	:	SOCK_DGRAM reconnect bug
+ *		Alan Cox	:	Moved a load of checks to the very
+ *					top level.
+ *		Alan Cox	:	Move address structures to/from user
+ *					mode above the protocol layers.
+ *		Rob Janssen	:	Allow 0 length sends.
+ *		Alan Cox	:	Asynchronous I/O support (cribbed from the
+ *					tty drivers).
+ *		Niibe Yutaka	:	Asynchronous I/O for writes (4.4BSD style)
+ *		Jeff Uphoff	:	Made max number of sockets command-line
+ *					configurable.
+ *		Matti Aarnio	:	Made the number of sockets dynamic,
+ *					to be allocated when needed, and mr.
+ *					Uphoff's max is used as max to be
+ *					allowed to allocate.
+ *		Linus		:	Argh. removed all the socket allocation
+ *					altogether: it's in the inode now.
+ *		Alan Cox	:	Made sock_alloc()/sock_release() public
+ *					for NetROM and future kernel nfsd type
+ *					stuff.
+ *		Alan Cox	:	sendmsg/recvmsg basics.
+ *		Tom Dyas	:	Export net symbols.
+ *		Marcin Dalecki	:	Fixed problems with CONFIG_NET="n".
+ *		Alan Cox	:	Added thread locking to sys_* calls
+ *					for sockets. May have errors at the
+ *					moment.
+ *		Kevin Buhr	:	Fixed the dumb errors in the above.
+ *		Andi Kleen	:	Some small cleanups, optimizations,
+ *					and fixed a copy_from_user() bug.
+ *		Tigran Aivazian	:	sys_send(args) calls sys_sendto(args, NULL, 0)
+ *		Tigran Aivazian	:	Made listen(2) backlog sanity checks 
+ *					protocol-independent
+ *
+ *
+ *		This program is free software; you can redistribute it and/or
+ *		modify it under the terms of the GNU General Public License
+ *		as published by the Free Software Foundation; either version
+ *		2 of the License, or (at your option) any later version.
+ *
+ *
+ *	This module is effectively the top level interface to the BSD socket
+ *	paradigm. 
+ *
+ *	Based upon Swansea University Computer Society NET3.039
+ */
+
+#include <linux/config.h>
+#include <linux/mm.h>
+#include <linux/smp_lock.h>
+#include <linux/socket.h>
+#include <linux/file.h>
+#include <linux/net.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/wanrouter.h>
+#include <linux/if_bridge.h>
+#include <linux/init.h>
+#include <linux/poll.h>
+#include <linux/cache.h>
+#include <linux/module.h>
+#include <linux/highmem.h>
+#include <linux/divert.h>
+#include <linux/mount.h>
+#include <linux/security.h>
+#include <linux/syscalls.h>
+#include <linux/compat.h>
+#include <linux/kmod.h>
+
+#ifdef CONFIG_NET_RADIO
+#include <linux/wireless.h>		/* Note : will define WIRELESS_EXT */
+#endif	/* CONFIG_NET_RADIO */
+
+#include <asm/uaccess.h>
+#include <asm/unistd.h>
+
+#include <net/compat.h>
+
+#include <net/sock.h>
+#include <linux/netfilter.h>
+
+static int sock_no_open(struct inode *irrelevant, struct file *dontcare);
+static ssize_t sock_aio_read(struct kiocb *iocb, char __user *buf,
+			 size_t size, loff_t pos);
+static ssize_t sock_aio_write(struct kiocb *iocb, const char __user *buf,
+			  size_t size, loff_t pos);
+static int sock_mmap(struct file *file, struct vm_area_struct * vma);
+
+static int sock_close(struct inode *inode, struct file *file);
+static unsigned int sock_poll(struct file *file,
+			      struct poll_table_struct *wait);
+static long sock_ioctl(struct file *file,
+		      unsigned int cmd, unsigned long arg);
+static int sock_fasync(int fd, struct file *filp, int on);
+static ssize_t sock_readv(struct file *file, const struct iovec *vector,
+			  unsigned long count, loff_t *ppos);
+static ssize_t sock_writev(struct file *file, const struct iovec *vector,
+			  unsigned long count, loff_t *ppos);
+static ssize_t sock_sendpage(struct file *file, struct page *page,
+			     int offset, size_t size, loff_t *ppos, int more);
+
+
+/*
+ *	Socket files have a set of 'special' operations as well as the generic file ones. These don't appear
+ *	in the operation structures but are done directly via the socketcall() multiplexor.
+ */
+
+static struct file_operations socket_file_ops = {
+	.owner =	THIS_MODULE,
+	.llseek =	no_llseek,
+	.aio_read =	sock_aio_read,
+	.aio_write =	sock_aio_write,
+	.poll =		sock_poll,
+	.unlocked_ioctl = sock_ioctl,
+	.mmap =		sock_mmap,
+	.open =		sock_no_open,	/* special open code to disallow open via /proc */
+	.release =	sock_close,
+	.fasync =	sock_fasync,
+	.readv =	sock_readv,
+	.writev =	sock_writev,
+	.sendpage =	sock_sendpage
+};
+
+/*
+ *	The protocol list. Each protocol is registered in here.
+ */
+
+static struct net_proto_family *net_families[NPROTO];
+
+#if defined(CONFIG_SMP) || defined(CONFIG_PREEMPT)
+static atomic_t net_family_lockct = ATOMIC_INIT(0);
+static DEFINE_SPINLOCK(net_family_lock);
+
+/* The strategy is: modifications net_family vector are short, do not
+   sleep and veeery rare, but read access should be free of any exclusive
+   locks.
+ */
+
+static void net_family_write_lock(void)
+{
+	spin_lock(&net_family_lock);
+	while (atomic_read(&net_family_lockct) != 0) {
+		spin_unlock(&net_family_lock);
+
+		yield();
+
+		spin_lock(&net_family_lock);
+	}
+}
+
+static __inline__ void net_family_write_unlock(void)
+{
+	spin_unlock(&net_family_lock);
+}
+
+static __inline__ void net_family_read_lock(void)
+{
+	atomic_inc(&net_family_lockct);
+	spin_unlock_wait(&net_family_lock);
+}
+
+static __inline__ void net_family_read_unlock(void)
+{
+	atomic_dec(&net_family_lockct);
+}
+
+#else
+#define net_family_write_lock() do { } while(0)
+#define net_family_write_unlock() do { } while(0)
+#define net_family_read_lock() do { } while(0)
+#define net_family_read_unlock() do { } while(0)
+#endif
+
+
+/*
+ *	Statistics counters of the socket lists
+ */
+
+static DEFINE_PER_CPU(int, sockets_in_use) = 0;
+
+/*
+ *	Support routines. Move socket addresses back and forth across the kernel/user
+ *	divide and look after the messy bits.
+ */
+
+#define MAX_SOCK_ADDR	128		/* 108 for Unix domain - 
+					   16 for IP, 16 for IPX,
+					   24 for IPv6,
+					   about 80 for AX.25 
+					   must be at least one bigger than
+					   the AF_UNIX size (see net/unix/af_unix.c
+					   :unix_mkname()).  
+					 */
+					 
+/**
+ *	move_addr_to_kernel	-	copy a socket address into kernel space
+ *	@uaddr: Address in user space
+ *	@kaddr: Address in kernel space
+ *	@ulen: Length in user space
+ *
+ *	The address is copied into kernel space. If the provided address is
+ *	too long an error code of -EINVAL is returned. If the copy gives
+ *	invalid addresses -EFAULT is returned. On a success 0 is returned.
+ */
+
+int move_addr_to_kernel(void __user *uaddr, int ulen, void *kaddr)
+{
+	if(ulen<0||ulen>MAX_SOCK_ADDR)
+		return -EINVAL;
+	if(ulen==0)
+		return 0;
+	if(copy_from_user(kaddr,uaddr,ulen))
+		return -EFAULT;
+	return 0;
+}
+
+/**
+ *	move_addr_to_user	-	copy an address to user space
+ *	@kaddr: kernel space address
+ *	@klen: length of address in kernel
+ *	@uaddr: user space address
+ *	@ulen: pointer to user length field
+ *
+ *	The value pointed to by ulen on entry is the buffer length available.
+ *	This is overwritten with the buffer space used. -EINVAL is returned
+ *	if an overlong buffer is specified or a negative buffer size. -EFAULT
+ *	is returned if either the buffer or the length field are not
+ *	accessible.
+ *	After copying the data up to the limit the user specifies, the true
+ *	length of the data is written over the length limit the user
+ *	specified. Zero is returned for a success.
+ */
+ 
+int move_addr_to_user(void *kaddr, int klen, void __user *uaddr, int __user *ulen)
+{
+	int err;
+	int len;
+
+	if((err=get_user(len, ulen)))
+		return err;
+	if(len>klen)
+		len=klen;
+	if(len<0 || len> MAX_SOCK_ADDR)
+		return -EINVAL;
+	if(len)
+	{
+		if(copy_to_user(uaddr,kaddr,len))
+			return -EFAULT;
+	}
+	/*
+	 *	"fromlen shall refer to the value before truncation.."
+	 *			1003.1g
+	 */
+	return __put_user(klen, ulen);
+}
+
+#define SOCKFS_MAGIC 0x534F434B
+
+static kmem_cache_t * sock_inode_cachep;
+
+static struct inode *sock_alloc_inode(struct super_block *sb)
+{
+	struct socket_alloc *ei;
+	ei = (struct socket_alloc *)kmem_cache_alloc(sock_inode_cachep, SLAB_KERNEL);
+	if (!ei)
+		return NULL;
+	init_waitqueue_head(&ei->socket.wait);
+	
+	ei->socket.fasync_list = NULL;
+	ei->socket.state = SS_UNCONNECTED;
+	ei->socket.flags = 0;
+	ei->socket.ops = NULL;
+	ei->socket.sk = NULL;
+	ei->socket.file = NULL;
+	ei->socket.passcred = 0;
+
+	return &ei->vfs_inode;
+}
+
+static void sock_destroy_inode(struct inode *inode)
+{
+	kmem_cache_free(sock_inode_cachep,
+			container_of(inode, struct socket_alloc, vfs_inode));
+}
+
+static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags)
+{
+	struct socket_alloc *ei = (struct socket_alloc *) foo;
+
+	if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
+	    SLAB_CTOR_CONSTRUCTOR)
+		inode_init_once(&ei->vfs_inode);
+}
+ 
+static int init_inodecache(void)
+{
+	sock_inode_cachep = kmem_cache_create("sock_inode_cache",
+				sizeof(struct socket_alloc),
+				0, SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT,
+				init_once, NULL);
+	if (sock_inode_cachep == NULL)
+		return -ENOMEM;
+	return 0;
+}
+
+static struct super_operations sockfs_ops = {
+	.alloc_inode =	sock_alloc_inode,
+	.destroy_inode =sock_destroy_inode,
+	.statfs =	simple_statfs,
+};
+
+static struct super_block *sockfs_get_sb(struct file_system_type *fs_type,
+	int flags, const char *dev_name, void *data)
+{
+	return get_sb_pseudo(fs_type, "socket:", &sockfs_ops, SOCKFS_MAGIC);
+}
+
+static struct vfsmount *sock_mnt;
+
+static struct file_system_type sock_fs_type = {
+	.name =		"sockfs",
+	.get_sb =	sockfs_get_sb,
+	.kill_sb =	kill_anon_super,
+};
+static int sockfs_delete_dentry(struct dentry *dentry)
+{
+	return 1;
+}
+static struct dentry_operations sockfs_dentry_operations = {
+	.d_delete =	sockfs_delete_dentry,
+};
+
+/*
+ *	Obtains the first available file descriptor and sets it up for use.
+ *
+ *	This function creates file structure and maps it to fd space
+ *	of current process. On success it returns file descriptor
+ *	and file struct implicitly stored in sock->file.
+ *	Note that another thread may close file descriptor before we return
+ *	from this function. We use the fact that now we do not refer
+ *	to socket after mapping. If one day we will need it, this
+ *	function will increment ref. count on file by 1.
+ *
+ *	In any case returned fd MAY BE not valid!
+ *	This race condition is unavoidable
+ *	with shared fd spaces, we cannot solve it inside kernel,
+ *	but we take care of internal coherence yet.
+ */
+
+int sock_map_fd(struct socket *sock)
+{
+	int fd;
+	struct qstr this;
+	char name[32];
+
+	/*
+	 *	Find a file descriptor suitable for return to the user. 
+	 */
+
+	fd = get_unused_fd();
+	if (fd >= 0) {
+		struct file *file = get_empty_filp();
+
+		if (!file) {
+			put_unused_fd(fd);
+			fd = -ENFILE;
+			goto out;
+		}
+
+		sprintf(name, "[%lu]", SOCK_INODE(sock)->i_ino);
+		this.name = name;
+		this.len = strlen(name);
+		this.hash = SOCK_INODE(sock)->i_ino;
+
+		file->f_dentry = d_alloc(sock_mnt->mnt_sb->s_root, &this);
+		if (!file->f_dentry) {
+			put_filp(file);
+			put_unused_fd(fd);
+			fd = -ENOMEM;
+			goto out;
+		}
+		file->f_dentry->d_op = &sockfs_dentry_operations;
+		d_add(file->f_dentry, SOCK_INODE(sock));
+		file->f_vfsmnt = mntget(sock_mnt);
+		file->f_mapping = file->f_dentry->d_inode->i_mapping;
+
+		sock->file = file;
+		file->f_op = SOCK_INODE(sock)->i_fop = &socket_file_ops;
+		file->f_mode = FMODE_READ | FMODE_WRITE;
+		file->f_flags = O_RDWR;
+		file->f_pos = 0;
+		fd_install(fd, file);
+	}
+
+out:
+	return fd;
+}
+
+/**
+ *	sockfd_lookup	- 	Go from a file number to its socket slot
+ *	@fd: file handle
+ *	@err: pointer to an error code return
+ *
+ *	The file handle passed in is locked and the socket it is bound
+ *	too is returned. If an error occurs the err pointer is overwritten
+ *	with a negative errno code and NULL is returned. The function checks
+ *	for both invalid handles and passing a handle which is not a socket.
+ *
+ *	On a success the socket object pointer is returned.
+ */
+
+struct socket *sockfd_lookup(int fd, int *err)
+{
+	struct file *file;
+	struct inode *inode;
+	struct socket *sock;
+
+	if (!(file = fget(fd)))
+	{
+		*err = -EBADF;
+		return NULL;
+	}
+
+	inode = file->f_dentry->d_inode;
+	if (!inode->i_sock || !(sock = SOCKET_I(inode)))
+	{
+		*err = -ENOTSOCK;
+		fput(file);
+		return NULL;
+	}
+
+	if (sock->file != file) {
+		printk(KERN_ERR "socki_lookup: socket file changed!\n");
+		sock->file = file;
+	}
+	return sock;
+}
+
+/**
+ *	sock_alloc	-	allocate a socket
+ *	
+ *	Allocate a new inode and socket object. The two are bound together
+ *	and initialised. The socket is then returned. If we are out of inodes
+ *	NULL is returned.
+ */
+
+static struct socket *sock_alloc(void)
+{
+	struct inode * inode;
+	struct socket * sock;
+
+	inode = new_inode(sock_mnt->mnt_sb);
+	if (!inode)
+		return NULL;
+
+	sock = SOCKET_I(inode);
+
+	inode->i_mode = S_IFSOCK|S_IRWXUGO;
+	inode->i_sock = 1;
+	inode->i_uid = current->fsuid;
+	inode->i_gid = current->fsgid;
+
+	get_cpu_var(sockets_in_use)++;
+	put_cpu_var(sockets_in_use);
+	return sock;
+}
+
+/*
+ *	In theory you can't get an open on this inode, but /proc provides
+ *	a back door. Remember to keep it shut otherwise you'll let the
+ *	creepy crawlies in.
+ */
+  
+static int sock_no_open(struct inode *irrelevant, struct file *dontcare)
+{
+	return -ENXIO;
+}
+
+struct file_operations bad_sock_fops = {
+	.owner = THIS_MODULE,
+	.open = sock_no_open,
+};
+
+/**
+ *	sock_release	-	close a socket
+ *	@sock: socket to close
+ *
+ *	The socket is released from the protocol stack if it has a release
+ *	callback, and the inode is then released if the socket is bound to
+ *	an inode not a file. 
+ */
+ 
+void sock_release(struct socket *sock)
+{
+	if (sock->ops) {
+		struct module *owner = sock->ops->owner;
+
+		sock->ops->release(sock);
+		sock->ops = NULL;
+		module_put(owner);
+	}
+
+	if (sock->fasync_list)
+		printk(KERN_ERR "sock_release: fasync list not empty!\n");
+
+	get_cpu_var(sockets_in_use)--;
+	put_cpu_var(sockets_in_use);
+	if (!sock->file) {
+		iput(SOCK_INODE(sock));
+		return;
+	}
+	sock->file=NULL;
+}
+
+static inline int __sock_sendmsg(struct kiocb *iocb, struct socket *sock, 
+				 struct msghdr *msg, size_t size)
+{
+	struct sock_iocb *si = kiocb_to_siocb(iocb);
+	int err;
+
+	si->sock = sock;
+	si->scm = NULL;
+	si->msg = msg;
+	si->size = size;
+
+	err = security_socket_sendmsg(sock, msg, size);
+	if (err)
+		return err;
+
+	return sock->ops->sendmsg(iocb, sock, msg, size);
+}
+
+int sock_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
+{
+	struct kiocb iocb;
+	struct sock_iocb siocb;
+	int ret;
+
+	init_sync_kiocb(&iocb, NULL);
+	iocb.private = &siocb;
+	ret = __sock_sendmsg(&iocb, sock, msg, size);
+	if (-EIOCBQUEUED == ret)
+		ret = wait_on_sync_kiocb(&iocb);
+	return ret;
+}
+
+int kernel_sendmsg(struct socket *sock, struct msghdr *msg,
+		   struct kvec *vec, size_t num, size_t size)
+{
+	mm_segment_t oldfs = get_fs();
+	int result;
+
+	set_fs(KERNEL_DS);
+	/*
+	 * the following is safe, since for compiler definitions of kvec and
+	 * iovec are identical, yielding the same in-core layout and alignment
+	 */
+	msg->msg_iov = (struct iovec *)vec,
+	msg->msg_iovlen = num;
+	result = sock_sendmsg(sock, msg, size);
+	set_fs(oldfs);
+	return result;
+}
+
+static inline int __sock_recvmsg(struct kiocb *iocb, struct socket *sock, 
+				 struct msghdr *msg, size_t size, int flags)
+{
+	int err;
+	struct sock_iocb *si = kiocb_to_siocb(iocb);
+
+	si->sock = sock;
+	si->scm = NULL;
+	si->msg = msg;
+	si->size = size;
+	si->flags = flags;
+
+	err = security_socket_recvmsg(sock, msg, size, flags);
+	if (err)
+		return err;
+
+	return sock->ops->recvmsg(iocb, sock, msg, size, flags);
+}
+
+int sock_recvmsg(struct socket *sock, struct msghdr *msg, 
+		 size_t size, int flags)
+{
+	struct kiocb iocb;
+	struct sock_iocb siocb;
+	int ret;
+
+        init_sync_kiocb(&iocb, NULL);
+	iocb.private = &siocb;
+	ret = __sock_recvmsg(&iocb, sock, msg, size, flags);
+	if (-EIOCBQUEUED == ret)
+		ret = wait_on_sync_kiocb(&iocb);
+	return ret;
+}
+
+int kernel_recvmsg(struct socket *sock, struct msghdr *msg, 
+		   struct kvec *vec, size_t num,
+		   size_t size, int flags)
+{
+	mm_segment_t oldfs = get_fs();
+	int result;
+
+	set_fs(KERNEL_DS);
+	/*
+	 * the following is safe, since for compiler definitions of kvec and
+	 * iovec are identical, yielding the same in-core layout and alignment
+	 */
+	msg->msg_iov = (struct iovec *)vec,
+	msg->msg_iovlen = num;
+	result = sock_recvmsg(sock, msg, size, flags);
+	set_fs(oldfs);
+	return result;
+}
+
+static void sock_aio_dtor(struct kiocb *iocb)
+{
+	kfree(iocb->private);
+}
+
+/*
+ *	Read data from a socket. ubuf is a user mode pointer. We make sure the user
+ *	area ubuf...ubuf+size-1 is writable before asking the protocol.
+ */
+
+static ssize_t sock_aio_read(struct kiocb *iocb, char __user *ubuf,
+			 size_t size, loff_t pos)
+{
+	struct sock_iocb *x, siocb;
+	struct socket *sock;
+	int flags;
+
+	if (pos != 0)
+		return -ESPIPE;
+	if (size==0)		/* Match SYS5 behaviour */
+		return 0;
+
+	if (is_sync_kiocb(iocb))
+		x = &siocb;
+	else {
+		x = kmalloc(sizeof(struct sock_iocb), GFP_KERNEL);
+		if (!x)
+			return -ENOMEM;
+		iocb->ki_dtor = sock_aio_dtor;
+	}
+	iocb->private = x;
+	x->kiocb = iocb;
+	sock = SOCKET_I(iocb->ki_filp->f_dentry->d_inode); 
+
+	x->async_msg.msg_name = NULL;
+	x->async_msg.msg_namelen = 0;
+	x->async_msg.msg_iov = &x->async_iov;
+	x->async_msg.msg_iovlen = 1;
+	x->async_msg.msg_control = NULL;
+	x->async_msg.msg_controllen = 0;
+	x->async_iov.iov_base = ubuf;
+	x->async_iov.iov_len = size;
+	flags = !(iocb->ki_filp->f_flags & O_NONBLOCK) ? 0 : MSG_DONTWAIT;
+
+	return __sock_recvmsg(iocb, sock, &x->async_msg, size, flags);
+}
+
+
+/*
+ *	Write data to a socket. We verify that the user area ubuf..ubuf+size-1
+ *	is readable by the user process.
+ */
+
+static ssize_t sock_aio_write(struct kiocb *iocb, const char __user *ubuf,
+			  size_t size, loff_t pos)
+{
+	struct sock_iocb *x, siocb;
+	struct socket *sock;
+	
+	if (pos != 0)
+		return -ESPIPE;
+	if(size==0)		/* Match SYS5 behaviour */
+		return 0;
+
+	if (is_sync_kiocb(iocb))
+		x = &siocb;
+	else {
+		x = kmalloc(sizeof(struct sock_iocb), GFP_KERNEL);
+		if (!x)
+			return -ENOMEM;
+		iocb->ki_dtor = sock_aio_dtor;
+	}
+	iocb->private = x;
+	x->kiocb = iocb;
+	sock = SOCKET_I(iocb->ki_filp->f_dentry->d_inode); 
+
+	x->async_msg.msg_name = NULL;
+	x->async_msg.msg_namelen = 0;
+	x->async_msg.msg_iov = &x->async_iov;
+	x->async_msg.msg_iovlen = 1;
+	x->async_msg.msg_control = NULL;
+	x->async_msg.msg_controllen = 0;
+	x->async_msg.msg_flags = !(iocb->ki_filp->f_flags & O_NONBLOCK) ? 0 : MSG_DONTWAIT;
+	if (sock->type == SOCK_SEQPACKET)
+		x->async_msg.msg_flags |= MSG_EOR;
+	x->async_iov.iov_base = (void __user *)ubuf;
+	x->async_iov.iov_len = size;
+	
+	return __sock_sendmsg(iocb, sock, &x->async_msg, size);
+}
+
+ssize_t sock_sendpage(struct file *file, struct page *page,
+		      int offset, size_t size, loff_t *ppos, int more)
+{
+	struct socket *sock;
+	int flags;
+
+	sock = SOCKET_I(file->f_dentry->d_inode);
+
+	flags = !(file->f_flags & O_NONBLOCK) ? 0 : MSG_DONTWAIT;
+	if (more)
+		flags |= MSG_MORE;
+
+	return sock->ops->sendpage(sock, page, offset, size, flags);
+}
+
+static int sock_readv_writev(int type, struct inode * inode,
+			     struct file * file, const struct iovec * iov,
+			     long count, size_t size)
+{
+	struct msghdr msg;
+	struct socket *sock;
+
+	sock = SOCKET_I(inode);
+
+	msg.msg_name = NULL;
+	msg.msg_namelen = 0;
+	msg.msg_control = NULL;
+	msg.msg_controllen = 0;
+	msg.msg_iov = (struct iovec *) iov;
+	msg.msg_iovlen = count;
+	msg.msg_flags = (file->f_flags & O_NONBLOCK) ? MSG_DONTWAIT : 0;
+
+	/* read() does a VERIFY_WRITE */
+	if (type == VERIFY_WRITE)
+		return sock_recvmsg(sock, &msg, size, msg.msg_flags);
+
+	if (sock->type == SOCK_SEQPACKET)
+		msg.msg_flags |= MSG_EOR;
+
+	return sock_sendmsg(sock, &msg, size);
+}
+
+static ssize_t sock_readv(struct file *file, const struct iovec *vector,
+			  unsigned long count, loff_t *ppos)
+{
+	size_t tot_len = 0;
+	int i;
+        for (i = 0 ; i < count ; i++)
+                tot_len += vector[i].iov_len;
+	return sock_readv_writev(VERIFY_WRITE, file->f_dentry->d_inode,
+				 file, vector, count, tot_len);
+}
+	
+static ssize_t sock_writev(struct file *file, const struct iovec *vector,
+			   unsigned long count, loff_t *ppos)
+{
+	size_t tot_len = 0;
+	int i;
+        for (i = 0 ; i < count ; i++)
+                tot_len += vector[i].iov_len;
+	return sock_readv_writev(VERIFY_READ, file->f_dentry->d_inode,
+				 file, vector, count, tot_len);
+}
+
+
+/*
+ * Atomic setting of ioctl hooks to avoid race
+ * with module unload.
+ */
+
+static DECLARE_MUTEX(br_ioctl_mutex);
+static int (*br_ioctl_hook)(unsigned int cmd, void __user *arg) = NULL;
+
+void brioctl_set(int (*hook)(unsigned int, void __user *))
+{
+	down(&br_ioctl_mutex);
+	br_ioctl_hook = hook;
+	up(&br_ioctl_mutex);
+}
+EXPORT_SYMBOL(brioctl_set);
+
+static DECLARE_MUTEX(vlan_ioctl_mutex);
+static int (*vlan_ioctl_hook)(void __user *arg);
+
+void vlan_ioctl_set(int (*hook)(void __user *))
+{
+	down(&vlan_ioctl_mutex);
+	vlan_ioctl_hook = hook;
+	up(&vlan_ioctl_mutex);
+}
+EXPORT_SYMBOL(vlan_ioctl_set);
+
+static DECLARE_MUTEX(dlci_ioctl_mutex);
+static int (*dlci_ioctl_hook)(unsigned int, void __user *);
+
+void dlci_ioctl_set(int (*hook)(unsigned int, void __user *))
+{
+	down(&dlci_ioctl_mutex);
+	dlci_ioctl_hook = hook;
+	up(&dlci_ioctl_mutex);
+}
+EXPORT_SYMBOL(dlci_ioctl_set);
+
+/*
+ *	With an ioctl, arg may well be a user mode pointer, but we don't know
+ *	what to do with it - that's up to the protocol still.
+ */
+
+static long sock_ioctl(struct file *file, unsigned cmd, unsigned long arg)
+{
+	struct socket *sock;
+	void __user *argp = (void __user *)arg;
+	int pid, err;
+
+	sock = SOCKET_I(file->f_dentry->d_inode);
+	if (cmd >= SIOCDEVPRIVATE && cmd <= (SIOCDEVPRIVATE + 15)) {
+		err = dev_ioctl(cmd, argp);
+	} else
+#ifdef WIRELESS_EXT
+	if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) {
+		err = dev_ioctl(cmd, argp);
+	} else
+#endif	/* WIRELESS_EXT */
+	switch (cmd) {
+		case FIOSETOWN:
+		case SIOCSPGRP:
+			err = -EFAULT;
+			if (get_user(pid, (int __user *)argp))
+				break;
+			err = f_setown(sock->file, pid, 1);
+			break;
+		case FIOGETOWN:
+		case SIOCGPGRP:
+			err = put_user(sock->file->f_owner.pid, (int __user *)argp);
+			break;
+		case SIOCGIFBR:
+		case SIOCSIFBR:
+		case SIOCBRADDBR:
+		case SIOCBRDELBR:
+			err = -ENOPKG;
+			if (!br_ioctl_hook)
+				request_module("bridge");
+
+			down(&br_ioctl_mutex);
+			if (br_ioctl_hook) 
+				err = br_ioctl_hook(cmd, argp);
+			up(&br_ioctl_mutex);
+			break;
+		case SIOCGIFVLAN:
+		case SIOCSIFVLAN:
+			err = -ENOPKG;
+			if (!vlan_ioctl_hook)
+				request_module("8021q");
+
+			down(&vlan_ioctl_mutex);
+			if (vlan_ioctl_hook)
+				err = vlan_ioctl_hook(argp);
+			up(&vlan_ioctl_mutex);
+			break;
+		case SIOCGIFDIVERT:
+		case SIOCSIFDIVERT:
+		/* Convert this to call through a hook */
+			err = divert_ioctl(cmd, argp);
+			break;
+		case SIOCADDDLCI:
+		case SIOCDELDLCI:
+			err = -ENOPKG;
+			if (!dlci_ioctl_hook)
+				request_module("dlci");
+
+			if (dlci_ioctl_hook) {
+				down(&dlci_ioctl_mutex);
+				err = dlci_ioctl_hook(cmd, argp);
+				up(&dlci_ioctl_mutex);
+			}
+			break;
+		default:
+			err = sock->ops->ioctl(sock, cmd, arg);
+			break;
+	}
+	return err;
+}
+
+int sock_create_lite(int family, int type, int protocol, struct socket **res)
+{
+	int err;
+	struct socket *sock = NULL;
+	
+	err = security_socket_create(family, type, protocol, 1);
+	if (err)
+		goto out;
+
+	sock = sock_alloc();
+	if (!sock) {
+		err = -ENOMEM;
+		goto out;
+	}
+
+	security_socket_post_create(sock, family, type, protocol, 1);
+	sock->type = type;
+out:
+	*res = sock;
+	return err;
+}
+
+/* No kernel lock held - perfect */
+static unsigned int sock_poll(struct file *file, poll_table * wait)
+{
+	struct socket *sock;
+
+	/*
+	 *	We can't return errors to poll, so it's either yes or no. 
+	 */
+	sock = SOCKET_I(file->f_dentry->d_inode);
+	return sock->ops->poll(file, sock, wait);
+}
+
+static int sock_mmap(struct file * file, struct vm_area_struct * vma)
+{
+	struct socket *sock = SOCKET_I(file->f_dentry->d_inode);
+
+	return sock->ops->mmap(file, sock, vma);
+}
+
+int sock_close(struct inode *inode, struct file *filp)
+{
+	/*
+	 *	It was possible the inode is NULL we were 
+	 *	closing an unfinished socket. 
+	 */
+
+	if (!inode)
+	{
+		printk(KERN_DEBUG "sock_close: NULL inode\n");
+		return 0;
+	}
+	sock_fasync(-1, filp, 0);
+	sock_release(SOCKET_I(inode));
+	return 0;
+}
+
+/*
+ *	Update the socket async list
+ *
+ *	Fasync_list locking strategy.
+ *
+ *	1. fasync_list is modified only under process context socket lock
+ *	   i.e. under semaphore.
+ *	2. fasync_list is used under read_lock(&sk->sk_callback_lock)
+ *	   or under socket lock.
+ *	3. fasync_list can be used from softirq context, so that
+ *	   modification under socket lock have to be enhanced with
+ *	   write_lock_bh(&sk->sk_callback_lock).
+ *							--ANK (990710)
+ */
+
+static int sock_fasync(int fd, struct file *filp, int on)
+{
+	struct fasync_struct *fa, *fna=NULL, **prev;
+	struct socket *sock;
+	struct sock *sk;
+
+	if (on)
+	{
+		fna=(struct fasync_struct *)kmalloc(sizeof(struct fasync_struct), GFP_KERNEL);
+		if(fna==NULL)
+			return -ENOMEM;
+	}
+
+	sock = SOCKET_I(filp->f_dentry->d_inode);
+
+	if ((sk=sock->sk) == NULL) {
+		if (fna)
+			kfree(fna);
+		return -EINVAL;
+	}
+
+	lock_sock(sk);
+
+	prev=&(sock->fasync_list);
+
+	for (fa=*prev; fa!=NULL; prev=&fa->fa_next,fa=*prev)
+		if (fa->fa_file==filp)
+			break;
+
+	if(on)
+	{
+		if(fa!=NULL)
+		{
+			write_lock_bh(&sk->sk_callback_lock);
+			fa->fa_fd=fd;
+			write_unlock_bh(&sk->sk_callback_lock);
+
+			kfree(fna);
+			goto out;
+		}
+		fna->fa_file=filp;
+		fna->fa_fd=fd;
+		fna->magic=FASYNC_MAGIC;
+		fna->fa_next=sock->fasync_list;
+		write_lock_bh(&sk->sk_callback_lock);
+		sock->fasync_list=fna;
+		write_unlock_bh(&sk->sk_callback_lock);
+	}
+	else
+	{
+		if (fa!=NULL)
+		{
+			write_lock_bh(&sk->sk_callback_lock);
+			*prev=fa->fa_next;
+			write_unlock_bh(&sk->sk_callback_lock);
+			kfree(fa);
+		}
+	}
+
+out:
+	release_sock(sock->sk);
+	return 0;
+}
+
+/* This function may be called only under socket lock or callback_lock */
+
+int sock_wake_async(struct socket *sock, int how, int band)
+{
+	if (!sock || !sock->fasync_list)
+		return -1;
+	switch (how)
+	{
+	case 1:
+		
+		if (test_bit(SOCK_ASYNC_WAITDATA, &sock->flags))
+			break;
+		goto call_kill;
+	case 2:
+		if (!test_and_clear_bit(SOCK_ASYNC_NOSPACE, &sock->flags))
+			break;
+		/* fall through */
+	case 0:
+	call_kill:
+		__kill_fasync(sock->fasync_list, SIGIO, band);
+		break;
+	case 3:
+		__kill_fasync(sock->fasync_list, SIGURG, band);
+	}
+	return 0;
+}
+
+static int __sock_create(int family, int type, int protocol, struct socket **res, int kern)
+{
+	int err;
+	struct socket *sock;
+
+	/*
+	 *	Check protocol is in range
+	 */
+	if (family < 0 || family >= NPROTO)
+		return -EAFNOSUPPORT;
+	if (type < 0 || type >= SOCK_MAX)
+		return -EINVAL;
+
+	/* Compatibility.
+
+	   This uglymoron is moved from INET layer to here to avoid
+	   deadlock in module load.
+	 */
+	if (family == PF_INET && type == SOCK_PACKET) {
+		static int warned; 
+		if (!warned) {
+			warned = 1;
+			printk(KERN_INFO "%s uses obsolete (PF_INET,SOCK_PACKET)\n", current->comm);
+		}
+		family = PF_PACKET;
+	}
+
+	err = security_socket_create(family, type, protocol, kern);
+	if (err)
+		return err;
+		
+#if defined(CONFIG_KMOD)
+	/* Attempt to load a protocol module if the find failed. 
+	 * 
+	 * 12/09/1996 Marcin: But! this makes REALLY only sense, if the user 
+	 * requested real, full-featured networking support upon configuration.
+	 * Otherwise module support will break!
+	 */
+	if (net_families[family]==NULL)
+	{
+		request_module("net-pf-%d",family);
+	}
+#endif
+
+	net_family_read_lock();
+	if (net_families[family] == NULL) {
+		err = -EAFNOSUPPORT;
+		goto out;
+	}
+
+/*
+ *	Allocate the socket and allow the family to set things up. if
+ *	the protocol is 0, the family is instructed to select an appropriate
+ *	default.
+ */
+
+	if (!(sock = sock_alloc())) {
+		printk(KERN_WARNING "socket: no more sockets\n");
+		err = -ENFILE;		/* Not exactly a match, but its the
+					   closest posix thing */
+		goto out;
+	}
+
+	sock->type  = type;
+
+	/*
+	 * We will call the ->create function, that possibly is in a loadable
+	 * module, so we have to bump that loadable module refcnt first.
+	 */
+	err = -EAFNOSUPPORT;
+	if (!try_module_get(net_families[family]->owner))
+		goto out_release;
+
+	if ((err = net_families[family]->create(sock, protocol)) < 0)
+		goto out_module_put;
+	/*
+	 * Now to bump the refcnt of the [loadable] module that owns this
+	 * socket at sock_release time we decrement its refcnt.
+	 */
+	if (!try_module_get(sock->ops->owner)) {
+		sock->ops = NULL;
+		goto out_module_put;
+	}
+	/*
+	 * Now that we're done with the ->create function, the [loadable]
+	 * module can have its refcnt decremented
+	 */
+	module_put(net_families[family]->owner);
+	*res = sock;
+	security_socket_post_create(sock, family, type, protocol, kern);
+
+out:
+	net_family_read_unlock();
+	return err;
+out_module_put:
+	module_put(net_families[family]->owner);
+out_release:
+	sock_release(sock);
+	goto out;
+}
+
+int sock_create(int family, int type, int protocol, struct socket **res)
+{
+	return __sock_create(family, type, protocol, res, 0);
+}
+
+int sock_create_kern(int family, int type, int protocol, struct socket **res)
+{
+	return __sock_create(family, type, protocol, res, 1);
+}
+
+asmlinkage long sys_socket(int family, int type, int protocol)
+{
+	int retval;
+	struct socket *sock;
+
+	retval = sock_create(family, type, protocol, &sock);
+	if (retval < 0)
+		goto out;
+
+	retval = sock_map_fd(sock);
+	if (retval < 0)
+		goto out_release;
+
+out:
+	/* It may be already another descriptor 8) Not kernel problem. */
+	return retval;
+
+out_release:
+	sock_release(sock);
+	return retval;
+}
+
+/*
+ *	Create a pair of connected sockets.
+ */
+
+asmlinkage long sys_socketpair(int family, int type, int protocol, int __user *usockvec)
+{
+	struct socket *sock1, *sock2;
+	int fd1, fd2, err;
+
+	/*
+	 * Obtain the first socket and check if the underlying protocol
+	 * supports the socketpair call.
+	 */
+
+	err = sock_create(family, type, protocol, &sock1);
+	if (err < 0)
+		goto out;
+
+	err = sock_create(family, type, protocol, &sock2);
+	if (err < 0)
+		goto out_release_1;
+
+	err = sock1->ops->socketpair(sock1, sock2);
+	if (err < 0) 
+		goto out_release_both;
+
+	fd1 = fd2 = -1;
+
+	err = sock_map_fd(sock1);
+	if (err < 0)
+		goto out_release_both;
+	fd1 = err;
+
+	err = sock_map_fd(sock2);
+	if (err < 0)
+		goto out_close_1;
+	fd2 = err;
+
+	/* fd1 and fd2 may be already another descriptors.
+	 * Not kernel problem.
+	 */
+
+	err = put_user(fd1, &usockvec[0]); 
+	if (!err)
+		err = put_user(fd2, &usockvec[1]);
+	if (!err)
+		return 0;
+
+	sys_close(fd2);
+	sys_close(fd1);
+	return err;
+
+out_close_1:
+        sock_release(sock2);
+	sys_close(fd1);
+	return err;
+
+out_release_both:
+        sock_release(sock2);
+out_release_1:
+        sock_release(sock1);
+out:
+	return err;
+}
+
+
+/*
+ *	Bind a name to a socket. Nothing much to do here since it's
+ *	the protocol's responsibility to handle the local address.
+ *
+ *	We move the socket address to kernel space before we call
+ *	the protocol layer (having also checked the address is ok).
+ */
+
+asmlinkage long sys_bind(int fd, struct sockaddr __user *umyaddr, int addrlen)
+{
+	struct socket *sock;
+	char address[MAX_SOCK_ADDR];
+	int err;
+
+	if((sock = sockfd_lookup(fd,&err))!=NULL)
+	{
+		if((err=move_addr_to_kernel(umyaddr,addrlen,address))>=0) {
+			err = security_socket_bind(sock, (struct sockaddr *)address, addrlen);
+			if (err) {
+				sockfd_put(sock);
+				return err;
+			}
+			err = sock->ops->bind(sock, (struct sockaddr *)address, addrlen);
+		}
+		sockfd_put(sock);
+	}			
+	return err;
+}
+
+
+/*
+ *	Perform a listen. Basically, we allow the protocol to do anything
+ *	necessary for a listen, and if that works, we mark the socket as
+ *	ready for listening.
+ */
+
+int sysctl_somaxconn = SOMAXCONN;
+
+asmlinkage long sys_listen(int fd, int backlog)
+{
+	struct socket *sock;
+	int err;
+	
+	if ((sock = sockfd_lookup(fd, &err)) != NULL) {
+		if ((unsigned) backlog > sysctl_somaxconn)
+			backlog = sysctl_somaxconn;
+
+		err = security_socket_listen(sock, backlog);
+		if (err) {
+			sockfd_put(sock);
+			return err;
+		}
+
+		err=sock->ops->listen(sock, backlog);
+		sockfd_put(sock);
+	}
+	return err;
+}
+
+
+/*
+ *	For accept, we attempt to create a new socket, set up the link
+ *	with the client, wake up the client, then return the new
+ *	connected fd. We collect the address of the connector in kernel
+ *	space and move it to user at the very end. This is unclean because
+ *	we open the socket then return an error.
+ *
+ *	1003.1g adds the ability to recvmsg() to query connection pending
+ *	status to recvmsg. We need to add that support in a way thats
+ *	clean when we restucture accept also.
+ */
+
+asmlinkage long sys_accept(int fd, struct sockaddr __user *upeer_sockaddr, int __user *upeer_addrlen)
+{
+	struct socket *sock, *newsock;
+	int err, len;
+	char address[MAX_SOCK_ADDR];
+
+	sock = sockfd_lookup(fd, &err);
+	if (!sock)
+		goto out;
+
+	err = -ENFILE;
+	if (!(newsock = sock_alloc())) 
+		goto out_put;
+
+	newsock->type = sock->type;
+	newsock->ops = sock->ops;
+
+	err = security_socket_accept(sock, newsock);
+	if (err)
+		goto out_release;
+
+	/*
+	 * We don't need try_module_get here, as the listening socket (sock)
+	 * has the protocol module (sock->ops->owner) held.
+	 */
+	__module_get(newsock->ops->owner);
+
+	err = sock->ops->accept(sock, newsock, sock->file->f_flags);
+	if (err < 0)
+		goto out_release;
+
+	if (upeer_sockaddr) {
+		if(newsock->ops->getname(newsock, (struct sockaddr *)address, &len, 2)<0) {
+			err = -ECONNABORTED;
+			goto out_release;
+		}
+		err = move_addr_to_user(address, len, upeer_sockaddr, upeer_addrlen);
+		if (err < 0)
+			goto out_release;
+	}
+
+	/* File flags are not inherited via accept() unlike another OSes. */
+
+	if ((err = sock_map_fd(newsock)) < 0)
+		goto out_release;
+
+	security_socket_post_accept(sock, newsock);
+
+out_put:
+	sockfd_put(sock);
+out:
+	return err;
+out_release:
+	sock_release(newsock);
+	goto out_put;
+}
+
+
+/*
+ *	Attempt to connect to a socket with the server address.  The address
+ *	is in user space so we verify it is OK and move it to kernel space.
+ *
+ *	For 1003.1g we need to add clean support for a bind to AF_UNSPEC to
+ *	break bindings
+ *
+ *	NOTE: 1003.1g draft 6.3 is broken with respect to AX.25/NetROM and
+ *	other SEQPACKET protocols that take time to connect() as it doesn't
+ *	include the -EINPROGRESS status for such sockets.
+ */
+
+asmlinkage long sys_connect(int fd, struct sockaddr __user *uservaddr, int addrlen)
+{
+	struct socket *sock;
+	char address[MAX_SOCK_ADDR];
+	int err;
+
+	sock = sockfd_lookup(fd, &err);
+	if (!sock)
+		goto out;
+	err = move_addr_to_kernel(uservaddr, addrlen, address);
+	if (err < 0)
+		goto out_put;
+
+	err = security_socket_connect(sock, (struct sockaddr *)address, addrlen);
+	if (err)
+		goto out_put;
+
+	err = sock->ops->connect(sock, (struct sockaddr *) address, addrlen,
+				 sock->file->f_flags);
+out_put:
+	sockfd_put(sock);
+out:
+	return err;
+}
+
+/*
+ *	Get the local address ('name') of a socket object. Move the obtained
+ *	name to user space.
+ */
+
+asmlinkage long sys_getsockname(int fd, struct sockaddr __user *usockaddr, int __user *usockaddr_len)
+{
+	struct socket *sock;
+	char address[MAX_SOCK_ADDR];
+	int len, err;
+	
+	sock = sockfd_lookup(fd, &err);
+	if (!sock)
+		goto out;
+
+	err = security_socket_getsockname(sock);
+	if (err)
+		goto out_put;
+
+	err = sock->ops->getname(sock, (struct sockaddr *)address, &len, 0);
+	if (err)
+		goto out_put;
+	err = move_addr_to_user(address, len, usockaddr, usockaddr_len);
+
+out_put:
+	sockfd_put(sock);
+out:
+	return err;
+}
+
+/*
+ *	Get the remote address ('name') of a socket object. Move the obtained
+ *	name to user space.
+ */
+
+asmlinkage long sys_getpeername(int fd, struct sockaddr __user *usockaddr, int __user *usockaddr_len)
+{
+	struct socket *sock;
+	char address[MAX_SOCK_ADDR];
+	int len, err;
+
+	if ((sock = sockfd_lookup(fd, &err))!=NULL)
+	{
+		err = security_socket_getpeername(sock);
+		if (err) {
+			sockfd_put(sock);
+			return err;
+		}
+
+		err = sock->ops->getname(sock, (struct sockaddr *)address, &len, 1);
+		if (!err)
+			err=move_addr_to_user(address,len, usockaddr, usockaddr_len);
+		sockfd_put(sock);
+	}
+	return err;
+}
+
+/*
+ *	Send a datagram to a given address. We move the address into kernel
+ *	space and check the user space data area is readable before invoking
+ *	the protocol.
+ */
+
+asmlinkage long sys_sendto(int fd, void __user * buff, size_t len, unsigned flags,
+			   struct sockaddr __user *addr, int addr_len)
+{
+	struct socket *sock;
+	char address[MAX_SOCK_ADDR];
+	int err;
+	struct msghdr msg;
+	struct iovec iov;
+	
+	sock = sockfd_lookup(fd, &err);
+	if (!sock)
+		goto out;
+	iov.iov_base=buff;
+	iov.iov_len=len;
+	msg.msg_name=NULL;
+	msg.msg_iov=&iov;
+	msg.msg_iovlen=1;
+	msg.msg_control=NULL;
+	msg.msg_controllen=0;
+	msg.msg_namelen=0;
+	if(addr)
+	{
+		err = move_addr_to_kernel(addr, addr_len, address);
+		if (err < 0)
+			goto out_put;
+		msg.msg_name=address;
+		msg.msg_namelen=addr_len;
+	}
+	if (sock->file->f_flags & O_NONBLOCK)
+		flags |= MSG_DONTWAIT;
+	msg.msg_flags = flags;
+	err = sock_sendmsg(sock, &msg, len);
+
+out_put:		
+	sockfd_put(sock);
+out:
+	return err;
+}
+
+/*
+ *	Send a datagram down a socket. 
+ */
+
+asmlinkage long sys_send(int fd, void __user * buff, size_t len, unsigned flags)
+{
+	return sys_sendto(fd, buff, len, flags, NULL, 0);
+}
+
+/*
+ *	Receive a frame from the socket and optionally record the address of the 
+ *	sender. We verify the buffers are writable and if needed move the
+ *	sender address from kernel to user space.
+ */
+
+asmlinkage long sys_recvfrom(int fd, void __user * ubuf, size_t size, unsigned flags,
+			     struct sockaddr __user *addr, int __user *addr_len)
+{
+	struct socket *sock;
+	struct iovec iov;
+	struct msghdr msg;
+	char address[MAX_SOCK_ADDR];
+	int err,err2;
+
+	sock = sockfd_lookup(fd, &err);
+	if (!sock)
+		goto out;
+
+	msg.msg_control=NULL;
+	msg.msg_controllen=0;
+	msg.msg_iovlen=1;
+	msg.msg_iov=&iov;
+	iov.iov_len=size;
+	iov.iov_base=ubuf;
+	msg.msg_name=address;
+	msg.msg_namelen=MAX_SOCK_ADDR;
+	if (sock->file->f_flags & O_NONBLOCK)
+		flags |= MSG_DONTWAIT;
+	err=sock_recvmsg(sock, &msg, size, flags);
+
+	if(err >= 0 && addr != NULL)
+	{
+		err2=move_addr_to_user(address, msg.msg_namelen, addr, addr_len);
+		if(err2<0)
+			err=err2;
+	}
+	sockfd_put(sock);			
+out:
+	return err;
+}
+
+/*
+ *	Receive a datagram from a socket. 
+ */
+
+asmlinkage long sys_recv(int fd, void __user * ubuf, size_t size, unsigned flags)
+{
+	return sys_recvfrom(fd, ubuf, size, flags, NULL, NULL);
+}
+
+/*
+ *	Set a socket option. Because we don't know the option lengths we have
+ *	to pass the user mode parameter for the protocols to sort out.
+ */
+
+asmlinkage long sys_setsockopt(int fd, int level, int optname, char __user *optval, int optlen)
+{
+	int err;
+	struct socket *sock;
+
+	if (optlen < 0)
+		return -EINVAL;
+			
+	if ((sock = sockfd_lookup(fd, &err))!=NULL)
+	{
+		err = security_socket_setsockopt(sock,level,optname);
+		if (err) {
+			sockfd_put(sock);
+			return err;
+		}
+
+		if (level == SOL_SOCKET)
+			err=sock_setsockopt(sock,level,optname,optval,optlen);
+		else
+			err=sock->ops->setsockopt(sock, level, optname, optval, optlen);
+		sockfd_put(sock);
+	}
+	return err;
+}
+
+/*
+ *	Get a socket option. Because we don't know the option lengths we have
+ *	to pass a user mode parameter for the protocols to sort out.
+ */
+
+asmlinkage long sys_getsockopt(int fd, int level, int optname, char __user *optval, int __user *optlen)
+{
+	int err;
+	struct socket *sock;
+
+	if ((sock = sockfd_lookup(fd, &err))!=NULL)
+	{
+		err = security_socket_getsockopt(sock, level, 
+							   optname);
+		if (err) {
+			sockfd_put(sock);
+			return err;
+		}
+
+		if (level == SOL_SOCKET)
+			err=sock_getsockopt(sock,level,optname,optval,optlen);
+		else
+			err=sock->ops->getsockopt(sock, level, optname, optval, optlen);
+		sockfd_put(sock);
+	}
+	return err;
+}
+
+
+/*
+ *	Shutdown a socket.
+ */
+
+asmlinkage long sys_shutdown(int fd, int how)
+{
+	int err;
+	struct socket *sock;
+
+	if ((sock = sockfd_lookup(fd, &err))!=NULL)
+	{
+		err = security_socket_shutdown(sock, how);
+		if (err) {
+			sockfd_put(sock);
+			return err;
+		}
+				
+		err=sock->ops->shutdown(sock, how);
+		sockfd_put(sock);
+	}
+	return err;
+}
+
+/* A couple of helpful macros for getting the address of the 32/64 bit 
+ * fields which are the same type (int / unsigned) on our platforms.
+ */
+#define COMPAT_MSG(msg, member)	((MSG_CMSG_COMPAT & flags) ? &msg##_compat->member : &msg->member)
+#define COMPAT_NAMELEN(msg)	COMPAT_MSG(msg, msg_namelen)
+#define COMPAT_FLAGS(msg)	COMPAT_MSG(msg, msg_flags)
+
+
+/*
+ *	BSD sendmsg interface
+ */
+
+asmlinkage long sys_sendmsg(int fd, struct msghdr __user *msg, unsigned flags)
+{
+	struct compat_msghdr __user *msg_compat = (struct compat_msghdr __user *)msg;
+	struct socket *sock;
+	char address[MAX_SOCK_ADDR];
+	struct iovec iovstack[UIO_FASTIOV], *iov = iovstack;
+	unsigned char ctl[sizeof(struct cmsghdr) + 20];	/* 20 is size of ipv6_pktinfo */
+	unsigned char *ctl_buf = ctl;
+	struct msghdr msg_sys;
+	int err, ctl_len, iov_size, total_len;
+	
+	err = -EFAULT;
+	if (MSG_CMSG_COMPAT & flags) {
+		if (get_compat_msghdr(&msg_sys, msg_compat))
+			return -EFAULT;
+	} else if (copy_from_user(&msg_sys, msg, sizeof(struct msghdr)))
+		return -EFAULT;
+
+	sock = sockfd_lookup(fd, &err);
+	if (!sock) 
+		goto out;
+
+	/* do not move before msg_sys is valid */
+	err = -EMSGSIZE;
+	if (msg_sys.msg_iovlen > UIO_MAXIOV)
+		goto out_put;
+
+	/* Check whether to allocate the iovec area*/
+	err = -ENOMEM;
+	iov_size = msg_sys.msg_iovlen * sizeof(struct iovec);
+	if (msg_sys.msg_iovlen > UIO_FASTIOV) {
+		iov = sock_kmalloc(sock->sk, iov_size, GFP_KERNEL);
+		if (!iov)
+			goto out_put;
+	}
+
+	/* This will also move the address data into kernel space */
+	if (MSG_CMSG_COMPAT & flags) {
+		err = verify_compat_iovec(&msg_sys, iov, address, VERIFY_READ);
+	} else
+		err = verify_iovec(&msg_sys, iov, address, VERIFY_READ);
+	if (err < 0) 
+		goto out_freeiov;
+	total_len = err;
+
+	err = -ENOBUFS;
+
+	if (msg_sys.msg_controllen > INT_MAX)
+		goto out_freeiov;
+	ctl_len = msg_sys.msg_controllen; 
+	if ((MSG_CMSG_COMPAT & flags) && ctl_len) {
+		err = cmsghdr_from_user_compat_to_kern(&msg_sys, ctl, sizeof(ctl));
+		if (err)
+			goto out_freeiov;
+		ctl_buf = msg_sys.msg_control;
+	} else if (ctl_len) {
+		if (ctl_len > sizeof(ctl))
+		{
+			ctl_buf = sock_kmalloc(sock->sk, ctl_len, GFP_KERNEL);
+			if (ctl_buf == NULL) 
+				goto out_freeiov;
+		}
+		err = -EFAULT;
+		/*
+		 * Careful! Before this, msg_sys.msg_control contains a user pointer.
+		 * Afterwards, it will be a kernel pointer. Thus the compiler-assisted
+		 * checking falls down on this.
+		 */
+		if (copy_from_user(ctl_buf, (void __user *) msg_sys.msg_control, ctl_len))
+			goto out_freectl;
+		msg_sys.msg_control = ctl_buf;
+	}
+	msg_sys.msg_flags = flags;
+
+	if (sock->file->f_flags & O_NONBLOCK)
+		msg_sys.msg_flags |= MSG_DONTWAIT;
+	err = sock_sendmsg(sock, &msg_sys, total_len);
+
+out_freectl:
+	if (ctl_buf != ctl)    
+		sock_kfree_s(sock->sk, ctl_buf, ctl_len);
+out_freeiov:
+	if (iov != iovstack)
+		sock_kfree_s(sock->sk, iov, iov_size);
+out_put:
+	sockfd_put(sock);
+out:       
+	return err;
+}
+
+/*
+ *	BSD recvmsg interface
+ */
+
+asmlinkage long sys_recvmsg(int fd, struct msghdr __user *msg, unsigned int flags)
+{
+	struct compat_msghdr __user *msg_compat = (struct compat_msghdr __user *)msg;
+	struct socket *sock;
+	struct iovec iovstack[UIO_FASTIOV];
+	struct iovec *iov=iovstack;
+	struct msghdr msg_sys;
+	unsigned long cmsg_ptr;
+	int err, iov_size, total_len, len;
+
+	/* kernel mode address */
+	char addr[MAX_SOCK_ADDR];
+
+	/* user mode address pointers */
+	struct sockaddr __user *uaddr;
+	int __user *uaddr_len;
+	
+	if (MSG_CMSG_COMPAT & flags) {
+		if (get_compat_msghdr(&msg_sys, msg_compat))
+			return -EFAULT;
+	} else
+		if (copy_from_user(&msg_sys,msg,sizeof(struct msghdr)))
+			return -EFAULT;
+
+	sock = sockfd_lookup(fd, &err);
+	if (!sock)
+		goto out;
+
+	err = -EMSGSIZE;
+	if (msg_sys.msg_iovlen > UIO_MAXIOV)
+		goto out_put;
+	
+	/* Check whether to allocate the iovec area*/
+	err = -ENOMEM;
+	iov_size = msg_sys.msg_iovlen * sizeof(struct iovec);
+	if (msg_sys.msg_iovlen > UIO_FASTIOV) {
+		iov = sock_kmalloc(sock->sk, iov_size, GFP_KERNEL);
+		if (!iov)
+			goto out_put;
+	}
+
+	/*
+	 *	Save the user-mode address (verify_iovec will change the
+	 *	kernel msghdr to use the kernel address space)
+	 */
+	 
+	uaddr = (void __user *) msg_sys.msg_name;
+	uaddr_len = COMPAT_NAMELEN(msg);
+	if (MSG_CMSG_COMPAT & flags) {
+		err = verify_compat_iovec(&msg_sys, iov, addr, VERIFY_WRITE);
+	} else
+		err = verify_iovec(&msg_sys, iov, addr, VERIFY_WRITE);
+	if (err < 0)
+		goto out_freeiov;
+	total_len=err;
+
+	cmsg_ptr = (unsigned long)msg_sys.msg_control;
+	msg_sys.msg_flags = 0;
+	if (MSG_CMSG_COMPAT & flags)
+		msg_sys.msg_flags = MSG_CMSG_COMPAT;
+	
+	if (sock->file->f_flags & O_NONBLOCK)
+		flags |= MSG_DONTWAIT;
+	err = sock_recvmsg(sock, &msg_sys, total_len, flags);
+	if (err < 0)
+		goto out_freeiov;
+	len = err;
+
+	if (uaddr != NULL) {
+		err = move_addr_to_user(addr, msg_sys.msg_namelen, uaddr, uaddr_len);
+		if (err < 0)
+			goto out_freeiov;
+	}
+	err = __put_user(msg_sys.msg_flags, COMPAT_FLAGS(msg));
+	if (err)
+		goto out_freeiov;
+	if (MSG_CMSG_COMPAT & flags)
+		err = __put_user((unsigned long)msg_sys.msg_control-cmsg_ptr, 
+				 &msg_compat->msg_controllen);
+	else
+		err = __put_user((unsigned long)msg_sys.msg_control-cmsg_ptr, 
+				 &msg->msg_controllen);
+	if (err)
+		goto out_freeiov;
+	err = len;
+
+out_freeiov:
+	if (iov != iovstack)
+		sock_kfree_s(sock->sk, iov, iov_size);
+out_put:
+	sockfd_put(sock);
+out:
+	return err;
+}
+
+#ifdef __ARCH_WANT_SYS_SOCKETCALL
+
+/* Argument list sizes for sys_socketcall */
+#define AL(x) ((x) * sizeof(unsigned long))
+static unsigned char nargs[18]={AL(0),AL(3),AL(3),AL(3),AL(2),AL(3),
+				AL(3),AL(3),AL(4),AL(4),AL(4),AL(6),
+				AL(6),AL(2),AL(5),AL(5),AL(3),AL(3)};
+#undef AL
+
+/*
+ *	System call vectors. 
+ *
+ *	Argument checking cleaned up. Saved 20% in size.
+ *  This function doesn't need to set the kernel lock because
+ *  it is set by the callees. 
+ */
+
+asmlinkage long sys_socketcall(int call, unsigned long __user *args)
+{
+	unsigned long a[6];
+	unsigned long a0,a1;
+	int err;
+
+	if(call<1||call>SYS_RECVMSG)
+		return -EINVAL;
+
+	/* copy_from_user should be SMP safe. */
+	if (copy_from_user(a, args, nargs[call]))
+		return -EFAULT;
+		
+	a0=a[0];
+	a1=a[1];
+	
+	switch(call) 
+	{
+		case SYS_SOCKET:
+			err = sys_socket(a0,a1,a[2]);
+			break;
+		case SYS_BIND:
+			err = sys_bind(a0,(struct sockaddr __user *)a1, a[2]);
+			break;
+		case SYS_CONNECT:
+			err = sys_connect(a0, (struct sockaddr __user *)a1, a[2]);
+			break;
+		case SYS_LISTEN:
+			err = sys_listen(a0,a1);
+			break;
+		case SYS_ACCEPT:
+			err = sys_accept(a0,(struct sockaddr __user *)a1, (int __user *)a[2]);
+			break;
+		case SYS_GETSOCKNAME:
+			err = sys_getsockname(a0,(struct sockaddr __user *)a1, (int __user *)a[2]);
+			break;
+		case SYS_GETPEERNAME:
+			err = sys_getpeername(a0, (struct sockaddr __user *)a1, (int __user *)a[2]);
+			break;
+		case SYS_SOCKETPAIR:
+			err = sys_socketpair(a0,a1, a[2], (int __user *)a[3]);
+			break;
+		case SYS_SEND:
+			err = sys_send(a0, (void __user *)a1, a[2], a[3]);
+			break;
+		case SYS_SENDTO:
+			err = sys_sendto(a0,(void __user *)a1, a[2], a[3],
+					 (struct sockaddr __user *)a[4], a[5]);
+			break;
+		case SYS_RECV:
+			err = sys_recv(a0, (void __user *)a1, a[2], a[3]);
+			break;
+		case SYS_RECVFROM:
+			err = sys_recvfrom(a0, (void __user *)a1, a[2], a[3],
+					   (struct sockaddr __user *)a[4], (int __user *)a[5]);
+			break;
+		case SYS_SHUTDOWN:
+			err = sys_shutdown(a0,a1);
+			break;
+		case SYS_SETSOCKOPT:
+			err = sys_setsockopt(a0, a1, a[2], (char __user *)a[3], a[4]);
+			break;
+		case SYS_GETSOCKOPT:
+			err = sys_getsockopt(a0, a1, a[2], (char __user *)a[3], (int __user *)a[4]);
+			break;
+		case SYS_SENDMSG:
+			err = sys_sendmsg(a0, (struct msghdr __user *) a1, a[2]);
+			break;
+		case SYS_RECVMSG:
+			err = sys_recvmsg(a0, (struct msghdr __user *) a1, a[2]);
+			break;
+		default:
+			err = -EINVAL;
+			break;
+	}
+	return err;
+}
+
+#endif /* __ARCH_WANT_SYS_SOCKETCALL */
+
+/*
+ *	This function is called by a protocol handler that wants to
+ *	advertise its address family, and have it linked into the
+ *	SOCKET module.
+ */
+
+int sock_register(struct net_proto_family *ops)
+{
+	int err;
+
+	if (ops->family >= NPROTO) {
+		printk(KERN_CRIT "protocol %d >= NPROTO(%d)\n", ops->family, NPROTO);
+		return -ENOBUFS;
+	}
+	net_family_write_lock();
+	err = -EEXIST;
+	if (net_families[ops->family] == NULL) {
+		net_families[ops->family]=ops;
+		err = 0;
+	}
+	net_family_write_unlock();
+	printk(KERN_INFO "NET: Registered protocol family %d\n",
+	       ops->family);
+	return err;
+}
+
+/*
+ *	This function is called by a protocol handler that wants to
+ *	remove its address family, and have it unlinked from the
+ *	SOCKET module.
+ */
+
+int sock_unregister(int family)
+{
+	if (family < 0 || family >= NPROTO)
+		return -1;
+
+	net_family_write_lock();
+	net_families[family]=NULL;
+	net_family_write_unlock();
+	printk(KERN_INFO "NET: Unregistered protocol family %d\n",
+	       family);
+	return 0;
+}
+
+
+extern void sk_init(void);
+
+void __init sock_init(void)
+{
+	/*
+	 *	Initialize sock SLAB cache.
+	 */
+	 
+	sk_init();
+
+#ifdef SLAB_SKB
+	/*
+	 *	Initialize skbuff SLAB cache 
+	 */
+	skb_init();
+#endif
+
+	/*
+	 *	Initialize the protocols module. 
+	 */
+
+	init_inodecache();
+	register_filesystem(&sock_fs_type);
+	sock_mnt = kern_mount(&sock_fs_type);
+	/* The real protocol initialization is performed when
+	 *  do_initcalls is run.  
+	 */
+
+#ifdef CONFIG_NETFILTER
+	netfilter_init();
+#endif
+}
+
+#ifdef CONFIG_PROC_FS
+void socket_seq_show(struct seq_file *seq)
+{
+	int cpu;
+	int counter = 0;
+
+	for (cpu = 0; cpu < NR_CPUS; cpu++)
+		counter += per_cpu(sockets_in_use, cpu);
+
+	/* It can be negative, by the way. 8) */
+	if (counter < 0)
+		counter = 0;
+
+	seq_printf(seq, "sockets: used %d\n", counter);
+}
+#endif /* CONFIG_PROC_FS */
+
+/* ABI emulation layers need these two */
+EXPORT_SYMBOL(move_addr_to_kernel);
+EXPORT_SYMBOL(move_addr_to_user);
+EXPORT_SYMBOL(sock_create);
+EXPORT_SYMBOL(sock_create_kern);
+EXPORT_SYMBOL(sock_create_lite);
+EXPORT_SYMBOL(sock_map_fd);
+EXPORT_SYMBOL(sock_recvmsg);
+EXPORT_SYMBOL(sock_register);
+EXPORT_SYMBOL(sock_release);
+EXPORT_SYMBOL(sock_sendmsg);
+EXPORT_SYMBOL(sock_unregister);
+EXPORT_SYMBOL(sock_wake_async);
+EXPORT_SYMBOL(sockfd_lookup);
+EXPORT_SYMBOL(kernel_sendmsg);
+EXPORT_SYMBOL(kernel_recvmsg);
diff -Nrub linux-2.6.11.7-orig/security/Kconfig linux-2.6.11.7-snare/security/Kconfig
--- linux-2.6.11.7-orig/security/Kconfig	2005-04-07 14:57:52.000000000 -0400
+++ linux-2.6.11.7-snare/security/Kconfig	2005-04-22 11:19:47.205664096 -0400
@@ -33,6 +33,18 @@
 	  pretends keys that are inaccessible to a process don't exist as far
 	  as that process is concerned.
 
+config C2_AUDIT
+	bool "SNARE Auditing Support"
+	default y
+	help
+	  If you say Y here, a user level program (SNARE) will be able to receive
+	  system audit events from the kernel.  Many government departments,
+	  and organizations that have significant security requirements, will want
+	  to turn this feature on.  Most normal users will not be interested in
+	  reviewing audit reports though, and should answer N here.
+	  
+	  If you are unsure how to answer this question, answer N.
+	
 config SECURITY
 	bool "Enable different security models"
 	help
diff -Nrub linux-2.6.11.7-orig/security/Makefile linux-2.6.11.7-snare/security/Makefile
--- linux-2.6.11.7-orig/security/Makefile	2005-04-07 14:57:53.000000000 -0400
+++ linux-2.6.11.7-snare/security/Makefile	2005-04-22 11:19:47.205664096 -0400
@@ -4,6 +4,7 @@
 
 obj-$(CONFIG_KEYS)			+= keys/
 subdir-$(CONFIG_SECURITY_SELINUX)	+= selinux
+subdir-$(CONFIG_C2_AUDIT)		+= snare
 
 # if we don't select a security model, use the default capabilities
 ifneq ($(CONFIG_SECURITY),y)
@@ -17,3 +18,4 @@
 obj-$(CONFIG_SECURITY_CAPABILITIES)	+= commoncap.o capability.o
 obj-$(CONFIG_SECURITY_ROOTPLUG)		+= commoncap.o root_plug.o
 obj-$(CONFIG_SECURITY_SECLVL)		+= seclvl.o
+obj-$(CONFIG_C2_AUDIT)			+= snare/built-in.o
diff -Nrub linux-2.6.11.7-orig/security/snare/Makefile linux-2.6.11.7-snare/security/snare/Makefile
--- linux-2.6.11.7-orig/security/snare/Makefile	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.11.7-snare/security/snare/Makefile	2005-04-22 11:19:47.208663640 -0400
@@ -0,0 +1,8 @@
+#
+# Makefile for building snare as part of the kernel tree.
+#
+
+obj-$(CONFIG_C2_AUDIT) := auditapi.o
+
+snare-y := auditapi.o
+
diff -Nrub linux-2.6.11.7-orig/security/snare/auditapi.c linux-2.6.11.7-snare/security/snare/auditapi.c
--- linux-2.6.11.7-orig/security/snare/auditapi.c	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.11.7-snare/security/snare/auditapi.c	2005-04-22 11:19:47.208663640 -0400
@@ -0,0 +1,2082 @@
+/* $Id: auditapi.c,v 1.3 2004/11/03 22:44:04 redphoenix Exp $
+ *
+ * security/snare/auditapi.c
+ *
+ * Original Copyright (c) 2002-2004 InterSect Alliance Pty Ltd
+ *           - www.intersectalliance.com
+ *
+ * Additions:
+ * 2004: Modified by Mark Westerman mark.westerman at westcam dot com 
+ * 2004: Copyright (c) 2004 Silicon Graphics, Inc. All rights reserved.
+ * 2004: Modified by Jonathan Abbey, UTexas
+ *
+ */
+
+// If CONFIG_C2_AUDIT is not defined, do NOTHING.
+// See also: linux/saudit.h - if not configured, all saudit_*
+// routines will be optimised out of existance by gcc.
+// (Thanks to Alan Cox for the suggestion).
+
+// Memory usage
+#include <linux/config.h>
+
+#ifdef CONFIG_C2_AUDIT
+
+// Total events and lost events not working now do not show
+// Modification By mark.westerman at westcam dot com
+#define SHOW_EVENT_COUNTER 0
+#define SNARE_RED_HAT_LINUX_KERNEL 1
+
+#define MAX_SOCK_ADDR 128	/* 108 for Unix domain */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/proc_fs.h>
+#include <linux/compiler.h>
+#include <linux/saudit.h>
+
+// #include <net/sock.h>
+#include <linux/file.h>
+#include <linux/ip.h>
+#include <linux/net.h>
+
+#include <asm/unistd.h>
+#include <asm/uaccess.h>
+
+#include <linux/version.h>
+
+// Externals
+// Not certain that we need this.
+extern struct socket *sockfd_lookup (int fd, int *err);
+
+
+// Function prototypes
+static int auditproc_ioctl(struct inode *, struct file *, unsigned int,
+			   unsigned long);
+static int auditproc_open(struct inode *, struct file *);
+static int auditproc_close(struct inode *, struct file *);
+static ssize_t auditproc_read(struct file *, char *, size_t, loff_t *);
+
+static int info_open(struct inode *inode, struct file *file);
+static ssize_t info_read(struct file *file, char *ubuf, size_t length,
+			 loff_t * ppos);
+static int info_close(struct inode *inode, struct file *file);
+
+static void audit_on(int auditnumber);
+static void audit_off(int auditnumber);
+static AuditNode *alloc_event(int token_size);
+static void append_event(AuditNode * nodepointer);
+
+static long GrabString(char *destination, const char *kernelstring,
+		const char __user *userstring, int length);
+
+static void audit_hwcheck(void);
+int saudit_return(int returncode);
+
+static DECLARE_MUTEX(audit_lock);
+static struct task_struct *auditdaemon_task_struct = NULL;	// Daemon interaction
+
+// If we have the audit daemon attached
+int AUDIT_IS_RUNNING = 0;
+static int AUDIT_IS_PAUSED = 0;	// Used for High Water Mark memory testing
+
+#if defined(SNARE_RED_HAT_LINUX_KERNEL) || ( LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) )
+#define PRIO_TO_NICE(prio)      ((prio) - MAX_RT_PRIO - 20)
+#endif
+
+// How many packets have we lost this session due to low buffer size?
+static int lost_events = 0;
+static int total_events = 0;
+
+// Linked list of audit events - used as an internal buffer
+// so that we don't slow down the kernel significantly while waiting
+// for the user space audit daemon to catch up.
+static AuditNode *list_head = (AuditNode *) NULL;
+static AuditNode *list_tail = (AuditNode *) NULL;
+
+// Audit daemon read position
+static void *read_position = (void *) NULL;
+
+// Setup a bitmask to work out which audit events are currently active.
+// Declare as volatile for the compiler not lock for test events
+volatile static unsigned long active_events[(MAXAUDIT + sizeof(long) - 1) /
+					    sizeof(long)] = { 0, };
+
+#define saudit_active(auditnumber) \
+	(test_bit((auditnumber),active_events))
+
+static struct file_operations info_ops = {
+      read:info_read,		// read
+      open:info_open,
+      release:info_close
+};
+
+static struct file_operations file_ops = {
+      read:auditproc_read,	// read
+      open:auditproc_open,	// open
+      ioctl:auditproc_ioctl,	// ioctl
+      release:auditproc_close	// release
+};
+
+
+static struct proc_dir_entry *ProcEntry;
+static struct proc_dir_entry *ProcInfoEntry;
+
+//
+// For compatibility with Non RedHat i.e. vanilla kernels
+// including debian kernels
+//
+
+#if !defined(SNARE_RED_HAT_LINUX_KERNEL) && ( LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) )
+#define set_user_nice(tsk, n) do { (tsk)->nice = n; } while(0)
+#endif
+
+//
+// Highwater mark varables for memory utilization
+//
+static unsigned long highwater_mark = 15000000;	// Set to 15 MB by Default
+static unsigned long memory_usage = 0;		// Current Memory Usage
+static int nicevalue;				// Nice Value that the
+						// Audit Daemon was stated with
+static int prio_change = 0;			// Has priority Changed
+static int highwater_pause = 100;		// % to of memory usage to pause
+static int lowwater_pause = 90;			// % to resume auditing
+static int highwater_nice = 80;			// % to change audit priority
+static int highwater_nice_val = -10;		// Nice value to change to
+static int lowwater_nice = 60;			// % to change back the priority
+static int highwater_mark_percent = 10;		// % of memory to use for High water mark
+
+wait_queue_head_t proc_audit_queue;
+
+// Use to calculate total ram
+#define BSHIFT(x) ((unsigned long)(x) << PAGE_SHIFT)
+
+// Initialise our audit environment
+void saudit_init(void)
+{
+	struct sysinfo i;
+
+	// Initialise audit here
+	// - create /proc entries etc.
+	printk("SNARE Audit capability is initialising\n");
+
+	// Create the proc file system entries.
+	if ((ProcInfoEntry =
+	     create_proc_entry(AUDITINFO_NAME, S_IRUGO | S_IWUSR,
+			       NULL)) == NULL) {
+		printk("Audit: Cannot create snare information interface in /proc");
+		return;
+	}
+	ProcInfoEntry->proc_fops = &info_ops;
+
+	if ((ProcEntry =
+	     create_proc_entry(AUDITDEV_NAME, S_IRUSR | S_IWUSR,
+			       NULL)) == NULL) {
+		printk("Audit: Cannot create snare daemon interface in /proc");
+		return;
+	}
+
+	ProcEntry->proc_fops = &file_ops;
+
+	// Initialise the wait queue for blocking read
+	init_waitqueue_head(&proc_audit_queue);
+	// Initialise the highwater mark system
+	memory_usage = 0;
+	AUDIT_IS_PAUSED = 0;
+	si_meminfo(&i);
+	// Set the highwater mark as a percentage of total ram
+	highwater_mark = (BSHIFT(i.totalram) / 100) * highwater_mark_percent;
+
+}
+
+static int info_open(struct inode *inode, struct file *file)
+{
+	enum { DATA_LEN = 500 };
+	char *data;
+	char processid[20];
+	int nv;
+	pid_t ad_task = 0;
+	int daemon_running = 0;
+#if SHOW_EVENT_COUNTER
+	int l_lost_events, l_total_events;
+#endif
+
+	if ((data = kmalloc(DATA_LEN, GFP_KERNEL)) == NULL)
+		return (0);
+
+	down(&audit_lock);
+
+	if (auditdaemon_task_struct != NULL) {
+		daemon_running = 1;
+		ad_task = auditdaemon_task_struct->pid;
+	}
+
+#if SHOW_EVENT_COUNTER
+	l_lost_events = lost_events;
+	l_total_events = total_events;
+#endif
+
+	up(&audit_lock);
+
+	if(daemon_running) {
+		snprintf(processid, 20, "%d", (int) ad_task);
+#if !defined(SNARE_RED_HAT_LINUX_KERNEL) && ( LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) )
+		nv = auditdaemon_task_struct->nice;
+#else
+		nv = PRIO_TO_NICE(auditdaemon_task_struct->static_prio);
+#endif
+	} else {
+		snprintf(processid, 20, "No Daemon Running");
+		nv = 0;
+	}
+
+
+#if SHOW_EVENT_COUNTER
+	// Just my perference I like the output lined up
+	snprintf(data, DATA_LEN,
+		 "SNARE Version:                 %d.%d.%d\n"
+		 "Audit Active:                  %s\n"
+		 "Audit Process ID:              %s\n"
+		 "Events Lost This Session:      %d\n"
+		 "Events Processed This Session: %d\n"
+		 "HighWater Mark:                %lu\n"
+		 "Memory Usage:                  %lu\n"
+		 "Audit Paused:                  %s\n"
+		 "Audit Nice Value:              %d\n",
+		 SNAREAUDIT_MAJOR_VERSION, SNAREAUDIT_MINOR_VERSION,
+		 SNAREAUDIT_PATCH_VERSION,
+		 (AUDIT_IS_RUNNING ? "yes" : "no"), processid, l_lost_events,
+		 l_total_events, highwater_mark, memory_usage,
+		 (AUDIT_IS_PAUSED ? "yes" : "no"), nv);
+#else
+	snprintf(data, DATA_LEN,
+		 "SNARE Version:    %d.%d.%d\n"
+		 "Audit Active:     %s\n"
+		 "Audit Process ID: %s\n"
+		 "HighWater Mark:   %lu\n"
+		 "Memory Usage:     %lu\n"
+		 "Audit Paused:     %s\n"
+		 "Audit Nice Value: %d\n",
+		 SNAREAUDIT_MAJOR_VERSION, SNAREAUDIT_MINOR_VERSION,
+		 SNAREAUDIT_PATCH_VERSION,
+		 (AUDIT_IS_RUNNING ? "yes" : "no"), processid,
+		 highwater_mark, memory_usage,
+		 (AUDIT_IS_PAUSED ? "yes" : "no"), nv);
+#endif
+
+	if (data == NULL)
+		return -ENODEV;
+
+	file->private_data = data;
+
+	return (0);
+}
+
+static void audit_hwcheck(void)
+{
+	unsigned long per;
+	int paused;
+
+
+	// We lock the kernel here to handle the
+	// the highwater mark system use lot of 
+	// global varables
+
+	down(&audit_lock);
+	paused = AUDIT_IS_PAUSED;
+	per = memory_usage / (highwater_mark / 100);
+
+	if (per > highwater_pause) {
+		AUDIT_IS_PAUSED = 1;
+		++lost_events;
+		up(&audit_lock);
+		if (!paused) {
+			printk("Auditing: is paused\n");
+		}
+		return;
+	}
+	if (per > highwater_nice) {
+		if (!AUDIT_IS_PAUSED) {
+			if (!prio_change) {
+				prio_change = 1;
+				up(&audit_lock);
+				printk
+				    ("Auditing: Changing Audit daemon Priority\n");
+				set_user_nice(auditdaemon_task_struct,
+					      highwater_nice_val);
+				return;
+			}
+			up(&audit_lock);
+			return;
+		}
+		++lost_events;
+		up(&audit_lock);
+		return;
+	}
+	if (AUDIT_IS_PAUSED && (per < lowwater_pause)) {
+		AUDIT_IS_PAUSED = 0;
+	} else {
+		paused = 0;
+	}
+	if (prio_change && per < lowwater_nice) {
+		prio_change = 0;
+		up(&audit_lock);
+		printk("Auditing: Resuming Audit daemon Priority\n");
+		set_user_nice(auditdaemon_task_struct, nicevalue);
+		return;
+	}
+	if (AUDIT_IS_PAUSED) {
+		++lost_events;
+	}
+
+	up(&audit_lock);
+	if (paused) {
+		printk("Auditing: is resuming\n");
+	}
+}
+
+static int info_close(struct inode *inode, struct file *file)
+{
+	kfree(file->private_data);
+	return (0);
+}
+
+static ssize_t info_read(struct file *file, char *ubuf, size_t length,
+			 loff_t * ppos)
+{
+	int bytes_to_write = 0;
+	int pos = *ppos;
+	char *data = file->private_data;
+
+	// User has asked for a zero-length read?
+	if (length == 0)
+		return (0);
+
+	if ((bytes_to_write = strlen(data) - pos) <= 0)
+		return 0;	// EOF
+	if (bytes_to_write >= length)
+		bytes_to_write = length;
+	if (copy_to_user(ubuf, data + pos, bytes_to_write))
+		return -EFAULT;
+	*ppos = pos + bytes_to_write;
+	return (bytes_to_write);
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+// This routine handles ioctl messages from the audit daemon.
+////////////////////////////////////////////////////////////////////////////////
+
+int auditproc_ioctl(struct inode *node, struct file *the_file,
+		    unsigned int command, unsigned long arg)
+{
+	// If the audit facility is running, there is not much point starting again!
+	if ((AUDIT_IS_RUNNING) && (command == AUDIT_START)) {
+		return -EBUSY;
+	}
+
+	// Don't alllow a forked or cloned task to ioctl.
+	// Only the task which opened the audit device may
+	// control this device.
+	if(auditdaemon_task_struct != current) {
+		return -EBUSY;
+	}
+
+	if (command == AUDIT_START) {
+		down(&audit_lock);
+		AUDIT_IS_RUNNING = 1;
+		up(&audit_lock);
+	} else if (command == AUDIT_STOP) {
+		down(&audit_lock);
+		AUDIT_IS_RUNNING = 0;
+		up(&audit_lock);
+	} else if (command == AUDIT_LOSTEVENTS) {
+		return (lost_events);
+	} else if (command == AUDIT_TOTALEVENTS) {
+		return (total_events);
+	} else if (command == AUDIT_EVENT_ON) {
+		if(arg <= 0 || arg > MAXAUDIT) {
+			return -EINVAL;
+		}
+		audit_on(arg);
+	} else if (command == AUDIT_EVENT_OFF) {
+		if(arg <= 0 || arg > MAXAUDIT) {
+			return -EINVAL;
+		}
+		audit_off(arg);
+		// Eg: ioctl(x,SYS_exit);
+	} else if (command == AUDIT_FLUSH) {
+		// Turn off all auditing.
+		// This is generally sent when /etc/audit/snare.conf has been updated
+		// and the user wishes to establish a new audit policy.
+		int counter;
+		for (counter = 1; counter <= MAXAUDIT; counter++) {
+			if (saudit_active(counter))
+				audit_off(counter);
+		}
+
+		// Also reset the lost/total event counters here.
+		lost_events = 0;
+		total_events = 0;
+		// High water mark system should add error checking here.
+	} else if (command == AUDIT_HIGHWATERMARK_MEM) {
+		highwater_mark = arg;
+	} else if (command == AUDIT_HIGHWATERMARK_PER) {
+		struct sysinfo i;
+		si_meminfo(&i);
+		highwater_mark_percent = arg;
+		highwater_mark =
+		    (BSHIFT(i.totalram) / 100) * highwater_mark_percent;
+	} else if (command == AUDIT_HIGHWATERMARK_PAUSE) {
+		highwater_pause = arg;
+	} else if (command == AUDIT_LOWWATERMARK_PAUSE) {
+		lowwater_pause = arg;
+	} else if (command == AUDIT_HIGHWATERMARK_NICE) {
+		highwater_nice_val = arg;
+	} else if (command == AUDIT_LOWWATERMARK_NICE) {
+		lowwater_nice = arg;
+	} else if (command == AUDIT_HIGHWATER_NICE_VAL) {
+		highwater_nice_val = arg;
+	} else {
+		return -ENOSYS;	// Redhat kernel team suggest ENOTTY ? Clarify this.
+	}
+
+	return 0;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Here we give the user the data.
+// NOTE that this module may be executed at any time - even in the middle
+// of the execution of something else. Hence, any variables that are changed
+// by this routine should be surrounded by a kernel lock if they are used
+// elsewhere!
+//
+// In particular:
+//  read_position
+//  list_head
+//  list_tail
+//
+// Let me just repeat this in another way:
+// WARNING: DO NOT CHANGE OR RELY ON THE VARIABLES ABOVE UNLESS YOU ARE
+//                IN A LOCKED STATE - THEY MAY BE CORRUPTED BY THE
+//                AUDIT_READ MODULE, WHICH IS CALLED BY SIGNAL/INTERRUPT
+//
+// Unfortunately, we also need lock within this routine also.
+// Otherwise, an interrupt might occur when we were half way through modifying
+// something like read_position - and if the interrupt happens to generate an
+// audit event, then we may be copying data to the wrong location in the array,
+// leading to data corruption.
+// Locking too much code slows down the system unfortunately. I'll try
+// and keep it minimal.
+////////////////////////////////////////////////////////////////////////////////
+
+static ssize_t auditproc_read(struct file *file, char *filebuffer,
+			      size_t length, loff_t * ppos)
+{
+	// Number of bytes actually written to the buffer
+	int bytes_avail_to_send = 0;
+	int bytes_to_write = 0;
+
+
+	// Don't allow a forked or cloned task to read.
+	// Only the task which opened the audit device may read.
+	if(auditdaemon_task_struct != current) {
+		return -EBUSY;
+	}
+
+	if (length == 0) {
+		return (0);	// Only want zero bytes? Here they are.
+	}
+
+	// if (ppos != &file->f_pos) {
+	//	return -ESPIPE;	// No pread() thanks.
+	//}
+
+	BUG_ON(auditdaemon_task_struct == NULL);
+
+	// Do we have any data?
+	if (list_head == (AuditNode *) NULL) {
+		if (file->f_flags & O_NONBLOCK) {
+			return -EAGAIN;
+		}
+		if (wait_event_interruptible(proc_audit_queue, list_head)) {
+			return -ERESTARTSYS;
+		}
+	}
+
+	// Lock the kernel
+	down(&audit_lock);
+	if (read_position == (void *) NULL) {
+		// Our first time into this routine, or first time back after all data has been read.
+		read_position = list_head->location;
+	}
+
+	bytes_avail_to_send =
+	    (int) (((void *) list_head->location + list_head->size) -
+		   (void *) read_position);
+
+	up(&audit_lock);
+
+	/*
+         * Give the caller all available data up to length.
+         * We know that at least one byte will be available,
+         * because we have the head node, all nodes must
+         * have some data, and the head node must have some
+         * data available (otherwise the head node would
+         * have been tossed in the last call to this function).
+         */
+	if(bytes_avail_to_send > length) {
+		bytes_to_write = length;
+        } else {
+                 bytes_to_write = bytes_avail_to_send;
+	}
+//	while (bytes_avail_to_send < length) {
+//		// up(&audit_lock);
+//
+//		if (file->f_flags & O_NONBLOCK) {
+//			return -EAGAIN;
+//		}
+//
+//		if (wait_event_interruptible(proc_audit_queue,
+//					     (bytes_avail_to_send =
+//					      (int) (list_head->location +
+//						     list_head->size -
+//						     read_position)) >=
+//					     length)) {
+//			return -ERESTARTSYS;
+//		}
+//		// down(&audit_lock);
+//	}
+//	bytes_to_write = length;
+
+
+	if (copy_to_user(filebuffer, read_position, bytes_to_write)) {
+		// up(&audit_lock);
+		return -EFAULT;
+	}
+
+	down(&audit_lock);
+	read_position += bytes_to_write;
+
+	// Have we reached the end of the current nodes data?
+	if ((void *) read_position >=
+	    ((void *) list_head->location + list_head->size)) {
+		AuditNode *tempnode;
+
+		tempnode = list_head;
+		list_head = list_head->next;
+
+		if (tempnode->size > memory_usage) {
+			memory_usage = 0;
+		} else {
+			memory_usage -= tempnode->size;
+		}
+		// Excellent, we're at the end of the current audit event.
+		// Remove this from the linked list, and set the read_position to the next record.
+		if (list_head != (AuditNode *) NULL) {
+			read_position = list_head->location;
+		} else {
+			read_position = (AuditNode *) NULL;
+		}
+
+		// No events remaining? Set list_tail to null also.
+		if (tempnode->next == (AuditNode *) NULL) {
+			list_tail = (AuditNode *) NULL;
+		}
+
+		// Free our tempnode->location pointer, which actually
+		// is a pointer to ex current->saudit_record
+		kfree(tempnode->location);
+		kfree(tempnode);
+	}
+
+	up(&audit_lock);
+
+	return (bytes_to_write);
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+// open the device
+////////////////////////////////////////////////////////////////////////////////
+static int auditproc_open(struct inode *node, struct file *the_file)
+{
+	// Note: This changes to minor(node...) in 2.6
+	// int device_minor = MINOR(node->i_rdev) & 0xf;	// Device minor number
+
+	// We can only have one audit device, and it can only be open once
+	//if (device_minor) {
+	//	return -EBUSY;
+	//}
+
+	down(&audit_lock);
+
+	if (auditdaemon_task_struct != NULL) {
+		up(&audit_lock);
+		return -EBUSY;
+	}
+
+	// reset the lost packets indicator
+	lost_events = 0;
+	total_events = 0;
+	memory_usage = 0;
+	AUDIT_IS_PAUSED = 0;
+
+	// Fetch the task structure of the process that opened the device
+	auditdaemon_task_struct = current;
+#if !defined(SNARE_RED_HAT_LINUX_KERNEL) && ( LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) )
+	nicevalue = auditdaemon_task_struct->nice;
+#else
+	nicevalue = PRIO_TO_NICE(auditdaemon_task_struct->static_prio);
+#endif
+
+	// reset our read position to the beginning of head event data
+	read_position = NULL;
+
+	up(&audit_lock);
+
+	printk("Auditing: Process %d has opened the device\n",
+	       auditdaemon_task_struct->pid);
+
+	return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// close the device and release resources
+////////////////////////////////////////////////////////////////////////////////
+static int auditproc_close(struct inode *node, struct file *the_file)
+{
+	int counter;
+#if SHOW_EVENT_COUNTER
+	int l_lost_events, l_total_events;
+#endif
+
+	// Only the task that opened the audit device can
+	// close the device. We don't want another thread
+	// to interfere.
+	if(auditdaemon_task_struct != current) {
+		return(0);
+	}
+
+	for (counter = 1; counter <= MAXAUDIT; counter++) {
+		audit_off(counter);
+	}
+
+	down(&audit_lock);
+	AUDIT_IS_RUNNING = 0;
+
+	auditdaemon_task_struct = NULL;
+#if SHOW_EVENT_COUNTER
+	l_lost_events = lost_events;
+	l_total_events = total_events;
+#endif
+	lost_events = 0;
+	total_events = 0;
+	memory_usage = 0;
+	AUDIT_IS_PAUSED = 0;
+
+	up(&audit_lock);
+
+	printk("AUDIT: Audit daemon has closed /proc/audit.\n");
+#if SHOW_EVENT_COUNTER
+	printk("AUDIT: Events lost due low memory this session: %d\n",
+	       l_lost_events);
+	printk("AUDIT: Total Events processed this session: %d\n",
+	       l_total_events);
+#endif
+
+	return 0;
+}
+
+static void audit_on(int auditnumber)
+{
+	if(auditnumber <= 0 || auditnumber > MAXAUDIT) {
+		return;
+	}
+	down(&audit_lock);
+	set_bit(auditnumber, active_events);
+	up(&audit_lock);
+}
+
+static void audit_off(int auditnumber)
+{
+	if(auditnumber <= 0 || auditnumber > MAXAUDIT) {
+		return;
+	}
+	down(&audit_lock);
+	clear_bit(auditnumber, active_events);
+	up(&audit_lock);
+}
+
+int _audit_mknod(const char *kfile, const char __user *ufile, int mode, dev_t dev,
+		int retval)
+{
+	io_class *record;
+
+	// If noone has requested MKNOD to be active, return quickly.
+	if (!saudit_active(AUDIT_mknod))
+		return (0);
+	audit_hwcheck();
+	if (current == auditdaemon_task_struct || AUDIT_IS_PAUSED)
+		return (0);
+	if (current->saudit_record)
+		return (1);
+
+	// alloc the audit record
+	current->saudit_record = kmalloc(sizeof(io_class), GFP_KERNEL);
+	if (!current->saudit_record) {
+		return (0);
+	}
+
+	record = (io_class *) current->saudit_record;
+
+	// Filename.
+	GrabString(record->t_path.path, kfile, ufile, MAX_PATH);
+
+	record->t_attributes.mode = (int) mode;
+	record->t_attributes.createmode = (unsigned long) dev;
+
+	record->t_header.event_id = AUDIT_mknod;
+	record->t_header.event_class = AUDIT_CLASS_IO;
+	saudit_return(retval);
+	return (1);
+}
+
+/*
+ * argcount() counts the number of strings in array ARGV.
+ */
+static int argcount(char __user * __user * argv, int max)
+{
+	int i = 0;
+
+	if (argv != NULL) {
+		for (;;) {
+			char __user * p;
+
+			if (get_user(p, argv))
+				return -EFAULT;
+			if (!p)
+				break;
+			argv++;
+			if(++i > max)
+				return -E2BIG;
+		}
+	}
+	return i;
+}
+
+char *saudit_copy_argv(char __user *__user *argv)
+{
+	char *returnstring;
+	// char tempstring[MAX_PATH];
+	char *tempstring;
+	int argc, argi;
+	char __user *str;
+	unsigned int delimiter_required = 0;
+
+	if (!saudit_active(AUDIT_execve))
+		return ((char *) NULL);
+	audit_hwcheck();
+	if (current == auditdaemon_task_struct || AUDIT_IS_PAUSED)
+		return ((char *) NULL);
+
+	argc=argcount(argv, 1024);	/* avoid infinite loop on bad data */
+	if (argc < 0) {
+		return ((char *) NULL);
+	}
+
+	//
+	// Dont check saudit_record for NULL Since it it will be here
+	//
+	tempstring = kmalloc(MAX_PATH, GFP_KERNEL);
+	if (!tempstring)
+		return ((char *) NULL);	// Out of mem
+
+	tempstring[0] = '\0';
+	returnstring = kmalloc(MAX_PATH, GFP_KERNEL);
+	if (!returnstring) {
+		kfree(tempstring);
+		return ((char *) NULL);	// Out of mem
+	}
+	returnstring[0] = '\0';
+	argi=0;
+	
+	while (argi < argc) {
+		if (get_user(str, argv+argi)) {
+			    continue;
+		}
+		strncpy_from_user(tempstring, str, MAX_PATH - 1);
+		tempstring[MAX_PATH-1] = '\0';
+
+		// Allow for the delimiter
+		if ((strlen(returnstring) + strlen(tempstring) + 1) <
+		    (MAX_PATH - 1)) {
+			if (!delimiter_required) {
+				delimiter_required = 1;
+			} else {
+				strcat(returnstring, " ");
+			}
+			strcat(returnstring, tempstring);
+		} else {
+			// If the argument size is greater than MAX_PATH, then
+			// ignore this argument, continue on to the next.
+			argi++;
+			continue;
+		}
+		argi++;
+	}
+	kfree(tempstring);
+
+	return (returnstring);
+}
+
+int _audit_execve(const char __user *ufilename, const char *kfilename,
+		 char *arguments, int retval)
+{
+	ex_class *record;
+
+	if (!saudit_active(AUDIT_execve)) {
+		if (arguments) {
+			kfree(arguments);
+		}
+		return (0);
+	}
+	audit_hwcheck();
+	if (current == auditdaemon_task_struct || AUDIT_IS_PAUSED) {
+		if (arguments) {
+			kfree(arguments);
+		}
+		return (0);
+	}
+
+	if (current->saudit_record) {
+		if (arguments) {
+			kfree(arguments);
+		}
+		return (1);
+	}
+
+	// alloc the audit record
+	current->saudit_record = kmalloc(sizeof(ex_class), GFP_KERNEL);
+	if (!current->saudit_record) {
+		if (arguments) {
+			kfree(arguments);
+		}
+		return (0);
+	}
+
+	record = (ex_class *) current->saudit_record;
+
+	// Use our new grabstring routine
+	GrabString(record->t_path.path, kfilename, ufilename, MAX_PATH);
+
+	if (arguments) {
+		strncpy(record->t_execargs.args, arguments, MAX_PATH);
+		kfree(arguments);
+	} else {
+		strcat(record->t_execargs.args, "");
+	}
+
+	record->t_header.event_id = AUDIT_execve;
+	record->t_header.event_class = AUDIT_CLASS_EXEC;
+
+	saudit_return(retval);
+	return (0);
+}
+
+
+int _audit_exit(int retval)
+{
+	pc_class *record;
+
+	if (!saudit_active(AUDIT_exit))
+		return (0);
+	audit_hwcheck();
+	if (current == auditdaemon_task_struct || AUDIT_IS_PAUSED)
+		return (0);
+	if (current->saudit_record)
+		return (1);
+
+	// alloc the audit record
+	current->saudit_record = kmalloc(sizeof(pc_class), GFP_KERNEL);
+	if (!current->saudit_record) {
+		return (0);
+	}
+
+	record = (pc_class *) current->saudit_record;
+	record->t_header.event_id = AUDIT_exit;
+	record->t_header.event_class = AUDIT_CLASS_PC;
+	saudit_return(retval);
+	return (1);
+}
+
+int _audit_fork(int retval)
+{
+	pc_class *record;
+
+	if (!saudit_active(AUDIT_fork))
+		return (0);
+	audit_hwcheck();
+	if (current == auditdaemon_task_struct || AUDIT_IS_PAUSED)
+		return (0);
+	if (current->saudit_record)
+		return (1);
+
+	// alloc the audit record
+	current->saudit_record = kmalloc(sizeof(pc_class), GFP_KERNEL);
+	if (!current->saudit_record) {
+		return (0);
+	}
+
+	record = (pc_class *) current->saudit_record;
+	record->t_header.event_id = AUDIT_fork;
+	record->t_header.event_class = AUDIT_CLASS_PC;
+	saudit_return(retval);
+	return (1);
+}
+
+int _audit_open(const char *kfile, const char __user *ufile, int flags, int mode,
+	       int retval)
+{
+	io_class *record;
+
+	if (!saudit_active(AUDIT_open))
+		return (0);
+	audit_hwcheck();
+	if (current == auditdaemon_task_struct || AUDIT_IS_PAUSED)
+		return (0);
+	if (current->saudit_record)
+		return (1);
+
+	// alloc the audit record
+	current->saudit_record = kmalloc(sizeof(io_class), GFP_KERNEL);
+	if (!current->saudit_record) {
+		return (0);
+	}
+
+	record = (io_class *) current->saudit_record;
+
+	// Filename.
+	GrabString(record->t_path.path, kfile, ufile, MAX_PATH);
+
+	record->t_attributes.mode = flags;
+	record->t_attributes.createmode = mode;
+
+	record->t_header.event_id = AUDIT_open;
+	record->t_header.event_class = AUDIT_CLASS_IO;
+
+	saudit_return(retval);
+	return (1);
+}
+
+int _audit_mkdir(const char *kfile, const char __user *ufile, int mode, int retval)
+{
+	io_class *record;
+
+	// If noone has requested the system call to be active, return quickly.
+	if (!saudit_active(AUDIT_mkdir))
+		return (0);
+	audit_hwcheck();
+	if (current == auditdaemon_task_struct || AUDIT_IS_PAUSED)
+		return (0);
+	if (current->saudit_record)
+		return (1);
+
+	// alloc the audit record
+	current->saudit_record = kmalloc(sizeof(io_class), GFP_KERNEL);
+	if (!current->saudit_record) {
+		return (0);
+	}
+
+	record = (io_class *) current->saudit_record;
+
+	// Filename.
+	GrabString(record->t_path.path, kfile, ufile, MAX_PATH);
+
+	record->t_attributes.mode = 0;
+	record->t_attributes.createmode = mode;
+
+	record->t_header.event_id = AUDIT_mkdir;
+	record->t_header.event_class = AUDIT_CLASS_IO;
+	saudit_return(retval);
+	return (1);
+}
+
+int _audit_unlink(const char *kfile, const char __user *ufile, int retval)
+{
+	io_class *record;
+
+	// If noone has requested the system call to be active, return quickly.
+	if (!saudit_active(AUDIT_unlink))
+		return (0);
+	audit_hwcheck();
+	if (current == auditdaemon_task_struct || AUDIT_IS_PAUSED)
+		return (0);
+	if (current->saudit_record)
+		return (1);
+
+	// alloc the audit record
+	current->saudit_record = kmalloc(sizeof(io_class), GFP_KERNEL);
+	if (!current->saudit_record) {
+		return (0);
+	}
+
+	record = (io_class *) current->saudit_record;
+
+	// Filename.
+	GrabString(record->t_path.path, kfile, ufile, MAX_PATH);
+
+	record->t_attributes.mode = 0;
+	record->t_attributes.createmode = 0;
+
+	record->t_header.event_id = AUDIT_unlink;
+	record->t_header.event_class = AUDIT_CLASS_IO;
+	saudit_return(retval);
+	return (1);
+}
+
+int _audit_rmdir(const char *kfile, const char __user *ufile, int retval)
+{
+	io_class *record;
+
+	// If noone has requested the system call to be active, return quickly.
+	if (!saudit_active(AUDIT_rmdir))
+		return (0);
+	audit_hwcheck();
+	if (current == auditdaemon_task_struct || AUDIT_IS_PAUSED)
+		return (0);
+	if (current->saudit_record)
+		return (1);
+
+	// alloc the audit record
+	current->saudit_record = kmalloc(sizeof(io_class), GFP_KERNEL);
+	if (!current->saudit_record) {
+		return (0);
+	}
+
+	record = (io_class *) current->saudit_record;
+
+	// Filename.
+	GrabString(record->t_path.path, kfile, ufile, MAX_PATH);
+
+	record->t_attributes.mode = 0;
+	record->t_attributes.createmode = 0;
+
+	record->t_header.event_id = AUDIT_rmdir;
+	record->t_header.event_class = AUDIT_CLASS_IO;
+	saudit_return(retval);
+	return (1);
+}
+
+int _audit_chown(const char *kfile, const char __user *ufile, uid_t user,
+		gid_t group, int retval)
+{
+	ch_class *record;
+
+	// If noone has requested the system call to be active, return quickly.
+	if (!saudit_active(AUDIT_chown))
+		return (0);
+	audit_hwcheck();
+	if (current == auditdaemon_task_struct || AUDIT_IS_PAUSED)
+		return (0);
+	if (current->saudit_record)
+		return (1);
+
+	// alloc the audit record
+	current->saudit_record = kmalloc(sizeof(ch_class), GFP_KERNEL);
+	if (!current->saudit_record) {
+		return (0);
+	}
+
+	record = (ch_class *) current->saudit_record;
+
+	// Filename.
+	GrabString(record->t_path.path, kfile, ufile, MAX_PATH);
+
+	record->t_owner.owner = (int) user;
+	record->t_owner.group = (int) group;
+
+	record->t_header.event_id = AUDIT_chown;
+	record->t_header.event_class = AUDIT_CLASS_CH;
+	saudit_return(retval);
+	return (1);
+}
+
+int _audit_chmod(const char *kfile, const char __user *ufile, mode_t mode,
+		int retval)
+{
+	io_class *record;
+
+	// If noone has requested the system call to be active, return quickly.
+	if (!saudit_active(AUDIT_chmod))
+		return (0);
+	audit_hwcheck();
+	if (current == auditdaemon_task_struct || AUDIT_IS_PAUSED)
+		return (0);
+	if (current->saudit_record)
+		return (1);
+
+	// alloc the audit record
+	current->saudit_record = kmalloc(sizeof(io_class), GFP_KERNEL);
+	if (!current->saudit_record) {
+		return (0);
+	}
+
+	record = (io_class *) current->saudit_record;
+
+	// Filename.
+	GrabString(record->t_path.path, kfile, ufile, MAX_PATH);
+
+	record->t_attributes.mode = 0;
+	record->t_attributes.createmode = (unsigned long) mode;
+
+	record->t_header.event_id = AUDIT_chmod;
+	record->t_header.event_class = AUDIT_CLASS_IO;
+	saudit_return(retval);
+	return (1);
+}
+
+int _audit_symlink(const char *kfrom, const char __user *ufrom, const char *kto,
+		  const char __user *uto, int retval)
+{
+	cp_class *record;
+
+	// If noone has requested the system call to be active, return quickly.
+	if (!saudit_active(AUDIT_symlink))
+		return (0);
+	audit_hwcheck();
+	if (current == auditdaemon_task_struct || AUDIT_IS_PAUSED)
+		return (0);
+
+	if (current->saudit_record) {
+		return (1);
+	}
+	// alloc the audit record
+	current->saudit_record = kmalloc(sizeof(cp_class), GFP_KERNEL);
+	if (!current->saudit_record) {
+		return (0);
+	}
+
+	record = (cp_class *) current->saudit_record;
+
+	// Filename.
+	GrabString(record->t_sourcepath.path, kfrom, ufrom, MAX_PATH);
+	GrabString(record->t_destpath.path, kto, uto, MAX_PATH);
+
+	record->t_header.event_id = AUDIT_symlink;
+	record->t_header.event_class = AUDIT_CLASS_CP;
+
+	saudit_return(retval);
+	return (1);
+}
+
+int _audit_link(const char *kfrom, const char __user *ufrom, const char *kto,
+	       const char __user *uto, int retval)
+{
+	cp_class *record;
+
+	// If noone has requested the system call to be active, return quickly.
+	if (!saudit_active(AUDIT_link))
+		return (0);
+	audit_hwcheck();
+	if (current == auditdaemon_task_struct || AUDIT_IS_PAUSED)
+		return (0);
+
+	if (current->saudit_record) {
+		return (1);
+	}
+	// alloc the audit record
+	current->saudit_record = kmalloc(sizeof(cp_class), GFP_KERNEL);
+	if (!current->saudit_record) {
+		return (0);
+	}
+
+	record = (cp_class *) current->saudit_record;
+
+	// Filename.
+	GrabString(record->t_sourcepath.path, kfrom, ufrom, MAX_PATH);
+	GrabString(record->t_destpath.path, kto, uto, MAX_PATH);
+
+	record->t_header.event_id = AUDIT_link;
+	record->t_header.event_class = AUDIT_CLASS_CP;
+
+	saudit_return(retval);
+	return (1);
+}
+
+int _audit_rename(const char *kfrom, const char __user *ufrom, const char *kto,
+		 const char __user *uto, int retval)
+{
+	cp_class *record;
+
+	// If noone has requested the system call to be active, return quickly.
+	if (!saudit_active(AUDIT_rename))
+		return (0);
+	audit_hwcheck();
+	if (current == auditdaemon_task_struct || AUDIT_IS_PAUSED)
+		return (0);
+	if (current->saudit_record)
+		return (1);
+
+	// alloc the audit record
+	current->saudit_record = kmalloc(sizeof(cp_class), GFP_KERNEL);
+	if (!current->saudit_record) {
+		return (0);
+	}
+
+	record = (cp_class *) current->saudit_record;
+
+	// Filename.
+	GrabString(record->t_sourcepath.path, kfrom, ufrom, MAX_PATH);
+	GrabString(record->t_destpath.path, kto, uto, MAX_PATH);
+
+	record->t_header.event_id = AUDIT_rename;
+	record->t_header.event_class = AUDIT_CLASS_CP;
+	saudit_return(retval);
+	return (1);
+}
+
+// Truncate - note that we need to grab the data from userspace.
+int _audit_truncate(const char *kfile, const char __user *ufile, loff_t length,
+		   int retval)
+{
+	io_class *record;
+
+	// If noone has requested the system call to be active, return quickly.
+	if (!saudit_active(AUDIT_truncate))
+		return (0);
+	audit_hwcheck();
+	if (current == auditdaemon_task_struct || AUDIT_IS_PAUSED)
+		return (0);
+
+	if (current->saudit_record) {
+		return (1);
+	}
+	// alloc the audit record
+	current->saudit_record = kmalloc(sizeof(io_class), GFP_KERNEL);
+	if (!current->saudit_record) {
+		return (0);
+	}
+
+	record = (io_class *) current->saudit_record;
+
+	// Filename.
+	GrabString(record->t_path.path, kfile, ufile, MAX_PATH);
+
+	record->t_attributes.createmode = (unsigned long) length;
+
+	record->t_header.event_id = AUDIT_truncate;
+	record->t_header.event_class = AUDIT_CLASS_IO;
+	saudit_return(retval);
+	return (1);
+}
+
+int _audit_chroot(const char *kfile, const char __user *ufile, int retval)
+{
+	io_class *record;
+
+	// If noone has requested the system call to be active, return quickly.
+	if (!saudit_active(AUDIT_chroot))
+		return (0);
+	audit_hwcheck();
+	if (current == auditdaemon_task_struct || AUDIT_IS_PAUSED)
+		return (0);
+
+	if (current->saudit_record) {
+		return (1);
+	}
+	// alloc the audit record
+	current->saudit_record = kmalloc(sizeof(io_class), GFP_KERNEL);
+	if (!current->saudit_record) {
+		return (0);
+	}
+
+	record = (io_class *) current->saudit_record;
+
+	// Filename.
+	GrabString(record->t_path.path, kfile, ufile, MAX_PATH);
+
+	record->t_attributes.createmode = (unsigned long) 0;
+
+	record->t_header.event_id = AUDIT_chroot;
+	record->t_header.event_class = AUDIT_CLASS_IO;
+
+	saudit_return(retval);
+	return (1);
+}
+
+int _audit_mount(const char *kdev_name, const char __user *udev_name,
+		const char *kdir_name, const char __user *udir_name,
+		unsigned long flags, int retval)
+{
+	cp_class *record;
+
+	// If noone has requested the system call to be active, return quickly.
+	if (!saudit_active(AUDIT_mount))
+		return (0);
+
+	audit_hwcheck();
+	if (current == auditdaemon_task_struct || AUDIT_IS_PAUSED)
+		return (0);
+	if (current->saudit_record)
+		return (1);
+
+	// alloc the audit record
+	current->saudit_record = kmalloc(sizeof(cp_class), GFP_KERNEL);
+	if (!current->saudit_record) {
+		return (0);
+	}
+
+	record = (cp_class *) current->saudit_record;
+
+	GrabString(record->t_sourcepath.path, kdev_name, udev_name,
+		   MAX_PATH);
+	GrabString(record->t_sourcepath.path, kdir_name, udir_name,
+		   MAX_PATH);
+
+	record->t_header.event_id = AUDIT_mount;
+	record->t_header.event_class = AUDIT_CLASS_CP;
+
+	saudit_return(retval);
+	return (1);
+}
+
+int _audit_umount(const char *kname, const char __user *uname, int flags,
+		 int retval)
+{
+	cp_class *record;
+
+	// If noone has requested the system call to be active, return quickly.
+	if (!saudit_active(AUDIT_umount))
+		return (0);
+	audit_hwcheck();
+	if (current == auditdaemon_task_struct || AUDIT_IS_PAUSED)
+		return (0);
+	if (current->saudit_record)
+		return (1);
+
+	// alloc the audit record
+	current->saudit_record = kmalloc(sizeof(cp_class), GFP_KERNEL);
+	if (!current->saudit_record) {
+		return (0);
+	}
+
+	record = (cp_class *) current->saudit_record;
+
+	record->t_sourcepath.path[0] = '\0';
+	// Name has not yet been grabbed from userspace by sys_umount
+	// Copy it from userspace here.
+	GrabString(record->t_sourcepath.path, kname, uname, MAX_PATH);
+
+	record->t_destpath.path[0] = '\0';
+
+	record->t_header.event_id = AUDIT_umount;
+	record->t_header.event_class = AUDIT_CLASS_CP;
+	saudit_return(retval);
+	return (1);
+}
+
+int _audit_setuid(uid_t uid, int retval)
+{
+	su_class *record;
+
+	// If noone has requested the system call to be active, return quickly.
+	if (!saudit_active(AUDIT_setuid))
+		return (0);
+	audit_hwcheck();
+	if (current == auditdaemon_task_struct || AUDIT_IS_PAUSED)
+		return (0);
+	if (current->saudit_record)
+		return (1);
+
+	// alloc the audit record
+	current->saudit_record = kmalloc(sizeof(su_class), GFP_KERNEL);
+	if (!current->saudit_record) {
+		return (0);
+	}
+
+	record = (su_class *) current->saudit_record;
+
+	record->t_target.id = uid;
+
+	record->t_header.event_id = AUDIT_setuid;
+	record->t_header.event_class = AUDIT_CLASS_SU;
+	saudit_return(retval);
+	return (1);
+}
+
+int _audit_setreuid(uid_t ruid, uid_t euid, int retval)
+{
+	su_class *record;
+
+	// If noone has requested the system call to be active, return quickly.
+	if (!saudit_active(AUDIT_setreuid))
+		return (0);
+	audit_hwcheck();
+	if (current == auditdaemon_task_struct || AUDIT_IS_PAUSED)
+		return (0);
+	if (current->saudit_record)
+		return (1);
+
+	// alloc the audit record
+	current->saudit_record = kmalloc(sizeof(su_class), GFP_KERNEL);
+	if (!current->saudit_record) {
+		return (0);
+	}
+
+	record = (su_class *) current->saudit_record;
+
+	record->t_target.id = euid;
+	record->t_target.rid = ruid;
+
+	record->t_header.event_id = AUDIT_setreuid;
+	record->t_header.event_class = AUDIT_CLASS_SU;
+	saudit_return(retval);
+	return (1);
+}
+
+int _audit_setresuid(uid_t ruid, uid_t euid, uid_t suid, int retval)
+{
+	su_class *record;
+
+	// If noone has requested the system call to be active, return quickly.
+	if (!saudit_active(AUDIT_setresuid))
+		return (0);
+	audit_hwcheck();
+	if (current == auditdaemon_task_struct || AUDIT_IS_PAUSED)
+		return (0);
+	if (current->saudit_record)
+		return (1);
+
+	// alloc the audit record
+	current->saudit_record = kmalloc(sizeof(su_class), GFP_KERNEL);
+	if (!current->saudit_record) {
+		return (0);
+	}
+
+	record = (su_class *) current->saudit_record;
+
+	record->t_target.id = euid;
+	record->t_target.rid = ruid;
+	record->t_target.sid = suid;
+
+	record->t_header.event_id = AUDIT_setresuid;
+	record->t_header.event_class = AUDIT_CLASS_SU;
+	saudit_return(retval);
+	return (1);
+}
+
+int _audit_setregid(gid_t rgid, gid_t egid, int retval)
+{
+	su_class *record;
+
+	// If noone has requested the system call to be active, return quickly.
+	if (!saudit_active(AUDIT_setregid))
+		return (0);
+	audit_hwcheck();
+	if (current == auditdaemon_task_struct || AUDIT_IS_PAUSED)
+		return (0);
+	if (current->saudit_record)
+		return (1);
+
+	// alloc the audit record
+	current->saudit_record = kmalloc(sizeof(su_class), GFP_KERNEL);
+	if (!current->saudit_record) {
+		return (0);
+	}
+
+	record = (su_class *) current->saudit_record;
+
+	record->t_target.id = egid;
+	record->t_target.rid = rgid;
+
+	record->t_header.event_id = AUDIT_setregid;
+	record->t_header.event_class = AUDIT_CLASS_SU;
+	saudit_return(retval);
+	return (1);
+}
+
+int _audit_setresgid(gid_t rgid, gid_t egid, gid_t sgid, int retval)
+{
+	su_class *record;
+
+	// If noone has requested the system call to be active, return quickly.
+	if (!saudit_active(AUDIT_setresgid))
+		return (0);
+	audit_hwcheck();
+	if (current == auditdaemon_task_struct || AUDIT_IS_PAUSED)
+		return (0);
+	if (current->saudit_record)
+		return (1);
+
+	// alloc the audit record
+	current->saudit_record = kmalloc(sizeof(su_class), GFP_KERNEL);
+	if (!current->saudit_record) {
+		return (0);
+	}
+
+	record = (su_class *) current->saudit_record;
+
+	record->t_target.id = egid;
+	record->t_target.rid = rgid;
+	record->t_target.sid = sgid;
+
+	record->t_header.event_id = AUDIT_setresgid;
+	record->t_header.event_class = AUDIT_CLASS_SU;
+	saudit_return(retval);
+	return (1);
+}
+
+int _audit_setgid(gid_t gid, int retval)
+{
+	su_class *record;
+
+	// If noone has requested the system call to be active, return quickly.
+	if (!saudit_active(AUDIT_setgid))
+		return (0);
+	audit_hwcheck();
+	if (current == auditdaemon_task_struct || AUDIT_IS_PAUSED)
+		return (0);
+	if (current->saudit_record)
+		return (1);
+
+	// alloc the audit record
+	current->saudit_record = kmalloc(sizeof(su_class), GFP_KERNEL);
+	if (!current->saudit_record) {
+		return (0);
+	}
+
+	record = (su_class *) current->saudit_record;
+
+	record->t_target.id = gid;
+
+	record->t_header.event_id = AUDIT_setgid;
+	record->t_header.event_class = AUDIT_CLASS_SU;
+	saudit_return(retval);
+	return (1);
+}
+
+// Create module
+int _audit_create_module(const char *kname, const char __user *uname, int retval)
+{
+	ad_class *record;
+
+	// If noone has requested the system call to be active, return quickly.
+	if (!saudit_active(AUDIT_create_module))
+		return (0);
+	audit_hwcheck();
+	if (current == auditdaemon_task_struct || AUDIT_IS_PAUSED)
+		return (0);
+
+	if (current->saudit_record) {
+		return (1);
+	}
+	// alloc the audit record
+	current->saudit_record = kmalloc(sizeof(ad_class), GFP_KERNEL);
+	if (!current->saudit_record) {
+		return (0);
+	}
+
+	record = (ad_class *) current->saudit_record;
+
+	// Module name
+	GrabString(record->t_name.path, kname, uname, MAX_PATH);
+
+	record->t_header.event_id = AUDIT_create_module;
+	record->t_header.event_class = AUDIT_CLASS_AD;
+	saudit_return(retval);
+	return (1);
+}
+
+// Delete module
+int _audit_delete_module(const char *kname, const char __user *uname, int retval)
+{
+	ad_class *record;
+
+	// If noone has requested the system call to be active, return quickly.
+	if (!saudit_active(AUDIT_delete_module))
+		return (0);
+	audit_hwcheck();
+	if (current == auditdaemon_task_struct || AUDIT_IS_PAUSED)
+		return (0);
+
+	if (current->saudit_record) {
+		return (1);
+	}
+	// alloc the audit record
+	current->saudit_record = kmalloc(sizeof(ad_class), GFP_KERNEL);
+	if (!current->saudit_record) {
+		return (0);
+	}
+
+	record = (ad_class *) current->saudit_record;
+
+	// Module name
+	GrabString(record->t_name.path, kname, uname, MAX_PATH);
+
+	record->t_header.event_id = AUDIT_delete_module;
+	record->t_header.event_class = AUDIT_CLASS_AD;
+	saudit_return(retval);
+	return (1);
+}
+
+int _audit_reboot(int magic1, int magic2, unsigned int cmd, void __user *arg,
+		 int retval)
+{
+	pc_class *record;
+
+	// If noone has requested the system call to be active, return quickly.
+	if (!saudit_active(AUDIT_reboot))
+		return (0);
+	audit_hwcheck();
+	if (current == auditdaemon_task_struct || AUDIT_IS_PAUSED)
+		return (0);
+	if (current->saudit_record)
+		return (1);
+
+	// alloc the audit record
+	current->saudit_record = kmalloc(sizeof(pc_class), GFP_KERNEL);
+	if (!current->saudit_record) {
+		return (0);
+	}
+
+	record = (pc_class *) current->saudit_record;
+	record->t_header.event_id = AUDIT_reboot;
+	record->t_header.event_class = AUDIT_CLASS_PC;
+	saudit_return(retval);
+	return (1);
+}
+
+int _audit_connect(int sockfd, struct sockaddr __user *serv_addr, int addrlen,
+		  int retval)
+{
+	nt_class *record;
+	struct socket *sock;
+	int err;
+	struct sockaddr_in *si;
+
+	// If noone has requested the system call to be active, return quickly.
+	if (!saudit_active(AUDIT_connect)) {
+		return (0);
+	}
+
+	audit_hwcheck();
+	if (current == auditdaemon_task_struct || AUDIT_IS_PAUSED)
+		return (0);
+	if (current->saudit_record)
+		return (0);
+
+	// If this is not an AF_INET, drop it.
+	if (!serv_addr) {
+		return (0);
+	}
+	if (serv_addr->sa_family != AF_INET) {
+		return (0);
+	}
+
+	if (serv_addr->sa_data) {
+		return (0);
+	}
+
+	// alloc the audit record
+	current->saudit_record = kmalloc(sizeof(nt_class), GFP_KERNEL);
+	if (!current->saudit_record) {
+		return (0);
+	}
+
+	record = (nt_class *) current->saudit_record;
+	record->t_header.event_id = AUDIT_connect;
+	record->t_header.event_class = AUDIT_CLASS_NET;
+
+	snprintf(record->t_connection.dst_ip, 16, "%u.%u.%u.%u",
+		 serv_addr->sa_data[2] & 255,
+		 serv_addr->sa_data[3] & 255,
+		 serv_addr->sa_data[4] & 255, serv_addr->sa_data[5] & 255);
+	strncpy(record->t_connection.src_ip, "127.0.0.1", 16);
+
+	si = (struct sockaddr_in *) serv_addr;
+
+	record->t_connection.src_port = 0;
+	record->t_connection.dst_port = ntohs(si->sin_port);
+
+	record->t_connection.protocol = 0;
+
+	if(sockfd) {
+		if ((sock = sockfd_lookup(sockfd, &err)) != NULL) {
+			// 2.4
+			// record->t_connection.src_port = sock->sk->num;
+			// record->t_connection.protocol = sock->sk->protocol;
+			// 2.6
+			record->t_connection.src_port = htons(inet_sk(sock->sk)->num);
+			record->t_connection.protocol = 0;
+			// fput(sock->file);
+			sockfd_put(sock);
+		}
+	}
+	saudit_return(retval);
+	return (1);
+}
+
+int _audit_accept(int sockfd, struct sockaddr __user *peer_addr, int *addrlen,
+		 int retval)
+{
+	nt_class *record;
+	struct sockaddr_in *si;
+	struct socket *sock;
+	int err;
+
+	// If noone has requested the system call to be active, return quickly.
+	if (!saudit_active(AUDIT_accept)) {
+		return (0);
+	}
+
+	audit_hwcheck();
+	if (current == auditdaemon_task_struct || AUDIT_IS_PAUSED)
+		return (0);
+	if (current->saudit_record)
+		return (0);
+
+	// If this is not an AF_INET, drop it.
+	if (!peer_addr) {
+		return (0);
+	}
+	if (peer_addr->sa_family != AF_INET) {
+		return (0);
+	}
+
+	if (peer_addr->sa_data) {
+		return (0);
+	}
+	// alloc the audit record
+	current->saudit_record = kmalloc(sizeof(nt_class), GFP_KERNEL);
+	if (!current->saudit_record) {
+		return (0);
+	}
+
+	record = (nt_class *) current->saudit_record;
+	record->t_header.event_id = AUDIT_accept;
+	record->t_header.event_class = AUDIT_CLASS_NET;
+
+	snprintf(record->t_connection.src_ip, 16, "%u.%u.%u.%u",
+		 peer_addr->sa_data[2] & 255,
+		 peer_addr->sa_data[3] & 255,
+		 peer_addr->sa_data[4] & 255, peer_addr->sa_data[5] & 255);
+	strncpy(record->t_connection.dst_ip, "127.0.0.1", 16);
+
+	si = (struct sockaddr_in *) peer_addr;
+
+	record->t_connection.dst_port = 0;
+	record->t_connection.src_port = ntohs(si->sin_port);
+
+	record->t_connection.protocol = 0;
+
+	if(sockfd) {
+		if ((sock = sockfd_lookup(sockfd, &err)) != NULL) {
+			// 2.4
+			// record->t_connection.dst_port = ntohs(sock->sk->sport);
+			// record->t_connection.protocol = sock->sk->protocol;
+			// 2.6
+			record->t_connection.dst_port = ntohs(inet_sk(sock->sk)->sport);
+			record->t_connection.protocol = 0;
+			sockfd_put(sock);
+			// fput(sock->file);
+		}
+	}
+	saudit_return(retval);
+	return (1);
+}
+
+int saudit_return(int returncode)
+{
+	// The static null_class and timeval were changed to 
+	// Local varable for smp machines
+
+	int class;
+	null_class *any_event;
+	AuditNode *node = (AuditNode *) NULL;
+	struct timeval time_temp;
+
+	if (!auditdaemon_task_struct) {
+		goto out;
+	}
+
+	if (current == auditdaemon_task_struct) {
+		return (0);
+	}
+
+	// Unaudited event
+	if (!current->saudit_record) {
+		goto out;
+	}
+
+	any_event = current->saudit_record;
+
+	do_gettimeofday(&time_temp);
+
+	class = any_event->t_header.event_class;
+
+	// Fill out as much of the header structure as we can.
+	any_event->t_header.user_id = current->uid;
+	any_event->t_header.group_id = current->gid;
+	any_event->t_header.euser_id = current->euid;
+	any_event->t_header.egroup_id = current->egid;
+	any_event->t_header.time.tv_sec = time_temp.tv_sec;
+	any_event->t_header.time.tv_usec = time_temp.tv_usec;
+	any_event->t_header.returncode = returncode;
+	strncpy(any_event->t_header.processname, current->comm,
+		MAXCOMMAND);
+	any_event->t_header.pid = current->pid;
+
+#if defined(SNARE_RED_HAT_LINUX_KERNEL)
+	// Note: RHEL 2.4.18 doesn't have current->parent..
+	if (current->parent) {
+		any_event->t_header.ppid = current->parent->pid;
+	} else {
+		any_event->t_header.ppid = 0;
+	}
+#else
+	if(current->p_pptr) {
+		any_event->t_header.ppid=current->p_pptr->pid;
+	} else {
+		any_event->t_header.ppid=0;
+	}
+#endif
+
+	// Modified by Mark Westerman Replaced the if else code with
+	// a switch to help perfromace.
+	switch (class) {
+	case AUDIT_CLASS_IO:
+		{
+			io_class *record;
+			char tmp[MAX_PATH];
+			record = (io_class *) current->saudit_record;
+
+			// Current PWD - IO CLASS, EXEC, CH, CP
+			// NOTE: This will just tell you the file name the user
+			// tried to access. If it's linked to another file, or
+			// from a mountpoint, it will just show you the filename
+			// used in the system call - not the RESOLVED path.
+			strncpy(record->t_pwd.path,
+				d_path(current->fs->pwd,
+				       current->fs->pwdmnt, tmp, MAX_PATH),
+				MAX_PATH);
+
+			record->t_header.event_size =
+			    sizeof(io_class) - sizeof(header_token);
+
+			if ((node = alloc_event(sizeof(io_class))) == NULL) {
+				goto out;
+			}
+			node->location=record;
+		}
+		break;
+	case AUDIT_CLASS_EXEC:
+		{
+			ex_class *record;
+			char tmp[MAX_PATH];
+			record = (ex_class *) current->saudit_record;
+
+			// Current PWD - IO CLASS, EXEC, CH, CP
+			strncpy(record->t_pwd.path,
+				d_path(current->fs->pwd,
+				       current->fs->pwdmnt, tmp, MAX_PATH),
+				MAX_PATH);
+
+			record->t_header.event_size =
+			    sizeof(ex_class) - sizeof(header_token);
+
+			if ((node = alloc_event(sizeof(ex_class))) == NULL) {
+				goto out;
+			}
+			node->location=record;
+		}
+		break;
+	case AUDIT_CLASS_PC:
+		{
+			pc_class *record;
+			record = (pc_class *) current->saudit_record;
+
+			// Current PWD - IO CLASS, EXEC, CH, CP
+			record->t_header.event_size =
+			    sizeof(pc_class) - sizeof(header_token);
+
+			if ((node = alloc_event(sizeof(pc_class))) == NULL) {
+				goto out;
+			}
+			node->location=record;
+		}
+		break;
+	case AUDIT_CLASS_CH:
+		{
+			ch_class *record;
+			char tmp[MAX_PATH];
+			record = (ch_class *) current->saudit_record;
+
+			// Current PWD - IO CLASS, EXEC, CH, CP
+			strncpy(record->t_pwd.path,
+				d_path(current->fs->pwd,
+				       current->fs->pwdmnt, tmp, MAX_PATH),
+				MAX_PATH);
+			record->t_header.event_size =
+			    sizeof(ch_class) - sizeof(header_token);
+
+			if ((node = alloc_event(sizeof(ch_class))) == NULL) {
+				goto out;
+			}
+			node->location=record;
+		}
+		break;
+	case AUDIT_CLASS_CP:
+		{
+			cp_class *record;
+			char tmp[MAX_PATH];
+			record = (cp_class *) current->saudit_record;
+
+			// Current PWD - IO CLASS, EXEC, CH, CP
+			strncpy(record->t_pwd.path,
+				d_path(current->fs->pwd,
+				       current->fs->pwdmnt, tmp, MAX_PATH),
+				MAX_PATH);
+			record->t_header.event_size =
+			    sizeof(cp_class) - sizeof(header_token);
+
+			if ((node = alloc_event(sizeof(cp_class))) == NULL) {
+				goto out;
+			}
+			node->location=record;
+		}
+		break;
+	case AUDIT_CLASS_SU:
+		{
+			su_class *record;
+			record = (su_class *) current->saudit_record;
+
+			record->t_header.event_size =
+			    sizeof(su_class) - sizeof(header_token);
+
+			if ((node = alloc_event(sizeof(su_class))) == NULL) {
+				goto out;
+			}
+			node->location=record;
+		}
+		break;
+	case AUDIT_CLASS_AD:
+		{
+			ad_class *record;
+			record = (ad_class *) current->saudit_record;
+
+			record->t_header.event_size =
+			    sizeof(ad_class) - sizeof(header_token);
+
+			if ((node = alloc_event(sizeof(ad_class))) == NULL) {
+				goto out;
+			}
+			node->location=record;
+		}
+		break;
+	case AUDIT_CLASS_NET:
+		{
+			nt_class *record;
+			record = (nt_class *) current->saudit_record;
+
+			record->t_header.event_size =
+			    sizeof(nt_class) - sizeof(header_token);
+
+			if ((node = alloc_event(sizeof(nt_class))) == NULL) {
+				goto out;
+			}
+			node->location=record;
+
+		}
+		break;
+	default:
+		printk("Auditing Invalid class\n");
+	}
+
+	// Cant free this here.. we're still using it.
+	current->saudit_record = NULL;
+
+	// Send the audit record out the door.
+	append_event(node);
+	wake_up_interruptible(&proc_audit_queue);
+
+	return (1);
+
+      out:
+	if (current->saudit_record) {
+		current->saudit_record = NULL;
+	}
+	if(auditdaemon_task_struct) {
+		down(&audit_lock);
+		lost_events++;
+		up(&audit_lock);
+	}
+	return (0);
+}
+
+static void append_event(AuditNode * nodepointer)
+{
+	down(&audit_lock);
+
+	if (list_tail != (AuditNode *) NULL) {
+		list_tail->next = nodepointer;
+	}
+
+	list_tail = nodepointer;
+
+	// Is this the first node in our linked list?
+	if (list_head == (AuditNode *) NULL) {
+		list_head = nodepointer;
+	}
+	memory_usage += nodepointer->size;
+	++total_events;
+	up(&audit_lock);
+}
+
+AuditNode *alloc_event(int token_size)
+{
+	AuditNode *nodepointer;
+
+	nodepointer = (AuditNode *) kmalloc(sizeof(AuditNode), GFP_KERNEL);
+	if (nodepointer == (AuditNode *) NULL) {
+		return (0);
+	}
+
+	nodepointer->location = (void *)NULL;
+	nodepointer->size = token_size;
+	nodepointer->next = (AuditNode *) NULL;	// This is the end.
+	return nodepointer;
+}
+
+// This grabs data from either kernel or userspace into a string.
+long GrabString(char *destination, const char *kernelstring,
+		const char __user *userstring, int length)
+{
+	if (!destination)
+		return (0);
+
+	if (length < 1)
+		return (0);
+
+	if (!userstring && !kernelstring) {
+		strncpy(destination, "AUDIT: Invalid data", length);
+		return (0);
+	}
+
+	if (kernelstring) {
+		strncpy(destination, kernelstring, length);
+		return (1);
+	} else {
+		if (strncpy_from_user(destination, userstring, length) ==
+		    -EFAULT) {
+			strncpy(destination, "AUDIT: Invalid data",
+				length);
+			return (0);
+		}
+		return (1);
+	}
+}
+#endif

