--- linux-2.4.20-gentoo-r1/Makefile	Sun Jan 19 19:26:03 2003
+++ linux-2.4.20-gentoo-r1-systrace-v2/Makefile	Sun Jan 19 20:19:22 2003
@@ -1,7 +1,7 @@
 VERSION = 2
 PATCHLEVEL = 4
 SUBLEVEL = 20
-EXTRAVERSION = -gentoo-r1
+EXTRAVERSION = -x
 
 KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
 
@@ -156,6 +156,7 @@
 DRIVERS-$(CONFIG_SCSI) += drivers/scsi/scsidrv.o
 DRIVERS-$(CONFIG_FUSION_BOOT) += drivers/message/fusion/fusion.o
 DRIVERS-$(CONFIG_IEEE1394) += drivers/ieee1394/ieee1394drv.o
+DRIVERS-$(CONFIG_SYSTRACE) += drivers/systrace/str.o
 
 ifneq ($(CONFIG_CD_NO_IDESCSI)$(CONFIG_BLK_DEV_IDECD)$(CONFIG_BLK_DEV_SR)$(CONFIG_PARIDE_PCD),)
 DRIVERS-y += drivers/cdrom/driver.o
--- linux-2.4.20-gentoo-r1/arch/i386/config.in	Sun Jan 19 19:26:03 2003
+++ linux-2.4.20-gentoo-r1-systrace-v2/arch/i386/config.in	Sun Jan 19 20:03:13 2003
@@ -313,6 +313,8 @@
 tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF
 tristate 'Kernel support for MISC binaries' CONFIG_BINFMT_MISC
 
+bool 'Systrace support' CONFIG_SYSTRACE
+
 bool 'Power Management support' CONFIG_PM
 
 dep_tristate '  Advanced Power Management BIOS support' CONFIG_APM $CONFIG_PM
--- linux-2.4.20-gentoo-r1/arch/i386/kernel/entry.S	Sun Jan 19 19:26:03 2003
+++ linux-2.4.20-gentoo-r1-systrace-v2/arch/i386/kernel/entry.S	Sun Jan 19 20:03:14 2003
@@ -230,8 +230,21 @@
 	jne tracesys
 	cmpl $(NR_syscalls),%eax
 	jae badsys
+#ifdef CONFIG_SYSTRACE
+	movl %esp,%eax
+	call SYMBOL_NAME(systrace_intercept)
+	cmpl $0,%eax
+	jl ret
+	movl ORIG_EAX(%esp),%eax
+#endif /* CONFIG_SYSTRACE */
 	call *SYMBOL_NAME(sys_call_table)(,%eax,4)
+ret:
 	movl %eax,EAX(%esp)		# save the return value
+#ifdef CONFIG_SYSTRACE
+	movl %esp,%eax                  # pass in stack
+	call SYMBOL_NAME(systrace_result)
+	movl EAX(%esp),%eax             # XXX: ?to be on the safe side
+#endif /* CONFIG_SYSTRACE */
 
 #ifdef CONFIG_GRKERNSEC_PAX_RANDKSTACK
 	cli                             # need_resched and signals atomic test
--- linux-2.4.20-gentoo-r1/drivers/Makefile	Sun Jan 19 19:26:03 2003
+++ linux-2.4.20-gentoo-r1-systrace-v2/drivers/Makefile	Sun Jan 19 20:02:46 2003
@@ -41,6 +41,7 @@
 subdir-$(CONFIG_ISDN_BOOL)	+= isdn
 subdir-$(CONFIG_ATM)		+= atm
 subdir-$(CONFIG_FC4)		+= fc4
+subdir-$(CONFIG_SYSTRACE)	+= systrace
 
 # CONFIG_HAMRADIO can be set without CONFIG_NETDEVICE being set  -- ch
 subdir-$(CONFIG_HAMRADIO)	+= net/hamradio
diff -urN linux-2.4.20/drivers/systrace/Makefile linux-2.4.20-04-hdrfix/drivers/systrace/Makefile
--- linux-2.4.20/drivers/systrace/Makefile	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.4.20-04-hdrfix/drivers/systrace/Makefile	2002-12-02 00:54:27.000000000 -0500
@@ -0,0 +1,6 @@
+O_TARGET := str.o
+
+obj-y    := systrace.o policy.o linux_sysent.o
+
+include $(TOPDIR)/Rules.make
+
diff -urN linux-2.4.20/drivers/systrace/linux_sysent.c linux-2.4.20-04-hdrfix/drivers/systrace/linux_sysent.c
--- linux-2.4.20/drivers/systrace/linux_sysent.c	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.4.20-04-hdrfix/drivers/systrace/linux_sysent.c	2003-01-18 13:35:38.000000000 -0500
@@ -0,0 +1,261 @@
+/*
+ * file taken from openbsd's compat/linux/linux_sysent.c
+ */
+
+/*	$OpenBSD: linux_sysent.c,v 1.36 2002/06/05 19:43:44 jasoni Exp $	*/
+
+
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+#include <linux/fs.h>
+#include <linux/wait.h>
+#include <linux/queue.h>
+
+#include <asm/uaccess.h>
+#include <asm/ptrace.h>
+#include <asm/semaphore.h>
+
+#include <linux/systrace.h>
+
+/* #define	s(type)	sizeof(type) */
+#define	s(type)	0
+
+struct sysent linux_sysent[] = {
+	{ 0, 0 },			/* 0 = syscall */
+	{ 1, s(struct sys_exit_args) },				/* 1 = exit */
+	{ 0, 0 },				/* 2 = fork */
+	{ 3, s(struct sys_read_args) },				/* 3 = read */
+	{ 3, s(struct sys_write_args) },			/* 4 = write */
+	{ 3, s(struct linux_sys_open_args) },			/* 5 = open */
+	{ 1, s(struct sys_close_args) },			/* 6 = close */
+	{ 3, s(struct linux_sys_waitpid_args) },		/* 7 = waitpid */
+	{ 2, s(struct linux_sys_creat_args) },			/* 8 = creat */
+	{ 2, s(struct sys_link_args) },				/* 9 = link */
+	{ 1, s(struct linux_sys_unlink_args) },			/* 10 = unlink */
+	{ 3, s(struct linux_sys_execve_args) },			/* 11 = execve */
+	{ 1, s(struct linux_sys_chdir_args) },			/* 12 = chdir */
+	{ 1, s(struct linux_sys_time_args) },			/* 13 = time */
+	{ 3, s(struct linux_sys_mknod_args) },			/* 14 = mknod */
+	{ 2, s(struct linux_sys_chmod_args) },			/* 15 = chmod */
+	{ 3, s(struct linux_sys_lchown16_args) },		/* 16 = lchown16 */
+	{ 1, s(struct linux_sys_break_args) },			/* 17 = break */
+	{ 0, 0 },			/* 18 = ostat */
+	{ 3, s(struct compat_43_sys_lseek_args) },		/* 19 = lseek */
+	{ 0, 0 },			/* 20 = getpid */
+	{ 5, s(struct linux_sys_mount_args) },			/* 21 = mount */
+	{ 1, s(struct linux_sys_umount_args) },			/* 22 = umount */
+	{ 1, s(struct sys_setuid_args) },			/* 23 = linux_setuid16 */
+	{ 0, 0 },			/* 24 = linux_getuid16 */
+	{ 1, s(struct linux_sys_stime_args) },			/* 25 = stime */
+	{ 0, 0 },			/* 26 = unimplemented ptrace */
+	{ 1, s(struct linux_sys_alarm_args) },			/* 27 = alarm */
+	{ 0, 0 },			/* 28 = ofstat */
+	{ 0, 0 },			/* 29 = pause */
+	{ 2, s(struct linux_sys_utime_args) },			/* 30 = utime */
+	{ 0, 0 },			/* 31 = stty */
+	{ 0, 0 },			/* 32 = gtty */
+	{ 2, s(struct linux_sys_access_args) },			/* 33 = access */
+	{ 1, s(struct linux_sys_nice_args) },			/* 34 = nice */
+	{ 0, 0 },			/* 35 = ftime */
+	{ 0, 0 },				/* 36 = sync */
+	{ 2, s(struct linux_sys_kill_args) },			/* 37 = kill */
+	{ 2, s(struct linux_sys_rename_args) },			/* 38 = rename */
+	{ 2, s(struct linux_sys_mkdir_args) },			/* 39 = mkdir */
+	{ 1, s(struct linux_sys_rmdir_args) },			/* 40 = rmdir */
+	{ 1, s(struct sys_dup_args) },				/* 41 = dup */
+	{ 1, s(struct linux_sys_pipe_args) },			/* 42 = pipe */
+	{ 1, s(struct linux_sys_times_args) },			/* 43 = times */
+	{ 0, 0 },			/* 44 = prof */
+	{ 1, s(struct linux_sys_brk_args) },			/* 45 = brk */
+	{ 1, s(struct sys_setgid_args) },			/* 46 = linux_setgid16 */
+	{ 0, 0 },			/* 47 = linux_getgid16 */
+	{ 2, s(struct linux_sys_signal_args) },			/* 48 = signal */
+	{ 0, 0 },			/* 49 = linux_geteuid16 */
+	{ 0, 0 },			/* 50 = linux_getegid16 */
+	{ 1, s(struct sys_acct_args) },				/* 51 = acct */
+	{ 0, 0 },			/* 52 = phys */
+	{ 0, 0 },			/* 53 = lock */
+	{ 3, s(struct linux_sys_ioctl_args) },			/* 54 = ioctl */
+	{ 3, s(struct linux_sys_fcntl_args) },			/* 55 = fcntl */
+	{ 0, 0 },			/* 56 = mpx */
+	{ 2, s(struct sys_setpgid_args) },			/* 57 = setpgid */
+	{ 0, 0 },			/* 58 = ulimit */
+	{ 1, s(struct linux_sys_oldolduname_args) },		/* 59 = oldolduname */
+	{ 1, s(struct sys_umask_args) },			/* 60 = umask */
+	{ 1, s(struct sys_chroot_args) },			/* 61 = chroot */
+	{ 0, 0 },			/* 62 = ustat */
+	{ 2, s(struct sys_dup2_args) },				/* 63 = dup2 */
+	{ 0, 0 },			/* 64 = getppid */
+	{ 0, 0 },			/* 65 = getpgrp */
+	{ 0, 0 },			/* 66 = setsid */
+	{ 3, s(struct linux_sys_sigaction_args) },		/* 67 = sigaction */
+	{ 0, 0 },		/* 68 = siggetmask */
+	{ 1, s(struct linux_sys_sigsetmask_args) },		/* 69 = sigsetmask */
+	{ 2, s(struct linux_sys_setreuid16_args) },		/* 70 = setreuid16 */
+	{ 2, s(struct linux_sys_setregid16_args) },		/* 71 = setregid16 */
+	{ 3, s(struct linux_sys_sigsuspend_args) },		/* 72 = sigsuspend */
+	{ 1, s(struct linux_sys_sigpending_args) },		/* 73 = sigpending */
+	{ 2, s(struct compat_43_sys_sethostname_args) },	/* 74 = sethostname */
+	{ 2, s(struct linux_sys_setrlimit_args) },		/* 75 = setrlimit */
+	{ 2, s(struct linux_sys_getrlimit_args) },		/* 76 = getrlimit */
+	{ 2, s(struct sys_getrusage_args) },			/* 77 = getrusage */
+	{ 2, s(struct sys_gettimeofday_args) },			/* 78 = gettimeofday */
+	{ 2, s(struct sys_settimeofday_args) },			/* 79 = settimeofday */
+	{ 2, s(struct sys_getgroups_args) },			/* 80 = linux_getgroups */
+	{ 2, s(struct sys_setgroups_args) },			/* 81 = linux_setgroups */
+	{ 1, s(struct linux_sys_oldselect_args) },		/* 82 = oldselect */
+	{ 2, s(struct linux_sys_symlink_args) },		/* 83 = symlink */
+	{ 2, s(struct compat_43_sys_lstat_args) },		/* 84 = olstat */
+	{ 3, s(struct linux_sys_readlink_args) },		/* 85 = readlink */
+	{ 1, s(struct linux_sys_uselib_args) },			/* 86 = uselib */
+	{ 1, s(struct sys_swapon_args) },			/* 87 = swapon */
+	{ 1, s(struct sys_reboot_args) },			/* 88 = reboot */
+	{ 3, s(struct linux_sys_readdir_args) },		/* 89 = readdir */
+	{ 1, s(struct linux_sys_mmap_args) },			/* 90 = mmap */
+	{ 2, s(struct sys_munmap_args) },			/* 91 = munmap */
+	{ 2, s(struct linux_sys_truncate_args) },		/* 92 = truncate */
+	{ 2, s(struct compat_43_sys_ftruncate_args) },		/* 93 = ftruncate */
+	{ 2, s(struct sys_fchmod_args) },			/* 94 = fchmod */
+	{ 3, s(struct linux_sys_fchown16_args) },		/* 95 = fchown16 */
+	{ 2, s(struct sys_getpriority_args) },			/* 96 = getpriority */
+	{ 3, s(struct sys_setpriority_args) },			/* 97 = setpriority */
+	{ 4, s(struct sys_profil_args) },			/* 98 = profil */
+	{ 2, s(struct linux_sys_statfs_args) },			/* 99 = statfs */
+	{ 2, s(struct linux_sys_fstatfs_args) },		/* 100 = fstatfs */
+#ifdef __i386__
+	{ 3, s(struct linux_sys_ioperm_args) },			/* 101 = ioperm */
+#else
+	{ 0, 0 },			/* 101 = ioperm */
+#endif
+	{ 2, s(struct linux_sys_socketcall_args) },		/* 102 = socketcall */
+	{ 0, 0 },			/* 103 = klog */
+	{ 3, s(struct sys_setitimer_args) },			/* 104 = setitimer */
+	{ 2, s(struct sys_getitimer_args) },			/* 105 = getitimer */
+	{ 2, s(struct linux_sys_stat_args) },			/* 106 = stat */
+	{ 2, s(struct linux_sys_lstat_args) },			/* 107 = lstat */
+	{ 2, s(struct linux_sys_fstat_args) },			/* 108 = fstat */
+	{ 1, s(struct linux_sys_olduname_args) },		/* 109 = olduname */
+#ifdef __i386__
+	{ 1, s(struct linux_sys_iopl_args) },			/* 110 = iopl */
+#else
+	{ 0, 0 },			/* 110 = iopl */
+#endif
+	{ 0, 0 },		/* 111 = vhangup */
+	{ 0, 0 },			/* 112 = idle */
+	{ 0, 0 },		/* 113 = vm86old */
+	{ 4, s(struct linux_sys_wait4_args) },			/* 114 = wait4 */
+	{ 0, 0 },		/* 115 = swapoff */
+	{ 0, 0 },		/* 116 = sysinfo */
+	{ 5, s(struct linux_sys_ipc_args) },			/* 117 = ipc */
+	{ 1, s(struct sys_fsync_args) },			/* 118 = fsync */
+	{ 1, s(struct linux_sys_sigreturn_args) },		/* 119 = sigreturn */
+	{ 2, s(struct linux_sys_clone_args) },			/* 120 = clone */
+	{ 2, s(struct compat_09_sys_setdomainname_args) },	/* 121 = setdomainname */
+	{ 1, s(struct linux_sys_uname_args) },			/* 122 = uname */
+#ifdef __i386__
+	{ 3, s(struct linux_sys_modify_ldt_args) },		/* 123 = modify_ldt */
+#else
+	{ 0, 0 },		/* 123 = modify_ldt */
+#endif
+	{ 0, 0 },		/* 124 = adjtimex */
+	{ 3, s(struct sys_mprotect_args) },			/* 125 = mprotect */
+	{ 3, s(struct linux_sys_sigprocmask_args) },		/* 126 = sigprocmask */
+	{ 0, 0 },		/* 127 = create_module */
+	{ 0, 0 },		/* 128 = init_module */
+	{ 0, 0 },		/* 129 = delete_module */
+	{ 0, 0 },	/* 130 = get_kernel_syms */
+	{ 0, 0 },		/* 131 = quotactl */
+	{ 1, s(struct linux_sys_getpgid_args) },		/* 132 = getpgid */
+	{ 1, s(struct sys_fchdir_args) },			/* 133 = fchdir */
+	{ 0, 0 },		/* 134 = bdflush */
+	{ 0, 0 },			/* 135 = sysfs */
+	{ 1, s(struct linux_sys_personality_args) },		/* 136 = personality */
+	{ 0, 0 },		/* 137 = afs_syscall */
+	{ 1, s(struct linux_sys_setfsuid_args) },		/* 138 = linux_setfsuid16 */
+	{ 0, 0 },		/* 139 = linux_getfsuid16 */
+	{ 5, s(struct linux_sys_llseek_args) },			/* 140 = llseek */
+	{ 3, s(struct linux_sys_getdents_args) },		/* 141 = getdents */
+	{ 5, s(struct linux_sys_select_args) },			/* 142 = select */
+	{ 2, s(struct sys_flock_args) },			/* 143 = flock */
+	{ 3, s(struct sys_msync_args) },			/* 144 = msync */
+	{ 3, s(struct sys_readv_args) },			/* 145 = readv */
+	{ 3, s(struct sys_writev_args) },			/* 146 = writev */
+	{ 1, s(struct linux_sys_getsid_args) },			/* 147 = getsid */
+	{ 1, s(struct linux_sys_fdatasync_args) },		/* 148 = fdatasync */
+	{ 1, s(struct linux_sys___sysctl_args) },		/* 149 = __sysctl */
+	{ 2, s(struct sys_mlock_args) },			/* 150 = mlock */
+	{ 2, s(struct sys_munlock_args) },			/* 151 = munlock */
+	{ 0, 0 },		/* 152 = mlockall */
+	{ 0, 0 },		/* 153 = munlockall */
+	{ 2, s(struct linux_sys_sched_setparam_args) },		/* 154 = sched_setparam */
+	{ 2, s(struct linux_sys_sched_getparam_args) },		/* 155 = sched_getparam */
+	{ 3, s(struct linux_sys_sched_setscheduler_args) },	/* 156 = sched_setscheduler */
+	{ 1, s(struct linux_sys_sched_getscheduler_args) },	/* 157 = sched_getscheduler */
+	{ 0, 0 },		/* 158 = sched_yield */
+	{ 1, s(struct linux_sys_sched_get_priority_max_args) },	/* 159 = sched_get_priority_max */
+	{ 1, s(struct linux_sys_sched_get_priority_min_args) },	/* 160 = sched_get_priority_min */
+	{ 0, 0 },	/* 161 = sched_rr_get_interval */
+	{ 2, s(struct sys_nanosleep_args) },			/* 162 = nanosleep */
+	{ 4, s(struct linux_sys_mremap_args) },			/* 163 = mremap */
+	{ 3, s(struct linux_sys_setresuid16_args) },		/* 164 = setresuid16 */
+	{ 3, s(struct linux_sys_getresuid_args) },		/* 165 = linux_getresuid16 */
+	{ 0, 0 },			/* 166 = vm86 */
+	{ 0, 0 },		/* 167 = query_module */
+	{ 3, s(struct sys_poll_args) },				/* 168 = poll */
+	{ 0, 0 },		/* 169 = nfsservctl */
+	{ 3, s(struct linux_sys_setresgid16_args) },		/* 170 = setresgid16 */
+	{ 3, s(struct linux_sys_getresgid16_args) },		/* 171 = getresgid16 */
+	{ 0, 0 },			/* 172 = prctl */
+	{ 1, s(struct linux_sys_rt_sigreturn_args) },	/* 173 = rt_sigreturn */
+	{ 4, s(struct linux_sys_rt_sigaction_args) },	/* 174 = rt_sigaction */
+	{ 4, s(struct linux_sys_rt_sigprocmask_args) },	/* 175 = rt_sigprocmask */
+	{ 2, s(struct linux_sys_rt_sigpending_args) },	/* 176 = rt_sigpending */
+	{ 0, 0 },	/* 177 = rt_sigtimedwait */
+	{ 0, 0 },		/* 178 = rt_queueinfo */
+	{ 2, s(struct linux_sys_rt_sigsuspend_args) },	/* 179 = rt_sigsuspend */
+	{ 4, s(struct linux_sys_pread_args) },		/* 180 = pread */
+	{ 4, s(struct linux_sys_pwrite_args) },		/* 181 = pwrite */
+	{ 3, s(struct linux_sys_chown16_args) },	/* 182 = chown16 */
+	{ 2, s(struct linux_sys_getcwd_args) },		/* 183 = getcwd */
+	{ 0, 0 },			/* 184 = capget */
+	{ 0, 0 },			/* 185 = capset */
+	{ 2, s(struct linux_sys_sigaltstack_args) },	/* 186 = sigaltstack */
+	{ 0, 0 },		/* 187 = sendfile */
+	{ 0, 0 },		/* 188 = getpmsg */
+	{ 0, 0 },		/* 189 = putpmsg */
+	{ 0, 0 },			/* 190 = vfork */
+	{ 2, s(struct linux_sys_ugetrlimit_args) },	/* 191 = ugetrlimit */
+	{ 0, 0 },			/* 192 = mmap2 */
+	{ 2, s(struct linux_sys_truncate64_args) },	/* 193 = truncate64 */
+	{ 2, s(struct sys_ftruncate_args) },		/* 194 = linux_ftruncate64 */
+	{ 2, s(struct linux_sys_stat64_args) },		/* 195 = stat64 */
+	{ 2, s(struct linux_sys_lstat64_args) },	/* 196 = lstat64 */
+	{ 2, s(struct linux_sys_fstat64_args) },	/* 197 = fstat64 */
+	{ 0, 0 },			/* 198 = lchown */
+	{ 0, 0 },			/* 199 = getuid */
+	{ 0, 0 },			/* 200 = getgid */
+	{ 0, 0 },			/* 201 = geteuid */
+	{ 0, 0 },			/* 202 = getegid */
+	{ 0, 0 },		/* 203 = setreuid */
+	{ 0, 0 },		/* 204 = setregid */
+	{ 2, s(struct sys_getgroups_args) },		/* 205 = getgroups */
+	{ 2, s(struct sys_setgroups_args) },		/* 206 = setgroups */
+	{ 0, 0 },			/* 207 = fchown */
+	{ 0, 0 },		/* 208 = setresuid */
+	{ 3, s(struct linux_sys_getresuid_args) },	/* 209 = getresuid */
+	{ 0, 0 },		/* 210 = setresgid */
+	{ 0, 0 },		/* 211 = getresgid */
+	{ 0, 0 },			/* 212 = chown */
+	{ 1, s(struct sys_setuid_args) },	/* 213 = setuid */
+	{ 1, s(struct sys_setgid_args) },	/* 214 = setgid */
+	{ 1, s(struct linux_sys_setfsuid_args) },		/* 215 = setfsuid */
+	{ 0, 0 },		/* 216 = setfsgid */
+	{ 0, 0 },		/* 217 = pivot_root */
+	{ 0, 0 },		/* 218 = mincore */
+	{ 0, 0 },		/* 219 = madvise */
+	{ 0, 0 },		/* 220 = getdents64 */
+	{ 3, s(struct linux_sys_fcntl64_args) },		/* 221 = fcntl64 */
+};
+
diff -urN linux-2.4.20/drivers/systrace/policy.c linux-2.4.20-04-hdrfix/drivers/systrace/policy.c
--- linux-2.4.20/drivers/systrace/policy.c	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.4.20-04-hdrfix/drivers/systrace/policy.c	2003-01-18 13:36:09.000000000 -0500
@@ -0,0 +1,159 @@
+/*
+ * policy.c
+ *
+ * Copyright (c) 2002 Marius Aamodt Eriksen <marius@umich.edu>
+ * Copyright (c) 2002 Niels Provos <provos@citi.umich.edu>
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions
+ *  are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. The names of the copyright holders may not be used to endorse or
+ *     promote products derived from this software without specific
+ *     prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ *  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ *  AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ *  THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ *  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ *  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ *  OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ *  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ *  ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+#include <linux/fs.h>
+#include <linux/wait.h>
+#include <linux/slab.h>
+#include <linux/queue.h>
+
+#include <asm/semaphore.h>
+#include <asm/uaccess.h>
+#include <asm/ptrace.h>
+
+#include <linux/queue.h>	
+#include <linux/systrace.h>
+
+#include "systrace-private.h"
+
+extern int systrace_debug;
+
+int
+systrace_policy(struct fsystrace *fst, struct systrace_policy *pol)
+{
+	struct str_policy *strpol;
+	struct str_process *strp;
+
+	switch(pol->strp_op) {
+	case SYSTR_POLICY_NEW:
+		DPRINTF(("%s: new, ents %d\n", __func__, pol->strp_maxents));
+
+		if (pol->strp_maxents <= 0 || pol->strp_maxents > 1024)
+			return (-EINVAL);
+		strpol = systrace_newpolicy(fst, pol->strp_maxents);
+		if (strpol == NULL)
+			return (-ENOBUFS);
+		pol->strp_num = strpol->nr;
+		break;
+	case SYSTR_POLICY_ASSIGN:
+		DPRINTF(("%s: %d -> pid %d\n", __func__,
+			    pol->strp_num, pol->strp_pid));
+
+		/* Find right policy by number */
+		TAILQ_FOREACH(strpol, &fst->policies, next)
+		    if (strpol->nr == pol->strp_num)
+			    break;
+		if (strpol == NULL)
+			return (-EINVAL);
+
+		strp = systrace_findpid(fst, pol->strp_pid);
+		if (strp == NULL)
+			return (-EINVAL);
+
+		if (strp->policy != NULL)
+			systrace_closepolicy(fst, strp->policy);
+		strp->policy = strpol;
+		strpol->refcount++;
+		break;
+	case SYSTR_POLICY_MODIFY:
+		DPRINTF(("%s: %d: code %d -> policy %d\n", __func__,
+			    pol->strp_num, pol->strp_code, pol->strp_policy));
+
+		if (!POLICY_VALID(pol->strp_policy) && pol->strp_policy >= 0)
+			return (-EINVAL);
+		TAILQ_FOREACH(strpol, &fst->policies, next)
+		    if (strpol->nr == pol->strp_num)
+			    break;
+		if (strpol == NULL)
+			return (-EINVAL);
+		if (pol->strp_code < 0 || pol->strp_code >= strpol->nsysent)
+			return (-EINVAL);
+		strpol->sysent[pol->strp_code] = pol->strp_policy;
+		break;
+	default:
+		return (-EINVAL);
+	}
+
+	return (0);
+}
+
+struct str_policy *
+systrace_newpolicy(struct fsystrace *fst, int maxents)
+{
+	struct str_policy *pol;
+	int i;
+
+	if (fst->npolicies > SYSTR_MAX_POLICIES /* && !fst->issuser */)
+		return (NULL);
+
+	if ((pol = kmalloc(sizeof(*pol), GFP_KERNEL)) == NULL)
+		return (NULL);
+
+	DPRINTF(("%s: allocating %d -> %lu\n", __func__,
+		    maxents, (u_long)maxents * sizeof(int)));
+
+	memset(pol, 0, sizeof(*pol));
+
+	if ((pol->sysent = kmalloc(maxents * sizeof(short), GFP_KERNEL)) == NULL) {
+		kfree(pol);
+		return (NULL);
+	}
+	pol->nsysent = maxents;
+	for (i = 0; i < maxents; i++)
+		pol->sysent[i] = SYSTR_POLICY_ASK;
+
+	fst->npolicies++;
+	pol->nr = fst->npolicynr++;
+	pol->refcount = 1;
+
+	TAILQ_INSERT_TAIL(&fst->policies, pol, next);
+
+	return (pol);
+}
+
+void
+systrace_closepolicy(struct fsystrace *fst, struct str_policy *policy)
+{
+	if (--policy->refcount)
+		return;
+
+	fst->npolicies--;
+
+	if (policy->nsysent)
+		kfree(policy->sysent);
+
+	TAILQ_REMOVE(&fst->policies, policy, next);
+
+	kfree(policy);
+}
diff -urN linux-2.4.20/drivers/systrace/systrace-private.h linux-2.4.20-04-hdrfix/drivers/systrace/systrace-private.h
--- linux-2.4.20/drivers/systrace/systrace-private.h	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.4.20-04-hdrfix/drivers/systrace/systrace-private.h	2002-12-12 00:55:27.000000000 -0500
@@ -0,0 +1,146 @@
+/*
+ * systrace-private.h
+ *
+ * Copyright (c) 2002 Marius Aamodt Eriksen <marius@umich.edu>
+ * Copyright (c) 2002 Niels Provos <provos@citi.umich.edu>
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions
+ *  are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. The names of the copyright holders may not be used to endorse or
+ *     promote products derived from this software without specific
+ *     prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ *  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ *  AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ *  THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ *  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ *  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ *  OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ *  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ *  ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef SYSTRACE_PRIVATE_H
+#define SYSTRACE_PRIVATE_H
+
+#define POLICY_VALID(x)	((x) == SYSTR_POLICY_PERMIT || \
+			 (x) == SYSTR_POLICY_ASK ||    \
+			 (x) == SYSTR_POLICY_NEVER)
+
+#define DPRINTF(x) if (systrace_debug) printk x
+
+struct str_policy {
+	int                      nr;
+	struct emul             *emul;	   /* XXX */
+	int                      refcount;
+	int                      nsysent;
+	short                   *sysent;
+	TAILQ_ENTRY(str_policy)  next;
+};
+
+#define STR_PROC_ONQUEUE	0x01
+#define STR_PROC_WAITANSWER	0x02
+#define STR_PROC_SYSCALLRES	0x04
+#define STR_PROC_REPORT		0x08	/* Report emulation */
+#define STR_PROC_FSCHANGE	0x10
+#define STR_PROC_SETEUID        0x20    /* Elevate privileges */ 
+#define STR_PROC_SETEGID        0x40
+
+
+struct str_process {
+	TAILQ_ENTRY(str_process)  next;
+	TAILQ_ENTRY(str_process)  msg_next;
+	struct semaphore          lock;	
+	struct task_struct       *proc;
+	pid_t pid;
+	struct fsystrace         *parent;
+	struct str_policy        *policy;
+	wait_queue_head_t         wqh;	
+	int                       flags;
+	short                     answer;
+	short                     error;
+	u16                       seqnr; /* XXX: convert to u_int16_t  */
+	struct str_message        msg;
+	struct systrace_replace  *replace;
+	int                       report;
+	mm_segment_t              oldfs;
+	int                       maycontrol;
+	int                       code;
+	register_t                args[8];
+	uid_t                     oldeuid;
+	gid_t                     oldegid;
+	uid_t                     savedeuid;
+	uid_t                     savedegid;
+	uid_t                     seteuid;
+	uid_t                     setegid;
+	int                       issuser;
+};
+
+/* VFS interface */
+int                systracef_ioctl(struct inode *, struct file *, unsigned int,
+    unsigned long);
+ssize_t            systracef_read(struct file *, char *, size_t, loff_t *);
+ssize_t            systracef_write(struct file *, const char *, size_t, loff_t *);
+int                systracef_open(struct inode *, struct file *);
+int                systracef_release(struct inode *, struct file *);
+unsigned int       systracef_poll(struct file *, struct poll_table_struct *);
+
+/* Policy handling */
+struct str_policy *systrace_newpolicy(struct fsystrace *, int);
+void               systrace_closepolicy(struct fsystrace *, struct str_policy *);
+int                systrace_policy(struct fsystrace *, struct systrace_policy *);
+struct str_policy *systrace_newpolicy(struct fsystrace *, int);
+
+/* Message utility functions */
+int                 systrace_msg_child(struct fsystrace *, struct str_process *, pid_t);
+int                 systrace_msg_result(struct fsystrace *, struct str_process *, int, int,
+    size_t, register_t[]);
+int                 systrace_msg_ask(struct fsystrace *, struct str_process *, int, size_t, register_t[]);
+int                 systrace_msg_ugid(struct fsystrace *, struct str_process *);
+int                 systrace_msg_execve(struct fsystrace *, struct str_process *, register_t);
+int                 systrace_make_msg(struct str_process *, int);
+int                 systrace_make_msg(struct str_process *, int);
+
+
+int                 systrace_io(struct str_process *, struct systrace_io *);
+int                 systrace_getcwd(struct fsystrace *, struct str_process *);
+int                 systrace_rescwd(struct fsystrace *);
+int                 systrace_attach(struct fsystrace *, pid_t);
+int                 systrace_detach(struct str_process *);
+int                 systrace_answer(struct str_process *, struct systrace_answer *);
+int                 systrace_insert_process(struct fsystrace *, struct task_struct *);
+int                 systrace_processready(struct str_process *);
+struct str_process *systrace_findpid(struct fsystrace *, pid_t);
+struct task_struct *systrace_find(struct str_process *);
+
+int                 systrace_preprepl(struct str_process *, struct systrace_replace *);
+int                 systrace_replace(struct str_process *, size_t, register_t[]);
+uid_t               systrace_seteuid(struct task_struct *, uid_t);
+gid_t               systrace_setegid(struct task_struct *, gid_t);
+
+#if 0
+void                systrace_lock(void);
+void                systrace_unlock(void);
+#endif /* 0 */
+/*
+ * Currently, disable the fine grained locking and use the big kernel
+ * lock instead.  The only thing keeping me from using the fine
+ * grained locking is in systrace_make_msg(); when fst->lock is
+ * relinquished, there is a race condition until we sleep on the strp;
+ * it could have been detached in the mean time, causing nasty things
+ * to happen.  When using the kernel lock, it is automatically
+ * relinquished when needed.
+ */
+#define systrace_lock(...) lock_kernel();
+#define systrace_unlock(...) unlock_kernel();
+
+#endif /* SYSTRACE_PRIVATE_H */
diff -urN linux-2.4.20/drivers/systrace/systrace.c linux-2.4.20-04-hdrfix/drivers/systrace/systrace.c
--- linux-2.4.20/drivers/systrace/systrace.c	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.4.20-04-hdrfix/drivers/systrace/systrace.c	2003-01-18 14:48:42.000000000 -0500
@@ -0,0 +1,1395 @@
+/*
+ * systrace.c
+ *
+ * Copyright (c) 2002 Marius Aamodt Eriksen <marius@umich.edu>
+ * Copyright (c) 2002 Niels Provos <provos@citi.umich.edu>
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions
+ *  are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. The names of the copyright holders may not be used to endorse or
+ *     promote products derived from this software without specific
+ *     prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ *  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ *  AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ *  THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ *  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ *  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ *  OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ *  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ *  ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * XXX clone()'s with same PID
+ */
+
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+#include <linux/fs.h>
+#include <linux/wait.h>
+#include <linux/slab.h>
+#include <linux/sys.h>
+#include <linux/miscdevice.h>
+#include <linux/queue.h>
+
+#include <asm/semaphore.h>
+#include <asm/uaccess.h>
+#include <asm/ptrace.h>
+
+#include <linux/queue.h>	
+#include <linux/systrace.h>
+#include <linux/poll.h>
+
+#include "systrace-private.h"
+
+#define FIXARGS(argsize, args, regs) do {   \
+	switch (argsize) {                  \
+	case 20:                            \
+		args[4] = regs->edi;        \
+	case 16:                            \
+		args[3] = regs->esi;        \
+	case 12:                            \
+		args[2] = regs->edx;        \
+	case 8:                             \
+		args[1] = regs->ecx;        \
+	case 4:                             \
+		args[0] = regs->ebx;        \
+	case 0:                             \
+		break;                      \
+	default:                            \
+		printk(KERN_ERR "systrace: Illegal argument size %d\n", argsize);\
+		BUG();                      \
+	}                                   \
+} while (0)
+
+#define SAVEARGS(argsize, args, regs) do {  \
+	switch (argsize) {                  \
+	case 20:                            \
+		regs->edi = args[4];        \
+	case 16:                            \
+		regs->esi = args[3];        \
+	case 12:                            \
+		regs->edx = args[2];        \
+	case 8:                             \
+		regs->ecx = args[1];        \
+	case 4:                             \
+		regs->ebx = args[0];        \
+	case 0:                             \
+		break;                      \
+	default:                            \
+		printk(KERN_ERR "systrace: Illegal argument size %d\n", argsize);\
+		BUG();                      \
+	}                                   \
+} while (0)
+
+#define PRINTARGS(argsize, regs) do {                \
+	switch (argsize) {                           \
+	case 20:                                     \
+		printk("    edi: %lx\n", regs->edi); \
+	case 16:                                     \
+		printk("    esi: %lx\n", regs->esi); \
+	case 12:                                     \
+		printk("    edx: %lx\n", regs->edx); \
+	case 8:                                      \
+		printk("    ecx: %lx\n", regs->ecx); \
+	case 4:                                      \
+		printk("    ebx: %lx\n", regs->ebx); \
+	case 0:                                      \
+		break;                               \
+	default:                                     \
+		printk(KERN_ERR "systrace: Illegal argument size %d\n", argsize);\
+		BUG();                               \
+	}                                            \
+} while (0)
+
+#define SYSTRACE_MINOR 223
+
+spinlock_t str_lck = SPIN_LOCK_UNLOCKED;
+int systrace_debug = 0;
+
+/* From kernel/sys.c */
+static inline void cap_emulate_setxuid(int, int, int);
+static long        __getcwd(char *, unsigned long);
+
+/*
+ * Pass by registers; we need the stack that the system call will see
+ * in order to examine it and possibly modify.
+ */
+
+int  FASTCALL(systrace_intercept(struct pt_regs *));
+void FASTCALL(systrace_result(struct pt_regs *));
+
+static struct file_operations systrace_fops = {
+	read:    &systracef_read,
+	write:   &systracef_write,
+	ioctl:   &systracef_ioctl,
+	release: &systracef_release,
+	open:    &systracef_open,
+	poll:    &systracef_poll
+};
+
+static struct miscdevice systrace_dev = {
+	SYSTRACE_MINOR,
+	"systrace",
+	&systrace_fops
+};
+
+void
+_systrace_lock(void)
+{
+	spin_lock(&str_lck);
+}
+
+void
+_systrace_unlock(void)
+{
+	spin_unlock(&str_lck);
+}
+
+int
+init_systrace(void)
+{
+	if (misc_register(&systrace_dev) < 0) {
+		printk(KERN_INFO "systrace: unable to register device\n");
+		return (-EIO);
+	}
+
+	printk(KERN_INFO "systrace: systrace initialized\n");
+
+	return (0);
+}
+
+int
+systracef_open(struct inode *inode, struct file *file)
+{
+	struct fsystrace *fst;
+	int error = 0;
+
+	if ((fst = kmalloc(sizeof(*fst), GFP_KERNEL)) == NULL) {
+		printk(KERN_ERR "systrace: Failed to allocate kernel memory.\n");
+		error = 0;
+		goto out;
+	}
+
+	memset(fst, 0, sizeof(*fst));
+
+	TAILQ_INIT(&fst->processes);
+	TAILQ_INIT(&fst->policies);
+	TAILQ_INIT(&fst->messages);
+
+	init_MUTEX(&fst->lock);
+	init_waitqueue_head(&fst->wqh);
+
+	fst->euid = current->euid;
+	fst->egid = current->egid;
+	fst->issuser = suser();
+	fst->pid = current->pid;
+
+	file->private_data = fst;
+
+ out:
+	return (error);
+}
+
+int
+systracef_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+    unsigned long arg)
+{
+	struct fsystrace *fst = (struct fsystrace *)file->private_data;
+	pid_t pid = 0;
+	struct str_process *strp = NULL;
+	int error = 0;
+	void *data = NULL;
+
+	if (fst == NULL) {
+		printk(KERN_ERR "systrace: in impossible state!\n");
+		BUG();
+	}
+
+	/* Argument santizing */
+	switch (cmd) {
+	case STRIOCATTACH:
+	case STRIOCANSWER:
+	case STRIOCIO:
+	case STRIOCGETCWD:
+	case STRIOCDETACH:
+	case STRIOCPOLICY:
+	case STRIOCREPLACE:
+		if ((void *)arg == NULL)
+			error = -EINVAL;
+		break;
+	case STRIOCRESCWD:
+	default:
+		break;
+	}
+
+	if (error != 0)
+		goto out;
+
+	switch (cmd) {
+	case STRIOCANSWER:
+		if ((data = kmalloc(sizeof(struct systrace_answer),
+			 GFP_KERNEL)) == NULL) {
+			error = -ENOSPC;
+			break;
+		}
+		if (copy_from_user((struct systrace_answer *)data,
+			(struct systrace_answer *)arg,
+			sizeof(struct systrace_answer)) != 0) {
+			kfree(data);
+			error = -EFAULT;
+			break;
+		}
+
+		pid = ((struct systrace_answer *)data)->stra_pid;
+		break;
+	case STRIOCIO:
+		if ((data = kmalloc(sizeof(struct systrace_io),
+			 GFP_KERNEL)) == NULL) { 
+			error = -ENOSPC;
+			break;
+		}
+		if (copy_from_user((struct systrace_io *)data,
+			(struct systrace_io *)arg,
+			sizeof(struct systrace_io)) != 0) {
+			kfree(data);
+			error = -EFAULT;
+			break;
+		}
+
+		pid = ((struct systrace_io *)data)->strio_pid;
+		break;
+	case STRIOCGETCWD:
+	case STRIOCDETACH:
+		if (get_user(pid, (pid_t *)arg) != 0)
+			error = -EFAULT;
+
+		if (pid == 0)
+			error = -EINVAL;
+		break;
+	case STRIOCATTACH:
+	case STRIOCRESCWD:
+		break;
+	case STRIOCPOLICY:
+		if ((data = kmalloc(sizeof(struct systrace_policy),
+			 GFP_KERNEL)) == NULL) {
+			error = -ENOSPC;
+			break;
+		}
+		if (copy_from_user((struct systrace_policy *)data,
+			(struct systrace_policy *)arg,
+			sizeof(struct systrace_policy)) != 0) {
+			kfree(data);
+			error = -EFAULT;
+			break;
+		}
+		break;
+	case STRIOCREPLACE:
+		if ((data = kmalloc(sizeof(struct systrace_replace),
+			 GFP_KERNEL)) == NULL) {
+			error = -ENOSPC;
+			break;
+		}
+		if (copy_from_user((struct systrace_replace *)data,
+			(struct systrace_replace *)arg,
+			sizeof(struct systrace_replace)) != 0) {
+			kfree(data);
+			error = -EFAULT;
+			break;
+		}
+
+		pid = ((struct systrace_replace *)data)->strr_pid;
+		break;
+	default:
+		error = -EINVAL;
+	}
+
+	if (error != 0)
+		goto out;
+
+	systrace_lock();
+	down(&fst->lock);
+	systrace_unlock();
+
+	if (pid != 0)
+		if ((strp = systrace_findpid(fst, pid)) == NULL) {
+			error = -EINVAL;
+			goto unlock;
+		}
+
+	switch (cmd) {
+	case STRIOCATTACH:
+		if (get_user(pid, (pid_t *)arg) != 0)
+			error = -EFAULT;
+
+		if (pid == 0)
+			error = -EINVAL;
+		else
+			error = systrace_attach(fst, *(pid_t *)arg);
+		break;
+	case STRIOCDETACH:
+		error = systrace_detach(strp);
+		break;
+	case STRIOCANSWER:
+		error = systrace_answer(strp, (struct systrace_answer *)data);
+		break;
+	case STRIOCIO:
+		error = systrace_io(strp, (struct systrace_io *)data);
+		break;
+	case STRIOCGETCWD:
+		error = systrace_getcwd(fst, strp);
+		break;
+	case STRIOCRESCWD:
+		error = systrace_rescwd(fst);
+		break;
+	case STRIOCPOLICY:
+		error = systrace_policy(fst, (struct systrace_policy *)data);
+		if (copy_to_user((struct systrace_policy *)arg,
+			(struct systrace_policy *)data,
+			sizeof(struct systrace_policy)) != 0)
+			error = -EFAULT;
+		break;
+	case STRIOCREPLACE:
+		error = systrace_preprepl(strp, (struct systrace_replace *)data);
+		break;
+	default:
+		/* XXX */
+		break;
+	}
+
+	if (data != NULL)
+		kfree(data);
+
+ unlock:
+	up(&fst->lock);
+ out:
+	return (error);
+}
+
+unsigned int
+systracef_poll(struct file *file, struct poll_table_struct *wait)
+{
+	struct fsystrace *fst = (struct fsystrace *)file->private_data;
+	unsigned int ret = 0;
+
+	systrace_lock();
+	down(&fst->lock);
+	systrace_unlock();
+
+	poll_wait(file, &fst->wqh, wait);
+
+	if (TAILQ_FIRST(&fst->messages) != NULL)
+		ret = POLLIN | POLLRDNORM;
+
+	up(&fst->lock);
+
+	return (ret);
+}
+
+ssize_t
+systracef_read(struct file *filp, char *buf, size_t count, loff_t *off)
+{
+	struct fsystrace *fst = (struct fsystrace *)filp->private_data;
+	struct str_process *strp;
+	int error = 0;
+
+	if (count != sizeof(struct str_message))
+		return (-EINVAL);
+
+ again:
+	systrace_lock();
+	down(&fst->lock);
+	systrace_unlock();
+
+	if ((strp = TAILQ_FIRST(&fst->messages)) != NULL) {
+		error = copy_to_user(buf, &strp->msg, sizeof(struct str_message));
+		if (error != 0) {
+			error = -EFAULT;
+		} else {
+			error = sizeof(struct str_message);
+			TAILQ_REMOVE(&fst->messages, strp, msg_next);
+			CLR(strp->flags, STR_PROC_ONQUEUE);
+
+			if (SYSTR_MSG_NOPROCESS(strp))
+				kfree(strp);
+		}
+	} else if (TAILQ_FIRST(&fst->processes) == NULL) {
+		/* EOF situation */
+		;
+	} else {
+		if (filp->f_flags & O_NONBLOCK) {
+			error = -EAGAIN;
+		} else {
+			up(&fst->lock);
+			interruptible_sleep_on(&fst->wqh);
+
+			if (signal_pending(current)) {
+				error = -ERESTARTSYS;
+				goto out;
+			}
+			goto again;
+		}
+	}
+
+	up(&fst->lock);
+ out:
+	return (error);
+}
+
+ssize_t
+systracef_write(struct file *filp, const char *buf, size_t count, loff_t *off)
+{
+	return (-ENOTSUPP);
+}
+
+int
+systracef_release(struct inode *inode, struct file *filp)
+{
+	struct str_process *strp;
+	struct fsystrace *fst = filp->private_data;
+	struct str_policy *strpol;
+
+	systrace_lock();
+	down(&fst->lock);
+	systrace_unlock();
+
+	/* Kill all traced processes */
+	while ((strp = TAILQ_FIRST(&fst->processes)) != NULL) {
+		struct task_struct *p = strp->proc;
+
+		systrace_detach(strp);
+		kill_proc(p->pid, SIGKILL, 1);
+	}
+
+	/* Clean up fork and exit messages */
+	while ((strp = TAILQ_FIRST(&fst->messages)) != NULL) {
+		TAILQ_REMOVE(&fst->messages, strp, msg_next);
+		kfree(strp);
+	}
+
+	/* Clean up policies */
+	while ((strpol = TAILQ_FIRST(&fst->policies)) != NULL)
+		systrace_closepolicy(fst, strpol);
+
+	up(&fst->lock);
+
+	kfree(filp->private_data);
+	filp->private_data = NULL;
+
+	return (0);
+}
+
+void
+systrace_fork(struct task_struct *parent, struct task_struct *child)
+{
+	struct str_process *parentstrp, *strp;
+	struct fsystrace *fst;
+
+	systrace_lock();
+	if ((parentstrp = parent->systrace) == NULL) {
+		systrace_unlock();
+		return;
+	}
+
+	fst = parentstrp->parent;
+	down(&fst->lock);
+	systrace_unlock();
+
+	if (systrace_insert_process(fst, child) != 0) {
+		printk(KERN_ERR "systrace: failed inserting process!\n");
+		goto out;
+	}
+
+	/* XXX make sure we have pid by this time in fork() */
+	if ((strp = systrace_findpid(fst, child->pid)) == NULL) {
+		printk(KERN_ERR "systrace: inconsistency in tracked process!\n");
+		BUG();
+	}
+
+	if ((strp->policy = parentstrp->policy) != NULL)
+		strp->policy->refcount++;
+
+	/* Fork message */
+	systrace_msg_child(fst, parentstrp, child->pid);
+ out:
+	up(&fst->lock);
+}
+
+void
+systrace_exit(struct task_struct *p)
+{
+	struct str_process *strp;
+	struct fsystrace *fst;
+
+	systrace_lock();
+	if ((strp = p->systrace) != NULL) {
+		fst = strp->parent;
+		down(&fst->lock);
+		systrace_unlock();
+
+		/* Notify our monitor of our death */
+		systrace_msg_child(fst, strp, -1);
+
+		systrace_detach(strp);
+		up(&fst->lock);
+	} else {
+		systrace_unlock();
+	}
+}
+
+void
+systrace_result(struct pt_regs *regs)
+{
+	struct str_process *strp;
+	struct fsystrace *fst;
+	int error, argsize, narg, code, maycontrol = 0;
+	extern struct sysent linux_sysent[];
+	char *path;
+
+	systrace_lock();
+
+	if ((strp = current->systrace) == NULL)
+		goto out;
+
+	code = strp->code;
+	narg = linux_sysent[code].sy_narg;
+	argsize = sizeof(register_t) * narg;
+
+	fst = strp->parent;
+
+	/* Restore elevated priveliges if appropriate */
+	if (strp->issuser) {
+		if (ISSET(strp->flags, STR_PROC_SETEUID)) {
+			if (current->euid == strp->seteuid) {
+				systrace_seteuid(current, strp->savedeuid);
+				CLR(strp->flags, STR_PROC_SETEUID);
+			}
+			if (current->egid == strp->setegid) {
+				systrace_setegid(current, strp->savedegid);
+				CLR(strp->flags, STR_PROC_SETEGID);
+			}
+		}
+	}
+
+	/* Change in UID/GID */
+	if (strp->oldegid != current->egid || strp->oldeuid != current->euid) {
+		down(&fst->lock);
+		systrace_unlock();
+
+		systrace_msg_ugid(fst, strp);
+		systrace_lock();
+		if ((strp = current->systrace) == NULL)
+			goto out;
+	}
+
+	if (ISSET(strp->flags, STR_PROC_SYSCALLRES)) {
+		CLR(strp->flags, STR_PROC_SYSCALLRES);
+
+ 		down(&fst->lock);
+		systrace_unlock();
+
+		error = regs->eax;
+
+		systrace_msg_result(fst, strp, error, code, argsize, strp->args);
+		systrace_lock();
+		if ((strp = current->systrace) == NULL)
+			goto out;
+	}
+
+	if (strp->replace != NULL) {
+		kfree(strp->replace);
+		strp->replace = NULL;
+	}
+
+	if (ISSET(strp->flags, STR_PROC_FSCHANGE))
+		set_fs(strp->oldfs);
+
+ out:
+	systrace_unlock();
+}
+
+/*
+ * XXX serialize system calls
+ */
+int
+systrace_intercept(struct pt_regs *regs)
+{
+	register_t args[8];
+	int argsize, narg, code, error = 0, maycontrol = 0, issuser = 0;
+	short policy;
+	struct str_process *strp;
+	struct fsystrace *fst = NULL;
+	extern struct sysent linux_sysent[];
+	struct str_policy *strpolicy;
+
+	systrace_lock();
+
+	if ((strp = current->systrace) == NULL) {
+		systrace_unlock();
+		goto out;
+	}
+
+	fst = strp->parent;
+
+	down(&fst->lock);
+	systrace_unlock();
+
+	CLR(strp->flags, STR_PROC_FSCHANGE);
+
+	if (regs != NULL) {
+		code = regs->orig_eax;
+	} else {
+		error = -EPERM;
+		goto out;
+ 	}
+
+	if (code > NR_syscalls) {
+		printk(KERN_ERR "systrace: in impossible state!\n");
+		BUG();
+	}
+
+	narg = linux_sysent[code].sy_narg;
+	argsize = sizeof(register_t) * narg;
+
+	/*
+	 * Linux passes system call arguments in registers.  We want
+	 * to be able to pass back an args array; convert
+	 * appropriately.
+	 */
+
+	FIXARGS(argsize, args, regs);
+
+	if (strp->proc != current) {
+		printk(KERN_ERR "systrace: inconsistency in process states!\n");
+		BUG();
+	}
+
+	if (fst->issuser) {
+		maycontrol = 1;
+		issuser = 1;
+	} else if (cap_isclear(current->cap_effective) &&
+	    !(current->flags & PF_SUPERPRIV) &&
+	    current->mm->dumpable) {
+		maycontrol = fst->euid == current->euid &&
+		    fst->egid == current->egid;
+	}
+
+	strp->code = code;
+	strp->maycontrol = maycontrol;
+	memcpy(strp->args, args, sizeof(strp->args));
+	strp->oldeuid = current->euid;
+	strp->oldegid = current->egid;
+	strp->issuser = fst->issuser;
+
+	if (!maycontrol) {
+		policy = SYSTR_POLICY_PERMIT;
+	} else {
+		/* Find out current policy */
+		if ((strpolicy = strp->policy) == NULL) {
+			policy = SYSTR_POLICY_ASK;
+		} else {
+			if (code >= strpolicy->nsysent)
+				policy = SYSTR_POLICY_NEVER;
+			else
+				policy = strpolicy->sysent[code];
+		}
+	}
+
+	switch (policy) {
+	case SYSTR_POLICY_PERMIT:
+		break;
+	case SYSTR_POLICY_ASK:
+		error = systrace_msg_ask(fst, strp, code, argsize, args);
+		/* systrace_msg_ask releases lock */
+		fst = NULL;
+		/* We might have detached by now for some reason */
+		if (error == 0 && (strp = current->systrace) != NULL) {
+			/* XXX - do I need to lock here? */
+			if (strp->answer == SYSTR_POLICY_NEVER) {
+				error = strp->error;
+				if (strp->replace != NULL) {
+					kfree(strp->replace);
+					strp->replace = NULL;
+				}
+			} else if (strp->replace != NULL) {
+				if ((error = systrace_replace(strp,
+					 argsize, args) == 0)) {
+					SAVEARGS(argsize, args, regs);
+					strp->oldfs = get_fs();
+					set_fs(get_ds());
+					SET(strp->flags, STR_PROC_FSCHANGE);
+				}
+			}
+		}
+		break;
+	case SYSTR_POLICY_NEVER:
+		error = -EPERM;
+		break;
+	default:
+		if (policy < 0)
+			error = policy;
+		else
+			error = -EPERM;
+		break;
+	}
+
+	/* XXX */
+/*
+	if (error != 0)
+		goto out;
+*/
+	systrace_lock();
+	if ((strp = current->systrace) != NULL) {
+		if (issuser) {
+			if (ISSET(strp->flags, STR_PROC_SETEUID))
+				strp->savedeuid = systrace_seteuid(current, strp->seteuid);
+			if (ISSET(strp->flags, STR_PROC_SETEGID))
+				strp->savedegid = systrace_setegid(current, strp->setegid);
+		} else {
+			CLR(strp->flags, STR_PROC_SETEUID | STR_PROC_SETEGID);
+		}
+	}
+	systrace_unlock();
+
+ out:
+	if (fst != NULL)
+		up(&fst->lock);
+
+	return (error);
+}
+
+int
+systrace_preprepl(struct str_process *strp, struct systrace_replace *repl)
+{
+	size_t len;
+	int i, error = 0;
+
+	if ((error = systrace_processready(strp)) != 0)
+		return (error);
+
+	if (strp->replace != NULL) {
+		kfree(strp->replace);
+		strp->replace = NULL;
+	}
+
+	if (repl->strr_nrepl < 0 || repl->strr_nrepl > SYSTR_MAXARGS)
+		return (-EINVAL);
+
+	for (i = 0, len = 0; i < repl->strr_nrepl; i++) {
+		len += repl->strr_offlen[i];
+		if (repl->strr_offlen[i] == 0)
+			continue;
+		if (repl->strr_offlen[i] + repl->strr_off[i] > len)
+			return (-EINVAL);
+	}
+
+	/* Make sure that the length adds up */
+	if (repl->strr_len != len)
+		return (-EINVAL);
+
+	/* Check against a maximum length */
+	if (repl->strr_len > 2048)
+		return (-EINVAL);
+
+	if ((strp->replace = kmalloc(sizeof(*strp->replace) + len, GFP_KERNEL))
+	    == NULL) 
+		return (-ENOSPC);
+
+	memcpy(strp->replace, repl, sizeof(*strp->replace));
+
+	if (copy_from_user(strp->replace + 1, repl->strr_base, len) != 0) {
+		kfree(strp->replace);
+		strp->replace = NULL;
+		return (-EFAULT);
+	}
+
+	/* Adjust the offset */
+	repl = strp->replace;
+	repl->strr_base = (void *)(repl + 1);
+
+	return (0);
+}
+
+/*
+ * Replace the arguments with arguments from the monitoring process.
+ */
+int
+systrace_replace(struct str_process *strp, size_t argsize, register_t args[])
+{
+	struct systrace_replace *repl = strp->replace;
+	void *kbase;
+	int i, maxarg, ind, ret = 0;
+
+	maxarg = argsize / sizeof(register_t);
+
+	kbase = repl->strr_base;
+	for (i = 0; i < maxarg && i < repl->strr_nrepl; i++) {
+		ind = repl->strr_argind[i];
+		if (ind < 0 || ind >= maxarg) {
+			kfree(repl);
+			strp->replace = NULL;
+			return (-EINVAL);
+		}
+		if (repl->strr_offlen[i] == 0) {
+			args[ind] = repl->strr_off[i];
+			continue;
+		}
+
+		/* Replace the argument with the new address */
+		args[ind] = (register_t)(kbase + repl->strr_off[i]);
+	}
+
+	return (ret);
+}
+
+int
+systrace_answer(struct str_process *strp, struct systrace_answer *ans)
+{
+	int error = 0;
+
+	if (!POLICY_VALID(ans->stra_policy)) {
+		error = -EINVAL;
+		goto out;
+	}
+
+	/* Check if answer is in sync with us */
+	if (ans->stra_seqnr != strp->seqnr) {
+		error = -EINVAL;
+		goto out;
+	}
+
+	if ((error = systrace_processready(strp)) != 0)
+		goto out;
+
+	strp->answer = ans->stra_policy;
+	strp->error = ans->stra_error;
+	if (!strp->error)
+		strp->error = -EPERM;
+	if (ISSET(ans->stra_flags, SYSTR_FLAGS_RESULT))
+		SET(strp->flags, STR_PROC_SYSCALLRES);
+
+        /* See if we should elevate privileges for this system call */
+        if (ISSET(ans->stra_flags, SYSTR_FLAGS_SETEUID)) {
+                SET(strp->flags, STR_PROC_SETEUID);
+                strp->seteuid = ans->stra_seteuid;
+        }
+        if (ISSET(ans->stra_flags, SYSTR_FLAGS_SETEGID)) {
+                SET(strp->flags, STR_PROC_SETEGID);
+                strp->setegid = ans->stra_setegid;
+        }
+
+	/* Clearing the flag indicates to the process that it woke up */
+	CLR(strp->flags, STR_PROC_WAITANSWER);
+	wake_up(&strp->wqh);
+ out:
+
+	return (error);
+}
+
+int
+systrace_io(struct str_process *strp, struct systrace_io *io)
+{
+	int rw, ret = 0, copied, maycontrol = 0;
+	void *buf;
+	struct fsystrace *fst = strp->parent;
+	struct task_struct *tsk = strp->proc;
+
+	if (fst->issuser) {
+		maycontrol = 1;
+	} else if (cap_isclear(tsk->cap_effective) &&
+	    !(tsk->flags & PF_SUPERPRIV) &&
+	    tsk->mm->dumpable) {
+		maycontrol = current->euid == tsk->euid &&
+		    current->egid == tsk->egid;
+	}
+
+	if (!maycontrol)
+		return (-EPERM);
+
+	if ((buf = kmalloc(io->strio_len, GFP_KERNEL)) == NULL) {
+		printk(KERN_ERR "systrace: failed to allocate kernel memory!\n");
+		return (-ENOMEM);
+	}
+
+	switch (io->strio_op) {
+	case SYSTR_READ:
+		rw = 0;
+		break;
+	case SYSTR_WRITE:
+		rw = 1;
+		if (copy_from_user(buf, io->strio_addr, io->strio_len)) {
+			ret = -EFAULT;
+			goto out;
+		}
+		break;
+	default:
+		return (-EINVAL);
+	}
+
+	copied = access_process_vm(tsk, (unsigned long)io->strio_offs, buf,
+	    io->strio_len, rw);
+
+	if (copied != io->strio_len) {
+		ret = -EFAULT;
+		goto out;
+	}
+
+	switch (io->strio_op) {
+	case SYSTR_READ:
+		if (copy_to_user(io->strio_addr, buf, io->strio_len)) {
+			ret = -EFAULT;
+			goto out;
+		}
+		break;
+	}
+
+ out:
+	kfree(buf);
+
+	return (ret);
+}
+
+int
+systrace_getcwd(struct fsystrace *fst, struct str_process *strp)
+{
+	struct fs_struct *fsc, *fsp;
+	int error = 0;
+
+	if ((error = systrace_processready(strp)) != 0)
+		return (error);
+
+	task_lock(current);
+	task_lock(strp->proc);
+	fsc = current->fs;
+	fsp = strp->proc->fs;
+
+	if (fsc == NULL || fsp == NULL) {
+		task_unlock(current);
+		task_unlock(strp->proc);
+		return (-EINVAL);
+	}
+
+	fst->pwd_pid = strp->pid;
+
+	/* XXX altroot? */
+	write_lock(&fsc->lock);
+
+	fst->pwd_mnt = fsc->pwdmnt;
+	fst->pwd_dentry = fsc->pwd;
+	fst->root_mnt = fsc->rootmnt;
+	fst->root_dentry = fsc->root;
+
+	read_lock(&fsp->lock);
+	fsc->pwdmnt = mntget(fsp->pwdmnt);
+	fsc->pwd = dget(fsp->pwd);
+	fsc->rootmnt = mntget(fsp->rootmnt);
+	fsc->root = dget(fsp->root);
+	read_unlock(&fsp->lock);
+
+	write_unlock(&fsc->lock);
+
+	task_unlock(current);
+	task_unlock(strp->proc);
+
+	return (0);
+}
+
+int
+systrace_rescwd(struct fsystrace *fst)
+{
+	struct fs_struct *fsc;
+
+	if (fst->pwd_pid == 0)
+		return (-EINVAL);
+
+	fsc = current->fs;
+
+	write_lock(&fsc->lock);
+	dput(fsc->pwd);
+	mntput(fsc->pwdmnt);
+	dput(fsc->root);
+	mntput(fsc->rootmnt);
+
+	fsc->pwd = fst->pwd_dentry;
+	fsc->pwdmnt = fst->pwd_mnt;
+	fsc->root = fst->root_dentry;
+	fsc->rootmnt = fst->root_mnt;
+	write_unlock(&fsc->lock);
+
+	fst->pwd_pid = 0;
+
+	return (0);
+}
+
+int
+systrace_processready(struct str_process *strp)
+{
+	if (ISSET(strp->flags, STR_PROC_ONQUEUE))
+		return (-EBUSY);
+
+	if (!ISSET(strp->flags, STR_PROC_WAITANSWER))
+		return (-EBUSY);
+
+	if (ISSET(strp->proc->flags, PF_EXITING))
+		return (-EBUSY);
+
+#if 0
+	if (strp->proc->state != 0)
+		return (-EBUSY);
+#endif /* 0 */
+
+	return (0);
+}
+
+int
+systrace_insert_process(struct fsystrace *fst, struct task_struct *p)
+{
+	struct str_process *strp;
+
+	if ((strp = kmalloc(sizeof(*strp), GFP_KERNEL)) == NULL)
+		return (-ENOMEM);
+
+	memset(strp, 0, sizeof(*strp));
+
+	strp->pid = p->pid;
+	strp->proc = p;
+	strp->parent = fst;
+
+	init_waitqueue_head(&strp->wqh);
+	init_MUTEX(&strp->lock);
+
+	/* Insert into parent's process list */
+	TAILQ_INSERT_TAIL(&fst->processes, strp, next);
+	fst->nprocesses++;
+
+	/* XXX need process flag*/
+	p->systrace = strp;
+
+	return (0);
+}
+
+struct str_process *
+systrace_findpid(struct fsystrace *fst, pid_t pid)
+{
+	struct str_process *strp;
+	struct task_struct *proc;
+
+	TAILQ_FOREACH(strp, &fst->processes, next)
+	    if (strp->pid == pid)
+		    break;
+
+	if (strp == NULL)
+		return (NULL);
+
+	proc = systrace_find(strp);
+
+	return (proc != NULL ? strp : NULL);
+}
+
+int
+systrace_attach(struct fsystrace *fst, pid_t pid)
+{
+	struct task_struct *proc;
+
+	proc = find_task_by_pid(pid);
+	if (proc == NULL)
+		return (-EINVAL);
+
+	/* (1) Same process */
+
+	if (proc->pid == current->pid)
+		return (-EINVAL);
+
+	/* (2) System process */
+	/* XXX */
+
+	/* (3) Already being systraced */
+
+	if (proc->systrace != NULL)
+		return (-EBUSY);
+
+	/*
+	 * (4) We do not own it, it's not set{u,g}id AND we are not
+	 *     root
+	 */
+	if ((!cap_isclear(proc->cap_permitted) || proc->flags & PF_SUPERPRIV ||
+		proc->euid != current->euid || proc->egid != current->egid) &&
+	    !suser())
+		return (-EPERM);
+
+	/* (5) It's init */
+	if (proc->pid == 1)
+		return (-EPERM);
+
+	return (systrace_insert_process(fst, proc));
+}
+
+int
+systrace_detach(struct str_process *strp)
+{
+	struct fsystrace *fst = strp->parent;
+	struct task_struct *proc;
+	int error = 0;
+
+	if ((proc = systrace_find(strp)) != NULL)
+		proc->systrace = NULL;
+	else
+		error = -EINVAL;
+
+	if (ISSET(strp->flags, STR_PROC_WAITANSWER)) {
+		CLR(strp->flags, STR_PROC_WAITANSWER);
+		wake_up(&strp->wqh);
+	}
+
+	fst = strp->parent;
+	wake_up(&fst->wqh);
+
+	if (ISSET(strp->flags, STR_PROC_ONQUEUE)) 
+		TAILQ_REMOVE(&fst->messages, strp, msg_next);
+
+	TAILQ_REMOVE(&fst->processes, strp, next);
+	fst->nprocesses--;
+
+	if (strp->policy != NULL)
+		systrace_closepolicy(fst, strp->policy);
+	if (strp->replace != NULL)
+		kfree(strp->replace);
+
+	kfree(strp);
+
+	return (error);
+}
+
+int
+systrace_msg_result(struct fsystrace *fst, struct str_process *strp,
+    int error, int code, size_t argsize, register_t args[])
+{
+	struct str_msg_ask *msg_ask = &strp->msg.msg_data.msg_ask;
+	int i;
+
+	msg_ask->code = code;
+	/* XXX argsize */
+	/* += fixup_socket_argsize ... () */
+	msg_ask->argsize = argsize;
+	msg_ask->result = error;
+	for (i = 0; i < argsize / sizeof(register_t) && i < SYSTR_MAXARGS; i++)
+		msg_ask->args[i] = args[i];
+
+	msg_ask->rval[0] = 0x42;
+	msg_ask->rval[1] = 0x42;
+
+	return (systrace_make_msg(strp, SYSTR_MSG_RES));
+}
+
+int
+systrace_msg_ask(struct fsystrace *fst, struct str_process *strp, int code,
+    size_t argsize, register_t args[])
+{
+	struct str_msg_ask *msg_ask = &strp->msg.msg_data.msg_ask;
+	int i;
+
+	msg_ask->code = code;
+	/* XXX argsize */
+	msg_ask->argsize = argsize;
+	for (i = 0; i < (argsize / sizeof(register_t)) && i < SYSTR_MAXARGS; i++)
+		msg_ask->args[i] = args[i];
+
+	return (systrace_make_msg(strp, SYSTR_MSG_ASK));
+}
+
+int
+systrace_msg_ugid(struct fsystrace *fst, struct str_process *strp)  
+{
+        struct str_msg_ugid *msg_ugid = &strp->msg.msg_data.msg_ugid;
+        struct task_struct *tsk = strp->proc;
+
+        msg_ugid->uid = tsk->euid;
+        msg_ugid->gid = tsk->egid;
+
+        return (systrace_make_msg(strp, SYSTR_MSG_UGID));
+}
+
+int
+systrace_msg_execve(struct fsystrace *fst, struct str_process *strp, register_t patharg)
+{
+        struct str_msg_execve *msg_execve = &strp->msg.msg_data.msg_execve;
+
+	msg_execve->patharg = patharg;
+
+        return (systrace_make_msg(strp, SYSTR_MSG_EXECVE));
+}
+
+int
+systrace_msg_child(struct fsystrace *fst, struct str_process *strp, pid_t npid)
+{
+	struct str_process *nstrp;
+	struct str_message *msg;
+	struct str_msg_child *msg_child;
+
+	/* XXX - use kmem cache!@; pool_*() like interface to it? */
+	if ((nstrp = kmalloc(sizeof(*nstrp), GFP_KERNEL)) == NULL)
+		return (-1);
+
+	memset(nstrp, 0, sizeof(*nstrp));
+
+	DPRINTF(("%s: %p: pid %d -> pid %d\n", __func__, nstrp, strp->pid, npid));
+
+	msg = &nstrp->msg;
+	msg_child = &msg->msg_data.msg_child;
+
+	msg->msg_type = SYSTR_MSG_CHILD;
+	msg->msg_pid = strp->pid;
+	if (strp->policy)
+		msg->msg_policy = strp->policy->nr;
+	else
+		msg->msg_policy = -1;
+	msg_child->new_pid = npid;
+
+	TAILQ_INSERT_TAIL(&fst->messages, nstrp, msg_next);
+
+	wake_up(&fst->wqh);
+
+	return (0);
+}
+
+int
+systrace_make_msg(struct str_process *strp, int type)
+{
+	struct str_message *msg = &strp->msg;
+	struct fsystrace *fst = strp->parent;
+	int error = 0;
+
+	msg->msg_seqnr = ++strp->seqnr;
+	msg->msg_type = type;
+	msg->msg_pid = strp->pid;
+
+	if (strp->policy)
+		msg->msg_policy = strp->policy->nr;
+	else
+		msg->msg_policy = -1;
+
+	SET(strp->flags, STR_PROC_WAITANSWER);
+	if (ISSET(strp->flags, STR_PROC_ONQUEUE))
+		goto out;
+
+	TAILQ_INSERT_TAIL(&fst->messages, strp, msg_next);
+	SET(strp->flags, STR_PROC_ONQUEUE);
+	/*
+	 * XXX; need to do schedule trick here; what if we sleep on
+	 * up(), then we might have awoken again, without knowing
+	 */
+ out:
+	wake_up(&fst->wqh);
+	lock_kernel();
+	up(&fst->lock);
+
+	/* Sleep until we have got a reply */
+	for (;;) {
+		interruptible_sleep_on(&strp->wqh);
+
+		if (signal_pending(current)) {
+			error = -EINTR;
+			break;
+		}
+
+		/* If we detach, then everything is permitted */
+		if ((strp = current->systrace) == NULL)
+			break;
+
+		if (!ISSET(strp->flags, STR_PROC_WAITANSWER))
+			break;
+	}
+
+	unlock_kernel();
+
+	return (0);
+}
+
+uid_t
+systrace_seteuid(struct task_struct *tsk, uid_t euid)
+{
+	uid_t oldeuid = tsk->euid;
+
+	if (euid == oldeuid)
+		return (oldeuid);
+
+	/* XXX */
+	tsk->mm->dumpable = 0;
+	wmb();
+
+	tsk->euid = euid;
+	tsk->fsuid = euid;
+
+	if (!issecure(SECURE_NO_SETUID_FIXUP))
+		cap_emulate_setxuid(tsk->uid, oldeuid, tsk->suid);
+
+	return (oldeuid);
+}
+
+gid_t
+systrace_setegid(struct task_struct *tsk, gid_t egid)
+{
+	uid_t oldegid = tsk->egid;
+
+	if (egid == oldegid)
+		return (oldegid);
+
+	/* XXX */
+	tsk->mm->dumpable = 0;
+	wmb();
+
+	tsk->egid = egid;
+	tsk->fsgid = egid;
+
+	return (oldegid);
+}
+
+struct task_struct *
+systrace_find(struct str_process *strp)
+{
+        struct task_struct *proc;
+
+        if ((proc = find_task_by_pid(strp->pid)) == NULL)
+                return (NULL);
+
+        if (proc != strp->proc)
+                return (NULL);
+
+	if (proc->systrace == NULL)
+                return (NULL);
+
+        return (proc);
+}
+
+
+/*
+ * From kernel/sys.c
+ */
+
+static inline void cap_emulate_setxuid(int old_ruid, int old_euid, int old_suid)
+{
+        if ((old_ruid == 0 || old_euid == 0 || old_suid == 0) &&
+            (current->uid != 0 && current->euid != 0 && current->suid != 0) &&
+            !current->keep_capabilities) {
+                cap_clear(current->cap_permitted);
+                cap_clear(current->cap_effective);
+        }
+        if (old_euid == 0 && current->euid != 0) {
+                cap_clear(current->cap_effective);
+        }
+        if (old_euid != 0 && current->euid == 0) {
+                current->cap_effective = current->cap_permitted;
+        }
+}
diff -urN linux-2.4.20/include/linux/queue.h linux-2.4.20-04-hdrfix/include/linux/queue.h
--- linux-2.4.20/include/linux/queue.h	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.4.20-04-hdrfix/include/linux/queue.h	2002-12-02 01:21:30.000000000 -0500
@@ -0,0 +1,145 @@
+/*	$OpenBSD: queue.h,v 1.22 2001/06/23 04:39:35 angelos Exp $	*/
+/*	$NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $	*/
+
+/*
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by the University of
+ *	California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	@(#)queue.h	8.5 (Berkeley) 8/20/94
+ */
+
+#ifndef	_SYS_QUEUE_H_
+#define	_SYS_QUEUE_H_
+
+/*
+ * Tail queue definitions.
+ */
+#define TAILQ_HEAD(name, type)						\
+struct name {								\
+	struct type *tqh_first;	/* first element */			\
+	struct type **tqh_last;	/* addr of last next element */		\
+}
+
+#define TAILQ_HEAD_INITIALIZER(head)					\
+	{ NULL, &(head).tqh_first }
+
+#define TAILQ_ENTRY(type)						\
+struct {								\
+	struct type *tqe_next;	/* next element */			\
+	struct type **tqe_prev;	/* address of previous next element */	\
+}
+
+/* 
+ * tail queue access methods 
+ */
+#define	TAILQ_FIRST(head)		((head)->tqh_first)
+#define	TAILQ_END(head)			NULL
+#define	TAILQ_NEXT(elm, field)		((elm)->field.tqe_next)
+#define TAILQ_LAST(head, headname)					\
+	(*(((struct headname *)((head)->tqh_last))->tqh_last))
+/* XXX */
+#define TAILQ_PREV(elm, headname, field)				\
+	(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
+#define	TAILQ_EMPTY(head)						\
+	(TAILQ_FIRST(head) == TAILQ_END(head))
+
+#define TAILQ_FOREACH(var, head, field)					\
+	for((var) = TAILQ_FIRST(head);					\
+	    (var) != TAILQ_END(head);					\
+	    (var) = TAILQ_NEXT(var, field))
+
+#define TAILQ_FOREACH_REVERSE(var, head, field, headname)		\
+	for((var) = TAILQ_LAST(head, headname);				\
+	    (var) != TAILQ_END(head);					\
+	    (var) = TAILQ_PREV(var, headname, field))
+
+/*
+ * Tail queue functions.
+ */
+#define	TAILQ_INIT(head) do {						\
+	(head)->tqh_first = NULL;					\
+	(head)->tqh_last = &(head)->tqh_first;				\
+} while (0)
+
+#define TAILQ_INSERT_HEAD(head, elm, field) do {			\
+	if (((elm)->field.tqe_next = (head)->tqh_first) != NULL)	\
+		(head)->tqh_first->field.tqe_prev =			\
+		    &(elm)->field.tqe_next;				\
+	else								\
+		(head)->tqh_last = &(elm)->field.tqe_next;		\
+	(head)->tqh_first = (elm);					\
+	(elm)->field.tqe_prev = &(head)->tqh_first;			\
+} while (0)
+
+#define TAILQ_INSERT_TAIL(head, elm, field) do {			\
+	(elm)->field.tqe_next = NULL;					\
+	(elm)->field.tqe_prev = (head)->tqh_last;			\
+	*(head)->tqh_last = (elm);					\
+	(head)->tqh_last = &(elm)->field.tqe_next;			\
+} while (0)
+
+#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do {		\
+	if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
+		(elm)->field.tqe_next->field.tqe_prev =			\
+		    &(elm)->field.tqe_next;				\
+	else								\
+		(head)->tqh_last = &(elm)->field.tqe_next;		\
+	(listelm)->field.tqe_next = (elm);				\
+	(elm)->field.tqe_prev = &(listelm)->field.tqe_next;		\
+} while (0)
+
+#define	TAILQ_INSERT_BEFORE(listelm, elm, field) do {			\
+	(elm)->field.tqe_prev = (listelm)->field.tqe_prev;		\
+	(elm)->field.tqe_next = (listelm);				\
+	*(listelm)->field.tqe_prev = (elm);				\
+	(listelm)->field.tqe_prev = &(elm)->field.tqe_next;		\
+} while (0)
+
+#define TAILQ_REMOVE(head, elm, field) do {				\
+	if (((elm)->field.tqe_next) != NULL)				\
+		(elm)->field.tqe_next->field.tqe_prev =			\
+		    (elm)->field.tqe_prev;				\
+	else								\
+		(head)->tqh_last = (elm)->field.tqe_prev;		\
+	*(elm)->field.tqe_prev = (elm)->field.tqe_next;			\
+} while (0)
+
+#define TAILQ_REPLACE(head, elm, elm2, field) do {			\
+	if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL)	\
+		(elm2)->field.tqe_next->field.tqe_prev =		\
+		    &(elm2)->field.tqe_next;				\
+	else								\
+		(head)->tqh_last = &(elm2)->field.tqe_next;		\
+	(elm2)->field.tqe_prev = (elm)->field.tqe_prev;			\
+	*(elm2)->field.tqe_prev = (elm2);				\
+} while (0)
+
+#endif	/* !_SYS_QUEUE_H_ */
--- linux-2.4.20-gentoo-r1/include/linux/sched.h	Sun Jan 19 19:26:03 2003
+++ linux-2.4.20-gentoo-r1-systrace-v2/include/linux/sched.h	Sun Jan 19 20:20:35 2003
@@ -375,6 +375,10 @@
 
 	unsigned long flags;
 
+#ifdef CONFIG_SYSTRACE
+	void *systrace;
+#endif /* CONFIG_SYSTRACE */
+
 /* task state */
 	struct linux_binfmt *binfmt;
 	int exit_code, exit_signal;
diff -urN linux-2.4.20/include/linux/systrace.h linux-2.4.20-04-hdrfix/include/linux/systrace.h
--- linux-2.4.20/include/linux/systrace.h	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.4.20-04-hdrfix/include/linux/systrace.h	2003-01-18 13:35:09.000000000 -0500
@@ -0,0 +1,216 @@
+/*
+ * systrace.h
+ *
+ * Copyright (c) 2002 Marius Aamodt Eriksen <marius@umich.edu>
+ *
+ * These definitions are muchly replicated from Niels Provos' OpenBSD
+ * implementation.
+ */
+
+#ifndef _SYSTRACE_H
+#define _SYSTRACE_H
+
+/*
+ * XXX this is kind of nasty -- should add manually to everything that
+ * needs it
+ */
+
+#define SYSTR_EMULEN    8       /* sync with sys proc */
+
+#ifdef __KERNEL__
+/* XXX ugly.  argh... linux... */
+typedef u32 register_t;
+#endif __KERNEL__
+
+struct str_msg_emul {
+        char emul[SYSTR_EMULEN];
+};
+
+#define SYSTR_MAX_POLICIES      64
+#define SYSTR_MAXARGS           64
+
+/* XXX change register_t (args, rval) to something portable. */
+struct str_msg_ask {
+        int code;
+        int argsize;
+        u32 args[SYSTR_MAXARGS];
+        u32 rval[2];
+        int result;
+};
+
+/* Queued on fork or exit of a process */
+
+struct str_msg_child {
+        pid_t new_pid;
+};
+struct str_msg_ugid {
+        uid_t uid;
+        gid_t gid;
+};
+
+struct str_msg_execve {
+	register_t patharg;
+};
+
+#define SYSTR_MSG_ASK     1
+#define SYSTR_MSG_RES     2
+#define SYSTR_MSG_EMUL    3
+#define SYSTR_MSG_CHILD   4
+#define SYSTR_MSG_UGID    5
+#define SYSTR_MSG_EXECVE  6
+
+#define SYSTR_MSG_NOPROCESS(x) \
+        ((x)->msg.msg_type == SYSTR_MSG_CHILD)
+
+#define MAXPATHLEN PATH_MAX
+
+struct str_message {
+	/* XXX - should be u_int16_t */
+	int   msg_seqnr;
+        int   msg_type;
+        pid_t msg_pid;
+        short msg_policy;
+        short reserved;
+        union {
+                struct str_msg_emul    msg_emul;
+                struct str_msg_ask     msg_ask;
+                struct str_msg_child   msg_child;
+                struct str_msg_ugid    msg_ugid;
+                struct str_msg_execve  msg_execve;
+        }     msg_data;
+};
+
+struct systrace_answer {
+	/* XXX - should be u_int16_t */
+	int   stra_seqnr;
+        pid_t stra_pid;
+        int   stra_policy;
+        int   stra_error;
+        int   stra_flags;
+	uid_t stra_seteuid;     /* elevated privileges for system call */
+        gid_t stra_setegid;
+};
+
+#define SYSTR_READ              1
+#define SYSTR_WRITE             2
+
+struct systrace_io {
+        pid_t   strio_pid;
+        int     strio_op;
+        void   *strio_offs;
+        void   *strio_addr;
+        size_t  strio_len;
+};
+
+#define SYSTR_POLICY_NEW        1
+#define SYSTR_POLICY_ASSIGN     2
+#define SYSTR_POLICY_MODIFY     3
+
+struct systrace_policy {
+        int strp_op;
+        int strp_num;
+        union {
+                struct {
+                        short code;
+                        short policy;
+                } assign;
+                pid_t pid;
+                int maxents;
+        } strp_data;
+};
+
+
+struct systrace_replace {
+	pid_t strr_pid;
+	int strr_nrepl;
+	void *strr_base;
+	size_t strr_len;
+	int strr_argind[SYSTR_MAXARGS];
+	size_t strr_off[SYSTR_MAXARGS];
+	size_t strr_offlen[SYSTR_MAXARGS];
+};
+
+#define strp_pid        strp_data.pid
+#define strp_maxents    strp_data.maxents
+#define strp_code       strp_data.assign.code
+#define strp_policy     strp_data.assign.policy
+
+/* ioctl definitions */
+#define STR_MAGIC 's'
+
+#define STRIOCATTACH  _IOW(STR_MAGIC, 101, pid_t)
+#define STRIOCDETACH  _IOW(STR_MAGIC, 102, pid_t)
+#define STRIOCANSWER  _IOW(STR_MAGIC, 103, struct systrace_answer)
+#define STRIOCIO      _IOWR(STR_MAGIC, 104, struct systrace_io)
+#define STRIOCPOLICY  _IOWR(STR_MAGIC, 105, struct systrace_policy)
+#define STRIOCGETCWD  _IOW(STR_MAGIC, 106, pid_t)
+#define STRIOCRESCWD  _IO(STR_MAGIC, 107)
+#define STRIOWAKE     _IO(STR_MAGIC, 108)
+#define STRIOCLONE    _IOW(STR_MAGIC, 109, int *);
+#define STRIOCREPLACE _IOW(STR_MAGIC, 110, struct systrace_replace)
+
+#define SYSTR_POLICY_ASK        0
+#define SYSTR_POLICY_PERMIT     1
+#define SYSTR_POLICY_NEVER      2
+
+#define SYSTR_FLAGS_RESULT      0x001
+#define SYSTR_FLAGS_SETEUID     0x002
+#define SYSTR_FLAGS_SETEGID     0x004
+
+#ifdef __KERNEL__
+
+struct str_process;
+struct fsystrace {
+	struct semaphore                      lock;
+	wait_queue_head_t                     wqh;
+        TAILQ_HEAD(strprocessq, str_process)  processes;
+        TAILQ_HEAD(strpolicyq, str_policy)    policies;
+        int                                   nprocesses;
+        struct strprocessq                    messages;
+        int                                   npolicynr;
+        int                                   npolicies;
+
+        int                                   issuser;
+	uid_t                                 euid;
+	gid_t                                 egid;
+
+	pid_t                                 pid;
+        /* cwd magic */
+        pid_t                                 pwd_pid;
+	struct vfsmount                      *pwd_mnt;
+        struct dentry                        *pwd_dentry;
+        struct vfsmount                      *root_mnt;
+        struct dentry                        *root_dentry;
+};
+
+/* Internal prototypes */
+
+/*
+  int systrace_redirect(int, struct proc *, void *, register_t *);
+  void systrace_exit(struct proc *);
+  void systrace_fork(struct proc *, struct proc *);
+*/
+
+int  init_systrace(void);
+void systrace_fork(struct task_struct *, struct task_struct *);
+void systrace_exit(struct task_struct *);
+
+/* crud needed to make systrace happy */
+struct sysent {             /* system call table */
+	short   sy_narg;    /* number of args */
+	short   sy_argsize; /* total size of arguments */
+};
+
+/* Macros to set/clear/test flags. */
+#define SET(t, f)       ((t) |= (f))
+#define CLR(t, f)       ((t) &= ~(f))
+#define ISSET(t, f)     ((t) & (f))
+
+
+#endif /* __KERNEL__ */
+
+#ifndef __KERNEL__
+//typedef u_int32_t register_t;
+#endif /* !__KERNEL__ */
+
+#endif /* _SYSTRACE_H */
--- linux-2.4.20-gentoo-r1/init/main.c	Sun Jan 19 19:26:03 2003
+++ linux-2.4.20-gentoo-r1-systrace-v2/init/main.c	Sun Jan 19 20:02:34 2003
@@ -70,6 +70,12 @@
 extern int irda_device_init(void);
 #endif
 
+#ifdef CONFIG_SYSTRACE
+#include <linux/queue.h>
+#include <asm/semaphore.h>
+#include <linux/systrace.h>
+#endif
+
 #ifdef CONFIG_X86_LOCAL_APIC
 #include <asm/smp.h>
 #endif
@@ -549,6 +555,9 @@
 	irda_proto_init();
 	irda_device_init(); /* Must be done after protocol initialization */
 #endif
+#ifdef CONFIG_SYSTRACE
+	init_systrace();
+#endif
 #ifdef CONFIG_PCMCIA
 	init_pcmcia_ds();		/* Do this last */
 #endif
--- linux-2.4.20-gentoo-r1/kernel/exit.c	Sun Jan 19 19:26:03 2003
+++ linux-2.4.20-gentoo-r1-systrace-v2/kernel/exit.c	Sun Jan 19 20:02:34 2003
@@ -16,6 +16,11 @@
 #ifdef CONFIG_BSD_PROCESS_ACCT
 #include <linux/acct.h>
 #endif
+#ifdef CONFIG_SYSTRACE
+#include <linux/queue.h>
+#include <asm/semaphore.h>
+#include <linux/systrace.h>
+#endif
 #include <linux/grsecurity.h>
 #include <linux/imon.h>  /* hopefully this can go away when imon is done */
 
@@ -497,6 +502,9 @@
 #ifdef CONFIG_BSD_PROCESS_ACCT
 	acct_process(code);
 #endif
+#ifdef CONFIG_SYSTRACE
+	systrace_exit(tsk);
+#endif
 	if (unlikely(current->acl_admin && gr_acl_is_enabled())) {
 		gr_set_acls(1);
 		security_alert_good(GR_ADMINL_ACL_MSG, GR_ADMINL_ACL_FLD,
--- linux-2.4.20-gentoo-r1/kernel/fork.c	Sun Jan 19 19:26:03 2003
+++ linux-2.4.20-gentoo-r1-systrace-v2/kernel/fork.c	Sun Jan 19 20:02:34 2003
@@ -29,6 +29,12 @@
 #include <asm/uaccess.h>
 #include <asm/mmu_context.h>
 
+#ifdef CONFIG_SYSTRACE
+#include <linux/queue.h>
+#include <asm/semaphore.h>
+#include <linux/systrace.h>
+#endif /* CONFIG_SYSTRACE */
+
 /* The idle threads do not count.. */
 int nr_threads;
 
@@ -794,6 +800,12 @@
 
 	if (p->ptrace & PT_PTRACED)
 		send_sig(SIGSTOP, p, 1);
+
+#ifdef CONFIG_SYSTRACE
+	if (current->systrace != NULL)
+		systrace_fork(current, p);
+#endif /* CONFIG_SYSTRACE */
+
 	wake_up_forked_process(p);	/* do this last */
 	++total_forks;
 	if (clone_flags & CLONE_VFORK)
