diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/alpha/config.in linux-2.4.25-leo/arch/alpha/config.in
--- linux-2.4.25/arch/alpha/config.in	2004-02-20 14:11:37.000000000 +0000
+++ linux-2.4.25-leo/arch/alpha/config.in	2004-02-20 18:22:22.000000000 +0000
@@ -468,3 +468,12 @@
 
 source crypto/Config.in
 source lib/Config.in
+
+mainmenu_option next_comment
+comment 'Grsecurity'
+bool 'Grsecurity' CONFIG_GRKERNSEC
+if [ "$CONFIG_GRKERNSEC" = "y" ]; then
+	source grsecurity/Config.in
+fi
+endmenu
+
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/alpha/kernel/osf_sys.c linux-2.4.25-leo/arch/alpha/kernel/osf_sys.c
--- linux-2.4.25/arch/alpha/kernel/osf_sys.c	2003-06-13 15:51:29.000000000 +0100
+++ linux-2.4.25-leo/arch/alpha/kernel/osf_sys.c	2004-02-20 18:22:22.000000000 +0000
@@ -33,6 +33,7 @@
 #include <linux/file.h>
 #include <linux/types.h>
 #include <linux/ipc.h>
+#include <linux/grsecurity.h>
 
 #include <asm/fpu.h>
 #include <asm/io.h>
@@ -230,6 +231,11 @@
 	struct file *file = NULL;
 	unsigned long ret = -EBADF;
 
+#ifdef CONFIG_GRKERNSEC_PAX_RANDEXEC
+	if (flags & MAP_MIRROR)
+		return -EINVAL;
+#endif
+
 #if 0
 	if (flags & (_MAP_HASSEMAPHORE | _MAP_INHERIT | _MAP_UNALIGNED))
 		printk("%s: unimplemented OSF mmap flags %04lx\n", 
@@ -240,6 +246,13 @@
 		if (!file)
 			goto out;
 	}
+
+	if(gr_handle_mmap(file, prot)) {
+		fput(file);
+		ret = -EACCES;
+		goto out;
+	}
+
 	flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
 	down_write(&current->mm->mmap_sem);
 	ret = do_mmap(file, addr, len, prot, flags, off);
@@ -1357,6 +1370,10 @@
 	   merely specific addresses, but regions of memory -- perhaps
 	   this feature should be incorporated into all ports?  */
 
+#ifdef CONFIG_GRKERNSEC_PAX_RANDMMAP
+	if (!(current->flags & PF_PAX_RANDMMAP) || !filp)
+#endif
+
 	if (addr) {
 		addr = arch_get_unmapped_area_1 (PAGE_ALIGN(addr), len, limit);
 		if (addr != -ENOMEM)
@@ -1364,8 +1381,15 @@
 	}
 
 	/* Next, try allocating at TASK_UNMAPPED_BASE.  */
-	addr = arch_get_unmapped_area_1 (PAGE_ALIGN(TASK_UNMAPPED_BASE),
-					 len, limit);
+
+	addr = TASK_UNMAPPED_BASE;
+
+#ifdef CONFIG_GRKERNSEC_PAX_RANDMMAP
+	if (current->flags & PF_PAX_RANDMMAP)
+		addr += current->mm->delta_mmap;
+#endif
+
+	addr = arch_get_unmapped_area_1 (PAGE_ALIGN(addr), len, limit);
 	if (addr != -ENOMEM)
 		return addr;
 
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/alpha/kernel/process.c linux-2.4.25-leo/arch/alpha/kernel/process.c
--- linux-2.4.25/arch/alpha/kernel/process.c	2003-08-25 12:44:39.000000000 +0100
+++ linux-2.4.25-leo/arch/alpha/kernel/process.c	2004-02-20 18:23:08.000000000 +0000
@@ -186,6 +186,7 @@
 	args.mode = mode;
 	args.restart_cmd = restart_cmd;
 #ifdef CONFIG_SMP
+	preempt_disable();
 	smp_call_function(common_shutdown_1, &args, 1, 0);
 #endif
 	common_shutdown_1(&args);
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/alpha/kernel/ptrace.c linux-2.4.25-leo/arch/alpha/kernel/ptrace.c
--- linux-2.4.25/arch/alpha/kernel/ptrace.c	2003-06-13 15:51:29.000000000 +0100
+++ linux-2.4.25-leo/arch/alpha/kernel/ptrace.c	2004-02-20 18:22:22.000000000 +0000
@@ -13,6 +13,7 @@
 #include <linux/ptrace.h>
 #include <linux/user.h>
 #include <linux/slab.h>
+#include <linux/grsecurity.h>
 
 #include <asm/uaccess.h>
 #include <asm/pgtable.h>
@@ -275,6 +276,10 @@
 	read_unlock(&tasklist_lock);
 	if (!child)
 		goto out_notsk;
+
+	if(gr_handle_ptrace(child, request))
+		goto out;
+
 	if (request == PTRACE_ATTACH) {
 		ret = ptrace_attach(child);
 		goto out;
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/alpha/mm/fault.c linux-2.4.25-leo/arch/alpha/mm/fault.c
--- linux-2.4.25/arch/alpha/mm/fault.c	2002-11-28 23:53:08.000000000 +0000
+++ linux-2.4.25-leo/arch/alpha/mm/fault.c	2004-02-20 18:22:22.000000000 +0000
@@ -53,6 +53,139 @@
 	__reload_thread(&current->thread);
 }
 
+/*
+ * PaX: decide what to do with offenders (regs->pc = fault address)
+ *
+ * returns 1 when task should be killed
+ *         2 when patched PLT trampoline was detected
+ *         3 when unpatched PLT trampoline was detected
+ *	   4 when legitimate ET_EXEC was detected
+ */
+#ifdef CONFIG_GRKERNSEC_PAX_PAGEEXEC
+static int pax_handle_fetch_fault(struct pt_regs *regs)
+{
+	int err;
+
+#ifdef CONFIG_GRKERNSEC_PAX_RANDEXEC
+	if (current->flags & PF_PAX_RANDEXEC) {
+		if (regs->pc >= current->mm->start_code &&
+		    regs->pc < current->mm->end_code)
+		{
+			if (regs->r26 == regs->pc)
+				return 1;
+			regs->pc += current->mm->delta_exec;
+			return 4;
+		}
+	}
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_EMUPLT
+	do { /* PaX: patched PLT emulation #1 */
+		unsigned int ldah, ldq, jmp;
+
+		err = get_user(ldah, (unsigned int *)regs->pc);
+		err |= get_user(ldq, (unsigned int *)(regs->pc+4));
+		err |= get_user(jmp, (unsigned int *)(regs->pc+8));
+
+		if (err)
+			break;
+
+		if ((ldah & 0xFFFF0000U)== 0x277B0000U &&
+		    (ldq & 0xFFFF0000U) == 0xA77B0000U &&
+		    jmp == 0x6BFB0000U)
+		{
+			unsigned long r27, addr;
+			unsigned long addrh = (ldah | 0xFFFFFFFFFFFF0000UL) << 16;
+			unsigned long addrl = ldq | 0xFFFFFFFFFFFF0000UL;
+
+			addr = regs->r27 + ((addrh ^ 0x80000000UL) + 0x80000000UL) + ((addrl ^ 0x8000UL) + 0x8000UL);
+			err = get_user(r27, (unsigned long*)addr);
+			if (err)
+				break;
+
+			regs->r27 = r27;
+			regs->pc = r27;
+			return 2;
+		}
+	} while (0);
+
+	do { /* PaX: patched PLT emulation #2 */
+		unsigned int ldah, lda, br;
+
+		err = get_user(ldah, (unsigned int *)regs->pc);
+		err |= get_user(lda, (unsigned int *)(regs->pc+4));
+		err |= get_user(br, (unsigned int *)(regs->pc+8));
+
+		if (err)
+			break;
+
+		if ((ldah & 0xFFFF0000U)== 0x277B0000U &&
+		    (lda & 0xFFFF0000U) == 0xA77B0000U &&
+		    (br & 0xFFE00000U) == 0xC3E00000U)
+		{
+			unsigned long addr = br | 0xFFFFFFFFFFE00000UL;
+			unsigned long addrh = (ldah | 0xFFFFFFFFFFFF0000UL) << 16;
+			unsigned long addrl = lda | 0xFFFFFFFFFFFF0000UL;
+
+			regs->r27 += ((addrh ^ 0x80000000UL) + 0x80000000UL) + ((addrl ^ 0x8000UL) + 0x8000UL);
+			regs->pc += 12 + (((addr ^ 0x00100000UL) + 0x00100000UL) << 2);
+			return 2;
+		}
+	} while (0);
+
+	do { /* PaX: unpatched PLT emulation */
+		unsigned int br;
+
+		err = get_user(br, (unsigned int *)regs->pc);
+
+		if (!err && (br & 0xFFE00000U) == 0xC3800000U) {
+			unsigned int br2, ldq, nop, jmp;
+			unsigned long addr = br | 0xFFFFFFFFFFE00000UL, resolver;
+
+			addr = regs->pc + 4 + (((addr ^ 0x00100000UL) + 0x00100000UL) << 2);
+			err = get_user(br2, (unsigned int *)addr);  
+			err |= get_user(ldq, (unsigned int *)(addr+4));
+			err |= get_user(nop, (unsigned int *)(addr+8));
+			err |= get_user(jmp, (unsigned int *)(addr+12));
+			err |= get_user(resolver, (unsigned long *)(addr+16));
+
+			if (err)
+				break;
+
+			if (br2 == 0xC3600000U &&
+			    ldq == 0xA77B000CU &&
+			    nop == 0x47FF041FU &&
+			    jmp == 0x6B7B0000U)
+			{
+				regs->r28 = regs->pc+4;
+				regs->r27 = addr+16;
+				regs->pc = resolver;
+				return 3;
+			}
+		}
+	} while (0);
+#endif
+
+	return 1;
+}
+
+void pax_report_insns(void *pc)
+{
+	unsigned long i;
+
+	printk(KERN_ERR "PAX: bytes at PC: ");
+	for (i = 0; i < 5; i++) {
+		unsigned int c;
+		if (get_user(c, (unsigned int*)pc+i)) {
+			printk("<invalid address>.");
+			break;
+		}
+		printk("%08x ", c);
+	}
+	printk("\n");
+}
+#endif
+
 
 /*
  * This routine handles page faults.  It determines the address,
@@ -133,8 +266,32 @@
 good_area:
 	info.si_code = SEGV_ACCERR;
 	if (cause < 0) {
-		if (!(vma->vm_flags & VM_EXEC))
+		if (!(vma->vm_flags & VM_EXEC)) {
+
+#ifdef CONFIG_GRKERNSEC_PAX_PAGEEXEC
+			if (!(current->flags & PF_PAX_PAGEEXEC) || address != regs->pc)
+				goto bad_area;
+
+			up_read(&mm->mmap_sem);
+			switch(pax_handle_fetch_fault(regs)) {
+
+#ifdef CONFIG_GRKERNSEC_PAX_EMUPLT
+			case 2:
+			case 3:
+				return;
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_RANDEXEC
+			case 4:
+				return;
+#endif
+			}
+			pax_report_fault(regs, (void*)regs->pc, (void*)rdusp());
+			do_exit(SIGKILL);
+#else
 			goto bad_area;
+#endif
+		}
 	} else if (!cause) {
 		/* Allow reads even for write-only mappings */
 		if (!(vma->vm_flags & (VM_READ | VM_WRITE)))
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/arm/config.in linux-2.4.25-leo/arch/arm/config.in
--- linux-2.4.25/arch/arm/config.in	2004-02-20 14:11:37.000000000 +0000
+++ linux-2.4.25-leo/arch/arm/config.in	2004-02-20 18:22:22.000000000 +0000
@@ -736,3 +736,11 @@
 
 source crypto/Config.in
 source lib/Config.in
+
+mainmenu_option next_comment
+comment 'Grsecurity'
+bool 'Grsecurity' CONFIG_GRKERNSEC
+if [ "$CONFIG_GRKERNSEC" = "y" ]; then
+	source grsecurity/Config.in
+fi
+endmenu
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/cris/config.in linux-2.4.25-leo/arch/cris/config.in
--- linux-2.4.25/arch/cris/config.in	2004-02-20 14:11:37.000000000 +0000
+++ linux-2.4.25-leo/arch/cris/config.in	2004-02-20 18:22:22.000000000 +0000
@@ -276,3 +276,12 @@
 source crypto/Config.in
 source lib/Config.in
 endmenu
+
+mainmenu_option next_comment
+comment 'Grsecurity'
+bool 'Grsecurity' CONFIG_GRKERNSEC
+if [ "$CONFIG_GRKERNSEC" = "y" ]; then
+    source grsecurity/Config.in
+fi
+endmenu
+
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/i386/boot/bootsect.S linux-2.4.25-leo/arch/i386/boot/bootsect.S
--- linux-2.4.25/arch/i386/boot/bootsect.S	2003-08-25 12:44:39.000000000 +0100
+++ linux-2.4.25-leo/arch/i386/boot/bootsect.S	2004-02-20 18:22:22.000000000 +0000
@@ -237,7 +237,7 @@
 #ifdef __BIG_KERNEL__
 					# look in setup.S for bootsect_kludge
 	bootsect_kludge = 0x220		# 0x200 + 0x20 which is the size of the
-	lcall	bootsect_kludge		# bootsector + bootsect_kludge offset
+	lcall	*bootsect_kludge	# bootsector + bootsect_kludge offset
 #else
 	movw	%es, %ax
 	subw	$SYSSEG, %ax
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/i386/boot/setup.S linux-2.4.25-leo/arch/i386/boot/setup.S
--- linux-2.4.25/arch/i386/boot/setup.S	2004-02-20 14:11:37.000000000 +0000
+++ linux-2.4.25-leo/arch/i386/boot/setup.S	2004-02-20 18:22:22.000000000 +0000
@@ -637,7 +637,7 @@
 	cmpw	$0, %cs:realmode_swtch
 	jz	rmodeswtch_normal
 
-	lcall	%cs:realmode_swtch
+	lcall	*%cs:realmode_swtch
 
 	jmp	rmodeswtch_end
 
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/i386/config.in linux-2.4.25-leo/arch/i386/config.in
--- linux-2.4.25/arch/i386/config.in	2004-02-20 14:11:37.000000000 +0000
+++ linux-2.4.25-leo/arch/i386/config.in	2004-02-20 18:23:08.000000000 +0000
@@ -99,6 +99,7 @@
 fi
 if [ "$CONFIG_M686" = "y" ]; then
    define_int  CONFIG_X86_L1_CACHE_SHIFT 5
+   define_bool CONFIG_X86_ALIGNMENT_16 y
    define_bool CONFIG_X86_HAS_TSC y
    define_bool CONFIG_X86_GOOD_APIC y
    bool 'PGE extensions (not for Cyrix/Transmeta)' CONFIG_X86_PGE
@@ -108,6 +109,7 @@
 fi
 if [ "$CONFIG_MPENTIUMIII" = "y" ]; then
    define_int  CONFIG_X86_L1_CACHE_SHIFT 5
+   define_bool CONFIG_X86_ALIGNMENT_16 y
    define_bool CONFIG_X86_HAS_TSC y
    define_bool CONFIG_X86_GOOD_APIC y
    define_bool CONFIG_X86_PGE y
@@ -116,6 +118,7 @@
 fi
 if [ "$CONFIG_MPENTIUM4" = "y" ]; then
    define_int  CONFIG_X86_L1_CACHE_SHIFT 7
+   define_bool CONFIG_X86_ALIGNMENT_16 y
    define_bool CONFIG_X86_HAS_TSC y
    define_bool CONFIG_X86_GOOD_APIC y
    define_bool CONFIG_X86_PGE y
@@ -135,6 +138,7 @@
 fi
 if [ "$CONFIG_MK7" = "y" ]; then
    define_int  CONFIG_X86_L1_CACHE_SHIFT 6
+   define_bool CONFIG_X86_ALIGNMENT_16 y
    define_bool CONFIG_X86_HAS_TSC y
    define_bool CONFIG_X86_GOOD_APIC y
    define_bool CONFIG_X86_USE_3DNOW y
@@ -225,6 +229,7 @@
 bool 'Math emulation' CONFIG_MATH_EMULATION
 bool 'MTRR (Memory Type Range Register) support' CONFIG_MTRR
 bool 'Symmetric multi-processing support' CONFIG_SMP
+bool 'Preemptible Kernel' CONFIG_PREEMPT
 if [ "$CONFIG_SMP" != "y" ]; then
    bool 'Local APIC support on uniprocessors' CONFIG_X86_UP_APIC
    dep_bool 'IO-APIC support on uniprocessors' CONFIG_X86_UP_IOAPIC $CONFIG_X86_UP_APIC
@@ -258,9 +263,12 @@
    fi
 fi
 
-if [ "$CONFIG_SMP" = "y" -a "$CONFIG_X86_CMPXCHG" = "y" ]; then
-   define_bool CONFIG_HAVE_DEC_LOCK y
+if [ "$CONFIG_SMP" = "y" -o "$CONFIG_PREEMPT" = "y" ]; then
+   if [ "$CONFIG_X86_CMPXCHG" = "y" ]; then
+      define_bool CONFIG_HAVE_DEC_LOCK y
+   fi
 fi
+
 endmenu
 
 mainmenu_option next_comment
@@ -487,3 +495,11 @@
 
 source crypto/Config.in
 source lib/Config.in
+
+mainmenu_option next_comment
+comment 'Grsecurity'
+bool 'Grsecurity' CONFIG_GRKERNSEC
+if [ "$CONFIG_GRKERNSEC" = "y" ]; then
+	source grsecurity/Config.in
+fi
+endmenu
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/i386/kernel/apm.c linux-2.4.25-leo/arch/i386/kernel/apm.c
--- linux-2.4.25/arch/i386/kernel/apm.c	2003-08-25 12:44:39.000000000 +0100
+++ linux-2.4.25-leo/arch/i386/kernel/apm.c	2004-02-20 18:22:22.000000000 +0000
@@ -614,7 +614,7 @@
 	__asm__ __volatile__(APM_DO_ZERO_SEGS
 		"pushl %%edi\n\t"
 		"pushl %%ebp\n\t"
-		"lcall %%cs:" SYMBOL_NAME_STR(apm_bios_entry) "\n\t"
+		"lcall *%%ss:" SYMBOL_NAME_STR(apm_bios_entry) "\n\t"
 		"setc %%al\n\t"
 		"popl %%ebp\n\t"
 		"popl %%edi\n\t"
@@ -666,7 +666,7 @@
 		__asm__ __volatile__(APM_DO_ZERO_SEGS
 			"pushl %%edi\n\t"
 			"pushl %%ebp\n\t"
-			"lcall %%cs:" SYMBOL_NAME_STR(apm_bios_entry) "\n\t"
+			"lcall *%%ss:" SYMBOL_NAME_STR(apm_bios_entry) "\n\t"
 			"setc %%bl\n\t"
 			"popl %%ebp\n\t"
 			"popl %%edi\n\t"
@@ -1985,6 +1985,12 @@
 		 __va((unsigned long)0x40 << 4));
 	_set_limit((char *)&gdt[APM_40 >> 3], 4095 - (0x40 << 4));
 
+#ifdef CONFIG_GRKERNSEC_PAX_SEGMEXEC
+	set_base(gdt2[APM_40 >> 3],
+		__va((unsigned long)0x40 << 4));
+	_set_limit((char *)&gdt2[APM_40 >> 3], 4095 - (0x40 << 4));
+#endif
+
 	apm_bios_entry.offset = apm_info.bios.offset;
 	apm_bios_entry.segment = APM_CS;
 	set_base(gdt[APM_CS >> 3],
@@ -1993,6 +1999,16 @@
 		 __va((unsigned long)apm_info.bios.cseg_16 << 4));
 	set_base(gdt[APM_DS >> 3],
 		 __va((unsigned long)apm_info.bios.dseg << 4));
+
+#ifdef CONFIG_GRKERNSEC_PAX_SEGMEXEC
+	set_base(gdt2[APM_CS >> 3],
+		__va((unsigned long)apm_info.bios.cseg << 4));
+	set_base(gdt2[APM_CS_16 >> 3],
+		__va((unsigned long)apm_info.bios.cseg_16 << 4));
+	set_base(gdt2[APM_DS >> 3],
+		__va((unsigned long)apm_info.bios.dseg << 4));
+#endif
+
 #ifndef APM_RELAX_SEGMENTS
 	if (apm_info.bios.version == 0x100) {
 #endif
@@ -2002,6 +2018,13 @@
 		_set_limit((char *)&gdt[APM_CS_16 >> 3], 64 * 1024 - 1);
 		/* For the DEC Hinote Ultra CT475 (and others?) */
 		_set_limit((char *)&gdt[APM_DS >> 3], 64 * 1024 - 1);
+
+#ifdef CONFIG_GRKERNSEC_PAX_SEGMEXEC
+		_set_limit((char *)&gdt2[APM_CS >> 3], 64 * 1024 - 1);
+		_set_limit((char *)&gdt2[APM_CS_16 >> 3], 64 * 1024 - 1);
+		_set_limit((char *)&gdt2[APM_DS >> 3], 64 * 1024 - 1);
+#endif
+
 #ifndef APM_RELAX_SEGMENTS
 	} else {
 		_set_limit((char *)&gdt[APM_CS >> 3],
@@ -2010,6 +2033,16 @@
 			(apm_info.bios.cseg_16_len - 1) & 0xffff);
 		_set_limit((char *)&gdt[APM_DS >> 3],
 			(apm_info.bios.dseg_len - 1) & 0xffff);
+
+#ifdef CONFIG_GRKERNSEC_PAX_SEGMEXEC
+		_set_limit((char *)&gdt2[APM_CS >> 3],
+			(apm_info.bios.cseg_len - 1) & 0xffff);
+		_set_limit((char *)&gdt2[APM_CS_16 >> 3],
+			(apm_info.bios.cseg_16_len - 1) & 0xffff);
+		_set_limit((char *)&gdt2[APM_DS >> 3],
+			(apm_info.bios.dseg_len - 1) & 0xffff);
+#endif
+
 	}
 #endif
 
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/i386/kernel/cpuid.c linux-2.4.25-leo/arch/i386/kernel/cpuid.c
--- linux-2.4.25/arch/i386/kernel/cpuid.c	2001-10-11 17:04:57.000000000 +0100
+++ linux-2.4.25-leo/arch/i386/kernel/cpuid.c	2004-02-20 18:23:08.000000000 +0000
@@ -60,7 +60,8 @@
 static inline void do_cpuid(int cpu, u32 reg, u32 *data)
 {
   struct cpuid_command cmd;
-  
+
+  preempt_disable();
   if ( cpu == smp_processor_id() ) {
     cpuid(reg, &data[0], &data[1], &data[2], &data[3]);
   } else {
@@ -70,6 +71,7 @@
     
     smp_call_function(cpuid_smp_cpuid, &cmd, 1, 1);
   }
+  preempt_enable();
 }
 #else /* ! CONFIG_SMP */
 
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/i386/kernel/entry.S linux-2.4.25-leo/arch/i386/kernel/entry.S
--- linux-2.4.25/arch/i386/kernel/entry.S	2003-06-13 15:51:29.000000000 +0100
+++ linux-2.4.25-leo/arch/i386/kernel/entry.S	2004-02-20 18:23:08.000000000 +0000
@@ -73,7 +73,7 @@
  * these are offsets into the task-struct.
  */
 state		=  0
-flags		=  4
+preempt_count	=  4
 sigpending	=  8
 addr_limit	= 12
 exec_domain	= 16
@@ -81,8 +81,28 @@
 tsk_ptrace	= 24
 processor	= 52
 
+/* These are offsets into the irq_stat structure
+ * There is one per cpu and it is aligned to 32
+ * byte boundry (we put that here as a shift count)
+ */
+irq_array_shift                 = CONFIG_X86_L1_CACHE_SHIFT
+
+irq_stat_local_irq_count        = 4
+irq_stat_local_bh_count         = 8
+
 ENOSYS = 38
 
+#ifdef CONFIG_SMP
+#define GET_CPU_INDX	movl processor(%ebx),%eax;  \
+                        shll $irq_array_shift,%eax
+#define GET_CURRENT_CPU_INDX GET_CURRENT(%ebx); \
+                             GET_CPU_INDX
+#define CPU_INDX (,%eax)
+#else
+#define GET_CPU_INDX
+#define GET_CURRENT_CPU_INDX GET_CURRENT(%ebx)
+#define CPU_INDX
+#endif
 
 #define SAVE_ALL \
 	cld; \
@@ -209,6 +229,17 @@
 	jae badsys
 	call *SYMBOL_NAME(sys_call_table)(,%eax,4)
 	movl %eax,EAX(%esp)		# save the return value
+
+#ifdef CONFIG_GRKERNSEC_PAX_RANDKSTACK
+	cli                             # need_resched and signals atomic test
+	cmpl $0,need_resched(%ebx)
+	jne reschedule
+	cmpl $0,sigpending(%ebx)
+	jne signal_return
+	call SYMBOL_NAME(pax_randomize_kstack)
+	jmp restore_all
+#endif
+
 ENTRY(ret_from_sys_call)
 	cli				# need_resched and signals atomic test
 	cmpl $0,need_resched(%ebx)
@@ -255,12 +286,30 @@
 	ALIGN
 ENTRY(ret_from_intr)
 	GET_CURRENT(%ebx)
+#ifdef CONFIG_PREEMPT
+	cli
+	decl preempt_count(%ebx)
+#endif
 ret_from_exception:
 	movl EFLAGS(%esp),%eax		# mix EFLAGS and CS
 	movb CS(%esp),%al
 	testl $(VM_MASK | 3),%eax	# return to VM86 mode or non-supervisor?
 	jne ret_from_sys_call
+#ifdef CONFIG_PREEMPT
+	cmpl $0,preempt_count(%ebx)
+	jnz restore_all
+	cmpl $0,need_resched(%ebx)
+	jz restore_all
+	movl SYMBOL_NAME(irq_stat)+irq_stat_local_bh_count CPU_INDX,%ecx
+	addl SYMBOL_NAME(irq_stat)+irq_stat_local_irq_count CPU_INDX,%ecx
+	jnz restore_all
+	incl preempt_count(%ebx)
+	sti
+	call SYMBOL_NAME(preempt_schedule)
+	jmp ret_from_intr
+#else
 	jmp restore_all
+#endif
 
 	ALIGN
 reschedule:
@@ -297,6 +346,9 @@
 	GET_CURRENT(%ebx)
 	call *%edi
 	addl $8,%esp
+#ifdef CONFIG_PREEMPT
+	cli
+#endif
 	jmp ret_from_exception
 
 ENTRY(coprocessor_error)
@@ -316,12 +368,18 @@
 	movl %cr0,%eax
 	testl $0x4,%eax			# EM (math emulation bit)
 	jne device_not_available_emulate
+#ifdef CONFIG_PREEMPT
+	cli
+#endif
 	call SYMBOL_NAME(math_state_restore)
 	jmp ret_from_exception
 device_not_available_emulate:
 	pushl $0		# temporary storage for ORIG_EIP
 	call  SYMBOL_NAME(math_emulate)
 	addl $4,%esp
+#ifdef CONFIG_PREEMPT
+	cli
+#endif
 	jmp ret_from_exception
 
 ENTRY(debug)
@@ -389,8 +447,56 @@
 	jmp error_code
 
 ENTRY(page_fault)
+#ifdef CONFIG_GRKERNSEC_PAX_PAGEEXEC
+	ALIGN
+	pushl $ SYMBOL_NAME(pax_do_page_fault)
+#else
 	pushl $ SYMBOL_NAME(do_page_fault)
+#endif
+
+#ifndef CONFIG_GRKERNSEC_PAX_EMUTRAMP
 	jmp error_code
+#else
+	pushl %ds
+	pushl %eax
+	xorl %eax,%eax
+	pushl %ebp
+	pushl %edi
+	pushl %esi
+	pushl %edx
+	decl %eax			# eax = -1
+	pushl %ecx
+	pushl %ebx
+	cld
+	movl %es,%ecx
+	movl ORIG_EAX(%esp), %esi	# get the error code
+	movl ES(%esp), %edi		# get the function address
+	movl %eax, ORIG_EAX(%esp)
+	movl %ecx, ES(%esp)
+	movl %esp,%edx
+	pushl %esi			# push the error code
+	pushl %edx			# push the pt_regs pointer
+	movl $(__KERNEL_DS),%edx
+	movl %edx,%ds
+	movl %edx,%es
+	GET_CURRENT(%ebx)
+	call *%edi
+	addl $8,%esp
+	decl %eax
+	jnz ret_from_exception
+
+	popl %ebx
+	popl %ecx
+	popl %edx
+	popl %esi
+	popl %edi
+	popl %ebp
+	popl %eax
+	popl %ds
+	popl %es
+	addl $4,%esp
+	jmp system_call
+#endif
 
 ENTRY(machine_check)
 	pushl $0
@@ -402,7 +508,7 @@
 	pushl $ SYMBOL_NAME(do_spurious_interrupt_bug)
 	jmp error_code
 
-.data
+.section .rodata, "a"
 ENTRY(sys_call_table)
 	.long SYMBOL_NAME(sys_ni_syscall)	/* 0  -  old "setup()" system call*/
 	.long SYMBOL_NAME(sys_exit)
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/i386/kernel/head.S linux-2.4.25-leo/arch/i386/kernel/head.S
--- linux-2.4.25/arch/i386/kernel/head.S	2003-11-28 18:26:19.000000000 +0000
+++ linux-2.4.25-leo/arch/i386/kernel/head.S	2004-02-20 18:22:22.000000000 +0000
@@ -41,6 +41,7 @@
  *
  * On entry, %esi points to the real-mode code as a 32-bit pointer.
  */
+.global startup_32
 startup_32:
 /*
  * Set segments to known values
@@ -86,7 +87,7 @@
 				   PRESENT+RW+USER */
 2:	stosl
 	add $0x1000,%eax
-	cmp $empty_zero_page-__PAGE_OFFSET,%edi
+	cmp $0x00c00007,%eax
 	jne 2b
 
 /*
@@ -100,9 +101,19 @@
 	movl %eax,%cr0		/* ..and set paging (PG) bit */
 	jmp 1f			/* flush the prefetch-queue */
 1:
+
+#if !defined(CONFIG_GRKERNSEC_PAX_KERNEXEC) || defined(CONFIG_SMP)
+
+#ifdef CONFIG_GRKERNSEC_PAX_KERNEXEC
+	orw  %bx,%bx
+	jz  1f
+#endif
+
 	movl $1f,%eax
 	jmp *%eax		/* make sure eip is relocated */
 1:
+#endif
+
 	/* Set up the stack pointer */
 	lss stack_start,%esp
 
@@ -121,7 +132,7 @@
  */
 	xorl %eax,%eax
 	movl $ SYMBOL_NAME(__bss_start),%edi
-	movl $ SYMBOL_NAME(_end),%ecx
+	movl $ SYMBOL_NAME(__bss_end),%ecx
 	subl %edi,%ecx
 	rep
 	stosb
@@ -272,8 +283,6 @@
 	jmp L6			# main should never return here, but
 				# just in case, we know what happens.
 
-ready:	.byte 0
-
 /*
  * We depend on ET to be correct. This checks for 287/387.
  */
@@ -319,13 +328,6 @@
 	jne rp_sidt
 	ret
 
-ENTRY(stack_start)
-	.long SYMBOL_NAME(init_task_union)+8192
-	.long __KERNEL_DS
-
-/* This is the default interrupt "handler" :-) */
-int_msg:
-	.asciz "Unknown interrupt\n"
 	ALIGN
 ignore_int:
 	cld
@@ -347,6 +349,18 @@
 	popl %eax
 	iret
 
+.data
+ready:	.byte 0
+
+ENTRY(stack_start)
+	.long SYMBOL_NAME(init_task_union)+8192
+	.long __KERNEL_DS
+
+.section .rodata,"a"
+/* This is the default interrupt "handler" :-) */
+int_msg:
+	.asciz "Unknown interrupt\n"
+
 /*
  * The interrupt descriptor table has room for 256 idt's,
  * the global descriptor table is dependent on the number
@@ -372,41 +386,58 @@
 SYMBOL_NAME(gdt):
 	.long SYMBOL_NAME(gdt_table)
 
+#ifdef CONFIG_GRKERNSEC_PAX_SEGMEXEC
+.globl SYMBOL_NAME(gdt2)
+	.word 0
+gdt_descr2:
+	.word GDT_ENTRIES*8-1
+SYMBOL_NAME(gdt2):
+	.long SYMBOL_NAME(gdt_table2)
+#endif
+
 /*
  * This is initialized to create an identity-mapping at 0-8M (for bootup
  * purposes) and another mapping of the 0-8M area at virtual address
  * PAGE_OFFSET.
  */
-.org 0x1000
+.section .data.swapper_pg_dir,"a"
 ENTRY(swapper_pg_dir)
-	.long 0x00102007
-	.long 0x00103007
-	.fill BOOT_USER_PGD_PTRS-2,4,0
+	.long pg0-__PAGE_OFFSET+7
+	.long pg1-__PAGE_OFFSET+7
+	.long pg2-__PAGE_OFFSET+7
+	.fill BOOT_USER_PGD_PTRS-3,4,0
 	/* default: 766 entries */
-	.long 0x00102007
-	.long 0x00103007
+	.long pg0-__PAGE_OFFSET+7
+	.long pg1-__PAGE_OFFSET+7
+	.long pg2-__PAGE_OFFSET+7
 	/* default: 254 entries */
-	.fill BOOT_KERNEL_PGD_PTRS-2,4,0
+	.fill BOOT_KERNEL_PGD_PTRS-3,4,0
 
 /*
  * The page tables are initialized to only 8MB here - the final page
  * tables are set up later depending on memory size.
  */
-.org 0x2000
+.section .data.pg0,"a"
 ENTRY(pg0)
+	.fill 1024,4,0
 
-.org 0x3000
+.section .data.pg1,"a"
 ENTRY(pg1)
+	.fill 1024,4,0
+
+.section .data.pg2,"a"
+ENTRY(pg2)
+	.fill 1024,4,0
 
 /*
  * empty_zero_page must immediately follow the page tables ! (The
  * initialization loop counts until empty_zero_page)
  */
-
-.org 0x4000
+.section .data.empty_zero_page,"a"
 ENTRY(empty_zero_page)
+	.fill 1024,4,0
 
-.org 0x5000
+.text
 
 /*
  * Real beginning of normal "text" segment
@@ -419,7 +450,7 @@
  * in the text section because it has alignment requirements
  * that we cannot fulfill any other way.
  */
-.data
+.section .rodata,"a"
 
 ALIGN
 /*
@@ -430,19 +461,55 @@
  */
 ENTRY(gdt_table)
 	.quad 0x0000000000000000	/* NULL descriptor */
-	.quad 0x0000000000000000	/* not used */
-	.quad 0x00cf9a000000ffff	/* 0x10 kernel 4GB code at 0x00000000 */
-	.quad 0x00cf92000000ffff	/* 0x18 kernel 4GB data at 0x00000000 */
-	.quad 0x00cffa000000ffff	/* 0x23 user   4GB code at 0x00000000 */
-	.quad 0x00cff2000000ffff	/* 0x2b user   4GB data at 0x00000000 */
+
+#ifdef CONFIG_GRKERNSEC_PAX_KERNEXEC
+	.quad 0x00cf9b000000ffff        /* 0x08 kernel 4GB code at 0x00000000 */
+	.quad 0xc0cf9b400000ffff        /* 0x10 kernel 4GB code at 0xc0400000 */
+#else
+	.quad 0x0000000000000000        /* not used */
+	.quad 0x00cf9b000000ffff        /* 0x10 kernel 4GB code at 0x00000000 */
+#endif
+
+	.quad 0x00cf93000000ffff	/* 0x18 kernel 4GB data at 0x00000000 */
+	.quad 0x00cffb000000ffff	/* 0x23 user   4GB code at 0x00000000 */
+	.quad 0x00cff3000000ffff	/* 0x2b user   4GB data at 0x00000000 */
+	.quad 0x0000000000000000        /* not used */
+	.quad 0x0000000000000000        /* not used */
+	/*
+	 * The APM segments have byte granularity and their bases
+	 * and limits are set at run time.
+	 */
+	.quad 0x0040930000000000        /* 0x40 APM set up for bad BIOS's */
+	.quad 0x00409b0000000000        /* 0x48 APM CS    code */
+	.quad 0x00009b0000000000        /* 0x50 APM CS 16 code (16 bit) */
+	.quad 0x0040930000000000        /* 0x58 APM DS    data */
+	.fill NR_CPUS*4,8,0             /* space for TSS's and LDT's */
+
+#ifdef CONFIG_GRKERNSEC_PAX_SEGMEXEC
+ENTRY(gdt_table2)
+	.quad 0x0000000000000000        /* NULL descriptor */
+
+#ifdef CONFIG_GRKERNSEC_PAX_KERNEXEC
+	.quad 0x00cf9b000000ffff        /* 0x08 kernel 4GB code at 0x00000000 */
+	.quad 0xc0cf9b400000ffff        /* 0x10 kernel 4GB code at 0xc0400000 */
+#else
+	.quad 0x0000000000000000        /* not used */
+	.quad 0x00cf9b000000ffff        /* 0x10 kernel 4GB code at 0x00000000 */
+#endif
+
+	.quad 0x00cf93000000ffff        /* 0x18 kernel 4GB data at 0x00000000 */
+	.quad 0x60c5fb000000ffff        /* 0x23 user 1.5GB code at 0x60000000 */
+	.quad 0x00c5f3000000ffff        /* 0x2b user 1.5GB data at 0x00000000 */
+
 	.quad 0x0000000000000000	/* not used */
 	.quad 0x0000000000000000	/* not used */
 	/*
 	 * The APM segments have byte granularity and their bases
 	 * and limits are set at run time.
 	 */
-	.quad 0x0040920000000000	/* 0x40 APM set up for bad BIOS's */
-	.quad 0x00409a0000000000	/* 0x48 APM CS    code */
-	.quad 0x00009a0000000000	/* 0x50 APM CS 16 code (16 bit) */
-	.quad 0x0040920000000000	/* 0x58 APM DS    data */
+	.quad 0x0040930000000000	/* 0x40 APM set up for bad BIOS's */
+	.quad 0x00409b0000000000	/* 0x48 APM CS    code */
+	.quad 0x00009b0000000000	/* 0x50 APM CS 16 code (16 bit) */
+	.quad 0x0040930000000000	/* 0x58 APM DS    data */
 	.fill NR_CPUS*4,8,0		/* space for TSS's and LDT's */
+#endif
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/i386/kernel/i386_ksyms.c linux-2.4.25-leo/arch/i386/kernel/i386_ksyms.c
--- linux-2.4.25/arch/i386/kernel/i386_ksyms.c	2004-02-20 14:11:37.000000000 +0000
+++ linux-2.4.25-leo/arch/i386/kernel/i386_ksyms.c	2004-02-20 18:22:22.000000000 +0000
@@ -74,6 +74,9 @@
 EXPORT_SYMBOL(get_cmos_time);
 EXPORT_SYMBOL(apm_info);
 EXPORT_SYMBOL(gdt);
+#ifdef CONFIG_GRKERNSEC_PAX_SEGMEXEC
+EXPORT_SYMBOL(gdt2);
+#endif
 EXPORT_SYMBOL(empty_zero_page);
 
 #ifdef CONFIG_DEBUG_IOVIRT
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/i386/kernel/i387.c linux-2.4.25-leo/arch/i386/kernel/i387.c
--- linux-2.4.25/arch/i386/kernel/i387.c	2003-08-25 12:44:39.000000000 +0100
+++ linux-2.4.25-leo/arch/i386/kernel/i387.c	2004-02-20 18:23:08.000000000 +0000
@@ -10,6 +10,7 @@
 
 #include <linux/config.h>
 #include <linux/sched.h>
+#include <linux/spinlock.h>
 #include <linux/init.h>
 #include <asm/processor.h>
 #include <asm/i387.h>
@@ -89,6 +90,8 @@
 {
 	struct task_struct *tsk = current;
 
+	preempt_disable();
+	
 	if (tsk->flags & PF_USEDFPU) {
 		__save_init_fpu(tsk);
 		return;
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/i386/kernel/ioport.c linux-2.4.25-leo/arch/i386/kernel/ioport.c
--- linux-2.4.25/arch/i386/kernel/ioport.c	2003-06-13 15:51:29.000000000 +0100
+++ linux-2.4.25-leo/arch/i386/kernel/ioport.c	2004-02-20 18:23:08.000000000 +0000
@@ -14,6 +14,7 @@
 #include <linux/smp.h>
 #include <linux/smp_lock.h>
 #include <linux/stddef.h>
+#include <linux/grsecurity.h>
 
 /* Set EXTENT bits starting at BASE in BITMAP to value TURN_ON. */
 static void set_bitmap(unsigned long *bitmap, short base, short extent, int new_value)
@@ -55,17 +56,27 @@
 asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int turn_on)
 {
 	struct thread_struct * t = &current->thread;
-	struct tss_struct * tss = init_tss + smp_processor_id();
+	struct tss_struct * tss;
 
 	if ((from + num <= from) || (from + num > IO_BITMAP_SIZE*32))
 		return -EINVAL;
+#ifdef CONFIG_GRKERNSEC_IO
+	if (turn_on) {
+		gr_handle_ioperm();
+#else
 	if (turn_on && !capable(CAP_SYS_RAWIO))
+#endif
 		return -EPERM;
+#ifdef CONFIG_GRKERNSEC_IO
+	}
+#endif
 	/*
 	 * If it's the first ioperm() call in this thread's lifetime, set the
 	 * IO bitmap up. ioperm() is much less timing critical than clone(),
 	 * this is why we delay this operation until now:
 	 */
+	preempt_disable();
+	tss = init_tss + smp_processor_id();
 	if (!t->ioperm) {
 		/*
 		 * just in case ...
@@ -84,6 +95,7 @@
 		memcpy(tss->io_bitmap, t->io_bitmap, IO_BITMAP_BYTES);
 		tss->bitmap = IO_BITMAP_OFFSET; /* Activate it in the TSS */
 	}
+	preempt_enable();
 
 	return 0;
 }
@@ -109,8 +121,13 @@
 		return -EINVAL;
 	/* Trying to gain more privileges? */
 	if (level > old) {
+#ifdef CONFIG_GRKERNSEC_IO
+		gr_handle_iopl();
+		return -EPERM;
+#else
 		if (!capable(CAP_SYS_RAWIO))
 			return -EPERM;
+#endif
 	}
 	regs->eflags = (regs->eflags & 0xffffcfff) | (level << 12);
 	return 0;
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/i386/kernel/irq.c linux-2.4.25-leo/arch/i386/kernel/irq.c
--- linux-2.4.25/arch/i386/kernel/irq.c	2003-11-28 18:26:19.000000000 +0000
+++ linux-2.4.25-leo/arch/i386/kernel/irq.c	2004-02-20 18:23:08.000000000 +0000
@@ -284,9 +284,11 @@
 				show("wait_on_irq");
 				count = ~0;
 			}
+			preempt_disable();
 			__sti();
 			SYNC_OTHER_CORES(cpu);
 			__cli();
+			preempt_enable_no_resched();
 			if (irqs_running())
 				continue;
 			if (global_irq_lock)
@@ -360,8 +362,9 @@
 
 	__save_flags(flags);
 	if (flags & (1 << EFLAGS_IF_SHIFT)) {
-		int cpu = smp_processor_id();
+		int cpu;
 		__cli();
+		cpu = smp_processor_id();
 		if (!local_irq_count(cpu))
 			get_irqlock(cpu);
 	}
@@ -369,11 +372,14 @@
 
 void __global_sti(void)
 {
-	int cpu = smp_processor_id();
+	int cpu;
 
+	preempt_disable();
+	cpu = smp_processor_id();
 	if (!local_irq_count(cpu))
 		release_irqlock(cpu);
 	__sti();
+	preempt_enable();
 }
 
 /*
@@ -388,13 +394,15 @@
 	int retval;
 	int local_enabled;
 	unsigned long flags;
-	int cpu = smp_processor_id();
+	int cpu;
 
 	__save_flags(flags);
 	local_enabled = (flags >> EFLAGS_IF_SHIFT) & 1;
 	/* default to local */
 	retval = 2 + local_enabled;
 
+	preempt_disable();
+	cpu = smp_processor_id();
 	/* check for global flags if we're not in an interrupt */
 	if (!local_irq_count(cpu)) {
 		if (local_enabled)
@@ -402,6 +410,7 @@
 		if (global_irq_holder == cpu)
 			retval = 0;
 	}
+	preempt_enable();
 	return retval;
 }
 
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/i386/kernel/ldt.c linux-2.4.25-leo/arch/i386/kernel/ldt.c
--- linux-2.4.25/arch/i386/kernel/ldt.c	2004-02-20 14:11:37.000000000 +0000
+++ linux-2.4.25-leo/arch/i386/kernel/ldt.c	2004-02-20 18:23:08.000000000 +0000
@@ -151,7 +151,7 @@
 {
 	int err;
 	unsigned long size;
-	void *address;
+	const void *address;
 
 	err = 0;
 	address = &default_ldt[0];
@@ -190,6 +190,7 @@
 			goto out;
 	}
 
+  preempt_disable();
 	down(&mm->context.sem);
 	if (ldt_info.entry_number >= mm->context.size) {
 		error = alloc_ldt(&current->mm->context, ldt_info.entry_number+1, 1);
@@ -214,6 +215,13 @@
 		}
 	}
 
+#ifdef CONFIG_GRKERNSEC_PAX_SEGMEXEC
+	if ((current->flags & PF_PAX_SEGMEXEC) && (ldt_info.contents & 2)) {
+		error = -EINVAL;
+		goto out_unlock;
+	}
+#endif
+
 	entry_1 = ((ldt_info.base_addr & 0x0000ffff) << 16) |
 		  (ldt_info.limit & 0x0ffff);
 	entry_2 = (ldt_info.base_addr & 0xff000000) |
@@ -224,7 +232,7 @@
 		  ((ldt_info.seg_not_present ^ 1) << 15) |
 		  (ldt_info.seg_32bit << 22) |
 		  (ldt_info.limit_in_pages << 23) |
-		  0x7000;
+		  0x7100;
 	if (!oldmode)
 		entry_2 |= (ldt_info.useable << 20);
 
@@ -236,6 +244,7 @@
 
 out_unlock:
 	up(&mm->context.sem);
+  preempt_enable();
 out:
 	return error;
 }
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/i386/kernel/microcode.c linux-2.4.25-leo/arch/i386/kernel/microcode.c
--- linux-2.4.25/arch/i386/kernel/microcode.c	2004-02-20 14:11:37.000000000 +0000
+++ linux-2.4.25-leo/arch/i386/kernel/microcode.c	2004-02-20 18:23:08.000000000 +0000
@@ -404,11 +404,13 @@
 		goto out_free;
 	}
 
+  preempt_disable();
 	if (smp_call_function(do_update_one, NULL, 1, 1) != 0) {
 		printk(KERN_ERR "microcode: Error! Could not run on all processors\n");
 		error = -EIO;
 	}
 	do_update_one(NULL);
+  preempt_enable();
 
 out_free:
 	for (i = 0; i < smp_num_cpus; i++) {
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/i386/kernel/msr.c linux-2.4.25-leo/arch/i386/kernel/msr.c
--- linux-2.4.25/arch/i386/kernel/msr.c	2001-10-11 17:04:57.000000000 +0100
+++ linux-2.4.25-leo/arch/i386/kernel/msr.c	2004-02-20 18:23:08.000000000 +0000
@@ -114,8 +114,9 @@
 {
   struct msr_command cmd;
 
+  preempt_disable();
   if ( cpu == smp_processor_id() ) {
-    return wrmsr_eio(reg, eax, edx);
+    cmd.err = wrmsr_eio(reg, eax, edx);
   } else {
     cmd.cpu = cpu;
     cmd.reg = reg;
@@ -123,16 +124,19 @@
     cmd.data[1] = edx;
     
     smp_call_function(msr_smp_wrmsr, &cmd, 1, 1);
-    return cmd.err;
   }
+
+  preempt_enable();
+  return cmd.err;
 }
 
 static inline int do_rdmsr(int cpu, u32 reg, u32 *eax, u32 *edx)
 {
   struct msr_command cmd;
 
+  preempt_disable();
   if ( cpu == smp_processor_id() ) {
-    return rdmsr_eio(reg, eax, edx);
+    cmd.err = rdmsr_eio(reg, eax, edx);
   } else {
     cmd.cpu = cpu;
     cmd.reg = reg;
@@ -141,9 +145,10 @@
     
     *eax = cmd.data[0];
     *edx = cmd.data[1];
-
-    return cmd.err;
   }
+
+  preempt_enable();
+  return cmd.err;
 }
 
 #else /* ! CONFIG_SMP */
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/i386/kernel/mtrr.c linux-2.4.25-leo/arch/i386/kernel/mtrr.c
--- linux-2.4.25/arch/i386/kernel/mtrr.c	2003-06-13 15:51:29.000000000 +0100
+++ linux-2.4.25-leo/arch/i386/kernel/mtrr.c	2004-02-20 18:23:08.000000000 +0000
@@ -1065,6 +1065,9 @@
     wait_barrier_execute = TRUE;
     wait_barrier_cache_enable = TRUE;
     atomic_set (&undone_count, smp_num_cpus - 1);
+
+    preempt_disable();
+
     /*  Start the ball rolling on other CPUs  */
     if (smp_call_function (ipi_handler, &data, 1, 0) != 0)
 	panic ("mtrr: timed out waiting for other CPUs\n");
@@ -1090,6 +1093,9 @@
 	then enable the local cache and return  */
     wait_barrier_cache_enable = FALSE;
     set_mtrr_done (&ctxt);
+
+    preempt_enable();
+
 }   /*  End Function set_mtrr_smp  */
 
 
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/i386/kernel/pci-pc.c linux-2.4.25-leo/arch/i386/kernel/pci-pc.c
--- linux-2.4.25/arch/i386/kernel/pci-pc.c	2003-11-28 18:26:19.000000000 +0000
+++ linux-2.4.25-leo/arch/i386/kernel/pci-pc.c	2004-02-20 18:22:22.000000000 +0000
@@ -17,6 +17,7 @@
 #include <asm/io.h>
 #include <asm/smp.h>
 #include <asm/smpboot.h>
+#include <asm/desc.h>
 
 #include "pci-i386.h"
 
@@ -575,10 +576,16 @@
  * the array there.
  */
 
+#ifdef CONFIG_GRKERNSEC_PAX_KERNEXEC
+#define __FLAT_KERNEL_CS 0x08
+#else
+#define __FLAT_KERNEL_CS __KERNEL_CS
+#endif
+
 static struct {
 	unsigned long address;
 	unsigned short segment;
-} bios32_indirect = { 0, __KERNEL_CS };
+} bios32_indirect = { 0, __FLAT_KERNEL_CS };
 
 /*
  * Returns the entry point for the given service, NULL on error
@@ -619,7 +626,9 @@
 static struct {
 	unsigned long address;
 	unsigned short segment;
-} pci_indirect = { 0, __KERNEL_CS };
+} pci_indirect = { 0, __FLAT_KERNEL_CS };
+
+#undef __FLAT_KERNEL_CS
 
 static int pci_bios_present;
 
@@ -1457,6 +1466,7 @@
 	if ((pci_probe & PCI_BIOS_SORT) && !(pci_probe & PCI_NO_SORT))
 		pcibios_sort();
 #endif
+
 }
 
 char * __devinit  pcibios_setup(char *str)
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/i386/kernel/process.c linux-2.4.25-leo/arch/i386/kernel/process.c
--- linux-2.4.25/arch/i386/kernel/process.c	2004-02-20 14:11:37.000000000 +0000
+++ linux-2.4.25-leo/arch/i386/kernel/process.c	2004-02-20 18:22:22.000000000 +0000
@@ -552,7 +552,11 @@
 {
 	struct pt_regs * childregs;
 
+#ifdef CONFIG_GRKERNSEC_PAX_RANDKSTACK
+	childregs = ((struct pt_regs *) (THREAD_SIZE + (unsigned long) p - sizeof(unsigned long))) - 1;
+#else
 	childregs = ((struct pt_regs *) (THREAD_SIZE + (unsigned long) p)) - 1;
+#endif
 	struct_cpy(childregs, regs);
 	childregs->eax = 0;
 	childregs->esp = esp;
@@ -613,6 +617,16 @@
 	dump->u_fpvalid = dump_fpu (regs, &dump->i387);
 }
 
+#ifdef CONFIG_GRKERNSEC_PAX_SEGMEXEC
+void pax_switch_segments(struct task_struct * tsk)
+{
+	if (tsk->flags & PF_PAX_SEGMEXEC)
+		__asm__ __volatile__("lgdt %0": "=m" (gdt_descr2));
+	else
+		__asm__ __volatile__("lgdt %0": "=m" (gdt_descr));
+}
+#endif
+
 /*
  * This special macro can be used to load a debugging register
  */
@@ -652,6 +666,10 @@
 
 	unlazy_fpu(prev_p);
 
+#ifdef CONFIG_GRKERNSEC_PAX_SEGMEXEC
+	pax_switch_segments(next_p);
+#endif
+
 	/*
 	 * Reload esp0, LDT and the page table pointer:
 	 */
@@ -792,3 +810,25 @@
 }
 #undef last_sched
 #undef first_sched
+
+#ifdef CONFIG_GRKERNSEC_PAX_RANDKSTACK
+asmlinkage void pax_randomize_kstack(void)
+{
+	struct tss_struct *tss = init_tss + smp_processor_id();
+	unsigned long time;
+
+	rdtscl(time);
+
+	/* P4 seems to return a 0 LSB, ignore it */
+#ifdef CONFIG_MPENTIUM4
+	time &= 0x3EUL;
+	time <<= 1;
+#else
+	time &= 0x1FUL;
+	time <<= 2;
+#endif
+
+	current->thread.esp0 ^= time;
+	tss->esp0 = current->thread.esp0;
+}
+#endif
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/i386/kernel/ptrace.c linux-2.4.25-leo/arch/i386/kernel/ptrace.c
--- linux-2.4.25/arch/i386/kernel/ptrace.c	2002-08-03 01:39:42.000000000 +0100
+++ linux-2.4.25-leo/arch/i386/kernel/ptrace.c	2004-02-20 18:22:22.000000000 +0000
@@ -13,6 +13,7 @@
 #include <linux/errno.h>
 #include <linux/ptrace.h>
 #include <linux/user.h>
+#include <linux/grsecurity.h>
 
 #include <asm/uaccess.h>
 #include <asm/pgtable.h>
@@ -177,6 +178,9 @@
 	if (pid == 1)		/* you may not mess with init */
 		goto out_tsk;
 
+	if(gr_handle_ptrace(child, request))
+		goto out_tsk;
+
 	if (request == PTRACE_ATTACH) {
 		ret = ptrace_attach(child);
 		goto out_tsk;
@@ -256,6 +260,17 @@
 			  if(addr < (long) &dummy->u_debugreg[4] &&
 			     ((unsigned long) data) >= TASK_SIZE-3) break;
 			  
+#ifdef CONFIG_GRKERNSEC
+			  if(addr >= (long) &dummy->u_debugreg[0] &&
+			     addr <= (long) &dummy->u_debugreg[3]){
+				  long reg   = (addr - (long) &dummy->u_debugreg[0]) >> 2;
+				  long type  = (child->thread.debugreg[7] >> (DR_CONTROL_SHIFT + 4*reg)) & 3;
+				  long align = (child->thread.debugreg[7] >> (DR_CONTROL_SHIFT + 2 + 4*reg)) & 3;
+				  if((type & 1) && (data & align))
+					break;
+			  }
+#endif
+
 			  if(addr == (long) &dummy->u_debugreg[7]) {
 				  data &= ~DR_CONTROL_RESERVED;
 				  for(i=0; i<4; i++)
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/i386/kernel/setup.c linux-2.4.25-leo/arch/i386/kernel/setup.c
--- linux-2.4.25/arch/i386/kernel/setup.c	2004-02-20 14:11:37.000000000 +0000
+++ linux-2.4.25-leo/arch/i386/kernel/setup.c	2004-02-20 18:22:22.000000000 +0000
@@ -3191,7 +3191,7 @@
 	set_tss_desc(nr,t);
 	gdt_table[__TSS(nr)].b &= 0xfffffdff;
 	load_TR(nr);
-	load_LDT(&init_mm.context);
+	_load_LDT(&init_mm.context);
 
 	/*
 	 * Clear all 6 debug registers:
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/i386/kernel/smp.c linux-2.4.25-leo/arch/i386/kernel/smp.c
--- linux-2.4.25/arch/i386/kernel/smp.c	2003-06-13 15:51:29.000000000 +0100
+++ linux-2.4.25-leo/arch/i386/kernel/smp.c	2004-02-20 18:23:08.000000000 +0000
@@ -360,10 +360,14 @@
 
 asmlinkage void smp_invalidate_interrupt (void)
 {
-	unsigned long cpu = smp_processor_id();
+	unsigned long cpu;
+
+	preempt_disable();
+
+	cpu = smp_processor_id();
 
 	if (!test_bit(cpu, &flush_cpumask))
-		return;
+		goto out;
 		/* 
 		 * This was a BUG() but until someone can quote me the
 		 * line from the intel manual that guarantees an IPI to
@@ -384,6 +388,8 @@
 	}
 	ack_APIC_irq();
 	clear_bit(cpu, &flush_cpumask);
+out:
+	preempt_enable();
 }
 
 static void flush_tlb_others (unsigned long cpumask, struct mm_struct *mm,
@@ -433,17 +439,22 @@
 void flush_tlb_current_task(void)
 {
 	struct mm_struct *mm = current->mm;
-	unsigned long cpu_mask = mm->cpu_vm_mask & ~(1 << smp_processor_id());
+	unsigned long cpu_mask;
 
+	preempt_disable();
+	cpu_mask = mm->cpu_vm_mask & ~(1UL << smp_processor_id());
 	local_flush_tlb();
 	if (cpu_mask)
 		flush_tlb_others(cpu_mask, mm, FLUSH_ALL);
+	preempt_enable();
 }
 
 void flush_tlb_mm (struct mm_struct * mm)
 {
-	unsigned long cpu_mask = mm->cpu_vm_mask & ~(1 << smp_processor_id());
+	unsigned long cpu_mask;
 
+	preempt_disable();
+	cpu_mask = mm->cpu_vm_mask & ~(1UL << smp_processor_id());
 	if (current->active_mm == mm) {
 		if (current->mm)
 			local_flush_tlb();
@@ -452,13 +463,16 @@
 	}
 	if (cpu_mask)
 		flush_tlb_others(cpu_mask, mm, FLUSH_ALL);
+	preempt_enable();
 }
 
 void flush_tlb_page(struct vm_area_struct * vma, unsigned long va)
 {
 	struct mm_struct *mm = vma->vm_mm;
-	unsigned long cpu_mask = mm->cpu_vm_mask & ~(1 << smp_processor_id());
+	unsigned long cpu_mask;
 
+	preempt_disable();
+	cpu_mask = mm->cpu_vm_mask & ~(1UL << smp_processor_id());
 	if (current->active_mm == mm) {
 		if(current->mm)
 			__flush_tlb_one(va);
@@ -468,6 +482,7 @@
 
 	if (cpu_mask)
 		flush_tlb_others(cpu_mask, mm, va);
+	preempt_enable();
 }
 
 static inline void do_flush_tlb_all_local(void)
@@ -486,9 +501,11 @@
 
 void flush_tlb_all(void)
 {
+	preempt_disable();
 	smp_call_function (flush_tlb_all_ipi,0,1,1);
 
 	do_flush_tlb_all_local();
+	preempt_enable();
 }
 
 /*
@@ -572,7 +589,7 @@
 static void stop_this_cpu (void * dummy)
 {
 	/*
-	 * Remove this CPU:
+	 * Remove this CPU: assumes preemption is disabled
 	 */
 	clear_bit(smp_processor_id(), &cpu_online_map);
 	__cli();
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/i386/kernel/sys_i386.c linux-2.4.25-leo/arch/i386/kernel/sys_i386.c
--- linux-2.4.25/arch/i386/kernel/sys_i386.c	2003-08-25 12:44:39.000000000 +0100
+++ linux-2.4.25-leo/arch/i386/kernel/sys_i386.c	2004-02-20 18:22:22.000000000 +0000
@@ -18,6 +18,7 @@
 #include <linux/mman.h>
 #include <linux/file.h>
 #include <linux/utsname.h>
+#include <linux/grsecurity.h>
 
 #include <asm/uaccess.h>
 #include <asm/ipc.h>
@@ -48,6 +49,11 @@
 	int error = -EBADF;
 	struct file * file = NULL;
 
+#if defined(CONFIG_GRKERNSEC_PAX_SEGMEXEC) || defined(CONFIG_GRKERNSEC_PAX_RANDEXEC)
+	if (flags & MAP_MIRROR)
+		return -EINVAL;
+#endif
+
 	flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
 	if (!(flags & MAP_ANONYMOUS)) {
 		file = fget(fd);
@@ -55,8 +61,14 @@
 			goto out;
 	}
 
+	if(gr_handle_mmap(file, prot)) {
+		fput(file);
+		error = -EACCES;
+		goto out;
+	}
+
 	down_write(&current->mm->mmap_sem);
-	error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
+	error = do_mmap(file, addr, len, prot, flags, pgoff << PAGE_SHIFT);
 	up_write(&current->mm->mmap_sem);
 
 	if (file)
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/i386/kernel/trampoline.S linux-2.4.25-leo/arch/i386/kernel/trampoline.S
--- linux-2.4.25/arch/i386/kernel/trampoline.S	2002-11-28 23:53:09.000000000 +0000
+++ linux-2.4.25-leo/arch/i386/kernel/trampoline.S	2004-02-20 18:22:22.000000000 +0000
@@ -54,7 +54,7 @@
 	lmsw	%ax		# into protected mode
 	jmp	flush_instr
 flush_instr:
-	ljmpl	$__KERNEL_CS, $0x00100000
+	ljmpl	$__KERNEL_CS, $SYMBOL_NAME(startup_32)-__PAGE_OFFSET
 			# jump to startup_32 in arch/i386/kernel/head.S
 
 idt_48:
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/i386/kernel/traps.c linux-2.4.25-leo/arch/i386/kernel/traps.c
--- linux-2.4.25/arch/i386/kernel/traps.c	2002-11-28 23:53:09.000000000 +0000
+++ linux-2.4.25-leo/arch/i386/kernel/traps.c	2004-02-20 18:23:08.000000000 +0000
@@ -54,7 +54,7 @@
 asmlinkage void lcall7(void);
 asmlinkage void lcall27(void);
 
-struct desc_struct default_ldt[] = { { 0, 0 }, { 0, 0 }, { 0, 0 },
+const struct desc_struct default_ldt[] = { { 0, 0 }, { 0, 0 }, { 0, 0 },
 		{ 0, 0 }, { 0, 0 } };
 
 /*
@@ -228,14 +228,23 @@
 		show_stack((unsigned long*)esp);
 
 		printk("\nCode: ");
+
+#ifndef CONFIG_GRKERNSEC_PAX_KERNEXEC
 		if(regs->eip < PAGE_OFFSET)
 			goto bad;
+#endif
 
 		for(i=0;i<20;i++)
 		{
 			unsigned char c;
+
+#ifdef CONFIG_GRKERNSEC_PAX_KERNEXEC
+			if(__get_user(c, &((unsigned char*)regs->eip)[i+__KERNEL_TEXT_OFFSET])) {
+#else
 			if(__get_user(c, &((unsigned char*)regs->eip)[i])) {
 bad:
+#endif
+
 				printk(" Bad EIP value.");
 				break;
 			}
@@ -258,8 +267,13 @@
 
 	eip = regs->eip;
 
+#ifdef CONFIG_GRKERNSEC_PAX_KERNEXEC
+	eip += __KERNEL_TEXT_OFFSET;
+#else
 	if (eip < PAGE_OFFSET)
 		goto no_bug;
+#endif
+
 	if (__get_user(ud2, (unsigned short *)eip))
 		goto no_bug;
 	if (ud2 != 0x0b0f)
@@ -267,7 +281,13 @@
 	if (__get_user(line, (unsigned short *)(eip + 2)))
 		goto bug;
 	if (__get_user(file, (char **)(eip + 4)) ||
+
+#ifdef CONFIG_GRKERNSEC_PAX_KERNEXEC
+		__get_user(c, file + __KERNEL_TEXT_OFFSET))
+#else
 		(unsigned long)file < PAGE_OFFSET || __get_user(c, file))
+#endif
+
 		file = "<bad filename>";
 
 	printk("kernel BUG at %s:%d!\n", file, line);
@@ -422,6 +442,13 @@
 			regs->eip = fixup;
 			return;
 		}
+
+#ifdef CONFIG_GRKERNSEC_PAX_KERNEXEC
+		if ((regs->xcs & 0xFFFF) == __KERNEL_CS)
+				die("PAX: suspicious general protection fault", regs, error_code);
+		else
+#endif
+
 		die("general protection fault", regs, error_code);
 	}
 }
@@ -527,13 +554,12 @@
 {
 	unsigned int condition;
 	struct task_struct *tsk = current;
-	unsigned long eip = regs->eip;
 	siginfo_t info;
 
 	__asm__ __volatile__("movl %%db6,%0" : "=r" (condition));
 
 	/* If the user set TF, it's simplest to clear it right away. */
-	if ((eip >=PAGE_OFFSET) && (regs->eflags & TF_MASK))
+	if (!(regs->xcs & 3) && (regs->eflags & TF_MASK) && !(regs->eflags & VM_MASK))
 		goto clear_TF;
 
 	/* Mask out spurious debug traps due to lazy DR7 setting */
@@ -751,6 +777,8 @@
  *
  * Careful.. There are problems with IBM-designed IRQ13 behaviour.
  * Don't touch unless you *really* know how it works.
+ *
+ * Must be called with kernel preemption disabled.
  */
 asmlinkage void math_state_restore(struct pt_regs regs)
 {
@@ -826,7 +854,7 @@
 	_set_gate(idt_table+n,15,3,addr);
 }
 
-static void __init set_call_gate(void *a, void *addr)
+static void __init set_call_gate(const void *a, void *addr)
 {
 	_set_gate(a,12,3,addr);
 }
@@ -852,14 +880,58 @@
 	"rorl $16,%%eax" \
 	: "=m"(*(n)) : "a" (addr), "r"(n), "ir"(limit), "i"(type))
 
-void set_tss_desc(unsigned int n, void *addr)
+void set_tss_desc(unsigned int n, const void *addr)
 {
 	_set_tssldt_desc(gdt_table+__TSS(n), (int)addr, 235, 0x89);
+
+#ifdef CONFIG_GRKERNSEC_PAX_SEGMEXEC
+	_set_tssldt_desc(gdt_table2+__TSS(n), (int)addr, 235, 0x89);
+#endif
+
 }
 
-void set_ldt_desc(unsigned int n, void *addr, unsigned int size)
+void __set_ldt_desc(unsigned int n, const void *addr, unsigned int size)
 {
 	_set_tssldt_desc(gdt_table+__LDT(n), (int)addr, ((size << 3)-1), 0x82);
+
+#ifdef CONFIG_GRKERNSEC_PAX_SEGMEXEC
+	_set_tssldt_desc(gdt_table2+__LDT(n), (int)addr, ((size << 3)-1), 0x82);
+#endif
+
+}
+
+void set_ldt_desc(unsigned int n, const void *addr, unsigned int size)
+{
+
+#ifdef CONFIG_GRKERNSEC_PAX_KERNEXEC
+	unsigned long temp, cr3;
+	pgd_t* pgd;
+	pmd_t* pmd;
+
+	asm("movl %%cr3,%0":"=r" (cr3));
+	for (temp = __KERNEL_TEXT_OFFSET; temp < __KERNEL_TEXT_OFFSET + 0x00400000UL; temp += (1UL << PMD_SHIFT)) {
+		pgd = (pgd_t *)__va(cr3) + __pgd_offset(temp);
+		pmd = pmd_offset(pgd, temp);
+		set_pmd(pmd, __pmd(pmd_val(*pmd) | _PAGE_RW));
+	}
+	__flush_tlb_all();
+#endif
+
+	_set_tssldt_desc(gdt_table+__LDT(n), (int)addr, ((size << 3)-1), 0x82);
+
+#ifdef CONFIG_GRKERNSEC_PAX_SEGMEXEC
+	_set_tssldt_desc(gdt_table2+__LDT(n), (int)addr, ((size << 3)-1), 0x82);
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_KERNEXEC
+	for (temp = __KERNEL_TEXT_OFFSET; temp < __KERNEL_TEXT_OFFSET + 0x00400000UL; temp += (1UL << PMD_SHIFT)) {
+		pgd = (pgd_t *)__va(cr3) + __pgd_offset(temp);
+		pmd = pmd_offset(pgd, temp);
+		set_pmd(pmd, __pmd(pmd_val(*pmd) & ~_PAGE_RW));
+	}
+	flush_tlb_all();
+#endif
+
 }
 
 #ifdef CONFIG_X86_VISWS_APIC
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/i386/lib/dec_and_lock.c linux-2.4.25-leo/arch/i386/lib/dec_and_lock.c
--- linux-2.4.25/arch/i386/lib/dec_and_lock.c	2000-07-08 02:20:16.000000000 +0100
+++ linux-2.4.25-leo/arch/i386/lib/dec_and_lock.c	2004-02-20 18:23:08.000000000 +0000
@@ -8,6 +8,7 @@
  */
 
 #include <linux/spinlock.h>
+#include <linux/sched.h>
 #include <asm/atomic.h>
 
 int atomic_dec_and_lock(atomic_t *atomic, spinlock_t *lock)
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/i386/Makefile linux-2.4.25-leo/arch/i386/Makefile
--- linux-2.4.25/arch/i386/Makefile	2003-06-13 15:51:29.000000000 +0100
+++ linux-2.4.25-leo/arch/i386/Makefile	2004-02-20 18:22:22.000000000 +0000
@@ -114,6 +114,9 @@
 
 MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot
 
+arch/i386/vmlinux.lds: arch/i386/vmlinux.lds.S FORCE
+	$(CPP) -C -P -I$(HPATH) -imacros $(HPATH)/linux/config.h -imacros $(HPATH)/asm-i386/segment.h -imacros $(HPATH)/asm-i386/page_offset.h -Ui386 arch/i386/vmlinux.lds.S >arch/i386/vmlinux.lds
+
 vmlinux: arch/i386/vmlinux.lds
 
 FORCE: ;
@@ -150,6 +153,7 @@
 	@$(MAKEBOOT) clean
 
 archmrproper:
+	rm -f arch/i386/vmlinux.lds
 
 archdep:
 	@$(MAKEBOOT) dep
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/i386/mm/fault.c linux-2.4.25-leo/arch/i386/mm/fault.c
--- linux-2.4.25/arch/i386/mm/fault.c	2004-02-20 14:11:37.000000000 +0000
+++ linux-2.4.25-leo/arch/i386/mm/fault.c	2004-02-20 18:22:22.000000000 +0000
@@ -4,6 +4,7 @@
  *  Copyright (C) 1995  Linus Torvalds
  */
 
+#include <linux/config.h>
 #include <linux/signal.h>
 #include <linux/sched.h>
 #include <linux/kernel.h>
@@ -19,6 +20,8 @@
 #include <linux/init.h>
 #include <linux/tty.h>
 #include <linux/vt_kern.h>		/* For unblank_screen() */
+#include <linux/unistd.h>
+#include <linux/compiler.h>
 
 #include <asm/system.h>
 #include <asm/uaccess.h>
@@ -127,6 +130,10 @@
 asmlinkage void do_invalid_op(struct pt_regs *, unsigned long);
 extern unsigned long idt;
 
+#if defined(CONFIG_GRKERNSEC_PAX_PAGEEXEC) || defined(CONFIG_GRKERNSEC_PAX_SEGMEXEC)
+static int pax_handle_fetch_fault(struct pt_regs *regs);
+#endif
+
 /*
  * This routine handles page faults.  It determines the address,
  * and the problem, and then passes it off to one of the appropriate
@@ -137,23 +144,31 @@
  *	bit 1 == 0 means read, 1 means write
  *	bit 2 == 0 means kernel, 1 means user-mode
  */
-asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code)
+#ifdef CONFIG_GRKERNSEC_PAX_PAGEEXEC
+static int do_page_fault(struct pt_regs *regs, unsigned long error_code, unsigned long address)
+#else
+asmlinkage int do_page_fault(struct pt_regs *regs, unsigned long error_code)
+#endif
 {
 	struct task_struct *tsk;
 	struct mm_struct *mm;
 	struct vm_area_struct * vma;
+#ifndef CONFIG_GRKERNSEC_PAX_PAGEEXEC
 	unsigned long address;
+#endif
 	unsigned long page;
 	unsigned long fixup;
 	int write;
 	siginfo_t info;
 
+#ifndef CONFIG_GRKERNSEC_PAX_PAGEEXEC
 	/* get the address */
 	__asm__("movl %%cr2,%0":"=r" (address));
 
 	/* It's safe to allow irq's after cr2 has been saved */
 	if (regs->eflags & X86_EFLAGS_IF)
 		local_irq_enable();
+#endif
 
 	tsk = current;
 
@@ -258,7 +273,7 @@
 			tsk->thread.screen_bitmap |= 1 << bit;
 	}
 	up_read(&mm->mmap_sem);
-	return;
+	return 0;
 
 /*
  * Something tried to access memory that isn't in our memory map..
@@ -269,6 +284,39 @@
 
 	/* User mode accesses just cause a SIGSEGV */
 	if (error_code & 4) {
+#ifdef CONFIG_GRKERNSEC_PAX_SEGMEXEC
+		if (current->flags & PF_PAX_SEGMEXEC) {
+
+#if defined(CONFIG_GRKERNSEC_PAX_EMUTRAMP) || defined(CONFIG_GRKERNSEC_PAX_RANDEXEC)
+		if ((error_code == 4) && (regs->eip + SEGMEXEC_TASK_SIZE == address)) {
+			switch (pax_handle_fetch_fault(regs)) {
+
+#ifdef CONFIG_GRKERNSEC_PAX_RANDEXEC
+			case 5:
+				return 0;
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_EMUTRAMP
+			case 4:
+				return 0;
+			case 3:
+			case 2:
+				return 1;
+#endif
+
+			case 1:
+			default:
+			}
+		}
+#endif
+
+			if (address >= SEGMEXEC_TASK_SIZE) {
+				pax_report_fault(regs, (void*)regs->eip, (void*)regs->esp);
+				do_exit(SIGKILL);
+			}
+		}
+#endif
+
 		tsk->thread.cr2 = address;
 		/* Kernel addresses are always protection faults */
 		tsk->thread.error_code = error_code | (address >= TASK_SIZE);
@@ -278,7 +326,7 @@
 		/* info.si_code has been set above */
 		info.si_addr = (void *)address;
 		force_sig_info(SIGSEGV, &info, tsk);
-		return;
+		return 0;
 	}
 
 	/*
@@ -291,7 +339,7 @@
 
 		if (nr == 6) {
 			do_invalid_op(regs, 0);
-			return;
+			return 0;
 		}
 	}
 
@@ -299,7 +347,7 @@
 	/* Are we prepared to handle this kernel fault?  */
 	if ((fixup = search_exception_table(regs->eip)) != 0) {
 		regs->eip = fixup;
-		return;
+		return 0;
 	}
 
 /*
@@ -311,6 +359,18 @@
 
 	if (address < PAGE_SIZE)
 		printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference");
+
+#ifdef CONFIG_GRKERNSEC_PAX_KERNEXEC
+	else if (init_mm.start_code + __KERNEL_TEXT_OFFSET <= address && address < init_mm.end_code + __KERNEL_TEXT_OFFSET) {
+		if (tsk->curr_ip)
+			printk(KERN_ERR "PAX: From %u.%u.%u.%u: %s:%d, uid/euid: %u/%u, attempted to modify kernel code",
+					 NIPQUAD(tsk->curr_ip), tsk->comm, tsk->pid, tsk->uid, tsk->euid);
+		else
+			printk(KERN_ERR "PAX: %s:%d, uid/euid: %u/%u, attempted to modify kernel code",
+					 tsk->comm, tsk->pid, tsk->uid, tsk->euid);
+	}
+#endif
+
 	else
 		printk(KERN_ALERT "Unable to handle kernel paging request");
 	printk(" at virtual address %08lx\n",address);
@@ -363,7 +423,7 @@
 	/* Kernel mode? Handle exceptions or die */
 	if (!(error_code & 4))
 		goto no_context;
-	return;
+	return 0;
 
 vmalloc_fault:
 	{
@@ -396,6 +456,448 @@
 		pte_k = pte_offset(pmd_k, address);
 		if (!pte_present(*pte_k))
 			goto no_context;
-		return;
+		return 0;
 	}
 }
+#ifdef CONFIG_GRKERNSEC_PAX_PAGEEXEC
+/* PaX: called with the page_table_lock spinlock held */
+static inline pte_t * pax_get_pte(struct mm_struct *mm, unsigned long address)
+{
+	pgd_t *pgd;
+	pmd_t *pmd;
+
+	pgd = pgd_offset(mm, address);
+	if (!pgd || !pgd_present(*pgd))
+		return 0;
+	pmd = pmd_offset(pgd, address);
+	if (!pmd || !pmd_present(*pmd))
+		return 0;
+	return pte_offset(pmd, address);
+}
+#endif
+
+/*
+ * PaX: decide what to do with offenders (regs->eip = fault address)
+ *
+ * returns 1 when task should be killed
+ *         2 when sigreturn trampoline was detected
+ *         3 when rt_sigreturn trampoline was detected
+ *         4 when gcc trampoline was detected
+ *	   5 when legitimate ET_EXEC was detected
+ */
+#if defined(CONFIG_GRKERNSEC_PAX_PAGEEXEC) || defined(CONFIG_GRKERNSEC_PAX_SEGMEXEC)
+static int pax_handle_fetch_fault(struct pt_regs *regs)
+{
+#ifdef CONFIG_GRKERNSEC_PAX_EMUTRAMP
+	static const unsigned char trans[8] = {6, 1, 2, 0, 13, 5, 3, 4};
+#endif
+	int err;
+	
+#ifdef CONFIG_GRKERNSEC_PAX_RANDEXEC
+	if (current->flags & PF_PAX_RANDEXEC) {
+		unsigned long esp_4;
+		if (regs->eip >= current->mm->start_code &&
+		    regs->eip < current->mm->end_code)
+		{
+			err = get_user(esp_4, (unsigned long*)(regs->esp-4UL));
+			if (err || esp_4 == regs->eip)
+				return 1;
+			regs->eip += current->mm->delta_exec;
+			return 5;
+		}
+	}
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_EMUTRAMP
+
+#ifndef CONFIG_GRKERNSEC_PAX_EMUSIGRT
+	if (!(current->flags & PF_PAX_EMUTRAMP))
+		return 1;
+#endif
+
+	do { /* PaX: sigreturn emulation */
+		unsigned char pop, mov;
+		unsigned short sys;
+		unsigned long nr;
+
+		err = get_user(pop, (unsigned char *)(regs->eip));
+		err |= get_user(mov, (unsigned char *)(regs->eip + 1));
+		err |= get_user(nr, (unsigned long *)(regs->eip + 2));
+		err |= get_user(sys, (unsigned short *)(regs->eip + 6));
+
+		if (err)
+			break;
+
+		if (pop == 0x58 &&
+		    mov == 0xb8 &&
+		    nr == __NR_sigreturn &&
+		    sys == 0x80cd)
+		{
+
+#ifdef CONFIG_GRKERNSEC_PAX_EMUSIGRT
+			int sig;
+			struct k_sigaction *ka;
+			__sighandler_t handler;
+
+			if (get_user(sig, (int *)regs->esp))
+				return 1;
+			if (sig < 1 || sig > _NSIG || sig == SIGKILL || sig == SIGSTOP)
+				return 1;
+			ka = &current->sig->action[sig-1];
+			handler = ka->sa.sa_handler;
+			if (handler == SIG_DFL || handler == SIG_IGN) {
+				if (!(current->flags & PF_PAX_EMUTRAMP))
+					return 1;
+			} else if (!(ka->sa.sa_flags & SA_SIGINFO))
+				return 1;
+#endif
+
+			regs->esp += 4;
+			regs->eax = nr;
+			regs->eip += 8;
+			return 2;
+		}
+	} while (0);
+
+	do { /* PaX: rt_sigreturn emulation */
+		unsigned char mov;
+		unsigned short sys;
+		unsigned long nr;
+
+		err = get_user(mov, (unsigned char *)(regs->eip));
+		err |= get_user(nr, (unsigned long *)(regs->eip + 1));
+		err |= get_user(sys, (unsigned short *)(regs->eip + 5));
+
+		if (err)
+			break;
+
+		if (mov == 0xb8 &&
+		    nr == __NR_rt_sigreturn &&
+		    sys == 0x80cd)
+		{
+
+#ifdef CONFIG_GRKERNSEC_PAX_EMUSIGRT
+			int sig;
+			struct k_sigaction *ka;
+			__sighandler_t handler;
+
+			if (get_user(sig, (int *)regs->esp))
+				return 1;
+			if (sig < 1 || sig > _NSIG || sig == SIGKILL || sig == SIGSTOP)
+				return 1;
+			ka = &current->sig->action[sig-1];
+			handler = ka->sa.sa_handler;
+			if (handler == SIG_DFL || handler == SIG_IGN) {
+				if (!(current->flags & PF_PAX_EMUTRAMP))
+					return 1;
+			} else if (ka->sa.sa_flags & SA_SIGINFO)
+				return 1;
+#endif
+
+			regs->eax = nr;
+			regs->eip += 7;
+			return 3;
+		}
+	} while (0);
+
+#ifdef CONFIG_GRKERNSEC_PAX_EMUSIGRT
+	if (!(current->flags & PF_PAX_EMUTRAMP))
+		return 1;
+#endif
+
+	do { /* PaX: gcc trampoline emulation #1 */
+		unsigned char mov1, mov2;
+		unsigned short jmp;
+		unsigned long addr1, addr2, ret;
+		unsigned short call;
+
+		err = get_user(mov1, (unsigned char *)regs->eip);
+		err |= get_user(addr1, (unsigned long *)(regs->eip + 1));
+		err |= get_user(mov2, (unsigned char *)(regs->eip + 5));
+		err |= get_user(addr2, (unsigned long *)(regs->eip + 6));
+		err |= get_user(jmp, (unsigned short *)(regs->eip + 10));
+		err |= get_user(ret, (unsigned long *)regs->esp);
+
+		if (err)
+			break;
+
+		err = get_user(call, (unsigned short *)(ret-2));
+		if (err)
+			break;
+
+		if ((mov1 & 0xF8) == 0xB8 &&
+		    (mov2 & 0xF8) == 0xB8 &&
+		    (mov1 & 0x07) != (mov2 & 0x07) &&
+		    (jmp & 0xF8FF) == 0xE0FF &&
+		    (mov2 & 0x07) == ((jmp>>8) & 0x07) &&
+		    (call & 0xF8FF) == 0xD0FF &&
+		    regs->eip == ((unsigned long*)regs)[trans[(call>>8) & 0x07]])
+		{
+			((unsigned long *)regs)[trans[mov1 & 0x07]] = addr1;
+			((unsigned long *)regs)[trans[mov2 & 0x07]] = addr2;
+			regs->eip = addr2;
+			return 4;
+		}
+	} while (0);
+
+	do { /* PaX: gcc trampoline emulation #2 */
+		unsigned char mov, jmp;
+		unsigned long addr1, addr2, ret;
+		unsigned short call;
+
+		err = get_user(mov, (unsigned char *)regs->eip);
+		err |= get_user(addr1, (unsigned long *)(regs->eip + 1));
+		err |= get_user(jmp, (unsigned char *)(regs->eip + 5));
+		err |= get_user(addr2, (unsigned long *)(regs->eip + 6));
+		err |= get_user(ret, (unsigned long *)regs->esp);
+
+		if (err)
+			break;
+
+		err = get_user(call, (unsigned short *)(ret-2));
+		if (err)
+			break;
+
+		if ((mov & 0xF8) == 0xB8 &&
+		    jmp == 0xE9 &&
+		    (call & 0xF8FF) == 0xD0FF &&
+		    regs->eip == ((unsigned long*)regs)[trans[(call>>8) & 0x07]])
+		{
+			((unsigned long *)regs)[trans[mov & 0x07]] = addr1;
+			regs->eip += addr2 + 10;
+			return 4;
+		}
+	} while (0);
+
+	do { /* PaX: gcc trampoline emulation #3 */
+		unsigned char mov, jmp;
+		char offset;
+		unsigned long addr1, addr2, ret;
+		unsigned short call;
+
+		err = get_user(mov, (unsigned char *)regs->eip);
+		err |= get_user(addr1, (unsigned long *)(regs->eip + 1));
+		err |= get_user(jmp, (unsigned char *)(regs->eip + 5));
+		err |= get_user(addr2, (unsigned long *)(regs->eip + 6));
+		err |= get_user(ret, (unsigned long *)regs->esp);
+
+		if (err)
+			break;
+
+		err = get_user(call, (unsigned short *)(ret-3));
+		err |= get_user(offset, (char *)(ret-1));
+		if (err)
+			break;
+
+		if ((mov & 0xF8) == 0xB8 &&
+		    jmp == 0xE9 &&
+		    call == 0x55FF)
+		{
+			unsigned long addr;
+
+			err = get_user(addr, (unsigned long*)(regs->ebp + (unsigned long)(long)offset));
+			if (err || regs->eip != addr)
+				break;
+
+			((unsigned long *)regs)[trans[mov & 0x07]] = addr1;
+			regs->eip += addr2 + 10;
+			return 4;
+		}
+	} while (0);
+
+	do { /* PaX: gcc trampoline emulation #4 */
+		unsigned char mov, jmp, sib;
+		char offset;
+		unsigned long addr1, addr2, ret;
+		unsigned short call;
+
+		err = get_user(mov, (unsigned char *)regs->eip);
+		err |= get_user(addr1, (unsigned long *)(regs->eip + 1));
+		err |= get_user(jmp, (unsigned char *)(regs->eip + 5));
+		err |= get_user(addr2, (unsigned long *)(regs->eip + 6));
+		err |= get_user(ret, (unsigned long *)regs->esp);
+
+		if (err)
+			break;
+
+		err = get_user(call, (unsigned short *)(ret-4));
+		err |= get_user(sib, (unsigned char *)(ret-2));
+		err |= get_user(offset, (char *)(ret-1));
+		if (err)
+			break;
+
+		if ((mov & 0xF8) == 0xB8 &&
+		    jmp == 0xE9 &&
+		    call == 0x54FF &&
+		    sib == 0x24)
+		{
+			unsigned long addr;
+
+			err = get_user(addr, (unsigned long*)(regs->esp + 4 + (unsigned long)(long)offset));
+			if (err || regs->eip != addr)
+				break;
+
+			((unsigned long *)regs)[trans[mov & 0x07]] = addr1;
+			regs->eip += addr2 + 10;
+			return 4;
+		}
+	} while (0);
+
+	do { /* PaX: gcc trampoline emulation #5 */
+		unsigned char mov, jmp, sib;
+		unsigned long addr1, addr2, ret, offset;
+		unsigned short call;
+
+		err = get_user(mov, (unsigned char *)regs->eip);
+		err |= get_user(addr1, (unsigned long *)(regs->eip + 1));
+		err |= get_user(jmp, (unsigned char *)(regs->eip + 5));
+		err |= get_user(addr2, (unsigned long *)(regs->eip + 6));
+		err |= get_user(ret, (unsigned long *)regs->esp);
+
+		if (err)
+			break;
+
+		err = get_user(call, (unsigned short *)(ret-7));
+		err |= get_user(sib, (unsigned char *)(ret-5));
+		err |= get_user(offset, (unsigned long *)(ret-4));
+		if (err)
+			break;
+
+		if ((mov & 0xF8) == 0xB8 &&
+		    jmp == 0xE9 &&
+		    call == 0x94FF &&
+		    sib == 0x24)
+		{
+			unsigned long addr;
+
+			err = get_user(addr, (unsigned long*)(regs->esp + 4 + offset));
+			if (err || regs->eip != addr)
+				break;
+
+			((unsigned long *)regs)[trans[mov & 0x07]] = addr1;
+			regs->eip += addr2 + 10;
+			return 4;
+		}
+	} while (0);
+#endif
+
+	return 1; /* PaX in action */
+}
+
+void pax_report_insns(void *pc)
+{
+	unsigned long i;
+
+	printk(KERN_ERR "PAX: bytes at PC: ");
+	for (i = 0; i < 20; i++) {
+		unsigned char c;
+		if (get_user(c, (unsigned char*)pc+i)) {
+			printk("<invalid address>.");
+			break;
+		}
+		printk("%02x ", c);
+	}
+	printk("\n");
+}
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_PAGEEXEC
+/*
+ * PaX: handle the extra page faults or pass it down to the original handler
+ *
+ * returns 0 when nothing special was detected
+ *         1 when sigreturn trampoline (syscall) has to be emulated
+ */
+asmlinkage int pax_do_page_fault(struct pt_regs *regs, unsigned long error_code)
+{
+	struct mm_struct *mm = current->mm;
+	unsigned long address;
+	pte_t *pte;
+	unsigned char pte_mask;
+	int ret;
+
+	__asm__("movl %%cr2,%0":"=r" (address));
+
+	/* It's safe to allow irq's after cr2 has been saved */
+	if (likely(regs->eflags & X86_EFLAGS_IF))
+		local_irq_enable();
+
+	if (unlikely((error_code & 5) != 5 ||
+		     address >= TASK_SIZE ||
+		     !(current->flags & PF_PAX_PAGEEXEC)))
+		return do_page_fault(regs, error_code, address);
+
+	/* PaX: it's our fault, let's handle it if we can */
+
+	/* PaX: take a look at read faults before acquiring any locks */
+	if (unlikely((error_code == 5) && (regs->eip == address))) { 
+		/* instruction fetch attempt from a protected page in user mode */
+		ret = pax_handle_fetch_fault(regs);
+		switch (ret) {
+#ifdef CONFIG_GRKERNSEC_PAX_RANDEXEC
+		case 5:
+			return 0;
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_EMUTRAMP
+		case 4:
+			return 0;
+		case 3:
+		case 2:
+			return 1;
+#endif
+		case 1:
+		default:
+			pax_report_fault(regs, (void*)regs->eip, (void*)regs->esp);
+			do_exit(SIGKILL);
+		}
+	}
+
+	pte_mask = _PAGE_ACCESSED | _PAGE_USER | ((error_code & 2) << (_PAGE_BIT_DIRTY-1));
+
+	spin_lock(&mm->page_table_lock);
+	pte = pax_get_pte(mm, address);
+	if (unlikely(!pte || !(pte_val(*pte) & _PAGE_PRESENT) || pte_exec(*pte))) {
+		spin_unlock(&mm->page_table_lock);
+		do_page_fault(regs, error_code, address);
+		return 0;
+	}
+
+	if (unlikely((error_code == 7) && !pte_write(*pte))) {
+		/* write attempt to a protected page in user mode */
+		spin_unlock(&mm->page_table_lock);
+		do_page_fault(regs, error_code, address);
+		return 0;
+	}
+
+	/*
+	 * PaX: fill DTLB with user rights and retry
+	 */
+	__asm__ __volatile__ (
+		"orb %2,%1\n"
+#if defined(CONFIG_M586) || defined(CONFIG_M586TSC)
+/*   
+ * PaX: let this uncommented 'invlpg' remind us on the behaviour of Intel's   
+ * (and AMD's) TLBs. namely, they do not cache PTEs that would raise *any*
+ * page fault when examined during a TLB load attempt. this is true not only
+ * for PTEs holding a non-present entry but also present entries that will
+ * raise a page fault (such as those set up by PaX, or the copy-on-write
+ * mechanism). in effect it means that we do *not* need to flush the TLBs
+ * for our target pages since their PTEs are simply not in the TLBs at all.
+ * the best thing in omitting it is that we gain around 15-20% speed in the
+ * fast path of the page fault handler and can get rid of tracing since we
+ * can no longer flush unintended entries.
+ */
+
+		"invlpg %0\n"
+#endif
+
+		"testb $0,%0\n"
+		"xorb %3,%1\n"
+		:
+		: "m" (*(char*)address), "m" (*(char*)pte) , "q" (pte_mask) , "i" (_PAGE_USER)
+		: "memory", "cc");
+	spin_unlock(&mm->page_table_lock);
+	return 0;
+}
+#endif
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/i386/mm/init.c linux-2.4.25-leo/arch/i386/mm/init.c
--- linux-2.4.25/arch/i386/mm/init.c	2003-06-13 15:51:29.000000000 +0100
+++ linux-2.4.25-leo/arch/i386/mm/init.c	2004-02-20 18:23:08.000000000 +0000
@@ -37,6 +37,8 @@
 #include <asm/e820.h>
 #include <asm/apic.h>
 #include <asm/tlb.h>
+#include <asm/page_offset.h>
+#include <asm/desc.h>
 
 mmu_gather_t mmu_gathers[NR_CPUS];
 unsigned long highstart_pfn, highend_pfn;
@@ -46,6 +48,7 @@
 int do_check_pgt_cache(int low, int high)
 {
 	int freed = 0;
+	preempt_disable();
 	if(pgtable_cache_size > high) {
 		do {
 			if (pgd_quicklist) {
@@ -62,6 +65,7 @@
 			}
 		} while(pgtable_cache_size > low);
 	}
+	preempt_enable();
 	return freed;
 }
 
@@ -122,7 +126,7 @@
 
 /* References to section boundaries */
 
-extern char _text, _etext, _edata, __bss_start, _end;
+extern char _text, _etext, _data, _edata, __bss_start, _end;
 extern char __init_begin, __init_end;
 
 static inline void set_pte_phys (unsigned long vaddr,
@@ -521,7 +525,7 @@
 	reservedpages = free_pages_init();
 
 	codesize =  (unsigned long) &_etext - (unsigned long) &_text;
-	datasize =  (unsigned long) &_edata - (unsigned long) &_etext;
+	datasize =  (unsigned long) &_edata - (unsigned long) &_data;
 	initsize =  (unsigned long) &__init_end - (unsigned long) &__init_begin;
 
 	printk(KERN_INFO "Memory: %luk/%luk available (%dk kernel code, %dk reserved, %dk data, %dk init, %ldk highmem)\n",
@@ -589,6 +593,42 @@
 		totalram_pages++;
 	}
 	printk (KERN_INFO "Freeing unused kernel memory: %dk freed\n", (&__init_end - &__init_begin) >> 10);
+
+#ifdef CONFIG_GRKERNSEC_PAX_KERNEXEC
+	/* PaX: limit KERNEL_CS to actual size */
+	{
+		unsigned long limit;
+
+		limit = (unsigned long)&_etext >> PAGE_SHIFT;
+		gdt_table[2].a = (gdt_table[2].a & 0xFFFF0000UL) | (limit & 0x0FFFFUL);
+		gdt_table[2].b = (gdt_table[2].b & 0xFFF0FFFFUL) | (limit & 0xF0000UL);
+
+		/* PaX: nuke __FLAT_KERNEL_CS, no longer needed */
+		gdt_table[1].a = 0UL;
+		gdt_table[1].b = 0UL;
+
+#ifdef CONFIG_GRKERNSEC_PAX_SEGMEXEC
+		gdt_table2[2].a = (gdt_table2[2].a & 0xFFFF0000UL) | (limit & 0x0FFFFUL);
+		gdt_table2[2].b = (gdt_table2[2].b & 0xFFF0FFFFUL) | (limit & 0xF0000UL);
+
+		/* PaX: nuke __FLAT_KERNEL_CS, no longer needed */
+		gdt_table2[1].a = 0UL;
+		gdt_table2[1].b = 0UL;
+#endif
+
+	/* PaX: make KERNEL_CS read-only */
+		for (addr = __KERNEL_TEXT_OFFSET; addr < __KERNEL_TEXT_OFFSET + 0x00400000UL; addr += (1UL << PMD_SHIFT)) {
+			pgd_t *pgd;
+			pmd_t *pmd;
+
+			pgd = pgd_offset_k(addr);
+			pmd = pmd_offset(pgd, addr);
+			set_pmd(pmd, __pmd(pmd_val(*pmd) & ~_PAGE_RW));
+		}
+		flush_tlb_all();
+	}
+#endif
+
 }
 
 #ifdef CONFIG_BLK_DEV_INITRD
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/i386/vmlinux.lds linux-2.4.25-leo/arch/i386/vmlinux.lds
--- linux-2.4.25/arch/i386/vmlinux.lds	2002-02-25 19:37:53.000000000 +0000
+++ linux-2.4.25-leo/arch/i386/vmlinux.lds	1970-01-01 01:00:00.000000000 +0100
@@ -1,82 +0,0 @@
-/* ld script to make i386 Linux kernel
- * Written by Martin Mares <mj@atrey.karlin.mff.cuni.cz>;
- */
-OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
-OUTPUT_ARCH(i386)
-ENTRY(_start)
-SECTIONS
-{
-  . = 0xC0000000 + 0x100000;
-  _text = .;			/* Text and read-only data */
-  .text : {
-	*(.text)
-	*(.fixup)
-	*(.gnu.warning)
-	} = 0x9090
-
-  _etext = .;			/* End of text section */
-
-  .rodata : { *(.rodata) *(.rodata.*) }
-  .kstrtab : { *(.kstrtab) }
-
-  . = ALIGN(16);		/* Exception table */
-  __start___ex_table = .;
-  __ex_table : { *(__ex_table) }
-  __stop___ex_table = .;
-
-  __start___ksymtab = .;	/* Kernel symbol table */
-  __ksymtab : { *(__ksymtab) }
-  __stop___ksymtab = .;
-
-  .data : {			/* Data */
-	*(.data)
-	CONSTRUCTORS
-	}
-
-  _edata = .;			/* End of data section */
-
-  . = ALIGN(8192);		/* init_task */
-  .data.init_task : { *(.data.init_task) }
-
-  . = ALIGN(4096);		/* Init code and data */
-  __init_begin = .;
-  .text.init : { *(.text.init) }
-  .data.init : { *(.data.init) }
-  . = ALIGN(16);
-  __setup_start = .;
-  .setup.init : { *(.setup.init) }
-  __setup_end = .;
-  __initcall_start = .;
-  .initcall.init : { *(.initcall.init) }
-  __initcall_end = .;
-  . = ALIGN(4096);
-  __init_end = .;
-
-  . = ALIGN(4096);
-  .data.page_aligned : { *(.data.idt) }
-
-  . = ALIGN(32);
-  .data.cacheline_aligned : { *(.data.cacheline_aligned) }
-
-  __bss_start = .;		/* BSS */
-  .bss : {
-	*(.bss)
-	}
-  _end = . ;
-
-  /* Sections to be discarded */
-  /DISCARD/ : {
-	*(.text.exit)
-	*(.data.exit)
-	*(.exitcall.exit)
-	}
-
-  /* Stabs debugging sections.  */
-  .stab 0 : { *(.stab) }
-  .stabstr 0 : { *(.stabstr) }
-  .stab.excl 0 : { *(.stab.excl) }
-  .stab.exclstr 0 : { *(.stab.exclstr) }
-  .stab.index 0 : { *(.stab.index) }
-  .stab.indexstr 0 : { *(.stab.indexstr) }
-  .comment 0 : { *(.comment) }
-}
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/i386/vmlinux.lds.S linux-2.4.25-leo/arch/i386/vmlinux.lds.S
--- linux-2.4.25/arch/i386/vmlinux.lds.S	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.25-leo/arch/i386/vmlinux.lds.S	2004-02-20 18:22:22.000000000 +0000
@@ -0,0 +1,136 @@
+/* ld script to make i386 Linux kernel
+ * Written by Martin Mares <mj@atrey.karlin.mff.cuni.cz>;
+ */
+OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
+OUTPUT_ARCH(i386)
+ENTRY(_start)
+SECTIONS
+{
+  . = __PAGE_OFFSET + 0x100000;
+  .text.startup : {
+	BYTE(0xEA) /* jmp far */
+
+#ifdef CONFIG_GRKERNSEC_PAX_KERNEXEC
+	LONG(startup_32 + __KERNEL_TEXT_OFFSET - __PAGE_OFFSET)
+#else
+	LONG(startup_32 - __PAGE_OFFSET)
+#endif
+
+	SHORT(__KERNEL_CS)
+	}
+
+  . = ALIGN(32);
+  _data = .;
+  .data : {			/* Data */
+	*(.data)
+	CONSTRUCTORS
+	}
+
+  . = ALIGN(32);
+  .data.cacheline_aligned : { *(.data.cacheline_aligned) }
+
+  . = ALIGN(8192);
+  .data.init_task : { *(.data.init_task) }
+
+  . = ALIGN(4096);
+  .data.page_aligned : { *(.data.swapper_pg_dir) }
+
+  _edata = .;			/* End of data section */
+
+  __bss_start = .;		/* BSS */
+  .bss : {
+	*(.bss)
+	LONG(0)
+	} 
+  __bss_end = . ;
+
+  . = ALIGN(4096);		/* Init code and data */
+  __init_begin = .;
+
+  .data.init : {
+	*(.data.pg0)
+	*(.data.pg1)
+	*(.data.pg2)
+	*(.data.init)
+	}
+  . = ALIGN(16);
+  __setup_start = .;
+  .setup.init : { *(.setup.init) }
+  __setup_end = .;
+  __initcall_start = .;
+  .initcall.init : { *(.initcall.init) }
+  __initcall_end = .;
+
+#ifdef CONFIG_GRKERNSEC_PAX_KERNEXEC
+  __text_init_start = .;
+  .text.init (. - __KERNEL_TEXT_OFFSET) : AT (__text_init_start) {
+	*(.text.init)
+	. = ALIGN(4*1024*1024) - 1;
+	BYTE(0)
+	}
+  __init_end = . + __KERNEL_TEXT_OFFSET;
+
+/*
+ * PaX: this must be kept in synch with the KERNEL_CS base
+ * in the GDTs in arch/i386/kernel/head.S
+ */
+  _text = .;			/* Text and read-only data */
+  .text : AT (. + __KERNEL_TEXT_OFFSET) {
+#else
+  .text.init : { *(.text.init) }
+  . = ALIGN(4096);
+  __init_end = .;
+  _text = .;			/* Text and read-only data */
+  .text : {
+#endif
+
+	*(.text)
+	*(.fixup)
+	*(.gnu.warning)
+	} = 0x9090
+
+  _etext = .;			/* End of text section */
+  . = ALIGN(4096);
+
+#ifdef CONFIG_GRKERNSEC_PAX_KERNEXEC
+  . += __KERNEL_TEXT_OFFSET;
+#endif
+
+  .rodata.page_aligned : {
+	*(.data.empty_zero_page)
+	*(.data.idt)
+	}
+  .rodata : { *(.rodata) *(.rodata.*) }
+  .kstrtab : { *(.kstrtab) }
+
+  . = ALIGN(16);		/* Exception table */
+  __start___ex_table = .;
+  __ex_table : { *(__ex_table) }
+  __stop___ex_table = .;
+
+  __start___ksymtab = .;	/* Kernel symbol table */
+  __ksymtab : { *(__ksymtab) }
+  __stop___ksymtab = .;
+
+#ifdef CONFIG_GRKERNSEC_PAX_KERNEXEC
+  _end = ALIGN(4*1024*1024);
+#else
+  _end = .;
+#endif
+
+  /* Sections to be discarded */
+  /DISCARD/ : {
+	*(.text.exit)
+	*(.data.exit)
+	*(.exitcall.exit)
+	}
+
+  /* Stabs debugging sections.  */
+  .stab 0 : { *(.stab) }
+  .stabstr 0 : { *(.stabstr) }
+  .stab.excl 0 : { *(.stab.excl) }
+  .stab.exclstr 0 : { *(.stab.exclstr) }
+  .stab.index 0 : { *(.stab.index) }
+  .stab.indexstr 0 : { *(.stab.indexstr) }
+  .comment 0 : { *(.comment) }
+}
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/ia64/config.in linux-2.4.25-leo/arch/ia64/config.in
--- linux-2.4.25/arch/ia64/config.in	2004-02-20 14:11:37.000000000 +0000
+++ linux-2.4.25-leo/arch/ia64/config.in	2004-02-20 18:22:22.000000000 +0000
@@ -319,3 +319,12 @@
 int 'Kernel messages buffer length shift (0 = default)' CONFIG_LOG_BUF_SHIFT 0
 
 endmenu
+
+mainmenu_option next_comment
+comment 'Grsecurity'
+bool 'Grsecurity' CONFIG_GRKERNSEC
+if [ "$CONFIG_GRKERNSEC" = "y" ]; then
+    source grsecurity/Config.in
+fi
+endmenu
+
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/ia64/kernel/ptrace.c linux-2.4.25-leo/arch/ia64/kernel/ptrace.c
--- linux-2.4.25/arch/ia64/kernel/ptrace.c	2003-11-28 18:26:19.000000000 +0000
+++ linux-2.4.25-leo/arch/ia64/kernel/ptrace.c	2004-02-20 18:22:22.000000000 +0000
@@ -16,6 +16,7 @@
 #include <linux/ptrace.h>
 #include <linux/smp_lock.h>
 #include <linux/user.h>
+#include <linux/grsecurity.h>
 
 #include <asm/pgtable.h>
 #include <asm/processor.h>
@@ -1273,6 +1274,9 @@
 	if (pid == 1)		/* no messing around with init! */
 		goto out_tsk;
 
+	if (gr_handle_ptrace(child, request))
+		goto out_tsk;
+
 	if (request == PTRACE_ATTACH) {
 		ret = ptrace_attach(child);
 		goto out_tsk;
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/ia64/kernel/sys_ia64.c linux-2.4.25-leo/arch/ia64/kernel/sys_ia64.c
--- linux-2.4.25/arch/ia64/kernel/sys_ia64.c	2004-02-20 14:11:37.000000000 +0000
+++ linux-2.4.25-leo/arch/ia64/kernel/sys_ia64.c	2004-02-20 18:22:22.000000000 +0000
@@ -16,6 +16,7 @@
 #include <linux/smp_lock.h>
 #include <linux/highuid.h>
 #include <linux/hugetlb.h>
+#include <linux/grsecurity.h>
 
 #include <asm/shmparam.h>
 #include <asm/uaccess.h>
@@ -211,6 +212,11 @@
 		goto out;
 	}
 
+	if (gr_handle_mmap(file, prot)) {
+		addr = -EACCES;
+		goto out;
+	}
+
 	down_write(&current->mm->mmap_sem);
 	addr = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
 	up_write(&current->mm->mmap_sem);
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/m68k/config.in linux-2.4.25-leo/arch/m68k/config.in
--- linux-2.4.25/arch/m68k/config.in	2004-02-20 14:11:37.000000000 +0000
+++ linux-2.4.25-leo/arch/m68k/config.in	2004-02-20 18:22:22.000000000 +0000
@@ -557,3 +557,11 @@
 
 source crypto/Config.in
 source lib/Config.in
+
+mainmenu_option next_comment
+comment 'Grsecurity'
+bool 'Grsecurity' CONFIG_GRKERNSEC
+if [ "$CONFIG_GRKERNSEC" = "y" ]; then
+	source grsecurity/Config.in
+fi
+endmenu
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/mips/config.in linux-2.4.25-leo/arch/mips/config.in
--- linux-2.4.25/arch/mips/config.in	2002-11-28 23:53:09.000000000 +0000
+++ linux-2.4.25-leo/arch/mips/config.in	2004-02-20 18:22:22.000000000 +0000
@@ -7,3 +7,11 @@
 define_bool CONFIG_MIPS64 n
 
 source arch/mips/config-shared.in
+
+mainmenu_option next_comment
+comment 'Grsecurity'
+bool 'Grsecurity' CONFIG_GRKERNSEC
+if [ "$CONFIG_GRKERNSEC" = "y" ]; then
+        source grsecurity/Config.in
+fi
+endmenu
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/mips/config-shared.in linux-2.4.25-leo/arch/mips/config-shared.in
--- linux-2.4.25/arch/mips/config-shared.in	2004-02-20 14:11:37.000000000 +0000
+++ linux-2.4.25-leo/arch/mips/config-shared.in	2004-02-20 18:23:08.000000000 +0000
@@ -937,6 +937,7 @@
    define_bool CONFIG_HOTPLUG_PCI n
 fi
 
+dep_bool 'Preemptible Kernel' CONFIG_PREEMPT $CONFIG_NEW_IRQ
 bool 'System V IPC' CONFIG_SYSVIPC
 bool 'BSD Process Accounting' CONFIG_BSD_PROCESS_ACCT
 bool 'Sysctl support' CONFIG_SYSCTL
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/mips/kernel/i8259.c linux-2.4.25-leo/arch/mips/kernel/i8259.c
--- linux-2.4.25/arch/mips/kernel/i8259.c	2003-08-25 12:44:40.000000000 +0100
+++ linux-2.4.25-leo/arch/mips/kernel/i8259.c	2004-02-20 18:23:08.000000000 +0000
@@ -8,6 +8,7 @@
  * Copyright (C) 1992 Linus Torvalds
  * Copyright (C) 1994 - 2000 Ralf Baechle
  */
+#include <linux/sched.h>
 #include <linux/delay.h>
 #include <linux/init.h>
 #include <linux/ioport.h>
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/mips/kernel/irq.c linux-2.4.25-leo/arch/mips/kernel/irq.c
--- linux-2.4.25/arch/mips/kernel/irq.c	2004-02-20 14:11:38.000000000 +0000
+++ linux-2.4.25-leo/arch/mips/kernel/irq.c	2004-02-20 18:23:08.000000000 +0000
@@ -8,6 +8,8 @@
  * Copyright (C) 1992 Linus Torvalds
  * Copyright (C) 1994 - 2000 Ralf Baechle
  */
+
+#include <linux/sched.h>
 #include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/delay.h>
@@ -19,11 +21,13 @@
 #include <linux/slab.h>
 #include <linux/mm.h>
 #include <linux/random.h>
-#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/ptrace.h>
 
 #include <asm/atomic.h>
 #include <asm/system.h>
 #include <asm/uaccess.h>
+#include <asm/debug.h>
 
 /*
  * Controller mappings for all interrupt sources:
@@ -429,6 +433,8 @@
 	struct irqaction * action;
 	unsigned int status;
 
+	preempt_disable();
+
 	kstat.irqs[cpu][irq]++;
 	spin_lock(&desc->lock);
 	desc->handler->ack(irq);
@@ -490,6 +496,27 @@
 
 	if (softirq_pending(cpu))
 		do_softirq();
+
+#if defined(CONFIG_PREEMPT)
+	while (--current->preempt_count == 0) {
+		db_assert(intr_off());
+		db_assert(!in_interrupt());
+
+		if (current->need_resched == 0) {
+			break;
+		}
+
+		current->preempt_count ++;
+		sti();
+		if (user_mode(regs)) {
+			schedule();
+		} else {
+			preempt_schedule();
+		}
+		cli();
+	}
+#endif
+
 	return 1;
 }
 
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/mips/mm/extable.c linux-2.4.25-leo/arch/mips/mm/extable.c
--- linux-2.4.25/arch/mips/mm/extable.c	2002-11-28 23:53:10.000000000 +0000
+++ linux-2.4.25-leo/arch/mips/mm/extable.c	2004-02-20 18:23:08.000000000 +0000
@@ -3,6 +3,7 @@
  */
 #include <linux/config.h>
 #include <linux/module.h>
+#include <linux/sched.h>
 #include <linux/spinlock.h>
 #include <asm/uaccess.h>
 
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/mips64/config.in linux-2.4.25-leo/arch/mips64/config.in
--- linux-2.4.25/arch/mips64/config.in	2002-11-28 23:53:10.000000000 +0000
+++ linux-2.4.25-leo/arch/mips64/config.in	2004-02-20 18:22:22.000000000 +0000
@@ -7,3 +7,11 @@
 define_bool CONFIG_MIPS64 y
 
 source arch/mips/config-shared.in
+
+mainmenu_option next_comment
+comment 'Grsecurity'
+bool 'Grsecurity' CONFIG_GRKERNSEC
+if [ "$CONFIG_GRKERNSEC" = "y" ]; then
+        source grsecurity/Config.in
+fi
+endmenu
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/mips64/kernel/ioctl32.c linux-2.4.25-leo/arch/mips64/kernel/ioctl32.c
--- linux-2.4.25/arch/mips64/kernel/ioctl32.c	2004-02-20 14:11:38.000000000 +0000
+++ linux-2.4.25-leo/arch/mips64/kernel/ioctl32.c	2004-02-20 18:21:57.000000000 +0000
@@ -62,6 +62,7 @@
 
 #include <linux/mtd/mtd.h>
 #include <linux/serial.h>
+#include <linux/dm-ioctl.h>
 
 #ifdef CONFIG_SIBYTE_TBPROF
 #include <asm/sibyte/trace_prof.h>
@@ -2327,6 +2328,22 @@
 	IOCTL32_DEFAULT(RESTART_ARRAY_RW),
 #endif /* CONFIG_MD */
 
+#if defined(CONFIG_BLK_DEV_DM) || defined(CONFIG_BLK_DEV_DM_MODULE)
+	IOCTL32_DEFAULT(DM_VERSION),
+	IOCTL32_DEFAULT(DM_REMOVE_ALL),
+	IOCTL32_DEFAULT(DM_DEV_CREATE),
+	IOCTL32_DEFAULT(DM_DEV_REMOVE),
+	IOCTL32_DEFAULT(DM_TABLE_LOAD),
+	IOCTL32_DEFAULT(DM_DEV_SUSPEND),
+	IOCTL32_DEFAULT(DM_DEV_RENAME),
+	IOCTL32_DEFAULT(DM_TABLE_DEPS),
+	IOCTL32_DEFAULT(DM_DEV_STATUS),
+	IOCTL32_DEFAULT(DM_TABLE_STATUS),
+	IOCTL32_DEFAULT(DM_DEV_WAIT),
+	IOCTL32_DEFAULT(DM_LIST_DEVICES),
+	IOCTL32_DEFAULT(DM_TABLE_CLEAR),
+#endif /* CONFIG_BLK_DEV_DM */
+
 #ifdef CONFIG_SIBYTE_TBPROF
 	IOCTL32_DEFAULT(SBPROF_ZBSTART),
 	IOCTL32_DEFAULT(SBPROF_ZBSTOP),
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/parisc/config.in linux-2.4.25-leo/arch/parisc/config.in
--- linux-2.4.25/arch/parisc/config.in	2004-02-20 14:11:38.000000000 +0000
+++ linux-2.4.25-leo/arch/parisc/config.in	2004-02-20 18:22:22.000000000 +0000
@@ -204,3 +204,11 @@
 
 source crypto/Config.in
 source lib/Config.in
+
+mainmenu_option next_comment
+comment 'Grsecurity'
+bool 'Grsecurity' CONFIG_GRKERNSEC
+if [ "$CONFIG_GRKERNSEC" = "y" ]; then
+	source grsecurity/Config.in
+fi
+endmenu
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/parisc/kernel/ioctl32.c linux-2.4.25-leo/arch/parisc/kernel/ioctl32.c
--- linux-2.4.25/arch/parisc/kernel/ioctl32.c	2003-08-25 12:44:40.000000000 +0100
+++ linux-2.4.25-leo/arch/parisc/kernel/ioctl32.c	2004-02-20 18:22:22.000000000 +0000
@@ -55,6 +55,7 @@
 #define max max */
 #include <linux/lvm.h>
 #endif /* LVM */
+#include <linux/dm-ioctl.h>
 
 #include <scsi/scsi.h>
 /* Ugly hack. */
@@ -1434,7 +1435,11 @@
 	 * To have permissions to do most of the vt ioctls, we either have
 	 * to be the owner of the tty, or super-user.
 	 */
+#ifdef CONFIG_GRKERNSEC
+	if (current->tty == tty || capable(CAP_SYS_TTY_CONFIG))
+#else
 	if (current->tty == tty || suser())
+#endif
 		return 1;
 	return 0;                                                    
 }
@@ -3423,6 +3428,22 @@
 COMPATIBLE_IOCTL(LV_BMAP)
 COMPATIBLE_IOCTL(LV_SNAPSHOT_USE_RATE)
 #endif /* LVM */
+/* Device-Mapper */
+#if defined(CONFIG_BLK_DEV_DM) || defined(CONFIG_BLK_DEV_DM_MODULE)
+COMPATIBLE_IOCTL(DM_VERSION)
+COMPATIBLE_IOCTL(DM_REMOVE_ALL)
+COMPATIBLE_IOCTL(DM_DEV_CREATE)
+COMPATIBLE_IOCTL(DM_DEV_REMOVE)
+COMPATIBLE_IOCTL(DM_TABLE_LOAD)
+COMPATIBLE_IOCTL(DM_DEV_SUSPEND)
+COMPATIBLE_IOCTL(DM_DEV_RENAME)
+COMPATIBLE_IOCTL(DM_TABLE_DEPS)
+COMPATIBLE_IOCTL(DM_DEV_STATUS)
+COMPATIBLE_IOCTL(DM_TABLE_STATUS)
+COMPATIBLE_IOCTL(DM_DEV_WAIT)
+COMPATIBLE_IOCTL(DM_LIST_DEVICES)
+COMPATIBLE_IOCTL(DM_TABLE_CLEAR)
+#endif /* CONFIG_BLK_DEV_DM */
 #if defined(CONFIG_DRM) || defined(CONFIG_DRM_MODULE)
 COMPATIBLE_IOCTL(DRM_IOCTL_GET_MAGIC)
 COMPATIBLE_IOCTL(DRM_IOCTL_IRQ_BUSID)
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/parisc/kernel/ptrace.c linux-2.4.25-leo/arch/parisc/kernel/ptrace.c
--- linux-2.4.25/arch/parisc/kernel/ptrace.c	2002-11-28 23:53:10.000000000 +0000
+++ linux-2.4.25-leo/arch/parisc/kernel/ptrace.c	2004-02-20 18:22:22.000000000 +0000
@@ -15,7 +15,7 @@
 #include <linux/ptrace.h>
 #include <linux/user.h>
 #include <linux/personality.h>
-
+#include <linux/grsecurity.h>
 #include <asm/uaccess.h>
 #include <asm/pgtable.h>
 #include <asm/system.h>
@@ -119,6 +119,9 @@
 	if (pid == 1)		/* no messing around with init! */
 		goto out_tsk;
 
+	if (gr_handle_ptrace(child, request))
+		goto out_tsk;
+
 	if (request == PTRACE_ATTACH) {
 		ret = ptrace_attach(child);
 		goto out_tsk;
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/parisc/kernel/sys_parisc32.c linux-2.4.25-leo/arch/parisc/kernel/sys_parisc32.c
--- linux-2.4.25/arch/parisc/kernel/sys_parisc32.c	2003-06-13 15:51:31.000000000 +0100
+++ linux-2.4.25-leo/arch/parisc/kernel/sys_parisc32.c	2004-02-20 18:22:22.000000000 +0000
@@ -50,6 +50,7 @@
 #include <linux/highmem.h>
 #include <linux/highuid.h>
 #include <linux/mman.h>
+#include <linux/grsecurity.h>
 
 #include <asm/types.h>
 #include <asm/uaccess.h>
@@ -177,6 +178,11 @@
 	struct file *file;
 	int retval;
 	int i;
+#ifdef CONFIG_GRKERNSEC
+	struct file *old_exec_file;
+	struct acl_subject_label *old_acl;
+	struct rlimit old_rlim[RLIM_NLIMITS];
+#endif
 
 	file = open_exec(filename);
 
@@ -184,7 +190,26 @@
 	if (IS_ERR(file))
 		return retval;
 
+	gr_learn_resource(current, RLIMIT_NPROC, atomic_read(&current->user->processes), 1);
+
+	if (gr_handle_nproc()) {
+		allow_write_access(file);
+		fput(file);
+		return -EAGAIN;
+	}
+
+	if (!gr_acl_handle_execve(file->f_dentry, file->f_vfsmnt)) {
+		allow_write_access(file);
+		fput(file);
+		return -EACCES;
+	}
+
 	bprm.p = PAGE_SIZE*MAX_ARG_PAGES-sizeof(void *);
+
+#ifdef CONFIG_GRKERNSEC_PAX_RANDUSTACK
+	bprm.p -= (get_random_long() & ~(sizeof(void *)-1)) & ~PAGE_MASK;
+#endif
+
 	memset(bprm.page, 0, MAX_ARG_PAGES*sizeof(bprm.page[0]));
 
 	DBG(("do_execve32(%s, %p, %p, %p)\n", filename, argv, envp, regs));
@@ -209,11 +234,24 @@
 	if (retval < 0)
 		goto out;
 	
+	if (!gr_tpe_allow(file)) {
+		retval = -EACCES;
+		goto out;
+	}
+
+	if (gr_check_crash_exec(file)) {
+		retval = -EACCES;
+		goto out;
+	}
+
 	retval = copy_strings_kernel(1, &bprm.filename, &bprm);
 	if (retval < 0)
 		goto out;
 
 	bprm.exec = bprm.p;
+
+	gr_log_chroot_exec(file->f_dentry, file->f_vfsmnt);
+
 	retval = copy_strings32(bprm.envc, envp, &bprm);
 	if (retval < 0)
 		goto out;
@@ -222,11 +260,32 @@
 	if (retval < 0)
 		goto out;
 
+#ifdef CONFIG_GRKERNSEC
+	old_acl = current->acl;
+	memcpy(old_rlim, current->rlim, sizeof(old_rlim));
+	old_exec_file = current->exec_file;
+	get_file(file);
+	current->exec_file = file;
+#endif
+
+	gr_set_proc_label(file->f_dentry, file->f_vfsmnt);
+
 	retval = search_binary_handler(&bprm,regs);
-	if (retval >= 0)
+	if (retval >= 0) {
+#ifdef CONFIG_GRKERNSEC
+		if (old_exec_file)
+			fput(old_exec_file);
+#endif
 		/* execve success */
 		return retval;
+	}
 
+#ifdef CONFIG_GRKERNSEC
+	current->acl = old_acl;
+	memcpy(current->rlim, old_rlim, sizeof(old_rlim));
+	fput(current->exec_file);
+	current->exec_file = old_exec_file;
+#endif
 out:
 	/* Something went wrong, return the inode and free the argument pages*/
 	allow_write_access(bprm.file);
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/parisc/kernel/sys_parisc.c linux-2.4.25-leo/arch/parisc/kernel/sys_parisc.c
--- linux-2.4.25/arch/parisc/kernel/sys_parisc.c	2002-11-28 23:53:10.000000000 +0000
+++ linux-2.4.25-leo/arch/parisc/kernel/sys_parisc.c	2004-02-20 18:22:22.000000000 +0000
@@ -12,6 +12,7 @@
 #include <linux/mman.h>
 #include <linux/shm.h>
 #include <linux/smp_lock.h>
+#include <linux/grsecurity.h>
 
 int sys_pipe(int *fildes)
 {
@@ -90,6 +91,11 @@
 		inode = filp->f_dentry->d_inode;
 	}
 
+#ifdef CONFIG_GRKERNSEC_PAX_RANDMMAP
+	if ((current->flags & PF_PAX_RANDMMAP) && (!addr || filp))
+		addr = TASK_UNMAPPED_BASE + current->mm->delta_mmap;
+#endif
+
 	if (inode && (flags & MAP_SHARED) && (inode->i_mapping->i_mmap_shared)) {
 		addr = get_shared_area(inode, addr, len, pgoff);
 	} else {
@@ -104,12 +110,23 @@
 {
 	struct file * file = NULL;
 	unsigned long error = -EBADF;
+
+#ifdef CONFIG_GRKERNSEC_PAX_RANDEXEC
+	if (flags & MAP_MIRROR)
+		return -EINVAL;
+#endif
+
 	if (!(flags & MAP_ANONYMOUS)) {
 		file = fget(fd);
 		if (!file)
 			goto out;
 	}
 
+	if (gr_handle_mmap(file, prot)) {
+		fput(file);
+		return -EACCES;
+	}
+
 	flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
 
 	down_write(&current->mm->mmap_sem);
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/parisc/kernel/traps.c linux-2.4.25-leo/arch/parisc/kernel/traps.c
--- linux-2.4.25/arch/parisc/kernel/traps.c	2003-08-25 12:44:40.000000000 +0100
+++ linux-2.4.25-leo/arch/parisc/kernel/traps.c	2004-02-20 18:22:22.000000000 +0000
@@ -637,9 +637,7 @@
 
 			down_read(&current->mm->mmap_sem);
 			vma = find_vma(current->mm,regs->iaoq[0]);
-			if (vma && (regs->iaoq[0] >= vma->vm_start)
-				&& (vma->vm_flags & VM_EXEC)) {
-
+			if (vma && (regs->iaoq[0] >= vma->vm_start)) {
 				fault_address = regs->iaoq[0];
 				fault_space = regs->iasq[0];
 
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/parisc/mm/fault.c linux-2.4.25-leo/arch/parisc/mm/fault.c
--- linux-2.4.25/arch/parisc/mm/fault.c	2003-06-13 15:51:31.000000000 +0100
+++ linux-2.4.25-leo/arch/parisc/mm/fault.c	2004-02-20 18:22:22.000000000 +0000
@@ -15,6 +15,7 @@
 #include <linux/ptrace.h>
 #include <linux/sched.h>
 #include <linux/interrupt.h>
+#include <linux/unistd.h>
 
 #include <asm/uaccess.h>
 #include <asm/traps.h>
@@ -53,7 +54,7 @@
 static unsigned long
 parisc_acctyp(unsigned long code, unsigned int inst)
 {
-	if (code == 6 || code == 16)
+	if (code == 6 || code == 7 || code == 16)
 	    return VM_EXEC;
 
 	switch (inst & 0xf0000000) {
@@ -139,6 +140,136 @@
 			}
 #endif
 
+/*
+ * PaX: decide what to do with offenders (instruction_pointer(regs) = fault address)
+ *
+ * returns 1 when task should be killed 
+ *	   2 when rt_sigreturn trampoline was detected
+ *	   3 when unpatched PLT trampoline was detected
+ *	   4 when legitimate ET_EXEC was detected
+ */
+#ifdef CONFIG_GRKERNSEC_PAX_PAGEEXEC  
+static int pax_handle_fetch_fault(struct pt_regs *regs)
+{
+	int err;
+
+#ifdef CONFIG_GRKERNSEC_PAX_RANDEXEC
+	if (current->flags & PF_PAX_RANDEXEC) {
+		if (instruction_pointer(regs) >= current->mm->start_code &&
+		    instruction_pointer(regs) < current->mm->end_code)
+		{
+#if 0
+			/* PaX: this needs fixing */
+			if ((regs->gr[2] & ~3UL) == instruction_pointer(regs))
+				return 1;
+#endif
+			regs->iaoq[0] += current->mm->delta_exec;
+			if ((regs->iaoq[1] & ~3UL) >= current->mm->start_code &&
+			    (regs->iaoq[1] & ~3UL) < current->mm->end_code)
+				regs->iaoq[1] += current->mm->delta_exec;
+			return 4;
+		}
+	}
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_EMUPLT
+	do { /* PaX: unpatched PLT emulation */
+		unsigned int bl, depwi;
+
+		err = get_user(bl, (unsigned int*)instruction_pointer(regs));
+		err |= get_user(depwi, (unsigned int*)(instruction_pointer(regs)+4));
+
+		if (err)
+			break;
+
+		if (bl == 0xEA9F1FDDU && depwi == 0xD6801C1EU) {
+			unsigned int ldw, bv, ldw2, addr = instruction_pointer(regs)-12;
+
+			err = get_user(ldw, (unsigned int*)addr);
+			err |= get_user(bv, (unsigned int*)(addr+4));
+			err |= get_user(ldw2, (unsigned int*)(addr+8));
+
+			if (err)
+				break;
+
+			if (ldw == 0x0E801096U &&
+			    bv == 0xEAC0C000U &&
+			    ldw2 == 0x0E881095U)
+			{
+				unsigned int resolver, map;
+
+				err = get_user(resolver, (unsigned int*)(instruction_pointer(regs)+8));
+				err |= get_user(map, (unsigned int*)(instruction_pointer(regs)+12));
+				if (err)
+					break;
+
+				regs->gr[20] = instruction_pointer(regs)+8;
+				regs->gr[21] = map;
+				regs->gr[22] = resolver;
+				regs->iaoq[0] = resolver | 3UL;
+				regs->iaoq[1] = regs->iaoq[0] + 4;
+				return 3;
+			}
+		}
+	} while (0);
+#endif
+ 
+#ifdef CONFIG_GRKERNSEC_PAX_EMUTRAMP
+
+#ifndef CONFIG_GRKERNSEC_PAX_EMUSIGRT
+	if (!(current->flags & PF_PAX_EMUTRAMP))
+		return 1;
+#endif
+
+	do { /* PaX: rt_sigreturn emulation */
+		unsigned int ldi1, ldi2, bel, nop;
+
+		err = get_user(ldi1, (unsigned int *)instruction_pointer(regs));
+		err |= get_user(ldi2, (unsigned int *)(instruction_pointer(regs)+4));
+		err |= get_user(bel, (unsigned int *)(instruction_pointer(regs)+8));
+		err |= get_user(nop, (unsigned int *)(instruction_pointer(regs)+12));
+
+		if (err)
+			break;
+
+                if ((ldi1 == 0x34190000U || ldi1 == 0x34190002U) &&
+		    ldi2 == 0x3414015AU &&
+		    bel == 0xE4008200U &&
+		    nop == 0x08000240U)
+		{
+			regs->gr[25] = (ldi1 & 2) >> 1;
+			regs->gr[20] = __NR_rt_sigreturn;
+			regs->gr[31] = regs->iaoq[1] + 16;
+			regs->sr[0] = regs->iasq[1];
+			regs->iaoq[0] = 0x100UL;
+			regs->iaoq[1] = regs->iaoq[0] + 4;
+			regs->iasq[0] = regs->sr[2];
+			regs->iasq[1] = regs->sr[2];
+			return 2;
+		}
+	} while (0);
+#endif
+
+	return 1;
+}
+
+void pax_report_insns(void *pc)
+{
+	unsigned long i;
+
+	printk(KERN_ERR "PAX: bytes at PC: ");
+	for (i = 0; i < 5; i++) {
+		unsigned int c;
+		if (get_user(c, (unsigned int*)pc+i)) {
+			printk("<invalid address>.");
+			break;
+		}
+		printk("%08x ", c);
+	}
+	printk("\n");
+}
+#endif
+
 void do_page_fault(struct pt_regs *regs, unsigned long code,
 			      unsigned long address)
 {
@@ -164,8 +295,38 @@
 
 	acc_type = parisc_acctyp(code,regs->iir);
 
-	if ((vma->vm_flags & acc_type) != acc_type)
+	if ((vma->vm_flags & acc_type) != acc_type) {
+
+#ifdef CONFIG_GRKERNSEC_PAX_PAGEEXEC
+		if ((current->flags & PF_PAX_PAGEEXEC) && (acc_type & VM_EXEC) &&
+		    (address & ~3UL) == instruction_pointer(regs))
+		   {
+			up_read(&mm->mmap_sem);
+			switch(pax_handle_fetch_fault(regs)) {
+
+#ifdef CONFIG_GRKERNSEC_PAX_RANDEXEC
+			case 4:
+				return;
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_EMUPLT
+			case 3:
+				return;
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_EMUTRAMP
+			case 2:
+				return;
+#endif
+
+			}
+			pax_report_fault(regs, (void*)instruction_pointer(regs), (void*)regs->gr[30]);
+			do_exit(SIGKILL);
+		}
+#endif
+
 		goto bad_area;
+	}
 
 	/*
 	 * If for any reason at all we couldn't handle the fault, make
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/ppc/config.in linux-2.4.25-leo/arch/ppc/config.in
--- linux-2.4.25/arch/ppc/config.in	2004-02-20 14:11:39.000000000 +0000
+++ linux-2.4.25-leo/arch/ppc/config.in	2004-02-20 18:23:08.000000000 +0000
@@ -151,6 +151,8 @@
   int  'Maximum number of CPUs (2-32)' CONFIG_NR_CPUS 32
 fi
 
+bool 'Preemptible kernel support' CONFIG_PREEMPT
+
 if [ "$CONFIG_6xx" = "y" -a "$CONFIG_8260" = "n" ];then
   bool 'AltiVec Support' CONFIG_ALTIVEC
   bool 'Thermal Management Support' CONFIG_TAU
@@ -634,3 +636,12 @@
 int 'Kernel messages buffer length shift (0 = default)' CONFIG_LOG_BUF_SHIFT 0
 
 endmenu
+
+mainmenu_option next_comment
+comment 'Grsecurity'
+bool 'Grsecurity' CONFIG_GRKERNSEC
+if [ "$CONFIG_GRKERNSEC" = "y" ]; then
+    source grsecurity/Config.in
+fi
+endmenu
+
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/ppc/kernel/entry.S linux-2.4.25-leo/arch/ppc/kernel/entry.S
--- linux-2.4.25/arch/ppc/kernel/entry.S	2003-11-28 18:26:19.000000000 +0000
+++ linux-2.4.25-leo/arch/ppc/kernel/entry.S	2004-02-20 18:23:08.000000000 +0000
@@ -283,6 +283,46 @@
 	 */
 	cmpi	0,r3,0
 	beq	restore
+#ifdef CONFIG_PREEMPT
+	lwz	r3,PREEMPT_COUNT(r2)
+	cmpi	0,r3,1
+	bge	ret_from_except
+	lwz	r5,_MSR(r1)
+	andi.	r5,r5,MSR_PR
+	bne	do_signal_ret
+	lwz	r5,NEED_RESCHED(r2)
+	cmpi	0,r5,0
+	beq	ret_from_except
+	lis	r3,irq_stat@h
+	ori	r3,r3,irq_stat@l
+#ifdef CONFIG_SMP
+	lwz     r5,CPU(r2)
+	rlwinm  r5,r5,5,0,26
+	add     r3,r3,r5
+#endif
+	lwz	r5,4(r3)
+	lwz	r3,8(r3)
+	add	r3,r3,r5
+	cmpi	0,r3,0
+	bne	ret_from_except
+	lwz	r3,PREEMPT_COUNT(r2)
+	addi	r3,r3,1
+	stw	r3,PREEMPT_COUNT(r2)
+	mfmsr	r0
+	ori	r0,r0,MSR_EE
+	mtmsr	r0
+	sync
+	bl	preempt_schedule
+	mfmsr	r0
+	rlwinm	r0,r0,0,17,15
+	mtmsr	r0
+	sync
+	lwz	r3,PREEMPT_COUNT(r2)
+	subi	r3,r3,1
+	stw	r3,PREEMPT_COUNT(r2)
+	li	r3,1
+	b	ret_from_intercept
+#endif /* CONFIG_PREEMPT */
 	.globl	ret_from_except
 ret_from_except:
 	lwz	r3,_MSR(r1)	/* Returning to user mode? */
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/ppc/kernel/irq.c linux-2.4.25-leo/arch/ppc/kernel/irq.c
--- linux-2.4.25/arch/ppc/kernel/irq.c	2003-11-28 18:26:19.000000000 +0000
+++ linux-2.4.25-leo/arch/ppc/kernel/irq.c	2004-02-20 18:23:08.000000000 +0000
@@ -551,6 +551,34 @@
 	return 1; /* lets ret_from_int know we can do checks */
 }
 
+#ifdef CONFIG_PREEMPT
+int
+preempt_intercept(struct pt_regs *regs)
+{
+	int ret;
+
+	preempt_disable();
+
+	switch(regs->trap) {
+	case 0x500:
+		ret = do_IRQ(regs);
+		break;
+#ifndef CONFIG_4xx
+	case 0x900:
+#else
+	case 0x1000:
+#endif
+		ret = timer_interrupt(regs);
+		break;
+	default:
+		BUG();
+	}
+
+	preempt_enable();
+	return ret;
+}
+#endif /* CONFIG_PREEMPT */
+
 unsigned long probe_irq_on (void)
 {
 	return 0;
@@ -647,11 +675,13 @@
 				show("wait_on_irq");
 				count = ~0;
 			}
+			preempt_disable();
 			__sti();
 			/* don't worry about the lock race Linus found
 			 * on intel here. -- Cort
 			 */
 			__cli();
+			preempt_enable_no_resched();
 			if (atomic_read(&global_irq_count))
 				continue;
 			if (global_irq_lock)
@@ -727,6 +757,8 @@
 	global_irq_holder = cpu;
 }
 
+#define	EFLAGS_IF_SHIFT	15
+
 /*
  * A global "cli()" while in an interrupt context
  * turns into just a local cli(). Interrupts
@@ -744,9 +776,10 @@
 	unsigned long flags;
 
 	__save_flags(flags);
-	if (flags & (1 << 15)) {
-		int cpu = smp_processor_id();
+	if (flags & (1 << EFLAGS_IF_SHIFT)) {
+		int cpu;
 		__cli();
+		cpu = smp_processor_id();
 		if (!local_irq_count(cpu))
 			get_irqlock(cpu);
 	}
@@ -754,11 +787,14 @@
 
 void __global_sti(void)
 {
-	int cpu = smp_processor_id();
+	int cpu;
 
+	preempt_disable();
+	cpu = smp_processor_id();
 	if (!local_irq_count(cpu))
 		release_irqlock(cpu);
 	__sti();
+	preempt_enable();
 }
 
 /*
@@ -773,19 +809,23 @@
 	int retval;
 	int local_enabled;
 	unsigned long flags;
+	int cpu;
 
 	__save_flags(flags);
-	local_enabled = (flags >> 15) & 1;
+	local_enabled = (flags >> EFLAGS_IF_SHIFT) & 1;
 	/* default to local */
 	retval = 2 + local_enabled;
 
 	/* check for global flags if we're not in an interrupt */
-	if (!local_irq_count(smp_processor_id())) {
+	preempt_disable();
+	cpu = smp_processor_id();
+	if (!local_irq_count(cpu)) {
 		if (local_enabled)
 			retval = 1;
-		if (global_irq_holder == (unsigned char) smp_processor_id())
+		if (global_irq_holder == cpu)
 			retval = 0;
 	}
+	preempt_enable();
 	return retval;
 }
 
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/ppc/kernel/mk_defs.c linux-2.4.25-leo/arch/ppc/kernel/mk_defs.c
--- linux-2.4.25/arch/ppc/kernel/mk_defs.c	2003-11-28 18:26:19.000000000 +0000
+++ linux-2.4.25-leo/arch/ppc/kernel/mk_defs.c	2004-02-20 18:23:08.000000000 +0000
@@ -39,6 +39,9 @@
 	DEFINE(SIGPENDING, offsetof(struct task_struct, sigpending));
 	DEFINE(THREAD, offsetof(struct task_struct, thread));
 	DEFINE(MM, offsetof(struct task_struct, mm));
+#ifdef CONFIG_PREEMPT
+	DEFINE(PREEMPT_COUNT, offsetof(struct task_struct, preempt_count));
+#endif
 	DEFINE(ACTIVE_MM, offsetof(struct task_struct, active_mm));
 	DEFINE(TASK_STRUCT_SIZE, sizeof(struct task_struct));
 	DEFINE(KSP, offsetof(struct thread_struct, ksp));
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/ppc/kernel/open_pic.c linux-2.4.25-leo/arch/ppc/kernel/open_pic.c
--- linux-2.4.25/arch/ppc/kernel/open_pic.c	2004-02-20 14:11:39.000000000 +0000
+++ linux-2.4.25-leo/arch/ppc/kernel/open_pic.c	2004-02-20 18:23:08.000000000 +0000
@@ -601,19 +601,24 @@
 void __init do_openpic_setup_cpu(void)
 {
  	int i;
-	u32 msk = 1 << smp_hw_index[smp_processor_id()];
+#ifdef CONFIG_IRQ_ALL_CPUS
+	u32 msk;
+#endif /* CONFIG_IRQ_ALL_CPUS */
 
 	spin_lock(&openpic_setup_lock);
 
 #ifdef CONFIG_IRQ_ALL_CPUS
+	msk = 1 << smp_hw_index[smp_processor_id()];
+
  	/* let the openpic know we want intrs. default affinity
  	 * is 0xffffffff until changed via /proc
  	 * That's how it's done on x86. If we want it differently, then
  	 * we should make sure we also change the default values of irq_affinity
  	 * in irq.c.
  	 */
- 	for (i = 0; i < NumSources; i++)
+ 	for (i = 0; i < NumSources; i++) {
 		openpic_mapirq(i, msk, ~0U);
+	}
 #endif /* CONFIG_IRQ_ALL_CPUS */
  	openpic_set_priority(0);
 
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/ppc/kernel/ptrace.c linux-2.4.25-leo/arch/ppc/kernel/ptrace.c
--- linux-2.4.25/arch/ppc/kernel/ptrace.c	2003-08-25 12:44:40.000000000 +0100
+++ linux-2.4.25-leo/arch/ppc/kernel/ptrace.c	2004-02-20 18:22:22.000000000 +0000
@@ -24,6 +24,7 @@
 #include <linux/errno.h>
 #include <linux/ptrace.h>
 #include <linux/user.h>
+#include <linux/grsecurity.h>
 
 #include <asm/uaccess.h>
 #include <asm/page.h>
@@ -195,6 +196,9 @@
 	if (pid == 1)		/* you may not mess with init */
 		goto out_tsk;
 
+	if (gr_handle_ptrace(child, request))
+		goto out_tsk;
+
 	if (request == PTRACE_ATTACH) {
 		ret = ptrace_attach(child);
 		goto out_tsk;
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/ppc/kernel/setup.c linux-2.4.25-leo/arch/ppc/kernel/setup.c
--- linux-2.4.25/arch/ppc/kernel/setup.c	2004-02-20 14:11:39.000000000 +0000
+++ linux-2.4.25-leo/arch/ppc/kernel/setup.c	2004-02-20 18:23:08.000000000 +0000
@@ -502,6 +502,20 @@
 	strcpy(cmd_line, CONFIG_CMDLINE);
 #endif /* CONFIG_CMDLINE */
 
+#ifdef CONFIG_PREEMPT
+	/* Override the irq routines for external & timer interrupts here,
+	 * as the MMU has only been minimally setup at this point and
+	 * there are no protections on page zero.
+	 */
+	{
+		extern int preempt_intercept(struct pt_regs *);
+	
+		do_IRQ_intercept = (unsigned long) &preempt_intercept;
+		timer_interrupt_intercept = (unsigned long) &preempt_intercept;
+
+	}
+#endif /* CONFIG_PREEMPT */
+
 	platform_init(r3, r4, r5, r6, r7);
 
 	if (ppc_md.progress)
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/ppc/kernel/syscalls.c linux-2.4.25-leo/arch/ppc/kernel/syscalls.c
--- linux-2.4.25/arch/ppc/kernel/syscalls.c	2003-11-28 18:26:19.000000000 +0000
+++ linux-2.4.25-leo/arch/ppc/kernel/syscalls.c	2004-02-20 18:22:22.000000000 +0000
@@ -35,6 +35,7 @@
 #include <linux/ipc.h>
 #include <linux/utsname.h>
 #include <linux/file.h>
+#include <linux/grsecurity.h>
 
 #include <asm/uaccess.h>
 #include <asm/ipc.h>
@@ -191,18 +192,29 @@
 	struct file * file = NULL;
 	int ret = -EBADF;
 
+#ifdef CONFIG_GRKERNSEC_PAX_RANDEXEC
+	if (flags & MAP_MIRROR)
+		return -EINVAL;
+#endif
+
 	flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
 	if (!(flags & MAP_ANONYMOUS)) {
 		if (!(file = fget(fd)))
 			goto out;
 	}
 
+	if (gr_handle_mmap(file, prot)) {
+		fput(file);
+		ret = -EACCES;
+		goto out;
+	}
+
 	ret = -EINVAL;
 	if ((! allow_mmap_address(addr)) && (flags & MAP_FIXED))
 		goto out;
 	
 	down_write(&current->mm->mmap_sem);
-	ret = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
+	ret = do_mmap(file, addr, len, prot, flags, pgoff << PAGE_SHIFT);
 	up_write(&current->mm->mmap_sem);
 	if (file)
 		fput(file);
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/ppc/kernel/temp.c linux-2.4.25-leo/arch/ppc/kernel/temp.c
--- linux-2.4.25/arch/ppc/kernel/temp.c	2003-08-25 12:44:40.000000000 +0100
+++ linux-2.4.25-leo/arch/ppc/kernel/temp.c	2004-02-20 18:23:08.000000000 +0000
@@ -138,7 +138,7 @@
 
 static void tau_timeout(void * info)
 {
-	unsigned long cpu = smp_processor_id();
+	unsigned long cpu;
 	unsigned long flags;
 	int size;
 	int shrink;
@@ -146,6 +146,8 @@
 	/* disabling interrupts *should* be okay */
 	save_flags(flags); cli();
 
+	cpu = smp_processor_id();
+
 #ifndef CONFIG_TAU_INT
 	TAUupdate(cpu);
 #endif
@@ -191,13 +193,15 @@
 
 static void tau_timeout_smp(unsigned long unused)
 {
-
 	/* schedule ourselves to be run again */
 	mod_timer(&tau_timer, jiffies + shrink_timer) ;
+
+	preempt_disable();
 #ifdef CONFIG_SMP
 	smp_call_function(tau_timeout, NULL, 1, 0);
 #endif
 	tau_timeout(NULL);
+	preempt_enable();
 }
 
 /*
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/ppc/lib/dec_and_lock.c linux-2.4.25-leo/arch/ppc/lib/dec_and_lock.c
--- linux-2.4.25/arch/ppc/lib/dec_and_lock.c	2001-11-16 18:10:08.000000000 +0000
+++ linux-2.4.25-leo/arch/ppc/lib/dec_and_lock.c	2004-02-20 18:23:08.000000000 +0000
@@ -1,4 +1,5 @@
 #include <linux/module.h>
+#include <linux/sched.h>
 #include <linux/spinlock.h>
 #include <asm/atomic.h>
 #include <asm/system.h>
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/ppc/mm/fault.c linux-2.4.25-leo/arch/ppc/mm/fault.c
--- linux-2.4.25/arch/ppc/mm/fault.c	2003-11-28 18:26:19.000000000 +0000
+++ linux-2.4.25-leo/arch/ppc/mm/fault.c	2004-02-20 18:22:22.000000000 +0000
@@ -26,6 +26,9 @@
 #include <linux/mman.h>
 #include <linux/mm.h>
 #include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/pagemap.h>
+#include <linux/compiler.h>
 
 #include <asm/page.h>
 #include <asm/pgtable.h>
@@ -52,6 +55,360 @@
 void bad_page_fault(struct pt_regs *, unsigned long, int sig);
 void do_page_fault(struct pt_regs *, unsigned long, unsigned long);
 
+#ifdef CONFIG_GRKERNSEC_PAX_EMUSIGRT
+void pax_syscall_close(struct vm_area_struct * vma)
+{
+	vma->vm_mm->call_syscall = 0UL;
+}
+
+static struct page* pax_syscall_nopage(struct vm_area_struct *vma, unsigned long address, int write_access)
+{
+	struct page* page;
+	unsigned int *kaddr;
+
+	page = alloc_page(GFP_HIGHUSER);
+	if (!page)
+		return page;
+
+	kaddr = kmap(page);
+	memset(kaddr, 0, PAGE_SIZE);
+	kaddr[0] = 0x44000002U; /* sc */
+	__flush_dcache_icache(kaddr);
+	kunmap(page);
+	return page;
+}
+
+static struct vm_operations_struct pax_vm_ops = {
+	close:		pax_syscall_close,
+	nopage:		pax_syscall_nopage,
+};
+
+static void pax_insert_vma(struct vm_area_struct *vma, unsigned long addr)
+{
+	vma->vm_mm = current->mm;
+	vma->vm_start = addr;
+	vma->vm_end = addr + PAGE_SIZE;
+	vma->vm_flags = VM_READ | VM_EXEC | VM_MAYREAD | VM_MAYEXEC;
+	vma->vm_page_prot = protection_map[vma->vm_flags & 0x0f];
+	vma->vm_ops = &pax_vm_ops;
+	vma->vm_pgoff = 0UL;
+	vma->vm_file = NULL;
+	vma->vm_private_data = NULL;
+	insert_vm_struct(current->mm, vma);
+	++current->mm->total_vm;
+}
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_PAGEEXEC
+/*
+ * PaX: decide what to do with offenders (regs->nip = fault address)
+ *
+ * returns 1 when task should be killed
+ *         2 when patched GOT trampoline was detected
+ *         3 when patched PLT trampoline was detected
+ *         4 when unpatched PLT trampoline was detected
+ *         5 when legitimate ET_EXEC was detected
+ *         6 when sigreturn trampoline was detected
+ *         7 when rt_sigreturn trampoline was detected
+ */
+static int pax_handle_fetch_fault(struct pt_regs *regs)
+{
+	int err;
+
+#ifdef CONFIG_GRKERNSEC_PAX_RANDEXEC
+	if (current->flags & PF_PAX_RANDEXEC) {
+		if (regs->nip >= current->mm->start_code &&
+		    regs->nip < current->mm->end_code)
+		{
+			if (regs->link == regs->nip)
+				return 1;
+
+			regs->nip += current->mm->delta_exec;
+			return 5;
+		}
+	}
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_EMUPLT
+	do { /* PaX: patched GOT emulation */
+		unsigned int blrl;
+
+		err = get_user(blrl, (unsigned int*)regs->nip);
+
+		if (!err && blrl == 0x4E800021U) {
+			unsigned long temp = regs->nip;
+
+			regs->nip = regs->link & 0xFFFFFFFCUL;
+			regs->link = temp + 4UL;
+			return 2;
+		}
+	} while (0);
+
+	do { /* PaX: patched PLT emulation #1 */
+		unsigned int b;
+
+		err = get_user(b, (unsigned int *)regs->nip);
+
+		if (!err && (b & 0xFC000003U) == 0x48000000U) {
+			regs->nip += (((b | 0xFC000000UL) ^ 0x02000000UL) + 0x02000000UL);
+			return 3;
+		}
+	} while (0);
+
+	do { /* PaX: unpatched PLT emulation #1 */
+		unsigned int li, b;
+
+		err = get_user(li, (unsigned int *)regs->nip);
+		err |= get_user(b, (unsigned int *)(regs->nip+4));
+
+		if (!err && (li & 0xFFFF0000U) == 0x39600000U && (b & 0xFC000003U) == 0x48000000U) {
+			unsigned int rlwinm, add, li2, addis2, mtctr, li3, addis3, bctr;
+                        unsigned long addr = b | 0xFC000000UL;
+
+                        addr = regs->nip + 4 + ((addr ^ 0x02000000UL) + 0x02000000UL);
+			err = get_user(rlwinm, (unsigned int*)addr);
+			err |= get_user(add, (unsigned int*)(addr+4));
+			err |= get_user(li2, (unsigned int*)(addr+8));
+			err |= get_user(addis2, (unsigned int*)(addr+12));
+			err |= get_user(mtctr, (unsigned int*)(addr+16));
+			err |= get_user(li3, (unsigned int*)(addr+20));
+			err |= get_user(addis3, (unsigned int*)(addr+24));
+			err |= get_user(bctr, (unsigned int*)(addr+28));
+
+			if (err)
+				break;
+
+			if (rlwinm == 0x556C083CU &&
+			    add == 0x7D6C5A14U &&
+			    (li2 & 0xFFFF0000U) == 0x39800000U &&
+			    (addis2 & 0xFFFF0000U) == 0x3D8C0000U &&
+			    mtctr == 0x7D8903A6U &&
+			    (li3 & 0xFFFF0000U) == 0x39800000U &&
+			    (addis3 & 0xFFFF0000U) == 0x3D8C0000U &&
+			    bctr == 0x4E800420U)
+			{
+				regs->gpr[PT_R11] = 3 * (((li | 0xFFFF0000UL) ^ 0x00008000UL) + 0x00008000UL);
+				regs->gpr[PT_R12] = (((li3 | 0xFFFF0000UL) ^ 0x00008000UL) + 0x00008000UL);
+				regs->gpr[PT_R12] += (addis3 & 0xFFFFU) << 16;
+				regs->ctr = (((li2 | 0xFFFF0000UL) ^ 0x00008000UL) + 0x00008000UL);
+				regs->ctr += (addis2 & 0xFFFFU) << 16;
+				regs->nip = regs->ctr;
+				return 4;
+			}
+		}
+	} while (0);
+
+#if 0
+	do { /* PaX: unpatched PLT emulation #2 */
+		unsigned int lis, lwzu, b, bctr;
+
+		err = get_user(lis, (unsigned int *)regs->nip);
+		err |= get_user(lwzu, (unsigned int *)(regs->nip+4));
+		err |= get_user(b, (unsigned int *)(regs->nip+8));
+		err |= get_user(bctr, (unsigned int *)(regs->nip+12));
+
+		if (err)
+			break;
+
+		if ((lis & 0xFFFF0000U) == 0x39600000U &&
+		    (lwzu & 0xU) == 0xU &&
+		    (b & 0xFC000003U) == 0x48000000U &&
+		    bctr == 0x4E800420U)
+		{
+			unsigned int addis, addi, rlwinm, add, li2, addis2, mtctr, li3, addis3, bctr;
+                        unsigned long addr = b | 0xFC000000UL;
+
+                        addr = regs->nip + 12 + ((addr ^ 0x02000000UL) + 0x02000000UL);
+			err = get_user(addis, (unsigned int*)addr);
+			err |= get_user(addi, (unsigned int*)(addr+4));
+			err |= get_user(rlwinm, (unsigned int*)(addr+8));
+			err |= get_user(add, (unsigned int*)(addr+12));
+			err |= get_user(li2, (unsigned int*)(addr+16));
+			err |= get_user(addis2, (unsigned int*)(addr+20));
+			err |= get_user(mtctr, (unsigned int*)(addr+24));
+			err |= get_user(li3, (unsigned int*)(addr+28));
+			err |= get_user(addis3, (unsigned int*)(addr+32));
+			err |= get_user(bctr, (unsigned int*)(addr+36));
+
+			if (err)
+				break;
+
+			if ((addis & 0xFFFF0000U) == 0x3D6B0000U &&
+			    (addi & 0xFFFF0000U) == 0x396B0000U &&
+			    rlwinm == 0x556C083CU &&
+			    add == 0x7D6C5A14U &&
+			    (li2 & 0xFFFF0000U) == 0x39800000U &&
+			    (addis2 & 0xFFFF0000U) == 0x3D8C0000U &&
+			    mtctr == 0x7D8903A6U &&
+			    (li3 & 0xFFFF0000U) == 0x39800000U &&
+			    (addis3 & 0xFFFF0000U) == 0x3D8C0000U &&
+			    bctr == 0x4E800420U)
+			{
+				regs->gpr[PT_R11] = 
+				regs->gpr[PT_R11] = 3 * (((li | 0xFFFF0000UL) ^ 0x00008000UL) + 0x00008000UL);
+				regs->gpr[PT_R12] = (((li3 | 0xFFFF0000UL) ^ 0x00008000UL) + 0x00008000UL);
+				regs->gpr[PT_R12] += (addis3 & 0xFFFFU) << 16;
+				regs->ctr = (((li2 | 0xFFFF0000UL) ^ 0x00008000UL) + 0x00008000UL);
+				regs->ctr += (addis2 & 0xFFFFU) << 16;
+				regs->nip = regs->ctr;
+				return 4;
+			}
+		}
+	} while (0);
+#endif
+
+	do { /* PaX: unpatched PLT emulation #3 */
+		unsigned int li, b;
+
+		err = get_user(li, (unsigned int *)regs->nip);
+		err |= get_user(b, (unsigned int *)(regs->nip+4));
+
+		if (!err && (li & 0xFFFF0000U) == 0x39600000U && (b & 0xFC000003U) == 0x48000000U) {
+			unsigned int addis, lwz, mtctr, bctr;
+			unsigned long addr = b | 0xFC000000UL;
+
+			addr = regs->nip + 4 + ((addr ^ 0x02000000UL) + 0x02000000UL);
+			err = get_user(addis, (unsigned int*)addr);
+			err |= get_user(lwz, (unsigned int*)(addr+4));
+			err |= get_user(mtctr, (unsigned int*)(addr+8));
+			err |= get_user(bctr, (unsigned int*)(addr+12));
+
+			if (err)
+				break;
+
+			if ((addis & 0xFFFF0000U) == 0x3D6B0000U &&
+			    (lwz & 0xFFFF0000U) == 0x816B0000U &&
+			    mtctr == 0x7D6903A6U &&
+			    bctr == 0x4E800420U)
+			{
+				unsigned int r11;
+
+				addr = (addis << 16) + (((li | 0xFFFF0000UL) ^ 0x00008000UL) + 0x00008000UL);
+				addr += (((lwz | 0xFFFF0000UL) ^ 0x00008000UL) + 0x00008000UL);
+
+				err = get_user(r11, (unsigned int*)addr);
+				if (err)
+					break;
+
+				regs->gpr[PT_R11] = r11;
+				regs->ctr = r11;
+				regs->nip = r11;
+				return 4;
+			}
+		}
+	} while (0);
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_EMUSIGRT
+	do { /* PaX: sigreturn emulation */
+		unsigned int li, sc;
+
+		err = get_user(li, (unsigned int *)regs->nip);
+		err |= get_user(sc, (unsigned int *)(regs->nip+4));
+
+		if (!err && li == 0x38007777U && sc == 0x44000002U) {
+			struct vm_area_struct *vma;
+			unsigned long call_syscall;
+
+			down_read(&current->mm->mmap_sem);
+			call_syscall = current->mm->call_syscall;
+			up_read(&current->mm->mmap_sem);
+			if (likely(call_syscall))
+				goto emulate;
+
+			vma = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
+
+			down_write(&current->mm->mmap_sem);
+			if (current->mm->call_syscall) {
+				call_syscall = current->mm->call_syscall;
+				up_write(&current->mm->mmap_sem);
+				if (vma) kmem_cache_free(vm_area_cachep, vma);
+				goto emulate;
+			}
+
+			call_syscall = get_unmapped_area(NULL, 0UL, PAGE_SIZE, 0UL, MAP_PRIVATE);
+			if (!vma || (call_syscall & ~PAGE_MASK)) {
+				up_write(&current->mm->mmap_sem);
+				if (vma) kmem_cache_free(vm_area_cachep, vma);
+				return 1;
+			}
+
+			pax_insert_vma(vma, call_syscall);
+			current->mm->call_syscall = call_syscall;
+			up_write(&current->mm->mmap_sem);
+
+emulate:
+			regs->gpr[PT_R0] = 0x7777UL;
+			regs->nip = call_syscall;
+			return 6;
+		}
+	} while (0);
+
+	do { /* PaX: rt_sigreturn emulation */
+		unsigned int li, sc;
+
+		err = get_user(li, (unsigned int *)regs->nip);
+		err |= get_user(sc, (unsigned int *)(regs->nip+4));
+
+		if (!err && li == 0x38006666U && sc == 0x44000002U) {
+			struct vm_area_struct *vma;
+			unsigned int call_syscall;
+
+			down_read(&current->mm->mmap_sem);
+			call_syscall = current->mm->call_syscall;
+			up_read(&current->mm->mmap_sem);
+			if (likely(call_syscall))
+				goto rt_emulate;
+
+			vma = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
+
+			down_write(&current->mm->mmap_sem);
+			if (current->mm->call_syscall) {
+				call_syscall = current->mm->call_syscall;
+				up_write(&current->mm->mmap_sem);
+				if (vma) kmem_cache_free(vm_area_cachep, vma);
+				goto rt_emulate;
+			}
+
+			call_syscall = get_unmapped_area(NULL, 0UL, PAGE_SIZE, 0UL, MAP_PRIVATE);
+			if (!vma || (call_syscall & ~PAGE_MASK)) {
+				up_write(&current->mm->mmap_sem);
+				if (vma) kmem_cache_free(vm_area_cachep, vma);
+				return 1;
+			}
+
+			pax_insert_vma(vma, call_syscall);
+			current->mm->call_syscall = call_syscall;
+			up_write(&current->mm->mmap_sem);
+
+rt_emulate:
+			regs->gpr[PT_R0] = 0x6666UL;
+			regs->nip = call_syscall;
+			return 7;
+		}
+	} while (0);
+#endif
+
+        return 1;
+}
+
+void pax_report_insns(void *pc)
+{
+	unsigned long i;
+
+	printk(KERN_ERR "PAX: bytes at PC: ");
+	for (i = 0; i < 5; i++) {
+		unsigned int c;
+		if (get_user(c, (unsigned int*)pc+i)) {
+			printk("<invalid address>.");
+			break;
+		}
+		printk("%08x ", c);
+	}
+	printk("\n");
+}
+#endif
+
 /*
  * Check whether the instruction at regs->nip is a store using
  * an update addressing form which will update r1.
@@ -112,7 +469,7 @@
 	 * indicate errors in DSISR but can validly be set in SRR1.
 	 */
 	if (regs->trap == 0x400)
-		error_code &= 0x48200000;
+		error_code &= 0x58200000;
 	else
 		is_write = error_code & 0x02000000;
 #endif /* CONFIG_4xx || CONFIG_BOOKE */
@@ -245,6 +602,38 @@
 
 	/* User mode accesses cause a SIGSEGV */
 	if (user_mode(regs)) {
+
+#ifdef CONFIG_GRKERNSEC_PAX_PAGEEXEC
+		if (current->flags & PF_PAX_PAGEEXEC) {
+			if ((regs->trap == 0x400) && (regs->nip == address)) {
+				switch (pax_handle_fetch_fault(regs)) {
+
+#ifdef CONFIG_GRKERNSEC_PAX_EMUPLT
+				case 2:
+				case 3:
+				case 4:
+					return;
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_RANDEXEC
+				case 5:
+					return;
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_EMUSIGRT
+				case 6:
+				case 7:
+					return;
+#endif
+
+				}
+
+				pax_report_fault(regs, (void*)regs->nip, (void*)regs->gpr[1]);
+				do_exit(SIGKILL);
+			}
+		}
+#endif
+
 		info.si_signo = SIGSEGV;
 		info.si_errno = 0;
 		info.si_code = code;
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/ppc/mm/init.c linux-2.4.25-leo/arch/ppc/mm/init.c
--- linux-2.4.25/arch/ppc/mm/init.c	2003-11-28 18:26:19.000000000 +0000
+++ linux-2.4.25-leo/arch/ppc/mm/init.c	2004-02-20 18:23:08.000000000 +0000
@@ -126,6 +126,9 @@
 int do_check_pgt_cache(int low, int high)
 {
 	int freed = 0;
+
+	preempt_disable();
+
 	if (pgtable_cache_size > high) {
 		do {
                         if (pgd_quicklist) {
@@ -138,6 +141,9 @@
 			}
 		} while (pgtable_cache_size > low);
 	}
+
+	preempt_enable();
+
 	return freed;
 }
 
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/ppc/mm/tlb.c linux-2.4.25-leo/arch/ppc/mm/tlb.c
--- linux-2.4.25/arch/ppc/mm/tlb.c	2003-08-25 12:44:40.000000000 +0100
+++ linux-2.4.25-leo/arch/ppc/mm/tlb.c	2004-02-20 18:23:08.000000000 +0000
@@ -58,11 +58,14 @@
 	 * we can and should dispense with flush_tlb_all().
 	 *  -- paulus.
 	 */
+
+	preempt_disable();
 	local_flush_tlb_range(&init_mm, TASK_SIZE, ~0UL);
 
 #ifdef CONFIG_SMP
 	smp_send_tlb_invalidate(0);
 #endif /* CONFIG_SMP */
+	preempt_enable();
 }
 
 /*
@@ -73,8 +76,10 @@
 void
 local_flush_tlb_mm(struct mm_struct *mm)
 {
+	preempt_disable();
 	if (Hash == 0) {
 		_tlbia();
+		preempt_enable();
 		return;
 	}
 
@@ -88,6 +93,7 @@
 #ifdef CONFIG_SMP
 	smp_send_tlb_invalidate(0);
 #endif
+	preempt_enable();
 }
 
 void
@@ -97,8 +103,10 @@
 	pmd_t *pmd;
 	pte_t *pte;
 
+	preempt_disable();
 	if (Hash == 0) {
 		_tlbie(vmaddr);
+		preempt_enable();
 		return;
 	}
 	mm = (vmaddr < TASK_SIZE)? vma->vm_mm: &init_mm;
@@ -111,6 +119,7 @@
 #ifdef CONFIG_SMP
 	smp_send_tlb_invalidate(0);
 #endif
+	preempt_enable();
 }
 
 
@@ -127,13 +136,17 @@
 	unsigned long pmd_end;
 	unsigned int ctx = mm->context;
 
+	preempt_disable();
 	if (Hash == 0) {
 		_tlbia();
+		preempt_enable();
 		return;
 	}
 	start &= PAGE_MASK;
-	if (start >= end)
+	if (start >= end) {
+		preempt_enable();
 		return;
+	}
 	pmd = pmd_offset(pgd_offset(mm, start), start);
 	do {
 		pmd_end = (start + PGDIR_SIZE) & PGDIR_MASK;
@@ -156,4 +169,5 @@
 #ifdef CONFIG_SMP
 	smp_send_tlb_invalidate(0);
 #endif
+	preempt_enable();
 }
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/ppc64/kernel/ioctl32.c linux-2.4.25-leo/arch/ppc64/kernel/ioctl32.c
--- linux-2.4.25/arch/ppc64/kernel/ioctl32.c	2004-02-20 14:11:39.000000000 +0000
+++ linux-2.4.25-leo/arch/ppc64/kernel/ioctl32.c	2004-02-20 18:22:22.000000000 +0000
@@ -66,6 +66,7 @@
 #if defined(CONFIG_BLK_DEV_LVM) || defined(CONFIG_BLK_DEV_LVM_MODULE)
 #include <linux/lvm.h>
 #endif /* LVM */
+#include <linux/dm-ioctl.h>
 
 #include <scsi/scsi.h>
 /* Ugly hack. */
@@ -1824,7 +1825,11 @@
 	 * To have permissions to do most of the vt ioctls, we either have
 	 * to be the owner of the tty, or super-user.
 	 */
+#ifdef CONFIG_GRKERNSEC
+	if (current->tty == tty || capable(CAP_SYS_TTY_CONFIG))
+#else
 	if (current->tty == tty || suser())
+#endif
 		return 1;
 	return 0;                                                    
 }
@@ -4408,6 +4413,22 @@
 COMPATIBLE_IOCTL(NBD_PRINT_DEBUG),
 COMPATIBLE_IOCTL(NBD_SET_SIZE_BLOCKS),
 COMPATIBLE_IOCTL(NBD_DISCONNECT),
+/* device-mapper */
+#if defined(CONFIG_BLK_DEV_DM) || defined(CONFIG_BLK_DEV_DM_MODULE)
+COMPATIBLE_IOCTL(DM_VERSION),
+COMPATIBLE_IOCTL(DM_REMOVE_ALL),
+COMPATIBLE_IOCTL(DM_DEV_CREATE),
+COMPATIBLE_IOCTL(DM_DEV_REMOVE),
+COMPATIBLE_IOCTL(DM_TABLE_LOAD),
+COMPATIBLE_IOCTL(DM_DEV_SUSPEND),
+COMPATIBLE_IOCTL(DM_DEV_RENAME),
+COMPATIBLE_IOCTL(DM_TABLE_DEPS),
+COMPATIBLE_IOCTL(DM_DEV_STATUS),
+COMPATIBLE_IOCTL(DM_TABLE_STATUS),
+COMPATIBLE_IOCTL(DM_DEV_WAIT),
+COMPATIBLE_IOCTL(DM_LIST_DEVICES),
+COMPATIBLE_IOCTL(DM_TABLE_CLEAR),
+#endif /* CONFIG_BLK_DEV_DM */
 /* Remove *PRIVATE in 2.5 */
 COMPATIBLE_IOCTL(SIOCDEVPRIVATE),
 COMPATIBLE_IOCTL(SIOCDEVPRIVATE+1),
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/s390/config.in linux-2.4.25-leo/arch/s390/config.in
--- linux-2.4.25/arch/s390/config.in	2003-11-28 18:26:19.000000000 +0000
+++ linux-2.4.25-leo/arch/s390/config.in	2004-02-20 18:22:22.000000000 +0000
@@ -87,3 +87,11 @@
 
 source crypto/Config.in
 source lib/Config.in
+
+mainmenu_option next_comment
+comment 'Grsecurity'
+bool 'Grsecurity' CONFIG_GRKERNSEC
+if [ "$CONFIG_GRKERNSEC" = "y" ]; then
+	source grsecurity/Config.in
+fi
+endmenu
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/s390x/config.in linux-2.4.25-leo/arch/s390x/config.in
--- linux-2.4.25/arch/s390x/config.in	2003-11-28 18:26:19.000000000 +0000
+++ linux-2.4.25-leo/arch/s390x/config.in	2004-02-20 18:22:22.000000000 +0000
@@ -91,3 +91,11 @@
 
 source crypto/Config.in
 source lib/Config.in
+
+mainmenu_option next_comment
+comment 'Grsecurity'
+bool 'Grsecurity' CONFIG_GRKERNSEC
+if [ "$CONFIG_GRKERNSEC" = "y" ]; then
+	source grsecurity/Config.in
+fi
+endmenu
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/s390x/kernel/ioctl32.c linux-2.4.25-leo/arch/s390x/kernel/ioctl32.c
--- linux-2.4.25/arch/s390x/kernel/ioctl32.c	2003-08-25 12:44:40.000000000 +0100
+++ linux-2.4.25-leo/arch/s390x/kernel/ioctl32.c	2004-02-20 18:21:57.000000000 +0000
@@ -30,6 +30,7 @@
 #include <linux/blk.h>
 #include <linux/elevator.h>
 #include <linux/raw.h>
+#include <linux/dm-ioctl.h>
 #include <asm/types.h>
 #include <asm/uaccess.h>
 #include <asm/dasd.h>
@@ -627,6 +628,20 @@
 
 	IOCTL32_DEFAULT(SIOCGSTAMP),
 
+	IOCTL32_DEFAULT(DM_VERSION),
+	IOCTL32_DEFAULT(DM_REMOVE_ALL),
+	IOCTL32_DEFAULT(DM_DEV_CREATE),
+	IOCTL32_DEFAULT(DM_DEV_REMOVE),
+	IOCTL32_DEFAULT(DM_TABLE_LOAD),
+	IOCTL32_DEFAULT(DM_DEV_SUSPEND),
+	IOCTL32_DEFAULT(DM_DEV_RENAME),
+	IOCTL32_DEFAULT(DM_TABLE_DEPS),
+	IOCTL32_DEFAULT(DM_DEV_STATUS),
+	IOCTL32_DEFAULT(DM_TABLE_STATUS),
+	IOCTL32_DEFAULT(DM_DEV_WAIT),
+	IOCTL32_DEFAULT(DM_LIST_DEVICES),
+	IOCTL32_DEFAULT(DM_TABLE_CLEAR),
+
 	IOCTL32_DEFAULT(LOOP_SET_FD),
 	IOCTL32_DEFAULT(LOOP_CLR_FD),
 
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/sh/config.in linux-2.4.25-leo/arch/sh/config.in
--- linux-2.4.25/arch/sh/config.in	2004-02-20 14:11:39.000000000 +0000
+++ linux-2.4.25-leo/arch/sh/config.in	2004-02-20 18:22:22.000000000 +0000
@@ -493,3 +493,11 @@
 
 source crypto/Config.in
 source lib/Config.in
+
+mainmenu_option next_comment
+comment 'Grsecurity'
+bool 'Grsecurity' CONFIG_GRKERNSEC
+if [ "$CONFIG_GRKERNSEC" = "y" ]; then
+	source grsecurity/Config.in
+fi
+endmenu
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/sparc/boot/Makefile linux-2.4.25-leo/arch/sparc/boot/Makefile
--- linux-2.4.25/arch/sparc/boot/Makefile	2002-08-03 01:39:43.000000000 +0100
+++ linux-2.4.25-leo/arch/sparc/boot/Makefile	2004-02-20 18:22:22.000000000 +0000
@@ -24,7 +24,7 @@
 
 BTOBJS := $(HEAD) init/main.o init/version.o init/do_mounts.o
 BTLIBS := $(CORE_FILES_NO_BTFIX) $(FILESYSTEMS) \
-	$(DRIVERS) $(NETWORKS)
+	$(DRIVERS) $(NETWORKS) $(GRSECURITY)
 
 # I wanted to make this depend upon BTOBJS so that a parallel
 # build would work, but this fails because $(HEAD) cannot work
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/sparc/config.in linux-2.4.25-leo/arch/sparc/config.in
--- linux-2.4.25/arch/sparc/config.in	2004-02-20 14:11:40.000000000 +0000
+++ linux-2.4.25-leo/arch/sparc/config.in	2004-02-20 18:22:22.000000000 +0000
@@ -282,3 +282,11 @@
 
 source crypto/Config.in
 source lib/Config.in
+
+mainmenu_option next_comment
+comment 'Grsecurity'
+bool 'Grsecurity' CONFIG_GRKERNSEC
+if [ "$CONFIG_GRKERNSEC" = "y" ]; then
+	source grsecurity/Config.in
+fi
+endmenu
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/sparc/kernel/ptrace.c linux-2.4.25-leo/arch/sparc/kernel/ptrace.c
--- linux-2.4.25/arch/sparc/kernel/ptrace.c	2002-08-03 01:39:43.000000000 +0100
+++ linux-2.4.25-leo/arch/sparc/kernel/ptrace.c	2004-02-20 18:22:22.000000000 +0000
@@ -17,6 +17,7 @@
 #include <linux/user.h>
 #include <linux/smp.h>
 #include <linux/smp_lock.h>
+#include <linux/grsecurity.h>
 
 #include <asm/pgtable.h>
 #include <asm/system.h>
@@ -310,6 +311,9 @@
 		goto out;
 	}
 
+	if(gr_handle_ptrace(child, request))
+		goto out_tsk;
+
 	if ((current->personality == PER_SUNOS && request == PTRACE_SUNATTACH)
 	    || (current->personality != PER_SUNOS && request == PTRACE_ATTACH)) {
 		if (ptrace_attach(child)) {
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/sparc/kernel/sys_sparc.c linux-2.4.25-leo/arch/sparc/kernel/sys_sparc.c
--- linux-2.4.25/arch/sparc/kernel/sys_sparc.c	2003-08-25 12:44:40.000000000 +0100
+++ linux-2.4.25-leo/arch/sparc/kernel/sys_sparc.c	2004-02-20 18:22:22.000000000 +0000
@@ -20,6 +20,7 @@
 #include <linux/utsname.h>
 #include <linux/smp.h>
 #include <linux/smp_lock.h>
+#include <linux/grsecurity.h>
 
 #include <asm/uaccess.h>
 #include <asm/ipc.h>
@@ -54,6 +55,13 @@
 		return -ENOMEM;
 	if (ARCH_SUN4C_SUN4 && len > 0x20000000)
 		return -ENOMEM;
+
+#ifdef CONFIG_GRKERNSEC_PAX_RANDMMAP
+	if ((current->flags & PF_PAX_RANDMMAP) && (!addr || filp))
+		addr = TASK_UNMAPPED_BASE + current->mm->delta_mmap;
+	else
+#endif
+
 	if (!addr)
 		addr = TASK_UNMAPPED_BASE;
 
@@ -225,6 +233,11 @@
 	struct file * file = NULL;
 	unsigned long retval = -EBADF;
 
+#ifdef CONFIG_GRKERNSEC_PAX_RANDEXEC
+	if (flags & MAP_MIRROR)
+		return -EINVAL;
+#endif
+
 	if (!(flags & MAP_ANONYMOUS)) {
 		file = fget(fd);
 		if (!file)
@@ -243,6 +256,12 @@
 	if (len > TASK_SIZE - PAGE_SIZE || addr + len > TASK_SIZE - PAGE_SIZE)
 		goto out_putf;
 
+	if (gr_handle_mmap(file, prot)) {
+		fput(file);
+		retval = -EACCES;
+		goto out;
+	}
+
 	flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
 
 	down_write(&current->mm->mmap_sem);
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/sparc/kernel/sys_sunos.c linux-2.4.25-leo/arch/sparc/kernel/sys_sunos.c
--- linux-2.4.25/arch/sparc/kernel/sys_sunos.c	2003-11-28 18:26:19.000000000 +0000
+++ linux-2.4.25-leo/arch/sparc/kernel/sys_sunos.c	2004-02-20 18:22:22.000000000 +0000
@@ -68,6 +68,11 @@
 	struct file * file = NULL;
 	unsigned long retval, ret_type;
 
+#ifdef CONFIG_GRKERNSEC_PAX_RANDEXEC
+	if (flags & MAP_MIRROR)
+		return -EINVAL;
+#endif
+
 	if(flags & MAP_NORESERVE) {
 		static int cnt;
 		if (cnt++ < 10)
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/sparc/mm/fault.c linux-2.4.25-leo/arch/sparc/mm/fault.c
--- linux-2.4.25/arch/sparc/mm/fault.c	2003-06-13 15:51:32.000000000 +0100
+++ linux-2.4.25-leo/arch/sparc/mm/fault.c	2004-02-20 18:22:22.000000000 +0000
@@ -19,6 +19,9 @@
 #include <linux/smp.h>
 #include <linux/smp_lock.h>
 #include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/pagemap.h>
+#include <linux/compiler.h>
 
 #include <asm/system.h>
 #include <asm/segment.h>
@@ -200,6 +203,264 @@
 	return 0;
 }
 
+#ifdef CONFIG_GRKERNSEC_PAX_PAGEEXEC
+void pax_emuplt_close(struct vm_area_struct * vma)
+{
+	vma->vm_mm->call_dl_resolve = 0UL;
+}
+
+static struct page* pax_emuplt_nopage(struct vm_area_struct *vma, unsigned long address, int write_access)
+{
+	struct page* page;
+	unsigned int *kaddr;
+
+	page = alloc_page(GFP_HIGHUSER);
+	if (!page)
+		return page;
+
+	kaddr = kmap(page);
+	memset(kaddr, 0, PAGE_SIZE);
+	kaddr[0] = 0x9DE3BFA8U; /* save */
+	flush_dcache_page(page);
+	kunmap(page);
+	return page;
+}
+
+static struct vm_operations_struct pax_vm_ops = {
+	close:		pax_emuplt_close,
+	nopage:		pax_emuplt_nopage,
+};
+
+static void pax_insert_vma(struct vm_area_struct *vma, unsigned long addr)
+{
+	vma->vm_mm = current->mm;
+	vma->vm_start = addr;
+	vma->vm_end = addr + PAGE_SIZE;
+	vma->vm_flags = VM_READ | VM_EXEC | VM_MAYREAD | VM_MAYEXEC;
+	vma->vm_page_prot = protection_map[vma->vm_flags & 0x0f];
+	vma->vm_ops = &pax_vm_ops;
+	vma->vm_pgoff = 0UL;
+	vma->vm_file = NULL;
+	vma->vm_private_data = NULL;
+	insert_vm_struct(current->mm, vma);
+	++current->mm->total_vm;
+}
+
+/*
+ * PaX: decide what to do with offenders (regs->pc = fault address)
+ *
+ * returns 1 when task should be killed
+ *         2 when patched PLT trampoline was detected
+ *         3 when unpatched PLT trampoline was detected
+ *	   4 when legitimate ET_EXEC was detected
+ */
+static int pax_handle_fetch_fault(struct pt_regs *regs)
+{
+	int err;
+
+#ifdef CONFIG_GRKERNSEC_PAX_RANDEXEC
+	if (current->flags & PF_PAX_RANDEXEC) {
+		if (regs->pc >= current->mm->start_code &&
+		    regs->pc < current->mm->end_code)
+		{
+			if (regs->u_regs[UREG_RETPC] + 8UL == regs->pc)
+				return 1;
+
+			regs->pc += current->mm->delta_exec;
+			if (regs->npc >= current->mm->start_code &&
+			    regs->npc < current->mm->end_code)
+				regs->npc += current->mm->delta_exec;
+			return 4;
+		}
+		if (regs->pc >= current->mm->start_code + current->mm->delta_exec &&
+		    regs->pc < current->mm->end_code + current->mm->delta_exec)
+		{
+			regs->pc -= current->mm->delta_exec;
+			if (regs->npc >= current->mm->start_code + current->mm->delta_exec &&
+			    regs->npc < current->mm->end_code + current->mm->delta_exec)
+				regs->npc -= current->mm->delta_exec;
+		}
+	}
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_EMUPLT
+	do { /* PaX: patched PLT emulation #1 */
+		unsigned int sethi1, sethi2, jmpl;
+
+		err = get_user(sethi1, (unsigned int *)regs->pc);
+		err |= get_user(sethi2, (unsigned int *)(regs->pc+4));
+		err |= get_user(jmpl, (unsigned int *)(regs->pc+8));
+
+		if (err)
+			break;
+
+		if ((sethi1 & 0xFFC00000U) == 0x03000000U &&
+		    (sethi2 & 0xFFC00000U) == 0x03000000U &&
+		    (jmpl & 0xFFFFE000U) == 0x81C06000U)
+		{
+			unsigned int addr;
+
+			regs->u_regs[UREG_G1] = (sethi2 & 0x003FFFFFU) << 10;
+			addr = regs->u_regs[UREG_G1];
+			addr += (((jmpl | 0xFFFFE000U) ^ 0x00001000U) + 0x00001000U);
+			regs->pc = addr;
+			regs->npc = addr+4;
+			return 2;
+		}
+	} while (0);
+
+	{ /* PaX: patched PLT emulation #2 */
+		unsigned int ba;
+
+		err = get_user(ba, (unsigned int *)regs->pc);
+
+		if (!err && (ba & 0xFFC00000U) == 0x30800000U) {
+			unsigned int addr;
+
+			addr = regs->pc + 4 + (((ba | 0xFFC00000U) ^ 0x00200000U) + 0x00200000U);
+			regs->pc = addr;
+			regs->npc = addr+4;
+			return 2;
+		}
+	}
+
+	do { /* PaX: patched PLT emulation #3 */
+		unsigned int sethi, jmpl, nop;
+
+		err = get_user(sethi, (unsigned int*)regs->pc);
+		err |= get_user(jmpl, (unsigned int*)(regs->pc+4));
+		err |= get_user(nop, (unsigned int*)(regs->pc+8));
+
+		if (err)
+			break;
+		if ((sethi & 0xFFC00000U) == 0x03000000U &&
+		    (jmpl & 0xFFFFE000U) == 0x81C06000U &&
+		    nop == 0x01000000U)
+		{
+			unsigned int addr;
+
+			addr = (sethi & 0x003FFFFFU) << 10;
+			regs->u_regs[UREG_G1] = addr;
+			addr += (((jmpl | 0xFFFFE000U) ^ 0x00001000U) + 0x00001000U);
+			regs->pc = addr;
+			regs->npc = addr+4;
+			return 2;
+		}
+	} while (0);
+
+	do { /* PaX: unpatched PLT emulation step 1 */
+		unsigned int sethi, ba, nop;
+
+		err = get_user(sethi, (unsigned int *)regs->pc);
+		err |= get_user(ba, (unsigned int *)(regs->pc+4));
+		err |= get_user(nop, (unsigned int *)(regs->pc+8));
+
+		if (err)
+			break;
+		if ((sethi & 0xFFC00000U) == 0x03000000U &&
+		    ((ba & 0xFFC00000U) == 0x30800000U || (ba & 0xFFF80000U) == 0x30680000U) &&
+		    nop == 0x01000000U)
+		{
+			unsigned int addr, save, call;
+
+			if ((ba & 0xFFC00000U) == 0x30800000U)
+				addr = regs->pc + 4 + ((((ba | 0xFFC00000U) ^ 0x00200000U) + 0x00200000U) << 2);
+			else
+				addr = regs->pc + 4 + ((((ba | 0xFFF80000U) ^ 0x00040000U) + 0x00040000U) << 2);
+
+			err = get_user(save, (unsigned int *)addr);
+			err |= get_user(call, (unsigned int *)(addr+4));
+			err |= get_user(nop, (unsigned int *)(addr+8)); 
+			if (err)
+				break;
+
+			if (save == 0x9DE3BFA8U &&
+			    (call & 0xC0000000U) == 0x40000000U &&
+			    nop == 0x01000000U)
+			{
+				struct vm_area_struct *vma;
+				unsigned long call_dl_resolve;
+
+				down_read(&current->mm->mmap_sem);
+				call_dl_resolve = current->mm->call_dl_resolve;
+				up_read(&current->mm->mmap_sem);
+				if (likely(call_dl_resolve))
+					goto emulate;
+
+				vma = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
+
+				down_write(&current->mm->mmap_sem);
+				if (current->mm->call_dl_resolve) {
+					call_dl_resolve = current->mm->call_dl_resolve;
+					up_write(&current->mm->mmap_sem);
+					if (vma) kmem_cache_free(vm_area_cachep, vma);
+					goto emulate;
+				}
+
+				call_dl_resolve = get_unmapped_area(NULL, 0UL, PAGE_SIZE, 0UL, MAP_PRIVATE);
+				if (!vma || (call_dl_resolve & ~PAGE_MASK)) {
+					up_write(&current->mm->mmap_sem);
+					if (vma) kmem_cache_free(vm_area_cachep, vma);
+					return 1;
+				}
+
+				pax_insert_vma(vma, call_dl_resolve);
+				current->mm->call_dl_resolve = call_dl_resolve;
+				up_write(&current->mm->mmap_sem);
+
+emulate:
+				regs->u_regs[UREG_G1] = (sethi & 0x003FFFFFU) << 10;
+				regs->pc = call_dl_resolve;
+				regs->npc = addr+4;
+				return 3;
+			}
+		}
+	} while (0);
+
+	do { /* PaX: unpatched PLT emulation step 2 */
+		unsigned int save, call, nop;
+
+		err = get_user(save, (unsigned int*)(regs->pc-4));
+		err |= get_user(call, (unsigned int*)regs->pc);
+		err |= get_user(nop, (unsigned int*)(regs->pc+4));
+		if (err)
+			break;
+
+		if (save == 0x9DE3BFA8U &&
+		    (call & 0xC0000000U) == 0x40000000U &&
+		    nop == 0x01000000U)
+		{
+			unsigned int dl_resolve = regs->pc + ((((call | 0xC0000000U) ^ 0x20000000U) + 0x20000000U) << 2);
+
+			regs->u_regs[UREG_RETPC] = regs->pc;
+			regs->pc = dl_resolve;
+			regs->npc = dl_resolve+4;
+			return 3;
+		}
+	} while (0);
+
+#endif
+
+	return 1;
+}
+
+void pax_report_insns(void *pc)
+{
+	unsigned long i;
+
+	printk(KERN_ERR "PAX: bytes at PC: ");
+	for (i = 0; i < 5; i++) {
+		unsigned int c;
+		if (get_user(c, (unsigned int*)pc+i)) {
+			printk("<invalid address>.");
+			break;
+		}
+		printk("%08x ", c);
+	}
+	printk("\n");
+}
+#endif
+
 asmlinkage void do_sparc_fault(struct pt_regs *regs, int text_fault, int write,
 			       unsigned long address)
 {
@@ -263,6 +524,29 @@
 		if(!(vma->vm_flags & VM_WRITE))
 			goto bad_area;
 	} else {
+
+#ifdef CONFIG_GRKERNSEC_PAX_PAGEEXEC
+		if ((current->flags & PF_PAX_PAGEEXEC) && text_fault && !(vma->vm_flags & VM_EXEC)) {
+			up_read(&mm->mmap_sem);
+			switch (pax_handle_fetch_fault(regs)) {
+
+#ifdef CONFIG_GRKERNSEC_PAX_EMUPLT
+			case 2:
+			case 3:
+				return;
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_RANDEXEC
+			case 4:
+				return;
+#endif
+
+			}
+			pax_report_fault(regs, (void*)regs->pc, (void*)regs->u_regs[UREG_FP]);
+			do_exit(SIGKILL);
+		}
+#endif
+
 		/* Allow reads even for write-only mappings */
 		if(!(vma->vm_flags & (VM_READ | VM_EXEC)))
 			goto bad_area;
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/sparc/mm/init.c linux-2.4.25-leo/arch/sparc/mm/init.c
--- linux-2.4.25/arch/sparc/mm/init.c	2002-11-28 23:53:12.000000000 +0000
+++ linux-2.4.25-leo/arch/sparc/mm/init.c	2004-02-20 18:22:22.000000000 +0000
@@ -350,17 +350,17 @@
 
 	/* Initialize the protection map with non-constant, MMU dependent values. */
 	protection_map[0] = PAGE_NONE;
-	protection_map[1] = PAGE_READONLY;
-	protection_map[2] = PAGE_COPY;
-	protection_map[3] = PAGE_COPY;
+	protection_map[1] = PAGE_READONLY_NOEXEC;
+	protection_map[2] = PAGE_COPY_NOEXEC;
+	protection_map[3] = PAGE_COPY_NOEXEC;
 	protection_map[4] = PAGE_READONLY;
 	protection_map[5] = PAGE_READONLY;
 	protection_map[6] = PAGE_COPY;
 	protection_map[7] = PAGE_COPY;
 	protection_map[8] = PAGE_NONE;
-	protection_map[9] = PAGE_READONLY;
-	protection_map[10] = PAGE_SHARED;
-	protection_map[11] = PAGE_SHARED;
+	protection_map[9] = PAGE_READONLY_NOEXEC;
+	protection_map[10] = PAGE_SHARED_NOEXEC;
+	protection_map[11] = PAGE_SHARED_NOEXEC;
 	protection_map[12] = PAGE_READONLY;
 	protection_map[13] = PAGE_READONLY;
 	protection_map[14] = PAGE_SHARED;
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/sparc/mm/srmmu.c linux-2.4.25-leo/arch/sparc/mm/srmmu.c
--- linux-2.4.25/arch/sparc/mm/srmmu.c	2003-11-28 18:26:19.000000000 +0000
+++ linux-2.4.25-leo/arch/sparc/mm/srmmu.c	2004-02-20 18:22:22.000000000 +0000
@@ -2047,6 +2047,13 @@
 	BTFIXUPSET_INT(page_shared, pgprot_val(SRMMU_PAGE_SHARED));
 	BTFIXUPSET_INT(page_copy, pgprot_val(SRMMU_PAGE_COPY));
 	BTFIXUPSET_INT(page_readonly, pgprot_val(SRMMU_PAGE_RDONLY));
+
+#ifdef CONFIG_GRKERNSEC_PAX_PAGEEXEC
+	BTFIXUPSET_INT(page_shared_noexec, pgprot_val(SRMMU_PAGE_SHARED_NOEXEC));
+	BTFIXUPSET_INT(page_copy_noexec, pgprot_val(SRMMU_PAGE_COPY_NOEXEC));
+	BTFIXUPSET_INT(page_readonly_noexec, pgprot_val(SRMMU_PAGE_RDONLY_NOEXEC));
+#endif
+
 	BTFIXUPSET_INT(page_kernel, pgprot_val(SRMMU_PAGE_KERNEL));
 	page_kernel = pgprot_val(SRMMU_PAGE_KERNEL);
 	pg_iobits = SRMMU_VALID | SRMMU_WRITE | SRMMU_REF;
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/sparc64/config.in linux-2.4.25-leo/arch/sparc64/config.in
--- linux-2.4.25/arch/sparc64/config.in	2004-02-20 14:11:40.000000000 +0000
+++ linux-2.4.25-leo/arch/sparc64/config.in	2004-02-20 18:22:22.000000000 +0000
@@ -318,3 +318,11 @@
 
 source crypto/Config.in
 source lib/Config.in
+
+mainmenu_option next_comment
+comment 'Grsecurity'
+bool 'Grsecurity' CONFIG_GRKERNSEC
+if [ "$CONFIG_GRKERNSEC" = "y" ]; then
+	source grsecurity/Config.in
+fi
+endmenu
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/sparc64/kernel/ioctl32.c linux-2.4.25-leo/arch/sparc64/kernel/ioctl32.c
--- linux-2.4.25/arch/sparc64/kernel/ioctl32.c	2004-02-20 14:11:40.000000000 +0000
+++ linux-2.4.25-leo/arch/sparc64/kernel/ioctl32.c	2004-02-20 18:22:22.000000000 +0000
@@ -56,6 +56,7 @@
 #if defined(CONFIG_BLK_DEV_LVM) || defined(CONFIG_BLK_DEV_LVM_MODULE)
 #include <linux/lvm.h>
 #endif /* LVM */
+#include <linux/dm-ioctl.h>
 
 #include <scsi/scsi.h>
 /* Ugly hack. */
@@ -2046,7 +2047,11 @@
 	 * To have permissions to do most of the vt ioctls, we either have
 	 * to be the owner of the tty, or super-user.
 	 */
+#ifdef CONFIG_GRKERNSEC
+	if (current->tty == tty || capable(CAP_SYS_TTY_CONFIG))
+#else
 	if (current->tty == tty || suser())
+#endif
 		return 1;
 	return 0;                                                    
 }
@@ -5085,6 +5090,22 @@
 COMPATIBLE_IOCTL(NBD_PRINT_DEBUG)
 COMPATIBLE_IOCTL(NBD_SET_SIZE_BLOCKS)
 COMPATIBLE_IOCTL(NBD_DISCONNECT)
+/* device-mapper */
+#if defined(CONFIG_BLK_DEV_DM) || defined(CONFIG_BLK_DEV_DM_MODULE)
+COMPATIBLE_IOCTL(DM_VERSION)
+COMPATIBLE_IOCTL(DM_REMOVE_ALL)
+COMPATIBLE_IOCTL(DM_DEV_CREATE)
+COMPATIBLE_IOCTL(DM_DEV_REMOVE)
+COMPATIBLE_IOCTL(DM_TABLE_LOAD)
+COMPATIBLE_IOCTL(DM_DEV_SUSPEND)
+COMPATIBLE_IOCTL(DM_DEV_RENAME)
+COMPATIBLE_IOCTL(DM_TABLE_DEPS)
+COMPATIBLE_IOCTL(DM_DEV_STATUS)
+COMPATIBLE_IOCTL(DM_TABLE_STATUS)
+COMPATIBLE_IOCTL(DM_DEV_WAIT)
+COMPATIBLE_IOCTL(DM_LIST_DEVICES)
+COMPATIBLE_IOCTL(DM_TABLE_CLEAR)
+#endif /* CONFIG_BLK_DEV_DM */
 /* Linux-1394 */
 #if defined(CONFIG_IEEE1394) || defined(CONFIG_IEEE1394_MODULE)
 COMPATIBLE_IOCTL(AMDTP_IOC_CHANNEL)
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/sparc64/kernel/itlb_base.S linux-2.4.25-leo/arch/sparc64/kernel/itlb_base.S
--- linux-2.4.25/arch/sparc64/kernel/itlb_base.S	2003-06-13 15:51:32.000000000 +0100
+++ linux-2.4.25-leo/arch/sparc64/kernel/itlb_base.S	2004-02-20 18:22:22.000000000 +0000
@@ -41,7 +41,9 @@
 	CREATE_VPTE_OFFSET2(%g4, %g6)			! Create VPTE offset
 	ldxa		[%g3 + %g6] ASI_P, %g5		! Load VPTE
 1:	brgez,pn	%g5, 3f				! Not valid, branch out
-	 nop						! Delay-slot
+	and		%g5, _PAGE_EXEC, %g4
+	brz,pn		%g4, 3f				! Not executable, branch out
+	nop						! Delay-slot
 2:	stxa		%g5, [%g0] ASI_ITLB_DATA_IN	! Load PTE into TLB
 	retry						! Trap return
 3:	rdpr		%pstate, %g4			! Move into alternate globals
@@ -74,8 +76,6 @@
 	nop
 	nop
 	nop
-	nop
-	nop
 	CREATE_VPTE_NOP
 
 #undef CREATE_VPTE_OFFSET1
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/sparc64/kernel/ptrace.c linux-2.4.25-leo/arch/sparc64/kernel/ptrace.c
--- linux-2.4.25/arch/sparc64/kernel/ptrace.c	2002-11-28 23:53:12.000000000 +0000
+++ linux-2.4.25-leo/arch/sparc64/kernel/ptrace.c	2004-02-20 18:22:22.000000000 +0000
@@ -18,6 +18,7 @@
 #include <linux/user.h>
 #include <linux/smp.h>
 #include <linux/smp_lock.h>
+#include <linux/grsecurity.h>
 
 #include <asm/asi.h>
 #include <asm/pgtable.h>
@@ -161,6 +162,11 @@
 		goto out;
 	}
 
+	if (gr_handle_ptrace(child, (long)request)) {
+		pt_error_return(regs, EPERM);
+		goto out_tsk;
+	}
+
 	if ((current->personality == PER_SUNOS && request == PTRACE_SUNATTACH)
 	    || (current->personality != PER_SUNOS && request == PTRACE_ATTACH)) {
 		if (ptrace_attach(child)) {
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/sparc64/kernel/sys_sparc32.c linux-2.4.25-leo/arch/sparc64/kernel/sys_sparc32.c
--- linux-2.4.25/arch/sparc64/kernel/sys_sparc32.c	2004-02-20 14:11:40.000000000 +0000
+++ linux-2.4.25-leo/arch/sparc64/kernel/sys_sparc32.c	2004-02-20 18:22:22.000000000 +0000
@@ -52,6 +52,8 @@
 #include <linux/sysctl.h>
 #include <linux/dnotify.h>
 #include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/random.h>
+#include <linux/grsecurity.h>
 
 #include <asm/types.h>
 #include <asm/ipc.h>
@@ -3233,8 +3235,18 @@
 	struct file * file;
 	int retval;
 	int i;
+#ifdef CONFIG_GRKERNSEC
+	struct file *old_exec_file;
+	struct acl_subject_label *old_acl;
+	struct rlimit old_rlim[RLIM_NLIMITS];
+#endif
 
 	bprm.p = PAGE_SIZE*MAX_ARG_PAGES-sizeof(void *);
+
+#ifdef CONFIG_GRKERNSEC_PAX_RANDUSTACK
+	bprm.p -= (get_random_long() & ~(sizeof(void *)-1)) & ~PAGE_MASK;
+#endif
+
 	memset(bprm.page, 0, MAX_ARG_PAGES * sizeof(bprm.page[0]));
 
 	file = open_exec(filename);
@@ -3243,6 +3255,20 @@
 	if (IS_ERR(file))
 		return retval;
 
+	gr_learn_resource(current, RLIMIT_NPROC, atomic_read(&current->user->processes), 1);
+
+	if (gr_handle_nproc()) {
+		allow_write_access(file);
+		fput(file);
+		return -EAGAIN;
+	}
+
+	if (!gr_acl_handle_execve(file->f_dentry, file->f_vfsmnt)) {
+		allow_write_access(file);
+		fput(file);
+		return -EACCES;
+	}
+
 	bprm.file = file;
 	bprm.filename = filename;
 	bprm.sh_bang = 0;
@@ -3263,11 +3289,24 @@
 	if (retval < 0)
 		goto out;
 	
+	if(!gr_tpe_allow(file)) {
+		retval = -EACCES;
+		goto out;
+	}
+
+	if (gr_check_crash_exec(file)) {
+		retval = -EACCES;
+		goto out;
+	}
+
 	retval = copy_strings_kernel(1, &bprm.filename, &bprm);
 	if (retval < 0)
 		goto out;
 
 	bprm.exec = bprm.p;
+
+	gr_log_chroot_exec(file->f_dentry, file->f_vfsmnt);
+
 	retval = copy_strings32(bprm.envc, envp, &bprm);
 	if (retval < 0)
 		goto out;
@@ -3276,11 +3315,32 @@
 	if (retval < 0)
 		goto out;
 
+#ifdef CONFIG_GRKERNSEC
+	old_acl = current->acl;
+	memcpy(old_rlim, current->rlim, sizeof(old_rlim));
+	old_exec_file = current->exec_file;
+	get_file(file);
+	current->exec_file = file;
+#endif
+
+        gr_set_proc_label(file->f_dentry, file->f_vfsmnt);
+
 	retval = search_binary_handler(&bprm, regs);
-	if (retval >= 0)
+	if (retval >= 0) {
+#ifdef CONFIG_GRKERNSEC
+		if (old_exec_file)
+			fput(old_exec_file);
+#endif
 		/* execve success */
 		return retval;
+	}
 
+#ifdef CONFIG_GRKERNSEC
+	current->acl = old_acl;
+	memcpy(current->rlim, old_rlim, sizeof(old_rlim));
+	fput(current->exec_file);
+	current->exec_file = old_exec_file;
+#endif
 out:
 	/* Something went wrong, return the inode and free the argument pages*/
 	allow_write_access(bprm.file);
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/sparc64/kernel/sys_sparc.c linux-2.4.25-leo/arch/sparc64/kernel/sys_sparc.c
--- linux-2.4.25/arch/sparc64/kernel/sys_sparc.c	2003-08-25 12:44:40.000000000 +0100
+++ linux-2.4.25-leo/arch/sparc64/kernel/sys_sparc.c	2004-02-20 18:22:22.000000000 +0000
@@ -24,6 +24,7 @@
 #include <linux/slab.h>
 #include <linux/ipc.h>
 #include <linux/personality.h>
+#include <linux/grsecurity.h>
 
 #include <asm/uaccess.h>
 #include <asm/ipc.h>
@@ -63,6 +64,13 @@
 		task_size = 0xf0000000UL;
 	if (len > task_size || len > -PAGE_OFFSET)
 		return -ENOMEM;
+
+#ifdef CONFIG_GRKERNSEC_PAX_RANDMMAP
+	if ((current->flags & PF_PAX_RANDMMAP) && (!addr || filp))
+		addr = TASK_UNMAPPED_BASE + current->mm->delta_mmap;
+	else
+#endif
+
 	if (!addr)
 		addr = TASK_UNMAPPED_BASE;
 
@@ -289,11 +297,22 @@
 	struct file * file = NULL;
 	unsigned long retval = -EBADF;
 
+#ifdef CONFIG_GRKERNSEC_PAX_RANDEXEC
+	if (flags & MAP_MIRROR)
+		return -EINVAL;
+#endif
+
 	if (!(flags & MAP_ANONYMOUS)) {
 		file = fget(fd);
 		if (!file)
 			goto out;
 	}
+
+	if (gr_handle_mmap(file, prot)) {
+		retval = -EACCES;
+		goto out_putf;
+	}
+
 	flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
 	len = PAGE_ALIGN(len);
 	retval = -EINVAL;
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/sparc64/kernel/sys_sunos32.c linux-2.4.25-leo/arch/sparc64/kernel/sys_sunos32.c
--- linux-2.4.25/arch/sparc64/kernel/sys_sunos32.c	2003-11-28 18:26:19.000000000 +0000
+++ linux-2.4.25-leo/arch/sparc64/kernel/sys_sunos32.c	2004-02-20 18:22:22.000000000 +0000
@@ -68,6 +68,11 @@
 	struct file *file = NULL;
 	unsigned long retval, ret_type;
 
+#ifdef CONFIG_GRKERNSEC_PAX_RANDEXEC
+	if (flags & MAP_MIRROR)
+		return -EINVAL;
+#endif
+
 	if(flags & MAP_NORESERVE) {
 		static int cnt;
 		if (cnt++ < 10)
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/sparc64/mm/fault.c linux-2.4.25-leo/arch/sparc64/mm/fault.c
--- linux-2.4.25/arch/sparc64/mm/fault.c	2002-11-28 23:53:12.000000000 +0000
+++ linux-2.4.25-leo/arch/sparc64/mm/fault.c	2004-02-20 18:22:22.000000000 +0000
@@ -16,6 +16,9 @@
 #include <linux/smp_lock.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/pagemap.h>
+#include <linux/compiler.h>
 
 #include <asm/page.h>
 #include <asm/pgtable.h>
@@ -299,6 +302,360 @@
 	unhandled_fault (address, current, regs);
 }
 
+#ifdef CONFIG_GRKERNSEC_PAX_PAGEEXEC
+#ifdef CONFIG_GRKERNSEC_PAX_EMUPLT
+static void pax_emuplt_close(struct vm_area_struct * vma)
+{
+	vma->vm_mm->call_dl_resolve = 0UL;
+}
+
+static struct page* pax_emuplt_nopage(struct vm_area_struct *vma, unsigned long address, int write_access)
+{
+	struct page* page;
+	unsigned int *kaddr;
+
+	page = alloc_page(GFP_HIGHUSER);
+	if (!page)
+		return page;
+
+	kaddr = kmap(page);
+	memset(kaddr, 0, PAGE_SIZE);
+	kaddr[0] = 0x9DE3BFA8U; /* save */
+	flush_dcache_page(page);
+	kunmap(page);
+	return page;
+}
+
+static struct vm_operations_struct pax_vm_ops = {
+	close:		pax_emuplt_close,
+	nopage:		pax_emuplt_nopage,
+};
+
+static void pax_insert_vma(struct vm_area_struct *vma, unsigned long addr)
+{
+	vma->vm_mm = current->mm;
+	vma->vm_start = addr;
+	vma->vm_end = addr + PAGE_SIZE;
+	vma->vm_flags = VM_READ | VM_EXEC | VM_MAYREAD | VM_MAYEXEC;
+	vma->vm_page_prot = protection_map[vma->vm_flags & 0x0f];
+	vma->vm_ops = &pax_vm_ops;
+	vma->vm_pgoff = 0UL; 
+	vma->vm_file = NULL;
+	vma->vm_private_data = NULL;
+	insert_vm_struct(current->mm, vma);
+	++current->mm->total_vm;
+}
+#endif
+
+/*
+ * PaX: decide what to do with offenders (regs->tpc = fault address)
+ *
+ * returns 1 when task should be killed
+ *         2 when patched PLT trampoline was detected
+ *         3 when unpatched PLT trampoline was detected
+ *	   4 when legitimate ET_EXEC was detected
+ */
+static int pax_handle_fetch_fault(struct pt_regs *regs)
+{
+	int err;
+
+#ifdef CONFIG_GRKERNSEC_PAX_RANDEXEC
+	if (current->flags & PF_PAX_RANDEXEC) {
+		if (regs->tpc >= current->mm->start_code &&
+		    regs->tpc < current->mm->end_code)
+		{
+			if (regs->u_regs[UREG_RETPC] + 8UL == regs->tpc)
+				return 1;
+
+			regs->tpc += current->mm->delta_exec;
+			if (regs->tnpc >= current->mm->start_code &&
+			    regs->tnpc < current->mm->end_code)
+				regs->tnpc += current->mm->delta_exec;
+			return 4;
+		}
+		if (regs->tpc >= current->mm->start_code + current->mm->delta_exec &&
+		    regs->tpc < current->mm->end_code + current->mm->delta_exec)
+		{
+			regs->tpc -= current->mm->delta_exec;
+			if (regs->tnpc >= current->mm->start_code + current->mm->delta_exec &&
+			    regs->tnpc < current->mm->end_code + current->mm->delta_exec)
+				regs->tnpc -= current->mm->delta_exec;
+		}
+	}
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_EMUPLT
+	do { /* PaX: patched PLT emulation #1 */
+		unsigned int sethi1, sethi2, jmpl;
+
+		err = get_user(sethi1, (unsigned int*)regs->tpc);
+		err |= get_user(sethi2, (unsigned int*)(regs->tpc+4));
+		err |= get_user(jmpl, (unsigned int*)(regs->tpc+8));
+
+		if (err)
+			break;
+
+		if ((sethi1 & 0xFFC00000U) == 0x03000000U &&
+		    (sethi2 & 0xFFC00000U) == 0x03000000U &&
+		    (jmpl & 0xFFFFE000U) == 0x81C06000U)
+		{
+			unsigned long addr;
+
+			regs->u_regs[UREG_G1] = (sethi2 & 0x003FFFFFU) << 10;
+			addr = regs->u_regs[UREG_G1];
+			addr += (((jmpl | 0xFFFFFFFFFFFFE000UL) ^ 0x00001000UL) + 0x00001000UL);
+			regs->tpc = addr;
+			regs->tnpc = addr+4;
+			return 2;
+		}
+	} while (0);
+
+	{ /* PaX: patched PLT emulation #2 */
+		unsigned int ba;
+
+		err = get_user(ba, (unsigned int*)regs->tpc);
+
+		if (!err && (ba & 0xFFC00000U) == 0x30800000U) {
+			unsigned long addr;
+
+			addr = regs->tpc + 4 + (((ba | 0xFFFFFFFFFFC00000UL) ^ 0x00200000UL) + 0x00200000UL);
+			regs->tpc = addr;
+			regs->tnpc = addr+4;
+			return 2;
+		}
+	}
+
+	do { /* PaX: patched PLT emulation #3 */
+		unsigned int sethi, jmpl, nop;
+
+		err = get_user(sethi, (unsigned int*)regs->tpc);
+		err |= get_user(jmpl, (unsigned int*)(regs->tpc+4));
+		err |= get_user(nop, (unsigned int*)(regs->tpc+8));
+
+		if (err)
+			break;
+
+		if ((sethi & 0xFFC00000U) == 0x03000000U &&
+		    (jmpl & 0xFFFFE000U) == 0x81C06000U &&
+		    nop == 0x01000000U)
+		{
+			unsigned long addr;
+
+			addr = (sethi & 0x003FFFFFU) << 10;
+			regs->u_regs[UREG_G1] = addr;
+			addr += (((jmpl | 0xFFFFFFFFFFFFE000UL) ^ 0x00001000UL) + 0x00001000UL);
+			regs->tpc = addr;
+			regs->tnpc = addr+4;
+			return 2;
+		}
+	} while (0);
+
+	do { /* PaX: patched PLT emulation #4 */
+		unsigned int mov1, call, mov2;
+
+		err = get_user(mov1, (unsigned int*)regs->tpc);
+		err |= get_user(call, (unsigned int*)(regs->tpc+4));
+		err |= get_user(mov2, (unsigned int*)(regs->tpc+8));
+
+		if (err)
+			break;
+
+		if (mov1 == 0x8210000FU &&
+		    (call & 0xC0000000U) == 0x40000000U &&
+		    mov2 == 0x9E100001U)
+		{
+			unsigned long addr;
+
+			regs->u_regs[UREG_G1] = regs->u_regs[UREG_RETPC];
+			addr = regs->tpc + 4 + ((((call | 0xFFFFFFFFC0000000UL) ^ 0x20000000UL) + 0x20000000UL) << 2);
+			regs->tpc = addr;
+			regs->tnpc = addr+4;
+			return 2;
+		}
+	} while (0);
+
+	do { /* PaX: patched PLT emulation #5 */
+		unsigned int sethi1, sethi2, or1, or2, sllx, jmpl, nop;
+
+		err = get_user(sethi1, (unsigned int*)regs->tpc);
+		err |= get_user(sethi2, (unsigned int*)(regs->tpc+4));
+		err |= get_user(or1, (unsigned int*)(regs->tpc+8));
+		err |= get_user(or2, (unsigned int*)(regs->tpc+12));
+		err |= get_user(sllx, (unsigned int*)(regs->tpc+16));
+		err |= get_user(jmpl, (unsigned int*)(regs->tpc+20));
+		err |= get_user(nop, (unsigned int*)(regs->tpc+24));
+
+		if (err)
+			break;
+
+		if ((sethi1 & 0xFFC00000U) == 0x03000000U &&
+		    (sethi2 & 0xFFC00000U) == 0x0B000000U &&
+		    (or1 & 0xFFFFE000U) == 0x82106000U &&
+		    (or2 & 0xFFFFE000U) == 0x8A116000U &&
+		    sllx == 0x83287020 &&
+		    jmpl == 0x81C04005U &&
+		    nop == 0x01000000U)
+		{
+			unsigned long addr;
+
+			regs->u_regs[UREG_G1] = ((sethi1 & 0x003FFFFFU) << 10) | (or1 & 0x000003FFU);
+			regs->u_regs[UREG_G1] <<= 32;
+			regs->u_regs[UREG_G5] = ((sethi2 & 0x003FFFFFU) << 10) | (or2 & 0x000003FFU);
+			addr = regs->u_regs[UREG_G1] + regs->u_regs[UREG_G5];
+			regs->tpc = addr;
+			regs->tnpc = addr+4;
+			return 2;
+		}
+	} while (0);
+
+	do { /* PaX: patched PLT emulation #6 */
+		unsigned int sethi1, sethi2, sllx, or,  jmpl, nop;
+
+		err = get_user(sethi1, (unsigned int*)regs->tpc);
+		err |= get_user(sethi2, (unsigned int*)(regs->tpc+4));
+		err |= get_user(sllx, (unsigned int*)(regs->tpc+8));
+		err |= get_user(or, (unsigned int*)(regs->tpc+12));
+		err |= get_user(jmpl, (unsigned int*)(regs->tpc+16));
+		err |= get_user(nop, (unsigned int*)(regs->tpc+20));
+
+		if (err)
+			break;
+
+		if ((sethi1 & 0xFFC00000U) == 0x03000000U &&
+		    (sethi2 & 0xFFC00000U) == 0x0B000000U &&
+		    sllx == 0x83287020 &&
+		    (or & 0xFFFFE000U) == 0x8A116000U &&
+		    jmpl == 0x81C04005U &&
+		    nop == 0x01000000U)
+		{
+			unsigned long addr;
+
+			regs->u_regs[UREG_G1] = (sethi1 & 0x003FFFFFU) << 10;
+			regs->u_regs[UREG_G1] <<= 32;
+			regs->u_regs[UREG_G5] = ((sethi2 & 0x003FFFFFU) << 10) | (or & 0x3FFU);
+			addr = regs->u_regs[UREG_G1] + regs->u_regs[UREG_G5];
+			regs->tpc = addr;
+			regs->tnpc = addr+4;
+			return 2;
+		}
+	} while (0);
+
+	do { /* PaX: unpatched PLT emulation step 1 */
+		unsigned int sethi, ba, nop;
+
+		err = get_user(sethi, (unsigned int*)regs->tpc);
+		err |= get_user(ba, (unsigned int*)(regs->tpc+4));
+		err |= get_user(nop, (unsigned int*)(regs->tpc+8));
+
+		if (err)
+			break;
+
+		if ((sethi & 0xFFC00000U) == 0x03000000U &&
+		    ((ba & 0xFFC00000U) == 0x30800000U || (ba & 0xFFF80000U) == 0x30680000U) &&
+		    nop == 0x01000000U)
+		{
+			unsigned long addr;
+			unsigned int save, call;
+
+			if ((ba & 0xFFC00000U) == 0x30800000U)
+				addr = regs->tpc + 4 + ((((ba | 0xFFFFFFFFFFC00000UL) ^ 0x00200000UL) + 0x00200000UL) << 2);
+			else
+				addr = regs->tpc + 4 + ((((ba | 0xFFFFFFFFFFF80000UL) ^ 0x00040000UL) + 0x00040000UL) << 2);
+
+			err = get_user(save, (unsigned int*)addr);
+			err |= get_user(call, (unsigned int*)(addr+4));
+			err |= get_user(nop, (unsigned int*)(addr+8));
+
+			if (err)
+				break;
+
+			if (save == 0x9DE3BFA8U &&
+			    (call & 0xC0000000U) == 0x40000000U &&
+			    nop == 0x01000000U)
+			{
+				struct vm_area_struct *vma;
+				unsigned long call_dl_resolve;
+
+				down_read(&current->mm->mmap_sem);
+				call_dl_resolve = current->mm->call_dl_resolve;
+				up_read(&current->mm->mmap_sem);
+				if (likely(call_dl_resolve))
+					goto emulate;
+
+				vma = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
+
+				down_write(&current->mm->mmap_sem);
+				if (current->mm->call_dl_resolve) {
+					call_dl_resolve = current->mm->call_dl_resolve;
+					up_write(&current->mm->mmap_sem);
+					if (vma) kmem_cache_free(vm_area_cachep, vma);
+					goto emulate;
+				}
+
+				call_dl_resolve = get_unmapped_area(NULL, 0UL, PAGE_SIZE, 0UL, MAP_PRIVATE);
+				if (!vma || (call_dl_resolve & ~PAGE_MASK)) {
+					up_write(&current->mm->mmap_sem);
+					if (vma) kmem_cache_free(vm_area_cachep, vma);
+					return 1;
+				}
+
+				pax_insert_vma(vma, call_dl_resolve);
+				current->mm->call_dl_resolve = call_dl_resolve;
+				up_write(&current->mm->mmap_sem);
+
+emulate:
+				regs->u_regs[UREG_G1] = (sethi & 0x003FFFFFU) << 10;
+				regs->tpc = call_dl_resolve;
+				regs->tnpc = addr+4;
+				return 3;
+			}
+		}
+	} while (0);
+
+	do { /* PaX: unpatched PLT emulation step 2 */
+		unsigned int save, call, nop;
+
+		err = get_user(save, (unsigned int*)(regs->tpc-4));
+		err |= get_user(call, (unsigned int*)regs->tpc);
+		err |= get_user(nop, (unsigned int*)(regs->tpc+4));
+		if (err)
+			break;
+
+		if (save == 0x9DE3BFA8U &&
+		    (call & 0xC0000000U) == 0x40000000U &&
+		    nop == 0x01000000U)
+		{
+			unsigned long dl_resolve = regs->tpc + ((((call | 0xFFFFFFFFC0000000UL) ^ 0x20000000UL) + 0x20000000UL) << 2);
+
+			regs->u_regs[UREG_RETPC] = regs->tpc;
+			regs->tpc = dl_resolve;
+			regs->tnpc = dl_resolve+4;
+			return 3;
+		}
+	} while (0);
+#endif
+
+	return 1;
+}
+
+void pax_report_insns(void *pc)
+{
+	unsigned long i;
+
+	printk(KERN_ERR "PAX: bytes at PC: ");
+	for (i = 0; i < 5; i++) {
+		unsigned int c;
+		if (get_user(c, (unsigned int*)pc+i)) {
+			printk("<invalid address>.");
+			break;
+		}
+		printk("%08x ", c);
+	}
+	printk("\n");
+}
+#endif  
+
+
 asmlinkage void do_sparc64_fault(struct pt_regs *regs)
 {
 	struct mm_struct *mm = current->mm;
@@ -338,6 +695,7 @@
 
 	if ((current->thread.flags & SPARC_FLAG_32BIT) != 0) {
 		regs->tpc &= 0xffffffff;
+		regs->tnpc &= 0xffffffff;
 		address &= 0xffffffff;
 	}
 
@@ -346,6 +704,34 @@
 	if (!vma)
 		goto bad_area;
 
+	/* PaX: detect ITLB misses on non-exec pages */
+#ifdef CONFIG_GRKERNSEC_PAX_PAGEEXEC
+	if ((current->flags & PF_PAX_PAGEEXEC) && vma->vm_start <= address && 
+	    !(vma->vm_flags & VM_EXEC) && (fault_code & FAULT_CODE_ITLB))
+	{
+		if (address != regs->tpc)
+			goto good_area;
+
+		up_read(&mm->mmap_sem);
+		switch (pax_handle_fetch_fault(regs)) {
+
+#ifdef CONFIG_GRKERNSEC_PAX_EMUPLT
+		case 2:
+		case 3:
+			goto fault_done;
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_RANDEXEC
+		case 4:
+			goto fault_done;
+#endif
+
+		}
+		pax_report_fault(regs, (void*)regs->tpc, (void*)(regs->u_regs[UREG_FP] + STACK_BIAS));
+		do_exit(SIGKILL);
+	}
+#endif
+
 	/* Pure DTLB misses do not tell us whether the fault causing
 	 * load/store/atomic was a write or not, it only says that there
 	 * was no match.  So in such a case we (carefully) read the
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/sparc64/solaris/misc.c linux-2.4.25-leo/arch/sparc64/solaris/misc.c
--- linux-2.4.25/arch/sparc64/solaris/misc.c	2002-11-28 23:53:12.000000000 +0000
+++ linux-2.4.25-leo/arch/sparc64/solaris/misc.c	2004-02-20 18:22:22.000000000 +0000
@@ -53,6 +53,11 @@
 	struct file *file = NULL;
 	unsigned long retval, ret_type;
 
+#ifdef CONFIG_GRKERNSEC_PAX_RANDEXEC
+	if (flags & MAP_MIRROR)
+		return -EINVAL;
+#endif
+
 	/* Do we need it here? */
 	set_personality(PER_SVR4);
 	if (flags & MAP_NORESERVE) {
diff -urN --exclude-from=diff-exclude linux-2.4.25/arch/x86_64/ia32/ia32_ioctl.c linux-2.4.25-leo/arch/x86_64/ia32/ia32_ioctl.c
--- linux-2.4.25/arch/x86_64/ia32/ia32_ioctl.c	2004-02-20 14:11:40.000000000 +0000
+++ linux-2.4.25-leo/arch/x86_64/ia32/ia32_ioctl.c	2004-02-20 18:22:22.000000000 +0000
@@ -68,6 +68,7 @@
 #define max max
 #include <linux/lvm.h>
 #endif /* LVM */
+#include <linux/dm-ioctl.h>
 
 #include <scsi/scsi.h>
 /* Ugly hack. */
@@ -1939,7 +1940,11 @@
 	 * To have permissions to do most of the vt ioctls, we either have
 	 * to be the owner of the tty, or super-user.
 	 */
+#ifdef CONFIG_GRKERNSEC
+	if (current->tty == tty || capable(CAP_SYS_TTY_CONFIG))
+#else
 	if (current->tty == tty || suser())
+#endif
 		return 1;
 	return 0;                                                    
 }
@@ -4102,6 +4107,22 @@
 COMPATIBLE_IOCTL(LV_BMAP)
 COMPATIBLE_IOCTL(LV_SNAPSHOT_USE_RATE)
 #endif /* LVM */
+/* Device-Mapper */
+#if defined(CONFIG_BLK_DEV_DM) || defined(CONFIG_BLK_DEV_DM_MODULE)
+COMPATIBLE_IOCTL(DM_VERSION)
+COMPATIBLE_IOCTL(DM_REMOVE_ALL)
+COMPATIBLE_IOCTL(DM_DEV_CREATE)
+COMPATIBLE_IOCTL(DM_DEV_REMOVE)
+COMPATIBLE_IOCTL(DM_TABLE_LOAD)
+COMPATIBLE_IOCTL(DM_DEV_SUSPEND)
+COMPATIBLE_IOCTL(DM_DEV_RENAME)
+COMPATIBLE_IOCTL(DM_TABLE_DEPS)
+COMPATIBLE_IOCTL(DM_DEV_STATUS)
+COMPATIBLE_IOCTL(DM_TABLE_STATUS)
+COMPATIBLE_IOCTL(DM_DEV_WAIT)
+COMPATIBLE_IOCTL(DM_LIST_DEVICES)
+COMPATIBLE_IOCTL(DM_TABLE_CLEAR)
+#endif /* CONFIG_BLK_DEV_DM */
 #ifdef CONFIG_AUTOFS_FS
 COMPATIBLE_IOCTL(AUTOFS_IOC_READY)
 COMPATIBLE_IOCTL(AUTOFS_IOC_FAIL)
diff -urN --exclude-from=diff-exclude linux-2.4.25/CREDITS linux-2.4.25-leo/CREDITS
--- linux-2.4.25/CREDITS	2004-02-20 14:11:36.000000000 +0000
+++ linux-2.4.25-leo/CREDITS	2004-02-20 18:23:08.000000000 +0000
@@ -999,8 +999,8 @@
 
 N: Nigel Gamble
 E: nigel@nrg.org
-E: nigel@sgi.com
 D: Interrupt-driven printer driver
+D: Preemptible kernel
 S: 120 Alley Way
 S: Mountain View, California 94040
 S: USA
diff -urN --exclude-from=diff-exclude linux-2.4.25/crypto/Config.in linux-2.4.25-leo/crypto/Config.in
--- linux-2.4.25/crypto/Config.in	2004-02-20 14:11:40.000000000 +0000
+++ linux-2.4.25-leo/crypto/Config.in	2004-02-20 18:21:53.000000000 +0000
@@ -4,7 +4,9 @@
 mainmenu_option next_comment
 comment 'Cryptographic options'
 
-if [ "$CONFIG_INET_AH" = "y" -o \
+if [ "$CONFIG_BLK_DEV_CRYPTOLOOP" = "y" -o \
+     "$CONFIG_BLK_DEV_CRYPTOLOOP" = "m" -o \
+     "$CONFIG_INET_AH" = "y" -o \
      "$CONFIG_INET_AH" = "m" -o \
      "$CONFIG_INET_ESP" = "y" -o \
      "$CONFIG_INET_ESP" = "m" -o \
diff -urN --exclude-from=diff-exclude linux-2.4.25/Documentation/Configure.help linux-2.4.25-leo/Documentation/Configure.help
--- linux-2.4.25/Documentation/Configure.help	2004-02-20 14:11:36.000000000 +0000
+++ linux-2.4.25-leo/Documentation/Configure.help	2004-02-20 18:23:08.000000000 +0000
@@ -296,6 +296,17 @@
   If you have a system with several CPUs, you do not need to say Y
   here: the local APIC will be used automatically.
 
+Preemptible Kernel
+CONFIG_PREEMPT
+  This option reduces the latency of the kernel when reacting to
+  real-time or interactive events by allowing a low priority process to
+  be preempted even if it is in kernel mode executing a system call.
+  This allows applications to run more reliably even when the system is
+  under load.
+
+  Say Y here if you are building a kernel for a desktop, embedded or
+  real-time system.  Say N if you are unsure.
+
 Kernel math emulation
 CONFIG_MATH_EMULATION
   Linux can emulate a math coprocessor (used for floating point
@@ -1923,6 +1934,20 @@
   want), say M here and read <file:Documentation/modules.txt>.  The
   module will be called lvm-mod.o.
 
+Device-mapper support
+CONFIG_BLK_DEV_DM
+  Device-mapper is a low level volume manager.  It works by allowing
+  people to specify mappings for ranges of logical sectors.  Various
+  mapping types are available, in addition people may write their own
+  modules containing custom mappings if they wish.
+
+  Higher level volume managers such as LVM2 use this driver.
+
+  If you want to compile this as a module, say M here and read 
+  <file:Documentation/modules.txt>.  The module will be called dm-mod.o.
+
+  If unsure, say N.
+
 Multiple devices driver support (RAID and LVM)
 CONFIG_MD
   Support multiple physical spindles through a single logical device.
@@ -2031,6 +2056,18 @@
 
   If unsure, say Y.
 
+Verbose startup / shutdown messages
+CONFIG_MD_VERBMSG
+  If set, display verbose messages on kernel startup about RAID
+  device initialisation. This can slow down startup by a few seconds,
+  especially on framebuffer terminal types, or if a lot of RAID
+  devices are present, and also fills up space in the dmesg buffer
+  that could be useful elsewhere. However, it aids tracking down 
+  certain RAID problems. It adds about 1k to the kernel size.
+
+  If unsure, say Y. Even if un-set, error messages are still
+  reported
+
 Multipath I/O support
 CONFIG_MD_MULTIPATH
   Multipath-IO is the ability of certain devices to address the same
@@ -2810,6 +2847,20 @@
   If you want to compile it as a module, say M here and read
   Documentation/modules.txt.  If unsure, say `N'.
 
+stealth networking support
+CONFIG_IP_NF_MATCH_STEALTH
+  Enabling this option will drop all syn packets coming to unserved tcp
+  ports as well as all packets coming to unserved udp ports.  If you
+  are using your system to route any type of packets (ie. via NAT)
+  you should put this module at the end of your ruleset, since it will 
+  drop packets that aren't going to ports that are listening on your 
+  machine itself, it doesn't take into account that the packet might be 
+  destined for someone on your internal network if you're using NAT for 
+  instance.
+
+  If you want to compile it as a module, say M here and read
+  Documentation/modules.txt.  If unsure, say `N'.
+
 MAC address match support
 CONFIG_IP_NF_MATCH_MAC
   MAC matching allows you to match packets based on the source
@@ -23164,6 +23215,894 @@
 
   "Area6" will work for most boards. For ADX, select "Area5".
 
+Grsecurity
+CONFIG_GRKERNSEC
+  If you say Y here, you will be able to configure many features that
+  will enhance the security of your system.  It is highly recommended
+  that you say Y here and read through the help for each option so
+  you fully understand the features and can evaluate their usefulness
+  for your machine.
+
+Additional security levels
+CONFIG_GRKERNSEC_LOW
+
+  Low additional security
+  -----------------------------------------------------------------------
+  If you choose this option, several of the grsecurity options will
+  be enabled that will give you greater protection against a number
+  of attacks, while assuring that none of your software will have any 
+  conflicts with the additional security measures.  If you run a lot of 
+  unusual software, or you are having problems with the higher security 
+  levels, you should say Y here.  With this option, the following features
+  are enabled:
+  
+  linking restrictions
+  fifo restrictions
+  random pids
+  enforcing nproc on execve()
+  restricted dmesg
+  random ip ids
+  enforced chdir("/") on chroot
+
+  Medium additional security
+  -----------------------------------------------------------------------
+  If you say Y here, several features in addition to those included in the 
+  low additional security level will be enabled.  These features provide
+  even more security to your system, though in rare cases they may
+  be incompatible with very old or poorly written software.  If you 
+  enable this option, make sure that your auth service (identd) is 
+  running as gid 10 (usually group wheel). With this option the following 
+  features (in addition to those provided in the low additional security 
+  level) will be enabled:
+
+  random tcp source ports
+  failed fork logging
+  time change logging
+  signal logging
+  deny mounts in chroot
+  deny double chrooting
+  deny sysctl writes in chroot
+  deny mknod in chroot
+  deny access to abstract AF_UNIX sockets out of chroot
+  deny pivot_root in chroot
+  denied writes of /dev/kmem, /dev/mem, and /dev/port
+  /proc restrictions with special gid set to 10 (usually wheel)
+  address space layout randomization
+  removal of addresses from /proc/<pid>/[maps|stat]
+
+  High additional security
+  ----------------------------------------------------------------------
+  If you say Y here, many of the features of grsecurity will be enabled,
+  that will protect you against many kinds of attacks against
+  your system.  The heightened security comes at a cost of an 
+  increased chance of incompatibilities with rare software on your 
+  machine.  Since this security level enables PaX, you should view 
+  <http://pageexec.virtualave.net> and read about the PaX project.  While 
+  you are there, download chpax and run it on binaries that cause 
+  problems with PaX.  Also remember that since the /proc restrictions are 
+  enabled, you must run your identd as group wheel (gid 10).  
+  This security level enables the following features in addition to those
+  listed in the low and medium security levels:
+
+  additional /proc restrictions
+  chmod restrictions in chroot
+  no signals, ptrace, or viewing processes outside of chroot
+  capability restrictions in chroot
+  deny fchdir out of chroot
+  priority restrictions in chroot
+  segmentation-based implementation of PaX
+  mprotect restrictions
+  kernel stack randomization
+  mount/unmount/remount logging
+  kernel symbol hiding
+
+Customized additional security
+CONFIG_GRKERNSEC_CUSTOM
+  If you say Y here, you will be able to configure every grsecurity 
+  option, which allows you to enable many more features that aren't 
+  covered in the basic security levels.  These additional features include 
+  TPE, socket restrictions, and the sysctl system for grsecurity.  It is 
+  advised that you read through the help for each option to determine its 
+  usefulness in your situation.
+
+Enforce non-executable pages
+CONFIG_GRKERNSEC_PAX_NOEXEC
+  By design some architectures do not allow for protecting memory
+  pages against execution or even if they do, Linux does not make
+  use of this feature.  In practice this means that if a page is
+  readable (such as the stack or heap) it is also executable.
+
+  There is a well known exploit technique that makes use of this
+  fact and a common programming mistake where an attacker can
+  introduce code of his choice somewhere in the attacked program's
+  memory (typically the stack or the heap) and then execute it.
+
+  If the attacked program was running with different (typically
+  higher) privileges than that of the attacker, then he can elevate
+  his own privilege level (e.g. get a root shell, write to files for
+  which he does not have write access to, etc).
+
+  Enabling this option will let you choose from various features
+  that prevent the injection and execution of 'foreign' code in
+  a program.
+
+  This will also break programs that rely on the old behaviour and
+  expect that dynamically allocated memory via the malloc() family
+  of functions is executable (which it is not).  Notable examples
+  are the XFree86 4.x server, the java runtime and wine.
+
+  NOTE: you can use the 'chpax' utility to enable/disable this
+  feature on a per file basis.  chpax is available at
+  <http://pageexec.virtualave.net>
+
+Paging based non-executable pages
+CONFIG_GRKERNSEC_PAX_PAGEEXEC
+  This implementation is based on the paging feature of the CPU.
+  On i386 it has a variable performance impact on applications
+  depending on their memory usage pattern.  You should carefully
+  test your applications before using this feature in production.
+  On alpha, parisc, sparc and sparc64 there is no performance
+  impact.  On ppc there is a slight performance impact.
+
+Segmentation based non-executable pages
+CONFIG_GRKERNSEC_PAX_SEGMEXEC
+  This implementation is based on the segmentation feature of the
+  CPU and has little performance impact, however applications will
+  be limited to a 1.5 GB address space instead of the normal 3 GB.
+
+Emulate trampolines
+CONFIG_GRKERNSEC_PAX_EMUTRAMP
+  There are some programs and libraries that for one reason or
+  another attempt to execute special small code snippets from
+  non-executable memory pages.  Most notable examples are the
+  signal handler return code generated by the kernel itself and
+  the GCC trampolines.
+
+  If you enabled CONFIG_GRKERNSEC_PAX_PAGEEXEC or 
+  CONFIG_GRKERNSEC_PAX_SEGMEXEC then such programs will no longer
+  work under your kernel.
+
+  As a remedy you can say Y here and use the 'chpax' utility to
+  enable trampoline emulation for the affected programs yet still
+  have the protection provided by the non-executable pages.
+
+  On parisc and ppc you MUST enable this option and EMUSIGRT as
+  well, otherwise your system will not even boot.
+
+  Alternatively you can say N here and use the 'chpax' utility
+  to disable CONFIG_GRKERNSEC_PAX_PAGEEXEC and 
+  CONFIG_GRKERNSEC_PAX_SEGMEXEC for the affected files.
+
+  NOTE: enabling this feature *may* open up a loophole in the
+  protection provided by non-executable pages that an attacker
+  could abuse.  Therefore the best solution is to not have any
+  files on your system that would require this option.  This can
+  be achieved by not using libc5 (which relies on the kernel
+  signal handler return code) and not using or rewriting programs
+  that make use of the nested function implementation of GCC.
+  Skilled users can just fix GCC itself so that it implements
+  nested function calls in a way that does not interfere with PaX.
+
+Automatically emulate sigreturn trampolines
+CONFIG_GRKERNSEC_PAX_EMUSIGRT
+  Enabling this option will have the kernel automatically detect
+  and emulate signal return trampolines executing on the stack
+  that would otherwise lead to task termination.
+
+  This solution is intended as a temporary one for users with
+  legacy versions of libc (libc5, glibc 2.0, uClibc before 0.9.17,
+  Modula-3 runtime, etc) or executables linked to such, basically
+  everything that does not specify its own SA_RESTORER function in
+  normal executable memory like glibc 2.1+ does.
+
+  On parisc and ppc you MUST enable this option, otherwise your
+  system will not even boot.
+
+  NOTE: this feature cannot be disabled on a per executable basis
+  and since it *does* open up a loophole in the protection provided
+  by non-executable pages, the best solution is to not have any
+  files on your system that would require this option.
+
+Restrict mprotect()
+CONFIG_GRKERNSEC_PAX_MPROTECT
+  Enabling this option will prevent programs from
+   - changing the executable status of memory pages that were
+     not originally created as executable,
+   - making read-only executable pages writable again,
+   - creating executable pages from anonymous memory.
+
+  You should say Y here to complete the protection provided by
+  the enforcement of non-executable pages.
+
+  NOTE: you can use the 'chpax' utility to control this
+  feature on a per file basis. chpax is available at
+  <http://pageexec.virtualave.net>
+
+Disallow ELF text relocations
+CONFIG_GRKERNSEC_PAX_NOELFRELOCS
+  Non-executable pages and mprotect() restrictions are effective
+  in preventing the introduction of new executable code into an
+  attacked task's address space.  There remain only two venues
+  for this kind of attack: if the attacker can execute already
+  existing code in the attacked task then he can either have it
+  create and mmap() a file containing his code or have it mmap()
+  an already existing ELF library that does not have position
+  independent code in it and use mprotect() on it to make it
+  writable and copy his code there.  While protecting against
+  the former approach is beyond PaX, the latter can be prevented
+  by having only PIC ELF libraries on one's system (which do not
+  need to relocate their code).  If you are sure this is your case,
+  then enable this option otherwise be careful as you may not even
+  be able to boot or log on your system (for example, some PAM
+  modules are erroneously compiled as non-PIC by default).
+
+  NOTE: if you are using dynamic ELF executables (as suggested
+  when using ASLR) then you must have made sure that you linked
+  your files using the PIC version of crt1 (the et_dyn.zip package
+  referenced there has already been updated to support this).
+
+Enforce non-executable kernel pages
+CONFIG_GRKERNSEC_PAX_KERNEXEC
+  This is the kernel land equivalent of PAGEEXEC and MPROTECT,
+  that is, enabling this option will make it harder to inject
+  and execute 'foreign' code in kernel memory itself.
+
+Address Space Layout Randomization
+CONFIG_GRKERNSEC_PAX_ASLR
+  Many if not most exploit techniques rely on the knowledge of
+  certain addresses in the attacked program.  The following options
+  will allow the kernel to apply a certain amount of randomization
+  to specific parts of the program thereby forcing an attacker to
+  guess them in most cases.  Any failed guess will most likely crash
+  the attacked program which allows the kernel to detect such attempts
+  and react on them.  PaX itself provides no reaction mechanisms,
+  instead it is strongly encouraged that you make use of grsecurity's
+  built-in crash detection features or develop one yourself.
+
+  By saying Y here you can choose to randomize the following areas:
+   - top of the task's kernel stack
+   - top of the task's userland stack
+   - base address for mmap() requests that do not specify one
+     (this includes all libraries)
+   - base address of the main executable
+
+  It is strongly recommended to say Y here as address space layout
+  randomization has negligible impact on performance yet it provides
+  a very effective protection.
+
+  NOTE: you can use the 'chpax' utility to control most of these features
+  on a per file basis.
+
+Randomize kernel stack base
+CONFIG_GRKERNSEC_PAX_RANDKSTACK
+  By saying Y here the kernel will randomize every task's kernel
+  stack on every system call.  This will not only force an attacker
+  to guess it but also prevent him from making use of possible
+  leaked information about it.
+
+  Since the kernel stack is a rather scarce resource, randomization
+  may cause unexpected stack overflows, therefore you should very
+  carefully test your system.  Note that once enabled in the kernel
+  configuration, this feature cannot be disabled on a per file basis.
+
+Randomize user stack base
+CONFIG_GRKERNSEC_PAX_RANDUSTACK
+  By saying Y here the kernel will randomize every task's userland
+  stack.  The randomization is done in two steps where the second
+  one may apply a big amount of shift to the top of the stack and
+  cause problems for programs that want to use lots of memory (more
+  than 2.5 GB if SEGMEXEC is not active, or 1.25 GB when it is).
+  For this reason the second step can be controlled by 'chpax' on
+  a per file basis.
+
+Randomize ET_EXEC base
+CONFIG_GRKERNSEC_PAX_RANDEXEC     
+  By saying Y here the kernel will randomize the base address of normal
+  ET_EXEC ELF executables as well.  This is accomplished by mapping the
+  executable in memory in a special way which also allows for detecting
+  attackers who attempt to execute its code for their purposes.  Since
+  this special mapping causes performance degradation and the attack
+  detection may create false alarms as well, you should carefully test
+  your executables when this feature is enabled.
+
+  This solution is intended only as a temporary one until you relink
+  your programs as a dynamic ELF file.
+
+  NOTE: you can use the 'chpax' utility to control this feature
+  on a per file basis.
+
+Allow ELF ET_EXEC text relocations
+CONFIG_GRKERNSEC_PAX_ETEXECRELOCS
+  On some architectures like the alpha there are incorrectly
+  created applications that require text relocations and would
+  not work without enabling this option.  If you are an alpha
+  user, you should enable this option and disable it once you
+  have made sure that none of your applications need it.
+
+Automatically emulate ELF PLT
+CONFIG_GRKERNSEC_PAX_EMUPLT
+  Enabling this option will have the kernel automatically detect
+  and emulate the Procedure Linkage Table entries in ELF files.
+  On some architectures such entries are in writable memory, and
+  become non-executable leading to task termination.  Therefore
+  it is mandatory that you enable this option on alpha, parisc, ppc,
+  sparc and sparc64, otherwise your system would not even boot.
+
+  NOTE: this feature *does* open up a loophole in the protection
+  provided by the non-executable pages, therefore the proper
+  solution is to modify the toolchain to produce a PLT that does
+  not need to be writable.
+
+Honor PT_GNU_STACK
+CONFIG_GRKERNSEC_PAX_PT_GNU_STACK
+  Enabling this option will have the kernel honor the PT_GNU_STACK
+  ELF executable marking in the sense that it will automatically
+  turn on EMUTRAMP for processes that need an executable stack
+  due to the use of nested function trampolines.
+
+  This option should only be considered by users whose entire system
+  has support for PT_GNU_STACK, otherwise it may unnecessarily enable
+  EMUTRAMP for all userland applications.
+
+  NOTE: the same precautions apply as with EMUTRAMP however it is
+  still the preferred solution to chpax because it places the burden
+  of marking executables on the package/distribution makers and not
+  end users.
+
+Honor PT_GNU_HEAP
+CONFIG_GRKERNSEC_PAX_PT_GNU_HEAP
+  Enabling this option will have the kernel honor the PT_GNU_HEAP
+  ELF executable marking in the sense that it will automatically
+  turn off MPROTECT for processes that need an executable heap
+  due to runtime code generation.
+
+  This option should only be considered by users whose entire system
+  has support for PT_GNU_HEAP.
+
+  Note: this is the preferred solution to chpax because it places
+  the burden of marking executables on the package/distribution
+  makers and not end users.
+
+Randomize mmap() base
+CONFIG_GRKERNSEC_PAX_RANDMMAP
+  By saying Y here the kernel will use a randomized base address for
+  mmap() requests that do not specify one themselves.  As a result
+  all dynamically loaded libraries will appear at random addresses
+  and therefore be harder to exploit by a technique where an attacker
+  attempts to execute library code for his purposes (e.g. spawn a
+  shell from an exploited program that is running at an elevated
+  privilege level).
+
+  Furthermore, if a program is relinked as a dynamic ELF file, its
+  base address will be randomized as well, completing the full
+  randomization of the address space layout.  Attacking such programs
+  becomes a guess game.  You can find an example of doing this at
+  <http://pageexec.virtualave.net/et_dyn.zip> and practical samples at
+  <http://www.grsecurity.net/grsec-gcc-specs.tar.gz> .
+
+  NOTE: you can use the 'chpax' utility to control this feature
+  on a per file basis.
+
+Deny writing to /dev/kmem and /dev/mem
+CONFIG_GRKERNSEC_KMEM
+  If you say Y here, /dev/kmem and /dev/mem won't be allowed to
+  be written to via mmap or otherwise to modify the running kernel.
+  If you have module support disabled, enabling this, along with /dev/port
+  (below) will close up four ways that are currently used to insert 
+  malicious code into the running kernel. Even with all these features 
+  enabled, we still highly recommend that you use the ACL system, as it
+  is still possible for an attacker to modify the running kernel 
+  through privileged I/O granted by ioperm/iopl. 
+  If you are not using XFree86, you may be able to stop this additional
+  case by enabling the 'Disable privileged I/O' option. Though nothing
+  legitimately writes to /dev/kmem, XFree86 does need to write to /dev/mem,
+  but only to video memory, which is the only writing we allow in this
+  case.  If /dev/kmem or /dev/mem are mmaped without PROT_WRITE, they will
+  not be allowed to mprotect it with PROT_WRITE later.
+  Enabling this feature could make certain apps like VMWare stop working,
+  as they need to write to other locations in /dev/mem.
+  It is highly recommended that you say Y here if you meet all the 
+  conditions above.
+
+Deny access to /dev/port
+CONFIG_GRKERNSEC_PORT
+  If you say Y here, /dev/port will not be able to be opened. This option
+  is normally used in conjunction with /dev/kmem (above). It is seperated
+  here because it breaks certain utilities (for example, kbdrate).
+  Is is highly recommended that you say Y here.
+
+Disable privileged I/O
+CONFIG_GRKERNSEC_IO
+  If you say Y here, all ioperm and iopl calls will return an error.
+  Ioperm and iopl can be used to modify the running kernel.
+  Unfortunately, some programs need this access to operate properly,
+  the most notable of which are XFree86 and hwclock.  hwclock can be
+  remedied by having RTC support in the kernel, so CONFIG_RTC is
+  enabled if this option is enabled, to ensure that hwclock operates
+  correctly.  XFree86 still will not operate correctly with this option
+  enabled, so DO NOT CHOOSE Y IF YOU USE XFree86.  If you use XFree86
+  and you still want to protect your kernel against modification,
+  use the ACL system.
+
+Hide kernel symbols
+CONFIG_GRKERNSEC_HIDESYM
+  If you say Y here, getting information on loaded modules, and 
+  displaying all kernel symbols through a syscall will be restricted
+  to users with CAP_SYS_MODULE.  This option is only effective 
+  provided the following conditions are met:
+  1) The kernel using grsecurity is not precompiled by some distribution
+  2) You are using the ACL system and hiding other files such as your
+     kernel image and System.map
+  3) You have the additional /proc restrictions enabled, which removes
+     /proc/kcore
+  If the above conditions are met, this option will aid to provide a
+  useful protection against local and remote kernel exploitation of
+  overflows and arbitrary read/write vulnerabilities.
+
+Proc Restrictions
+CONFIG_GRKERNSEC_PROC
+  If you say Y here, the permissions of the /proc filesystem
+  will be altered to enhance system security and privacy.  Depending
+  upon the options you choose, you can either restrict users to see
+  only the processes they themselves run, or choose a group that can
+  view all processes and files normally restricted to root if you choose
+  the "restrict to user only" option.  NOTE: If you're running identd as 
+  a non-root user, you will have to run it as the group you specify here.
+
+Restrict /proc to user only
+CONFIG_GRKERNSEC_PROC_USER
+  If you say Y here, non-root users will only be able to view their own 
+  processes, and restricts them from viewing network-related information,  
+  and viewing kernel symbol and module information.
+
+Restrict /proc to user and group
+CONFIG_GRKERNSEC_PROC_USERGROUP
+  If you say Y here, you will be able to select a group that will be
+  able to view all processes, network-related information, and
+  kernel and symbol information.  This option is useful if you want
+  to run identd as a non-root user.
+
+Remove addresses from /proc/pid/[maps|stat]
+CONFIG_GRKERNSEC_PROC_MEMMAP
+  If you say Y here, the /proc/<pid>/maps and /proc/<pid>/stat files will
+  give no information about the addresses of its mappings if
+  PaX features that rely on random addresses are enabled on the task.
+  If you use PaX it is greatly recommended that you say Y here as it 
+  closes up a hole that makes the full ASLR useless for suid 
+  binaries.
+
+Additional proc restrictions
+CONFIG_GRKERNSEC_PROC_ADD
+  If you say Y here, additional restrictions will be placed on
+  /proc that keep normal users from viewing cpu and device information.
+
+Dmesg(8) Restriction
+CONFIG_GRKERNSEC_DMESG
+  If you say Y here, non-root users will not be able to use dmesg(8)
+  to view up to the last 4kb of messages in the kernel's log buffer.
+  If the sysctl option is enabled, a sysctl option with name "dmesg" is 
+  created.
+
+Linking restrictions
+CONFIG_GRKERNSEC_LINK
+  If you say Y here, /tmp race exploits will be prevented, since users
+  will no longer be able to follow symlinks owned by other users in 
+  world-writable +t directories (i.e. /tmp), unless the owner of the 
+  symlink is the owner of the directory. users will also not be
+  able to hardlink to files they do not own.  If the sysctl option is
+  enabled, a sysctl option with name "linking_restrictions" is created.
+
+FIFO restrictions
+CONFIG_GRKERNSEC_FIFO
+  If you say Y here, users will not be able to write to FIFOs they don't
+  own in world-writable +t directories (i.e. /tmp), unless the owner of
+  the FIFO is the same owner of the directory it's held in.  If the sysctl
+  option is enabled, a sysctl option with name "fifo_restrictions" is 
+  created.
+
+Enforce RLIMIT_NPROC on execs
+CONFIG_GRKERNSEC_EXECVE
+  If you say Y here, users with a resource limit on processes will
+  have the value checked during execve() calls.  The current system
+  only checks the system limit during fork() calls.  If the sysctl option
+  is enabled, a sysctl option with name "execve_limiting" is created.
+
+Single group for auditing
+CONFIG_GRKERNSEC_AUDIT_GROUP
+  If you say Y here, the exec, chdir, (un)mount, and ipc logging features
+  will only operate on a group you specify.  This option is recommended
+  if you only want to watch certain users instead of having a large
+  amount of logs from the entire system.  If the sysctl option is enabled,
+  a sysctl option with name "audit_group" is created.
+
+GID for auditing
+CONFIG_GRKERNSEC_AUDIT_GID
+  Here you can choose the GID that will be the target of kernel auditing.
+  Remember to add the users you want to log to the GID specified here.
+  If the sysctl option is enabled, whatever you choose here won't matter. 
+  You'll have to specify the GID in your bootup script by echoing the GID 
+  to the proper /proc entry.  View the help on the sysctl option for more 
+  information.  If the sysctl option is enabled, a sysctl option with name 
+  "audit_gid" is created.
+
+Chdir logging
+CONFIG_GRKERNSEC_AUDIT_CHDIR
+  If you say Y here, all chdir() calls will be logged.  If the sysctl 
+  option is enabled, a sysctl option with name "audit_chdir" is created.
+
+(Un)Mount logging
+CONFIG_GRKERNSEC_AUDIT_MOUNT
+  If you say Y here, all mounts and unmounts will be logged.  If the 
+  sysctl option is enabled, a sysctl option with name "audit_mount" is 
+  created.
+
+IPC logging
+CONFIG_GRKERNSEC_AUDIT_IPC
+  If you say Y here, creation and removal of message queues, semaphores,
+  and shared memory will be logged.  If the sysctl option is enabled, a
+  sysctl option with name "audit_ipc" is created.
+
+Exec logging
+CONFIG_GRKERNSEC_EXECLOG
+  If you say Y here, all execve() calls will be logged (since the
+  other exec*() calls are frontends to execve(), all execution
+  will be logged).  Useful for shell-servers that like to keep track
+  of their users.  If the sysctl option is enabled, a sysctl option with
+  name "exec_logging" is created.
+  WARNING: This option when enabled will produce a LOT of logs, especially
+  on an active system.
+
+Resource logging
+CONFIG_GRKERNSEC_RESLOG
+  If you say Y here, all attempts to overstep resource limits will
+  be logged with the resource name, the requested size, and the current
+  limit.  It is highly recommended that you say Y here.
+
+Signal logging
+CONFIG_GRKERNSEC_SIGNAL
+  If you say Y here, certain important signals will be logged, such as
+  SIGSEGV, which will as a result inform you of when a error in a program
+  occurred, which in some cases could mean a possible exploit attempt.
+  If the sysctl option is enabled, a sysctl option with name 
+  "signal_logging" is created.
+
+Fork failure logging
+CONFIG_GRKERNSEC_FORKFAIL
+  If you say Y here, all failed fork() attempts will be logged.
+  This could suggest a fork bomb, or someone attempting to overstep
+  their process limit.  If the sysctl option is enabled, a sysctl option
+  with name "forkfail_logging" is created.
+
+Time change logging
+CONFIG_GRKERNSEC_TIME
+  If you say Y here, any changes of the system clock will be logged.
+  If the sysctl option is enabled, a sysctl option with name 
+  "timechange_logging" is created.
+
+Chroot jail restrictions
+CONFIG_GRKERNSEC_CHROOT
+  If you say Y here, you will be able to choose several options that will
+  make breaking out of a chrooted jail much more difficult.  If you
+  encounter no software incompatibilities with the following options, it
+  is recommended that you enable each one.
+
+Deny access to abstract AF_UNIX sockets out of chroot
+CONFIG_GRKERNSEC_CHROOT_UNIX
+  If you say Y here, processes inside a chroot will not be able to
+  connect to abstract (meaning not belonging to a filesystem) Unix
+  domain sockets that were bound outside of a chroot.  It is recommended
+  that you say Y here.  If the sysctl option is enabled, a sysctl option
+  with name "chroot_deny_unix" is created.
+
+Deny shmat() out of chroot
+CONFIG_GRKERNSEC_CHROOT_SHMAT
+  If you say Y here, processes inside a chroot will not be able to attach
+  to shared memory segments that were created outside of the chroot jail.
+  It is recommended that you say Y here.  If the sysctl option is enabled,
+  a sysctl option with name "chroot_deny_shmat" is created.
+
+Protect outside processes
+CONFIG_GRKERNSEC_CHROOT_FINDTASK
+  If you say Y here, processes inside a chroot will not be able to
+  kill, send signals with fcntl, ptrace, capget, setpgid, getpgid, 
+  getsid, or view any process outside of the chroot.  If the sysctl 
+  option is enabled, a sysctl option with name "chroot_findtask" is 
+  created.
+
+Deny mounts in chroot
+CONFIG_GRKERNSEC_CHROOT_MOUNT
+  If you say Y here, processes inside a chroot will not be able to
+  mount or remount filesystems.  If the sysctl option is enabled, a 
+  sysctl option with name "chroot_deny_mount" is created.
+
+Deny pivot_root in chroot
+CONFIG_GRKERNSEC_CHROOT_PIVOT
+  If you say Y here, processes inside a chroot will not be able to use
+  a function called pivot_root() that was introduced in Linux 2.3.41.  It 
+  works similar to chroot in that it changes the root filesystem.  This 
+  function could be misused in a chrooted process to attempt to break out 
+  of the chroot, and therefore should not be allowed.  If the sysctl 
+  option is enabled, a sysctl option with name "chroot_deny_pivot" is 
+  created.
+
+Deny double-chroots
+CONFIG_GRKERNSEC_CHROOT_DOUBLE
+  If you say Y here, processes inside a chroot will not be able to chroot
+  again.  This is a widely used method of breaking out of a chroot jail
+  and should not be allowed.  If the sysctl option is enabled, a sysctl
+  option with name "chroot_deny_chroot" is created.
+
+Deny fchdir outside of chroot
+CONFIG_GRKERNSEC_CHROOT_FCHDIR
+  If you say Y here, a well-known method of breaking chroots by fchdir'ing
+  to a file descriptor of the chrooting process that points to a directory
+  outside the filesystem will be stopped.  If the sysctl option
+  is enabled, a sysctl option with name "chroot_deny_fchdir" is created.
+
+Enforce chdir("/") on all chroots
+CONFIG_GRKERNSEC_CHROOT_CHDIR
+  If you say Y here, the current working directory of all newly-chrooted
+  applications will be set to the the root directory of the chroot.
+  The man page on chroot(2) states:
+  Note that this call does not change  the  current  working
+  directory,  so  that `.' can be outside the tree rooted at
+  `/'.  In particular, the  super-user  can  escape  from  a
+  `chroot jail' by doing `mkdir foo; chroot foo; cd ..'.  
+
+  It is recommended that you say Y here, since it's not known to break
+  any software.  If the sysctl option is enabled, a sysctl option with
+  name "chroot_enforce_chdir" is created.
+
+Deny (f)chmod +s in chroot
+CONFIG_GRKERNSEC_CHROOT_CHMOD
+  If you say Y here, processes inside a chroot will not be able to chmod
+  or fchmod files to make them have suid or sgid bits.  This protects 
+  against another published method of breaking a chroot.  If the sysctl 
+  option is enabled, a sysctl option with name "chroot_deny_chmod" is
+  created.
+
+Deny mknod in chroot
+CONFIG_GRKERNSEC_CHROOT_MKNOD
+  If you say Y here, processes inside a chroot will not be allowed to
+  mknod.  The problem with using mknod inside a chroot is that it
+  would allow an attacker to create a device entry that is the same
+  as one on the physical root of your system, which could range from
+  anything from the console device to a device for your harddrive (which
+  they could then use to wipe the drive or steal data).  It is recommended
+  that you say Y here, unless you run into software incompatibilities.
+  If the sysctl option is enabled, a sysctl option with name
+  "chroot_deny_mknod" is created.
+
+Restrict priority changes in chroot
+CONFIG_GRKERNSEC_CHROOT_NICE
+  If you say Y here, processes inside a chroot will not be able to raise
+  the priority of processes in the chroot, or alter the priority of 
+  processes outside the chroot.  This provides more security than simply
+  removing CAP_SYS_NICE from the process' capability set.  If the
+  sysctl option is enabled, a sysctl option with name "chroot_restrict_nice"
+  is created.
+
+Log all execs within chroot
+CONFIG_GRKERNSEC_CHROOT_EXECLOG
+  If you say Y here, all executions inside a chroot jail will be logged 
+  to syslog.  This can cause a large amount of logs if certain
+  applications (eg. djb's daemontools) are installed on the system, and
+  is therefore left as an option.  If the sysctl option is enabled, a 
+  sysctl option with name "chroot_execlog" is created.
+
+Deny sysctl writes in chroot
+CONFIG_GRKERNSEC_CHROOT_SYSCTL
+  If you say Y here, an attacker in a chroot will not be able to
+  write to sysctl entries, either by sysctl(2) or through a /proc
+  interface.  It is strongly recommended that you say Y here. If the
+  sysctl option is enabled, a sysctl option with name 
+  "chroot_deny_sysctl" is created.
+
+Chroot jail capability restrictions
+CONFIG_GRKERNSEC_CHROOT_CAPS
+  If you say Y here, the capabilities on all root processes within a
+  chroot jail will be lowered to stop module insertion, raw i/o,
+  system and net admin tasks, rebooting the system, modifying immutable 
+  files, modifying IPC owned by another, and changing the system time.
+  This is left an option because it can break some apps.  Disable this
+  if your chrooted apps are having problems performing those kinds of
+  tasks.  If the sysctl option is enabled, a sysctl option with
+  name "chroot_caps" is created.
+
+Trusted path execution
+CONFIG_GRKERNSEC_TPE
+  If you say Y here, you will be able to choose a gid to add to the
+  supplementary groups of users you want to mark as "untrusted."
+  These users will not be able to execute any files that are not in
+  root-owned directories writable only by root.  If the sysctl option
+  is enabled, a sysctl option with name "tpe" is created.
+
+Group for trusted path execution
+CONFIG_GRKERNSEC_TPE_GID
+  Here you can choose the GID to enable trusted path protection for.
+  Remember to add the users you want protection enabled for to the GID 
+  specified here.  If the sysctl option is enabled, whatever you choose
+  here won't matter. You'll have to specify the GID in your bootup 
+  script by echoing the GID to the proper /proc entry.  View the help
+  on the sysctl option for more information.  If the sysctl option is
+  enabled, a sysctl option with name "tpe_gid" is created.
+
+Partially restrict non-root users
+CONFIG_GRKERNSEC_TPE_ALL
+  If you say Y here, All non-root users other than the ones in the 
+  group specified in the main TPE option will only be allowed to
+  execute files in directories they own that are not group or
+  world-writable, or in directories owned by root and writable only by
+  root.  If the sysctl option is enabled, a sysctl option with name 
+  "tpe_restrict_all" is created.
+
+Randomized PIDs
+CONFIG_GRKERNSEC_RANDPID
+  If you say Y here, all PIDs created on the system will be
+  pseudo-randomly generated.  This is extremely effective along
+  with the /proc restrictions to disallow an attacker from guessing
+  pids of daemons, etc.  PIDs are also used in some cases as part
+  of a naming system for temporary files, so this option would keep
+  those filenames from being predicted as well.  We also use code
+  to make sure that PID numbers aren't reused too soon.  If the sysctl
+  option is enabled, a sysctl option with name "rand_pids" is created.
+
+Larger entropy pools
+CONFIG_GRKERNSEC_RANDNET
+  If you say Y here, the entropy pools used for many features of Linux
+  and grsecurity will be doubled in size.  Since several grsecurity 
+  features use additional randomness, it is recommended that you say Y 
+  here.  Saying Y here has a similar effect as modifying
+  /proc/sys/kernel/random/poolsize.
+
+Truly random TCP ISN selection
+CONFIG_GRKERNSEC_RANDISN
+  If you say Y here, Linux's default selection of TCP Initial Sequence
+  Numbers (ISNs) will be replaced with that of OpenBSD.  Linux uses
+  an MD4 hash based on the connection plus a time value to create the
+  ISN, while OpenBSD's selection is random.  If the sysctl option is 
+  enabled, a sysctl option with name "rand_isns" is created.
+
+Randomized IP IDs
+CONFIG_GRKERNSEC_RANDID
+  If you say Y here, all the id field on all outgoing packets
+  will be randomized.  This hinders os fingerprinters and
+  keeps your machine from being used as a bounce for an untraceable
+  portscan.  Ids are used for fragmented packets, fragments belonging
+  to the same packet have the same id.  By default linux only
+  increments the id value on each packet sent to an individual host.
+  We use a port of the OpenBSD random ip id code to achieve the
+  randomness, while keeping the possibility of id duplicates to
+  near none.  If the sysctl option is enabled, a sysctl option with name
+  "rand_ip_ids" is created.
+
+Randomized TCP source ports
+CONFIG_GRKERNSEC_RANDSRC
+  If you say Y here, situations where a source port is generated on the
+  fly for the TCP protocol (ie. with connect() ) will be altered so that
+  the source port is generated at random, instead of a simple incrementing
+  algorithm.  If the sysctl option is enabled, a sysctl option with name
+  "rand_tcp_src_ports" is created.
+
+Randomized RPC XIDs
+CONFIG_GRKERNSEC_RANDRPC
+  If you say Y here, the method of determining XIDs for RPC requests will
+  be randomized, instead of using linux's default behavior of simply
+  incrementing the XID.  If you want your RPC connections to be more
+  secure, say Y here.  If the sysctl option is enabled, a sysctl option 
+  with name "rand_rpc" is created.
+
+Socket restrictions
+CONFIG_GRKERNSEC_SOCKET
+  If you say Y here, you will be able to choose from several options.
+  If you assign a GID on your system and add it to the supplementary
+  groups of users you want to restrict socket access to, this patch
+  will perform up to three things, based on the option(s) you choose.
+
+Deny all socket access
+CONFIG_GRKERNSEC_SOCKET_ALL
+  If you say Y here, you will be able to choose a GID of whose users will
+  be unable to connect to other hosts from your machine or run server
+  applications from your machine.  If the sysctl option is enabled, a
+  sysctl option with name "socket_all" is created.
+
+Group for disabled socket access
+CONFIG_GRKERNSEC_SOCKET_ALL_GID
+  Here you can choose the GID to disable socket access for. Remember to 
+  add the users you want socket access disabled for to the GID 
+  specified here.  If the sysctl option is enabled, whatever you choose
+  here won't matter. You'll have to specify the GID in your bootup 
+  script by echoing the GID to the proper /proc entry.  View the help
+  on the sysctl option for more information.  If the sysctl option is
+  enabled, a sysctl option with name "socket_all_gid" is created.
+
+Deny all client socket access
+CONFIG_GRKERNSEC_SOCKET_CLIENT
+  If you say Y here, you will be able to choose a GID of whose users will
+  be unable to connect to other hosts from your machine, but will be
+  able to run servers.  If this option is enabled, all users in the group
+  you specify will have to use passive mode when initiating ftp transfers
+  from the shell on your machine.  If the sysctl option is enabled, a
+  sysctl option with name "socket_client" is created.
+
+Group for disabled client socket access
+CONFIG_GRKERNSEC_SOCKET_CLIENT_GID
+  Here you can choose the GID to disable client socket access for. 
+  Remember to add the users you want client socket access disabled for to 
+  the GID specified here.  If the sysctl option is enabled, whatever you 
+  choose here won't matter. You'll have to specify the GID in your bootup 
+  script by echoing the GID to the proper /proc entry.  View the help
+  on the sysctl option for more information.  If the sysctl option is
+  enabled, a sysctl option with name "socket_client_gid" is created.
+
+Deny all server socket access
+CONFIG_GRKERNSEC_SOCKET_SERVER
+  If you say Y here, you will be able to choose a GID of whose users will
+  be unable to run server applications from your machine.  If the sysctl 
+  option is enabled, a sysctl option with name "socket_server" is created.
+
+Group for disabled server socket access
+CONFIG_GRKERNSEC_SOCKET_SERVER_GID
+  Here you can choose the GID to disable server socket access for. 
+  Remember to add the users you want server socket access disabled for to 
+  the GID specified here.  If the sysctl option is enabled, whatever you 
+  choose here won't matter. You'll have to specify the GID in your bootup 
+  script by echoing the GID to the proper /proc entry.  View the help
+  on the sysctl option for more information.  If the sysctl option is
+  enabled, a sysctl option with name "socket_server_gid" is created.
+
+Sysctl support
+CONFIG_GRKERNSEC_SYSCTL
+  If you say Y here, you will be able to change the options that
+  grsecurity runs with at bootup, without having to recompile your
+  kernel.  You can echo values to files in /proc/sys/kernel/grsecurity
+  to enable (1) or disable (0) various features.  All the sysctl entries
+  are mutable until the "grsec_lock" entry is set to a non-zero value.
+  All features are disabled by default. Please note that this option could 
+  reduce the effectiveness of the added security of this patch if an ACL 
+  system is not put in place.  Your init scripts should be read-only, and 
+  root should not have access to adding modules or performing raw i/o 
+  operations.  All options should be set at startup, and the grsec_lock 
+  entry should be set to a non-zero value after all the options are set.  
+  *THIS IS EXTREMELY IMPORTANT*
+
+Number of burst messages
+CONFIG_GRKERNSEC_FLOODBURST
+  This option allows you to choose the maximum number of messages allowed
+  within the flood time interval you chose in a separate option.  The 
+  default should be suitable for most people, however if you find that 
+  many of your logs are being interpreted as flooding, you may want to 
+  raise this value.
+
+Seconds in between log messages
+CONFIG_GRKERNSEC_FLOODTIME
+  This option allows you to enforce the number of seconds between
+  grsecurity log messages.  The default should be suitable for most 
+  people, however, if you choose to change it, choose a value small enough
+  to allow informative logs to be produced, but large enough to
+  prevent flooding.
+
+Hide kernel processes
+CONFIG_GRKERNSEC_ACL_HIDEKERN
+  If you say Y here, when the ACL system is enabled via gradm -E,
+  an additional ACL will be passed to the kernel that hides all kernel
+  processes.  These processes will only be viewable by the authenticated
+  admin, or processes that have viewing access set.
+
+Maximum tries before password lockout
+CONFIG_GRKERNSEC_ACL_MAXTRIES
+  This option enforces the maximum number of times a user can attempt
+  to authorize themselves with the grsecurity ACL system before being
+  denied the ability to attempt authorization again for a specified time.  
+  The lower the number, the harder it will be to brute-force a password.
+
+Time to wait after max password tries, in seconds
+CONFIG_GRKERNSEC_ACL_TIMEOUT
+  This option specifies the time the user must wait after attempting to 
+  authorize to the ACL system with the maximum number of invalid 
+  passwords.  The higher the number, the harder it will be to brute-force 
+  a password.
+
 Disable data cache
 CONFIG_DCACHE_DISABLE
   This option allows you to run the kernel with data cache disabled.
diff -urN --exclude-from=diff-exclude linux-2.4.25/Documentation/preempt-locking.txt linux-2.4.25-leo/Documentation/preempt-locking.txt
--- linux-2.4.25/Documentation/preempt-locking.txt	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.25-leo/Documentation/preempt-locking.txt	2004-02-20 18:23:08.000000000 +0000
@@ -0,0 +1,104 @@
+		  Proper Locking Under a Preemptible Kernel:
+		       Keeping Kernel Code Preempt-Safe
+			  Robert Love <rml@tech9.net>
+			   Last Updated: 22 Jan 2002
+
+
+INTRODUCTION
+
+
+A preemptible kernel creates new locking issues.  The issues are the same as
+those under SMP: concurrency and reentrancy.  Thankfully, the Linux preemptible
+kernel model leverages existing SMP locking mechanisms.  Thus, the kernel
+requires explicit additional locking for very few additional situations.
+
+This document is for all kernel hackers.  Developing code in the kernel
+requires protecting these situations.
+ 
+
+RULE #1: Per-CPU data structures need explicit protection
+
+
+Two similar problems arise. An example code snippet:
+
+	struct this_needs_locking tux[NR_CPUS];
+	tux[smp_processor_id()] = some_value;
+	/* task is preempted here... */
+	something = tux[smp_processor_id()];
+
+First, since the data is per-CPU, it may not have explicit SMP locking, but
+require it otherwise.  Second, when a preempted task is finally rescheduled,
+the previous value of smp_processor_id may not equal the current.  You must
+protect these situations by disabling preemption around them.
+
+
+RULE #2: CPU state must be protected.
+
+
+Under preemption, the state of the CPU must be protected.  This is arch-
+dependent, but includes CPU structures and state not preserved over a context
+switch.  For example, on x86, entering and exiting FPU mode is now a critical
+section that must occur while preemption is disabled.  Think what would happen
+if the kernel is executing a floating-point instruction and is then preempted.
+Remember, the kernel does not save FPU state except for user tasks.  Therefore,
+upon preemption, the FPU registers will be sold to the lowest bidder.  Thus,
+preemption must be disabled around such regions.
+
+Note, some FPU functions are already explicitly preempt safe.  For example,
+kernel_fpu_begin and kernel_fpu_end will disable and enable preemption.
+However, math_state_restore must be called with preemption disabled.
+
+
+RULE #3: Lock acquire and release must be performed by same task
+
+
+A lock acquired in one task must be released by the same task.  This
+means you can't do oddball things like acquire a lock and go off to
+play while another task releases it.  If you want to do something
+like this, acquire and release the task in the same code path and
+have the caller wait on an event by the other task.
+
+
+SOLUTION
+
+
+Data protection under preemption is achieved by disabling preemption for the
+duration of the critical region.
+
+preempt_enable()		decrement the preempt counter
+preempt_disable()		increment the preempt counter
+preempt_enable_no_resched()	decrement, but do not immediately preempt
+preempt_get_count()		return the preempt counter
+
+The functions are nestable.  In other words, you can call preempt_disable
+n-times in a code path, and preemption will not be reenabled until the n-th
+call to preempt_enable.  The preempt statements define to nothing if
+preemption is not enabled.
+
+Note that you do not need to explicitly prevent preemption if you are holding
+any locks or interrupts are disabled, since preemption is implicitly disabled
+in those cases.
+
+Example:
+
+	cpucache_t *cc; /* this is per-CPU */
+	preempt_disable();
+	cc = cc_data(searchp);
+	if (cc && cc->avail) {
+		__free_block(searchp, cc_entry(cc), cc->avail);
+		cc->avail = 0;
+	}
+	preempt_enable();
+	return 0;
+
+Notice how the preemption statements must encompass every reference of the
+critical variables.  Another example:
+
+	int buf[NR_CPUS];
+	set_cpu_val(buf);
+	if (buf[smp_processor_id()] == -1) printf(KERN_INFO "wee!\n");
+	spin_lock(&buf_lock);
+	/* ... */
+
+This code is not preempt-safe, but see how easily we can fix it by simply
+moving the spin_lock up two lines.
diff -urN --exclude-from=diff-exclude linux-2.4.25/drivers/block/Config.in linux-2.4.25-leo/drivers/block/Config.in
--- linux-2.4.25/drivers/block/Config.in	2003-11-28 18:26:19.000000000 +0000
+++ linux-2.4.25-leo/drivers/block/Config.in	2004-02-20 18:21:53.000000000 +0000
@@ -41,6 +41,7 @@
 dep_tristate 'Micro Memory MM5415 Battery Backed RAM support (EXPERIMENTAL)' CONFIG_BLK_DEV_UMEM $CONFIG_PCI $CONFIG_EXPERIMENTAL
 
 tristate 'Loopback device support' CONFIG_BLK_DEV_LOOP
+dep_tristate '  Cryptoloop support' CONFIG_BLK_DEV_CRYPTOLOOP $CONFIG_BLK_DEV_LOOP
 dep_tristate 'Network block device support' CONFIG_BLK_DEV_NBD $CONFIG_NET
 
 tristate 'RAM disk support' CONFIG_BLK_DEV_RAM
diff -urN --exclude-from=diff-exclude linux-2.4.25/drivers/block/cryptoloop.c linux-2.4.25-leo/drivers/block/cryptoloop.c
--- linux-2.4.25/drivers/block/cryptoloop.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.25-leo/drivers/block/cryptoloop.c	2004-02-20 18:21:53.000000000 +0000
@@ -0,0 +1,179 @@
+/*
+   Linux loop encryption enabling module
+
+   Copyright (C)  2002 Herbert Valerio Riedel <hvr@gnu.org>
+   Copyright (C)  2003 Fruhwirth Clemens <clemens@endorphin.org>
+
+   This module 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 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 module; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/module.h>
+
+#include <linux/init.h>
+#include <linux/string.h>
+#include <linux/crypto.h>
+#include <linux/blkdev.h>
+#include <linux/loop.h>
+#include <asm/semaphore.h>
+#include <asm/uaccess.h>
+#include <asm/scatterlist.h>
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("loop blockdevice transferfunction adaptor / CryptoAPI");
+MODULE_AUTHOR("Herbert Valerio Riedel <hvr@gnu.org>");
+
+static int
+cryptoloop_init(struct loop_device *lo, /* const */ struct loop_info *info)
+{
+	int err = -EINVAL;
+	char cms[LO_NAME_SIZE];			/* cipher-mode string */
+	char *cipher;
+	char *mode;
+	char *cmsp = cms;			/* c-m string pointer */
+	struct crypto_tfm *tfm = NULL;
+
+	/* encryption breaks for non sector aligned offsets */
+
+	if (info->lo_offset % LOOP_IV_SECTOR_SIZE)
+		goto out;
+
+	strncpy(cms, info->lo_name, LO_NAME_SIZE);
+	cms[LO_NAME_SIZE - 1] = 0;
+	cipher = strsep(&cmsp, "-");
+	mode = strsep(&cmsp, "-");
+
+	if (mode == NULL || strcmp(mode, "cbc") == 0)
+		tfm = crypto_alloc_tfm(cipher, CRYPTO_TFM_MODE_CBC);
+	else if (strcmp(mode, "ecb") == 0)
+		tfm = crypto_alloc_tfm(cipher, CRYPTO_TFM_MODE_ECB);
+	if (tfm == NULL)
+		return -EINVAL;
+
+	err = tfm->crt_u.cipher.cit_setkey(tfm, info->lo_encrypt_key,
+					   info->lo_encrypt_key_size);
+	
+	if (err != 0)
+		goto out_free_tfm;
+
+	lo->key_data = tfm;
+	return 0;
+
+ out_free_tfm:
+	crypto_free_tfm(tfm);
+
+ out:
+	return err;
+}
+
+typedef int (*encdec_t)(struct crypto_tfm *tfm,
+			struct scatterlist *sg_out,
+			struct scatterlist *sg_in,
+			unsigned int nsg, u8 *iv);
+
+static int
+cryptoloop_transfer(struct loop_device *lo, int cmd, char *raw_buf,
+		     char *loop_buf, int size, sector_t IV)
+{
+	struct crypto_tfm *tfm = (struct crypto_tfm *) lo->key_data;
+	struct scatterlist sg_out = { 0, };
+	struct scatterlist sg_in = { 0, };
+
+	encdec_t encdecfunc;
+	char const *in;
+	char *out;
+
+	if (cmd == READ) {
+		in = raw_buf;
+		out = loop_buf;
+		encdecfunc = tfm->crt_u.cipher.cit_decrypt_iv;
+	} else {
+		in = loop_buf;
+		out = raw_buf;
+		encdecfunc = tfm->crt_u.cipher.cit_encrypt_iv;
+	}
+
+	while (size > 0) {
+		const int sz = min(size, LOOP_IV_SECTOR_SIZE);
+		u32 iv[4] = { 0, };
+		iv[0] = cpu_to_le32(IV & 0xffffffff);
+
+		sg_in.page = virt_to_page(in);
+		sg_in.offset = (unsigned long)in & ~PAGE_MASK;
+		sg_in.length = sz;
+
+		sg_out.page = virt_to_page(out);
+		sg_out.offset = (unsigned long)out & ~PAGE_MASK;
+		sg_out.length = sz;
+
+		encdecfunc(tfm, &sg_out, &sg_in, sz, (u8 *)iv);
+
+		IV++;
+		size -= sz;
+		in += sz;
+		out += sz;
+	}
+
+	return 0;
+}
+
+
+static int
+cryptoloop_ioctl(struct loop_device *lo, int cmd, unsigned long arg)
+{
+	return -EINVAL;
+}
+
+static int
+cryptoloop_release(struct loop_device *lo)
+{
+	struct crypto_tfm *tfm = (struct crypto_tfm *) lo->key_data;
+	if (tfm != NULL) {
+		crypto_free_tfm(tfm);
+		lo->key_data = NULL;
+		return 0;
+	}
+	printk(KERN_ERR "cryptoloop_release(): tfm == NULL?\n");
+	return -EINVAL;
+}
+
+static struct loop_func_table cryptoloop_funcs = {
+	.number = LO_CRYPT_CRYPTOAPI,
+	.init = cryptoloop_init,
+	.ioctl = cryptoloop_ioctl,
+	.transfer = cryptoloop_transfer,
+	.release = cryptoloop_release,
+	/* .owner = THIS_MODULE */
+};
+
+static int __init
+init_cryptoloop(void)
+{
+	int rc = loop_register_transfer(&cryptoloop_funcs);
+
+	if (rc)
+		printk(KERN_ERR "cryptoloop: loop_register_transfer failed\n");
+	return rc;
+}
+
+static void __exit
+cleanup_cryptoloop(void)
+{
+	if (loop_unregister_transfer(LO_CRYPT_CRYPTOAPI))
+		printk(KERN_ERR
+			"cryptoloop: loop_unregister_transfer failed\n");
+}
+
+module_init(init_cryptoloop);
+module_exit(cleanup_cryptoloop);
diff -urN --exclude-from=diff-exclude linux-2.4.25/drivers/block/loop.c linux-2.4.25-leo/drivers/block/loop.c
--- linux-2.4.25/drivers/block/loop.c	2003-08-25 12:44:41.000000000 +0100
+++ linux-2.4.25-leo/drivers/block/loop.c	2004-02-20 18:21:53.000000000 +0000
@@ -88,7 +88,7 @@
  * Transfer functions
  */
 static int transfer_none(struct loop_device *lo, int cmd, char *raw_buf,
-			 char *loop_buf, int size, int real_block)
+			 char *loop_buf, int size, sector_t IV)
 {
 	if (raw_buf != loop_buf) {
 		if (cmd == READ)
@@ -101,7 +101,7 @@
 }
 
 static int transfer_xor(struct loop_device *lo, int cmd, char *raw_buf,
-			char *loop_buf, int size, int real_block)
+			char *loop_buf, int size, sector_t IV)
 {
 	char	*in, *out, *key;
 	int	i, keysize;
@@ -189,7 +189,7 @@
 	len = bh->b_size;
 	data = bh->b_data;
 	while (len > 0) {
-		int IV = index * (PAGE_CACHE_SIZE/bsize) + offset/bsize;
+		const sector_t IV = (index << (PAGE_CACHE_SHIFT - LOOP_IV_SECTOR_BITS)) + (offset >> LOOP_IV_SECTOR_BITS);
 		int transfer_result;
 
 		size = PAGE_CACHE_SIZE - offset;
@@ -249,7 +249,7 @@
 	unsigned long count = desc->count;
 	struct lo_read_data *p = (struct lo_read_data*)desc->buf;
 	struct loop_device *lo = p->lo;
-	int IV = page->index * (PAGE_CACHE_SIZE/p->bsize) + offset/p->bsize;
+	const sector_t IV = (page->index << (PAGE_CACHE_SHIFT - LOOP_IV_SECTOR_BITS)) + (offset >> LOOP_IV_SECTOR_BITS);
 
 	if (size > count)
 		size = count;
@@ -301,20 +301,6 @@
 	return bs;
 }
 
-static inline unsigned long loop_get_iv(struct loop_device *lo,
-					unsigned long sector)
-{
-	int bs = loop_get_bs(lo);
-	unsigned long offset, IV;
-
-	IV = sector / (bs >> 9) + lo->lo_offset / bs;
-	offset = ((sector % (bs >> 9)) << 9) + lo->lo_offset % bs;
-	if (offset >= bs)
-		IV++;
-
-	return IV;
-}
-
 static int do_bh_filebacked(struct loop_device *lo, struct buffer_head *bh, int rw)
 {
 	loff_t pos;
@@ -462,7 +448,7 @@
 {
 	struct buffer_head *bh = NULL;
 	struct loop_device *lo;
-	unsigned long IV;
+	sector_t IV;
 
 	if (!buffer_locked(rbh))
 		BUG();
@@ -507,7 +493,7 @@
 	 * piggy old buffer on original, and submit for I/O
 	 */
 	bh = loop_get_buffer(lo, rbh);
-	IV = loop_get_iv(lo, rbh->b_rsector);
+	IV = rbh->b_rsector + (lo->lo_offset >> LOOP_IV_SECTOR_BITS);
 	if (rw == WRITE) {
 		set_bit(BH_Dirty, &bh->b_state);
 		if (lo_do_transfer(lo, WRITE, bh->b_data, rbh->b_data,
@@ -544,7 +530,7 @@
 		bh->b_end_io(bh, !ret);
 	} else {
 		struct buffer_head *rbh = bh->b_private;
-		unsigned long IV = loop_get_iv(lo, rbh->b_rsector);
+		const sector_t IV = rbh->b_rsector + (lo->lo_offset >> LOOP_IV_SECTOR_BITS);
 
 		ret = lo_do_transfer(lo, READ, bh->b_data, rbh->b_data,
 				     bh->b_size, IV);
diff -urN --exclude-from=diff-exclude linux-2.4.25/drivers/block/Makefile linux-2.4.25-leo/drivers/block/Makefile
--- linux-2.4.25/drivers/block/Makefile	2003-06-13 15:51:32.000000000 +0100
+++ linux-2.4.25-leo/drivers/block/Makefile	2004-02-20 18:21:53.000000000 +0000
@@ -31,6 +31,7 @@
 obj-$(CONFIG_BLK_DEV_DAC960)	+= DAC960.o
 obj-$(CONFIG_BLK_DEV_UMEM)	+= umem.o
 obj-$(CONFIG_BLK_DEV_NBD)	+= nbd.o
+obj-$(CONFIG_BLK_DEV_CRYPTOLOOP) += cryptoloop.o
 
 subdir-$(CONFIG_PARIDE) += paride
 
diff -urN --exclude-from=diff-exclude linux-2.4.25/drivers/char/keyboard.c linux-2.4.25-leo/drivers/char/keyboard.c
--- linux-2.4.25/drivers/char/keyboard.c	2003-11-28 18:26:20.000000000 +0000
+++ linux-2.4.25-leo/drivers/char/keyboard.c	2004-02-20 18:22:22.000000000 +0000
@@ -545,6 +545,16 @@
 	if ((kbd->kbdmode == VC_RAW || kbd->kbdmode == VC_MEDIUMRAW) &&
 	    !(SPECIALS_ALLOWED_IN_RAW_MODE & (1 << value)))
 		return;
+
+#if defined(CONFIG_GRKERNSEC_PROC) || defined(CONFIG_GRKERNSEC_PROC_MEMMAP)
+	{
+		void *func = spec_fn_table[value];
+		if (func == show_state || func == show_ptregs ||
+		    func == show_mem)
+			return;
+	}
+#endif
+
 	spec_fn_table[value]();
 }
 
diff -urN --exclude-from=diff-exclude linux-2.4.25/drivers/char/mem.c linux-2.4.25-leo/drivers/char/mem.c
--- linux-2.4.25/drivers/char/mem.c	2003-11-28 18:26:20.000000000 +0000
+++ linux-2.4.25-leo/drivers/char/mem.c	2004-02-20 18:22:48.000000000 +0000
@@ -22,6 +22,7 @@
 #include <linux/tty.h>
 #include <linux/capability.h>
 #include <linux/ptrace.h>
+#include <linux/grsecurity.h>
 
 #include <asm/uaccess.h>
 #include <asm/io.h>
@@ -115,6 +116,11 @@
 	unsigned long p = *ppos;
 	unsigned long end_mem;
 
+#ifdef CONFIG_GRKERNSEC_KMEM
+	gr_handle_mem_write();
+	return -EPERM;
+#endif
+
 	end_mem = __pa(high_memory);
 	if (p >= end_mem)
 		return 0;
@@ -187,6 +193,12 @@
 {
 	unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
 
+#ifdef CONFIG_GRKERNSEC_KMEM
+	if (gr_handle_mem_mmap(offset, vma))
+		return -EPERM;
+#endif
+
+
 	/*
 	 * Accessing memory above the top the kernel knows about or
 	 * through a file pointer that was marked O_SYNC will be
@@ -286,6 +298,11 @@
 	ssize_t virtr = 0;
 	char * kbuf; /* k-addr because vwrite() takes vmlist_lock rwlock */
 
+#ifdef CONFIG_GRKERNSEC_KMEM
+	gr_handle_kmem_write();
+	return -EPERM;
+#endif
+
 	if (p < (unsigned long) high_memory) {
 		wrote = count;
 		if (count > (unsigned long) high_memory - p)
@@ -402,7 +419,7 @@
 			count = size;
 
 		zap_page_range(mm, addr, count);
-        	zeromap_page_range(addr, count, PAGE_COPY);
+	        zeromap_page_range(addr, count, vma->vm_page_prot); 
 
 		size -= count;
 		buf += count;
@@ -525,6 +542,15 @@
 
 static int open_port(struct inode * inode, struct file * filp)
 {
+#ifdef CONFIG_GRKERNSEC_PORT
+	gr_handle_open_port();
+	return -EPERM;
+#endif
+	return capable(CAP_SYS_RAWIO) ? 0 : -EPERM;
+}
+
+static int open_mem(struct inode * inode, struct file * filp)
+{
 	return capable(CAP_SYS_RAWIO) ? 0 : -EPERM;
 }
 
@@ -582,6 +608,11 @@
 	unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
 	unsigned long size = vma->vm_end - vma->vm_start;
 
+#ifdef CONFIG_GRKERNSEC_KMEM
+	if (gr_handle_mem_mmap(offset, vma))
+		return -EPERM;
+#endif
+
 	/*
 	 * If the user is not attempting to mmap a high memory address then
 	 * the standard mmap_mem mechanism will work.  High memory addresses
@@ -617,7 +648,6 @@
 #define full_lseek      null_lseek
 #define write_zero	write_null
 #define read_full       read_zero
-#define open_mem	open_port
 #define open_kmem	open_mem
 
 static struct file_operations mem_fops = {
diff -urN --exclude-from=diff-exclude linux-2.4.25/drivers/char/random.c linux-2.4.25-leo/drivers/char/random.c
--- linux-2.4.25/drivers/char/random.c	2004-02-20 14:11:41.000000000 +0000
+++ linux-2.4.25-leo/drivers/char/random.c	2004-02-20 18:22:22.000000000 +0000
@@ -262,9 +262,15 @@
 /*
  * Configuration information
  */
+#ifdef CONFIG_GRKERNSEC_RANDNET
+#define DEFAULT_POOL_SIZE 1024
+#define SECONDARY_POOL_SIZE 256
+#define BATCH_ENTROPY_SIZE 512
+#else
 #define DEFAULT_POOL_SIZE 512
 #define SECONDARY_POOL_SIZE 128
 #define BATCH_ENTROPY_SIZE 256
+#endif
 #define USE_SHA
 
 /*
@@ -389,6 +395,7 @@
 /*
  * Static global variables
  */
+
 static struct entropy_store *random_state; /* The default global store */
 static struct entropy_store *sec_random_state; /* secondary store */
 static DECLARE_WAIT_QUEUE_HEAD(random_read_wait);
@@ -2210,6 +2217,29 @@
 	return halfMD4Transform(hash, keyptr->secret);
 }
 
+#ifdef CONFIG_GRKERNSEC
+/* the following function is provided by PaX under the GPL */
+unsigned long get_random_long(void)
+{
+	static time_t rekey_time;
+	static __u32 secret[12];
+	time_t t;
+
+	/*
+	 * Pick a random secret every REKEY_INTERVAL seconds
+	 */
+	t = CURRENT_TIME;
+	if (!rekey_time || (t - rekey_time) > REKEY_INTERVAL) {
+		rekey_time = t;
+		get_random_bytes(secret, sizeof(secret));
+	}
+
+	secret[1] = halfMD4Transform(secret+8, secret);
+	secret[0] = halfMD4Transform(secret+8, secret);
+	return *(unsigned long *)secret;
+}
+#endif
+
 #ifdef CONFIG_SYN_COOKIES
 /*
  * Secure SYN cookie computation. This is the algorithm worked out by
diff -urN --exclude-from=diff-exclude linux-2.4.25/drivers/char/tty_io.c linux-2.4.25-leo/drivers/char/tty_io.c
--- linux-2.4.25/drivers/char/tty_io.c	2004-02-20 14:11:41.000000000 +0000
+++ linux-2.4.25-leo/drivers/char/tty_io.c	2004-02-20 18:22:22.000000000 +0000
@@ -99,7 +99,7 @@
 #include <linux/vt_kern.h>
 #include <linux/selection.h>
 #include <linux/devfs_fs_kernel.h>
-
+#include <linux/grsecurity.h>
 #include <linux/kmod.h>
 
 #ifdef CONFIG_VT
@@ -1404,7 +1404,11 @@
 		retval = -ENODEV;
 	filp->f_flags = saved_flags;
 
+#ifdef CONFIG_GRKERNSEC
+	if (!retval && test_bit(TTY_EXCLUSIVE, &tty->flags) && !capable(CAP_SYS_TTY_CONFIG))
+#else
 	if (!retval && test_bit(TTY_EXCLUSIVE, &tty->flags) && !suser())
+#endif
 		retval = -EBUSY;
 
 	if (retval) {
@@ -1506,7 +1510,11 @@
 {
 	char ch, mbz = 0;
 
+#ifdef CONFIG_GRKERNSEC
+	if ((current->tty != tty) && !capable(CAP_SYS_TTY_CONFIG))
+#else
 	if ((current->tty != tty) && !suser())
+#endif
 		return -EPERM;
 	if (get_user(ch, arg))
 		return -EFAULT;
@@ -1544,7 +1552,11 @@
 	if (inode->i_rdev == SYSCONS_DEV ||
 	    inode->i_rdev == CONSOLE_DEV) {
 		struct file *f;
+#ifdef CONFIG_GRKERNSEC
+		if (!capable(CAP_SYS_TTY_CONFIG))
+#else
 		if (!suser())
+#endif
 			return -EPERM;
 		spin_lock(&redirect_lock);
 		f = redirect;
@@ -1596,7 +1608,11 @@
 		 * This tty is already the controlling
 		 * tty for another session group!
 		 */
+#ifdef CONFIG_GRKERNSEC
+		if ((arg == 1) && capable(CAP_SYS_ADMIN)) {
+#else
 		if ((arg == 1) && suser()) {
+#endif
 			/*
 			 * Steal it away
 			 */
diff -urN --exclude-from=diff-exclude linux-2.4.25/drivers/char/vt.c linux-2.4.25-leo/drivers/char/vt.c
--- linux-2.4.25/drivers/char/vt.c	2002-11-28 23:53:12.000000000 +0000
+++ linux-2.4.25-leo/drivers/char/vt.c	2004-02-20 18:22:22.000000000 +0000
@@ -32,6 +32,7 @@
 #include <linux/vt_kern.h>
 #include <linux/kbd_diacr.h>
 #include <linux/selection.h>
+#include <linux/grsecurity.h>
 
 #ifdef CONFIG_FB_COMPAT_XPMAC
 #include <asm/vc_ioctl.h>
@@ -443,7 +444,11 @@
 	 * to be the owner of the tty, or super-user.
 	 */
 	perm = 0;
+#ifdef CONFIG_GRKERNSEC
+	if (current->tty == tty || capable(CAP_SYS_TTY_CONFIG))
+#else
 	if (current->tty == tty || suser())
+#endif
 		perm = 1;
  
 	kbd = kbd_table + console;
@@ -1038,12 +1043,20 @@
 		return do_unimap_ioctl(cmd, (struct unimapdesc *)arg, perm);
 
 	case VT_LOCKSWITCH:
+#ifdef CONFIG_GRKERNSEC
+		if (!capable(CAP_SYS_TTY_CONFIG))
+#else
 		if (!suser())
+#endif
 		   return -EPERM;
 		vt_dont_switch = 1;
 		return 0;
 	case VT_UNLOCKSWITCH:
+#ifdef CONFIG_GRKERNSEC
+		if (!capable(CAP_SYS_TTY_CONFIG))
+#else
 		if (!suser())
+#endif
 		   return -EPERM;
 		vt_dont_switch = 0;
 		return 0;
diff -urN --exclude-from=diff-exclude linux-2.4.25/drivers/ieee1394/csr.c linux-2.4.25-leo/drivers/ieee1394/csr.c
--- linux-2.4.25/drivers/ieee1394/csr.c	2004-02-20 14:11:42.000000000 +0000
+++ linux-2.4.25-leo/drivers/ieee1394/csr.c	2004-02-20 18:23:08.000000000 +0000
@@ -18,6 +18,7 @@
  */
 
 #include <linux/string.h>
+#include <linux/sched.h>
 #include <linux/module.h> /* needed for MODULE_PARM */
 #include <linux/param.h>
 #include <linux/spinlock.h>
diff -urN --exclude-from=diff-exclude linux-2.4.25/drivers/md/Config.in linux-2.4.25-leo/drivers/md/Config.in
--- linux-2.4.25/drivers/md/Config.in	2001-09-14 22:22:18.000000000 +0100
+++ linux-2.4.25-leo/drivers/md/Config.in	2004-02-20 18:22:56.000000000 +0000
@@ -12,7 +12,11 @@
 dep_tristate '  RAID-1 (mirroring) mode' CONFIG_MD_RAID1 $CONFIG_BLK_DEV_MD
 dep_tristate '  RAID-4/RAID-5 mode' CONFIG_MD_RAID5 $CONFIG_BLK_DEV_MD
 dep_tristate '  Multipath I/O support' CONFIG_MD_MULTIPATH $CONFIG_BLK_DEV_MD
+dep_bool     '  Verbose startup messages' CONFIG_MD_VERBMSG $CONFIG_BLK_DEV_MD
+
 
 dep_tristate ' Logical volume manager (LVM) support' CONFIG_BLK_DEV_LVM $CONFIG_MD
+dep_tristate ' Device-mapper support' CONFIG_BLK_DEV_DM $CONFIG_MD
+dep_tristate '  Mirror (RAID-1) support' CONFIG_BLK_DEV_DM_MIRROR $CONFIG_BLK_DEV_DM
 
 endmenu
diff -urN --exclude-from=diff-exclude linux-2.4.25/drivers/md/dm.c linux-2.4.25-leo/drivers/md/dm.c
--- linux-2.4.25/drivers/md/dm.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.25-leo/drivers/md/dm.c	2004-02-20 18:21:57.000000000 +0000
@@ -0,0 +1,1115 @@
+/*
+ * Copyright (C) 2001, 2002 Sistina Software (UK) Limited.
+ *
+ * This file is released under the GPL.
+ */
+
+#include "dm.h"
+#include "kcopyd.h"
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/blk.h>
+#include <linux/blkpg.h>
+#include <linux/mempool.h>
+#include <linux/slab.h>
+#include <linux/major.h>
+#include <linux/kdev_t.h>
+#include <linux/lvm.h>
+
+#include <asm/uaccess.h>
+
+static const char *_name = DM_NAME;
+#define DEFAULT_READ_AHEAD 64
+
+struct dm_io {
+	struct mapped_device *md;
+
+	struct dm_target *ti;
+	int rw;
+	union map_info map_context;
+	void (*end_io) (struct buffer_head * bh, int uptodate);
+	void *context;
+};
+
+struct deferred_io {
+	int rw;
+	struct buffer_head *bh;
+	struct deferred_io *next;
+};
+
+/*
+ * Bits for the md->flags field.
+ */
+#define DMF_BLOCK_IO 0
+#define DMF_SUSPENDED 1
+
+struct mapped_device {
+	struct rw_semaphore lock;
+	atomic_t holders;
+
+	kdev_t dev;
+	unsigned long flags;
+
+	/*
+	 * A list of ios that arrived while we were suspended.
+	 */
+	atomic_t pending;
+	wait_queue_head_t wait;
+	struct deferred_io *deferred;
+
+	/*
+	 * The current mapping.
+	 */
+	struct dm_table *map;
+
+	/*
+	 * io objects are allocated from here.
+	 */
+	mempool_t *io_pool;
+
+	/*
+	 * Event handling.
+	 */
+	uint32_t event_nr;
+	wait_queue_head_t eventq;
+};
+
+#define MIN_IOS 256
+static kmem_cache_t *_io_cache;
+
+static struct mapped_device *get_kdev(kdev_t dev);
+static int dm_request(request_queue_t *q, int rw, struct buffer_head *bh);
+static int dm_user_bmap(struct inode *inode, struct lv_bmap *lvb);
+
+/*-----------------------------------------------------------------
+ * In order to avoid the 256 minor number limit we are going to
+ * register more major numbers as neccessary.
+ *---------------------------------------------------------------*/
+#define MAX_MINORS (1 << MINORBITS)
+
+struct major_details {
+	unsigned int major;
+
+	int transient;
+	struct list_head transient_list;
+
+	unsigned int first_free_minor;
+	int nr_free_minors;
+
+	struct mapped_device *mds[MAX_MINORS];
+	int blk_size[MAX_MINORS];
+	int blksize_size[MAX_MINORS];
+	int hardsect_size[MAX_MINORS];
+};
+
+static struct rw_semaphore _dev_lock;
+static struct major_details *_majors[MAX_BLKDEV];
+
+/*
+ * This holds a list of majors that non-specified device numbers
+ * may be allocated from.  Only majors with free minors appear on
+ * this list.
+ */
+static LIST_HEAD(_transients_free);
+
+static int __alloc_major(unsigned int major, struct major_details **result)
+{
+	int r;
+	unsigned int transient = !major;
+	struct major_details *maj;
+
+	/* Major already allocated? */
+	if (major && _majors[major])
+		return 0;
+
+	maj = kmalloc(sizeof(*maj), GFP_KERNEL);
+	if (!maj)
+		return -ENOMEM;
+
+	memset(maj, 0, sizeof(*maj));
+	INIT_LIST_HEAD(&maj->transient_list);
+
+	maj->nr_free_minors = MAX_MINORS;
+
+	r = register_blkdev(major, _name, &dm_blk_dops);
+	if (r < 0) {
+		DMERR("register_blkdev failed for %d", major);
+		kfree(maj);
+		return r;
+	}
+	if (r > 0)
+		major = r;
+
+	maj->major = major;
+
+	if (transient) {
+		maj->transient = transient;
+		list_add_tail(&maj->transient_list, &_transients_free);
+	}
+
+	_majors[major] = maj;
+
+	blk_size[major] = maj->blk_size;
+	blksize_size[major] = maj->blksize_size;
+	hardsect_size[major] = maj->hardsect_size;
+	read_ahead[major] = DEFAULT_READ_AHEAD;
+
+	blk_queue_make_request(BLK_DEFAULT_QUEUE(major), dm_request);
+
+	*result = maj;
+	return 0;
+}
+
+static void __free_major(struct major_details *maj)
+{
+	unsigned int major = maj->major;
+
+	list_del(&maj->transient_list);
+
+	read_ahead[major] = 0;
+	blk_size[major] = NULL;
+	blksize_size[major] = NULL;
+	hardsect_size[major] = NULL;
+
+	_majors[major] = NULL;
+	kfree(maj);
+
+	if (unregister_blkdev(major, _name) < 0)
+		DMERR("devfs_unregister_blkdev failed");
+}
+
+static void free_all_majors(void)
+{
+	unsigned int major = ARRAY_SIZE(_majors);
+
+	down_write(&_dev_lock);
+
+	while (major--)
+		if (_majors[major])
+			__free_major(_majors[major]);
+
+	up_write(&_dev_lock);
+}
+
+static void free_dev(kdev_t dev)
+{
+	unsigned int major = major(dev);
+	unsigned int minor = minor(dev);
+	struct major_details *maj;
+
+	down_write(&_dev_lock);
+
+	maj = _majors[major];
+	if (!maj)
+		goto out;
+
+	maj->mds[minor] = NULL;
+	maj->nr_free_minors++;
+
+	if (maj->nr_free_minors == MAX_MINORS) {
+		__free_major(maj);
+		goto out;
+	}
+
+	if (!maj->transient)
+		goto out;
+
+	if (maj->nr_free_minors == 1)
+		list_add_tail(&maj->transient_list, &_transients_free);
+
+	if (minor < maj->first_free_minor)
+		maj->first_free_minor = minor;
+
+      out:
+	up_write(&_dev_lock);
+}
+
+static void __alloc_minor(struct major_details *maj, unsigned int minor,
+			  struct mapped_device *md)
+{
+	maj->mds[minor] = md;
+	md->dev = mk_kdev(maj->major, minor);
+	maj->nr_free_minors--;
+
+	if (maj->transient && !maj->nr_free_minors)
+		list_del_init(&maj->transient_list);
+}
+
+/*
+ * See if requested kdev_t is available.
+ */
+static int specific_dev(kdev_t dev, struct mapped_device *md)
+{
+	int r = 0;
+	unsigned int major = major(dev);
+	unsigned int minor = minor(dev);
+	struct major_details *maj;
+
+	if (!major || (major > MAX_BLKDEV) || (minor >= MAX_MINORS)) {
+		DMWARN("device number requested out of range (%d, %d)",
+		       major, minor);
+		return -EINVAL;
+	}
+
+	down_write(&_dev_lock);
+	maj = _majors[major];
+
+	/* Register requested major? */
+	if (!maj) {
+		r = __alloc_major(major, &maj);
+		if (r)
+			goto out;
+
+		major = maj->major;
+	}
+
+	if (maj->mds[minor]) {
+		r = -EBUSY;
+		goto out;
+	}
+
+	__alloc_minor(maj, minor, md);
+
+      out:
+	up_write(&_dev_lock);
+
+	return r;
+}
+
+/*
+ * Find first unused device number, requesting a new major number if required.
+ */
+static int first_free_dev(struct mapped_device *md)
+{
+	int r = 0;
+	struct major_details *maj;
+
+	down_write(&_dev_lock);
+
+	if (list_empty(&_transients_free)) {
+		r = __alloc_major(0, &maj);
+		if (r)
+			goto out;
+	} else
+		maj = list_entry(_transients_free.next, struct major_details,
+				 transient_list);
+
+	while (maj->mds[maj->first_free_minor++])
+		;
+
+	__alloc_minor(maj, maj->first_free_minor - 1, md);
+
+      out:
+	up_write(&_dev_lock);
+
+	return r;
+}
+
+static struct mapped_device *get_kdev(kdev_t dev)
+{
+	struct mapped_device *md;
+	struct major_details *maj;
+
+	down_read(&_dev_lock);
+	maj = _majors[major(dev)];
+	if (!maj) {
+		md = NULL;
+		goto out;
+	}
+	md = maj->mds[minor(dev)];
+	if (md)
+		dm_get(md);
+      out:
+	up_read(&_dev_lock);
+
+	return md;
+}
+
+/*-----------------------------------------------------------------
+ * init/exit code
+ *---------------------------------------------------------------*/
+
+static __init int local_init(void)
+{
+	init_rwsem(&_dev_lock);
+
+	/* allocate a slab for the dm_ios */
+	_io_cache = kmem_cache_create("dm io",
+				      sizeof(struct dm_io), 0, 0, NULL, NULL);
+
+	if (!_io_cache)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static void local_exit(void)
+{
+	kmem_cache_destroy(_io_cache);
+	free_all_majors();
+
+	DMINFO("cleaned up");
+}
+
+/*
+ * We have a lot of init/exit functions, so it seems easier to
+ * store them in an array.  The disposable macro 'xx'
+ * expands a prefix into a pair of function names.
+ */
+static struct {
+	int (*init) (void);
+	void (*exit) (void);
+
+} _inits[] = {
+#define xx(n) {n ## _init, n ## _exit},
+	xx(local)
+	xx(kcopyd)
+	xx(dm_target)
+	xx(dm_linear)
+	xx(dm_stripe)
+	xx(dm_snapshot)
+	xx(dm_interface)
+#undef xx
+};
+
+static int __init dm_init(void)
+{
+	const int count = ARRAY_SIZE(_inits);
+
+	int r, i;
+
+	for (i = 0; i < count; i++) {
+		r = _inits[i].init();
+		if (r)
+			goto bad;
+	}
+
+	return 0;
+
+      bad:
+	while (i--)
+		_inits[i].exit();
+
+	return r;
+}
+
+static void __exit dm_exit(void)
+{
+	int i = ARRAY_SIZE(_inits);
+
+	while (i--)
+		_inits[i].exit();
+}
+
+/*
+ * Block device functions
+ */
+static int dm_blk_open(struct inode *inode, struct file *file)
+{
+	struct mapped_device *md;
+
+	md = get_kdev(inode->i_rdev);
+	if (!md)
+		return -ENXIO;
+
+	return 0;
+}
+
+static int dm_blk_close(struct inode *inode, struct file *file)
+{
+	struct mapped_device *md;
+
+	md = get_kdev(inode->i_rdev);
+	dm_put(md);		/* put the reference gained by dm_blk_open */
+	dm_put(md);
+	return 0;
+}
+
+static inline struct dm_io *alloc_io(struct mapped_device *md)
+{
+	return mempool_alloc(md->io_pool, GFP_NOIO);
+}
+
+static inline void free_io(struct mapped_device *md, struct dm_io *io)
+{
+	mempool_free(io, md->io_pool);
+}
+
+static inline struct deferred_io *alloc_deferred(void)
+{
+	return kmalloc(sizeof(struct deferred_io), GFP_NOIO);
+}
+
+static inline void free_deferred(struct deferred_io *di)
+{
+	kfree(di);
+}
+
+static inline sector_t volume_size(kdev_t dev)
+{
+	return blk_size[major(dev)][minor(dev)] << 1;
+}
+
+/* FIXME: check this */
+static int dm_blk_ioctl(struct inode *inode, struct file *file,
+			unsigned int command, unsigned long a)
+{
+	kdev_t dev = inode->i_rdev;
+	long size;
+
+	switch (command) {
+	case BLKROSET:
+	case BLKROGET:
+	case BLKRASET:
+	case BLKRAGET:
+	case BLKFLSBUF:
+	case BLKSSZGET:
+		//case BLKRRPART: /* Re-read partition tables */
+		//case BLKPG:
+	case BLKELVGET:
+	case BLKELVSET:
+	case BLKBSZGET:
+	case BLKBSZSET:
+		return blk_ioctl(dev, command, a);
+		break;
+
+	case BLKGETSIZE:
+		size = volume_size(dev);
+		if (copy_to_user((void *) a, &size, sizeof(long)))
+			return -EFAULT;
+		break;
+
+	case BLKGETSIZE64:
+		size = volume_size(dev);
+		if (put_user((u64) ((u64) size) << 9, (u64 *) a))
+			return -EFAULT;
+		break;
+
+	case BLKRRPART:
+		return -ENOTTY;
+
+	case LV_BMAP:
+		return dm_user_bmap(inode, (struct lv_bmap *) a);
+
+	default:
+		DMWARN("unknown block ioctl 0x%x", command);
+		return -ENOTTY;
+	}
+
+	return 0;
+}
+
+/*
+ * Add the buffer to the list of deferred io.
+ */
+static int queue_io(struct mapped_device *md, struct buffer_head *bh, int rw)
+{
+	struct deferred_io *di;
+
+	di = alloc_deferred();
+	if (!di)
+		return -ENOMEM;
+
+	down_write(&md->lock);
+
+	if (!test_bit(DMF_BLOCK_IO, &md->flags)) {
+		up_write(&md->lock);
+		free_deferred(di);
+		return 1;
+	}
+
+	di->bh = bh;
+	di->rw = rw;
+	di->next = md->deferred;
+	md->deferred = di;
+
+	up_write(&md->lock);
+	return 0;		/* deferred successfully */
+}
+
+/*
+ * bh->b_end_io routine that decrements the pending count
+ * and then calls the original bh->b_end_io fn.
+ */
+static void dec_pending(struct buffer_head *bh, int uptodate)
+{
+	int r;
+	struct dm_io *io = bh->b_private;
+	dm_endio_fn endio = io->ti->type->end_io;
+
+	if (endio) {
+		r = endio(io->ti, bh, io->rw, uptodate ? 0 : -EIO,
+			  &io->map_context);
+		if (r < 0)
+			uptodate = 0;
+
+		else if (r > 0)
+			/* the target wants another shot at the io */
+			return;
+	}
+
+	if (atomic_dec_and_test(&io->md->pending))
+		/* nudge anyone waiting on suspend queue */
+		wake_up(&io->md->wait);
+
+	bh->b_end_io = io->end_io;
+	bh->b_private = io->context;
+	free_io(io->md, io);
+
+	bh->b_end_io(bh, uptodate);
+}
+
+/*
+ * Do the bh mapping for a given leaf
+ */
+static inline int __map_buffer(struct mapped_device *md, int rw,
+			       struct buffer_head *bh, struct dm_io *io)
+{
+	struct dm_target *ti;
+
+	if (!md->map)
+		return -EINVAL;
+
+	ti = dm_table_find_target(md->map, bh->b_rsector);
+	if (!ti->type)
+		return -EINVAL;
+
+	/* hook the end io request fn */
+	atomic_inc(&md->pending);
+	io->md = md;
+	io->ti = ti;
+	io->rw = rw;
+	io->end_io = bh->b_end_io;
+	io->context = bh->b_private;
+	bh->b_end_io = dec_pending;
+	bh->b_private = io;
+
+	return ti->type->map(ti, bh, rw, &io->map_context);
+}
+
+/*
+ * Checks to see if we should be deferring io, if so it queues it
+ * and returns 1.
+ */
+static inline int __deferring(struct mapped_device *md, int rw,
+			      struct buffer_head *bh)
+{
+	int r;
+
+	/*
+	 * If we're suspended we have to queue this io for later.
+	 */
+	while (test_bit(DMF_BLOCK_IO, &md->flags)) {
+		up_read(&md->lock);
+
+		/*
+		 * There's no point deferring a read ahead
+		 * request, just drop it.
+		 */
+		if (rw == READA) {
+			down_read(&md->lock);
+			return -EIO;
+		}
+
+		r = queue_io(md, bh, rw);
+		down_read(&md->lock);
+
+		if (r < 0)
+			return r;
+
+		if (r == 0)
+			return 1;	/* deferred successfully */
+
+	}
+
+	return 0;
+}
+
+static int dm_request(request_queue_t *q, int rw, struct buffer_head *bh)
+{
+	int r;
+	struct dm_io *io;
+	struct mapped_device *md;
+
+	md = get_kdev(bh->b_rdev);
+	if (!md) {
+		buffer_IO_error(bh);
+		return 0;
+	}
+
+	io = alloc_io(md);
+	down_read(&md->lock);
+
+	r = __deferring(md, rw, bh);
+	if (r < 0)
+		goto bad;
+
+	else if (!r) {
+		/* not deferring */
+		r = __map_buffer(md, rw, bh, io);
+		if (r < 0)
+			goto bad;
+	} else
+		r = 0;
+
+	up_read(&md->lock);
+	dm_put(md);
+	return r;
+
+      bad:
+	buffer_IO_error(bh);
+	up_read(&md->lock);
+	dm_put(md);
+	return 0;
+}
+
+static int check_dev_size(kdev_t dev, unsigned long block)
+{
+	unsigned int major = major(dev);
+	unsigned int minor = minor(dev);
+
+	/* FIXME: check this */
+	unsigned long max_sector = (blk_size[major][minor] << 1) + 1;
+	unsigned long sector = (block + 1) * (blksize_size[major][minor] >> 9);
+
+	return (sector > max_sector) ? 0 : 1;
+}
+
+/*
+ * Creates a dummy buffer head and maps it (for lilo).
+ */
+static int __bmap(struct mapped_device *md, kdev_t dev, unsigned long block,
+		  kdev_t *r_dev, unsigned long *r_block)
+{
+	struct buffer_head bh;
+	struct dm_target *ti;
+	union map_info map_context;
+	int r;
+
+	if (test_bit(DMF_BLOCK_IO, &md->flags)) {
+		return -EPERM;
+	}
+
+	if (!check_dev_size(dev, block)) {
+		return -EINVAL;
+	}
+
+	if (!md->map)
+		return -EINVAL;
+
+	/* setup dummy bh */
+	memset(&bh, 0, sizeof(bh));
+	bh.b_blocknr = block;
+	bh.b_dev = bh.b_rdev = dev;
+	bh.b_size = blksize_size[major(dev)][minor(dev)];
+	bh.b_rsector = block * (bh.b_size >> 9);
+
+	/* find target */
+	ti = dm_table_find_target(md->map, bh.b_rsector);
+
+	/* do the mapping */
+	r = ti->type->map(ti, &bh, READ, &map_context);
+	ti->type->end_io(ti, &bh, READ, 0, &map_context);
+
+	if (!r) {
+		*r_dev = bh.b_rdev;
+		*r_block = bh.b_rsector / (bh.b_size >> 9);
+	}
+
+	return r;
+}
+
+/*
+ * Marshals arguments and results between user and kernel space.
+ */
+static int dm_user_bmap(struct inode *inode, struct lv_bmap *lvb)
+{
+	struct mapped_device *md;
+	unsigned long block, r_block;
+	kdev_t r_dev;
+	int r;
+
+	if (get_user(block, &lvb->lv_block))
+		return -EFAULT;
+
+	md = get_kdev(inode->i_rdev);
+	if (!md)
+		return -ENXIO;
+
+	down_read(&md->lock);
+	r = __bmap(md, inode->i_rdev, block, &r_dev, &r_block);
+	up_read(&md->lock);
+	dm_put(md);
+
+	if (!r && (put_user(kdev_t_to_nr(r_dev), &lvb->lv_dev) ||
+		   put_user(r_block, &lvb->lv_block)))
+		r = -EFAULT;
+
+	return r;
+}
+
+static void free_md(struct mapped_device *md)
+{
+	free_dev(md->dev);
+	mempool_destroy(md->io_pool);
+	kfree(md);
+}
+
+/*
+ * Allocate and initialise a blank device with a given minor.
+ */
+static struct mapped_device *alloc_md(kdev_t dev)
+{
+	int r;
+	struct mapped_device *md = kmalloc(sizeof(*md), GFP_KERNEL);
+
+	if (!md) {
+		DMWARN("unable to allocate device, out of memory.");
+		return NULL;
+	}
+
+	memset(md, 0, sizeof(*md));
+
+	/* Allocate suitable device number */
+	if (!dev)
+		r = first_free_dev(md);
+	else
+		r = specific_dev(dev, md);
+
+	if (r) {
+		kfree(md);
+		return NULL;
+	}
+
+	md->io_pool = mempool_create(MIN_IOS, mempool_alloc_slab,
+				     mempool_free_slab, _io_cache);
+	if (!md->io_pool) {
+		free_md(md);
+		kfree(md);
+		return NULL;
+	}
+
+	init_rwsem(&md->lock);
+	atomic_set(&md->holders, 1);
+	atomic_set(&md->pending, 0);
+	init_waitqueue_head(&md->wait);
+	init_waitqueue_head(&md->eventq);
+
+	return md;
+}
+
+/*
+ * The hardsect size for a mapped device is the largest hardsect size
+ * from the devices it maps onto.
+ */
+static int __find_hardsect_size(struct list_head *devices)
+{
+	int result = 512, size;
+	struct list_head *tmp;
+
+	list_for_each (tmp, devices) {
+		struct dm_dev *dd = list_entry(tmp, struct dm_dev, list);
+		size = get_hardsect_size(dd->dev);
+		if (size > result)
+			result = size;
+	}
+
+	return result;
+}
+
+/*
+ * Bind a table to the device.
+ */
+static void event_callback(void *context)
+{
+	struct mapped_device *md = (struct mapped_device *) context;
+
+	down_write(&md->lock);
+	md->event_nr++;
+	wake_up_interruptible(&md->eventq);
+	up_write(&md->lock);
+}
+
+static int __bind(struct mapped_device *md, struct dm_table *t)
+{
+	unsigned int minor = minor(md->dev);
+	unsigned int major = major(md->dev);
+	md->map = t;
+
+	/* in k */
+	blk_size[major][minor] = dm_table_get_size(t) >> 1;
+	blksize_size[major][minor] = BLOCK_SIZE;
+	hardsect_size[major][minor] =
+	    __find_hardsect_size(dm_table_get_devices(t));
+	register_disk(NULL, md->dev, 1, &dm_blk_dops, blk_size[major][minor]);
+
+	dm_table_event_callback(md->map, event_callback, md);
+	dm_table_get(t);
+	return 0;
+}
+
+static void __unbind(struct mapped_device *md)
+{
+	unsigned int minor = minor(md->dev);
+	unsigned int major = major(md->dev);
+
+	if (md->map) {
+		dm_table_event_callback(md->map, NULL, NULL);
+		dm_table_put(md->map);
+		md->map = NULL;
+
+	}
+
+	blk_size[major][minor] = 0;
+	blksize_size[major][minor] = 0;
+	hardsect_size[major][minor] = 0;
+}
+
+/*
+ * Constructor for a new device.
+ */
+int dm_create(kdev_t dev, struct mapped_device **result)
+{
+	struct mapped_device *md;
+
+	md = alloc_md(dev);
+	if (!md)
+		return -ENXIO;
+
+	__unbind(md);	/* Ensure zero device size */
+
+	*result = md;
+	return 0;
+}
+
+void dm_get(struct mapped_device *md)
+{
+	atomic_inc(&md->holders);
+}
+
+void dm_put(struct mapped_device *md)
+{
+	if (atomic_dec_and_test(&md->holders)) {
+		if (md->map)
+			dm_table_suspend_targets(md->map);
+		__unbind(md);
+		free_md(md);
+	}
+}
+
+/*
+ * Requeue the deferred io by calling generic_make_request.
+ */
+static void flush_deferred_io(struct deferred_io *c)
+{
+	struct deferred_io *n;
+
+	while (c) {
+		n = c->next;
+		generic_make_request(c->rw, c->bh);
+		free_deferred(c);
+		c = n;
+	}
+}
+
+/*
+ * Swap in a new table (destroying old one).
+ */
+int dm_swap_table(struct mapped_device *md, struct dm_table *table)
+{
+	int r;
+
+	down_write(&md->lock);
+
+	/*
+	 * The device must be suspended, or have no table bound yet.
+	 */
+	if (md->map && !test_bit(DMF_SUSPENDED, &md->flags)) {
+		up_write(&md->lock);
+		return -EPERM;
+	}
+
+	__unbind(md);
+	r = __bind(md, table);
+	if (r)
+		return r;
+
+	up_write(&md->lock);
+	return 0;
+}
+
+/*
+ * We need to be able to change a mapping table under a mounted
+ * filesystem.  For example we might want to move some data in
+ * the background.  Before the table can be swapped with
+ * dm_bind_table, dm_suspend must be called to flush any in
+ * flight io and ensure that any further io gets deferred.
+ */
+int dm_suspend(struct mapped_device *md)
+{
+	int r = 0;
+	DECLARE_WAITQUEUE(wait, current);
+
+	down_write(&md->lock);
+
+	/*
+	 * First we set the BLOCK_IO flag so no more ios will be
+	 * mapped.
+	 */
+	if (test_bit(DMF_BLOCK_IO, &md->flags)) {
+		up_write(&md->lock);
+		return -EINVAL;
+	}
+
+	set_bit(DMF_BLOCK_IO, &md->flags);
+	add_wait_queue(&md->wait, &wait);
+	up_write(&md->lock);
+
+	/*
+	 * Then we wait for the already mapped ios to
+	 * complete.
+	 */
+	run_task_queue(&tq_disk);
+	while (1) {
+		set_current_state(TASK_INTERRUPTIBLE);
+
+		if (!atomic_read(&md->pending) || signal_pending(current))
+			break;
+
+		schedule();
+	}
+	set_current_state(TASK_RUNNING);
+
+	down_write(&md->lock);
+	remove_wait_queue(&md->wait, &wait);
+
+	/* did we flush everything ? */
+	if (atomic_read(&md->pending)) {
+		clear_bit(DMF_BLOCK_IO, &md->flags);
+		r = -EINTR;
+	} else {
+		set_bit(DMF_SUSPENDED, &md->flags);
+		if (md->map)
+			dm_table_suspend_targets(md->map);
+	}
+	up_write(&md->lock);
+
+	return r;
+}
+
+int dm_resume(struct mapped_device *md)
+{
+	struct deferred_io *def;
+
+	down_write(&md->lock);
+	if (!test_bit(DMF_SUSPENDED, &md->flags)) {
+		up_write(&md->lock);
+		return -EINVAL;
+	}
+
+	if (md->map)
+		dm_table_resume_targets(md->map);
+
+	clear_bit(DMF_SUSPENDED, &md->flags);
+	clear_bit(DMF_BLOCK_IO, &md->flags);
+	def = md->deferred;
+	md->deferred = NULL;
+	up_write(&md->lock);
+
+	flush_deferred_io(def);
+	run_task_queue(&tq_disk);
+
+	return 0;
+}
+
+struct dm_table *dm_get_table(struct mapped_device *md)
+{
+	struct dm_table *t;
+
+	down_read(&md->lock);
+	t = md->map;
+	if (t)
+		dm_table_get(t);
+	up_read(&md->lock);
+
+	return t;
+}
+
+/*-----------------------------------------------------------------
+ * Event notification.
+ *---------------------------------------------------------------*/
+uint32_t dm_get_event_nr(struct mapped_device *md)
+{
+	uint32_t r;
+
+	down_read(&md->lock);
+	r = md->event_nr;
+	up_read(&md->lock);
+
+	return r;
+}
+
+int dm_add_wait_queue(struct mapped_device *md, wait_queue_t *wq,
+		      uint32_t event_nr)
+{
+	down_write(&md->lock);
+	if (event_nr != md->event_nr) {
+		up_write(&md->lock);
+		return 1;
+	}
+
+	add_wait_queue(&md->eventq, wq);
+	up_write(&md->lock);
+
+	return 0;
+}
+
+const char *dm_kdevname(kdev_t dev)
+{
+	static char buffer[32];
+	sprintf(buffer, "%03d:%03d", MAJOR(dev), MINOR(dev));
+	return buffer;
+}
+
+void dm_remove_wait_queue(struct mapped_device *md, wait_queue_t *wq)
+{
+	down_write(&md->lock);
+	remove_wait_queue(&md->eventq, wq);
+	up_write(&md->lock);
+}
+
+kdev_t dm_kdev(struct mapped_device *md)
+{
+	kdev_t dev;
+
+	down_read(&md->lock);
+	dev = md->dev;
+	up_read(&md->lock);
+
+	return dev;
+}
+
+int dm_suspended(struct mapped_device *md)
+{
+	return test_bit(DMF_SUSPENDED, &md->flags);
+}
+
+struct block_device_operations dm_blk_dops = {
+	.open = dm_blk_open,
+	.release = dm_blk_close,
+	.ioctl = dm_blk_ioctl,
+	.owner = THIS_MODULE
+};
+
+/*
+ * module hooks
+ */
+module_init(dm_init);
+module_exit(dm_exit);
+
+MODULE_DESCRIPTION(DM_NAME " driver");
+MODULE_AUTHOR("Joe Thornber <thornber@sistina.com>");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(dm_kdevname);
diff -urN --exclude-from=diff-exclude linux-2.4.25/drivers/md/dm-daemon.c linux-2.4.25-leo/drivers/md/dm-daemon.c
--- linux-2.4.25/drivers/md/dm-daemon.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.25-leo/drivers/md/dm-daemon.c	2004-02-20 18:21:57.000000000 +0000
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2003 Sistina Software
+ *
+ * This file is released under the LGPL.
+ */
+
+#include "dm.h"
+#include "dm-daemon.h"
+
+#include <linux/module.h>
+#include <linux/sched.h>
+
+static int daemon(void *arg)
+{
+	struct dm_daemon *dd = (struct dm_daemon *) arg;
+	DECLARE_WAITQUEUE(wq, current);
+
+	daemonize();
+	reparent_to_init();
+
+	/* block all signals */
+	spin_lock_irq(&current->sigmask_lock);
+	sigfillset(&current->blocked);
+	flush_signals(current);
+	spin_unlock_irq(&current->sigmask_lock);
+
+	strcpy(current->comm, dd->name);
+	atomic_set(&dd->please_die, 0);
+
+	add_wait_queue(&dd->job_queue, &wq);
+
+	down(&dd->run_lock);
+	up(&dd->start_lock);
+
+	/*
+	 * dd->fn() could do anything, very likely it will
+	 * suspend.  So we can't set the state to
+	 * TASK_INTERRUPTIBLE before calling it.  In order to
+	 * prevent a race with a waking thread we do this little
+	 * dance with the dd->woken variable.
+	 */
+	while (1) {
+		do {
+			set_current_state(TASK_RUNNING);
+
+			if (atomic_read(&dd->please_die))
+				goto out;
+
+			atomic_set(&dd->woken, 0);
+			dd->fn();
+			yield();
+
+			set_current_state(TASK_INTERRUPTIBLE);
+		} while (atomic_read(&dd->woken));
+
+		schedule();
+	}
+
+ out:
+	remove_wait_queue(&dd->job_queue, &wq);
+	up(&dd->run_lock);
+	return 0;
+}
+
+int dm_daemon_start(struct dm_daemon *dd, const char *name, void (*fn)(void))
+{
+	pid_t pid = 0;
+
+	/*
+	 * Initialise the dm_daemon.
+	 */
+	dd->fn = fn;
+	strncpy(dd->name, name, sizeof(dd->name) - 1);
+	sema_init(&dd->start_lock, 1);
+	sema_init(&dd->run_lock, 1);
+	init_waitqueue_head(&dd->job_queue);
+
+	/*
+	 * Start the new thread.
+	 */
+	down(&dd->start_lock);
+	pid = kernel_thread(daemon, dd, 0);
+	if (pid <= 0) {
+		DMERR("Failed to start %s thread", name);
+		return -EAGAIN;
+	}
+
+	/*
+	 * wait for the daemon to up this mutex.
+	 */
+	down(&dd->start_lock);
+	up(&dd->start_lock);
+
+	return 0;
+}
+
+void dm_daemon_stop(struct dm_daemon *dd)
+{
+	atomic_set(&dd->please_die, 1);
+	dm_daemon_wake(dd);
+	down(&dd->run_lock);
+	up(&dd->run_lock);
+}
+
+void dm_daemon_wake(struct dm_daemon *dd)
+{
+	atomic_set(&dd->woken, 1);
+	wake_up_interruptible(&dd->job_queue);
+}
+
+EXPORT_SYMBOL(dm_daemon_start);
+EXPORT_SYMBOL(dm_daemon_stop);
+EXPORT_SYMBOL(dm_daemon_wake);
diff -urN --exclude-from=diff-exclude linux-2.4.25/drivers/md/dm-daemon.h linux-2.4.25-leo/drivers/md/dm-daemon.h
--- linux-2.4.25/drivers/md/dm-daemon.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.25-leo/drivers/md/dm-daemon.h	2004-02-20 18:37:07.000000000 +0000
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2003 Sistina Software
+ *
+ * This file is released under the LGPL.
+ */
+
+#ifndef DM_DAEMON_H
+#define DM_DAEMON_H
+
+#include <asm/atomic.h>
+#include <asm/semaphore.h>
+
+struct dm_daemon {
+	void (*fn)(void);
+	char name[16];
+	atomic_t please_die;
+	struct semaphore start_lock;
+	struct semaphore run_lock;
+
+	atomic_t woken;
+	wait_queue_head_t job_queue;
+};
+
+int dm_daemon_start(struct dm_daemon *dd, const char *name, void (*fn)(void));
+void dm_daemon_stop(struct dm_daemon *dd);
+void dm_daemon_wake(struct dm_daemon *dd);
+int dm_daemon_running(struct dm_daemon *dd);
+
+#endif
diff -urN --exclude-from=diff-exclude linux-2.4.25/drivers/md/dm-exception-store.c linux-2.4.25-leo/drivers/md/dm-exception-store.c
--- linux-2.4.25/drivers/md/dm-exception-store.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.25-leo/drivers/md/dm-exception-store.c	2004-02-20 18:21:57.000000000 +0000
@@ -0,0 +1,673 @@
+/*
+ * dm-snapshot.c
+ *
+ * Copyright (C) 2001-2002 Sistina Software (UK) Limited.
+ *
+ * This file is released under the GPL.
+ */
+
+#include "dm-snapshot.h"
+#include "dm-io.h"
+#include "kcopyd.h"
+
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+
+/*-----------------------------------------------------------------
+ * Persistent snapshots, by persistent we mean that the snapshot
+ * will survive a reboot.
+ *---------------------------------------------------------------*/
+
+/*
+ * We need to store a record of which parts of the origin have
+ * been copied to the snapshot device.  The snapshot code
+ * requires that we copy exception chunks to chunk aligned areas
+ * of the COW store.  It makes sense therefore, to store the
+ * metadata in chunk size blocks.
+ *
+ * There is no backward or forward compatibility implemented,
+ * snapshots with different disk versions than the kernel will
+ * not be usable.  It is expected that "lvcreate" will blank out
+ * the start of a fresh COW device before calling the snapshot
+ * constructor.
+ *
+ * The first chunk of the COW device just contains the header.
+ * After this there is a chunk filled with exception metadata,
+ * followed by as many exception chunks as can fit in the
+ * metadata areas.
+ *
+ * All on disk structures are in little-endian format.  The end
+ * of the exceptions info is indicated by an exception with a
+ * new_chunk of 0, which is invalid since it would point to the
+ * header chunk.
+ */
+
+/*
+ * Magic for persistent snapshots: "SnAp" - Feeble isn't it.
+ */
+#define SNAP_MAGIC 0x70416e53
+
+/*
+ * The on-disk version of the metadata.
+ */
+#define SNAPSHOT_DISK_VERSION 1
+
+struct disk_header {
+	uint32_t magic;
+
+	/*
+	 * Is this snapshot valid.  There is no way of recovering
+	 * an invalid snapshot.
+	 */
+	uint32_t valid;
+
+	/*
+	 * Simple, incrementing version. no backward
+	 * compatibility.
+	 */
+	uint32_t version;
+
+	/* In sectors */
+	uint32_t chunk_size;
+};
+
+struct disk_exception {
+	uint64_t old_chunk;
+	uint64_t new_chunk;
+};
+
+struct commit_callback {
+	void (*callback)(void *, int success);
+	void *context;
+};
+
+/*
+ * The top level structure for a persistent exception store.
+ */
+struct pstore {
+	struct dm_snapshot *snap;	/* up pointer to my snapshot */
+	int version;
+	int valid;
+	uint32_t chunk_size;
+	uint32_t exceptions_per_area;
+
+	/*
+	 * Now that we have an asynchronous kcopyd there is no
+	 * need for large chunk sizes, so it wont hurt to have a
+	 * whole chunks worth of metadata in memory at once.
+	 */
+	void *area;
+
+	/*
+	 * Used to keep track of which metadata area the data in
+	 * 'chunk' refers to.
+	 */
+	uint32_t current_area;
+
+	/*
+	 * The next free chunk for an exception.
+	 */
+	uint32_t next_free;
+
+	/*
+	 * The index of next free exception in the current
+	 * metadata area.
+	 */
+	uint32_t current_committed;
+
+	atomic_t pending_count;
+	uint32_t callback_count;
+	struct commit_callback *callbacks;
+};
+
+static inline unsigned int sectors_to_pages(unsigned int sectors)
+{
+	return sectors / (PAGE_SIZE / SECTOR_SIZE);
+}
+
+static int alloc_area(struct pstore *ps)
+{
+	int r = -ENOMEM;
+	size_t i, len, nr_pages;
+	struct page *page, *last = NULL;
+
+	len = ps->chunk_size << SECTOR_SHIFT;
+
+	/*
+	 * Allocate the chunk_size block of memory that will hold
+	 * a single metadata area.
+	 */
+	ps->area = vmalloc(len);
+	if (!ps->area)
+		return r;
+
+	nr_pages = sectors_to_pages(ps->chunk_size);
+
+	/*
+	 * We lock the pages for ps->area into memory since
+	 * they'll be doing a lot of io.  We also chain them
+	 * together ready for dm-io.
+	 */
+	for (i = 0; i < nr_pages; i++) {
+		page = vmalloc_to_page(ps->area + (i * PAGE_SIZE));
+		LockPage(page);
+		if (last)
+			last->list.next = &page->list;
+		last = page;
+	}
+
+	return 0;
+}
+
+static void free_area(struct pstore *ps)
+{
+	size_t i, nr_pages;
+	struct page *page;
+
+	nr_pages = sectors_to_pages(ps->chunk_size);
+	for (i = 0; i < nr_pages; i++) {
+		page = vmalloc_to_page(ps->area + (i * PAGE_SIZE));
+		page->list.next = NULL;
+		UnlockPage(page);
+	}
+
+	vfree(ps->area);
+}
+
+/*
+ * Read or write a chunk aligned and sized block of data from a device.
+ */
+static int chunk_io(struct pstore *ps, uint32_t chunk, int rw)
+{
+	struct io_region where;
+	unsigned int bits;
+
+	where.dev = ps->snap->cow->dev;
+	where.sector = ps->chunk_size * chunk;
+	where.count = ps->chunk_size;
+
+	return dm_io_sync(1, &where, rw, vmalloc_to_page(ps->area), 0, &bits);
+}
+
+/*
+ * Read or write a metadata area.  Remembering to skip the first
+ * chunk which holds the header.
+ */
+static int area_io(struct pstore *ps, uint32_t area, int rw)
+{
+	int r;
+	uint32_t chunk;
+
+	/* convert a metadata area index to a chunk index */
+	chunk = 1 + ((ps->exceptions_per_area + 1) * area);
+
+	r = chunk_io(ps, chunk, rw);
+	if (r)
+		return r;
+
+	ps->current_area = area;
+	return 0;
+}
+
+static int zero_area(struct pstore *ps, uint32_t area)
+{
+	memset(ps->area, 0, ps->chunk_size << SECTOR_SHIFT);
+	return area_io(ps, area, WRITE);
+}
+
+static int read_header(struct pstore *ps, int *new_snapshot)
+{
+	int r;
+	struct disk_header *dh;
+
+	r = chunk_io(ps, 0, READ);
+	if (r)
+		return r;
+
+	dh = (struct disk_header *) ps->area;
+
+	if (le32_to_cpu(dh->magic) == 0) {
+		*new_snapshot = 1;
+
+	} else if (le32_to_cpu(dh->magic) == SNAP_MAGIC) {
+		*new_snapshot = 0;
+		ps->valid = le32_to_cpu(dh->valid);
+		ps->version = le32_to_cpu(dh->version);
+		ps->chunk_size = le32_to_cpu(dh->chunk_size);
+
+	} else {
+		DMWARN("Invalid/corrupt snapshot");
+		r = -ENXIO;
+	}
+
+	return r;
+}
+
+static int write_header(struct pstore *ps)
+{
+	struct disk_header *dh;
+
+	memset(ps->area, 0, ps->chunk_size << SECTOR_SHIFT);
+
+	dh = (struct disk_header *) ps->area;
+	dh->magic = cpu_to_le32(SNAP_MAGIC);
+	dh->valid = cpu_to_le32(ps->valid);
+	dh->version = cpu_to_le32(ps->version);
+	dh->chunk_size = cpu_to_le32(ps->chunk_size);
+
+	return chunk_io(ps, 0, WRITE);
+}
+
+/*
+ * Access functions for the disk exceptions, these do the endian conversions.
+ */
+static struct disk_exception *get_exception(struct pstore *ps, uint32_t index)
+{
+	if (index >= ps->exceptions_per_area)
+		return NULL;
+
+	return ((struct disk_exception *) ps->area) + index;
+}
+
+static int read_exception(struct pstore *ps,
+			  uint32_t index, struct disk_exception *result)
+{
+	struct disk_exception *e;
+
+	e = get_exception(ps, index);
+	if (!e)
+		return -EINVAL;
+
+	/* copy it */
+	result->old_chunk = le64_to_cpu(e->old_chunk);
+	result->new_chunk = le64_to_cpu(e->new_chunk);
+
+	return 0;
+}
+
+static int write_exception(struct pstore *ps,
+			   uint32_t index, struct disk_exception *de)
+{
+	struct disk_exception *e;
+
+	e = get_exception(ps, index);
+	if (!e)
+		return -EINVAL;
+
+	/* copy it */
+	e->old_chunk = cpu_to_le64(de->old_chunk);
+	e->new_chunk = cpu_to_le64(de->new_chunk);
+
+	return 0;
+}
+
+/*
+ * Registers the exceptions that are present in the current area.
+ * 'full' is filled in to indicate if the area has been
+ * filled.
+ */
+static int insert_exceptions(struct pstore *ps, int *full)
+{
+	int r;
+	unsigned int i;
+	struct disk_exception de;
+
+	/* presume the area is full */
+	*full = 1;
+
+	for (i = 0; i < ps->exceptions_per_area; i++) {
+		r = read_exception(ps, i, &de);
+
+		if (r)
+			return r;
+
+		/*
+		 * If the new_chunk is pointing at the start of
+		 * the COW device, where the first metadata area
+		 * is we know that we've hit the end of the
+		 * exceptions.  Therefore the area is not full.
+		 */
+		if (de.new_chunk == 0LL) {
+			ps->current_committed = i;
+			*full = 0;
+			break;
+		}
+
+		/*
+		 * Keep track of the start of the free chunks.
+		 */
+		if (ps->next_free <= de.new_chunk)
+			ps->next_free = de.new_chunk + 1;
+
+		/*
+		 * Otherwise we add the exception to the snapshot.
+		 */
+		r = dm_add_exception(ps->snap, de.old_chunk, de.new_chunk);
+		if (r)
+			return r;
+	}
+
+	return 0;
+}
+
+static int read_exceptions(struct pstore *ps)
+{
+	uint32_t area;
+	int r, full = 1;
+
+	/*
+	 * Keeping reading chunks and inserting exceptions until
+	 * we find a partially full area.
+	 */
+	for (area = 0; full; area++) {
+		r = area_io(ps, area, READ);
+		if (r)
+			return r;
+
+		r = insert_exceptions(ps, &full);
+		if (r)
+			return r;
+	}
+
+	return 0;
+}
+
+static inline struct pstore *get_info(struct exception_store *store)
+{
+	return (struct pstore *) store->context;
+}
+
+static void persistent_fraction_full(struct exception_store *store,
+				     sector_t *numerator, sector_t *denominator)
+{
+	*numerator = get_info(store)->next_free * store->snap->chunk_size;
+	*denominator = get_dev_size(store->snap->cow->dev);
+}
+
+static void persistent_destroy(struct exception_store *store)
+{
+	struct pstore *ps = get_info(store);
+
+	dm_io_put(sectors_to_pages(ps->chunk_size));
+	vfree(ps->callbacks);
+	free_area(ps);
+	kfree(ps);
+}
+
+static int persistent_read_metadata(struct exception_store *store)
+{
+	int r, new_snapshot;
+	struct pstore *ps = get_info(store);
+
+	/*
+	 * Read the snapshot header.
+	 */
+	r = read_header(ps, &new_snapshot);
+	if (r)
+		return r;
+
+	/*
+	 * Do we need to setup a new snapshot ?
+	 */
+	if (new_snapshot) {
+		r = write_header(ps);
+		if (r) {
+			DMWARN("write_header failed");
+			return r;
+		}
+
+		r = zero_area(ps, 0);
+		if (r) {
+			DMWARN("zero_area(0) failed");
+			return r;
+		}
+
+	} else {
+		/*
+		 * Sanity checks.
+		 */
+		if (!ps->valid) {
+			DMWARN("snapshot is marked invalid");
+			return -EINVAL;
+		}
+
+		if (ps->version != SNAPSHOT_DISK_VERSION) {
+			DMWARN("unable to handle snapshot disk version %d",
+			       ps->version);
+			return -EINVAL;
+		}
+
+		/*
+		 * Read the metadata.
+		 */
+		r = read_exceptions(ps);
+		if (r)
+			return r;
+	}
+
+	return 0;
+}
+
+static int persistent_prepare(struct exception_store *store,
+			      struct exception *e)
+{
+	struct pstore *ps = get_info(store);
+	uint32_t stride;
+	sector_t size = get_dev_size(store->snap->cow->dev);
+
+	/* Is there enough room ? */
+	if (size < ((ps->next_free + 1) * store->snap->chunk_size))
+		return -ENOSPC;
+
+	e->new_chunk = ps->next_free;
+
+	/*
+	 * Move onto the next free pending, making sure to take
+	 * into account the location of the metadata chunks.
+	 */
+	stride = (ps->exceptions_per_area + 1);
+	if ((++ps->next_free % stride) == 1)
+		ps->next_free++;
+
+	atomic_inc(&ps->pending_count);
+	return 0;
+}
+
+static void persistent_commit(struct exception_store *store,
+			      struct exception *e,
+			      void (*callback) (void *, int success),
+			      void *callback_context)
+{
+	int r;
+	unsigned int i;
+	struct pstore *ps = get_info(store);
+	struct disk_exception de;
+	struct commit_callback *cb;
+
+	de.old_chunk = e->old_chunk;
+	de.new_chunk = e->new_chunk;
+	write_exception(ps, ps->current_committed++, &de);
+
+	/*
+	 * Add the callback to the back of the array.  This code
+	 * is the only place where the callback array is
+	 * manipulated, and we know that it will never be called
+	 * multiple times concurrently.
+	 */
+	cb = ps->callbacks + ps->callback_count++;
+	cb->callback = callback;
+	cb->context = callback_context;
+
+	/*
+	 * If there are no more exceptions in flight, or we have
+	 * filled this metadata area we commit the exceptions to
+	 * disk.
+	 */
+	if (atomic_dec_and_test(&ps->pending_count) ||
+	    (ps->current_committed == ps->exceptions_per_area)) {
+		r = area_io(ps, ps->current_area, WRITE);
+		if (r)
+			ps->valid = 0;
+
+		for (i = 0; i < ps->callback_count; i++) {
+			cb = ps->callbacks + i;
+			cb->callback(cb->context, r == 0 ? 1 : 0);
+		}
+
+		ps->callback_count = 0;
+	}
+
+	/*
+	 * Have we completely filled the current area ?
+	 */
+	if (ps->current_committed == ps->exceptions_per_area) {
+		ps->current_committed = 0;
+		r = zero_area(ps, ps->current_area + 1);
+		if (r)
+			ps->valid = 0;
+	}
+}
+
+static void persistent_drop(struct exception_store *store)
+{
+	struct pstore *ps = get_info(store);
+
+	ps->valid = 0;
+	if (write_header(ps))
+		DMWARN("write header failed");
+}
+
+int dm_create_persistent(struct exception_store *store, uint32_t chunk_size)
+{
+	int r;
+	struct pstore *ps;
+
+	r = dm_io_get(sectors_to_pages(chunk_size));
+	if (r)
+		return r;
+
+	/* allocate the pstore */
+	ps = kmalloc(sizeof(*ps), GFP_KERNEL);
+	if (!ps) {
+		r = -ENOMEM;
+		goto bad;
+	}
+
+	ps->snap = store->snap;
+	ps->valid = 1;
+	ps->version = SNAPSHOT_DISK_VERSION;
+	ps->chunk_size = chunk_size;
+	ps->exceptions_per_area = (chunk_size << SECTOR_SHIFT) /
+	    sizeof(struct disk_exception);
+	ps->next_free = 2;	/* skipping the header and first area */
+	ps->current_committed = 0;
+
+	r = alloc_area(ps);
+	if (r)
+		goto bad;
+
+	/*
+	 * Allocate space for all the callbacks.
+	 */
+	ps->callback_count = 0;
+	atomic_set(&ps->pending_count, 0);
+	ps->callbacks = vcalloc(ps->exceptions_per_area,
+				sizeof(*ps->callbacks));
+
+	if (!ps->callbacks) {
+		r = -ENOMEM;
+		goto bad;
+	}
+
+	store->destroy = persistent_destroy;
+	store->read_metadata = persistent_read_metadata;
+	store->prepare_exception = persistent_prepare;
+	store->commit_exception = persistent_commit;
+	store->drop_snapshot = persistent_drop;
+	store->fraction_full = persistent_fraction_full;
+	store->context = ps;
+
+	return 0;
+
+      bad:
+	dm_io_put(sectors_to_pages(chunk_size));
+	if (ps) {
+		if (ps->callbacks)
+			vfree(ps->callbacks);
+
+		kfree(ps);
+	}
+	return r;
+}
+
+/*-----------------------------------------------------------------
+ * Implementation of the store for non-persistent snapshots.
+ *---------------------------------------------------------------*/
+struct transient_c {
+	sector_t next_free;
+};
+
+void transient_destroy(struct exception_store *store)
+{
+	kfree(store->context);
+}
+
+int transient_read_metadata(struct exception_store *store)
+{
+	return 0;
+}
+
+int transient_prepare(struct exception_store *store, struct exception *e)
+{
+	struct transient_c *tc = (struct transient_c *) store->context;
+	sector_t size = get_dev_size(store->snap->cow->dev);
+
+	if (size < (tc->next_free + store->snap->chunk_size))
+		return -1;
+
+	e->new_chunk = sector_to_chunk(store->snap, tc->next_free);
+	tc->next_free += store->snap->chunk_size;
+
+	return 0;
+}
+
+void transient_commit(struct exception_store *store,
+		      struct exception *e,
+		      void (*callback) (void *, int success),
+		      void *callback_context)
+{
+	/* Just succeed */
+	callback(callback_context, 1);
+}
+
+static void transient_fraction_full(struct exception_store *store,
+				    sector_t *numerator, sector_t *denominator)
+{
+	*numerator = ((struct transient_c *) store->context)->next_free;
+	*denominator = get_dev_size(store->snap->cow->dev);
+}
+
+int dm_create_transient(struct exception_store *store,
+			struct dm_snapshot *s, int blocksize)
+{
+	struct transient_c *tc;
+
+	memset(store, 0, sizeof(*store));
+	store->destroy = transient_destroy;
+	store->read_metadata = transient_read_metadata;
+	store->prepare_exception = transient_prepare;
+	store->commit_exception = transient_commit;
+	store->fraction_full = transient_fraction_full;
+	store->snap = s;
+
+	tc = kmalloc(sizeof(struct transient_c), GFP_KERNEL);
+	if (!tc)
+		return -ENOMEM;
+
+	tc->next_free = 0;
+	store->context = tc;
+
+	return 0;
+}
diff -urN --exclude-from=diff-exclude linux-2.4.25/drivers/md/dm.h linux-2.4.25-leo/drivers/md/dm.h
--- linux-2.4.25/drivers/md/dm.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.25-leo/drivers/md/dm.h	2004-02-20 18:37:03.000000000 +0000
@@ -0,0 +1,175 @@
+/*
+ * Internal header file for device mapper
+ *
+ * Copyright (C) 2001, 2002 Sistina Software
+ *
+ * This file is released under the LGPL.
+ */
+
+#ifndef DM_INTERNAL_H
+#define DM_INTERNAL_H
+
+#include <linux/fs.h>
+#include <linux/device-mapper.h>
+#include <linux/list.h>
+#include <linux/blkdev.h>
+
+#define DM_NAME "device-mapper"
+#define DMWARN(f, x...) printk(KERN_WARNING DM_NAME ": " f "\n" , ## x)
+#define DMERR(f, x...) printk(KERN_ERR DM_NAME ": " f "\n" , ## x)
+#define DMINFO(f, x...) printk(KERN_INFO DM_NAME ": " f "\n" , ## x)
+
+/*
+ * FIXME: I think this should be with the definition of sector_t
+ * in types.h.
+ */
+#ifdef CONFIG_LBD
+#define SECTOR_FORMAT "%Lu"
+#else
+#define SECTOR_FORMAT "%lu"
+#endif
+
+#define SECTOR_SHIFT 9
+#define SECTOR_SIZE (1 << SECTOR_SHIFT)
+
+extern struct block_device_operations dm_blk_dops;
+
+/*
+ * List of devices that a metadevice uses and should open/close.
+ */
+struct dm_dev {
+	struct list_head list;
+
+	atomic_t count;
+	int mode;
+	kdev_t dev;
+	struct block_device *bdev;
+};
+
+struct dm_table;
+struct mapped_device;
+
+/*-----------------------------------------------------------------
+ * Functions for manipulating a struct mapped_device.
+ * Drop the reference with dm_put when you finish with the object.
+ *---------------------------------------------------------------*/
+int dm_create(kdev_t dev, struct mapped_device **md);
+
+/*
+ * Reference counting for md.
+ */
+void dm_get(struct mapped_device *md);
+void dm_put(struct mapped_device *md);
+
+/*
+ * A device can still be used while suspended, but I/O is deferred.
+ */
+int dm_suspend(struct mapped_device *md);
+int dm_resume(struct mapped_device *md);
+
+/*
+ * The device must be suspended before calling this method.
+ */
+int dm_swap_table(struct mapped_device *md, struct dm_table *t);
+
+/*
+ * Drop a reference on the table when you've finished with the
+ * result.
+ */
+struct dm_table *dm_get_table(struct mapped_device *md);
+
+/*
+ * Event functions.
+ */
+uint32_t dm_get_event_nr(struct mapped_device *md);
+int dm_add_wait_queue(struct mapped_device *md, wait_queue_t *wq,
+		      uint32_t event_nr);
+void dm_remove_wait_queue(struct mapped_device *md, wait_queue_t *wq);
+
+/*
+ * Info functions.
+ */
+kdev_t dm_kdev(struct mapped_device *md);
+int dm_suspended(struct mapped_device *md);
+
+/*-----------------------------------------------------------------
+ * Functions for manipulating a table.  Tables are also reference
+ * counted.
+ *---------------------------------------------------------------*/
+int dm_table_create(struct dm_table **result, int mode, unsigned num_targets);
+
+void dm_table_get(struct dm_table *t);
+void dm_table_put(struct dm_table *t);
+
+int dm_table_add_target(struct dm_table *t, const char *type,
+			sector_t start,	sector_t len, char *params);
+int dm_table_complete(struct dm_table *t);
+void dm_table_event_callback(struct dm_table *t,
+			     void (*fn)(void *), void *context);
+void dm_table_event(struct dm_table *t);
+sector_t dm_table_get_size(struct dm_table *t);
+struct dm_target *dm_table_get_target(struct dm_table *t, unsigned int index);
+struct dm_target *dm_table_find_target(struct dm_table *t, sector_t sector);
+unsigned int dm_table_get_num_targets(struct dm_table *t);
+struct list_head *dm_table_get_devices(struct dm_table *t);
+int dm_table_get_mode(struct dm_table *t);
+void dm_table_suspend_targets(struct dm_table *t);
+void dm_table_resume_targets(struct dm_table *t);
+
+/*-----------------------------------------------------------------
+ * A registry of target types.
+ *---------------------------------------------------------------*/
+int dm_target_init(void);
+void dm_target_exit(void);
+struct target_type *dm_get_target_type(const char *name);
+void dm_put_target_type(struct target_type *t);
+
+
+/*-----------------------------------------------------------------
+ * Useful inlines.
+ *---------------------------------------------------------------*/
+static inline int array_too_big(unsigned long fixed, unsigned long obj,
+				unsigned long num)
+{
+	return (num > (ULONG_MAX - fixed) / obj);
+}
+
+/*
+ * ceiling(n / size) * size
+ */
+static inline unsigned long dm_round_up(unsigned long n, unsigned long size)
+{
+	unsigned long r = n % size;
+	return n + (r ? (size - r) : 0);
+}
+
+/*
+ * Ceiling(n / size)
+ */
+static inline unsigned long dm_div_up(unsigned long n, unsigned long size)
+{
+	return dm_round_up(n, size) / size;
+}
+
+const char *dm_kdevname(kdev_t dev);
+
+/*
+ * The device-mapper can be driven through one of two interfaces;
+ * ioctl or filesystem, depending which patch you have applied.
+ */
+int dm_interface_init(void);
+void dm_interface_exit(void);
+
+/*
+ * Targets for linear and striped mappings
+ */
+int dm_linear_init(void);
+void dm_linear_exit(void);
+
+int dm_stripe_init(void);
+void dm_stripe_exit(void);
+
+int dm_snapshot_init(void);
+void dm_snapshot_exit(void);
+
+#endif
diff -urN --exclude-from=diff-exclude linux-2.4.25/drivers/md/dm-io.c linux-2.4.25-leo/drivers/md/dm-io.c
--- linux-2.4.25/drivers/md/dm-io.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.25-leo/drivers/md/dm-io.c	2004-02-20 18:21:57.000000000 +0000
@@ -0,0 +1,361 @@
+/*
+ * Copyright (C) 2003 Sistina Software
+ *
+ * This file is released under the GPL.
+ */
+
+#include "dm-io.h"
+
+#include <linux/mempool.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/bitops.h>
+
+/* FIXME: can we shrink this ? */
+struct io_context {
+	int rw;
+	unsigned int error;
+	atomic_t count;
+	struct task_struct *sleeper;
+	io_notify_fn callback;
+	void *context;
+};
+
+/*
+ * We maintain a pool of buffer heads for dispatching the io.
+ */
+static unsigned int _num_bhs;
+static mempool_t *_buffer_pool;
+
+/*
+ * io contexts are only dynamically allocated for asynchronous
+ * io.  Since async io is likely to be the majority of io we'll
+ * have the same number of io contexts as buffer heads ! (FIXME:
+ * must reduce this).
+ */
+mempool_t *_io_pool;
+
+static void *alloc_bh(int gfp_mask, void *pool_data)
+{
+	struct buffer_head *bh;
+
+	bh = kmem_cache_alloc(bh_cachep, gfp_mask);
+	if (bh) {
+		bh->b_reqnext = NULL;
+		init_waitqueue_head(&bh->b_wait);
+		INIT_LIST_HEAD(&bh->b_inode_buffers);
+	}
+
+	return bh;
+}
+
+static void *alloc_io(int gfp_mask, void *pool_data)
+{
+	return kmalloc(sizeof(struct io_context), gfp_mask);
+}
+
+static void free_io(void *element, void *pool_data)
+{
+	kfree(element);
+}
+
+static unsigned int pages_to_buffers(unsigned int pages)
+{
+	return 4 * pages;	/* too many ? */
+}
+
+static int resize_pool(unsigned int new_bhs)
+{
+	int r = 0;
+
+	if (_buffer_pool) {
+		if (new_bhs == 0) {
+			/* free off the pools */
+			mempool_destroy(_buffer_pool);
+			mempool_destroy(_io_pool);
+			_buffer_pool = _io_pool = NULL;
+		} else {
+			/* resize the pools */
+			r = mempool_resize(_buffer_pool, new_bhs, GFP_KERNEL);
+			if (!r)
+				r = mempool_resize(_io_pool,
+						   new_bhs, GFP_KERNEL);
+		}
+	} else {
+		/* create new pools */
+		_buffer_pool = mempool_create(new_bhs, alloc_bh,
+					      mempool_free_slab, bh_cachep);
+		if (!_buffer_pool)
+			r = -ENOMEM;
+
+		_io_pool = mempool_create(new_bhs, alloc_io, free_io, NULL);
+		if (!_io_pool) {
+			mempool_destroy(_buffer_pool);
+			_buffer_pool = NULL;
+			r = -ENOMEM;
+		}
+	}
+
+	if (!r)
+		_num_bhs = new_bhs;
+
+	return r;
+}
+
+int dm_io_get(unsigned int num_pages)
+{
+	return resize_pool(_num_bhs + pages_to_buffers(num_pages));
+}
+
+void dm_io_put(unsigned int num_pages)
+{
+	resize_pool(_num_bhs - pages_to_buffers(num_pages));
+}
+
+/*-----------------------------------------------------------------
+ * We need to keep track of which region a buffer is doing io
+ * for.  In order to save a memory allocation we store this in an
+ * unused field of the buffer head, and provide these access
+ * functions.
+ *
+ * FIXME: add compile time check that an unsigned int can fit
+ * into a pointer.
+ *
+ *---------------------------------------------------------------*/
+static inline void bh_set_region(struct buffer_head *bh, unsigned int region)
+{
+	bh->b_journal_head = (void *) region;
+}
+
+static inline int bh_get_region(struct buffer_head *bh)
+{
+	return (unsigned int) bh->b_journal_head;
+}
+
+/*-----------------------------------------------------------------
+ * We need an io object to keep track of the number of bhs that
+ * have been dispatched for a particular io.
+ *---------------------------------------------------------------*/
+static void dec_count(struct io_context *io, unsigned int region, int error)
+{
+	if (error)
+		set_bit(region, &io->error);
+
+	if (atomic_dec_and_test(&io->count)) {
+		if (io->sleeper)
+			wake_up_process(io->sleeper);
+
+		else {
+			int r = io->error;
+			io_notify_fn fn = io->callback;
+			void *context = io->context;
+
+			mempool_free(io, _io_pool);
+			fn(r, context);
+		}
+	}
+}
+
+static void endio(struct buffer_head *bh, int uptodate)
+{
+	struct io_context *io = (struct io_context *) bh->b_private;
+
+	if (!uptodate && io->rw != WRITE) {
+		/*
+		 * We need to zero this region, otherwise people
+		 * like kcopyd may write the arbitrary contents
+		 * of the page.
+		 */
+		memset(bh->b_data, 0, bh->b_size);
+	}
+
+	dec_count((struct io_context *) bh->b_private,
+		  bh_get_region(bh), !uptodate);
+	mempool_free(bh, _buffer_pool);
+}
+
+/*
+ * Primitives for alignment calculations.
+ */
+int fls(unsigned n)
+{
+	return generic_fls32(n);
+}
+
+static inline int log2_floor(unsigned n)
+{
+	return ffs(n) - 1;
+}
+
+static inline int log2_align(unsigned n)
+{
+	return fls(n) - 1;
+}
+
+/*
+ * Returns the next block for io.
+ */
+static int do_page(kdev_t dev, sector_t *block, sector_t end_block,
+		   unsigned int block_size,
+		   struct page *p, unsigned int offset,
+		   unsigned int region, struct io_context *io)
+{
+	struct buffer_head *bh;
+	sector_t b = *block;
+	sector_t blocks_per_page = PAGE_SIZE / block_size;
+	unsigned int this_size; /* holds the size of the current io */
+	sector_t len;
+
+	if (!blocks_per_page) {
+		DMERR("dm-io: PAGE_SIZE (%lu) < block_size (%u) unsupported",
+		      PAGE_SIZE, block_size);
+		return 0;
+	}
+
+	while ((offset < PAGE_SIZE) && (b != end_block)) {
+		bh = mempool_alloc(_buffer_pool, GFP_NOIO);
+		init_buffer(bh, endio, io);
+		bh_set_region(bh, region);
+
+		/*
+		 * Block size must be a power of 2 and aligned
+		 * correctly.
+		 */
+
+		len = min(end_block - b, blocks_per_page);
+		len = min(len, blocks_per_page - offset / block_size);
+
+		if (!len) {
+			DMERR("dm-io: Invalid offset/block_size (%u/%u).",
+			      offset, block_size);
+			return 0;
+		}
+
+		this_size = 1 << log2_align(len);
+		if (b)
+			this_size = min(this_size,
+					(unsigned) 1 << log2_floor(b));
+
+		/*
+		 * Add in the job offset.
+		 */
+		bh->b_blocknr = (b / this_size);
+		bh->b_size = block_size * this_size;
+		set_bh_page(bh, p, offset);
+		bh->b_this_page = bh;
+
+		bh->b_dev = dev;
+		atomic_set(&bh->b_count, 1);
+
+		bh->b_state = ((1 << BH_Uptodate) | (1 << BH_Mapped) |
+			       (1 << BH_Lock));
+
+		if (io->rw == WRITE)
+			clear_bit(BH_Dirty, &bh->b_state);
+
+		atomic_inc(&io->count);
+		submit_bh(io->rw, bh);
+
+		b += this_size;
+		offset += block_size * this_size;
+	}
+
+	*block = b;
+	return (b == end_block);
+}
+
+static void do_region(unsigned int region, struct io_region *where,
+		      struct page *page, unsigned int offset,
+		      struct io_context *io)
+{
+	unsigned int block_size = get_hardsect_size(where->dev);
+	unsigned int sblock_size = block_size >> 9;
+	sector_t block = where->sector / sblock_size;
+	sector_t end_block = (where->sector + where->count) / sblock_size;
+
+	while (1) {
+		if (do_page(where->dev, &block, end_block, block_size,
+			    page, offset, region, io))
+			break;
+
+		offset = 0;	/* only offset the first page */
+
+		page = list_entry(page->list.next, struct page, list);
+	}
+}
+
+static void dispatch_io(unsigned int num_regions, struct io_region *where,
+			struct page *pages, unsigned int offset,
+			struct io_context *io)
+{
+	int i;
+
+	for (i = 0; i < num_regions; i++)
+		if (where[i].count)
+			do_region(i, where + i, pages, offset, io);
+
+	/*
+	 * Drop the extra refence that we were holding to avoid
+	 * the io being completed too early.
+	 */
+	dec_count(io, 0, 0);
+}
+
+/*
+ * Synchronous io
+ */
+int dm_io_sync(unsigned int num_regions, struct io_region *where,
+	       int rw, struct page *pages, unsigned int offset,
+	       unsigned int *error_bits)
+{
+	struct io_context io;
+
+	BUG_ON(num_regions > 1 && rw != WRITE);
+
+	io.rw = rw;
+	io.error = 0;
+	atomic_set(&io.count, 1); /* see dispatch_io() */
+	io.sleeper = current;
+
+	dispatch_io(num_regions, where, pages, offset, &io);
+	run_task_queue(&tq_disk);
+
+	while (1) {
+		set_current_state(TASK_UNINTERRUPTIBLE);
+
+		if (!atomic_read(&io.count))
+			break;
+
+		schedule();
+	}
+	set_current_state(TASK_RUNNING);
+
+	*error_bits = io.error;
+	return io.error ? -EIO : 0;
+}
+
+/*
+ * Asynchronous io
+ */
+int dm_io_async(unsigned int num_regions, struct io_region *where, int rw,
+		struct page *pages, unsigned int offset,
+		io_notify_fn fn, void *context)
+{
+	struct io_context *io = mempool_alloc(_io_pool, GFP_NOIO);
+
+	io->rw = rw;
+	io->error = 0;
+	atomic_set(&io->count, 1); /* see dispatch_io() */
+	io->sleeper = NULL;
+	io->callback = fn;
+	io->context = context;
+
+	dispatch_io(num_regions, where, pages, offset, io);
+	return 0;
+}
+
+EXPORT_SYMBOL(dm_io_get);
+EXPORT_SYMBOL(dm_io_put);
+EXPORT_SYMBOL(dm_io_sync);
+EXPORT_SYMBOL(dm_io_async);
diff -urN --exclude-from=diff-exclude linux-2.4.25/drivers/md/dm-ioctl.c linux-2.4.25-leo/drivers/md/dm-ioctl.c
--- linux-2.4.25/drivers/md/dm-ioctl.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.25-leo/drivers/md/dm-ioctl.c	2004-02-20 18:21:57.000000000 +0000
@@ -0,0 +1,1284 @@
+/*
+ * Copyright (C) 2001, 2002 Sistina Software (UK) Limited.
+ *
+ * This file is released under the GPL.
+ */
+
+#include "dm.h"
+
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+#include <linux/miscdevice.h>
+#include <linux/dm-ioctl.h>
+#include <linux/init.h>
+#include <linux/wait.h>
+#include <linux/blk.h>
+#include <linux/slab.h>
+
+#include <asm/uaccess.h>
+
+#define DM_DRIVER_EMAIL "dm@uk.sistina.com"
+
+/*-----------------------------------------------------------------
+ * The ioctl interface needs to be able to look up devices by
+ * name or uuid.
+ *---------------------------------------------------------------*/
+struct hash_cell {
+	struct list_head name_list;
+	struct list_head uuid_list;
+
+	char *name;
+	char *uuid;
+	struct mapped_device *md;
+	struct dm_table *new_map;
+
+	/* I hate devfs */
+	devfs_handle_t devfs_entry;
+};
+
+#define NUM_BUCKETS 64
+#define MASK_BUCKETS (NUM_BUCKETS - 1)
+static struct list_head _name_buckets[NUM_BUCKETS];
+static struct list_head _uuid_buckets[NUM_BUCKETS];
+
+static devfs_handle_t _dev_dir;
+void dm_hash_remove_all(void);
+
+/*
+ * Guards access to both hash tables.
+ */
+static DECLARE_RWSEM(_hash_lock);
+
+static void init_buckets(struct list_head *buckets)
+{
+	unsigned int i;
+
+	for (i = 0; i < NUM_BUCKETS; i++)
+		INIT_LIST_HEAD(buckets + i);
+}
+
+int dm_hash_init(void)
+{
+	init_buckets(_name_buckets);
+	init_buckets(_uuid_buckets);
+	_dev_dir = devfs_mk_dir(0, DM_DIR, NULL);
+	return 0;
+}
+
+void dm_hash_exit(void)
+{
+	dm_hash_remove_all();
+	devfs_unregister(_dev_dir);
+}
+
+/*-----------------------------------------------------------------
+ * Hash function:
+ * We're not really concerned with the str hash function being
+ * fast since it's only used by the ioctl interface.
+ *---------------------------------------------------------------*/
+static unsigned int hash_str(const char *str)
+{
+	const unsigned int hash_mult = 2654435387U;
+	unsigned int h = 0;
+
+	while (*str)
+		h = (h + (unsigned int) *str++) * hash_mult;
+
+	return h & MASK_BUCKETS;
+}
+
+/*-----------------------------------------------------------------
+ * Code for looking up a device by name
+ *---------------------------------------------------------------*/
+static struct hash_cell *__get_name_cell(const char *str)
+{
+	struct list_head *tmp;
+	struct hash_cell *hc;
+	unsigned int h = hash_str(str);
+
+	list_for_each (tmp, _name_buckets + h) {
+		hc = list_entry(tmp, struct hash_cell, name_list);
+		if (!strcmp(hc->name, str))
+			return hc;
+	}
+
+	return NULL;
+}
+
+static struct hash_cell *__get_uuid_cell(const char *str)
+{
+	struct list_head *tmp;
+	struct hash_cell *hc;
+	unsigned int h = hash_str(str);
+
+	list_for_each (tmp, _uuid_buckets + h) {
+		hc = list_entry(tmp, struct hash_cell, uuid_list);
+		if (!strcmp(hc->uuid, str))
+			return hc;
+	}
+
+	return NULL;
+}
+
+/*-----------------------------------------------------------------
+ * Inserting, removing and renaming a device.
+ *---------------------------------------------------------------*/
+static inline char *kstrdup(const char *str)
+{
+	char *r = kmalloc(strlen(str) + 1, GFP_KERNEL);
+	if (r)
+		strcpy(r, str);
+	return r;
+}
+
+static struct hash_cell *alloc_cell(const char *name, const char *uuid,
+				    struct mapped_device *md)
+{
+	struct hash_cell *hc;
+
+	hc = kmalloc(sizeof(*hc), GFP_KERNEL);
+	if (!hc)
+		return NULL;
+
+	hc->name = kstrdup(name);
+	if (!hc->name) {
+		kfree(hc);
+		return NULL;
+	}
+
+	if (!uuid)
+		hc->uuid = NULL;
+
+	else {
+		hc->uuid = kstrdup(uuid);
+		if (!hc->uuid) {
+			kfree(hc->name);
+			kfree(hc);
+			return NULL;
+		}
+	}
+
+	INIT_LIST_HEAD(&hc->name_list);
+	INIT_LIST_HEAD(&hc->uuid_list);
+	hc->md = md;
+	hc->new_map = NULL;
+	return hc;
+}
+
+static void free_cell(struct hash_cell *hc)
+{
+	if (hc) {
+		kfree(hc->name);
+		kfree(hc->uuid);
+		kfree(hc);
+	}
+}
+
+/*
+ * devfs stuff.
+ */
+static int register_with_devfs(struct hash_cell *hc)
+{
+	kdev_t dev = dm_kdev(hc->md);
+
+	hc->devfs_entry =
+	    devfs_register(_dev_dir, hc->name, DEVFS_FL_CURRENT_OWNER,
+			   major(dev), minor(dev),
+			   S_IFBLK | S_IRUSR | S_IWUSR | S_IRGRP,
+			   &dm_blk_dops, NULL);
+
+	return 0;
+}
+
+static int unregister_with_devfs(struct hash_cell *hc)
+{
+	devfs_unregister(hc->devfs_entry);
+	return 0;
+}
+
+/*
+ * The kdev_t and uuid of a device can never change once it is
+ * initially inserted.
+ */
+int dm_hash_insert(const char *name, const char *uuid, struct mapped_device *md)
+{
+	struct hash_cell *cell;
+
+	/*
+	 * Allocate the new cells.
+	 */
+	cell = alloc_cell(name, uuid, md);
+	if (!cell)
+		return -ENOMEM;
+
+	/*
+	 * Insert the cell into both hash tables.
+	 */
+	down_write(&_hash_lock);
+	if (__get_name_cell(name))
+		goto bad;
+
+	list_add(&cell->name_list, _name_buckets + hash_str(name));
+
+	if (uuid) {
+		if (__get_uuid_cell(uuid)) {
+			list_del(&cell->name_list);
+			goto bad;
+		}
+		list_add(&cell->uuid_list, _uuid_buckets + hash_str(uuid));
+	}
+	register_with_devfs(cell);
+	dm_get(md);
+	up_write(&_hash_lock);
+
+	return 0;
+
+      bad:
+	up_write(&_hash_lock);
+	free_cell(cell);
+	return -EBUSY;
+}
+
+void __hash_remove(struct hash_cell *hc)
+{
+	/* remove from the dev hash */
+	list_del(&hc->uuid_list);
+	list_del(&hc->name_list);
+	unregister_with_devfs(hc);
+	dm_put(hc->md);
+	if (hc->new_map)
+		dm_table_put(hc->new_map);
+	free_cell(hc);
+}
+
+void dm_hash_remove_all(void)
+{
+	int i;
+	struct hash_cell *hc;
+	struct list_head *tmp, *n;
+
+	down_write(&_hash_lock);
+	for (i = 0; i < NUM_BUCKETS; i++) {
+		list_for_each_safe (tmp, n, _name_buckets + i) {
+			hc = list_entry(tmp, struct hash_cell, name_list);
+			__hash_remove(hc);
+		}
+	}
+	up_write(&_hash_lock);
+}
+
+int dm_hash_rename(const char *old, const char *new)
+{
+	char *new_name, *old_name;
+	struct hash_cell *hc;
+
+	/*
+	 * duplicate new.
+	 */
+	new_name = kstrdup(new);
+	if (!new_name)
+		return -ENOMEM;
+
+	down_write(&_hash_lock);
+
+	/*
+	 * Is new free ?
+	 */
+	hc = __get_name_cell(new);
+	if (hc) {
+		DMWARN("asked to rename to an already existing name %s -> %s",
+		       old, new);
+		up_write(&_hash_lock);
+		kfree(new_name);
+		return -EBUSY;
+	}
+
+	/*
+	 * Is there such a device as 'old' ?
+	 */
+	hc = __get_name_cell(old);
+	if (!hc) {
+		DMWARN("asked to rename a non existent device %s -> %s",
+		       old, new);
+		up_write(&_hash_lock);
+		kfree(new_name);
+		return -ENXIO;
+	}
+
+	/*
+	 * rename and move the name cell.
+	 */
+	list_del(&hc->name_list);
+	old_name = hc->name;
+	hc->name = new_name;
+	list_add(&hc->name_list, _name_buckets + hash_str(new_name));
+
+	/* rename the device node in devfs */
+	unregister_with_devfs(hc);
+	register_with_devfs(hc);
+
+	up_write(&_hash_lock);
+	kfree(old_name);
+	return 0;
+}
+
+/*-----------------------------------------------------------------
+ * Implementation of the ioctl commands
+ *---------------------------------------------------------------*/
+/*
+ * All the ioctl commands get dispatched to functions with this
+ * prototype.
+ */
+typedef int (*ioctl_fn)(struct dm_ioctl *param, size_t param_size);
+
+static int remove_all(struct dm_ioctl *param, size_t param_size)
+{
+	dm_hash_remove_all();
+	param->data_size = 0;
+	return 0;
+}
+
+/*
+ * Round up the ptr to an 8-byte boundary.
+ */
+#define ALIGN_MASK 7
+static inline void *align_ptr(void *ptr)
+{
+	return (void *) (((size_t) (ptr + ALIGN_MASK)) & ~ALIGN_MASK);
+}
+
+/*
+ * Retrieves the data payload buffer from an already allocated
+ * struct dm_ioctl.
+ */
+static void *get_result_buffer(struct dm_ioctl *param, size_t param_size,
+			       size_t *len)
+{
+	param->data_start = align_ptr(param + 1) - (void *) param;
+
+	if (param->data_start < param_size)
+		*len = param_size - param->data_start;
+	else
+		*len = 0;
+
+	return ((void *) param) + param->data_start;
+}
+
+static int list_devices(struct dm_ioctl *param, size_t param_size)
+{
+	unsigned int i;
+	struct hash_cell *hc;
+	size_t len, needed = 0;
+	struct dm_name_list *nl, *old_nl = NULL;
+
+	down_write(&_hash_lock);
+
+	/*
+	 * Loop through all the devices working out how much
+	 * space we need.
+	 */
+	for (i = 0; i < NUM_BUCKETS; i++) {
+		list_for_each_entry (hc, _name_buckets + i, name_list) {
+			needed += sizeof(struct dm_name_list);
+			needed += strlen(hc->name);
+			needed += ALIGN_MASK;
+		}
+	}
+
+	/*
+	 * Grab our output buffer.
+	 */
+	nl = get_result_buffer(param, param_size, &len);
+	if (len < needed) {
+		param->flags |= DM_BUFFER_FULL_FLAG;
+		goto out;
+	}
+	param->data_size = param->data_start + needed;
+
+	nl->dev = 0;	/* Flags no data */
+
+	/*
+	 * Now loop through filling out the names.
+	 */
+	for (i = 0; i < NUM_BUCKETS; i++) {
+		list_for_each_entry (hc, _name_buckets + i, name_list) {
+			if (old_nl)
+				old_nl->next = (uint32_t) ((void *) nl -
+							   (void *) old_nl);
+
+			nl->dev = dm_kdev(hc->md);
+			nl->next = 0;
+			strcpy(nl->name, hc->name);
+
+			old_nl = nl;
+			nl = align_ptr(((void *) ++nl) + strlen(hc->name) + 1);
+		}
+	}
+
+ out:
+	up_write(&_hash_lock);
+	return 0;
+}
+
+static int check_name(const char *name)
+{
+	if (strchr(name, '/')) {
+		DMWARN("invalid device name");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/*
+ * Fills in a dm_ioctl structure, ready for sending back to
+ * userland.
+ */
+static int __dev_status(struct mapped_device *md, struct dm_ioctl *param)
+{
+	kdev_t dev = dm_kdev(md);
+	struct dm_table *table;
+	struct block_device *bdev;
+
+	param->flags &= ~(DM_SUSPEND_FLAG | DM_READONLY_FLAG |
+			  DM_ACTIVE_PRESENT_FLAG);
+
+	if (dm_suspended(md))
+		param->flags |= DM_SUSPEND_FLAG;
+
+	param->dev = kdev_t_to_nr(dev);
+
+	if (is_read_only(dev))
+		param->flags |= DM_READONLY_FLAG;
+
+	param->event_nr = dm_get_event_nr(md);
+
+	table = dm_get_table(md);
+	if (table) {
+		param->flags |= DM_ACTIVE_PRESENT_FLAG;
+		param->target_count = dm_table_get_num_targets(table);
+		dm_table_put(table);
+	} else
+		param->target_count = 0;
+
+	bdev = bdget(param->dev);
+	if (!bdev)
+		return -ENXIO;
+	param->open_count = bdev->bd_openers;
+	bdput(bdev);
+
+	return 0;
+}
+
+static int dev_create(struct dm_ioctl *param, size_t param_size)
+{
+	int r;
+	kdev_t dev = 0;
+	struct mapped_device *md;
+
+	r = check_name(param->name);
+	if (r)
+		return r;
+
+	if (param->flags & DM_PERSISTENT_DEV_FLAG)
+		dev = to_kdev_t(param->dev);
+
+	r = dm_create(dev, &md);
+	if (r)
+		return r;
+
+	r = dm_hash_insert(param->name, *param->uuid ? param->uuid : NULL, md);
+	if (r) {
+		dm_put(md);
+		return r;
+	}
+
+	param->flags &= ~DM_INACTIVE_PRESENT_FLAG;
+
+	r = __dev_status(md, param);
+	dm_put(md);
+
+	return r;
+}
+
+/*
+ * Always use UUID for lookups if it's present, otherwise use name.
+ */
+static inline struct hash_cell *__find_device_hash_cell(struct dm_ioctl *param)
+{
+	return *param->uuid ?
+	    __get_uuid_cell(param->uuid) : __get_name_cell(param->name);
+}
+
+static inline struct mapped_device *find_device(struct dm_ioctl *param)
+{
+	struct hash_cell *hc;
+	struct mapped_device *md = NULL;
+
+	down_read(&_hash_lock);
+	hc = __find_device_hash_cell(param);
+	if (hc) {
+		md = hc->md;
+
+		/*
+		 * Sneakily write in both the name and the uuid
+		 * while we have the cell.
+		 */
+		strncpy(param->name, hc->name, sizeof(param->name));
+		if (hc->uuid)
+			strncpy(param->uuid, hc->uuid, sizeof(param->uuid) - 1);
+		else
+			param->uuid[0] = '\0';
+
+		if (hc->new_map)
+			param->flags |= DM_INACTIVE_PRESENT_FLAG;
+		else
+			param->flags &= ~DM_INACTIVE_PRESENT_FLAG;
+
+		dm_get(md);
+	}
+	up_read(&_hash_lock);
+
+	return md;
+}
+
+static int dev_remove(struct dm_ioctl *param, size_t param_size)
+{
+	struct hash_cell *hc;
+
+	down_write(&_hash_lock);
+	hc = __find_device_hash_cell(param);
+
+	if (!hc) {
+		DMWARN("device doesn't appear to be in the dev hash table.");
+		up_write(&_hash_lock);
+		return -ENXIO;
+	}
+
+	__hash_remove(hc);
+	up_write(&_hash_lock);
+	param->data_size = 0;
+	return 0;
+}
+
+/*
+ * Check a string doesn't overrun the chunk of
+ * memory we copied from userland.
+ */
+static int invalid_str(char *str, void *end)
+{
+	while ((void *) str < end)
+		if (!*str++)
+			return 0;
+
+	return -EINVAL;
+}
+
+static int dev_rename(struct dm_ioctl *param, size_t param_size)
+{
+	int r;
+	char *new_name = (char *) param + param->data_start;
+
+	if (new_name < (char *) (param + 1) ||
+	    invalid_str(new_name, (void *) param + param_size)) {
+		DMWARN("Invalid new logical volume name supplied.");
+		return -EINVAL;
+	}
+
+	r = check_name(new_name);
+	if (r)
+		return r;
+
+	param->data_size = 0;
+	return dm_hash_rename(param->name, new_name);
+}
+
+static int do_suspend(struct dm_ioctl *param)
+{
+	int r = 0;
+	struct mapped_device *md;
+
+	md = find_device(param);
+	if (!md)
+		return -ENXIO;
+
+	if (!dm_suspended(md))
+		r = dm_suspend(md);
+
+	if (!r)
+		r = __dev_status(md, param);
+
+	dm_put(md);
+	return r;
+}
+
+static int do_resume(struct dm_ioctl *param)
+{
+	int r = 0;
+	struct hash_cell *hc;
+	struct mapped_device *md;
+	struct dm_table *new_map;
+
+	down_write(&_hash_lock);
+
+	hc = __find_device_hash_cell(param);
+	if (!hc) {
+		DMWARN("device doesn't appear to be in the dev hash table.");
+		up_write(&_hash_lock);
+		return -ENXIO;
+	}
+
+	md = hc->md;
+	dm_get(md);
+
+	new_map = hc->new_map;
+	hc->new_map = NULL;
+	param->flags &= ~DM_INACTIVE_PRESENT_FLAG;
+
+	up_write(&_hash_lock);
+
+	/* Do we need to load a new map ? */
+	if (new_map) {
+		/* Suspend if it isn't already suspended */
+		if (!dm_suspended(md))
+			dm_suspend(md);
+
+		r = dm_swap_table(md, new_map);
+		if (r) {
+			dm_put(md);
+			dm_table_put(new_map);
+			return r;
+		}
+
+		if (dm_table_get_mode(new_map) & FMODE_WRITE)
+			set_device_ro(dm_kdev(md), 0);
+		else
+			set_device_ro(dm_kdev(md), 1);
+
+		dm_table_put(new_map);
+	}
+
+	if (dm_suspended(md))
+		r = dm_resume(md);
+
+	if (!r)
+		r = __dev_status(md, param);
+
+	dm_put(md);
+	return r;
+}
+
+/*
+ * Set or unset the suspension state of a device.
+ * If the device already is in the requested state we just return its status.
+ */
+static int dev_suspend(struct dm_ioctl *param, size_t param_size)
+{
+	if (param->flags & DM_SUSPEND_FLAG)
+		return do_suspend(param);
+
+	return do_resume(param);
+}
+
+/*
+ * Copies device info back to user space, used by
+ * the create and info ioctls.
+ */
+static int dev_status(struct dm_ioctl *param, size_t param_size)
+{
+	int r;
+	struct mapped_device *md;
+
+	md = find_device(param);
+	if (!md)
+		return -ENXIO;
+
+	r = __dev_status(md, param);
+	dm_put(md);
+	return r;
+}
+
+/*
+ * Build up the status struct for each target
+ */
+static void retrieve_status(struct dm_table *table, struct dm_ioctl *param,
+			    size_t param_size)
+{
+	unsigned int i, num_targets;
+	struct dm_target_spec *spec;
+	char *outbuf, *outptr;
+	status_type_t type;
+	size_t remaining, len, used = 0;
+
+	outptr = outbuf = get_result_buffer(param, param_size, &len);
+
+	if (param->flags & DM_STATUS_TABLE_FLAG)
+		type = STATUSTYPE_TABLE;
+	else
+		type = STATUSTYPE_INFO;
+
+	/* Get all the target info */
+	num_targets = dm_table_get_num_targets(table);
+	for (i = 0; i < num_targets; i++) {
+		struct dm_target *ti = dm_table_get_target(table, i);
+
+		remaining = len - (outptr - outbuf);
+		if (remaining < sizeof(struct dm_target_spec)) {
+			param->flags |= DM_BUFFER_FULL_FLAG;
+			break;
+		}
+
+		spec = (struct dm_target_spec *) outptr;
+
+		spec->status = 0;
+		spec->sector_start = ti->begin;
+		spec->length = ti->len;
+		strncpy(spec->target_type, ti->type->name,
+			sizeof(spec->target_type));
+
+		outptr += sizeof(struct dm_target_spec);
+		remaining = len - (outptr - outbuf);
+
+		/* Get the status/table string from the target driver */
+		if (ti->type->status) {
+			if (ti->type->status(ti, type, outptr, remaining)) {
+				param->flags |= DM_BUFFER_FULL_FLAG;
+				break;
+			}
+		} else
+			outptr[0] = '\0';
+
+		outptr += strlen(outptr) + 1;
+		used = param->data_start + (outptr - outbuf);
+
+		align_ptr(outptr);
+		spec->next = outptr - outbuf;
+	}
+
+	if (used)
+		param->data_size = used;
+
+	param->target_count = num_targets;
+}
+
+/*
+ * Wait for a device to report an event
+ */
+static int dev_wait(struct dm_ioctl *param, size_t param_size)
+{
+	int r;
+	struct mapped_device *md;
+	struct dm_table *table;
+	DECLARE_WAITQUEUE(wq, current);
+
+	md = find_device(param);
+	if (!md)
+		return -ENXIO;
+
+	/*
+	 * Wait for a notification event
+	 */
+	set_current_state(TASK_INTERRUPTIBLE);
+	if (!dm_add_wait_queue(md, &wq, param->event_nr)) {
+		schedule();
+		dm_remove_wait_queue(md, &wq);
+	}
+	set_current_state(TASK_RUNNING);
+
+	/*
+	 * The userland program is going to want to know what
+	 * changed to trigger the event, so we may as well tell
+	 * him and save an ioctl.
+	 */
+	r = __dev_status(md, param);
+	if (r)
+		goto out;
+
+	table = dm_get_table(md);
+	if (table) {
+		retrieve_status(table, param, param_size);
+		dm_table_put(table);
+	}
+
+ out:
+	dm_put(md);
+	return r;
+}
+
+static inline int get_mode(struct dm_ioctl *param)
+{
+	int mode = FMODE_READ | FMODE_WRITE;
+
+	if (param->flags & DM_READONLY_FLAG)
+		mode = FMODE_READ;
+
+	return mode;
+}
+
+static int next_target(struct dm_target_spec *last, uint32_t next, void *end,
+		       struct dm_target_spec **spec, char **target_params)
+{
+	*spec = (struct dm_target_spec *) ((unsigned char *) last + next);
+	*target_params = (char *) (*spec + 1);
+
+	if (*spec < (last + 1))
+		return -EINVAL;
+
+	return invalid_str(*target_params, end);
+}
+
+static int populate_table(struct dm_table *table, struct dm_ioctl *param,
+			  size_t param_size)
+{
+	int r;
+	unsigned int i = 0;
+	struct dm_target_spec *spec = (struct dm_target_spec *) param;
+	uint32_t next = param->data_start;
+	void *end = (void *) param + param_size;
+	char *target_params;
+
+	if (!param->target_count) {
+		DMWARN("populate_table: no targets specified");
+		return -EINVAL;
+	}
+
+	for (i = 0; i < param->target_count; i++) {
+
+		r = next_target(spec, next, end, &spec, &target_params);
+		if (r) {
+			DMWARN("unable to find target");
+			return r;
+		}
+
+		r = dm_table_add_target(table, spec->target_type,
+					(sector_t) spec->sector_start,
+					(sector_t) spec->length,
+					target_params);
+		if (r) {
+			DMWARN("error adding target to table");
+			return r;
+		}
+
+		next = spec->next;
+	}
+
+	return dm_table_complete(table);
+}
+
+static int table_load(struct dm_ioctl *param, size_t param_size)
+{
+	int r;
+	struct hash_cell *hc;
+	struct dm_table *t;
+
+	r = dm_table_create(&t, get_mode(param), param->target_count);
+	if (r)
+		return r;
+
+	r = populate_table(t, param, param_size);
+	if (r) {
+		dm_table_put(t);
+		return r;
+	}
+
+	down_write(&_hash_lock);
+	hc = __find_device_hash_cell(param);
+	if (!hc) {
+		DMWARN("device doesn't appear to be in the dev hash table.");
+		up_write(&_hash_lock);
+		return -ENXIO;
+	}
+
+	if (hc->new_map)
+		dm_table_put(hc->new_map);
+	hc->new_map = t;
+	param->flags |= DM_INACTIVE_PRESENT_FLAG;
+
+	r = __dev_status(hc->md, param);
+	up_write(&_hash_lock);
+	return r;
+}
+
+static int table_clear(struct dm_ioctl *param, size_t param_size)
+{
+	int r;
+	struct hash_cell *hc;
+
+	down_write(&_hash_lock);
+
+	hc = __find_device_hash_cell(param);
+	if (!hc) {
+		DMWARN("device doesn't appear to be in the dev hash table.");
+		up_write(&_hash_lock);
+		return -ENXIO;
+	}
+
+	if (hc->new_map) {
+		dm_table_put(hc->new_map);
+		hc->new_map = NULL;
+	}
+
+	param->flags &= ~DM_INACTIVE_PRESENT_FLAG;
+
+	r = __dev_status(hc->md, param);
+	up_write(&_hash_lock);
+	return r;
+}
+
+/*
+ * Retrieves a list of devices used by a particular dm device.
+ */
+static void retrieve_deps(struct dm_table *table, struct dm_ioctl *param,
+			  size_t param_size)
+{
+	unsigned int count = 0;
+	struct list_head *tmp;
+	size_t len, needed;
+	struct dm_target_deps *deps;
+
+	deps = get_result_buffer(param, param_size, &len);
+
+	/*
+	 * Count the devices.
+	 */
+	list_for_each(tmp, dm_table_get_devices(table))
+		count++;
+
+	/*
+	 * Check we have enough space.
+	 */
+	needed = sizeof(*deps) + (sizeof(*deps->dev) * count);
+	if (len < needed) {
+		param->flags |= DM_BUFFER_FULL_FLAG;
+		return;
+	}
+
+	/*
+	 * Fill in the devices.
+	 */
+	deps->count = count;
+	count = 0;
+	list_for_each(tmp, dm_table_get_devices(table)) {
+		struct dm_dev *dd = list_entry(tmp, struct dm_dev, list);
+		deps->dev[count++] = dd->bdev->bd_dev;
+	}
+
+	param->data_size = param->data_start + needed;
+}
+
+static int table_deps(struct dm_ioctl *param, size_t param_size)
+{
+	int r;
+	struct mapped_device *md;
+	struct dm_table *table;
+
+	md = find_device(param);
+	if (!md)
+		return -ENXIO;
+
+	r = __dev_status(md, param);
+	if (r)
+		goto out;
+
+	table = dm_get_table(md);
+	if (table) {
+		retrieve_deps(table, param, param_size);
+		dm_table_put(table);
+	}
+
+ out:
+	dm_put(md);
+	return r;
+}
+
+/*
+ * Return the status of a device as a text string for each
+ * target.
+ */
+static int table_status(struct dm_ioctl *param, size_t param_size)
+{
+	int r;
+	struct mapped_device *md;
+	struct dm_table *table;
+
+	md = find_device(param);
+	if (!md)
+		return -ENXIO;
+
+	r = __dev_status(md, param);
+	if (r)
+		goto out;
+ 
+	table = dm_get_table(md);
+	if (table) {
+		retrieve_status(table, param, param_size);
+		dm_table_put(table);
+	}
+
+ out:
+	dm_put(md);
+	return r;
+}
+
+/*-----------------------------------------------------------------
+ * Implementation of open/close/ioctl on the special char
+ * device.
+ *---------------------------------------------------------------*/
+static ioctl_fn lookup_ioctl(unsigned int cmd)
+{
+	static struct {
+		int cmd;
+		ioctl_fn fn;
+	} _ioctls[] = {
+		{DM_VERSION_CMD, NULL},	/* version is dealt with elsewhere */
+		{DM_REMOVE_ALL_CMD, remove_all},
+		{DM_LIST_DEVICES_CMD, list_devices},
+
+		{DM_DEV_CREATE_CMD, dev_create},
+		{DM_DEV_REMOVE_CMD, dev_remove},
+		{DM_DEV_RENAME_CMD, dev_rename},
+		{DM_DEV_SUSPEND_CMD, dev_suspend},
+		{DM_DEV_STATUS_CMD, dev_status},
+		{DM_DEV_WAIT_CMD, dev_wait},
+
+		{DM_TABLE_LOAD_CMD, table_load},
+		{DM_TABLE_CLEAR_CMD, table_clear},
+		{DM_TABLE_DEPS_CMD, table_deps},
+		{DM_TABLE_STATUS_CMD, table_status}
+	};
+
+	return (cmd >= ARRAY_SIZE(_ioctls)) ? NULL : _ioctls[cmd].fn;
+}
+
+/*
+ * As well as checking the version compatibility this always
+ * copies the kernel interface version out.
+ */
+static int check_version(unsigned int cmd, struct dm_ioctl *user)
+{
+	uint32_t version[3];
+	int r = 0;
+
+	if (copy_from_user(version, user->version, sizeof(version)))
+		return -EFAULT;
+
+	if ((DM_VERSION_MAJOR != version[0]) ||
+	    (DM_VERSION_MINOR < version[1])) {
+		DMWARN("ioctl interface mismatch: "
+		       "kernel(%u.%u.%u), user(%u.%u.%u), cmd(%d)",
+		       DM_VERSION_MAJOR, DM_VERSION_MINOR,
+		       DM_VERSION_PATCHLEVEL,
+		       version[0], version[1], version[2], cmd);
+		r = -EINVAL;
+	}
+
+	/*
+	 * Fill in the kernel version.
+	 */
+	version[0] = DM_VERSION_MAJOR;
+	version[1] = DM_VERSION_MINOR;
+	version[2] = DM_VERSION_PATCHLEVEL;
+	if (copy_to_user(user->version, version, sizeof(version)))
+		return -EFAULT;
+
+	return r;
+}
+
+static void free_params(struct dm_ioctl *param)
+{
+	vfree(param);
+}
+
+static int copy_params(struct dm_ioctl *user, struct dm_ioctl **param)
+{
+	struct dm_ioctl tmp, *dmi;
+
+	if (copy_from_user(&tmp, user, sizeof(tmp)))
+		return -EFAULT;
+
+	if (tmp.data_size < sizeof(tmp))
+		return -EINVAL;
+
+	dmi = (struct dm_ioctl *) vmalloc(tmp.data_size);
+	if (!dmi)
+		return -ENOMEM;
+
+	if (copy_from_user(dmi, user, tmp.data_size)) {
+		vfree(dmi);
+		return -EFAULT;
+	}
+
+	*param = dmi;
+	return 0;
+}
+
+static int validate_params(uint cmd, struct dm_ioctl *param)
+{
+	/* Always clear this flag */
+	param->flags &= ~DM_BUFFER_FULL_FLAG;
+
+	/* Ignores parameters */
+	if (cmd == DM_REMOVE_ALL_CMD || cmd == DM_LIST_DEVICES_CMD)
+		return 0;
+
+	/* Unless creating, either name or uuid but not both */
+	if (cmd != DM_DEV_CREATE_CMD) {
+		if ((!*param->uuid && !*param->name) ||
+		    (*param->uuid && *param->name)) {
+			DMWARN("one of name or uuid must be supplied, cmd(%u)",
+			       cmd);
+			return -EINVAL;
+		}
+	}
+
+	/* Ensure strings are terminated */
+	param->name[DM_NAME_LEN - 1] = '\0';
+	param->uuid[DM_UUID_LEN - 1] = '\0';
+
+	return 0;
+}
+
+static int ctl_ioctl(struct inode *inode, struct file *file,
+		     uint command, ulong u)
+{
+	int r = 0;
+	unsigned int cmd;
+	struct dm_ioctl *param;
+	struct dm_ioctl *user = (struct dm_ioctl *) u;
+	ioctl_fn fn = NULL;
+	size_t param_size;
+
+	/* only root can play with this */
+	if (!capable(CAP_SYS_ADMIN))
+		return -EACCES;
+
+	if (_IOC_TYPE(command) != DM_IOCTL)
+		return -ENOTTY;
+
+	cmd = _IOC_NR(command);
+
+	/*
+	 * Check the interface version passed in.  This also
+	 * writes out the kernel's interface version.
+	 */
+	r = check_version(cmd, user);
+	if (r)
+		return r;
+
+	/*
+	 * Nothing more to do for the version command.
+	 */
+	if (cmd == DM_VERSION_CMD)
+		return 0;
+
+	fn = lookup_ioctl(cmd);
+	if (!fn) {
+		DMWARN("dm_ctl_ioctl: unknown command 0x%x", command);
+		return -ENOTTY;
+	}
+
+	/*
+	 * FIXME: I don't like this, we're trying to avoid low
+	 * memory issues when a device is suspended.
+	 */
+	current->flags |= PF_MEMALLOC;
+
+	/*
+	 * Copy the parameters into kernel space.
+	 */
+	r = copy_params(user, &param);
+	if (r) {
+		current->flags &= ~PF_MEMALLOC;
+		return r;
+	}
+
+	r = validate_params(cmd, param);
+	if (r)
+		goto out;
+
+	param_size = param->data_size;
+	param->data_size = sizeof(*param);
+	r = fn(param, param_size);
+
+	/*
+	 * Copy the results back to userland.
+	 */
+	if (!r && copy_to_user(user, param, param->data_size))
+		r = -EFAULT;
+
+ out:
+	free_params(param);
+	current->flags &= ~PF_MEMALLOC;
+	return r;
+}
+
+static struct file_operations _ctl_fops = {
+	.ioctl	 = ctl_ioctl,
+	.owner	 = THIS_MODULE,
+};
+
+static devfs_handle_t _ctl_handle;
+
+static struct miscdevice _dm_misc = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name  = DM_NAME,
+	.fops  = &_ctl_fops
+};
+
+/*
+ * Create misc character device and link to DM_DIR/control.
+ */
+int __init dm_interface_init(void)
+{
+	int r;
+	char rname[64];
+
+	r = dm_hash_init();
+	if (r)
+		return r;
+
+	r = misc_register(&_dm_misc);
+	if (r) {
+		DMERR("misc_register failed for control device");
+		dm_hash_exit();
+		return r;
+	}
+
+	r = devfs_generate_path(_dm_misc.devfs_handle, rname + 3,
+				sizeof rname - 3);
+	if (r == -ENOSYS)
+		goto done;	/* devfs not present */
+
+	if (r < 0) {
+		DMERR("devfs_generate_path failed for control device");
+		goto failed;
+	}
+
+	strncpy(rname + r, "../", 3);
+	r = devfs_mk_symlink(NULL, DM_DIR "/control",
+			     DEVFS_FL_DEFAULT, rname + r, &_ctl_handle, NULL);
+	if (r) {
+		DMERR("devfs_mk_symlink failed for control device");
+		goto failed;
+	}
+	devfs_auto_unregister(_dm_misc.devfs_handle, _ctl_handle);
+
+      done:
+	DMINFO("%d.%d.%d%s initialised: %s", DM_VERSION_MAJOR,
+	       DM_VERSION_MINOR, DM_VERSION_PATCHLEVEL, DM_VERSION_EXTRA,
+	       DM_DRIVER_EMAIL);
+	return 0;
+
+      failed:
+	misc_deregister(&_dm_misc);
+	dm_hash_exit();
+	return r;
+}
+
+void dm_interface_exit(void)
+{
+	if (misc_deregister(&_dm_misc) < 0)
+		DMERR("misc_deregister failed for control device");
+
+	dm_hash_exit();
+}
diff -urN --exclude-from=diff-exclude linux-2.4.25/drivers/md/dm-io.h linux-2.4.25-leo/drivers/md/dm-io.h
--- linux-2.4.25/drivers/md/dm-io.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.25-leo/drivers/md/dm-io.h	2004-02-20 18:37:03.000000000 +0000
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2003 Sistina Software
+ *
+ * This file is released under the GPL.
+ */
+
+#ifndef _DM_IO_H
+#define _DM_IO_H
+
+#include "dm.h"
+
+#include <linux/list.h>
+
+/* Move these to bitops.h eventually */
+/* Improved generic_fls algorithm (in 2.4 there is no generic_fls so far) */
+/* (c) 2002, D.Phillips and Sistina Software */
+/* Licensed under Version 2 of the GPL */
+
+static unsigned generic_fls8(unsigned n)
+{
+	return n & 0xf0 ?
+	    n & 0xc0 ? (n >> 7) + 7 : (n >> 5) + 5:
+	    n & 0x0c ? (n >> 3) + 3 : n - ((n + 1) >> 2);
+}
+
+static inline unsigned generic_fls16(unsigned n)
+{
+	return	n & 0xff00? generic_fls8(n >> 8) + 8 : generic_fls8(n);
+}
+
+static inline unsigned generic_fls32(unsigned n)
+{
+	return	n & 0xffff0000 ? generic_fls16(n >> 16) + 16 : generic_fls16(n);
+}
+
+/* FIXME make this configurable */
+#define DM_MAX_IO_REGIONS 8
+
+struct io_region {
+	kdev_t dev;
+	sector_t sector;
+	sector_t count;
+};
+
+
+/*
+ * 'error' is a bitset, with each bit indicating whether an error
+ * occurred doing io to the corresponding region.
+ */
+typedef void (*io_notify_fn)(unsigned int error, void *context);
+
+
+/*
+ * Before anyone uses the IO interface they should call
+ * dm_io_get(), specifying roughly how many pages they are
+ * expecting to perform io on concurrently.
+ *
+ * This function may block.
+ */
+int dm_io_get(unsigned int num_pages);
+void dm_io_put(unsigned int num_pages);
+
+
+/*
+ * Synchronous IO.
+ *
+ * Please ensure that the rw flag in the next two functions is
+ * either READ or WRITE, ie. we don't take READA.  Any
+ * regions with a zero count field will be ignored.
+ */
+int dm_io_sync(unsigned int num_regions, struct io_region *where, int rw,
+	       struct page *pages, unsigned int offset,
+	       unsigned int *error_bits);
+
+
+/*
+ * Aynchronous IO.
+ *
+ * The 'where' array may be safely allocated on the stack since
+ * the function takes a copy.
+ */
+int dm_io_async(unsigned int num_regions, struct io_region *where, int rw,
+		struct page *pages, unsigned int offset,
+		io_notify_fn fn, void *context);
+
+#endif
diff -urN --exclude-from=diff-exclude linux-2.4.25/drivers/md/dm-linear.c linux-2.4.25-leo/drivers/md/dm-linear.c
--- linux-2.4.25/drivers/md/dm-linear.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.25-leo/drivers/md/dm-linear.c	2004-02-20 18:21:57.000000000 +0000
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2001 Sistina Software (UK) Limited.
+ *
+ * This file is released under the GPL.
+ */
+
+#include "dm.h"
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/blkdev.h>
+#include <linux/slab.h>
+
+/*
+ * Linear: maps a linear range of a device.
+ */
+struct linear_c {
+	struct dm_dev *dev;
+	sector_t start;
+};
+
+/*
+ * Construct a linear mapping: <dev_path> <offset>
+ */
+static int linear_ctr(struct dm_target *ti, unsigned int argc, char **argv)
+{
+	struct linear_c *lc;
+
+	if (argc != 2) {
+		ti->error = "dm-linear: Invalid argument count";
+		return -EINVAL;
+	}
+
+	lc = kmalloc(sizeof(*lc), GFP_KERNEL);
+	if (lc == NULL) {
+		ti->error = "dm-linear: Cannot allocate linear context";
+		return -ENOMEM;
+	}
+
+	if (sscanf(argv[1], SECTOR_FORMAT, &lc->start) != 1) {
+		ti->error = "dm-linear: Invalid device sector";
+		goto bad;
+	}
+
+	if (dm_get_device(ti, argv[0], lc->start, ti->len,
+			  dm_table_get_mode(ti->table), &lc->dev)) {
+		ti->error = "dm-linear: Device lookup failed";
+		goto bad;
+	}
+
+	ti->private = lc;
+	return 0;
+
+      bad:
+	kfree(lc);
+	return -EINVAL;
+}
+
+static void linear_dtr(struct dm_target *ti)
+{
+	struct linear_c *lc = (struct linear_c *) ti->private;
+
+	dm_put_device(ti, lc->dev);
+	kfree(lc);
+}
+
+static int linear_map(struct dm_target *ti, struct buffer_head *bh, int rw,
+		      union map_info *map_context)
+{
+	struct linear_c *lc = (struct linear_c *) ti->private;
+
+	bh->b_rdev = lc->dev->dev;
+	bh->b_rsector = lc->start + (bh->b_rsector - ti->begin);
+
+	return 1;
+}
+
+static int linear_status(struct dm_target *ti, status_type_t type,
+			 char *result, unsigned int maxlen)
+{
+	struct linear_c *lc = (struct linear_c *) ti->private;
+	kdev_t kdev;
+
+	switch (type) {
+	case STATUSTYPE_INFO:
+		result[0] = '\0';
+		break;
+
+	case STATUSTYPE_TABLE:
+		kdev = to_kdev_t(lc->dev->bdev->bd_dev);
+		snprintf(result, maxlen, "%s " SECTOR_FORMAT,
+			 dm_kdevname(kdev), lc->start);
+		break;
+	}
+	return 0;
+}
+
+static struct target_type linear_target = {
+	.name   = "linear",
+	.module = THIS_MODULE,
+	.ctr    = linear_ctr,
+	.dtr    = linear_dtr,
+	.map    = linear_map,
+	.status = linear_status,
+};
+
+int __init dm_linear_init(void)
+{
+	int r = dm_register_target(&linear_target);
+
+	if (r < 0)
+		DMERR("linear: register failed %d", r);
+
+	return r;
+}
+
+void dm_linear_exit(void)
+{
+	int r = dm_unregister_target(&linear_target);
+
+	if (r < 0)
+		DMERR("linear: unregister failed %d", r);
+}
diff -urN --exclude-from=diff-exclude linux-2.4.25/drivers/md/dm-log.c linux-2.4.25-leo/drivers/md/dm-log.c
--- linux-2.4.25/drivers/md/dm-log.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.25-leo/drivers/md/dm-log.c	2004-02-20 18:21:57.000000000 +0000
@@ -0,0 +1,310 @@
+/*
+ * Copyright (C) 2003 Sistina Software
+ *
+ * This file is released under the LGPL.
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+
+#include "dm-log.h"
+#include "dm-io.h"
+
+static LIST_HEAD(_log_types);
+static spinlock_t _lock = SPIN_LOCK_UNLOCKED;
+
+int dm_register_dirty_log_type(struct dirty_log_type *type)
+{
+	spin_lock(&_lock);
+	type->use_count = 0;
+	if (type->module)
+		__MOD_INC_USE_COUNT(type->module);
+
+	list_add(&type->list, &_log_types);
+	spin_unlock(&_lock);
+
+	return 0;
+}
+
+int dm_unregister_dirty_log_type(struct dirty_log_type *type)
+{
+	spin_lock(&_lock);
+
+	if (type->use_count)
+		DMWARN("Attempt to unregister a log type that is still in use");
+	else {
+		list_del(&type->list);
+		if (type->module)
+			__MOD_DEC_USE_COUNT(type->module);
+	}
+
+	spin_unlock(&_lock);
+
+	return 0;
+}
+
+static struct dirty_log_type *get_type(const char *type_name)
+{
+	struct dirty_log_type *type;
+	struct list_head *tmp;
+
+	spin_lock(&_lock);
+	list_for_each (tmp, &_log_types) {
+		type = list_entry(tmp, struct dirty_log_type, list);
+		if (!strcmp(type_name, type->name)) {
+			type->use_count++;
+			spin_unlock(&_lock);
+			return type;
+		}
+	}
+
+	spin_unlock(&_lock);
+	return NULL;
+}
+
+static void put_type(struct dirty_log_type *type)
+{
+	spin_lock(&_lock);
+	type->use_count--;
+	spin_unlock(&_lock);
+}
+
+struct dirty_log *dm_create_dirty_log(const char *type_name, sector_t dev_size,
+				      unsigned int argc, char **argv)
+{
+	struct dirty_log_type *type;
+	struct dirty_log *log;
+
+	log = kmalloc(sizeof(*log), GFP_KERNEL);
+	if (!log)
+		return NULL;
+
+	type = get_type(type_name);
+	if (!type) {
+		kfree(log);
+		return NULL;
+	}
+
+	log->type = type;
+	if (type->ctr(log, dev_size, argc, argv)) {
+		kfree(log);
+		put_type(type);
+		return NULL;
+	}
+
+	return log;
+}
+
+void dm_destroy_dirty_log(struct dirty_log *log)
+{
+	log->type->dtr(log);
+	put_type(log->type);
+	kfree(log);
+}
+
+
+/*-----------------------------------------------------------------
+ * In core log, ie. trivial, non-persistent
+ *
+ * For now we'll keep this simple and just have 2 bitsets, one
+ * for clean/dirty, the other for sync/nosync.  The sync bitset
+ * will be freed when everything is in sync.
+ *
+ * FIXME: problems with a 64bit sector_t
+ *---------------------------------------------------------------*/
+struct core_log {
+	sector_t region_size;
+	unsigned int region_count;
+	unsigned long *clean_bits;
+	unsigned long *sync_bits;
+	unsigned long *recovering_bits;	/* FIXME: this seems excessive */
+
+	int sync_search;
+};
+
+#define BYTE_SHIFT 3
+
+static int core_ctr(struct dirty_log *log, sector_t dev_size,
+		    unsigned int argc, char **argv)
+{
+	struct core_log *clog;
+	sector_t region_size;
+	unsigned int region_count;
+	size_t bitset_size;
+
+	if (argc != 1) {
+		DMWARN("wrong number of arguments to core_log");
+		return -EINVAL;
+	}
+
+	if (sscanf(argv[0], SECTOR_FORMAT, &region_size) != 1) {
+		DMWARN("invalid region size string");
+		return -EINVAL;
+	}
+
+	region_count = dm_div_up(dev_size, region_size);
+
+	clog = kmalloc(sizeof(*clog), GFP_KERNEL);
+	if (!clog) {
+		DMWARN("couldn't allocate core log");
+		return -ENOMEM;
+	}
+
+	clog->region_size = region_size;
+	clog->region_count = region_count;
+
+	/*
+ 	 * Work out how many words we need to hold the bitset.
+ 	 */
+	bitset_size = dm_round_up(region_count,
+				  sizeof(*clog->clean_bits) << BYTE_SHIFT);
+	bitset_size >>= BYTE_SHIFT;
+
+	clog->clean_bits = vmalloc(bitset_size);
+	if (!clog->clean_bits) {
+		DMWARN("couldn't allocate clean bitset");
+		kfree(clog);
+		return -ENOMEM;
+	}
+	memset(clog->clean_bits, -1, bitset_size);
+
+	clog->sync_bits = vmalloc(bitset_size);
+	if (!clog->sync_bits) {
+		DMWARN("couldn't allocate sync bitset");
+		vfree(clog->clean_bits);
+		kfree(clog);
+		return -ENOMEM;
+	}
+	memset(clog->sync_bits, 0, bitset_size);
+
+	clog->recovering_bits = vmalloc(bitset_size);
+	if (!clog->recovering_bits) {
+		DMWARN("couldn't allocate sync bitset");
+		vfree(clog->sync_bits);
+		vfree(clog->clean_bits);
+		kfree(clog);
+		return -ENOMEM;
+	}
+	memset(clog->recovering_bits, 0, bitset_size);
+	clog->sync_search = 0;
+	log->context = clog;
+	return 0;
+}
+
+static void core_dtr(struct dirty_log *log)
+{
+	struct core_log *clog = (struct core_log *) log->context;
+	vfree(clog->clean_bits);
+	vfree(clog->sync_bits);
+	vfree(clog->recovering_bits);
+	kfree(clog);
+}
+
+static sector_t core_get_region_size(struct dirty_log *log)
+{
+	struct core_log *clog = (struct core_log *) log->context;
+	return clog->region_size;
+}
+
+static int core_is_clean(struct dirty_log *log, region_t region)
+{
+	struct core_log *clog = (struct core_log *) log->context;
+	return test_bit(region, clog->clean_bits);
+}
+
+static int core_in_sync(struct dirty_log *log, region_t region, int block)
+{
+	struct core_log *clog = (struct core_log *) log->context;
+
+	return test_bit(region, clog->sync_bits) ? 1 : 0;
+}
+
+static int core_flush(struct dirty_log *log)
+{
+	/* no op */
+	return 0;
+}
+
+static void core_mark_region(struct dirty_log *log, region_t region)
+{
+	struct core_log *clog = (struct core_log *) log->context;
+	clear_bit(region, clog->clean_bits);
+}
+
+static void core_clear_region(struct dirty_log *log, region_t region)
+{
+	struct core_log *clog = (struct core_log *) log->context;
+	set_bit(region, clog->clean_bits);
+}
+
+static int core_get_resync_work(struct dirty_log *log, region_t *region)
+{
+	struct core_log *clog = (struct core_log *) log->context;
+
+	if (clog->sync_search >= clog->region_count)
+		return 0;
+
+	do {
+		*region = find_next_zero_bit(clog->sync_bits,
+					     clog->region_count,
+					     clog->sync_search);
+		clog->sync_search = *region + 1;
+
+		if (*region == clog->region_count)
+			return 0;
+
+	} while (test_bit(*region, clog->recovering_bits));
+
+	set_bit(*region, clog->recovering_bits);
+	return 1;
+}
+
+static void core_complete_resync_work(struct dirty_log *log, region_t region,
+				      int success)
+{
+	struct core_log *clog = (struct core_log *) log->context;
+
+	clear_bit(region, clog->recovering_bits);
+	if (success)
+		set_bit(region, clog->sync_bits);
+}
+
+static struct dirty_log_type _core_type = {
+	.name = "core",
+
+	.ctr = core_ctr,
+	.dtr = core_dtr,
+	.get_region_size = core_get_region_size,
+	.is_clean = core_is_clean,
+	.in_sync = core_in_sync,
+	.flush = core_flush,
+	.mark_region = core_mark_region,
+	.clear_region = core_clear_region,
+	.get_resync_work = core_get_resync_work,
+	.complete_resync_work = core_complete_resync_work
+};
+
+__init int dm_dirty_log_init(void)
+{
+	int r;
+
+	r = dm_register_dirty_log_type(&_core_type);
+	if (r)
+		DMWARN("couldn't register core log");
+
+	return r;
+}
+
+void dm_dirty_log_exit(void)
+{
+	dm_unregister_dirty_log_type(&_core_type);
+}
+
+EXPORT_SYMBOL(dm_register_dirty_log_type);
+EXPORT_SYMBOL(dm_unregister_dirty_log_type);
+EXPORT_SYMBOL(dm_dirty_log_init);
+EXPORT_SYMBOL(dm_dirty_log_exit);
+EXPORT_SYMBOL(dm_create_dirty_log);
+EXPORT_SYMBOL(dm_destroy_dirty_log);
diff -urN --exclude-from=diff-exclude linux-2.4.25/drivers/md/dm-log.h linux-2.4.25-leo/drivers/md/dm-log.h
--- linux-2.4.25/drivers/md/dm-log.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.25-leo/drivers/md/dm-log.h	2004-02-20 18:37:09.000000000 +0000
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2003 Sistina Software
+ *
+ * This file is released under the LGPL.
+ */
+
+#ifndef DM_DIRTY_LOG
+#define DM_DIRTY_LOG
+
+#include "dm.h"
+
+typedef sector_t region_t;
+
+struct dirty_log_type;
+
+struct dirty_log {
+	struct dirty_log_type *type;
+	void *context;
+};
+
+struct dirty_log_type {
+	struct list_head list;
+	const char *name;
+	struct module *module;
+	unsigned int use_count;
+
+	int (*ctr)(struct dirty_log *log, sector_t dev_size,
+		   unsigned int argc, char **argv);
+	void (*dtr)(struct dirty_log *log);
+
+	/*
+	 * Retrieves the smallest size of region that the log can
+	 * deal with.
+	 */
+	sector_t (*get_region_size)(struct dirty_log *log);
+
+        /*
+	 * A predicate to say whether a region is clean or not.
+	 * May block.
+	 */
+	int (*is_clean)(struct dirty_log *log, region_t region);
+
+	/*
+	 *  Returns: 0, 1, -EWOULDBLOCK, < 0
+	 *
+	 * A predicate function to check the area given by
+	 * [sector, sector + len) is in sync.
+	 *
+	 * If -EWOULDBLOCK is returned the state of the region is
+	 * unknown, typically this will result in a read being
+	 * passed to a daemon to deal with, since a daemon is
+	 * allowed to block.
+	 */
+	int (*in_sync)(struct dirty_log *log, region_t region, int can_block);
+
+	/*
+	 * Flush the current log state (eg, to disk).  This
+	 * function may block.
+	 */
+	int (*flush)(struct dirty_log *log);
+
+	/*
+	 * Mark an area as clean or dirty.  These functions may
+	 * block, though for performance reasons blocking should
+	 * be extremely rare (eg, allocating another chunk of
+	 * memory for some reason).
+	 */
+	void (*mark_region)(struct dirty_log *log, region_t region);
+	void (*clear_region)(struct dirty_log *log, region_t region);
+
+	/*
+	 * Returns: <0 (error), 0 (no region), 1 (region)
+	 *
+	 * The mirrord will need perform recovery on regions of
+	 * the mirror that are in the NOSYNC state.  This
+	 * function asks the log to tell the caller about the
+	 * next region that this machine should recover.
+	 *
+	 * Do not confuse this function with 'in_sync()', one
+	 * tells you if an area is synchronised, the other
+	 * assigns recovery work.
+	*/
+	int (*get_resync_work)(struct dirty_log *log, region_t *region);
+
+	/*
+	 * This notifies the log that the resync of an area has
+	 * been completed.  The log should then mark this region
+	 * as CLEAN.
+	 */
+	void (*complete_resync_work)(struct dirty_log *log,
+				     region_t region, int success);
+};
+
+int dm_register_dirty_log_type(struct dirty_log_type *type);
+int dm_unregister_dirty_log_type(struct dirty_log_type *type);
+
+
+/*
+ * Make sure you use these two functions, rather than calling
+ * type->constructor/destructor() directly.
+ */
+struct dirty_log *dm_create_dirty_log(const char *type_name, sector_t dev_size,
+				      unsigned int argc, char **argv);
+void dm_destroy_dirty_log(struct dirty_log *log);
+
+/*
+ * init/exit functions.
+ */
+int dm_dirty_log_init(void);
+void dm_dirty_log_exit(void);
+
+#endif
diff -urN --exclude-from=diff-exclude linux-2.4.25/drivers/md/dm-raid1.c linux-2.4.25-leo/drivers/md/dm-raid1.c
--- linux-2.4.25/drivers/md/dm-raid1.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.25-leo/drivers/md/dm-raid1.c	2004-02-20 18:21:57.000000000 +0000
@@ -0,0 +1,1294 @@
+/*
+ * Copyright (C) 2003 Sistina Software Limited.
+ *
+ * This file is released under the GPL.
+ */
+
+#include "dm.h"
+#include "dm-daemon.h"
+#include "dm-io.h"
+#include "dm-log.h"
+#include "kcopyd.h"
+
+#include <linux/ctype.h>
+#include <linux/init.h>
+#include <linux/mempool.h>
+#include <linux/module.h>
+#include <linux/pagemap.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <linux/vmalloc.h>
+
+static struct dm_daemon _kmirrord;
+
+/*-----------------------------------------------------------------
+ * buffer lists:
+ *
+ * We play with singly linked lists of buffers, but we want to be
+ * careful to add new buffers to the back of the list, to avoid
+ * buffers being starved of attention.
+ *---------------------------------------------------------------*/
+struct buffer_list {
+	struct buffer_head *head;
+	struct buffer_head *tail;
+};
+
+static inline void buffer_list_init(struct buffer_list *bl)
+{
+	bl->head = bl->tail = NULL;
+}
+
+static inline void buffer_list_add(struct buffer_list *bl,
+				   struct buffer_head *bh)
+{
+	bh->b_reqnext = NULL;
+
+	if (bl->tail) {
+		bl->tail->b_reqnext = bh;
+		bl->tail = bh;
+	} else
+		bl->head = bl->tail = bh;
+}
+
+static struct buffer_head *buffer_list_pop(struct buffer_list *bl)
+{
+	struct buffer_head *bh = bl->head;
+
+	if (bh) {
+		bl->head = bl->head->b_reqnext;
+		if (!bl->head)
+			bl->tail = NULL;
+
+		bh->b_reqnext = NULL;
+	}
+
+	return bh;
+}
+
+/*-----------------------------------------------------------------
+ * Region hash
+ *
+ * The mirror splits itself up into discrete regions.  Each
+ * region can be in one of three states: clean, dirty,
+ * nosync.  There is no need to put clean regions in the hash.
+ *
+ * In addition to being present in the hash table a region _may_
+ * be present on one of three lists.
+ *
+ *   clean_regions: Regions on this list have no io pending to
+ *   them, they are in sync, we are no longer interested in them,
+ *   they are dull.  rh_update_states() will remove them from the
+ *   hash table.
+ *
+ *   quiesced_regions: These regions have been spun down, ready
+ *   for recovery.  rh_recovery_start() will remove regions from
+ *   this list and hand them to kmirrord, which will schedule the
+ *   recovery io with kcopyd.
+ *
+ *   recovered_regions: Regions that kcopyd has successfully
+ *   recovered.  rh_update_states() will now schedule any delayed
+ *   io, up the recovery_count, and remove the region from the
+ *   hash.
+ *
+ * There are 2 locks:
+ *   A rw spin lock 'hash_lock' protects just the hash table,
+ *   this is never held in write mode from interrupt context,
+ *   which I believe means that we only have to disable irqs when
+ *   doing a write lock.
+ *
+ *   An ordinary spin lock 'region_lock' that protects the three
+ *   lists in the region_hash, with the 'state', 'list' and
+ *   'bhs_delayed' fields of the regions.  This is used from irq
+ *   context, so all other uses will have to suspend local irqs.
+ *---------------------------------------------------------------*/
+struct mirror_set;
+struct region_hash {
+	struct mirror_set *ms;
+	sector_t region_size;
+
+	/* holds persistent region state */
+	struct dirty_log *log;
+
+	/* hash table */
+	rwlock_t hash_lock;
+	mempool_t *region_pool;
+	unsigned int mask;
+	unsigned int nr_buckets;
+	struct list_head *buckets;
+
+	spinlock_t region_lock;
+	struct semaphore recovery_count;
+	struct list_head clean_regions;
+	struct list_head quiesced_regions;
+	struct list_head recovered_regions;
+};
+
+enum {
+	RH_CLEAN,
+	RH_DIRTY,
+	RH_NOSYNC,
+	RH_RECOVERING
+};
+
+struct region {
+	struct region_hash *rh;	/* FIXME: can we get rid of this ? */
+	region_t key;
+	int state;
+
+	struct list_head hash_list;
+	struct list_head list;
+
+	atomic_t pending;
+	struct buffer_head *delayed_bhs;
+};
+
+/*
+ * Conversion fns
+ */
+static inline region_t bh_to_region(struct region_hash *rh,
+				    struct buffer_head *bh)
+{
+	return bh->b_rsector / rh->region_size;
+}
+
+static inline sector_t region_to_sector(struct region_hash *rh, region_t region)
+{
+	return region * rh->region_size;
+}
+
+/* FIXME move this */
+static void queue_bh(struct mirror_set *ms, struct buffer_head *bh, int rw);
+
+static void *region_alloc(int gfp_mask, void *pool_data)
+{
+	return kmalloc(sizeof(struct region), gfp_mask);
+}
+
+static void region_free(void *element, void *pool_data)
+{
+	kfree(element);
+}
+
+#define MIN_REGIONS 64
+#define MAX_RECOVERY 1
+static int rh_init(struct region_hash *rh, struct mirror_set *ms,
+		   struct dirty_log *log, sector_t region_size,
+		   region_t nr_regions)
+{
+	unsigned int nr_buckets, max_buckets;
+	size_t i;
+
+	/*
+	 * Calculate a suitable number of buckets for our hash
+	 * table.
+	 */
+	max_buckets = nr_regions >> 6;
+	for (nr_buckets = 128u; nr_buckets < max_buckets; nr_buckets <<= 1)
+		;
+	nr_buckets >>= 1;
+
+	rh->ms = ms;
+	rh->log = log;
+	rh->region_size = region_size;
+	rwlock_init(&rh->hash_lock);
+	rh->mask = nr_buckets - 1;
+	rh->nr_buckets = nr_buckets;
+
+	rh->buckets = vmalloc(nr_buckets * sizeof(*rh->buckets));
+	if (!rh->buckets) {
+		DMERR("unable to allocate region hash memory");
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < nr_buckets; i++)
+		INIT_LIST_HEAD(rh->buckets + i);
+
+	spin_lock_init(&rh->region_lock);
+	sema_init(&rh->recovery_count, 0);
+	INIT_LIST_HEAD(&rh->clean_regions);
+	INIT_LIST_HEAD(&rh->quiesced_regions);
+	INIT_LIST_HEAD(&rh->recovered_regions);
+
+	rh->region_pool = mempool_create(MIN_REGIONS, region_alloc,
+					 region_free, NULL);
+	if (!rh->region_pool) {
+		vfree(rh->buckets);
+		rh->buckets = NULL;
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static void rh_exit(struct region_hash *rh)
+{
+	unsigned int h;
+	struct region *reg;
+	struct list_head *tmp, *tmp2;
+
+	BUG_ON(!list_empty(&rh->quiesced_regions));
+	for (h = 0; h < rh->nr_buckets; h++) {
+		list_for_each_safe (tmp, tmp2, rh->buckets + h) {
+			reg = list_entry(tmp, struct region, hash_list);
+			BUG_ON(atomic_read(&reg->pending));
+			mempool_free(reg, rh->region_pool);
+		}
+	}
+
+	if (rh->log)
+		dm_destroy_dirty_log(rh->log);
+	if (rh->region_pool)
+		mempool_destroy(rh->region_pool);
+	vfree(rh->buckets);
+}
+
+#define RH_HASH_MULT 2654435387U
+
+static inline unsigned int rh_hash(struct region_hash *rh, region_t region)
+{
+	return (unsigned int) ((region * RH_HASH_MULT) >> 12) & rh->mask;
+}
+
+static struct region *__rh_lookup(struct region_hash *rh, region_t region)
+{
+	struct region *reg;
+
+	list_for_each_entry (reg, rh->buckets + rh_hash(rh, region), hash_list)
+		if (reg->key == region)
+			return reg;
+
+	return NULL;
+}
+
+static void __rh_insert(struct region_hash *rh, struct region *reg)
+{
+	unsigned int h = rh_hash(rh, reg->key);
+	list_add(&reg->hash_list, rh->buckets + h);
+}
+
+static struct region *__rh_alloc(struct region_hash *rh, region_t region)
+{
+	struct region *reg, *nreg;
+
+	read_unlock(&rh->hash_lock);
+	nreg = mempool_alloc(rh->region_pool, GFP_NOIO);
+	nreg->state = rh->log->type->in_sync(rh->log, region, 1) ?
+		RH_CLEAN : RH_NOSYNC;
+	nreg->rh = rh;
+	nreg->key = region;
+
+	INIT_LIST_HEAD(&nreg->list);
+
+	atomic_set(&nreg->pending, 0);
+	nreg->delayed_bhs = NULL;
+	write_lock_irq(&rh->hash_lock);
+
+	reg = __rh_lookup(rh, region);
+	if (reg)
+		/* we lost the race */
+		mempool_free(nreg, rh->region_pool);
+
+	else {
+		__rh_insert(rh, nreg);
+		if (nreg->state == RH_CLEAN) {
+			spin_lock_irq(&rh->region_lock);
+			list_add(&nreg->list, &rh->clean_regions);
+			spin_unlock_irq(&rh->region_lock);
+		}
+		reg = nreg;
+	}
+	write_unlock_irq(&rh->hash_lock);
+	read_lock(&rh->hash_lock);
+
+	return reg;
+}
+
+static inline struct region *__rh_find(struct region_hash *rh, region_t region)
+{
+	struct region *reg;
+
+	reg = __rh_lookup(rh, region);
+	if (!reg)
+		reg = __rh_alloc(rh, region);
+
+	return reg;
+}
+
+static int rh_state(struct region_hash *rh, region_t region, int may_block)
+{
+	int r;
+	struct region *reg;
+
+	read_lock(&rh->hash_lock);
+	reg = __rh_lookup(rh, region);
+	read_unlock(&rh->hash_lock);
+
+	if (reg)
+		return reg->state;
+
+	/*
+	 * The region wasn't in the hash, so we fall back to the
+	 * dirty log.
+	 */
+	r = rh->log->type->in_sync(rh->log, region, may_block);
+
+	/*
+	 * Any error from the dirty log (eg. -EWOULDBLOCK) gets
+	 * taken as a RH_NOSYNC
+	 */
+	return r == 1 ? RH_CLEAN : RH_NOSYNC;
+}
+
+static inline int rh_in_sync(struct region_hash *rh,
+			     region_t region, int may_block)
+{
+	int state = rh_state(rh, region, may_block);
+	return state == RH_CLEAN || state == RH_DIRTY;
+}
+
+static void dispatch_buffers(struct mirror_set *ms, struct buffer_head *bh)
+{
+	struct buffer_head *nbh;
+
+	while (bh) {
+		nbh = bh->b_reqnext;
+		queue_bh(ms, bh, WRITE);
+		bh = nbh;
+	}
+}
+
+static void rh_update_states(struct region_hash *rh)
+{
+	struct list_head *tmp, *tmp2;
+	struct region *reg;
+
+	LIST_HEAD(clean);
+	LIST_HEAD(recovered);
+
+	/*
+	 * Quickly grab the lists.
+	 */
+	write_lock_irq(&rh->hash_lock);
+	spin_lock(&rh->region_lock);
+	if (!list_empty(&rh->clean_regions)) {
+		list_splice(&rh->clean_regions, &clean);
+		INIT_LIST_HEAD(&rh->clean_regions);
+
+		list_for_each_entry (reg, &clean, list) {
+			rh->log->type->clear_region(rh->log, reg->key);
+			list_del(&reg->hash_list);
+		}
+	}
+
+	if (!list_empty(&rh->recovered_regions)) {
+		list_splice(&rh->recovered_regions, &recovered);
+		INIT_LIST_HEAD(&rh->recovered_regions);
+
+		list_for_each_entry (reg, &recovered, list)
+			list_del(&reg->hash_list);
+	}
+	spin_unlock(&rh->region_lock);
+	write_unlock_irq(&rh->hash_lock);
+
+	/*
+	 * All the regions on the recovered and clean lists have
+	 * now been pulled out of the system, so no need to do
+	 * any more locking.
+	 */
+	list_for_each_safe (tmp, tmp2, &recovered) {
+		reg = list_entry(tmp, struct region, list);
+
+		rh->log->type->complete_resync_work(rh->log, reg->key, 1);
+		dispatch_buffers(rh->ms, reg->delayed_bhs);
+		up(&rh->recovery_count);
+		mempool_free(reg, rh->region_pool);
+	}
+
+	list_for_each_safe (tmp, tmp2, &clean) {
+		reg = list_entry(tmp, struct region, list);
+		mempool_free(reg, rh->region_pool);
+	}
+}
+
+static void rh_inc(struct region_hash *rh, region_t region)
+{
+	struct region *reg;
+
+	read_lock(&rh->hash_lock);
+	reg = __rh_find(rh, region);
+	if (reg->state == RH_CLEAN) {
+		rh->log->type->mark_region(rh->log, reg->key);
+
+		spin_lock_irq(&rh->region_lock);
+		reg->state = RH_DIRTY;
+		list_del_init(&reg->list);	/* take off the clean list */
+		spin_unlock_irq(&rh->region_lock);
+	}
+
+	atomic_inc(&reg->pending);
+	read_unlock(&rh->hash_lock);
+}
+
+static void rh_inc_pending(struct region_hash *rh, struct buffer_list *buffers)
+{
+	struct buffer_head *bh;
+
+	for (bh = buffers->head; bh; bh = bh->b_reqnext)
+		rh_inc(rh, bh_to_region(rh, bh));
+}
+
+static void rh_dec(struct region_hash *rh, region_t region)
+{
+	unsigned long flags;
+	struct region *reg;
+	int wake = 0;
+
+	read_lock(&rh->hash_lock);
+	reg = __rh_lookup(rh, region);
+	read_unlock(&rh->hash_lock);
+
+	if (atomic_dec_and_test(&reg->pending)) {
+		spin_lock_irqsave(&rh->region_lock, flags);
+		if (reg->state == RH_RECOVERING) {
+			list_add_tail(&reg->list, &rh->quiesced_regions);
+		} else {
+			reg->state = RH_CLEAN;
+			list_add(&reg->list, &rh->clean_regions);
+		}
+		spin_unlock_irqrestore(&rh->region_lock, flags);
+		wake = 1;
+	}
+
+	if (wake)
+		dm_daemon_wake(&_kmirrord);
+}
+
+/*
+ * Starts quiescing a region in preparation for recovery.
+ */
+static int __rh_recovery_prepare(struct region_hash *rh)
+{
+	int r;
+	struct region *reg;
+	region_t region;
+
+	/*
+	 * Ask the dirty log what's next.
+	 */
+	r = rh->log->type->get_resync_work(rh->log, &region);
+	if (r <= 0)
+		return r;
+
+	/*
+	 * Get this region, and start it quiescing by setting the
+	 * recovering flag.
+	 */
+	read_lock(&rh->hash_lock);
+	reg = __rh_find(rh, region);
+	read_unlock(&rh->hash_lock);
+
+	spin_lock_irq(&rh->region_lock);
+	reg->state = RH_RECOVERING;
+
+	/* Already quiesced ? */
+	if (atomic_read(&reg->pending))
+		list_del_init(&reg->list);
+
+	else {
+		list_del_init(&reg->list);
+		list_add(&reg->list, &rh->quiesced_regions);
+	}
+	spin_unlock_irq(&rh->region_lock);
+
+	return 1;
+}
+
+static void rh_recovery_prepare(struct region_hash *rh)
+{
+	while (!down_trylock(&rh->recovery_count))
+		if (__rh_recovery_prepare(rh) <= 0) {
+			up(&rh->recovery_count);
+			break;
+		}
+}
+
+/*
+ * Returns any quiesced regions.
+ */
+static struct region *rh_recovery_start(struct region_hash *rh)
+{
+	struct region *reg = NULL;
+
+	spin_lock_irq(&rh->region_lock);
+	if (!list_empty(&rh->quiesced_regions)) {
+		reg = list_entry(rh->quiesced_regions.next,
+				 struct region, list);
+		list_del_init(&reg->list);	/* remove from the quiesced list */
+	}
+	spin_unlock_irq(&rh->region_lock);
+
+	return reg;
+}
+
+/* FIXME: success ignored for now */
+static void rh_recovery_end(struct region *reg, int success)
+{
+	struct region_hash *rh = reg->rh;
+
+	spin_lock_irq(&rh->region_lock);
+	list_add(&reg->list, &reg->rh->recovered_regions);
+	spin_unlock_irq(&rh->region_lock);
+
+	dm_daemon_wake(&_kmirrord);
+}
+
+static void rh_flush(struct region_hash *rh)
+{
+	rh->log->type->flush(rh->log);
+}
+
+static void rh_delay(struct region_hash *rh, struct buffer_head *bh)
+{
+	struct region *reg;
+
+	read_lock(&rh->hash_lock);
+	reg = __rh_find(rh, bh_to_region(rh, bh));
+	bh->b_reqnext = reg->delayed_bhs;
+	reg->delayed_bhs = bh;
+	read_unlock(&rh->hash_lock);
+}
+
+static void rh_stop_recovery(struct region_hash *rh)
+{
+	int i;
+
+	/* wait for any recovering regions */
+	for (i = 0; i < MAX_RECOVERY; i++)
+		down(&rh->recovery_count);
+}
+
+static void rh_start_recovery(struct region_hash *rh)
+{
+	int i;
+
+	for (i = 0; i < MAX_RECOVERY; i++)
+		up(&rh->recovery_count);
+
+	dm_daemon_wake(&_kmirrord);
+}
+
+/*-----------------------------------------------------------------
+ * Mirror set structures.
+ *---------------------------------------------------------------*/
+struct mirror {
+	atomic_t error_count;
+	struct dm_dev *dev;
+	sector_t offset;
+};
+
+struct mirror_set {
+	struct dm_target *ti;
+	struct list_head list;
+	struct region_hash rh;
+	struct kcopyd_client *kcopyd_client;
+
+	spinlock_t lock;	/* protects the next two lists */
+	struct buffer_list reads;
+	struct buffer_list writes;
+
+	/* recovery */
+	region_t nr_regions;
+	region_t sync_count;
+
+	unsigned int nr_mirrors;
+	struct mirror mirror[0];
+};
+
+/*
+ * Every mirror should look like this one.
+ */
+#define DEFAULT_MIRROR 0
+
+/*
+ * This is yucky.  We squirrel the mirror_set struct away inside
+ * b_reqnext for write buffers.  This is safe since the bh
+ * doesn't get submitted to the lower levels of block layer.
+ */
+static struct mirror_set *bh_get_ms(struct buffer_head *bh)
+{
+	return (struct mirror_set *) bh->b_reqnext;
+}
+
+static void bh_set_ms(struct buffer_head *bh, struct mirror_set *ms)
+{
+	bh->b_reqnext = (struct buffer_head *) ms;
+}
+
+/*-----------------------------------------------------------------
+ * Recovery.
+ *
+ * When a mirror is first activated we may find that some regions
+ * are in the no-sync state.  We have to recover these by
+ * recopying from the default mirror to all the others.
+ *---------------------------------------------------------------*/
+static void recovery_complete(int read_err, unsigned int write_err,
+			      void *context)
+{
+	struct region *reg = (struct region *) context;
+	struct mirror_set *ms = reg->rh->ms;
+
+	/* FIXME: better error handling */
+	rh_recovery_end(reg, read_err || write_err);
+	if (++ms->sync_count == ms->nr_regions)
+		/* the sync is complete */
+		dm_table_event(ms->ti->table);
+}
+
+static int recover(struct mirror_set *ms, struct region *reg)
+{
+	int r;
+	unsigned int i;
+	struct io_region from, to[ms->nr_mirrors - 1], *dest;
+	struct mirror *m;
+	unsigned int flags = 0;
+
+	/* fill in the source */
+	m = ms->mirror + DEFAULT_MIRROR;
+	from.dev = m->dev->dev;
+	from.sector = m->offset + region_to_sector(reg->rh, reg->key);
+	if (reg->key == (ms->nr_regions - 1)) {
+		/*
+		 * The final region may be smaller than
+		 * region_size.
+		 */
+		from.count = ms->ti->len & (reg->rh->region_size - 1);
+		if (!from.count)
+			from.count = reg->rh->region_size;
+	} else
+		from.count = reg->rh->region_size;
+
+	/* fill in the destinations */
+	for (i = 1; i < ms->nr_mirrors; i++) {
+		m = ms->mirror + i;
+		dest = to + (i - 1);
+
+		dest->dev = m->dev->dev;
+		dest->sector = m->offset + region_to_sector(reg->rh, reg->key);
+		dest->count = from.count;
+	}
+
+	/* hand to kcopyd */
+	set_bit(KCOPYD_IGNORE_ERROR, &flags);
+	r = kcopyd_copy(ms->kcopyd_client, &from, ms->nr_mirrors - 1, to, flags,
+			recovery_complete, reg);
+
+	return r;
+}
+
+static void do_recovery(struct mirror_set *ms)
+{
+	int r;
+	struct region *reg;
+
+	/*
+	 * Start quiescing some regions.
+	 */
+	rh_recovery_prepare(&ms->rh);
+
+	/*
+	 * Copy any already quiesced regions.
+	 */
+	while ((reg = rh_recovery_start(&ms->rh))) {
+		r = recover(ms, reg);
+		if (r)
+			rh_recovery_end(reg, 0);
+	}
+}
+
+/*-----------------------------------------------------------------
+ * Reads
+ *---------------------------------------------------------------*/
+static struct mirror *choose_mirror(struct mirror_set *ms, sector_t sector)
+{
+	/* FIXME: add read balancing */
+	return ms->mirror + DEFAULT_MIRROR;
+}
+
+/*
+ * remap a buffer to a particular mirror.
+ */
+static void map_buffer(struct mirror_set *ms,
+		       struct mirror *m, struct buffer_head *bh)
+{
+	bh->b_rdev = m->dev->dev;
+	bh->b_rsector = m->offset + (bh->b_rsector - ms->ti->begin);
+}
+
+static void do_reads(struct mirror_set *ms, struct buffer_list *reads)
+{
+	region_t region;
+	struct buffer_head *bh;
+	struct mirror *m;
+
+	while ((bh = buffer_list_pop(reads))) {
+		region = bh_to_region(&ms->rh, bh);
+
+		/*
+		 * We can only read balance if the region is in sync.
+		 */
+		if (rh_in_sync(&ms->rh, region, 0))
+			m = choose_mirror(ms, bh->b_rsector);
+		else
+			m = ms->mirror + DEFAULT_MIRROR;
+
+		map_buffer(ms, m, bh);
+		generic_make_request(READ, bh);
+	}
+}
+
+/*-----------------------------------------------------------------
+ * Writes.
+ *
+ * We do different things with the write io depending on the
+ * state of the region that it's in:
+ *
+ * SYNC: 	increment pending, use kcopyd to write to *all* mirrors
+ * RECOVERING:	delay the io until recovery completes
+ * NOSYNC:	increment pending, just write to the default mirror
+ *---------------------------------------------------------------*/
+static void write_callback(unsigned int error, void *context)
+{
+	unsigned int i;
+	int uptodate = 1;
+	struct buffer_head *bh = (struct buffer_head *) context;
+	struct mirror_set *ms;
+
+	ms = bh_get_ms(bh);
+	bh_set_ms(bh, NULL);
+
+	/*
+	 * NOTE: We don't decrement the pending count here,
+	 * instead it is done by the targets endio function.
+	 * This way we handle both writes to SYNC and NOSYNC
+	 * regions with the same code.
+	 */
+
+	if (error) {
+		/*
+		 * only error the io if all mirrors failed.
+		 * FIXME: bogus
+		 */
+		uptodate = 0;
+		for (i = 0; i < ms->nr_mirrors; i++)
+			if (!test_bit(i, &error)) {
+				uptodate = 1;
+				break;
+			}
+	}
+	bh->b_end_io(bh, uptodate);
+}
+
+static void do_write(struct mirror_set *ms, struct buffer_head *bh)
+{
+	unsigned int i;
+	struct io_region io[ms->nr_mirrors];
+	struct mirror *m;
+
+	for (i = 0; i < ms->nr_mirrors; i++) {
+		m = ms->mirror + i;
+
+		io[i].dev = m->dev->dev;
+		io[i].sector = m->offset + (bh->b_rsector - ms->ti->begin);
+		io[i].count = bh->b_size >> 9;
+	}
+
+	bh_set_ms(bh, ms);
+	dm_io_async(ms->nr_mirrors, io, WRITE, bh->b_page,
+		    (unsigned int) bh->b_data & ~PAGE_MASK, write_callback, bh);
+}
+
+static void do_writes(struct mirror_set *ms, struct buffer_list *writes)
+{
+	int state;
+	struct buffer_head *bh;
+	struct buffer_list sync, nosync, recover, *this_list = NULL;
+
+	if (!writes->head)
+		return;
+
+	/*
+	 * Classify each write.
+	 */
+	buffer_list_init(&sync);
+	buffer_list_init(&nosync);
+	buffer_list_init(&recover);
+
+	while ((bh = buffer_list_pop(writes))) {
+		state = rh_state(&ms->rh, bh_to_region(&ms->rh, bh), 1);
+		switch (state) {
+		case RH_CLEAN:
+		case RH_DIRTY:
+			this_list = &sync;
+			break;
+
+		case RH_NOSYNC:
+			this_list = &nosync;
+			break;
+
+		case RH_RECOVERING:
+			this_list = &recover;
+			break;
+		}
+
+		buffer_list_add(this_list, bh);
+	}
+
+	/*
+	 * Increment the pending counts for any regions that will
+	 * be written to (writes to recover regions are going to
+	 * be delayed).
+	 */
+	rh_inc_pending(&ms->rh, &sync);
+	rh_inc_pending(&ms->rh, &nosync);
+	rh_flush(&ms->rh);
+
+	/*
+	 * Dispatch io.
+	 */
+	while ((bh = buffer_list_pop(&sync)))
+		do_write(ms, bh);
+
+	while ((bh = buffer_list_pop(&recover)))
+		rh_delay(&ms->rh, bh);
+
+	while ((bh = buffer_list_pop(&nosync))) {
+		map_buffer(ms, ms->mirror + DEFAULT_MIRROR, bh);
+		generic_make_request(WRITE, bh);
+	}
+}
+
+/*-----------------------------------------------------------------
+ * kmirrord
+ *---------------------------------------------------------------*/
+static LIST_HEAD(_mirror_sets);
+static DECLARE_RWSEM(_mirror_sets_lock);
+
+static void do_mirror(struct mirror_set *ms)
+{
+	struct buffer_list reads, writes;
+
+	spin_lock(&ms->lock);
+	memcpy(&reads, &ms->reads, sizeof(reads));
+	buffer_list_init(&ms->reads);
+	memcpy(&writes, &ms->writes, sizeof(writes));
+	buffer_list_init(&ms->writes);
+	spin_unlock(&ms->lock);
+
+	rh_update_states(&ms->rh);
+	do_recovery(ms);
+	do_reads(ms, &reads);
+	do_writes(ms, &writes);
+	run_task_queue(&tq_disk);
+}
+
+static void do_work(void)
+{
+	struct mirror_set *ms;
+
+	down_read(&_mirror_sets_lock);
+	list_for_each_entry (ms, &_mirror_sets, list)
+		do_mirror(ms);
+	up_read(&_mirror_sets_lock);
+}
+
+/*-----------------------------------------------------------------
+ * Target functions
+ *---------------------------------------------------------------*/
+static struct mirror_set *alloc_context(unsigned int nr_mirrors,
+					sector_t region_size,
+					struct dm_target *ti,
+					struct dirty_log *dl)
+{
+	size_t len;
+	struct mirror_set *ms = NULL;
+
+	if (array_too_big(sizeof(*ms), sizeof(ms->mirror[0]), nr_mirrors))
+		return NULL;
+
+	len = sizeof(*ms) + (sizeof(ms->mirror[0]) * nr_mirrors);
+
+	ms = kmalloc(len, GFP_KERNEL);
+	if (!ms) {
+		ti->error = "dm-mirror: Cannot allocate mirror context";
+		return NULL;
+	}
+
+	memset(ms, 0, len);
+	spin_lock_init(&ms->lock);
+
+	ms->ti = ti;
+	ms->nr_mirrors = nr_mirrors;
+	ms->nr_regions = dm_div_up(ti->len, region_size);
+
+	if (rh_init(&ms->rh, ms, dl, region_size, ms->nr_regions)) {
+		ti->error = "dm-mirror: Error creating dirty region hash";
+		kfree(ms);
+		return NULL;
+	}
+
+	return ms;
+}
+
+static void free_context(struct mirror_set *ms, struct dm_target *ti,
+			 unsigned int m)
+{
+	while (m--)
+		dm_put_device(ti, ms->mirror[m].dev);
+
+	rh_exit(&ms->rh);
+	kfree(ms);
+}
+
+static inline int _check_region_size(struct dm_target *ti, sector_t size)
+{
+	return !(size % (PAGE_SIZE >> 9) || (size & (size - 1)) ||
+		 size > ti->len);
+}
+
+static int get_mirror(struct mirror_set *ms, struct dm_target *ti,
+		      unsigned int mirror, char **argv)
+{
+	sector_t offset;
+
+	if (sscanf(argv[1], SECTOR_FORMAT, &offset) != 1) {
+		ti->error = "dm-mirror: Invalid offset";
+		return -EINVAL;
+	}
+
+	if (dm_get_device(ti, argv[0], offset, ti->len,
+			  dm_table_get_mode(ti->table),
+			  &ms->mirror[mirror].dev)) {
+		ti->error = "dm-mirror: Device lookup failure";
+		return -ENXIO;
+	}
+
+	ms->mirror[mirror].offset = offset;
+
+	return 0;
+}
+
+static int add_mirror_set(struct mirror_set *ms)
+{
+	down_write(&_mirror_sets_lock);
+	list_add_tail(&ms->list, &_mirror_sets);
+	up_write(&_mirror_sets_lock);
+	dm_daemon_wake(&_kmirrord);
+
+	return 0;
+}
+
+static void del_mirror_set(struct mirror_set *ms)
+{
+	down_write(&_mirror_sets_lock);
+	list_del(&ms->list);
+	up_write(&_mirror_sets_lock);
+}
+
+/*
+ * Create dirty log: log_type #log_params <log_params>
+ */
+static struct dirty_log *create_dirty_log(struct dm_target *ti,
+					  unsigned int argc, char **argv,
+					  unsigned int *args_used)
+{
+	unsigned int param_count;
+	struct dirty_log *dl;
+
+	if (argc < 2) {
+		ti->error = "dm-mirror: Insufficient mirror log arguments";
+		return NULL;
+	}
+
+	if (sscanf(argv[1], "%u", &param_count) != 1 || param_count != 1) {
+		ti->error = "dm-mirror: Invalid mirror log argument count";
+		return NULL;
+	}
+
+	*args_used = 2 + param_count;
+
+	if (argc < *args_used) {
+		ti->error = "dm-mirror: Insufficient mirror log arguments";
+		return NULL;
+	}
+
+	dl = dm_create_dirty_log(argv[0], ti->len, param_count, argv + 2);
+	if (!dl) {
+		ti->error = "dm-mirror: Error creating mirror dirty log";
+		return NULL;
+	}
+
+	if (!_check_region_size(ti, dl->type->get_region_size(dl))) {
+		ti->error = "dm-mirror: Invalid region size";
+		dm_destroy_dirty_log(dl);
+		return NULL;
+	}
+
+	return dl;
+}
+
+/*
+ * Construct a mirror mapping:
+ *
+ * log_type #log_params <log_params>
+ * #mirrors [mirror_path offset]{2,}
+ *
+ * For now, #log_params = 1, log_type = "core"
+ *
+ */
+#define DM_IO_PAGES 64
+static int mirror_ctr(struct dm_target *ti, unsigned int argc, char **argv)
+{
+	int r;
+	unsigned int nr_mirrors, m, args_used;
+	struct mirror_set *ms;
+	struct dirty_log *dl;
+
+	dl = create_dirty_log(ti, argc, argv, &args_used);
+	if (!dl)
+		return -EINVAL;
+
+	argv += args_used;
+	argc -= args_used;
+
+	if (!argc || sscanf(argv[0], "%u", &nr_mirrors) != 1 ||
+	    nr_mirrors < 2) {
+		ti->error = "dm-mirror: Invalid number of mirrors";
+		dm_destroy_dirty_log(dl);
+		return -EINVAL;
+	}
+
+	argv++, argc--;
+
+	if (argc != nr_mirrors * 2) {
+		ti->error = "dm-mirror: Wrong number of mirror arguments";
+		dm_destroy_dirty_log(dl);
+		return -EINVAL;
+	}
+
+	ms = alloc_context(nr_mirrors, dl->type->get_region_size(dl), ti, dl);
+	if (!ms) {
+		dm_destroy_dirty_log(dl);
+		return -ENOMEM;
+	}
+
+	/* Get the mirror parameter sets */
+	for (m = 0; m < nr_mirrors; m++) {
+		r = get_mirror(ms, ti, m, argv);
+		if (r) {
+			free_context(ms, ti, m);
+			return r;
+		}
+		argv += 2;
+		argc -= 2;
+	}
+
+	ti->private = ms;
+
+	r = kcopyd_client_create(DM_IO_PAGES, &ms->kcopyd_client);
+	if (r) {
+		free_context(ms, ti, ms->nr_mirrors);
+		return r;
+	}
+
+	add_mirror_set(ms);
+	return 0;
+}
+
+static void mirror_dtr(struct dm_target *ti)
+{
+	struct mirror_set *ms = (struct mirror_set *) ti->private;
+
+	del_mirror_set(ms);
+	kcopyd_client_destroy(ms->kcopyd_client);
+	free_context(ms, ti, ms->nr_mirrors);
+}
+
+static void queue_bh(struct mirror_set *ms, struct buffer_head *bh, int rw)
+{
+	int wake = 0;
+	struct buffer_list *bl;
+
+	bl = (rw == WRITE) ? &ms->writes : &ms->reads;
+	spin_lock(&ms->lock);
+	wake = !(bl->head);
+	buffer_list_add(bl, bh);
+	spin_unlock(&ms->lock);
+
+	if (wake)
+		dm_daemon_wake(&_kmirrord);
+}
+
+/*
+ * Mirror mapping function
+ */
+static int mirror_map(struct dm_target *ti, struct buffer_head *bh,
+		      int rw, union map_info *map_context)
+{
+	int r;
+	struct mirror *m;
+	struct mirror_set *ms = ti->private;
+
+	/* FIXME: nasty hack, 32 bit sector_t only */
+	map_context->ll = bh->b_rsector / ms->rh.region_size;
+
+	if (rw == WRITE) {
+		queue_bh(ms, bh, rw);
+		return 0;
+	}
+
+	r = ms->rh.log->type->in_sync(ms->rh.log, bh_to_region(&ms->rh, bh), 0);
+	if (r < 0 && r != -EWOULDBLOCK)
+		return r;
+
+	if (r == -EWOULDBLOCK)	/* FIXME: ugly */
+		r = 0;
+
+	/*
+	 * We don't want to fast track a recovery just for a read
+	 * ahead.  So we just let it silently fail.
+	 * FIXME: get rid of this.
+	 */
+	if (!r && rw == READA)
+		return -EIO;
+
+	if (!r) {
+		/* Pass this io over to the daemon */
+		queue_bh(ms, bh, rw);
+		return 0;
+	}
+
+	m = choose_mirror(ms, bh->b_rsector);
+	if (!m)
+		return -EIO;
+
+	map_buffer(ms, m, bh);
+	return 1;
+}
+
+static int mirror_end_io(struct dm_target *ti, struct buffer_head *bh,
+			 int rw, int error, union map_info *map_context)
+{
+	struct mirror_set *ms = (struct mirror_set *) ti->private;
+	region_t region = map_context->ll;
+
+	/*
+	 * We need to dec pending if this was a write.
+	 */
+	if (rw == WRITE)
+		rh_dec(&ms->rh, region);
+
+	return 0;
+}
+
+static void mirror_suspend(struct dm_target *ti)
+{
+	struct mirror_set *ms = (struct mirror_set *) ti->private;
+	rh_stop_recovery(&ms->rh);
+}
+
+static void mirror_resume(struct dm_target *ti)
+{
+	struct mirror_set *ms = (struct mirror_set *) ti->private;
+	rh_start_recovery(&ms->rh);
+}
+
+static int mirror_status(struct dm_target *ti, status_type_t type,
+			 char *result, unsigned int maxlen)
+{
+	unsigned int m, sz = 0;
+	struct mirror_set *ms = (struct mirror_set *) ti->private;
+
+	switch (type) {
+	case STATUSTYPE_INFO:
+		sz += snprintf(result + sz, maxlen - sz, "%d ", ms->nr_mirrors);
+
+		for (m = 0; m < ms->nr_mirrors; m++)
+			sz += snprintf(result + sz, maxlen - sz, "%s ",
+				       dm_kdevname(ms->mirror[m].dev->dev));
+
+		sz += snprintf(result + sz, maxlen - sz, "%lu/%lu",
+			       ms->sync_count, ms->nr_regions);
+		break;
+
+	case STATUSTYPE_TABLE:
+		sz += snprintf(result + sz, maxlen - sz,
+			       "%s 1 " SECTOR_FORMAT " %d ",
+			       ms->rh.log->type->name, ms->rh.region_size,
+			       ms->nr_mirrors);
+
+		for (m = 0; m < ms->nr_mirrors; m++)
+			sz += snprintf(result + sz, maxlen - sz, "%s %ld ",
+				       dm_kdevname(ms->mirror[m].dev->dev),
+				       ms->mirror[m].offset);
+	}
+
+	return 0;
+}
+
+static struct target_type mirror_target = {
+	.name	 = "mirror",
+	.module	 = THIS_MODULE,
+	.ctr	 = mirror_ctr,
+	.dtr	 = mirror_dtr,
+	.map	 = mirror_map,
+	.end_io	 = mirror_end_io,
+	.suspend = mirror_suspend,
+	.resume	 = mirror_resume,
+	.status	 = mirror_status,
+};
+
+static int __init dm_mirror_init(void)
+{
+	int r;
+
+	r = dm_dirty_log_init();
+	if (r)
+		return r;
+
+	r = dm_daemon_start(&_kmirrord, "kmirrord", do_work);
+	if (r) {
+		DMERR("couldn't start kmirrord");
+		dm_dirty_log_exit();
+		return r;
+	}
+
+	r = dm_register_target(&mirror_target);
+	if (r < 0) {
+		DMERR("%s: Failed to register mirror target",
+		      mirror_target.name);
+		dm_dirty_log_exit();
+		dm_daemon_stop(&_kmirrord);
+	}
+
+	return r;
+}
+
+static void __exit dm_mirror_exit(void)
+{
+	int r;
+
+	r = dm_unregister_target(&mirror_target);
+	if (r < 0)
+		DMERR("%s: unregister failed %d", mirror_target.name, r);
+
+	dm_daemon_stop(&_kmirrord);
+	dm_dirty_log_exit();
+}
+
+/* Module hooks */
+module_init(dm_mirror_init);
+module_exit(dm_mirror_exit);
+
+MODULE_DESCRIPTION(DM_NAME " mirror target");
+MODULE_AUTHOR("Heinz Mauelshagen <mge@sistina.com>");
+MODULE_LICENSE("GPL");
diff -urN --exclude-from=diff-exclude linux-2.4.25/drivers/md/dm-snapshot.c linux-2.4.25-leo/drivers/md/dm-snapshot.c
--- linux-2.4.25/drivers/md/dm-snapshot.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.25-leo/drivers/md/dm-snapshot.c	2004-02-20 18:21:57.000000000 +0000
@@ -0,0 +1,1237 @@
+/*
+ * dm-snapshot.c
+ *
+ * Copyright (C) 2001-2002 Sistina Software (UK) Limited.
+ *
+ * This file is released under the GPL.
+ */
+
+#include <linux/config.h>
+#include <linux/ctype.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/fs.h>
+#include <linux/blkdev.h>
+#include <linux/mempool.h>
+#include <linux/device-mapper.h>
+#include <linux/vmalloc.h>
+
+#include "dm-snapshot.h"
+#include "kcopyd.h"
+
+/*
+ * FIXME: Remove this before release.
+ */
+#if 0
+#define DMDEBUG(x...) DMWARN( ## x)
+#else
+#define DMDEBUG(x...)
+#endif
+
+/*
+ * The percentage increment we will wake up users at
+ */
+#define WAKE_UP_PERCENT 5
+
+/*
+ * kcopyd priority of snapshot operations
+ */
+#define SNAPSHOT_COPY_PRIORITY 2
+
+/*
+ * Each snapshot reserves this many pages for io
+ * FIXME: calculate this
+ */
+#define SNAPSHOT_PAGES 256
+
+struct pending_exception {
+	struct exception e;
+
+	/*
+	 * Origin buffers waiting for this to complete are held
+	 * in a list (using b_reqnext).
+	 */
+	struct buffer_head *origin_bhs;
+	struct buffer_head *snapshot_bhs;
+
+	/*
+	 * Other pending_exceptions that are processing this
+	 * chunk.  When this list is empty, we know we can
+	 * complete the origins.
+	 */
+	struct list_head siblings;
+
+	/* Pointer back to snapshot context */
+	struct dm_snapshot *snap;
+
+	/*
+	 * 1 indicates the exception has already been sent to
+	 * kcopyd.
+	 */
+	int started;
+};
+
+/*
+ * Hash table mapping origin volumes to lists of snapshots and
+ * a lock to protect it
+ */
+static kmem_cache_t *exception_cache;
+static kmem_cache_t *pending_cache;
+static mempool_t *pending_pool;
+
+/*
+ * One of these per registered origin, held in the snapshot_origins hash
+ */
+struct origin {
+	/* The origin device */
+	kdev_t dev;
+
+	struct list_head hash_list;
+
+	/* List of snapshots for this origin */
+	struct list_head snapshots;
+};
+
+/*
+ * Size of the hash table for origin volumes. If we make this
+ * the size of the minors list then it should be nearly perfect
+ */
+#define ORIGIN_HASH_SIZE 256
+#define ORIGIN_MASK      0xFF
+static struct list_head *_origins;
+static struct rw_semaphore _origins_lock;
+
+static int init_origin_hash(void)
+{
+	int i;
+
+	_origins = kmalloc(ORIGIN_HASH_SIZE * sizeof(struct list_head),
+			   GFP_KERNEL);
+	if (!_origins) {
+		DMERR("Device mapper: Snapshot: unable to allocate memory");
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < ORIGIN_HASH_SIZE; i++)
+		INIT_LIST_HEAD(_origins + i);
+	init_rwsem(&_origins_lock);
+
+	return 0;
+}
+
+static void exit_origin_hash(void)
+{
+	kfree(_origins);
+}
+
+static inline unsigned int origin_hash(kdev_t dev)
+{
+	return MINOR(dev) & ORIGIN_MASK;
+}
+
+static struct origin *__lookup_origin(kdev_t origin)
+{
+	struct list_head *slist;
+	struct list_head *ol;
+	struct origin *o;
+
+	ol = &_origins[origin_hash(origin)];
+	list_for_each(slist, ol) {
+		o = list_entry(slist, struct origin, hash_list);
+
+		if (o->dev == origin)
+			return o;
+	}
+
+	return NULL;
+}
+
+static void __insert_origin(struct origin *o)
+{
+	struct list_head *sl = &_origins[origin_hash(o->dev)];
+	list_add_tail(&o->hash_list, sl);
+}
+
+/*
+ * Make a note of the snapshot and its origin so we can look it
+ * up when the origin has a write on it.
+ */
+static int register_snapshot(struct dm_snapshot *snap)
+{
+	struct origin *o;
+	kdev_t dev = snap->origin->dev;
+
+	down_write(&_origins_lock);
+	o = __lookup_origin(dev);
+
+	if (!o) {
+		/* New origin */
+		o = kmalloc(sizeof(*o), GFP_KERNEL);
+		if (!o) {
+			up_write(&_origins_lock);
+			return -ENOMEM;
+		}
+
+		/* Initialise the struct */
+		INIT_LIST_HEAD(&o->snapshots);
+		o->dev = dev;
+
+		__insert_origin(o);
+	}
+
+	list_add_tail(&snap->list, &o->snapshots);
+
+	up_write(&_origins_lock);
+	return 0;
+}
+
+static void unregister_snapshot(struct dm_snapshot *s)
+{
+	struct origin *o;
+
+	down_write(&_origins_lock);
+	o = __lookup_origin(s->origin->dev);
+
+	list_del(&s->list);
+	if (list_empty(&o->snapshots)) {
+		list_del(&o->hash_list);
+		kfree(o);
+	}
+
+	up_write(&_origins_lock);
+}
+
+/*
+ * Implementation of the exception hash tables.
+ */
+static int init_exception_table(struct exception_table *et, uint32_t size)
+{
+	unsigned int i;
+
+	et->hash_mask = size - 1;
+	et->table = vcalloc(size, sizeof(struct list_head));
+	if (!et->table)
+		return -ENOMEM;
+
+	for (i = 0; i < size; i++)
+		INIT_LIST_HEAD(et->table + i);
+
+	return 0;
+}
+
+static void exit_exception_table(struct exception_table *et, kmem_cache_t *mem)
+{
+	struct list_head *slot, *entry, *temp;
+	struct exception *ex;
+	int i, size;
+
+	size = et->hash_mask + 1;
+	for (i = 0; i < size; i++) {
+		slot = et->table + i;
+
+		list_for_each_safe(entry, temp, slot) {
+			ex = list_entry(entry, struct exception, hash_list);
+			kmem_cache_free(mem, ex);
+		}
+	}
+
+	vfree(et->table);
+}
+
+/*
+ * FIXME: check how this hash fn is performing.
+ */
+static inline uint32_t exception_hash(struct exception_table *et, chunk_t chunk)
+{
+	return chunk & et->hash_mask;
+}
+
+static void insert_exception(struct exception_table *eh, struct exception *e)
+{
+	struct list_head *l = &eh->table[exception_hash(eh, e->old_chunk)];
+	list_add(&e->hash_list, l);
+}
+
+static inline void remove_exception(struct exception *e)
+{
+	list_del(&e->hash_list);
+}
+
+/*
+ * Return the exception data for a sector, or NULL if not
+ * remapped.
+ */
+static struct exception *lookup_exception(struct exception_table *et,
+					  chunk_t chunk)
+{
+	struct list_head *slot, *el;
+	struct exception *e;
+
+	slot = &et->table[exception_hash(et, chunk)];
+	list_for_each(el, slot) {
+		e = list_entry(el, struct exception, hash_list);
+		if (e->old_chunk == chunk)
+			return e;
+	}
+
+	return NULL;
+}
+
+static inline struct exception *alloc_exception(void)
+{
+	struct exception *e;
+
+	e = kmem_cache_alloc(exception_cache, GFP_NOIO);
+	if (!e)
+		e = kmem_cache_alloc(exception_cache, GFP_ATOMIC);
+
+	return e;
+}
+
+static inline void free_exception(struct exception *e)
+{
+	kmem_cache_free(exception_cache, e);
+}
+
+static inline struct pending_exception *alloc_pending_exception(void)
+{
+	return mempool_alloc(pending_pool, GFP_NOIO);
+}
+
+static inline void free_pending_exception(struct pending_exception *pe)
+{
+	mempool_free(pe, pending_pool);
+}
+
+int dm_add_exception(struct dm_snapshot *s, chunk_t old, chunk_t new)
+{
+	struct exception *e;
+
+	e = alloc_exception();
+	if (!e)
+		return -ENOMEM;
+
+	e->old_chunk = old;
+	e->new_chunk = new;
+	insert_exception(&s->complete, e);
+	return 0;
+}
+
+/*
+ * Hard coded magic.
+ */
+static int calc_max_buckets(void)
+{
+	unsigned long mem;
+
+	mem = num_physpages << PAGE_SHIFT;
+	mem /= 50;
+	mem /= sizeof(struct list_head);
+
+	return mem;
+}
+
+/*
+ * Rounds a number down to a power of 2.
+ */
+static inline uint32_t round_down(uint32_t n)
+{
+	while (n & (n - 1))
+		n &= (n - 1);
+	return n;
+}
+
+/*
+ * Allocate room for a suitable hash table.
+ */
+static int init_hash_tables(struct dm_snapshot *s)
+{
+	sector_t hash_size, cow_dev_size, origin_dev_size, max_buckets;
+
+	/*
+	 * Calculate based on the size of the original volume or
+	 * the COW volume...
+	 */
+	cow_dev_size = get_dev_size(s->cow->dev);
+	origin_dev_size = get_dev_size(s->origin->dev);
+	max_buckets = calc_max_buckets();
+
+	hash_size = min(origin_dev_size, cow_dev_size) / s->chunk_size;
+	hash_size = min(hash_size, max_buckets);
+
+	/* Round it down to a power of 2 */
+	hash_size = round_down(hash_size);
+	if (init_exception_table(&s->complete, hash_size))
+		return -ENOMEM;
+
+	/*
+	 * Allocate hash table for in-flight exceptions
+	 * Make this smaller than the real hash table
+	 */
+	hash_size >>= 3;
+	if (!hash_size)
+		hash_size = 64;
+
+	if (init_exception_table(&s->pending, hash_size)) {
+		exit_exception_table(&s->complete, exception_cache);
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+/*
+ * Round a number up to the nearest 'size' boundary.  size must
+ * be a power of 2.
+ */
+static inline ulong round_up(ulong n, ulong size)
+{
+	size--;
+	return (n + size) & ~size;
+}
+
+/*
+ * Construct a snapshot mapping: <origin_dev> <COW-dev> <p/n> <chunk-size>
+ */
+static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv)
+{
+	struct dm_snapshot *s;
+	unsigned long chunk_size;
+	int r = -EINVAL;
+	char persistent;
+	char *origin_path;
+	char *cow_path;
+	char *value;
+	int blocksize;
+
+	if (argc < 4) {
+		ti->error = "dm-snapshot: requires exactly 4 arguments";
+		r = -EINVAL;
+		goto bad1;
+	}
+
+	origin_path = argv[0];
+	cow_path = argv[1];
+	persistent = toupper(*argv[2]);
+
+	if (persistent != 'P' && persistent != 'N') {
+		ti->error = "Persistent flag is not P or N";
+		r = -EINVAL;
+		goto bad1;
+	}
+
+	chunk_size = simple_strtoul(argv[3], &value, 10);
+	if (chunk_size == 0 || value == NULL) {
+		ti->error = "Invalid chunk size";
+		r = -EINVAL;
+		goto bad1;
+	}
+
+	s = kmalloc(sizeof(*s), GFP_KERNEL);
+	if (s == NULL) {
+		ti->error = "Cannot allocate snapshot context private "
+		    "structure";
+		r = -ENOMEM;
+		goto bad1;
+	}
+
+	r = dm_get_device(ti, origin_path, 0, ti->len, FMODE_READ, &s->origin);
+	if (r) {
+		ti->error = "Cannot get origin device";
+		goto bad2;
+	}
+
+	/* FIXME: get cow length */
+	r = dm_get_device(ti, cow_path, 0, 0,
+			  FMODE_READ | FMODE_WRITE, &s->cow);
+	if (r) {
+		dm_put_device(ti, s->origin);
+		ti->error = "Cannot get COW device";
+		goto bad2;
+	}
+
+	/*
+	 * Chunk size must be multiple of page size.  Silently
+	 * round up if it's not.
+	 */
+	chunk_size = round_up(chunk_size, PAGE_SIZE / SECTOR_SIZE);
+
+	/* Validate the chunk size against the device block size */
+	blocksize = get_hardsect_size(s->cow->dev);
+	if (chunk_size % (blocksize / SECTOR_SIZE)) {
+		ti->error = "Chunk size is not a multiple of device blocksize";
+		r = -EINVAL;
+		goto bad3;
+	}
+
+	/* Check the sizes are small enough to fit in one kiovec */
+	if (chunk_size > KIO_MAX_SECTORS) {
+		ti->error = "Chunk size is too big";
+		r = -EINVAL;
+		goto bad3;
+	}
+
+	/* Check chunk_size is a power of 2 */
+	if (chunk_size & (chunk_size - 1)) {
+		ti->error = "Chunk size is not a power of 2";
+		r = -EINVAL;
+		goto bad3;
+	}
+
+	s->chunk_size = chunk_size;
+	s->chunk_mask = chunk_size - 1;
+	s->type = persistent;
+	for (s->chunk_shift = 0; chunk_size;
+	     s->chunk_shift++, chunk_size >>= 1)
+		;
+	s->chunk_shift--;
+
+	s->valid = 1;
+	s->have_metadata = 0;
+	s->last_percent = 0;
+	init_rwsem(&s->lock);
+	s->table = ti->table;
+
+	/* Allocate hash table for COW data */
+	if (init_hash_tables(s)) {
+		ti->error = "Unable to allocate hash table space";
+		r = -ENOMEM;
+		goto bad3;
+	}
+
+	/*
+	 * Check the persistent flag - done here because we need the iobuf
+	 * to check the LV header
+	 */
+	s->store.snap = s;
+
+	if (persistent == 'P')
+		r = dm_create_persistent(&s->store, s->chunk_size);
+	else
+		r = dm_create_transient(&s->store, s, blocksize);
+
+	if (r) {
+		ti->error = "Couldn't create exception store";
+		r = -EINVAL;
+		goto bad4;
+	}
+
+	r = kcopyd_client_create(SNAPSHOT_PAGES, &s->kcopyd_client);
+	if (r) {
+		ti->error = "Could not create kcopyd client";
+		goto bad5;
+	}
+
+	/* Flush IO to the origin device */
+	fsync_dev_lockfs(s->origin->dev);
+
+	/* Add snapshot to the list of snapshots for this origin */
+	if (register_snapshot(s)) {
+		r = -EINVAL;
+		ti->error = "Cannot register snapshot origin";
+		goto bad6;
+	}
+	unlockfs(s->origin->dev);
+
+	ti->private = s;
+	return 0;
+
+ bad6:
+	unlockfs(s->origin->dev);
+	kcopyd_client_destroy(s->kcopyd_client);
+
+ bad5:
+	s->store.destroy(&s->store);
+
+ bad4:
+	exit_exception_table(&s->pending, pending_cache);
+	exit_exception_table(&s->complete, exception_cache);
+
+ bad3:
+	dm_put_device(ti, s->cow);
+	dm_put_device(ti, s->origin);
+
+ bad2:
+	kfree(s);
+
+ bad1:
+	return r;
+}
+
+static void snapshot_dtr(struct dm_target *ti)
+{
+	struct dm_snapshot *s = (struct dm_snapshot *) ti->private;
+
+	dm_table_event(ti->table);
+
+	unregister_snapshot(s);
+
+	exit_exception_table(&s->pending, pending_cache);
+	exit_exception_table(&s->complete, exception_cache);
+
+	/* Deallocate memory used */
+	s->store.destroy(&s->store);
+
+	dm_put_device(ti, s->origin);
+	dm_put_device(ti, s->cow);
+	kcopyd_client_destroy(s->kcopyd_client);
+	kfree(s);
+}
+
+/*
+ * We hold lists of buffer_heads, using the b_reqnext field.
+ */
+static void queue_buffer(struct buffer_head **queue, struct buffer_head *bh)
+{
+	bh->b_reqnext = *queue;
+	*queue = bh;
+}
+
+/*
+ * FIXME: inefficient.
+ */
+static void queue_buffers(struct buffer_head **queue, struct buffer_head *bhs)
+{
+	while (*queue)
+		queue = &((*queue)->b_reqnext);
+
+	*queue = bhs;
+}
+
+/*
+ * Flush a list of buffers.
+ */
+static void flush_buffers(struct buffer_head *bh)
+{
+	struct buffer_head *n;
+
+	DMDEBUG("begin flush");
+	while (bh) {
+		n = bh->b_reqnext;
+		bh->b_reqnext = NULL;
+		DMDEBUG("flushing %p", bh);
+		generic_make_request(WRITE, bh);
+		bh = n;
+	}
+
+	run_task_queue(&tq_disk);
+}
+
+/*
+ * Error a list of buffers.
+ */
+static void error_buffers(struct buffer_head *bh)
+{
+	struct buffer_head *n;
+
+	while (bh) {
+		n = bh->b_reqnext;
+		bh->b_reqnext = NULL;
+		buffer_IO_error(bh);
+		bh = n;
+	}
+}
+
+static struct buffer_head *__flush_bhs(struct pending_exception *pe)
+{
+	struct pending_exception *sibling;
+
+	if (list_empty(&pe->siblings))
+		return pe->origin_bhs;
+
+	sibling = list_entry(pe->siblings.next,
+			     struct pending_exception, siblings);
+
+	list_del(&pe->siblings);
+
+	/* FIXME: I think there's a race on SMP machines here, add spin lock */
+	queue_buffers(&sibling->origin_bhs, pe->origin_bhs);
+
+	return NULL;
+}
+
+static void pending_complete(struct pending_exception *pe, int success)
+{
+	struct exception *e;
+	struct dm_snapshot *s = pe->snap;
+	struct buffer_head *flush = NULL;
+
+	if (success) {
+		e = alloc_exception();
+		if (!e) {
+			DMWARN("Unable to allocate exception.");
+			down_write(&s->lock);
+			s->store.drop_snapshot(&s->store);
+			s->valid = 0;
+			flush = __flush_bhs(pe);
+			up_write(&s->lock);
+
+			error_buffers(pe->snapshot_bhs);
+			goto out;
+		}
+
+		/*
+		 * Add a proper exception, and remove the
+		 * in-flight exception from the list.
+		 */
+		down_write(&s->lock);
+
+		memcpy(e, &pe->e, sizeof(*e));
+		insert_exception(&s->complete, e);
+		remove_exception(&pe->e);
+		flush = __flush_bhs(pe);
+
+		/* Submit any pending write BHs */
+		up_write(&s->lock);
+
+		flush_buffers(pe->snapshot_bhs);
+		DMDEBUG("Exception completed successfully.");
+
+		/* Notify any interested parties */
+		if (s->store.fraction_full) {
+			sector_t numerator, denominator;
+			int pc;
+
+			s->store.fraction_full(&s->store, &numerator,
+					       &denominator);
+			pc = numerator * 100 / denominator;
+
+			if (pc >= s->last_percent + WAKE_UP_PERCENT) {
+				dm_table_event(s->table);
+				s->last_percent = pc - pc % WAKE_UP_PERCENT;
+			}
+		}
+
+	} else {
+		/* Read/write error - snapshot is unusable */
+		down_write(&s->lock);
+		if (s->valid)
+			DMERR("Error reading/writing snapshot");
+		s->store.drop_snapshot(&s->store);
+		s->valid = 0;
+		remove_exception(&pe->e);
+		flush = __flush_bhs(pe);
+		up_write(&s->lock);
+
+		error_buffers(pe->snapshot_bhs);
+
+		dm_table_event(s->table);
+		DMDEBUG("Exception failed.");
+	}
+
+ out:
+	if (flush)
+		flush_buffers(flush);
+
+	free_pending_exception(pe);
+}
+
+static void commit_callback(void *context, int success)
+{
+	struct pending_exception *pe = (struct pending_exception *) context;
+	pending_complete(pe, success);
+}
+
+/*
+ * Called when the copy I/O has finished.  kcopyd actually runs
+ * this code so don't block.
+ */
+static void copy_callback(int read_err, unsigned int write_err, void *context)
+{
+	struct pending_exception *pe = (struct pending_exception *) context;
+	struct dm_snapshot *s = pe->snap;
+
+	if (read_err || write_err)
+		pending_complete(pe, 0);
+
+	else
+		/* Update the metadata if we are persistent */
+		s->store.commit_exception(&s->store, &pe->e, commit_callback,
+					  pe);
+}
+
+/*
+ * Dispatches the copy operation to kcopyd.
+ */
+static inline void start_copy(struct pending_exception *pe)
+{
+	struct dm_snapshot *s = pe->snap;
+	struct io_region src, dest;
+	kdev_t dev = s->origin->dev;
+	int *sizes = blk_size[major(dev)];
+	sector_t dev_size = (sector_t) -1;
+
+	if (pe->started)
+		return;
+
+	/* this is protected by snap->lock */
+	pe->started = 1;
+
+	if (sizes && sizes[minor(dev)])
+		dev_size = sizes[minor(dev)] << 1;
+
+	src.dev = dev;
+	src.sector = chunk_to_sector(s, pe->e.old_chunk);
+	src.count = min(s->chunk_size, dev_size - src.sector);
+
+	dest.dev = s->cow->dev;
+	dest.sector = chunk_to_sector(s, pe->e.new_chunk);
+	dest.count = src.count;
+
+	/* Hand over to kcopyd */
+	kcopyd_copy(s->kcopyd_client,
+		    &src, 1, &dest, 0, copy_callback, pe);
+}
+
+/*
+ * Looks to see if this snapshot already has a pending exception
+ * for this chunk, otherwise it allocates a new one and inserts
+ * it into the pending table.
+ */
+static struct pending_exception *find_pending_exception(struct dm_snapshot *s,
+							struct buffer_head *bh)
+{
+	struct exception *e;
+	struct pending_exception *pe;
+	chunk_t chunk = sector_to_chunk(s, bh->b_rsector);
+
+	/*
+	 * Is there a pending exception for this already ?
+	 */
+	e = lookup_exception(&s->pending, chunk);
+	if (e) {
+		/* cast the exception to a pending exception */
+		pe = list_entry(e, struct pending_exception, e);
+
+	} else {
+		/* Create a new pending exception */
+		pe = alloc_pending_exception();
+		pe->e.old_chunk = chunk;
+		pe->origin_bhs = pe->snapshot_bhs = NULL;
+		INIT_LIST_HEAD(&pe->siblings);
+		pe->snap = s;
+		pe->started = 0;
+
+		if (s->store.prepare_exception(&s->store, &pe->e)) {
+			free_pending_exception(pe);
+			s->valid = 0;
+			return NULL;
+		}
+
+		insert_exception(&s->pending, &pe->e);
+	}
+
+	return pe;
+}
+
+static inline void remap_exception(struct dm_snapshot *s, struct exception *e,
+				   struct buffer_head *bh)
+{
+	bh->b_rdev = s->cow->dev;
+	bh->b_rsector = chunk_to_sector(s, e->new_chunk) +
+	    (bh->b_rsector & s->chunk_mask);
+}
+
+static int snapshot_map(struct dm_target *ti, struct buffer_head *bh, int rw,
+			union map_info *map_context)
+{
+	struct exception *e;
+	struct dm_snapshot *s = (struct dm_snapshot *) ti->private;
+	int r = 1;
+	chunk_t chunk;
+	struct pending_exception *pe;
+
+	chunk = sector_to_chunk(s, bh->b_rsector);
+
+	/* Full snapshots are not usable */
+	if (!s->valid)
+		return -1;
+
+	/*
+	 * Write to snapshot - higher level takes care of RW/RO
+	 * flags so we should only get this if we are
+	 * writeable.
+	 */
+	if (rw == WRITE) {
+
+		down_write(&s->lock);
+
+		/* If the block is already remapped - use that, else remap it */
+		e = lookup_exception(&s->complete, chunk);
+		if (e)
+			remap_exception(s, e, bh);
+
+		else {
+			pe = find_pending_exception(s, bh);
+
+			if (!pe) {
+				s->store.drop_snapshot(&s->store);
+				s->valid = 0;
+				r = -EIO;
+			} else {
+				remap_exception(s, &pe->e, bh);
+				queue_buffer(&pe->snapshot_bhs, bh);
+				start_copy(pe);
+				r = 0;
+			}
+		}
+
+		up_write(&s->lock);
+
+	} else {
+		/*
+		 * FIXME: this read path scares me because we
+		 * always use the origin when we have a pending
+		 * exception.  However I can't think of a
+		 * situation where this is wrong - ejt.
+		 */
+
+		/* Do reads */
+		down_read(&s->lock);
+
+		/* See if it it has been remapped */
+		e = lookup_exception(&s->complete, chunk);
+		if (e)
+			remap_exception(s, e, bh);
+		else
+			bh->b_rdev = s->origin->dev;
+
+		up_read(&s->lock);
+	}
+
+	return r;
+}
+
+void snapshot_resume(struct dm_target *ti)
+{
+	struct dm_snapshot *s = (struct dm_snapshot *) ti->private;
+
+	if (s->have_metadata)
+		return;
+
+	if (s->store.read_metadata(&s->store)) {
+		down_write(&s->lock);
+		s->valid = 0;
+		up_write(&s->lock);
+	}
+
+	s->have_metadata = 1;
+}
+
+static int snapshot_status(struct dm_target *ti, status_type_t type,
+			   char *result, unsigned int maxlen)
+{
+	struct dm_snapshot *snap = (struct dm_snapshot *) ti->private;
+	char cow[16];
+	char org[16];
+
+	switch (type) {
+	case STATUSTYPE_INFO:
+		if (!snap->valid)
+			snprintf(result, maxlen, "Invalid");
+		else {
+			if (snap->store.fraction_full) {
+				sector_t numerator, denominator;
+				snap->store.fraction_full(&snap->store,
+							  &numerator,
+							  &denominator);
+				snprintf(result, maxlen,
+					 SECTOR_FORMAT "/" SECTOR_FORMAT,
+					 numerator, denominator);
+			}
+			else
+				snprintf(result, maxlen, "Unknown");
+		}
+		break;
+
+	case STATUSTYPE_TABLE:
+		/*
+		 * kdevname returns a static pointer so we need
+		 * to make private copies if the output is to
+		 * make sense.
+		 */
+		strncpy(cow, dm_kdevname(snap->cow->dev), sizeof(cow));
+		strncpy(org, dm_kdevname(snap->origin->dev), sizeof(org));
+		snprintf(result, maxlen, "%s %s %c %ld", org, cow,
+			 snap->type, snap->chunk_size);
+		break;
+	}
+
+	return 0;
+}
+
+/*-----------------------------------------------------------------
+ * Origin methods
+ *---------------------------------------------------------------*/
+static void list_merge(struct list_head *l1, struct list_head *l2)
+{
+	struct list_head *l1_n, *l2_p;
+
+	l1_n = l1->next;
+	l2_p = l2->prev;
+
+	l1->next = l2;
+	l2->prev = l1;
+
+	l2_p->next = l1_n;
+	l1_n->prev = l2_p;
+}
+
+static int __origin_write(struct list_head *snapshots, struct buffer_head *bh)
+{
+	int r = 1, first = 1;
+	struct list_head *sl;
+	struct dm_snapshot *snap;
+	struct exception *e;
+	struct pending_exception *pe, *last = NULL;
+	chunk_t chunk;
+
+	/* Do all the snapshots on this origin */
+	list_for_each(sl, snapshots) {
+		snap = list_entry(sl, struct dm_snapshot, list);
+
+		/* Only deal with valid snapshots */
+		if (!snap->valid)
+			continue;
+
+		down_write(&snap->lock);
+
+		/*
+		 * Remember, different snapshots can have
+		 * different chunk sizes.
+		 */
+		chunk = sector_to_chunk(snap, bh->b_rsector);
+
+		/*
+		 * Check exception table to see if block
+		 * is already remapped in this snapshot
+		 * and trigger an exception if not.
+		 */
+		e = lookup_exception(&snap->complete, chunk);
+		if (!e) {
+			pe = find_pending_exception(snap, bh);
+			if (!pe) {
+				snap->store.drop_snapshot(&snap->store);
+				snap->valid = 0;
+
+			} else {
+				if (last)
+					list_merge(&pe->siblings,
+						   &last->siblings);
+
+				last = pe;
+				r = 0;
+			}
+		}
+
+		up_write(&snap->lock);
+	}
+
+	/*
+	 * Now that we have a complete pe list we can start the copying.
+	 */
+	if (last) {
+		pe = last;
+		do {
+			down_write(&pe->snap->lock);
+			if (first)
+				queue_buffer(&pe->origin_bhs, bh);
+			start_copy(pe);
+			up_write(&pe->snap->lock);
+			first = 0;
+			pe = list_entry(pe->siblings.next,
+					struct pending_exception, siblings);
+
+		} while (pe != last);
+	}
+
+	return r;
+}
+
+/*
+ * Called on a write from the origin driver.
+ */
+int do_origin(struct dm_dev *origin, struct buffer_head *bh)
+{
+	struct origin *o;
+	int r;
+
+	down_read(&_origins_lock);
+	o = __lookup_origin(origin->dev);
+	if (!o)
+		BUG();
+
+	r = __origin_write(&o->snapshots, bh);
+	up_read(&_origins_lock);
+
+	return r;
+}
+
+/*
+ * Origin: maps a linear range of a device, with hooks for snapshotting.
+ */
+
+/*
+ * Construct an origin mapping: <dev_path>
+ * The context for an origin is merely a 'struct dm_dev *'
+ * pointing to the real device.
+ */
+static int origin_ctr(struct dm_target *ti, unsigned int argc, char **argv)
+{
+	int r;
+	struct dm_dev *dev;
+
+	if (argc != 1) {
+		ti->error = "dm-origin: incorrect number of arguments";
+		return -EINVAL;
+	}
+
+	r = dm_get_device(ti, argv[0], 0, ti->len,
+			  dm_table_get_mode(ti->table), &dev);
+	if (r) {
+		ti->error = "Cannot get target device";
+		return r;
+	}
+
+	ti->private = dev;
+	return 0;
+}
+
+static void origin_dtr(struct dm_target *ti)
+{
+	struct dm_dev *dev = (struct dm_dev *) ti->private;
+	dm_put_device(ti, dev);
+}
+
+static int origin_map(struct dm_target *ti, struct buffer_head *bh, int rw,
+		      union map_info *map_context)
+{
+	struct dm_dev *dev = (struct dm_dev *) ti->private;
+	bh->b_rdev = dev->dev;
+
+	/* Only tell snapshots if this is a write */
+	return (rw == WRITE) ? do_origin(dev, bh) : 1;
+}
+
+static int origin_status(struct dm_target *ti, status_type_t type, char *result,
+			 unsigned int maxlen)
+{
+	struct dm_dev *dev = (struct dm_dev *) ti->private;
+
+	switch (type) {
+	case STATUSTYPE_INFO:
+		result[0] = '\0';
+		break;
+
+	case STATUSTYPE_TABLE:
+		snprintf(result, maxlen, "%s", dm_kdevname(dev->dev));
+		break;
+	}
+
+	return 0;
+}
+
+static struct target_type origin_target = {
+	name:	"snapshot-origin",
+	module:	THIS_MODULE,
+	ctr:	origin_ctr,
+	dtr:	origin_dtr,
+	map:	origin_map,
+	status:	origin_status,
+};
+
+static struct target_type snapshot_target = {
+	name:	"snapshot",
+	module:	THIS_MODULE,
+	ctr:	snapshot_ctr,
+	dtr:	snapshot_dtr,
+	map:	snapshot_map,
+	resume: snapshot_resume,
+	status:	snapshot_status,
+};
+
+int __init dm_snapshot_init(void)
+{
+	int r;
+
+	r = dm_register_target(&snapshot_target);
+	if (r) {
+		DMERR("snapshot target register failed %d", r);
+		return r;
+	}
+
+	r = dm_register_target(&origin_target);
+	if (r < 0) {
+		DMERR("Device mapper: Origin: register failed %d\n", r);
+		goto bad1;
+	}
+
+	r = init_origin_hash();
+	if (r) {
+		DMERR("init_origin_hash failed.");
+		goto bad2;
+	}
+
+	exception_cache = kmem_cache_create("dm-snapshot-ex",
+					    sizeof(struct exception),
+					    __alignof__(struct exception),
+					    0, NULL, NULL);
+	if (!exception_cache) {
+		DMERR("Couldn't create exception cache.");
+		r = -ENOMEM;
+		goto bad3;
+	}
+
+	pending_cache =
+	    kmem_cache_create("dm-snapshot-in",
+			      sizeof(struct pending_exception),
+			      __alignof__(struct pending_exception),
+			      0, NULL, NULL);
+	if (!pending_cache) {
+		DMERR("Couldn't create pending cache.");
+		r = -ENOMEM;
+		goto bad4;
+	}
+
+	pending_pool = mempool_create(128, mempool_alloc_slab,
+				      mempool_free_slab, pending_cache);
+	if (!pending_pool) {
+		DMERR("Couldn't create pending pool.");
+		r = -ENOMEM;
+		goto bad5;
+	}
+
+	return 0;
+
+      bad5:
+	kmem_cache_destroy(pending_cache);
+      bad4:
+	kmem_cache_destroy(exception_cache);
+      bad3:
+	exit_origin_hash();
+      bad2:
+	dm_unregister_target(&origin_target);
+      bad1:
+	dm_unregister_target(&snapshot_target);
+	return r;
+}
+
+void dm_snapshot_exit(void)
+{
+	int r;
+
+	r = dm_unregister_target(&snapshot_target);
+	if (r)
+		DMERR("snapshot unregister failed %d", r);
+
+	r = dm_unregister_target(&origin_target);
+	if (r)
+		DMERR("origin unregister failed %d", r);
+
+	exit_origin_hash();
+	mempool_destroy(pending_pool);
+	kmem_cache_destroy(pending_cache);
+	kmem_cache_destroy(exception_cache);
+}
diff -urN --exclude-from=diff-exclude linux-2.4.25/drivers/md/dm-snapshot.h linux-2.4.25-leo/drivers/md/dm-snapshot.h
--- linux-2.4.25/drivers/md/dm-snapshot.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.25-leo/drivers/md/dm-snapshot.h	2004-02-20 18:37:06.000000000 +0000
@@ -0,0 +1,158 @@
+/*
+ * dm-snapshot.c
+ *
+ * Copyright (C) 2001-2002 Sistina Software (UK) Limited.
+ *
+ * This file is released under the GPL.
+ */
+
+#ifndef DM_SNAPSHOT_H
+#define DM_SNAPSHOT_H
+
+#include "dm.h"
+#include <linux/blkdev.h>
+
+struct exception_table {
+	uint32_t hash_mask;
+	struct list_head *table;
+};
+
+/*
+ * The snapshot code deals with largish chunks of the disk at a
+ * time. Typically 64k - 256k.
+ */
+/* FIXME: can we get away with limiting these to a uint32_t ? */
+typedef sector_t chunk_t;
+
+/*
+ * An exception is used where an old chunk of data has been
+ * replaced by a new one.
+ */
+struct exception {
+	struct list_head hash_list;
+
+	chunk_t old_chunk;
+	chunk_t new_chunk;
+};
+
+/*
+ * Abstraction to handle the meta/layout of exception stores (the
+ * COW device).
+ */
+struct exception_store {
+
+	/*
+	 * Destroys this object when you've finished with it.
+	 */
+	void (*destroy) (struct exception_store *store);
+
+	/*
+	 * The target shouldn't read the COW device until this is
+	 * called.
+	 */
+	int (*read_metadata) (struct exception_store *store);
+
+	/*
+	 * Find somewhere to store the next exception.
+	 */
+	int (*prepare_exception) (struct exception_store *store,
+				  struct exception *e);
+
+	/*
+	 * Update the metadata with this exception.
+	 */
+	void (*commit_exception) (struct exception_store *store,
+				  struct exception *e,
+				  void (*callback) (void *, int success),
+				  void *callback_context);
+
+	/*
+	 * The snapshot is invalid, note this in the metadata.
+	 */
+	void (*drop_snapshot) (struct exception_store *store);
+
+	/*
+	 * Return how full the snapshot is.
+	 */
+	void (*fraction_full) (struct exception_store *store,
+			       sector_t *numerator,
+			       sector_t *denominator);
+
+	struct dm_snapshot *snap;
+	void *context;
+};
+
+struct dm_snapshot {
+	struct rw_semaphore lock;
+	struct dm_table *table;
+
+	struct dm_dev *origin;
+	struct dm_dev *cow;
+
+	/* List of snapshots per Origin */
+	struct list_head list;
+
+	/* Size of data blocks saved - must be a power of 2 */
+	chunk_t chunk_size;
+	chunk_t chunk_mask;
+	chunk_t chunk_shift;
+
+	/* You can't use a snapshot if this is 0 (e.g. if full) */
+	int valid;
+	int have_metadata;
+
+	/* Used for display of table */
+	char type;
+
+	/* The last percentage we notified */
+	int last_percent;
+
+	struct exception_table pending;
+	struct exception_table complete;
+
+	/* The on disk metadata handler */
+	struct exception_store store;
+
+	struct kcopyd_client *kcopyd_client;
+};
+
+/*
+ * Used by the exception stores to load exceptions hen
+ * initialising.
+ */
+int dm_add_exception(struct dm_snapshot *s, chunk_t old, chunk_t new);
+
+/*
+ * Constructor and destructor for the default persistent
+ * store.
+ */
+int dm_create_persistent(struct exception_store *store, uint32_t chunk_size);
+
+int dm_create_transient(struct exception_store *store,
+			struct dm_snapshot *s, int blocksize);
+
+/*
+ * Return the number of sectors in the device.
+ */
+static inline sector_t get_dev_size(kdev_t dev)
+{
+	int *sizes;
+
+	sizes = blk_size[MAJOR(dev)];
+	if (sizes)
+		return sizes[MINOR(dev)] << 1;
+
+	return 0;
+}
+
+static inline chunk_t sector_to_chunk(struct dm_snapshot *s, sector_t sector)
+{
+	return (sector & ~s->chunk_mask) >> s->chunk_shift;
+}
+
+static inline sector_t chunk_to_sector(struct dm_snapshot *s, chunk_t chunk)
+{
+	return chunk << s->chunk_shift;
+}
+
+#endif
diff -urN --exclude-from=diff-exclude linux-2.4.25/drivers/md/dm-stripe.c linux-2.4.25-leo/drivers/md/dm-stripe.c
--- linux-2.4.25/drivers/md/dm-stripe.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.25-leo/drivers/md/dm-stripe.c	2004-02-20 18:21:57.000000000 +0000
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2001 Sistina Software (UK) Limited.
+ *
+ * This file is released under the GPL.
+ */
+
+#include "dm.h"
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/blkdev.h>
+#include <linux/slab.h>
+
+struct stripe {
+	struct dm_dev *dev;
+	sector_t physical_start;
+};
+
+struct stripe_c {
+	uint32_t stripes;
+
+	/* The size of this target / num. stripes */
+	uint32_t stripe_width;
+
+	/* stripe chunk size */
+	uint32_t chunk_shift;
+	sector_t chunk_mask;
+
+	struct stripe stripe[0];
+};
+
+static inline struct stripe_c *alloc_context(unsigned int stripes)
+{
+	size_t len;
+
+	if (array_too_big(sizeof(struct stripe_c), sizeof(struct stripe),
+			  stripes))
+		return NULL;
+
+	len = sizeof(struct stripe_c) + (sizeof(struct stripe) * stripes);
+
+	return kmalloc(len, GFP_KERNEL);
+}
+
+/*
+ * Parse a single <dev> <sector> pair
+ */
+static int get_stripe(struct dm_target *ti, struct stripe_c *sc,
+		      unsigned int stripe, char **argv)
+{
+	sector_t start;
+
+	if (sscanf(argv[1], SECTOR_FORMAT, &start) != 1)
+		return -EINVAL;
+
+	if (dm_get_device(ti, argv[0], start, sc->stripe_width,
+			  dm_table_get_mode(ti->table),
+			  &sc->stripe[stripe].dev))
+		return -ENXIO;
+
+	sc->stripe[stripe].physical_start = start;
+	return 0;
+}
+
+/*
+ * FIXME: Nasty function, only present because we can't link
+ * against __moddi3 and __divdi3.
+ *
+ * returns a == b * n
+ */
+static int multiple(sector_t a, sector_t b, sector_t *n)
+{
+	sector_t acc, prev, i;
+
+	*n = 0;
+	while (a >= b) {
+		for (acc = b, prev = 0, i = 1;
+		     acc <= a;
+		     prev = acc, acc <<= 1, i <<= 1)
+			;
+
+		a -= prev;
+		*n += i >> 1;
+	}
+
+	return a == 0;
+}
+
+/*
+ * Construct a striped mapping.
+ * <number of stripes> <chunk size (2^^n)> [<dev_path> <offset>]+
+ */
+static int stripe_ctr(struct dm_target *ti, unsigned int argc, char **argv)
+{
+	struct stripe_c *sc;
+	sector_t width;
+	uint32_t stripes;
+	uint32_t chunk_size;
+	char *end;
+	int r;
+	unsigned int i;
+
+	if (argc < 2) {
+		ti->error = "dm-stripe: Not enough arguments";
+		return -EINVAL;
+	}
+
+	stripes = simple_strtoul(argv[0], &end, 10);
+	if (*end) {
+		ti->error = "dm-stripe: Invalid stripe count";
+		return -EINVAL;
+	}
+
+	chunk_size = simple_strtoul(argv[1], &end, 10);
+	if (*end) {
+		ti->error = "dm-stripe: Invalid chunk_size";
+		return -EINVAL;
+	}
+
+	/*
+	 * chunk_size is a power of two
+	 */
+	if (!chunk_size || (chunk_size & (chunk_size - 1))) {
+		ti->error = "dm-stripe: Invalid chunk size";
+		return -EINVAL;
+	}
+
+	if (!multiple(ti->len, stripes, &width)) {
+		ti->error = "dm-stripe: Target length not divisable by "
+		    "number of stripes";
+		return -EINVAL;
+	}
+
+	/*
+	 * Do we have enough arguments for that many stripes ?
+	 */
+	if (argc != (2 + 2 * stripes)) {
+		ti->error = "dm-stripe: Not enough destinations specified";
+		return -EINVAL;
+	}
+
+	sc = alloc_context(stripes);
+	if (!sc) {
+		ti->error = "dm-stripe: Memory allocation for striped context "
+		    "failed";
+		return -ENOMEM;
+	}
+
+	sc->stripes = stripes;
+	sc->stripe_width = width;
+
+	sc->chunk_mask = ((sector_t) chunk_size) - 1;
+	for (sc->chunk_shift = 0; chunk_size; sc->chunk_shift++)
+		chunk_size >>= 1;
+	sc->chunk_shift--;
+
+	/*
+	 * Get the stripe destinations.
+	 */
+	for (i = 0; i < stripes; i++) {
+		argv += 2;
+
+		r = get_stripe(ti, sc, i, argv);
+		if (r < 0) {
+			ti->error = "dm-stripe: Couldn't parse stripe "
+			    "destination";
+			while (i--)
+				dm_put_device(ti, sc->stripe[i].dev);
+			kfree(sc);
+			return r;
+		}
+	}
+
+	ti->private = sc;
+	return 0;
+}
+
+static void stripe_dtr(struct dm_target *ti)
+{
+	unsigned int i;
+	struct stripe_c *sc = (struct stripe_c *) ti->private;
+
+	for (i = 0; i < sc->stripes; i++)
+		dm_put_device(ti, sc->stripe[i].dev);
+
+	kfree(sc);
+}
+
+static int stripe_map(struct dm_target *ti, struct buffer_head *bh, int rw,
+		      union map_info *context)
+{
+	struct stripe_c *sc = (struct stripe_c *) ti->private;
+
+	sector_t offset = bh->b_rsector - ti->begin;
+	uint32_t chunk = (uint32_t) (offset >> sc->chunk_shift);
+	uint32_t stripe = chunk % sc->stripes;	/* 32bit modulus */
+	chunk = chunk / sc->stripes;
+
+	bh->b_rdev = sc->stripe[stripe].dev->dev;
+	bh->b_rsector = sc->stripe[stripe].physical_start +
+	    (chunk << sc->chunk_shift) + (offset & sc->chunk_mask);
+	return 1;
+}
+
+static int stripe_status(struct dm_target *ti, status_type_t type,
+			 char *result, unsigned int maxlen)
+{
+	struct stripe_c *sc = (struct stripe_c *) ti->private;
+	int offset;
+	unsigned int i;
+
+	switch (type) {
+	case STATUSTYPE_INFO:
+		result[0] = '\0';
+		break;
+
+	case STATUSTYPE_TABLE:
+		offset = snprintf(result, maxlen, "%d " SECTOR_FORMAT,
+				  sc->stripes, sc->chunk_mask + 1);
+		for (i = 0; i < sc->stripes; i++) {
+			offset +=
+			    snprintf(result + offset, maxlen - offset,
+				     " %s " SECTOR_FORMAT,
+		       dm_kdevname(to_kdev_t(sc->stripe[i].dev->bdev->bd_dev)),
+				     sc->stripe[i].physical_start);
+		}
+		break;
+	}
+	return 0;
+}
+
+static struct target_type stripe_target = {
+	.name   = "striped",
+	.module = THIS_MODULE,
+	.ctr    = stripe_ctr,
+	.dtr    = stripe_dtr,
+	.map    = stripe_map,
+	.status = stripe_status,
+};
+
+int __init dm_stripe_init(void)
+{
+	int r;
+
+	r = dm_register_target(&stripe_target);
+	if (r < 0)
+		DMWARN("striped target registration failed");
+
+	return r;
+}
+
+void dm_stripe_exit(void)
+{
+	if (dm_unregister_target(&stripe_target))
+		DMWARN("striped target unregistration failed");
+
+	return;
+}
diff -urN --exclude-from=diff-exclude linux-2.4.25/drivers/md/dm-table.c linux-2.4.25-leo/drivers/md/dm-table.c
--- linux-2.4.25/drivers/md/dm-table.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.25-leo/drivers/md/dm-table.c	2004-02-20 18:21:57.000000000 +0000
@@ -0,0 +1,679 @@
+/*
+ * Copyright (C) 2001 Sistina Software (UK) Limited.
+ *
+ * This file is released under the GPL.
+ */
+
+#include "dm.h"
+
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+#include <linux/blkdev.h>
+#include <linux/ctype.h>
+#include <linux/slab.h>
+#include <asm/atomic.h>
+
+#define MAX_DEPTH 16
+#define NODE_SIZE L1_CACHE_BYTES
+#define KEYS_PER_NODE (NODE_SIZE / sizeof(sector_t))
+#define CHILDREN_PER_NODE (KEYS_PER_NODE + 1)
+
+struct dm_table {
+	atomic_t holders;
+
+	/* btree table */
+	unsigned int depth;
+	unsigned int counts[MAX_DEPTH];	/* in nodes */
+	sector_t *index[MAX_DEPTH];
+
+	unsigned int num_targets;
+	unsigned int num_allocated;
+	sector_t *highs;
+	struct dm_target *targets;
+
+	/*
+	 * Indicates the rw permissions for the new logical
+	 * device.  This should be a combination of FMODE_READ
+	 * and FMODE_WRITE.
+	 */
+	int mode;
+
+	/* a list of devices used by this table */
+	struct list_head devices;
+
+	/* events get handed up using this callback */
+	void (*event_fn)(void *);
+	void *event_context;
+};
+
+/*
+ * Similar to ceiling(log_size(n))
+ */
+static unsigned int int_log(unsigned long n, unsigned long base)
+{
+	int result = 0;
+
+	while (n > 1) {
+		n = dm_div_up(n, base);
+		result++;
+	}
+
+	return result;
+}
+
+/*
+ * Calculate the index of the child node of the n'th node k'th key.
+ */
+static inline unsigned int get_child(unsigned int n, unsigned int k)
+{
+	return (n * CHILDREN_PER_NODE) + k;
+}
+
+/*
+ * Return the n'th node of level l from table t.
+ */
+static inline sector_t *get_node(struct dm_table *t, unsigned int l,
+				 unsigned int n)
+{
+	return t->index[l] + (n * KEYS_PER_NODE);
+}
+
+/*
+ * Return the highest key that you could lookup from the n'th
+ * node on level l of the btree.
+ */
+static sector_t high(struct dm_table *t, unsigned int l, unsigned int n)
+{
+	for (; l < t->depth - 1; l++)
+		n = get_child(n, CHILDREN_PER_NODE - 1);
+
+	if (n >= t->counts[l])
+		return (sector_t) - 1;
+
+	return get_node(t, l, n)[KEYS_PER_NODE - 1];
+}
+
+/*
+ * Fills in a level of the btree based on the highs of the level
+ * below it.
+ */
+static int setup_btree_index(unsigned int l, struct dm_table *t)
+{
+	unsigned int n, k;
+	sector_t *node;
+
+	for (n = 0U; n < t->counts[l]; n++) {
+		node = get_node(t, l, n);
+
+		for (k = 0U; k < KEYS_PER_NODE; k++)
+			node[k] = high(t, l + 1, get_child(n, k));
+	}
+
+	return 0;
+}
+
+
+
+int dm_table_create(struct dm_table **result, int mode, unsigned num_targets)
+{
+	struct dm_table *t = kmalloc(sizeof(*t), GFP_KERNEL);
+
+	if (!t)
+		return -ENOMEM;
+
+	memset(t, 0, sizeof(*t));
+	INIT_LIST_HEAD(&t->devices);
+	atomic_set(&t->holders, 1);
+
+	num_targets = dm_round_up(num_targets, KEYS_PER_NODE);
+
+	/* Allocate both the target array and offset array at once. */
+	t->highs = (sector_t *) vcalloc(sizeof(struct dm_target) +
+					sizeof(sector_t), num_targets);
+	if (!t->highs) {
+		kfree(t);
+		return -ENOMEM;
+	}
+
+	memset(t->highs, -1, sizeof(*t->highs) * num_targets);
+
+	t->targets = (struct dm_target *) (t->highs + num_targets);
+	t->num_allocated = num_targets;
+	t->mode = mode;
+	*result = t;
+	return 0;
+}
+
+static void free_devices(struct list_head *devices)
+{
+	struct list_head *tmp, *next;
+
+	for (tmp = devices->next; tmp != devices; tmp = next) {
+		struct dm_dev *dd = list_entry(tmp, struct dm_dev, list);
+		next = tmp->next;
+		kfree(dd);
+	}
+}
+
+void table_destroy(struct dm_table *t)
+{
+	unsigned int i;
+
+	/* free the indexes (see dm_table_complete) */
+	if (t->depth >= 2)
+		vfree(t->index[t->depth - 2]);
+
+	/* free the targets */
+	for (i = 0; i < t->num_targets; i++) {
+		struct dm_target *tgt = t->targets + i;
+
+		if (tgt->type->dtr)
+			tgt->type->dtr(tgt);
+
+		dm_put_target_type(tgt->type);
+	}
+
+	vfree(t->highs);
+
+	/* free the device list */
+	if (t->devices.next != &t->devices) {
+		DMWARN("devices still present during destroy: "
+		       "dm_table_remove_device calls missing");
+
+		free_devices(&t->devices);
+	}
+
+	kfree(t);
+}
+
+void dm_table_get(struct dm_table *t)
+{
+	atomic_inc(&t->holders);
+}
+
+void dm_table_put(struct dm_table *t)
+{
+	if (atomic_dec_and_test(&t->holders))
+		table_destroy(t);
+}
+
+/*
+ * Convert a device path to a dev_t.
+ */
+static int lookup_device(const char *path, kdev_t *dev)
+{
+	int r;
+	struct nameidata nd;
+	struct inode *inode;
+
+	if (!path_init(path, LOOKUP_FOLLOW, &nd))
+		return 0;
+
+	if ((r = path_walk(path, &nd)))
+		goto out;
+
+	inode = nd.dentry->d_inode;
+	if (!inode) {
+		r = -ENOENT;
+		goto out;
+	}
+
+	if (!S_ISBLK(inode->i_mode)) {
+		r = -ENOTBLK;
+		goto out;
+	}
+
+	*dev = inode->i_rdev;
+
+      out:
+	path_release(&nd);
+	return r;
+}
+
+/*
+ * See if we've already got a device in the list.
+ */
+static struct dm_dev *find_device(struct list_head *l, kdev_t dev)
+{
+	struct list_head *tmp;
+
+	list_for_each(tmp, l) {
+		struct dm_dev *dd = list_entry(tmp, struct dm_dev, list);
+		if (kdev_same(dd->dev, dev))
+			return dd;
+	}
+
+	return NULL;
+}
+
+/*
+ * Open a device so we can use it as a map destination.
+ */
+static int open_dev(struct dm_dev *dd)
+{
+	if (dd->bdev)
+		BUG();
+
+	dd->bdev = bdget(kdev_t_to_nr(dd->dev));
+	if (!dd->bdev)
+		return -ENOMEM;
+
+	return blkdev_get(dd->bdev, dd->mode, 0, BDEV_RAW);
+}
+
+/*
+ * Close a device that we've been using.
+ */
+static void close_dev(struct dm_dev *dd)
+{
+	if (!dd->bdev)
+		return;
+
+	blkdev_put(dd->bdev, BDEV_RAW);
+	dd->bdev = NULL;
+}
+
+/*
+ * If possible (ie. blk_size[major] is set), this checks an area
+ * of a destination device is valid.
+ */
+static int check_device_area(kdev_t dev, sector_t start, sector_t len)
+{
+	int *sizes;
+	sector_t dev_size;
+
+	if (!(sizes = blk_size[major(dev)]) || !(dev_size = sizes[minor(dev)]))
+		/* we don't know the device details,
+		 * so give the benefit of the doubt */
+		return 1;
+
+	/* convert to 512-byte sectors */
+	dev_size <<= 1;
+
+	return ((start < dev_size) && (len <= (dev_size - start)));
+}
+
+/*
+ * This upgrades the mode on an already open dm_dev.  Being
+ * careful to leave things as they were if we fail to reopen the
+ * device.
+ */
+static int upgrade_mode(struct dm_dev *dd, int new_mode)
+{
+	int r;
+	struct dm_dev dd_copy;
+
+	memcpy(&dd_copy, dd, sizeof(dd_copy));
+
+	dd->mode |= new_mode;
+	dd->bdev = NULL;
+	r = open_dev(dd);
+	if (!r)
+		close_dev(&dd_copy);
+	else
+		memcpy(dd, &dd_copy, sizeof(dd_copy));
+
+	return r;
+}
+
+/*
+ * Add a device to the list, or just increment the usage count if
+ * it's already present.
+ */
+int dm_get_device(struct dm_target *ti, const char *path, sector_t start,
+		  sector_t len, int mode, struct dm_dev **result)
+{
+	int r;
+	kdev_t dev;
+	struct dm_dev *dd;
+	unsigned major, minor;
+	struct dm_table *t = ti->table;
+
+	if (!t)
+		BUG();
+
+	if (sscanf(path, "%u:%u", &major, &minor) == 2) {
+		/* Extract the major/minor numbers */
+		dev = mk_kdev(major, minor);
+	} else {
+		/* convert the path to a device */
+		if ((r = lookup_device(path, &dev)))
+			return r;
+	}
+
+	dd = find_device(&t->devices, dev);
+	if (!dd) {
+		dd = kmalloc(sizeof(*dd), GFP_KERNEL);
+		if (!dd)
+			return -ENOMEM;
+
+		dd->dev = dev;
+		dd->mode = mode;
+		dd->bdev = NULL;
+
+		if ((r = open_dev(dd))) {
+			kfree(dd);
+			return r;
+		}
+
+		atomic_set(&dd->count, 0);
+		list_add(&dd->list, &t->devices);
+
+	} else if (dd->mode != (mode | dd->mode)) {
+		r = upgrade_mode(dd, mode);
+		if (r)
+			return r;
+	}
+	atomic_inc(&dd->count);
+
+	if (!check_device_area(dd->dev, start, len)) {
+		DMWARN("device %s too small for target", path);
+		dm_put_device(ti, dd);
+		return -EINVAL;
+	}
+
+	*result = dd;
+
+	return 0;
+}
+
+/*
+ * Decrement a devices use count and remove it if neccessary.
+ */
+void dm_put_device(struct dm_target *ti, struct dm_dev *dd)
+{
+	if (atomic_dec_and_test(&dd->count)) {
+		close_dev(dd);
+		list_del(&dd->list);
+		kfree(dd);
+	}
+}
+
+/*
+ * Checks to see if the target joins onto the end of the table.
+ */
+static int adjoin(struct dm_table *table, struct dm_target *ti)
+{
+	struct dm_target *prev;
+
+	if (!table->num_targets)
+		return !ti->begin;
+
+	prev = &table->targets[table->num_targets - 1];
+	return (ti->begin == (prev->begin + prev->len));
+}
+
+/*
+ * Used to dynamically allocate the arg array.
+ */
+static char **realloc_argv(unsigned *array_size, char **old_argv)
+{
+	char **argv;
+	unsigned new_size;
+
+	new_size = *array_size ? *array_size * 2 : 64;
+	argv = kmalloc(new_size * sizeof(*argv), GFP_KERNEL);
+	if (argv) {
+		memcpy(argv, old_argv, *array_size * sizeof(*argv));
+		*array_size = new_size;
+	}
+
+	kfree(old_argv);
+	return argv;
+}
+
+/*
+ * Destructively splits up the argument list to pass to ctr.
+ */
+static int split_args(int *argc, char ***argvp, char *input)
+{
+	char *start, *end = input, *out, **argv = NULL;
+	unsigned array_size = 0;
+
+	*argc = 0;
+	argv = realloc_argv(&array_size, argv);
+	if (!argv)
+		return -ENOMEM;
+
+	while (1) {
+		start = end;
+
+		/* Skip whitespace */
+		while (*start && isspace(*start))
+			start++;
+
+		if (!*start)
+			break;	/* success, we hit the end */
+
+		/* 'out' is used to remove any back-quotes */
+		end = out = start;
+		while (*end) {
+			/* Everything apart from '\0' can be quoted */
+			if (*end == '\\' && *(end + 1)) {
+				*out++ = *(end + 1);
+				end += 2;
+				continue;
+			}
+
+			if (isspace(*end))
+				break;	/* end of token */
+
+			*out++ = *end++;
+		}
+
+		/* have we already filled the array ? */
+		if ((*argc + 1) > array_size) {
+			argv = realloc_argv(&array_size, argv);
+			if (!argv)
+				return -ENOMEM;
+		}
+
+		/* we know this is whitespace */
+		if (*end)
+			end++;
+
+		/* terminate the string and put it in the array */
+		*out = '\0';
+		argv[*argc] = start;
+		(*argc)++;
+	}
+
+	*argvp = argv;
+	return 0;
+}
+
+int dm_table_add_target(struct dm_table *t, const char *type,
+			sector_t start, sector_t len, char *params)
+{
+	int r = -EINVAL, argc;
+	char **argv;
+	struct dm_target *tgt;
+
+	if (t->num_targets >= t->num_allocated)
+		return -ENOMEM;
+
+	tgt = t->targets + t->num_targets;
+	memset(tgt, 0, sizeof(*tgt));
+
+	tgt->type = dm_get_target_type(type);
+	if (!tgt->type) {
+		tgt->error = "unknown target type";
+		return -EINVAL;
+	}
+
+	tgt->table = t;
+	tgt->begin = start;
+	tgt->len = len;
+	tgt->error = "Unknown error";
+
+	/*
+	 * Does this target adjoin the previous one ?
+	 */
+	if (!adjoin(t, tgt)) {
+		tgt->error = "Gap in table";
+		r = -EINVAL;
+		goto bad;
+	}
+
+	r = split_args(&argc, &argv, params);
+	if (r) {
+		tgt->error = "couldn't split parameters (insufficient memory)";
+		goto bad;
+	}
+
+	r = tgt->type->ctr(tgt, argc, argv);
+	kfree(argv);
+	if (r)
+		goto bad;
+
+	t->highs[t->num_targets++] = tgt->begin + tgt->len - 1;
+	return 0;
+
+      bad:
+	printk(KERN_ERR DM_NAME ": %s\n", tgt->error);
+	dm_put_target_type(tgt->type);
+	return r;
+}
+
+static int setup_indexes(struct dm_table *t)
+{
+	int i;
+	unsigned int total = 0;
+	sector_t *indexes;
+
+	/* allocate the space for *all* the indexes */
+	for (i = t->depth - 2; i >= 0; i--) {
+		t->counts[i] = dm_div_up(t->counts[i + 1], CHILDREN_PER_NODE);
+		total += t->counts[i];
+	}
+
+	indexes = (sector_t *) vcalloc(total, (unsigned long) NODE_SIZE);
+	if (!indexes)
+		return -ENOMEM;
+
+	/* set up internal nodes, bottom-up */
+	for (i = t->depth - 2, total = 0; i >= 0; i--) {
+		t->index[i] = indexes;
+		indexes += (KEYS_PER_NODE * t->counts[i]);
+		setup_btree_index(i, t);
+	}
+
+	return 0;
+}
+
+/*
+ * Builds the btree to index the map.
+ */
+int dm_table_complete(struct dm_table *t)
+{
+	int r = 0;
+	unsigned int leaf_nodes;
+
+	/* how many indexes will the btree have ? */
+	leaf_nodes = dm_div_up(t->num_targets, KEYS_PER_NODE);
+	t->depth = 1 + int_log(leaf_nodes, CHILDREN_PER_NODE);
+
+	/* leaf layer has already been set up */
+	t->counts[t->depth - 1] = leaf_nodes;
+	t->index[t->depth - 1] = t->highs;
+
+	if (t->depth >= 2)
+		r = setup_indexes(t);
+
+	return r;
+}
+
+static spinlock_t _event_lock = SPIN_LOCK_UNLOCKED;
+void dm_table_event_callback(struct dm_table *t,
+			     void (*fn)(void *), void *context)
+{
+	spin_lock_irq(&_event_lock);
+	t->event_fn = fn;
+	t->event_context = context;
+	spin_unlock_irq(&_event_lock);
+}
+
+void dm_table_event(struct dm_table *t)
+{
+	spin_lock(&_event_lock);
+	if (t->event_fn)
+		t->event_fn(t->event_context);
+	spin_unlock(&_event_lock);
+}
+
+sector_t dm_table_get_size(struct dm_table *t)
+{
+	return t->num_targets ? (t->highs[t->num_targets - 1] + 1) : 0;
+}
+
+struct dm_target *dm_table_get_target(struct dm_table *t, unsigned int index)
+{
+	if (index > t->num_targets)
+		return NULL;
+
+	return t->targets + index;
+}
+
+/*
+ * Search the btree for the correct target.
+ */
+struct dm_target *dm_table_find_target(struct dm_table *t, sector_t sector)
+{
+	unsigned int l, n = 0, k = 0;
+	sector_t *node;
+
+	for (l = 0; l < t->depth; l++) {
+		n = get_child(n, k);
+		node = get_node(t, l, n);
+
+		for (k = 0; k < KEYS_PER_NODE; k++)
+			if (node[k] >= sector)
+				break;
+	}
+
+	return &t->targets[(KEYS_PER_NODE * n) + k];
+}
+
+unsigned int dm_table_get_num_targets(struct dm_table *t)
+{
+	return t->num_targets;
+}
+
+struct list_head *dm_table_get_devices(struct dm_table *t)
+{
+	return &t->devices;
+}
+
+int dm_table_get_mode(struct dm_table *t)
+{
+	return t->mode;
+}
+
+void dm_table_suspend_targets(struct dm_table *t)
+{
+	int i;
+
+	for (i = 0; i < t->num_targets; i++) {
+		struct dm_target *ti = t->targets + i;
+
+		if (ti->type->suspend)
+			ti->type->suspend(ti);
+	}
+}
+
+void dm_table_resume_targets(struct dm_table *t)
+{
+	int i;
+
+	for (i = 0; i < t->num_targets; i++) {
+		struct dm_target *ti = t->targets + i;
+
+		if (ti->type->resume)
+			ti->type->resume(ti);
+	}
+}
+
+EXPORT_SYMBOL(dm_get_device);
+EXPORT_SYMBOL(dm_put_device);
+EXPORT_SYMBOL(dm_table_event);
+EXPORT_SYMBOL(dm_table_get_mode);
diff -urN --exclude-from=diff-exclude linux-2.4.25/drivers/md/dm-target.c linux-2.4.25-leo/drivers/md/dm-target.c
--- linux-2.4.25/drivers/md/dm-target.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.25-leo/drivers/md/dm-target.c	2004-02-20 18:21:57.000000000 +0000
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2001 Sistina Software (UK) Limited
+ *
+ * This file is released under the GPL.
+ */
+
+#include "dm.h"
+
+#include <linux/module.h>
+#include <linux/kmod.h>
+#include <linux/slab.h>
+
+struct tt_internal {
+	struct target_type tt;
+
+	struct list_head list;
+	long use;
+};
+
+static LIST_HEAD(_targets);
+static DECLARE_RWSEM(_lock);
+
+#define DM_MOD_NAME_SIZE 32
+
+static inline struct tt_internal *__find_target_type(const char *name)
+{
+	struct list_head *tih;
+	struct tt_internal *ti;
+
+	list_for_each(tih, &_targets) {
+		ti = list_entry(tih, struct tt_internal, list);
+
+		if (!strcmp(name, ti->tt.name))
+			return ti;
+	}
+
+	return NULL;
+}
+
+static struct tt_internal *get_target_type(const char *name)
+{
+	struct tt_internal *ti;
+
+	down_read(&_lock);
+	ti = __find_target_type(name);
+
+	if (ti) {
+		if (ti->use == 0 && ti->tt.module)
+			__MOD_INC_USE_COUNT(ti->tt.module);
+		ti->use++;
+	}
+	up_read(&_lock);
+
+	return ti;
+}
+
+static void load_module(const char *name)
+{
+	char module_name[DM_MOD_NAME_SIZE] = "dm-";
+
+	/* Length check for strcat() below */
+	if (strlen(name) > (DM_MOD_NAME_SIZE - 4))
+		return;
+
+	strcat(module_name, name);
+	request_module(module_name);
+}
+
+struct target_type *dm_get_target_type(const char *name)
+{
+	struct tt_internal *ti = get_target_type(name);
+
+	if (!ti) {
+		load_module(name);
+		ti = get_target_type(name);
+	}
+
+	return ti ? &ti->tt : NULL;
+}
+
+void dm_put_target_type(struct target_type *t)
+{
+	struct tt_internal *ti = (struct tt_internal *) t;
+
+	down_read(&_lock);
+	if (--ti->use == 0 && ti->tt.module)
+		__MOD_DEC_USE_COUNT(ti->tt.module);
+
+	if (ti->use < 0)
+		BUG();
+	up_read(&_lock);
+
+	return;
+}
+
+static struct tt_internal *alloc_target(struct target_type *t)
+{
+	struct tt_internal *ti = kmalloc(sizeof(*ti), GFP_KERNEL);
+
+	if (ti) {
+		memset(ti, 0, sizeof(*ti));
+		ti->tt = *t;
+	}
+
+	return ti;
+}
+
+int dm_register_target(struct target_type *t)
+{
+	int rv = 0;
+	struct tt_internal *ti = alloc_target(t);
+
+	if (!ti)
+		return -ENOMEM;
+
+	down_write(&_lock);
+	if (__find_target_type(t->name)) {
+		kfree(ti);
+		rv = -EEXIST;
+	} else
+		list_add(&ti->list, &_targets);
+
+	up_write(&_lock);
+	return rv;
+}
+
+int dm_unregister_target(struct target_type *t)
+{
+	struct tt_internal *ti;
+
+	down_write(&_lock);
+	if (!(ti = __find_target_type(t->name))) {
+		up_write(&_lock);
+		return -EINVAL;
+	}
+
+	if (ti->use) {
+		up_write(&_lock);
+		return -ETXTBSY;
+	}
+
+	list_del(&ti->list);
+	kfree(ti);
+
+	up_write(&_lock);
+	return 0;
+}
+
+/*
+ * io-err: always fails an io, useful for bringing
+ * up LVs that have holes in them.
+ */
+static int io_err_ctr(struct dm_target *ti, unsigned int argc, char **args)
+{
+	return 0;
+}
+
+static void io_err_dtr(struct dm_target *ti)
+{
+	/* empty */
+}
+
+static int io_err_map(struct dm_target *ti, struct buffer_head *bh, int rw,
+		      union map_info *map_context)
+{
+	return -EIO;
+}
+
+static struct target_type error_target = {
+	.name = "error",
+	.ctr  = io_err_ctr,
+	.dtr  = io_err_dtr,
+	.map  = io_err_map,
+};
+
+int dm_target_init(void)
+{
+	return dm_register_target(&error_target);
+}
+
+void dm_target_exit(void)
+{
+	if (dm_unregister_target(&error_target))
+		DMWARN("error target unregistration failed");
+}
+
+EXPORT_SYMBOL(dm_register_target);
+EXPORT_SYMBOL(dm_unregister_target);
diff -urN --exclude-from=diff-exclude linux-2.4.25/drivers/md/kcopyd.c linux-2.4.25-leo/drivers/md/kcopyd.c
--- linux-2.4.25/drivers/md/kcopyd.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.25-leo/drivers/md/kcopyd.c	2004-02-20 18:21:57.000000000 +0000
@@ -0,0 +1,666 @@
+/*
+ * Copyright (C) 2002 Sistina Software (UK) Limited.
+ *
+ * This file is released under the GPL.
+ */
+
+#include <asm/atomic.h>
+
+#include <linux/blkdev.h>
+#include <linux/config.h>
+#include <linux/device-mapper.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/locks.h>
+#include <linux/mempool.h>
+#include <linux/module.h>
+#include <linux/pagemap.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+
+#include "kcopyd.h"
+#include "dm-daemon.h"
+
+/* FIXME: this is only needed for the DMERR macros */
+#include "dm.h"
+
+static struct dm_daemon _kcopyd;
+
+#define SECTORS_PER_PAGE (PAGE_SIZE / SECTOR_SIZE)
+#define SUB_JOB_SIZE 128
+#define PAGES_PER_SUB_JOB (SUB_JOB_SIZE / SECTORS_PER_PAGE)
+#define SUB_JOB_COUNT 8
+
+/*-----------------------------------------------------------------
+ * Each kcopyd client has its own little pool of preallocated
+ * pages for kcopyd io.
+ *---------------------------------------------------------------*/
+struct kcopyd_client {
+	struct list_head list;
+
+	spinlock_t lock;
+	struct list_head pages;
+	unsigned int nr_pages;
+	unsigned int nr_free_pages;
+	unsigned int max_split;
+};
+
+static inline void __push_page(struct kcopyd_client *kc, struct page *p)
+{
+	list_add(&p->list, &kc->pages);
+	kc->nr_free_pages++;
+}
+
+static inline struct page *__pop_page(struct kcopyd_client *kc)
+{
+	struct page *p;
+
+	p = list_entry(kc->pages.next, struct page, list);
+	list_del(&p->list);
+	kc->nr_free_pages--;
+
+	return p;
+}
+
+static int kcopyd_get_pages(struct kcopyd_client *kc,
+			    unsigned int nr, struct list_head *pages)
+{
+	struct page *p;
+	INIT_LIST_HEAD(pages);
+
+	spin_lock(&kc->lock);
+	if (kc->nr_free_pages < nr) {
+		spin_unlock(&kc->lock);
+		return -ENOMEM;
+	}
+
+	while (nr--) {
+		p = __pop_page(kc);
+		list_add(&p->list, pages);
+	}
+	spin_unlock(&kc->lock);
+
+	return 0;
+}
+
+static void kcopyd_put_pages(struct kcopyd_client *kc, struct list_head *pages)
+{
+	struct list_head *tmp, *tmp2;
+
+	spin_lock(&kc->lock);
+	list_for_each_safe (tmp, tmp2, pages)
+		__push_page(kc, list_entry(tmp, struct page, list));
+	spin_unlock(&kc->lock);
+}
+
+/*
+ * These three functions resize the page pool.
+ */
+static void release_pages(struct list_head *pages)
+{
+	struct page *p;
+	struct list_head *tmp, *tmp2;
+
+	list_for_each_safe (tmp, tmp2, pages) {
+		p = list_entry(tmp, struct page, list);
+		UnlockPage(p);
+		__free_page(p);
+	}
+}
+
+static int client_alloc_pages(struct kcopyd_client *kc, unsigned int nr)
+{
+	unsigned int i;
+	struct page *p;
+	LIST_HEAD(new);
+
+	for (i = 0; i < nr; i++) {
+		p = alloc_page(GFP_KERNEL);
+		if (!p) {
+			release_pages(&new);
+			return -ENOMEM;
+		}
+
+		LockPage(p);
+		list_add(&p->list, &new);
+	}
+
+	kcopyd_put_pages(kc, &new);
+	kc->nr_pages += nr;
+	kc->max_split = kc->nr_pages / PAGES_PER_SUB_JOB;
+	if (kc->max_split > SUB_JOB_COUNT)
+		kc->max_split = SUB_JOB_COUNT;
+
+	return 0;
+}
+
+static void client_free_pages(struct kcopyd_client *kc)
+{
+	BUG_ON(kc->nr_free_pages != kc->nr_pages);
+	release_pages(&kc->pages);
+	kc->nr_free_pages = kc->nr_pages = 0;
+}
+
+/*-----------------------------------------------------------------
+ * kcopyd_jobs need to be allocated by the *clients* of kcopyd,
+ * for this reason we use a mempool to prevent the client from
+ * ever having to do io (which could cause a deadlock).
+ *---------------------------------------------------------------*/
+struct kcopyd_job {
+	struct kcopyd_client *kc;
+	struct list_head list;
+	unsigned int flags;
+
+	/*
+	 * Error state of the job.
+	 */
+	int read_err;
+	unsigned int write_err;
+
+	/*
+	 * Either READ or WRITE
+	 */
+	int rw;
+	struct io_region source;
+
+	/*
+	 * The destinations for the transfer.
+	 */
+	unsigned int num_dests;
+	struct io_region dests[KCOPYD_MAX_REGIONS];
+
+	sector_t offset;
+	unsigned int nr_pages;
+	struct list_head pages;
+
+	/*
+	 * Set this to ensure you are notified when the job has
+	 * completed.  'context' is for callback to use.
+	 */
+	kcopyd_notify_fn fn;
+	void *context;
+
+	/*
+	 * These fields are only used if the job has been split
+	 * into more manageable parts.
+	 */
+	struct semaphore lock;
+	atomic_t sub_jobs;
+	sector_t progress;
+};
+
+/* FIXME: this should scale with the number of pages */
+#define MIN_JOBS 512
+
+static kmem_cache_t *_job_cache;
+static mempool_t *_job_pool;
+
+/*
+ * We maintain three lists of jobs:
+ *
+ * i)   jobs waiting for pages
+ * ii)  jobs that have pages, and are waiting for the io to be issued.
+ * iii) jobs that have completed.
+ *
+ * All three of these are protected by job_lock.
+ */
+static spinlock_t _job_lock = SPIN_LOCK_UNLOCKED;
+
+static LIST_HEAD(_complete_jobs);
+static LIST_HEAD(_io_jobs);
+static LIST_HEAD(_pages_jobs);
+
+static int jobs_init(void)
+{
+	INIT_LIST_HEAD(&_complete_jobs);
+	INIT_LIST_HEAD(&_io_jobs);
+	INIT_LIST_HEAD(&_pages_jobs);
+
+	_job_cache = kmem_cache_create("kcopyd-jobs",
+				       sizeof(struct kcopyd_job),
+				       __alignof__(struct kcopyd_job),
+				       0, NULL, NULL);
+	if (!_job_cache)
+		return -ENOMEM;
+
+	_job_pool = mempool_create(MIN_JOBS, mempool_alloc_slab,
+				   mempool_free_slab, _job_cache);
+	if (!_job_pool) {
+		kmem_cache_destroy(_job_cache);
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static void jobs_exit(void)
+{
+	BUG_ON(!list_empty(&_complete_jobs));
+	BUG_ON(!list_empty(&_io_jobs));
+	BUG_ON(!list_empty(&_pages_jobs));
+
+	mempool_destroy(_job_pool);
+	kmem_cache_destroy(_job_cache);
+}
+
+/*
+ * Functions to push and pop a job onto the head of a given job
+ * list.
+ */
+static inline struct kcopyd_job *pop(struct list_head *jobs)
+{
+	struct kcopyd_job *job = NULL;
+	unsigned long flags;
+
+	spin_lock_irqsave(&_job_lock, flags);
+
+	if (!list_empty(jobs)) {
+		job = list_entry(jobs->next, struct kcopyd_job, list);
+		list_del(&job->list);
+	}
+	spin_unlock_irqrestore(&_job_lock, flags);
+
+	return job;
+}
+
+static inline void push(struct list_head *jobs, struct kcopyd_job *job)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&_job_lock, flags);
+	list_add_tail(&job->list, jobs);
+	spin_unlock_irqrestore(&_job_lock, flags);
+}
+
+/*
+ * These three functions process 1 item from the corresponding
+ * job list.
+ *
+ * They return:
+ * < 0: error
+ *   0: success
+ * > 0: can't process yet.
+ */
+static int run_complete_job(struct kcopyd_job *job)
+{
+	void *context = job->context;
+	int read_err = job->read_err;
+	unsigned int write_err = job->write_err;
+	kcopyd_notify_fn fn = job->fn;
+
+	kcopyd_put_pages(job->kc, &job->pages);
+	mempool_free(job, _job_pool);
+	fn(read_err, write_err, context);
+	return 0;
+}
+
+static void complete_io(unsigned int error, void *context)
+{
+	struct kcopyd_job *job = (struct kcopyd_job *) context;
+
+	if (error) {
+		if (job->rw == WRITE)
+			job->write_err &= error;
+		else
+			job->read_err = 1;
+
+		if (!test_bit(KCOPYD_IGNORE_ERROR, &job->flags)) {
+			push(&_complete_jobs, job);
+			dm_daemon_wake(&_kcopyd);
+			return;
+		}
+	}
+
+	if (job->rw == WRITE)
+		push(&_complete_jobs, job);
+
+	else {
+		job->rw = WRITE;
+		push(&_io_jobs, job);
+	}
+
+	dm_daemon_wake(&_kcopyd);
+}
+
+/*
+ * Request io on as many buffer heads as we can currently get for
+ * a particular job.
+ */
+static int run_io_job(struct kcopyd_job *job)
+{
+	int r;
+
+	if (job->rw == READ)
+		r = dm_io_async(1, &job->source, job->rw,
+				list_entry(job->pages.next, struct page, list),
+				job->offset, complete_io, job);
+
+	else
+		r = dm_io_async(job->num_dests, job->dests, job->rw,
+				list_entry(job->pages.next, struct page, list),
+				job->offset, complete_io, job);
+
+	return r;
+}
+
+static int run_pages_job(struct kcopyd_job *job)
+{
+	int r;
+
+	job->nr_pages = dm_div_up(job->dests[0].count + job->offset,
+				  SECTORS_PER_PAGE);
+	r = kcopyd_get_pages(job->kc, job->nr_pages, &job->pages);
+	if (!r) {
+		/* this job is ready for io */
+		push(&_io_jobs, job);
+		return 0;
+	}
+
+	if (r == -ENOMEM)
+		/* can't complete now */
+		return 1;
+
+	return r;
+}
+
+/*
+ * Run through a list for as long as possible.  Returns the count
+ * of successful jobs.
+ */
+static int process_jobs(struct list_head *jobs, int (*fn) (struct kcopyd_job *))
+{
+	struct kcopyd_job *job;
+	int r, count = 0;
+
+	while ((job = pop(jobs))) {
+
+		r = fn(job);
+
+		if (r < 0) {
+			/* error this rogue job */
+			if (job->rw == WRITE)
+				job->write_err = (unsigned int) -1;
+			else
+				job->read_err = 1;
+			push(&_complete_jobs, job);
+			break;
+		}
+
+		if (r > 0) {
+			/*
+			 * We couldn't service this job ATM, so
+			 * push this job back onto the list.
+			 */
+			push(jobs, job);
+			break;
+		}
+
+		count++;
+	}
+
+	return count;
+}
+
+/*
+ * kcopyd does this every time it's woken up.
+ */
+static void do_work(void)
+{
+	/*
+	 * The order that these are called is *very* important.
+	 * complete jobs can free some pages for pages jobs.
+	 * Pages jobs when successful will jump onto the io jobs
+	 * list.  io jobs call wake when they complete and it all
+	 * starts again.
+	 */
+	process_jobs(&_complete_jobs, run_complete_job);
+	process_jobs(&_pages_jobs, run_pages_job);
+	process_jobs(&_io_jobs, run_io_job);
+	run_task_queue(&tq_disk);
+}
+
+/*
+ * If we are copying a small region we just dispatch a single job
+ * to do the copy, otherwise the io has to be split up into many
+ * jobs.
+ */
+static void dispatch_job(struct kcopyd_job *job)
+{
+	push(&_pages_jobs, job);
+	dm_daemon_wake(&_kcopyd);
+}
+
+static void segment_complete(int read_err,
+			     unsigned int write_err, void *context)
+{
+	/* FIXME: tidy this function */
+	sector_t progress = 0;
+	sector_t count = 0;
+	struct kcopyd_job *job = (struct kcopyd_job *) context;
+
+	down(&job->lock);
+
+	/* update the error */
+	if (read_err)
+		job->read_err = 1;
+
+	if (write_err)
+		job->write_err &= write_err;
+
+	/*
+	 * Only dispatch more work if there hasn't been an error.
+	 */
+	if ((!job->read_err && !job->write_err) ||
+	    test_bit(KCOPYD_IGNORE_ERROR, &job->flags)) {
+		/* get the next chunk of work */
+		progress = job->progress;
+		count = job->source.count - progress;
+		if (count) {
+			if (count > SUB_JOB_SIZE)
+				count = SUB_JOB_SIZE;
+
+			job->progress += count;
+		}
+	}
+	up(&job->lock);
+
+	if (count) {
+		int i;
+		struct kcopyd_job *sub_job = mempool_alloc(_job_pool, GFP_NOIO);
+
+		memcpy(sub_job, job, sizeof(*job));
+		sub_job->source.sector += progress;
+		sub_job->source.count = count;
+
+		for (i = 0; i < job->num_dests; i++) {
+			sub_job->dests[i].sector += progress;
+			sub_job->dests[i].count = count;
+		}
+
+		sub_job->fn = segment_complete;
+		sub_job->context = job;
+		dispatch_job(sub_job);
+
+	} else if (atomic_dec_and_test(&job->sub_jobs)) {
+
+		/*
+		 * To avoid a race we must keep the job around
+		 * until after the notify function has completed.
+		 * Otherwise the client may try and stop the job
+		 * after we've completed.
+		 */
+		job->fn(read_err, write_err, job->context);
+		mempool_free(job, _job_pool);
+	}
+}
+
+/*
+ * Create some little jobs that will do the move between
+ * them.
+ */
+static void split_job(struct kcopyd_job *job)
+{
+	int nr;
+
+	nr = dm_div_up(job->source.count, SUB_JOB_SIZE);
+	if (nr > job->kc->max_split)
+		nr = job->kc->max_split;
+
+	atomic_set(&job->sub_jobs, nr);
+	while (nr--)
+		segment_complete(0, 0u, job);
+}
+
+int kcopyd_copy(struct kcopyd_client *kc, struct io_region *from,
+		unsigned int num_dests, struct io_region *dests,
+		unsigned int flags, kcopyd_notify_fn fn, void *context)
+{
+	struct kcopyd_job *job;
+
+	/*
+	 * Allocate a new job.
+	 */
+	job = mempool_alloc(_job_pool, GFP_NOIO);
+
+	/*
+	 * set up for the read.
+	 */
+	job->kc = kc;
+	job->flags = flags;
+	job->read_err = 0;
+	job->write_err = 0;
+	job->rw = READ;
+
+	memcpy(&job->source, from, sizeof(*from));
+
+	job->num_dests = num_dests;
+	memcpy(&job->dests, dests, sizeof(*dests) * num_dests);
+
+	job->offset = 0;
+	job->nr_pages = 0;
+	INIT_LIST_HEAD(&job->pages);
+
+	job->fn = fn;
+	job->context = context;
+
+	if (job->source.count < SUB_JOB_SIZE)
+		dispatch_job(job);
+
+	else {
+		init_MUTEX(&job->lock);
+		job->progress = 0;
+		split_job(job);
+	}
+
+	return 0;
+}
+
+/*
+ * Cancels a kcopyd job, eg. someone might be deactivating a
+ * mirror.
+ */
+int kcopyd_cancel(struct kcopyd_job *job, int block)
+{
+	/* FIXME: finish */
+	return -1;
+}
+
+/*-----------------------------------------------------------------
+ * Unit setup
+ *---------------------------------------------------------------*/
+static DECLARE_MUTEX(_client_lock);
+static LIST_HEAD(_clients);
+
+static int client_add(struct kcopyd_client *kc)
+{
+	down(&_client_lock);
+	list_add(&kc->list, &_clients);
+	up(&_client_lock);
+	return 0;
+}
+
+static void client_del(struct kcopyd_client *kc)
+{
+	down(&_client_lock);
+	list_del(&kc->list);
+	up(&_client_lock);
+}
+
+int kcopyd_client_create(unsigned int nr_pages, struct kcopyd_client **result)
+{
+	int r = 0;
+	struct kcopyd_client *kc;
+
+	if (nr_pages * SECTORS_PER_PAGE < SUB_JOB_SIZE) {
+		DMERR("kcopyd client requested %u pages: minimum is %lu",
+		      nr_pages, SUB_JOB_SIZE / SECTORS_PER_PAGE);
+		return -ENOMEM;
+	}
+
+	kc = kmalloc(sizeof(*kc), GFP_KERNEL);
+	if (!kc)
+		return -ENOMEM;
+
+	kc->lock = SPIN_LOCK_UNLOCKED;
+	INIT_LIST_HEAD(&kc->pages);
+	kc->nr_pages = kc->nr_free_pages = 0;
+	r = client_alloc_pages(kc, nr_pages);
+	if (r) {
+		kfree(kc);
+		return r;
+	}
+
+	r = dm_io_get(nr_pages);
+	if (r) {
+		client_free_pages(kc);
+		kfree(kc);
+		return r;
+	}
+
+	r = client_add(kc);
+	if (r) {
+		dm_io_put(nr_pages);
+		client_free_pages(kc);
+		kfree(kc);
+		return r;
+	}
+
+	*result = kc;
+	return 0;
+}
+
+void kcopyd_client_destroy(struct kcopyd_client *kc)
+{
+	dm_io_put(kc->nr_pages);
+	client_free_pages(kc);
+	client_del(kc);
+	kfree(kc);
+}
+
+
+int __init kcopyd_init(void)
+{
+	int r;
+
+	r = jobs_init();
+	if (r)
+		return r;
+
+	r = dm_daemon_start(&_kcopyd, "kcopyd", do_work);
+	if (r)
+		jobs_exit();
+
+	return r;
+}
+
+void kcopyd_exit(void)
+{
+	jobs_exit();
+	dm_daemon_stop(&_kcopyd);
+}
+
+EXPORT_SYMBOL(kcopyd_client_create);
+EXPORT_SYMBOL(kcopyd_client_destroy);
+EXPORT_SYMBOL(kcopyd_copy);
+EXPORT_SYMBOL(kcopyd_cancel);
diff -urN --exclude-from=diff-exclude linux-2.4.25/drivers/md/kcopyd.h linux-2.4.25-leo/drivers/md/kcopyd.h
--- linux-2.4.25/drivers/md/kcopyd.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.25-leo/drivers/md/kcopyd.h	2004-02-20 18:37:03.000000000 +0000
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2001 Sistina Software
+ *
+ * This file is released under the GPL.
+ */
+
+#ifndef DM_KCOPYD_H
+#define DM_KCOPYD_H
+
+/*
+ * Needed for the definition of offset_t.
+ */
+#include <linux/device-mapper.h>
+#include <linux/iobuf.h>
+
+#include "dm-io.h"
+
+int kcopyd_init(void);
+void kcopyd_exit(void);
+
+/* FIXME: make this configurable */
+#define KCOPYD_MAX_REGIONS 8
+
+#define KCOPYD_IGNORE_ERROR 1
+
+/*
+ * To use kcopyd you must first create a kcopyd client object.
+ */
+struct kcopyd_client;
+int kcopyd_client_create(unsigned int num_pages, struct kcopyd_client **result);
+void kcopyd_client_destroy(struct kcopyd_client *kc);
+
+/*
+ * Submit a copy job to kcopyd.  This is built on top of the
+ * previous three fns.
+ *
+ * read_err is a boolean,
+ * write_err is a bitset, with 1 bit for each destination region
+ */
+typedef void (*kcopyd_notify_fn)(int read_err,
+				 unsigned int write_err, void *context);
+
+int kcopyd_copy(struct kcopyd_client *kc, struct io_region *from,
+		unsigned int num_dests, struct io_region *dests,
+		unsigned int flags, kcopyd_notify_fn fn, void *context);
+
+#endif
diff -urN --exclude-from=diff-exclude linux-2.4.25/drivers/md/lvm.c linux-2.4.25-leo/drivers/md/lvm.c
--- linux-2.4.25/drivers/md/lvm.c	2004-02-20 14:11:42.000000000 +0000
+++ linux-2.4.25-leo/drivers/md/lvm.c	2004-02-20 18:21:57.000000000 +0000
@@ -236,9 +236,6 @@
 #define DEVICE_OFF(device)
 #define LOCAL_END_REQUEST
 
-/* lvm_do_lv_create calls fsync_dev_lockfs()/unlockfs() */
-/* #define	LVM_VFS_ENHANCEMENT */
-
 #include <linux/config.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
@@ -2250,12 +2247,8 @@
 	if (lv_ptr->lv_access & LV_SNAPSHOT) {
 		lv_t *org = lv_ptr->lv_snapshot_org, *last;
 
-		/* sync the original logical volume */
-		fsync_dev(org->lv_dev);
-#ifdef	LVM_VFS_ENHANCEMENT
 		/* VFS function call to sync and lock the filesystem */
 		fsync_dev_lockfs(org->lv_dev);
-#endif
 
 		down_write(&org->lv_lock);
 		org->lv_access |= LV_SNAPSHOT_ORG;
@@ -2281,11 +2274,9 @@
 	else
 		set_device_ro(lv_ptr->lv_dev, 1);
 
-#ifdef	LVM_VFS_ENHANCEMENT
 /* VFS function call to unlock the filesystem */
 	if (lv_ptr->lv_access & LV_SNAPSHOT)
 		unlockfs(lv_ptr->lv_snapshot_org->lv_dev);
-#endif
 
 	lvm_gendisk.part[MINOR(lv_ptr->lv_dev)].de =
 	    lvm_fs_create_lv(vg_ptr, lv_ptr);
diff -urN --exclude-from=diff-exclude linux-2.4.25/drivers/md/Makefile linux-2.4.25-leo/drivers/md/Makefile
--- linux-2.4.25/drivers/md/Makefile	2001-11-11 18:09:32.000000000 +0000
+++ linux-2.4.25-leo/drivers/md/Makefile	2004-02-20 18:21:57.000000000 +0000
@@ -4,24 +4,41 @@
 
 O_TARGET	:= mddev.o
 
-export-objs	:= md.o xor.o
-list-multi	:= lvm-mod.o
+export-objs	:= md.o xor.o dm-table.o dm-target.o kcopyd.o dm-daemon.o \
+		   dm-log.o dm-io.o dm.o
+
+list-multi	:= lvm-mod.o dm-mod.o dm-mirror-mod.o
 lvm-mod-objs	:= lvm.o lvm-snap.o lvm-fs.o
+dm-mod-objs	:= dm.o dm-table.o dm-target.o dm-ioctl.o \
+		   dm-linear.o dm-stripe.o dm-snapshot.o dm-exception-store.o \
+		   kcopyd.o dm-daemon.o dm-io.o
+dm-mirror-mod-objs := dm-raid1.o dm-log.o
 
 # Note: link order is important.  All raid personalities
 # and xor.o must come before md.o, as they each initialise 
 # themselves, and md.o may use the personalities when it 
 # auto-initialised.
 
-obj-$(CONFIG_MD_LINEAR)		+= linear.o
-obj-$(CONFIG_MD_RAID0)		+= raid0.o
-obj-$(CONFIG_MD_RAID1)		+= raid1.o
-obj-$(CONFIG_MD_RAID5)		+= raid5.o xor.o
-obj-$(CONFIG_MD_MULTIPATH)	+= multipath.o
-obj-$(CONFIG_BLK_DEV_MD)	+= md.o
-obj-$(CONFIG_BLK_DEV_LVM)	+= lvm-mod.o
+obj-$(CONFIG_MD_LINEAR)			+= linear.o
+obj-$(CONFIG_MD_RAID0)			+= raid0.o
+obj-$(CONFIG_MD_RAID1)			+= raid1.o
+obj-$(CONFIG_MD_RAID5)			+= raid5.o xor.o
+obj-$(CONFIG_MD_MULTIPATH)		+= multipath.o
+obj-$(CONFIG_BLK_DEV_MD)		+= md.o
+
+obj-$(CONFIG_BLK_DEV_LVM)		+= lvm-mod.o
+
+obj-$(CONFIG_BLK_DEV_DM)		+= dm-mod.o
+obj-$(CONFIG_BLK_DEV_DM_MIRROR)		+= dm-mirror.o
 
 include $(TOPDIR)/Rules.make
 
 lvm-mod.o: $(lvm-mod-objs)
 	$(LD) -r -o $@ $(lvm-mod-objs)
+
+dm-mod.o: $(dm-mod-objs)
+	$(LD) -r -o $@ $(dm-mod-objs)
+
+dm-mirror.o: $(dm-mirror-mod-objs)
+	$(LD) -r -o $@ $(dm-mirror-mod-objs)
+
diff -urN --exclude-from=diff-exclude linux-2.4.25/drivers/md/md.c linux-2.4.25-leo/drivers/md/md.c
--- linux-2.4.25/drivers/md/md.c	2003-08-25 12:44:42.000000000 +0100
+++ linux-2.4.25-leo/drivers/md/md.c	2004-02-20 18:22:56.000000000 +0000
@@ -12,6 +12,7 @@
    - kmod support by: Cyrus Durgin
    - RAID0 bugfixes: Mark Anthony Lisher <markal@iname.com>
    - Devfs support by Richard Gooch <rgooch@atnf.csiro.au>
+   - Verbose startup messages by Paul Evans <nerd@freeuk.com>
 
    - lots of fixes and improvements to the RAID1/RAID5 and generic
      RAID code (such as request based resynchronization):
@@ -524,7 +525,8 @@
 		printk(NO_SB,partition_name(dev));
 		return -EINVAL;
 	}
-	printk(KERN_INFO " [events: %08lx]\n", (unsigned long)rdev->sb->events_lo);
+	VERBMSG(printk(KERN_INFO " [events: %08lx]\n", 
+                        (unsigned long)rdev->sb->events_lo))
 	ret = 0;
 abort:
 	return ret;
@@ -633,7 +635,8 @@
 	md_list_add(&rdev->same_set, &mddev->disks);
 	rdev->mddev = mddev;
 	mddev->nb_dev++;
-	printk(KERN_INFO "md: bind<%s,%d>\n", partition_name(rdev->dev), mddev->nb_dev);
+	VERBMSG(printk(KERN_INFO "md: bind<%s,%d>\n", 
+                partition_name(rdev->dev), mddev->nb_dev))
 }
 
 static void unbind_rdev_from_array(mdk_rdev_t * rdev)
@@ -645,8 +648,8 @@
 	md_list_del(&rdev->same_set);
 	MD_INIT_LIST_HEAD(&rdev->same_set);
 	rdev->mddev->nb_dev--;
-	printk(KERN_INFO "md: unbind<%s,%d>\n", partition_name(rdev->dev),
-						 rdev->mddev->nb_dev);
+	VERBMSG(printk(KERN_INFO "md: unbind<%s,%d>\n", 
+                partition_name(rdev->dev), rdev->mddev->nb_dev))
 	rdev->mddev = NULL;
 }
 
@@ -949,7 +952,8 @@
 		goto skip;
 	}
 
-	printk(KERN_INFO "(write) %s's sb offset: %ld\n", partition_name(dev), sb_offset);
+	VERBMSG(printk(KERN_INFO "(write) %s's sb offset: %ld\n", 
+		partition_name(dev), sb_offset))
 
 	if (!sync_page_io(dev, sb_offset<<1, MD_SB_BYTES, rdev->sb_page, WRITE)) {
 		printk("md: write_disk_sb failed for device %s\n", partition_name(dev));
@@ -1009,6 +1013,7 @@
 	int err, count = 100;
 	struct md_list_head *tmp;
 	mdk_rdev_t *rdev;
+	char *part_name;
 
 	if (!mddev->sb_dirty) {
 		printk("hm, md_update_sb() called without ->sb_dirty == 1, from %p.\n", __builtin_return_address(0));
@@ -1038,29 +1043,30 @@
 	if (mddev->sb->not_persistent)
 		return 0;
 
-	printk(KERN_INFO "md: updating md%d RAID superblock on device\n",
-					mdidx(mddev));
+	VERBMSG(printk(KERN_INFO "md: updating md%d RAID superblock on device\n",
+					mdidx(mddev)))
 
 	err = 0;
 	ITERATE_RDEV(mddev,rdev,tmp) {
-		printk(KERN_INFO "md: ");
+		part_name = partition_name(rdev->dev);
 		if (rdev->faulty)
-			printk("(skipping faulty ");
+			printk(KERN_INFO "md: (skipping faulty %s)\n", part_name);
 		if (rdev->alias_device)
-			printk("(skipping alias ");
+			printk(KERN_INFO "md: (skipping alias %s)\n", part_name);
 		if (!rdev->faulty && disk_faulty(&rdev->sb->this_disk)) {
-			printk("(skipping new-faulty %s )\n",
-			       partition_name(rdev->dev));
+			printk(KERN_INFO "md: (skipping new-faulty %s)\n",
+			       part_name);
 			continue;
 		}
-		printk("%s ", partition_name(rdev->dev));
+		VERBMSG(printk(KERN_INFO "md: %s ", part_name))
+
 		if (!rdev->faulty && !rdev->alias_device) {
-			printk("[events: %08lx]",
-				(unsigned long)rdev->sb->events_lo);
+			VERBMSG(printk("[events: %08lx]",
+				(unsigned long)rdev->sb->events_lo))
 			err += write_disk_sb(rdev);
-		} else
-			printk(")\n");
+		}
 	}
+	VERBMSG(printk("\n"))
 	if (err) {
 		if (--count) {
 			printk(KERN_ERR "md: errors occurred during superblock update, repeating\n");
@@ -1246,9 +1252,9 @@
 					rdev->sb->events_hi--;
 		}
 
-		printk(KERN_INFO "md: %s's event counter: %08lx\n",
+		VERBMSG(printk(KERN_INFO "md: %s's event counter: %08lx\n",
 		       partition_name(rdev->dev),
-			(unsigned long)rdev->sb->events_lo);
+			(unsigned long)rdev->sb->events_lo))
 		if (!freshest) {
 			freshest = rdev;
 			continue;
@@ -1600,12 +1606,13 @@
 	}
 	md_maxreadahead[mdidx(mddev)] = readahead;
 
-	printk(KERN_INFO "md%d: max total readahead window set to %ldk\n",
-		mdidx(mddev), readahead*(PAGE_SIZE/1024));
+	VERBMSG(printk(KERN_INFO "md%d: max total readahead window set to %ldk\n",
+	        	mdidx(mddev), readahead*(PAGE_SIZE/1024));
+
+        	printk(KERN_INFO
+		        "md%d: %d data-disks, max readahead per data-disk: %ldk\n",
+			mdidx(mddev), data_disks, readahead/data_disks*(PAGE_SIZE/1024)))
 
-	printk(KERN_INFO
-		"md%d: %d data-disks, max readahead per data-disk: %ldk\n",
-			mdidx(mddev), data_disks, readahead/data_disks*(PAGE_SIZE/1024));
 	return 0;
 abort:
 	return 1;
@@ -1684,9 +1691,10 @@
 			return -EINVAL;
 		}
 	} else
-		if (chunk_size)
-			printk(KERN_INFO "md: RAID level %d does not need chunksize! Continuing anyway.\n",
-			       mddev->sb->level);
+		if (chunk_size) {
+			VERBMSG(printk(KERN_INFO "md: RAID level %d does not need chunksize! Continuing anyway.\n",
+			       mddev->sb->level))
+                }
 
 	if (pnum >= MAX_PERSONALITY) {
 		MD_BUG();
@@ -1857,7 +1865,7 @@
 			 * interrupted.
 			 */
 			if (!mddev->recovery_running && !resync_interrupted) {
-				printk(KERN_INFO "md: marking sb clean...\n");
+				VERBMSG(printk(KERN_INFO "md: marking sb clean...\n"))
 				mddev->sb->state |= 1 << MD_SB_CLEAN;
 			}
 			mddev->sb_dirty = 1;
@@ -1871,11 +1879,13 @@
 	 * Free resources if final stop
 	 */
 	if (!ro) {
-		printk(KERN_INFO "md: md%d stopped.\n", mdidx(mddev));
+		VERBMSG(printk(KERN_INFO "md: md%d stopped.\n", mdidx(mddev)))
 		free_mddev(mddev);
 
-	} else
-		printk(KERN_INFO "md: md%d switched to read-only mode.\n", mdidx(mddev));
+	} else {
+		VERBMSG(printk(KERN_INFO "md: md%d switched to read-only mode.\n", mdidx(mddev)))
+        }
+
 out:
 	return err;
 }
@@ -1898,8 +1908,6 @@
 
 static void autorun_array(mddev_t *mddev)
 {
-	mdk_rdev_t *rdev;
-	struct md_list_head *tmp;
 	int err;
 
 	if (mddev->disks.prev == &mddev->disks) {
@@ -1907,12 +1915,17 @@
 		return;
 	}
 
-	printk(KERN_INFO "md: running: ");
-
-	ITERATE_RDEV(mddev,rdev,tmp) {
-		printk("<%s>", partition_name(rdev->dev));
-	}
-	printk("\n");
+        VERBMSG({       /* remember; VERBMSG is just an #ifdef section */
+                mdk_rdev_t *rdev;
+                struct md_list_head *tmp;
+                
+        	printk(KERN_INFO "md: running: ");
+
+        	ITERATE_RDEV(mddev,rdev,tmp) {
+	        	printk("<%s>", partition_name(rdev->dev));
+        	}
+	        printk("\n");
+        })
 
 	err = do_md_run (mddev);
 	if (err) {
@@ -1946,12 +1959,13 @@
 	kdev_t md_kdev;
 
 
-	printk(KERN_INFO "md: autorun ...\n");
+	VERBMSG(printk(KERN_INFO "md: autorun ...\n"))
 	while (pending_raid_disks.next != &pending_raid_disks) {
 		rdev0 = md_list_entry(pending_raid_disks.next,
 					 mdk_rdev_t, pending);
 
-		printk(KERN_INFO "md: considering %s ...\n", partition_name(rdev0->dev));
+		VERBMSG(printk(KERN_INFO "md: considering %s ...\n", 
+                        partition_name(rdev0->dev)))
 		MD_INIT_LIST_HEAD(&candidates);
 		ITERATE_RDEV_PENDING(rdev,tmp) {
 			if (uuid_equal(rdev0, rdev)) {
@@ -1961,7 +1975,8 @@
 					       partition_name(rdev->dev), partition_name(rdev0->dev));
 					continue;
 				}
-				printk(KERN_INFO "md:  adding %s ...\n", partition_name(rdev->dev));
+				VERBMSG(printk(KERN_INFO "md:  adding %s ...\n", 
+                                        partition_name(rdev->dev)))
 				md_list_del(&rdev->pending);
 				md_list_add(&rdev->pending, &candidates);
 			}
@@ -1995,7 +2010,7 @@
 		}
 		autorun_array(mddev);
 	}
-	printk(KERN_INFO "md: ... autorun DONE.\n");
+	VERBMSG(printk(KERN_INFO "md: ... autorun DONE.\n"))
 }
 
 /*
@@ -3315,7 +3330,7 @@
 	}
 
 	pers[pnum] = p;
-	printk(KERN_INFO "md: %s personality registered as nr %d\n", p->name, pnum);
+	VERBMSG(printk(KERN_INFO "md: %s personality registered as nr %d\n", p->name, pnum))
 	return 0;
 }
 
@@ -3326,7 +3341,7 @@
 		return -EINVAL;
 	}
 
-	printk(KERN_INFO "md: %s personality unregistered\n", pers[pnum]->name);
+	VERBMSG(printk(KERN_INFO "md: %s personality unregistered\n", pers[pnum]->name))
 	pers[pnum] = NULL;
 	return 0;
 }
@@ -3690,6 +3705,7 @@
 		 */
 		md_mdelay(1000*1);
 	}
+
 	return NOTIFY_DONE;
 }
 
diff -urN --exclude-from=diff-exclude linux-2.4.25/drivers/md/raid0.c linux-2.4.25-leo/drivers/md/raid0.c
--- linux-2.4.25/drivers/md/raid0.c	2003-06-13 15:51:34.000000000 +0100
+++ linux-2.4.25-leo/drivers/md/raid0.c	2004-02-20 18:22:56.000000000 +0000
@@ -38,12 +38,12 @@
 	conf->nr_strip_zones = 0;
  
 	ITERATE_RDEV_ORDERED(mddev,rdev1,j1) {
-		printk("raid0: looking at %s\n", partition_name(rdev1->dev));
+                VERBMSG(printk("raid0: looking at %s\n", partition_name(rdev1->dev)))
 		c = 0;
 		ITERATE_RDEV_ORDERED(mddev,rdev2,j2) {
-			printk("raid0:   comparing %s(%ld) with %s(%ld)\n", partition_name(rdev1->dev), rdev1->size, partition_name(rdev2->dev), rdev2->size);
+			VERBMSG(printk("raid0:   comparing %s(%ld) with %s(%ld)\n", partition_name(rdev1->dev), rdev1->size, partition_name(rdev2->dev), rdev2->size))
 			if (rdev2 == rdev1) {
-				printk("raid0:   END\n");
+				VERBMSG(printk("raid0:   END\n"))
 				break;
 			}
 			if (rdev2->size == rdev1->size)
@@ -52,19 +52,19 @@
 				 * Not unique, dont count it as a new
 				 * group
 				 */
-				printk("raid0:   EQUAL\n");
+				VERBMSG(printk("raid0:   EQUAL\n"))
 				c = 1;
 				break;
 			}
-			printk("raid0:   NOT EQUAL\n");
+			VERBMSG(printk("raid0:   NOT EQUAL\n"))
 		}
 		if (!c) {
-			printk("raid0:   ==> UNIQUE\n");
+			VERBMSG(printk("raid0:   ==> UNIQUE\n"))
 			conf->nr_strip_zones++;
-			printk("raid0: %d zones\n", conf->nr_strip_zones);
+			VERBMSG(printk("raid0: %d zones\n", conf->nr_strip_zones))
 		}
 	}
-		printk("raid0: FINAL %d zones\n", conf->nr_strip_zones);
+		VERBMSG(printk("raid0: FINAL %d zones\n", conf->nr_strip_zones))
 
 	conf->strip_zone = vmalloc(sizeof(struct strip_zone)*
 				conf->nr_strip_zones);
@@ -80,30 +80,31 @@
 	{
 		struct strip_zone *zone = conf->strip_zone + i;
 
-		printk("raid0: zone %d\n", i);
+		VERBMSG(printk("raid0: zone %d\n", i))
 		zone->dev_offset = current_offset;
 		smallest = NULL;
 		c = 0;
 
 		ITERATE_RDEV_ORDERED(mddev,rdev,j) {
 
-			printk("raid0: checking %s ...", partition_name(rdev->dev));
+			VERBMSG(printk("raid0: checking %s ...", partition_name(rdev->dev)))
 			if (rdev->size > current_offset)
 			{
-				printk(" contained as device %d\n", c);
+				VERBMSG(printk(" contained as device %d\n", c))
 				zone->dev[c] = rdev;
 				c++;
 				if (!smallest || (rdev->size <smallest->size)) {
 					smallest = rdev;
-					printk("  (%ld) is smallest!.\n", rdev->size);
+					VERBMSG(printk("  (%ld) is smallest!.\n", rdev->size))
 				}
-			} else
-				printk(" nope.\n");
+			} else {
+				VERBMSG(printk(" nope.\n"))
+                        }
 		}
 
 		zone->nb_dev = c;
 		zone->size = (smallest->size - current_offset) * c;
-		printk("raid0: zone->nb_dev: %d, size: %ld\n",zone->nb_dev,zone->size);
+		VERBMSG(printk("raid0: zone->nb_dev: %d, size: %ld\n",zone->nb_dev,zone->size))
 
 		if (!conf->smallest || (zone->size < conf->smallest->size))
 			conf->smallest = zone;
@@ -112,9 +113,9 @@
 		curr_zone_offset += zone->size;
 
 		current_offset = smallest->size;
-		printk("raid0: current zone offset: %ld\n", current_offset);
+		VERBMSG(printk("raid0: current zone offset: %ld\n", current_offset))
 	}
-	printk("raid0: done.\n");
+	VERBMSG(printk("raid0: done.\n"))
 	return 0;
 }
 
@@ -138,15 +139,15 @@
 	if (create_strip_zones (mddev)) 
 		goto out_free_conf;
 
-	printk("raid0 : md_size is %d blocks.\n", md_size[mdidx(mddev)]);
-	printk("raid0 : conf->smallest->size is %ld blocks.\n", conf->smallest->size);
+	VERBMSG(printk("raid0: md_size is %d blocks.\n", md_size[mdidx(mddev)]);
+	          printk("raid0: conf->smallest->size is %ld blocks.\n", conf->smallest->size))
 	nb_zone = md_size[mdidx(mddev)]/conf->smallest->size +
 			(md_size[mdidx(mddev)] % conf->smallest->size ? 1 : 0);
-	printk("raid0 : nb_zone is %ld.\n", nb_zone);
+	VERBMSG(printk("raid0: nb_zone is %ld.\n", nb_zone))
 	conf->nr_zones = nb_zone;
 
-	printk("raid0 : Allocating %ld bytes for hash.\n",
-				nb_zone*sizeof(struct raid0_hash));
+	VERBMSG(printk("raid0: Allocating %ld bytes for hash.\n",
+				nb_zone*sizeof(struct raid0_hash)))
 
 	conf->hash_table = vmalloc (sizeof (struct raid0_hash)*nb_zone);
 	if (!conf->hash_table)
diff -urN --exclude-from=diff-exclude linux-2.4.25/drivers/md/raid1.c linux-2.4.25-leo/drivers/md/raid1.c
--- linux-2.4.25/drivers/md/raid1.c	2004-02-20 14:11:42.000000000 +0000
+++ linux-2.4.25-leo/drivers/md/raid1.c	2004-02-20 18:22:56.000000000 +0000
@@ -1616,8 +1616,9 @@
 					disk_idx);
 				continue;
 			}
-			printk(OPERATIONAL, partition_name(rdev->dev),
- 					disk_idx);
+			VERBMSG(printk(OPERATIONAL, 
+                                        partition_name(rdev->dev),
+ 					disk_idx))
 			disk->number = descriptor->number;
 			disk->raid_disk = disk_idx;
 			disk->dev = rdev->dev;
@@ -1632,7 +1633,7 @@
 		/*
 		 * Must be a spare disk ..
 		 */
-			printk(SPARE, partition_name(rdev->dev));
+			VERBMSG(printk(SPARE, partition_name(rdev->dev)))
 			disk->number = descriptor->number;
 			disk->raid_disk = disk_idx;
 			disk->dev = rdev->dev;
@@ -1755,7 +1756,8 @@
 		md_recover_arrays();
 
 
-	printk(ARRAY_IS_ACTIVE, mdidx(mddev), sb->active_disks, sb->raid_disks);
+	VERBMSG(printk(ARRAY_IS_ACTIVE, mdidx(mddev), 
+			sb->active_disks, sb->raid_disks))
 	/*
 	 * Ok, everything is just fine now
 	 */
diff -urN --exclude-from=diff-exclude linux-2.4.25/drivers/md/raid5.c linux-2.4.25-leo/drivers/md/raid5.c
--- linux-2.4.25/drivers/md/raid5.c	2003-08-25 12:44:42.000000000 +0100
+++ linux-2.4.25-leo/drivers/md/raid5.c	2004-02-20 18:22:56.000000000 +0000
@@ -1448,7 +1448,8 @@
 				printk(KERN_ERR "raid5: disabled device %s (device %d already operational)\n", partition_name(rdev->dev), raid_disk);
 				continue;
 			}
-			printk(KERN_INFO "raid5: device %s operational as raid disk %d\n", partition_name(rdev->dev), raid_disk);
+			VERBMSG(printk(KERN_INFO "raid5: device %s operational as raid disk %d\n", 
+                                partition_name(rdev->dev), raid_disk))
 	
 			disk->number = desc->number;
 			disk->raid_disk = raid_disk;
@@ -1461,7 +1462,8 @@
 			/*
 			 * Must be a spare disk ..
 			 */
-			printk(KERN_INFO "raid5: spare disk %s\n", partition_name(rdev->dev));
+			VERBMSG(printk(KERN_INFO "raid5: spare disk %s\n", 
+                                partition_name(rdev->dev)))
 			disk->number = desc->number;
 			disk->raid_disk = raid_disk;
 			disk->dev = rdev->dev;
diff -urN --exclude-from=diff-exclude linux-2.4.25/drivers/pci/proc.c linux-2.4.25-leo/drivers/pci/proc.c
--- linux-2.4.25/drivers/pci/proc.c	2002-11-28 23:53:14.000000000 +0000
+++ linux-2.4.25-leo/drivers/pci/proc.c	2004-02-20 18:22:22.000000000 +0000
@@ -562,7 +562,15 @@
 		pci_for_each_dev(dev) {
 			pci_proc_attach_device(dev);
 		}
+#ifdef CONFIG_GRKERNSEC_PROC_ADD
+#ifdef CONFIG_GRKERNSEC_PROC_USER
+		entry = create_proc_entry("pci", S_IRUSR, NULL);
+#elif CONFIG_GRKERNSEC_PROC_USERGROUP
+		entry = create_proc_entry("pci", S_IRUSR | S_IRGRP, NULL);
+#endif
+#else
 		entry = create_proc_entry("pci", 0, NULL);
+#endif
 		if (entry)
 			entry->proc_fops = &proc_pci_operations;
 	}
diff -urN --exclude-from=diff-exclude linux-2.4.25/drivers/sound/sound_core.c linux-2.4.25-leo/drivers/sound/sound_core.c
--- linux-2.4.25/drivers/sound/sound_core.c	2001-09-30 20:26:08.000000000 +0100
+++ linux-2.4.25-leo/drivers/sound/sound_core.c	2004-02-20 18:23:08.000000000 +0000
@@ -37,6 +37,7 @@
 #include <linux/config.h>
 #include <linux/module.h>
 #include <linux/init.h>
+#include <linux/sched.h>
 #include <linux/slab.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
diff -urN --exclude-from=diff-exclude linux-2.4.25/drivers/usb/storage/unusual_devs.h linux-2.4.25-leo/drivers/usb/storage/unusual_devs.h
--- linux-2.4.25/drivers/usb/storage/unusual_devs.h	2004-02-20 14:11:44.000000000 +0000
+++ linux-2.4.25-leo/drivers/usb/storage/unusual_devs.h	2004-02-20 18:51:38.000000000 +0000
@@ -315,6 +315,18 @@
 		"USB Hard Disk",
 		US_SC_RBC, US_PR_CB, NULL, 0 ), 
 
+/* Included by Paul Evans <nerd@freeuk.com>
+ * For A-mego Dual Slot USB Card Reader/Writer
+ * From instructions found on:
+ * http://www.qbik.ch/usb/devices/showdev.php?id=1262
+ */
+
+UNUSUAL_DEV(  0x0aec, 0x5010, 0x0100, 0x0100, 
+		"A-Mego", 
+		"CSU-LA1 Dual Slot USB Card Reader / Writer", 
+		US_SC_SCSI, US_PR_BULK, NULL, 
+		US_FL_FIX_INQUIRY ),
+
 #ifdef CONFIG_USB_STORAGE_ISD200
 UNUSUAL_DEV(  0x05ab, 0x0031, 0x0100, 0x0110,
 		"In-System",
diff -urN --exclude-from=diff-exclude linux-2.4.25/fs/adfs/map.c linux-2.4.25-leo/fs/adfs/map.c
--- linux-2.4.25/fs/adfs/map.c	2001-10-25 21:53:53.000000000 +0100
+++ linux-2.4.25-leo/fs/adfs/map.c	2004-02-20 18:23:08.000000000 +0000
@@ -12,6 +12,7 @@
 #include <linux/fs.h>
 #include <linux/adfs_fs.h>
 #include <linux/spinlock.h>
+#include <linux/sched.h>
 
 #include "adfs.h"
 
diff -urN --exclude-from=diff-exclude linux-2.4.25/fs/binfmt_aout.c linux-2.4.25-leo/fs/binfmt_aout.c
--- linux-2.4.25/fs/binfmt_aout.c	2001-11-03 01:39:20.000000000 +0000
+++ linux-2.4.25-leo/fs/binfmt_aout.c	2004-02-20 18:22:22.000000000 +0000
@@ -5,6 +5,7 @@
  */
 
 #include <linux/module.h>
+#include <linux/config.h>
 
 #include <linux/sched.h>
 #include <linux/kernel.h>
@@ -113,10 +114,12 @@
 /* If the size of the dump file exceeds the rlimit, then see what would happen
    if we wrote the stack, but not the data area.  */
 #ifdef __sparc__
+	gr_learn_resource(current, RLIMIT_CORE, dump.u_dsize+dump.u_ssize, 1);
 	if ((dump.u_dsize+dump.u_ssize) >
 	    current->rlim[RLIMIT_CORE].rlim_cur)
 		dump.u_dsize = 0;
 #else
+	gr_learn_resource(current, RLIMIT_CORE, (dump.u_dsize+dump.u_ssize+1) * PAGE_SIZE, 1);
 	if ((dump.u_dsize+dump.u_ssize+1) * PAGE_SIZE >
 	    current->rlim[RLIMIT_CORE].rlim_cur)
 		dump.u_dsize = 0;
@@ -124,10 +127,12 @@
 
 /* Make sure we have enough room to write the stack and data areas. */
 #ifdef __sparc__
+	gr_learn_resource(current, RLIMIT_CORE, dump.u_ssize, 1);
 	if ((dump.u_ssize) >
 	    current->rlim[RLIMIT_CORE].rlim_cur)
 		dump.u_ssize = 0;
 #else
+	gr_learn_resource(current, RLIMIT_CORE, (dump.u_ssize+1) * PAGE_SIZE, 1);
 	if ((dump.u_ssize+1) * PAGE_SIZE >
 	    current->rlim[RLIMIT_CORE].rlim_cur)
 		dump.u_ssize = 0;
@@ -276,6 +281,8 @@
 	rlim = current->rlim[RLIMIT_DATA].rlim_cur;
 	if (rlim >= RLIM_INFINITY)
 		rlim = ~0;
+
+	gr_learn_resource(current, RLIMIT_DATA, ex.a_data + ex.a_bss, 1);
 	if (ex.a_data + ex.a_bss > rlim)
 		return -ENOMEM;
 
@@ -307,6 +314,24 @@
 	current->mm->mmap = NULL;
 	compute_creds(bprm);
  	current->flags &= ~PF_FORKNOEXEC;
+
+#ifdef CONFIG_GRKERNSEC_PAX_PAGEEXEC
+	if (!(N_FLAGS(ex) & F_PAX_PAGEEXEC)) {
+		current->flags |= PF_PAX_PAGEEXEC;
+
+#ifdef CONFIG_GRKERNSEC_PAX_EMUTRAMP
+		if (N_FLAGS(ex) & F_PAX_EMUTRAMP)
+			current->flags |= PF_PAX_EMUTRAMP;
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_MPROTECT
+		if (!(N_FLAGS(ex) & F_PAX_MPROTECT))        
+			current->flags |= PF_PAX_MPROTECT;
+#endif
+
+	}
+#endif
+
 #ifdef __sparc__
 	if (N_MAGIC(ex) == NMAGIC) {
 		loff_t pos = fd_offset;
@@ -393,7 +418,7 @@
 
 		down_write(&current->mm->mmap_sem);
  		error = do_mmap(bprm->file, N_DATADDR(ex), ex.a_data,
-				PROT_READ | PROT_WRITE | PROT_EXEC,
+				PROT_READ | PROT_WRITE,
 				MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE,
 				fd_offset + ex.a_text);
 		up_write(&current->mm->mmap_sem);
diff -urN --exclude-from=diff-exclude linux-2.4.25/fs/binfmt_elf.c linux-2.4.25-leo/fs/binfmt_elf.c
--- linux-2.4.25/fs/binfmt_elf.c	2004-02-20 14:11:44.000000000 +0000
+++ linux-2.4.25-leo/fs/binfmt_elf.c	2004-02-20 18:22:22.000000000 +0000
@@ -11,6 +11,7 @@
 
 #include <linux/module.h>
 
+#include <linux/config.h>
 #include <linux/fs.h>
 #include <linux/stat.h>
 #include <linux/sched.h>
@@ -33,10 +34,13 @@
 #include <linux/smp_lock.h>
 #include <linux/compiler.h>
 #include <linux/highmem.h>
+#include <linux/random.h>
+#include <linux/grsecurity.h>
 
 #include <asm/uaccess.h>
 #include <asm/param.h>
 #include <asm/pgalloc.h>
+#include <asm/system.h>
 
 #define DLINFO_ITEMS 13
 
@@ -86,6 +90,12 @@
 	if (end <= start)
 		return;
 	do_brk(start, end - start);
+
+#ifdef CONFIG_GRKERNSEC_PAX_RANDEXEC
+	if (current->flags & PF_PAX_RANDEXEC)
+		do_mmap_pgoff(NULL, ELF_PAGEALIGN(start + current->mm->delta_exec), 0UL, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_FIXED | MAP_MIRROR, start);
+#endif
+
 }
 
 
@@ -446,6 +456,11 @@
   	struct exec interp_ex;
 	char passed_fileno[6];
 	struct files_struct *files;
+
+#ifdef CONFIG_GRKERNSEC_PAX_RANDEXEC
+	unsigned long load_addr_random = 0UL;
+	unsigned long load_bias_random = 0UL;
+#endif
 	
 	/* Get the exec-header */
 	elf_ex = *((struct elfhdr *) bprm->buf);
@@ -622,7 +637,92 @@
 	current->mm->end_data = 0;
 	current->mm->end_code = 0;
 	current->mm->mmap = NULL;
+
+#ifdef CONFIG_GRKERNSEC_PAX_ASLR
+	current->mm->delta_mmap = 0UL;
+	current->mm->delta_exec = 0UL;
+	current->mm->delta_stack = 0UL;
+#endif
+
 	current->flags &= ~PF_FORKNOEXEC;
+
+#ifdef CONFIG_GRKERNSEC_PAX_PAGEEXEC
+	if (!(elf_ex.e_ident[EI_PAX] & EF_PAX_PAGEEXEC))
+		current->flags |= PF_PAX_PAGEEXEC;
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_SEGMEXEC
+	if (!(elf_ex.e_ident[EI_PAX] & EF_PAX_SEGMEXEC)) {
+		current->flags &= ~PF_PAX_PAGEEXEC;
+		current->flags |= PF_PAX_SEGMEXEC;
+	}
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_EMUTRAMP
+	if ((current->flags & (PF_PAX_PAGEEXEC | PF_PAX_SEGMEXEC)) && (elf_ex.e_ident[EI_PAX] & EF_PAX_EMUTRAMP))
+		current->flags |= PF_PAX_EMUTRAMP;
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_MPROTECT
+	if ((current->flags & (PF_PAX_PAGEEXEC | PF_PAX_SEGMEXEC)) && !(elf_ex.e_ident[EI_PAX] & EF_PAX_MPROTECT))
+		current->flags |= PF_PAX_MPROTECT;
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_ASLR
+	if (!(elf_ex.e_ident[EI_PAX] & EF_PAX_RANDMMAP))
+		current->flags |= PF_PAX_RANDMMAP;
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_RANDEXEC
+	if ((elf_ex.e_ident[EI_PAX] & EF_PAX_RANDEXEC) && (elf_ex.e_type == ET_EXEC) &&
+	    (current->flags & (PF_PAX_PAGEEXEC | PF_PAX_SEGMEXEC)))
+		current->flags |= PF_PAX_RANDEXEC;
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_PT_GNU_HEAP
+	elf_ppnt = elf_phdata;
+	for (i = 0; i < elf_ex.e_phnum; i++, elf_ppnt++)
+		if (elf_ppnt->p_type == PT_GNU_HEAP) {
+			if (elf_ppnt->p_flags & PF_X)
+				current->flags & ~PF_PAX_MPROTECT;
+			break;
+		}
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_PT_GNU_STACK
+	elf_ppnt = elf_phdata;
+	for (i = 0; i < elf_ex.e_phnum; i++, elf_ppnt++)
+		if (elf_ppnt->p_type == PT_GNU_STACK) {
+			if ((elf_ppnt->p_flags & PF_X) && (current->flags & PF_PAX_MPROTECT))
+				current->flags |= PF_PAX_EMUTRAMP;
+			break;
+		}
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_HAVE_ACL_FLAGS
+	pax_set_flags(bprm);
+#elif defined(CONFIG_GRKERNSEC_PAX_HOOK_ACL_FLAGS)
+	if (pax_set_flags_func)
+		(*pax_set_flags_func)(bprm);
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_DLRESOLVE
+	if (current->flags & PF_PAX_PAGEEXEC)
+		current->mm->call_dl_resolve = 0UL;
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_ASLR
+	if (current->flags & PF_PAX_RANDMMAP) {
+#define pax_delta_mask(delta, lsb, len) (((delta) & ((1UL << (len)) - 1)) << (lsb))
+
+		current->mm->delta_mmap = pax_delta_mask(get_random_long(), PAX_DELTA_MMAP_LSB(current), PAX_DELTA_MMAP_LEN(current));
+		current->mm->delta_exec = pax_delta_mask(get_random_long(), PAX_DELTA_EXEC_LSB(current), PAX_DELTA_EXEC_LEN(current));
+		current->mm->delta_stack = pax_delta_mask(get_random_long(), PAX_DELTA_STACK_LSB(current), PAX_DELTA_STACK_LEN(current));
+	}
+#endif
+
+	gr_set_pax_flags(current);	
+
 	elf_entry = (unsigned long) elf_ex.e_entry;
 
 	/* Do this so that we can load the interpreter, if need be.  We will
@@ -631,7 +731,7 @@
 	retval = setup_arg_pages(bprm);
 	if (retval < 0) {
 		send_sig(SIGKILL, current, 0);
-		return retval;
+		goto out_free_dentry;
 	}
 	
 	current->mm->start_stack = bprm->p;
@@ -678,11 +778,84 @@
 			   base, as well as whatever program they might try to exec.  This
 		           is because the brk will follow the loader, and is not movable.  */
 			load_bias = ELF_PAGESTART(ELF_ET_DYN_BASE - vaddr);
+
+#ifdef CONFIG_GRKERNSEC_PAX_RANDMMAP
+			/* PaX: randomize base address at the default exe base if requested */
+			if (current->flags & PF_PAX_RANDMMAP) {
+				load_bias = ELF_PAGESTART(PAX_ELF_ET_DYN_BASE(current) - vaddr + current->mm->delta_exec);
+				elf_flags |= MAP_FIXED;
+			}
+#endif
+
 		}
 
-		error = elf_map(bprm->file, load_bias + vaddr, elf_ppnt, elf_prot, elf_flags);
-		if (BAD_ADDR(error))
-			continue;
+#ifdef CONFIG_GRKERNSEC_PAX_RANDEXEC
+		if ((current->flags & PF_PAX_RANDEXEC) && (elf_ex.e_type == ET_EXEC)) {
+			error = -ENOMEM;
+
+#ifdef CONFIG_GRKERNSEC_PAX_PAGEEXEC
+			if (current->flags & PF_PAX_PAGEEXEC)
+				error = elf_map(bprm->file, load_bias + vaddr, elf_ppnt, elf_prot & ~PROT_EXEC, elf_flags);
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_SEGMEXEC
+			if (current->flags & PF_PAX_SEGMEXEC) {
+				unsigned long addr, len;
+
+				addr = ELF_PAGESTART(load_bias + vaddr);
+				len = elf_ppnt->p_filesz + ELF_PAGEOFFSET(elf_ppnt->p_vaddr);
+				if (len > SEGMEXEC_TASK_SIZE || addr > SEGMEXEC_TASK_SIZE-len)
+					continue;
+				down_write(&current->mm->mmap_sem);
+				error = do_mmap_pgoff(bprm->file, addr, len, elf_prot, elf_flags, (elf_ppnt->p_offset - ELF_PAGEOFFSET(elf_ppnt->p_vaddr)) >> PAGE_SHIFT);
+				up_write(&current->mm->mmap_sem);
+			}
+#endif
+
+			if (BAD_ADDR(error))
+				continue;
+
+			/* PaX: mirror at a randomized base */
+			down_write(&current->mm->mmap_sem);
+
+			if (!load_addr_set) {
+				load_addr_random = get_unmapped_area(bprm->file, 0UL, elf_ppnt->p_filesz + ELF_PAGEOFFSET(elf_ppnt->p_vaddr), (elf_ppnt->p_offset - ELF_PAGEOFFSET(elf_ppnt->p_vaddr)) >> PAGE_SHIFT, MAP_PRIVATE);
+				if (BAD_ADDR(load_addr_random)) {
+					up_write(&current->mm->mmap_sem);
+					continue;
+				}
+				load_bias_random = load_addr_random - vaddr;
+			}
+
+#ifdef CONFIG_GRKERNSEC_PAX_PAGEEXEC
+			if (current->flags & PF_PAX_PAGEEXEC)
+				load_addr_random = do_mmap_pgoff(NULL, ELF_PAGESTART(load_bias_random + vaddr), 0UL, elf_prot, elf_flags | MAP_MIRROR, error);
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_SEGMEXEC
+			if (current->flags & PF_PAX_SEGMEXEC) {
+				if (elf_prot & PROT_EXEC) {
+					load_addr_random = do_mmap_pgoff(NULL, ELF_PAGESTART(load_bias_random + vaddr), elf_ppnt->p_memsz + ELF_PAGEOFFSET(elf_ppnt->p_vaddr), PROT_NONE, MAP_PRIVATE | MAP_FIXED, 0UL);
+					if (!BAD_ADDR(load_addr_random)) {
+						load_addr_random = do_mmap_pgoff(NULL, ELF_PAGESTART(load_bias_random + vaddr + SEGMEXEC_TASK_SIZE), 0UL, elf_prot, elf_flags | MAP_MIRROR, error);
+						if (!BAD_ADDR(load_addr_random))
+							load_addr_random -= SEGMEXEC_TASK_SIZE;
+					}
+				} else
+					load_addr_random = do_mmap_pgoff(NULL, ELF_PAGESTART(load_bias_random + vaddr), 0UL, elf_prot, elf_flags | MAP_MIRROR, error);
+			}
+#endif
+
+			up_write(&current->mm->mmap_sem);
+			if (BAD_ADDR(load_addr_random))
+				continue;
+		} else
+#endif
+		{
+			error = elf_map(bprm->file, load_bias + vaddr, elf_ppnt, elf_prot, elf_flags);
+			if (BAD_ADDR(error))
+				continue;
+		}
 
 		if (!load_addr_set) {
 			load_addr_set = 1;
@@ -693,6 +866,11 @@
 				load_addr += load_bias;
 				reloc_func_desc = load_addr;
 			}
+
+#ifdef CONFIG_GRKERNSEC_PAX_RANDEXEC
+			current->mm->delta_exec = load_addr_random - load_addr;
+#endif
+
 		}
 		k = elf_ppnt->p_vaddr;
 		if (k < start_code) start_code = k;
@@ -719,6 +897,18 @@
 	start_data += load_bias;
 	end_data += load_bias;
 
+#ifdef CONFIG_GRKERNSEC_PAX_RANDMMAP
+	elf_brk += pax_delta_mask(get_random_long(), 4, PAGE_SHIFT);
+#undef pax_delta_mask
+#endif
+
+	/* Calling set_brk effectively mmaps the pages that we need
+	 * for the bss and break sections
+	 */
+	set_brk(elf_bss, elf_brk);
+
+	padzero(elf_bss);
+
 	if (elf_interpreter) {
 		if (interpreter_type == INTERPRETER_AOUT)
 			elf_entry = load_aout_interp(&interp_ex,
@@ -767,13 +957,6 @@
 	current->mm->end_data = end_data;
 	current->mm->start_stack = bprm->p;
 
-	/* Calling set_brk effectively mmaps the pages that we need
-	 * for the bss and break sections
-	 */
-	set_brk(elf_bss, elf_brk);
-
-	padzero(elf_bss);
-
 #if 0
 	printk("(start_brk) %lx\n" , (long) current->mm->start_brk);
 	printk("(end_code) %lx\n" , (long) current->mm->end_code);
@@ -810,6 +993,10 @@
 	ELF_PLAT_INIT(regs, reloc_func_desc);
 #endif
 
+#ifdef CONFIG_GRKERNSEC_PAX_SEGMEXEC
+	pax_switch_segments(current);
+#endif
+
 	start_thread(regs, elf_entry, bprm->p);
 	if (current->ptrace & PT_PTRACED)
 		send_sig(SIGTRAP, current, 0);
@@ -1037,8 +1224,11 @@
 #undef DUMP_SEEK
 
 #define DUMP_WRITE(addr, nr)	\
+	do { \
+	gr_learn_resource(current, RLIMIT_CORE, size + (nr), 1); \
 	if ((size += (nr)) > limit || !dump_write(file, (addr), (nr))) \
-		goto end_coredump;
+		goto end_coredump; \
+	} while (0);
 #define DUMP_SEEK(off)	\
 	if (!dump_seek(file, (off))) \
 		goto end_coredump;
diff -urN --exclude-from=diff-exclude linux-2.4.25/fs/buffer.c linux-2.4.25-leo/fs/buffer.c
--- linux-2.4.25/fs/buffer.c	2004-02-20 14:11:44.000000000 +0000
+++ linux-2.4.25-leo/fs/buffer.c	2004-02-20 18:22:22.000000000 +0000
@@ -419,6 +419,34 @@
 	fsync_dev(dev);
 }
 
+int fsync_dev_lockfs(kdev_t dev)
+{
+	/* you are not allowed to try locking all the filesystems
+	** on the system, your chances of getting through without
+	** total deadlock are slim to none.
+	*/
+	if (!dev)
+		return fsync_dev(dev) ;
+
+	sync_buffers(dev, 0);
+
+	lock_kernel();
+	/* note, the FS might need to start transactions to 
+	** sync the inodes, or the quota, no locking until
+	** after these are done
+	*/
+	sync_inodes(dev);
+	DQUOT_SYNC_DEV(dev);
+	/* if inodes or quotas could be dirtied during the
+	** sync_supers_lockfs call, the FS is responsible for getting
+	** them on disk, without deadlocking against the lock
+	*/
+	sync_supers_lockfs(dev) ;
+	unlock_kernel();
+
+	return sync_buffers(dev, 1) ;
+}
+
 asmlinkage long sys_sync(void)
 {
 	fsync_dev(0);
@@ -799,6 +827,7 @@
 	bh->b_list = BUF_CLEAN;
 	bh->b_end_io = handler;
 	bh->b_private = private;
+	bh->b_journal_head = NULL;
 }
 
 void end_buffer_io_async(struct buffer_head * bh, int uptodate)
@@ -1863,6 +1892,9 @@
 	int err;
 
 	err = -EFBIG;
+
+	gr_learn_resource(current, RLIMIT_FSIZE, (unsigned long) size, 1);
+
         limit = current->rlim[RLIMIT_FSIZE].rlim_cur;
 	if (limit != RLIM_INFINITY && size > (loff_t)limit) {
 		send_sig(SIGXFSZ, current, 0);
diff -urN --exclude-from=diff-exclude linux-2.4.25/fs/exec.c linux-2.4.25-leo/fs/exec.c
--- linux-2.4.25/fs/exec.c	2004-02-20 14:11:44.000000000 +0000
+++ linux-2.4.25-leo/fs/exec.c	2004-02-20 18:23:08.000000000 +0000
@@ -43,6 +43,9 @@
 #include <asm/uaccess.h>
 #include <asm/pgalloc.h>
 #include <asm/mmu_context.h>
+#include <linux/major.h>
+#include <linux/random.h>
+#include <linux/grsecurity.h>
 
 #ifdef CONFIG_KMOD
 #include <linux/kmod.h>
@@ -56,6 +59,11 @@
 static struct linux_binfmt *formats;
 static rwlock_t binfmt_lock = RW_LOCK_UNLOCKED;
 
+#ifdef CONFIG_GRKERNSEC_PAX_HOOK_ACL_FLAGS
+void (*pax_set_flags_func)(struct linux_binprm *bprm);
+EXPORT_SYMBOL(pax_set_flags_func);
+#endif
+
 int register_binfmt(struct linux_binfmt * fmt)
 {
 	struct linux_binfmt ** tmp = &formats;
@@ -346,6 +354,13 @@
 		mpnt->vm_start = PAGE_MASK & (unsigned long) bprm->p;
 		mpnt->vm_end = STACK_TOP;
 		mpnt->vm_flags = VM_STACK_FLAGS;
+
+#ifdef CONFIG_GRKERNSEC_PAX_PAGEEXEC
+		if (!(current->flags & PF_PAX_PAGEEXEC))
+			mpnt->vm_page_prot = protection_map[(VM_STACK_FLAGS | VM_EXEC) & 0x7];
+		else
+#endif
+		
 		mpnt->vm_page_prot = protection_map[VM_STACK_FLAGS & 0x7];
 		mpnt->vm_ops = NULL;
 		mpnt->vm_pgoff = 0;
@@ -455,8 +470,8 @@
 		active_mm = current->active_mm;
 		current->mm = mm;
 		current->active_mm = mm;
-		task_unlock(current);
 		activate_mm(active_mm, mm);
+		task_unlock(current);
 		mm_release();
 		if (old_mm) {
 			if (active_mm != old_mm) BUG();
@@ -615,6 +630,30 @@
 	}
 	current->comm[i] = '\0';
 
+#ifdef CONFIG_GRKERNSEC_PAX_PAGEEXEC
+	current->flags &= ~PF_PAX_PAGEEXEC;
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_EMUTRAMP
+	current->flags &= ~PF_PAX_EMUTRAMP;
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_MPROTECT
+	current->flags &= ~PF_PAX_MPROTECT;
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_ASLR
+	current->flags &= ~PF_PAX_RANDMMAP;
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_RANDEXEC
+	current->flags &= ~PF_PAX_RANDEXEC;
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_SEGMEXEC
+	current->flags &= ~PF_PAX_SEGMEXEC;
+#endif
+
 	flush_thread();
 
 	de_thread(current);
@@ -714,6 +753,9 @@
 			cap_set_full(bprm->cap_effective);
 	}
 
+	if (gr_handle_ptrace_exec(bprm->file->f_dentry, bprm->file->f_vfsmnt))
+		return -EACCES;
+
 	memset(bprm->buf,0,BINPRM_BUF_SIZE);
 	return kernel_read(bprm->file,0,bprm->buf,BINPRM_BUF_SIZE);
 }
@@ -779,6 +821,8 @@
         current->suid = current->euid = current->fsuid = bprm->e_uid;
         current->sgid = current->egid = current->fsgid = bprm->e_gid;
 
+	gr_handle_chroot_caps(current);
+
 	if(do_unlock)
 		unlock_kernel();
 	current->keep_capabilities = 0;
@@ -912,6 +956,11 @@
 	struct file *file;
 	int retval;
 	int i;
+#ifdef CONFIG_GRKERNSEC
+	struct file *old_exec_file;
+	struct acl_subject_label *old_acl;
+	struct rlimit old_rlim[RLIM_NLIMITS];
+#endif
 
 	file = open_exec(filename);
 
@@ -919,7 +968,26 @@
 	if (IS_ERR(file))
 		return retval;
 
+	gr_learn_resource(current, RLIMIT_NPROC, atomic_read(&current->user->processes), 1);
+
+	if (gr_handle_nproc()) {
+		allow_write_access(file);
+		fput(file);
+		return -EAGAIN;
+	}
+
+	if (!gr_acl_handle_execve(file->f_dentry, file->f_vfsmnt)) {
+		allow_write_access(file);
+		fput(file);
+		return -EACCES;
+	}
+
 	bprm.p = PAGE_SIZE*MAX_ARG_PAGES-sizeof(void *);
+
+#ifdef CONFIG_GRKERNSEC_PAX_RANDUSTACK
+	bprm.p -= (get_random_long() & ~(sizeof(void *)-1)) & ~PAGE_MASK;
+#endif
+
 	memset(bprm.page, 0, MAX_ARG_PAGES*sizeof(bprm.page[0])); 
 
 	bprm.file = file;
@@ -943,11 +1011,26 @@
 	if (retval < 0) 
 		goto out; 
 
+	if (!gr_tpe_allow(file)) {
+		retval = -EACCES;
+		goto out;
+	}
+
+	if(gr_check_crash_exec(file)) {
+		retval = -EACCES;
+		goto out;
+	}
+
 	retval = copy_strings_kernel(1, &bprm.filename, &bprm);
 	if (retval < 0) 
 		goto out; 
 
 	bprm.exec = bprm.p;
+
+	gr_log_chroot_exec(file->f_dentry, file->f_vfsmnt);
+
+	gr_handle_exec_args(&bprm, argv);
+
 	retval = copy_strings(bprm.envc, envp, &bprm);
 	if (retval < 0) 
 		goto out; 
@@ -956,11 +1039,32 @@
 	if (retval < 0) 
 		goto out; 
 
+#ifdef CONFIG_GRKERNSEC
+	old_acl = current->acl;
+	memcpy(old_rlim, current->rlim, sizeof(old_rlim));
+	old_exec_file = current->exec_file;
+	get_file(file);
+	current->exec_file = file;
+#endif
+
+	gr_set_proc_label(file->f_dentry, file->f_vfsmnt);
+
 	retval = search_binary_handler(&bprm,regs);
-	if (retval >= 0)
+	if (retval >= 0) {
+#ifdef CONFIG_GRKERNSEC
+		if (old_exec_file)
+			fput(old_exec_file);
+#endif
 		/* execve success */
 		return retval;
+	}
 
+#ifdef CONFIG_GRKERNSEC
+	current->acl = old_acl;
+	memcpy(current->rlim, old_rlim, sizeof(old_rlim));
+	fput(current->exec_file);
+	current->exec_file = old_exec_file;
+#endif
 out:
 	/* Something went wrong, return the inode and free the argument pages*/
 	allow_write_access(bprm.file);
@@ -1102,6 +1206,110 @@
 	*out_ptr = 0;
 }
 
+int pax_check_flags(unsigned long * flags)
+{
+	int retval = 0;
+
+#if !defined(__i386__) || !defined(CONFIG_GRKERNSEC_PAX_SEGMEXEC)
+	if (*flags & PF_PAX_SEGMEXEC)
+	{
+		*flags &= ~PF_PAX_SEGMEXEC;
+		retval = -EINVAL;
+	}
+#endif
+
+	if ((*flags & PF_PAX_PAGEEXEC)
+
+#ifdef CONFIG_GRKERNSEC_PAX_PAGEXEC
+	    &&  (*flags & PF_PAX_SEGMEXEC)
+#endif
+
+	   )
+	{
+		*flags &= ~PF_PAX_PAGEEXEC;
+		retval = -EINVAL;
+	}
+
+	if ((*flags & PF_PAX_MPROTECT)
+
+#ifdef CONFIG_GRKERNSEC_PAX_MPROTECT
+	    && !(*flags & (PF_PAX_PAGEEXEC | PF_PAX_SEGMEXEC))
+#endif
+
+	   )
+	{
+		*flags &= ~PF_PAX_MPROTECT;
+		retval = -EINVAL;
+	}
+
+	if ((*flags & PF_PAX_EMUTRAMP)
+
+#ifdef CONFIG_GRKERNSEC_PAX_EMUTRAMP
+	    && !(*flags & (PF_PAX_PAGEEXEC | PF_PAX_SEGMEXEC))
+#endif
+
+	   )
+	{
+		*flags &= ~PF_PAX_EMUTRAMP;
+		retval = -EINVAL;
+	}
+
+	if ((*flags & PF_PAX_RANDEXEC)
+
+#ifdef CONFIG_GRKERNSEC_PAX_RANDEXEC
+	    && !(*flags & (PF_PAX_PAGEEXEC | PF_PAX_SEGMEXEC))
+#endif
+
+	   )
+	{
+		*flags &= ~PF_PAX_RANDEXEC;
+		retval = -EINVAL;
+	}
+
+	return retval;
+}
+
+EXPORT_SYMBOL(pax_check_flags);
+
+#if defined(CONFIG_GRKERNSEC_PAX_PAGEEXEC) || defined(CONFIG_GRKERNSEC_PAX_SEGMEXEC)
+void pax_report_fault(struct pt_regs *regs, void *pc, void *sp)
+{
+	struct task_struct *tsk = current;
+	struct mm_struct *mm = current->mm;
+	char* buffer = (char*)__get_free_page(GFP_ATOMIC);
+	char* path=NULL;
+
+	if (buffer) {
+		struct vm_area_struct* vma;
+
+		down_read(&mm->mmap_sem);
+		vma = mm->mmap;
+		while (vma) {
+			if ((vma->vm_flags & VM_EXECUTABLE) && vma->vm_file) {
+				break;
+			}
+			vma = vma->vm_next;
+		}
+		if (vma)
+			path = d_path(vma->vm_file->f_dentry, vma->vm_file->f_vfsmnt, buffer, PAGE_SIZE);
+		up_read(&mm->mmap_sem);
+		if (IS_ERR(path))
+			path = strcpy(buffer, "<path too long>");
+	}
+	if (tsk->curr_ip)
+		printk(KERN_ERR "PAX: From %u.%u.%u.%u: terminating task: %s(%s):%d, uid/euid: %u/%u, "
+				"PC: %p, SP: %p\n", NIPQUAD(tsk->curr_ip), path, tsk->comm, tsk->pid,
+				tsk->uid, tsk->euid, pc, sp);
+	else
+		printk(KERN_ERR "PAX: terminating task: %s(%s):%d, uid/euid: %u/%u, "
+				"PC: %p, SP: %p\n", path, tsk->comm, tsk->pid,
+				tsk->uid, tsk->euid, pc, sp);
+	if (buffer) free_page((unsigned long)buffer);
+	pax_report_insns(pc);
+	do_coredump(SIGKILL, regs);
+}
+#endif
+
 int do_coredump(long signr, struct pt_regs * regs)
 {
 	struct linux_binfmt * binfmt;
@@ -1122,6 +1330,7 @@
 		current->fsuid = 0;
 	}
 	current->mm->dumpable = 0;
+	gr_learn_resource(current, RLIMIT_CORE, binfmt->min_coredump, 1);
 	if (current->rlim[RLIMIT_CORE].rlim_cur < binfmt->min_coredump)
 		goto fail;
 
@@ -1141,7 +1350,7 @@
 		goto close_fail;
 	if (!file->f_op->write)
 		goto close_fail;
-	if (do_truncate(file->f_dentry, 0) != 0)
+	if (do_truncate(file->f_dentry, 0, file->f_vfsmnt) != 0)
 		goto close_fail;
 
 	retval = binfmt->core_dump(signr, regs, file);
diff -urN --exclude-from=diff-exclude linux-2.4.25/fs/fat/cache.c linux-2.4.25-leo/fs/fat/cache.c
--- linux-2.4.25/fs/fat/cache.c	2001-10-12 21:48:42.000000000 +0100
+++ linux-2.4.25-leo/fs/fat/cache.c	2004-02-20 18:23:08.000000000 +0000
@@ -14,6 +14,7 @@
 #include <linux/string.h>
 #include <linux/stat.h>
 #include <linux/fat_cvf.h>
+#include <linux/sched.h>
 
 #if 0
 #  define PRINTK(x) printk x
diff -urN --exclude-from=diff-exclude linux-2.4.25/fs/fat/inode.c linux-2.4.25-leo/fs/fat/inode.c
--- linux-2.4.25/fs/fat/inode.c	2004-02-20 14:11:44.000000000 +0000
+++ linux-2.4.25-leo/fs/fat/inode.c	2004-02-20 18:22:17.000000000 +0000
@@ -8,6 +8,12 @@
  *  Fixes:
  *
  *  	Max Cohan: Fixed invalid FSINFO offset when info_sector is 0
+ *
+ *  Added:
+ *
+ *      Paul Evans: Added fmode/dmode options for separate file/dir permissions
+ *                  See also include/linux/msdos_fs_sb.h
+ *
  */
 
 #include <linux/module.h>
@@ -75,6 +81,9 @@
 static struct list_head fat_inode_hashtable[FAT_HASH_SIZE];
 spinlock_t fat_inode_lock = SPIN_LOCK_UNLOCKED;
 
+/* mask for inode permission bits - used by fmode/dmode */
+#define MODEMASK (~0777)
+
 void fat_hash_init(void)
 {
 	int i;
@@ -220,6 +229,8 @@
 	opts->fs_uid = current->uid;
 	opts->fs_gid = current->gid;
 	opts->fs_umask = current->fs->umask;
+	opts->fs_fmode = 0666; /* rw-rw-rw- */
+	opts->fs_dmode = 0777; /* rwxrwxrwx */
 	opts->quiet = opts->sys_immutable = opts->dotsOK = opts->showexec = 0;
 	opts->codepage = 0;
 	opts->nocase = 0;
@@ -299,6 +310,20 @@
 				if (*value) ret = 0;
 			}
 		}
+		else if (!strcmp(this_char,"fmode")) {
+			if (!value || !*value) ret = 0;
+			else {
+				opts->fs_fmode = simple_strtoul(value,&value,8);
+				if (*value) ret = 0;
+			}
+		}
+		else if (!strcmp(this_char,"dmode")) {
+			if (!value || !*value) ret = 0;
+			else {
+				opts->fs_dmode = simple_strtoul(value,&value,8);
+				if (*value) ret = 0;
+			}
+		}
 		else if (!strcmp(this_char,"debug")) {
 			if (value) ret = 0;
 			else *debug = 1;
@@ -385,7 +410,7 @@
 	inode->i_gid = sbi->options.fs_gid;
 	inode->i_version = ++event;
 	inode->i_generation = 0;
-	inode->i_mode = (S_IRWXUGO & ~sbi->options.fs_umask) | S_IFDIR;
+	inode->i_mode = (S_IRWXUGO & ~sbi->options.fs_umask & (sbi->options.fs_dmode | MODEMASK)) | S_IFDIR;
 	inode->i_op = sbi->dir_ops;
 	inode->i_fop = &fat_dir_operations;
 	if (sbi->fat_bits == 32) {
@@ -737,9 +762,10 @@
 	if (error || debug) {
 		/* The MSDOS_CAN_BMAP is obsolete, but left just to remember */
 		printk("[MS-DOS FS Rel. 12,FAT %d,check=%c,conv=%c,"
-		       "uid=%d,gid=%d,umask=%03o%s]\n",
+		       "uid=%d,gid=%d,umask=%03o,fmode=%03o,dmode=%03o%s]\n",
 		       sbi->fat_bits,opts.name_check,
 		       opts.conversion,opts.fs_uid,opts.fs_gid,opts.fs_umask,
+		       opts.fs_fmode,opts.fs_dmode,
 		       MSDOS_CAN_BMAP(sbi) ? ",bmap" : "");
 		printk("[me=0x%x,cs=%d,#f=%d,fs=%d,fl=%ld,ds=%ld,de=%d,data=%ld,"
 		       "se=%u,ts=%u,ls=%d,rc=%ld,fc=%u]\n",
@@ -905,7 +931,7 @@
 	if ((de->attr & ATTR_DIR) && !IS_FREE(de->name)) {
 		inode->i_generation &= ~1;
 		inode->i_mode = MSDOS_MKMODE(de->attr,S_IRWXUGO &
-		    ~sbi->options.fs_umask) | S_IFDIR;
+		    ~sbi->options.fs_umask & (sbi->options.fs_dmode | MODEMASK)) | S_IFDIR;
 		inode->i_op = sbi->dir_ops;
 		inode->i_fop = &fat_dir_operations;
 
@@ -938,7 +964,7 @@
 		    ((sbi->options.showexec &&
 		       !is_exec(de->ext))
 		    	? S_IRUGO|S_IWUGO : S_IRWXUGO)
-		    & ~sbi->options.fs_umask) | S_IFREG;
+		    & ~sbi->options.fs_umask & (sbi->options.fs_fmode | MODEMASK)) | S_IFREG;
 		MSDOS_I(inode)->i_start = CF_LE_W(de->start);
 		if (sbi->fat_bits == 32)
 			MSDOS_I(inode)->i_start |= (CF_LE_W(de->starthi) << 16);
@@ -1029,6 +1055,7 @@
 	struct super_block *sb = dentry->d_sb;
 	struct inode *inode = dentry->d_inode;
 	int error;
+	unsigned int mode;
 
 	/* FAT cannot truncate to a longer file */
 	if (attr->ia_valid & ATTR_SIZE) {
@@ -1055,12 +1082,17 @@
 	if (error)
 		return error;
 
-	if (S_ISDIR(inode->i_mode))
+	if (S_ISDIR(inode->i_mode)) {
 		inode->i_mode |= S_IXUGO;
+		mode = ~MSDOS_SB(sb)->options.fs_umask & (MSDOS_SB(sb)->options.fs_dmode | MODEMASK);
+		}
+	else
+		mode = ~MSDOS_SB(sb)->options.fs_umask & (MSDOS_SB(sb)->options.fs_fmode | MODEMASK);
+
+	inode->i_mode = ((inode->i_mode & S_IFMT) | 
+			 ((((inode->i_mode & S_IRWXU & mode) | S_IRUSR) >> 6)*S_IXUGO)
+			) & mode;
 
-	inode->i_mode = ((inode->i_mode & S_IFMT) | ((((inode->i_mode & S_IRWXU
-	    & ~MSDOS_SB(sb)->options.fs_umask) | S_IRUSR) >> 6)*S_IXUGO)) &
-	    ~MSDOS_SB(sb)->options.fs_umask;
 	return 0;
 }
 MODULE_LICENSE("GPL");
diff -urN --exclude-from=diff-exclude linux-2.4.25/fs/fcntl.c linux-2.4.25-leo/fs/fcntl.c
--- linux-2.4.25/fs/fcntl.c	2003-11-28 18:26:21.000000000 +0000
+++ linux-2.4.25-leo/fs/fcntl.c	2004-02-20 18:22:22.000000000 +0000
@@ -12,6 +12,7 @@
 #include <linux/slab.h>
 #include <linux/iobuf.h>
 #include <linux/ptrace.h>
+#include <linux/grsecurity.h>
 
 #include <asm/poll.h>
 #include <asm/siginfo.h>
@@ -68,6 +69,7 @@
 	write_lock(&files->file_lock);
 	
 	error = -EINVAL;
+	gr_learn_resource(current, RLIMIT_NOFILE, orig_start, 0);
 	if (orig_start >= current->rlim[RLIMIT_NOFILE].rlim_cur)
 		goto out;
 
@@ -87,6 +89,7 @@
 	}
 	
 	error = -EMFILE;
+	gr_learn_resource(current, RLIMIT_NOFILE, newfd, 0);
 	if (newfd >= current->rlim[RLIMIT_NOFILE].rlim_cur)
 		goto out;
 
@@ -149,6 +152,7 @@
 	if (newfd == oldfd)
 		goto out_unlock;
 	err = -EBADF;
+	gr_learn_resource(current, RLIMIT_NOFILE, newfd, 0);
 	if (newfd >= current->rlim[RLIMIT_NOFILE].rlim_cur)
 		goto out_unlock;
 	get_file(file);			/* We are now finished with oldfd */
@@ -450,6 +454,10 @@
 			match = -p->pgrp;
 		if (pid != match)
 			continue;
+		if (gr_check_protected_task(p))
+			continue;
+		if (gr_pid_is_chrooted(p))
+			continue;
 		send_sigio_to_task(p, fown, fd, band);
 	}
 out:
diff -urN --exclude-from=diff-exclude linux-2.4.25/fs/jbd/journal.c linux-2.4.25-leo/fs/jbd/journal.c
--- linux-2.4.25/fs/jbd/journal.c	2004-02-20 14:11:44.000000000 +0000
+++ linux-2.4.25-leo/fs/jbd/journal.c	2004-02-20 18:21:57.000000000 +0000
@@ -1803,9 +1803,9 @@
 
 		if (buffer_jbd(bh)) {
 			/* Someone did it for us! */
-			J_ASSERT_BH(bh, bh->b_private != NULL);
+ 			J_ASSERT_BH(bh, bh->b_journal_head != NULL);
 			journal_free_journal_head(jh);
-			jh = bh->b_private;
+ 			jh = bh->b_journal_head;
 		} else {
 			/*
 			 * We actually don't need jh_splice_lock when
@@ -1813,7 +1813,7 @@
 			 */
 			spin_lock(&jh_splice_lock);
 			set_bit(BH_JBD, &bh->b_state);
-			bh->b_private = jh;
+			bh->b_journal_head = jh;
 			jh->b_bh = bh;
 			atomic_inc(&bh->b_count);
 			spin_unlock(&jh_splice_lock);
@@ -1822,7 +1822,7 @@
 	}
 	jh->b_jcount++;
 	spin_unlock(&journal_datalist_lock);
-	return bh->b_private;
+	return bh->b_journal_head;
 }
 
 /*
@@ -1855,7 +1855,7 @@
 			J_ASSERT_BH(bh, jh2bh(jh) == bh);
 			BUFFER_TRACE(bh, "remove journal_head");
 			spin_lock(&jh_splice_lock);
-			bh->b_private = NULL;
+			bh->b_journal_head = NULL;
 			jh->b_bh = NULL;	/* debug, really */
 			clear_bit(BH_JBD, &bh->b_state);
 			__brelse(bh);
diff -urN --exclude-from=diff-exclude linux-2.4.25/fs/Makefile linux-2.4.25-leo/fs/Makefile
--- linux-2.4.25/fs/Makefile	2004-02-20 14:11:44.000000000 +0000
+++ linux-2.4.25-leo/fs/Makefile	2004-02-20 18:22:22.000000000 +0000
@@ -7,7 +7,7 @@
 
 O_TARGET := fs.o
 
-export-objs :=	filesystems.o open.o dcache.o buffer.o dquot.o
+export-objs :=	filesystems.o open.o dcache.o buffer.o dquot.o exec.o
 mod-subdirs :=	nls
 
 obj-y :=	open.o read_write.o devices.o file_table.o buffer.o \
diff -urN --exclude-from=diff-exclude linux-2.4.25/fs/namei.c linux-2.4.25-leo/fs/namei.c
--- linux-2.4.25/fs/namei.c	2003-08-25 12:44:43.000000000 +0100
+++ linux-2.4.25-leo/fs/namei.c	2004-02-20 18:22:22.000000000 +0000
@@ -22,6 +22,7 @@
 #include <linux/dnotify.h>
 #include <linux/smp_lock.h>
 #include <linux/personality.h>
+#include <linux/grsecurity.h>
 
 #include <asm/namei.h>
 #include <asm/uaccess.h>
@@ -343,6 +344,13 @@
 		current->state = TASK_RUNNING;
 		schedule();
 	}
+
+	if (gr_handle_follow_link(dentry->d_parent->d_inode,
+				  dentry->d_inode, dentry, nd->mnt)) {
+		path_release(nd);
+		return -EACCES;
+	}
+
 	current->link_count++;
 	current->total_link_count++;
 	UPDATE_ATIME(dentry->d_inode);
@@ -643,6 +651,10 @@
 			}
 		}
 return_base:
+		if (!gr_acl_handle_hidden_file(nd->dentry, nd->mnt)) {
+			path_release(nd);
+			return -ENOENT;
+		}
 		return 0;
 out_dput:
 		dput(dentry);
@@ -1005,7 +1017,7 @@
 	struct dentry *dentry;
 	struct dentry *dir;
 	int count = 0;
-
+	
 	acc_mode = ACC_MODE(flag);
 
 	/*
@@ -1015,7 +1027,21 @@
 		error = path_lookup(pathname, lookup_flags(flag), nd);
 		if (error)
 			return error;
+
+		if (gr_acl_is_enabled() && nd->dentry->d_inode && 
+		    S_ISBLK(nd->dentry->d_inode->i_mode) &&
+	    	    !capable(CAP_SYS_RAWIO)) {
+			error = -EPERM;
+			goto exit;
+		}
+	
+		if (!gr_acl_handle_open(nd->dentry, nd->mnt, flag)) {
+			error = -EACCES;
+			goto exit;
+		}
+
 		dentry = nd->dentry;
+
 		goto ok;
 	}
 
@@ -1048,8 +1074,22 @@
 
 	/* Negative dentry, just create the file */
 	if (!dentry->d_inode) {
+		if (gr_handle_chroot_chmod(dentry, nd->mnt, mode)) {
+			error = -EACCES;
+			up(&dir->d_inode->i_sem);
+			goto exit_dput;
+		}
+		if (!gr_acl_handle_creat(dentry, nd->dentry, nd->mnt, flag)) {
+			error = -EACCES;
+			up(&dir->d_inode->i_sem);
+			goto exit_dput;
+		}
+
 		error = vfs_create(dir->d_inode, dentry,
 				   mode & ~current->fs->umask);
+		if (!error)
+			gr_handle_create(dentry, nd->mnt);
+
 		up(&dir->d_inode->i_sem);
 		dput(nd->dentry);
 		nd->dentry = dentry;
@@ -1058,12 +1098,35 @@
 		/* Don't check for write permission, don't truncate */
 		acc_mode = 0;
 		flag &= ~O_TRUNC;
+
 		goto ok;
 	}
 
 	/*
 	 * It already exists.
 	 */
+
+	if (gr_acl_is_enabled() && S_ISBLK(dentry->d_inode->i_mode) &&
+	    !capable(CAP_SYS_RAWIO)) {
+		error = -EPERM;
+		up(&dir->d_inode->i_sem);
+		goto exit_dput;
+	}
+
+	if (!gr_acl_handle_open(dentry, nd->mnt, flag)) {
+		error = -EACCES;
+		up(&dir->d_inode->i_sem);
+		goto exit_dput;
+	}
+
+	inode = dentry->d_inode;
+
+	if (gr_handle_fifo(dentry, nd->mnt, dir, flag, acc_mode)) {
+		up(&dir->d_inode->i_sem);
+		error = -EACCES;
+		goto exit_dput;
+	}
+
 	up(&dir->d_inode->i_sem);
 
 	error = -EEXIST;
@@ -1153,7 +1216,7 @@
 		if (!error) {
 			DQUOT_INIT(inode);
 			
-			error = do_truncate(dentry, 0);
+			error = do_truncate(dentry,0,nd->mnt);
 		}
 		put_write_access(inode);
 		if (error)
@@ -1184,6 +1247,13 @@
 	 * stored in nd->last.name and we will have to putname() it when we
 	 * are done. Procfs-like symlinks just set LAST_BIND.
 	 */
+
+	if (gr_handle_follow_link(dentry->d_parent->d_inode, dentry->d_inode,
+				  dentry, nd->mnt)) {
+		error = -EACCES;
+		goto exit_dput;
+	}
+
 	UPDATE_ATIME(dentry->d_inode);
 	error = dentry->d_inode->i_op->follow_link(dentry, nd);
 	dput(dentry);
@@ -1282,6 +1352,19 @@
 
 	mode &= ~current->fs->umask;
 	if (!IS_ERR(dentry)) {
+		if (gr_handle_chroot_mknod(dentry, nd.mnt, mode) ||
+		    gr_handle_chroot_chmod(dentry, nd.mnt, mode)) {
+			error = -EPERM;
+			dput(dentry);
+			goto out_dput;
+		}
+
+		if (!gr_acl_handle_mknod(dentry, nd.dentry, nd.mnt)) {
+			error = -EACCES;
+			dput(dentry);
+			goto out_dput;
+		}
+	
 		switch (mode & S_IFMT) {
 		case 0: case S_IFREG:
 			error = vfs_create(nd.dentry->d_inode,dentry,mode);
@@ -1295,8 +1378,13 @@
 		default:
 			error = -EINVAL;
 		}
+
+		if(!error)
+			gr_handle_create(dentry, nd.mnt);
+
 		dput(dentry);
 	}
+out_dput:
 	up(&nd.dentry->d_inode->i_sem);
 	path_release(&nd);
 out:
@@ -1348,8 +1436,17 @@
 		dentry = lookup_create(&nd, 1);
 		error = PTR_ERR(dentry);
 		if (!IS_ERR(dentry)) {
-			error = vfs_mkdir(nd.dentry->d_inode, dentry,
+			error = 0;
+
+			if (!gr_acl_handle_mkdir(dentry, nd.dentry, nd.mnt))
+				error = -EACCES;
+
+			if(!error)
+				error = vfs_mkdir(nd.dentry->d_inode, dentry,
 					  mode & ~current->fs->umask);
+			if(!error)
+				gr_handle_create(dentry, nd.mnt);
+			
 			dput(dentry);
 		}
 		up(&nd.dentry->d_inode->i_sem);
@@ -1433,6 +1530,8 @@
 	char * name;
 	struct dentry *dentry;
 	struct nameidata nd;
+	ino_t saved_ino = 0;
+	kdev_t saved_dev = 0;
 
 	name = getname(pathname);
 	if(IS_ERR(name))
@@ -1457,7 +1556,22 @@
 	dentry = lookup_hash(&nd.last, nd.dentry);
 	error = PTR_ERR(dentry);
 	if (!IS_ERR(dentry)) {
-		error = vfs_rmdir(nd.dentry->d_inode, dentry);
+		error = 0;
+		if (dentry->d_inode) {
+			if (dentry->d_inode->i_nlink <= 1) {
+				saved_ino = dentry->d_inode->i_ino;
+				saved_dev = dentry->d_inode->i_dev;
+			}
+
+			if (!gr_acl_handle_rmdir(dentry, nd.mnt))
+				error = -EACCES;
+		}
+
+		if (!error)
+			error = vfs_rmdir(nd.dentry->d_inode, dentry);
+		if (!error && (saved_dev || saved_ino))
+			gr_handle_delete(saved_ino,saved_dev);
+
 		dput(dentry);
 	}
 	up(&nd.dentry->d_inode->i_sem);
@@ -1501,6 +1615,8 @@
 	char * name;
 	struct dentry *dentry;
 	struct nameidata nd;
+	ino_t saved_ino = 0;
+	kdev_t saved_dev = 0;
 
 	name = getname(pathname);
 	if(IS_ERR(name))
@@ -1519,7 +1635,21 @@
 		/* Why not before? Because we want correct error value */
 		if (nd.last.name[nd.last.len])
 			goto slashes;
-		error = vfs_unlink(nd.dentry->d_inode, dentry);
+		error = 0;
+		if (dentry->d_inode) {
+			if (dentry->d_inode->i_nlink <= 1) {
+				saved_ino = dentry->d_inode->i_ino;
+				saved_dev = dentry->d_inode->i_dev;
+			}
+
+			if (!gr_acl_handle_unlink(dentry, nd.mnt))
+				error = -EACCES;
+		}
+
+		if (!error)
+			error = vfs_unlink(nd.dentry->d_inode, dentry);
+		if (!error && (saved_ino || saved_dev))
+			gr_handle_delete(saved_ino,saved_dev);
 	exit2:
 		dput(dentry);
 	}
@@ -1583,7 +1713,15 @@
 		dentry = lookup_create(&nd, 0);
 		error = PTR_ERR(dentry);
 		if (!IS_ERR(dentry)) {
-			error = vfs_symlink(nd.dentry->d_inode, dentry, from);
+			error = 0;
+
+			if (!gr_acl_handle_symlink(dentry, nd.dentry, nd.mnt, from))
+				error = -EACCES;
+
+			if(!error)	
+				error = vfs_symlink(nd.dentry->d_inode, dentry, from);
+			if (!error)
+				gr_handle_create(dentry, nd.mnt);
 			dput(dentry);
 		}
 		up(&nd.dentry->d_inode->i_sem);
@@ -1667,7 +1805,27 @@
 		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);
+			error = 0;
+
+			if (gr_handle_hardlink(old_nd.dentry, old_nd.mnt,
+					       old_nd.dentry->d_inode,
+					       old_nd.dentry->d_inode->i_mode, to)) {
+				error = -EPERM;
+				goto out_error;
+			}
+
+			if (!gr_acl_handle_link(new_dentry, nd.dentry, nd.mnt,
+						 old_nd.dentry, old_nd.mnt, to)) {
+				error = -EACCES;
+				goto out_error;
+			}
+
+			error = vfs_link(old_nd.dentry, 
+					nd.dentry->d_inode, new_dentry);
+
+			if (!error)
+				gr_handle_create(new_dentry, nd.mnt);
+out_error:
 			dput(new_dentry);
 		}
 		up(&nd.dentry->d_inode->i_sem);
@@ -1898,10 +2056,15 @@
 	if (IS_ERR(new_dentry))
 		goto exit4;
 
-	lock_kernel();
-	error = vfs_rename(old_dir->d_inode, old_dentry,
+	error = gr_acl_handle_rename(new_dentry, newnd.dentry, newnd.mnt,
+				     old_dentry, old_dir->d_inode, oldnd.mnt, newname);
+
+	if (error == 1) {
+		lock_kernel();
+		error = vfs_rename(old_dir->d_inode, old_dentry,
 				   new_dir->d_inode, new_dentry);
-	unlock_kernel();
+		unlock_kernel();
+	}
 
 	dput(new_dentry);
 exit4:
diff -urN --exclude-from=diff-exclude linux-2.4.25/fs/namespace.c linux-2.4.25-leo/fs/namespace.c
--- linux-2.4.25/fs/namespace.c	2004-02-20 14:11:44.000000000 +0000
+++ linux-2.4.25-leo/fs/namespace.c	2004-02-20 18:22:22.000000000 +0000
@@ -15,6 +15,8 @@
 #include <linux/quotaops.h>
 #include <linux/acct.h>
 #include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/grsecurity.h>
 
 #include <asm/uaccess.h>
 
@@ -325,6 +327,8 @@
 			lock_kernel();
 			retval = do_remount_sb(sb, MS_RDONLY, 0);
 			unlock_kernel();
+
+			gr_log_remount(mnt->mnt_devname, retval);
 		}
 		up_write(&sb->s_umount);
 		return retval;
@@ -350,6 +354,9 @@
 	}
 	spin_unlock(&dcache_lock);
 	up_write(&current->namespace->sem);
+
+	gr_log_unmount(mnt->mnt_devname, retval);
+
 	return retval;
 }
 
@@ -732,6 +739,12 @@
 	if (retval)
 		return retval;
 
+	if (gr_handle_chroot_mount(nd.dentry, nd.mnt, dev_name)) {
+		retval = -EPERM;
+		path_release(&nd);
+		return retval;
+	}
+
 	if (flags & MS_REMOUNT)
 		retval = do_remount(&nd, flags & ~MS_REMOUNT, mnt_flags,
 				    data_page);
@@ -743,6 +756,9 @@
 		retval = do_add_mount(&nd, type_page, flags, mnt_flags,
 				      dev_name, data_page);
 	path_release(&nd);
+
+	gr_log_mount(dev_name, dir_name, retval);
+
 	return retval;
 }
 
@@ -912,6 +928,9 @@
 	if (!capable(CAP_SYS_ADMIN))
 		return -EPERM;
 
+	if (gr_handle_chroot_pivot())
+		return -EPERM;
+
 	lock_kernel();
 
 	error = __user_walk(new_root, LOOKUP_POSITIVE|LOOKUP_FOLLOW|LOOKUP_DIRECTORY, &new_nd);
diff -urN --exclude-from=diff-exclude linux-2.4.25/fs/nfsd/nfssvc.c linux-2.4.25-leo/fs/nfsd/nfssvc.c
--- linux-2.4.25/fs/nfsd/nfssvc.c	2002-11-28 23:53:15.000000000 +0000
+++ linux-2.4.25-leo/fs/nfsd/nfssvc.c	2004-02-20 18:23:08.000000000 +0000
@@ -250,6 +250,7 @@
 	svc_exit_thread(rqstp);
 
 	/* Release module */
+	unlock_kernel();
 	MOD_DEC_USE_COUNT;
 }
 
diff -urN --exclude-from=diff-exclude linux-2.4.25/fs/nls/nls_base.c linux-2.4.25-leo/fs/nls/nls_base.c
--- linux-2.4.25/fs/nls/nls_base.c	2002-08-03 01:39:45.000000000 +0100
+++ linux-2.4.25-leo/fs/nls/nls_base.c	2004-02-20 18:23:08.000000000 +0000
@@ -18,6 +18,7 @@
 #ifdef CONFIG_KMOD
 #include <linux/kmod.h>
 #endif
+#include <linux/sched.h>
 #include <linux/spinlock.h>
 
 static struct nls_table *tables;
diff -urN --exclude-from=diff-exclude linux-2.4.25/fs/open.c linux-2.4.25-leo/fs/open.c
--- linux-2.4.25/fs/open.c	2004-02-20 14:11:44.000000000 +0000
+++ linux-2.4.25-leo/fs/open.c	2004-02-20 18:22:22.000000000 +0000
@@ -15,6 +15,7 @@
 #include <linux/slab.h>
 #include <linux/tty.h>
 #include <linux/iobuf.h>
+#include <linux/grsecurity.h>
 
 #include <asm/uaccess.h>
 
@@ -95,7 +96,7 @@
 	write_unlock(&files->file_lock);
 }
 
-int do_truncate(struct dentry *dentry, loff_t length)
+int do_truncate(struct dentry *dentry, loff_t length, struct vfsmount *mnt)
 {
 	struct inode *inode = dentry->d_inode;
 	int error;
@@ -105,6 +106,9 @@
 	if (length < 0)
 		return -EINVAL;
 
+	if (!gr_acl_handle_truncate(dentry, mnt))
+		return -EACCES;
+
 	down_write(&inode->i_alloc_sem);
 	down(&inode->i_sem);
 	newattrs.ia_size = length;
@@ -165,7 +169,7 @@
 	error = locks_verify_truncate(inode, NULL, length);
 	if (!error) {
 		DQUOT_INIT(inode);
-		error = do_truncate(nd.dentry, length);
+		error = do_truncate(nd.dentry, length, nd.mnt);
 	}
 	put_write_access(inode);
 
@@ -217,7 +221,7 @@
 
 	error = locks_verify_truncate(inode, file, length);
 	if (!error)
-		error = do_truncate(dentry, length);
+		error = do_truncate(dentry, length, file->f_vfsmnt);
 out_putf:
 	fput(file);
 out:
@@ -292,6 +296,12 @@
 		    (error = permission(inode,MAY_WRITE)) != 0)
 			goto dput_and_out;
 	}
+
+	if (!gr_acl_handle_utime(nd.dentry, nd.mnt)) {
+		error = -EACCES;
+		goto dput_and_out;
+	}
+
 	error = notify_change(nd.dentry, &newattrs);
 dput_and_out:
 	path_release(&nd);
@@ -344,6 +354,12 @@
 		    (error = permission(inode,MAY_WRITE)) != 0)
 			goto dput_and_out;
 	}
+
+	if (!gr_acl_handle_utime(nd.dentry, nd.mnt)) {
+		error = -EACCES;
+		goto dput_and_out;
+	}
+
 	error = notify_change(nd.dentry, &newattrs);
 dput_and_out:
 	path_release(&nd);
@@ -386,6 +402,10 @@
 		if(!res && (mode & S_IWOTH) && IS_RDONLY(nd.dentry->d_inode)
 		   && !special_file(nd.dentry->d_inode->i_mode))
 			res = -EROFS;
+		
+		if (!res && !gr_acl_handle_access(nd.dentry, nd.mnt, mode))
+			res =  -EACCES;
+
 		path_release(&nd);
 	}
 
@@ -409,6 +429,8 @@
 	if (error)
 		goto dput_and_out;
 
+	gr_log_chdir(nd.dentry, nd.mnt);
+
 	set_fs_pwd(current->fs, nd.mnt, nd.dentry);
 
 dput_and_out:
@@ -439,6 +461,13 @@
 		goto out_putf;
 
 	error = permission(inode, MAY_EXEC);
+
+	if (!error && !gr_chroot_fchdir(dentry, mnt))
+		error = -EPERM;
+
+	if (!error)
+		gr_log_chdir(dentry, mnt);
+
 	if (!error)
 		set_fs_pwd(current->fs, mnt, dentry);
 out_putf:
@@ -465,8 +494,16 @@
 	if (!capable(CAP_SYS_CHROOT))
 		goto dput_and_out;
 
+	if (gr_handle_chroot_chroot(nd.dentry, nd.mnt))
+		goto dput_and_out;
+
 	set_fs_root(current->fs, nd.mnt, nd.dentry);
 	set_fs_altroot();
+
+	gr_handle_chroot_caps(current);
+
+	gr_handle_chroot_chdir(nd.dentry, nd.mnt);
+
 	error = 0;
 dput_and_out:
 	path_release(&nd);
@@ -495,8 +532,20 @@
 	err = -EPERM;
 	if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
 		goto out_putf;
+
+	if (!gr_acl_handle_fchmod(dentry, file->f_vfsmnt)) {
+		err = -EACCES;
+		goto out_putf;
+	}
+
 	if (mode == (mode_t) -1)
 		mode = inode->i_mode;
+
+	if (gr_handle_chroot_chmod(dentry, file->f_vfsmnt, mode)) {
+		err = -EPERM;
+		goto out_putf;
+	}	    
+
 	newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
 	newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
 	err = notify_change(dentry, &newattrs);
@@ -527,8 +576,19 @@
 	if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
 		goto dput_and_out;
 
+	if (!gr_acl_handle_chmod(nd.dentry, nd.mnt)) {
+		error = -EACCES;
+		goto dput_and_out;
+	}
+
 	if (mode == (mode_t) -1)
 		mode = inode->i_mode;
+
+	if (gr_handle_chroot_chmod(nd.dentry, nd.mnt, mode)) {
+		error = -EACCES;
+		goto dput_and_out;
+	}
+
 	newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
 	newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
 	error = notify_change(nd.dentry, &newattrs);
@@ -539,7 +599,7 @@
 	return error;
 }
 
-static int chown_common(struct dentry * dentry, uid_t user, gid_t group)
+static int chown_common(struct dentry * dentry, uid_t user, gid_t group, struct vfsmount *mnt)
 {
 	struct inode * inode;
 	int error;
@@ -556,6 +616,12 @@
 	error = -EPERM;
 	if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
 		goto out;
+
+	if (!gr_acl_handle_chown(dentry, mnt)) {
+		error = -EACCES;
+		goto out;
+	}
+
 	if (user == (uid_t) -1)
 		user = inode->i_uid;
 	if (group == (gid_t) -1)
@@ -606,7 +672,7 @@
 
 	error = user_path_walk(filename, &nd);
 	if (!error) {
-		error = chown_common(nd.dentry, user, group);
+		error = chown_common(nd.dentry, user, group, nd.mnt);
 		path_release(&nd);
 	}
 	return error;
@@ -619,7 +685,7 @@
 
 	error = user_path_walk_link(filename, &nd);
 	if (!error) {
-		error = chown_common(nd.dentry, user, group);
+		error = chown_common(nd.dentry, user, group, nd.mnt);
 		path_release(&nd);
 	}
 	return error;
@@ -633,7 +699,8 @@
 
 	file = fget(fd);
 	if (file) {
-		error = chown_common(file->f_dentry, user, group);
+		error = chown_common(file->f_dentry, user,
+				group, file->f_vfsmnt);
 		fput(file);
 	}
 	return error;
@@ -753,6 +820,7 @@
 	 * N.B. For clone tasks sharing a files structure, this test
 	 * will limit the total number of files that can be opened.
 	 */
+	gr_learn_resource(current, RLIMIT_NOFILE, fd, 0);
 	if (fd >= current->rlim[RLIMIT_NOFILE].rlim_cur)
 		goto out;
 
diff -urN --exclude-from=diff-exclude linux-2.4.25/fs/proc/array.c linux-2.4.25-leo/fs/proc/array.c
--- linux-2.4.25/fs/proc/array.c	2003-11-28 18:26:21.000000000 +0000
+++ linux-2.4.25-leo/fs/proc/array.c	2004-02-20 18:22:22.000000000 +0000
@@ -298,6 +298,12 @@
 	return buffer - orig;
 }
 
+#ifdef CONFIG_GRKERNSEC_PROC_MEMMAP
+#define PAX_RAND_FLAGS (task->flags & PF_PAX_RANDMMAP || \
+		        task->flags & PF_PAX_SEGMEXEC || \
+			task->flags & PF_PAX_RANDEXEC)
+#endif
+
 int proc_pid_stat(struct task_struct *task, char * buffer)
 {
 	unsigned long vsize, eip, esp, wchan;
@@ -335,6 +341,19 @@
 
 	wchan = get_wchan(task);
 
+#ifdef CONFIG_GRKERNSEC_PROC_MEMMAP
+	if (PAX_RAND_FLAGS) {
+		eip = 0;
+		esp = 0;
+		wchan = 0;
+	}
+#endif
+#ifdef CONFIG_GRKERNSEC_HIDESYM
+	wchan = 0;
+	eip = 0;
+	esp = 0;
+#endif
+
 	collect_sigign_sigcatch(task, &sigign, &sigcatch);
 
 	/* scale priority and nice values from timeslices to -20..20 */
@@ -374,9 +393,15 @@
 		vsize,
 		mm ? mm->rss : 0, /* you might want to shift this left 3 */
 		task->rlim[RLIMIT_RSS].rlim_cur,
+#ifdef CONFIG_GRKERNSEC_PROC_MEMMAP
+		PAX_RAND_FLAGS ? 0 : (mm ? mm->start_code : 0),
+		PAX_RAND_FLAGS ? 0 : (mm ? mm->end_code : 0),
+		PAX_RAND_FLAGS ? 0 : (mm ? mm->start_stack : 0),
+#else
 		mm ? mm->start_code : 0,
 		mm ? mm->end_code : 0,
 		mm ? mm->start_stack : 0,
+#endif
 		esp,
 		eip,
 		/* The signal information here is obsolete.
@@ -514,6 +539,7 @@
 
 static int show_map(struct seq_file *m, void *v)
 {
+	struct task_struct *task = m->private;
 	struct vm_area_struct *map = v;
 	struct file *file = map->vm_file;
 	int flags = map->vm_flags;
@@ -528,8 +554,13 @@
 	}
 
 	seq_printf(m, "%08lx-%08lx %c%c%c%c %08lx %02x:%02x %lu %n",
+#ifdef CONFIG_GRKERNSEC_PROC_MEMMAP
+			PAX_RAND_FLAGS ? 0UL : map->vm_start,
+			PAX_RAND_FLAGS ? 0UL : map->vm_end,
+#else
 			map->vm_start,
 			map->vm_end,
+#endif
 			flags & VM_READ ? 'r' : '-',
 			flags & VM_WRITE ? 'w' : '-',
 			flags & VM_EXEC ? 'x' : '-',
diff -urN --exclude-from=diff-exclude linux-2.4.25/fs/proc/base.c linux-2.4.25-leo/fs/proc/base.c
--- linux-2.4.25/fs/proc/base.c	2003-11-28 18:26:21.000000000 +0000
+++ linux-2.4.25-leo/fs/proc/base.c	2004-02-20 18:22:22.000000000 +0000
@@ -25,6 +25,7 @@
 #include <linux/string.h>
 #include <linux/seq_file.h>
 #include <linux/namespace.h>
+#include <linux/grsecurity.h>
 
 /*
  * For hysterical raisins we keep the same inumbers as in the old procfs.
@@ -263,9 +264,22 @@
 
 static int proc_permission(struct inode *inode, int mask)
 {
+	int ret;
+	struct task_struct *task;
+
 	if (vfs_permission(inode, mask) != 0)
 		return -EACCES;
-	return proc_check_root(inode);
+	ret = proc_check_root(inode);
+
+	if (ret)
+		return ret;
+
+	task = inode->u.proc_i.task;
+
+	if (!task)
+		return 0;
+
+	return gr_acl_handle_procpidmem(task);
 }
 
 extern struct seq_operations proc_pid_maps_op;
@@ -769,10 +783,17 @@
 	get_task_struct(task);
 	inode->u.proc_i.task = task;
 	inode->i_uid = 0;
+#ifdef CONFIG_GRKERNSEC_PROC_USERGROUP
+	inode->i_gid = CONFIG_GRKERNSEC_PROC_GID;
+#else
 	inode->i_gid = 0;
+#endif
+
 	if (ino == PROC_PID_INO || task_dumpable(task)) {
 		inode->i_uid = task->euid;
+#ifndef CONFIG_GRKERNSEC_PROC_USERGROUP
 		inode->i_gid = task->egid;
+#endif
 	}
 
 out:
@@ -1078,13 +1099,35 @@
 	if (!task)
 		goto out;
 
+	if(gr_check_hidden_task(task)) {
+		free_task_struct(task);
+		goto out;
+	}
+	
+#ifdef CONFIG_GRKERNSEC_PROC
+	if (current->uid && (task->uid != current->uid)
+#ifdef CONFIG_GRKERNSEC_PROC_USERGROUP
+	    && !in_group_p(CONFIG_GRKERNSEC_PROC_GID)
+#endif
+	) {
+		free_task_struct(task);
+		goto out;
+	}	
+#endif
 	inode = proc_pid_make_inode(dir->i_sb, task, PROC_PID_INO);
 
 	free_task_struct(task);
 
 	if (!inode)
 		goto out;
+#ifdef CONFIG_GRKERNSEC_PROC_USER
+	inode->i_mode = S_IFDIR|S_IRUSR|S_IXUSR;
+#elif CONFIG_GRKERNSEC_PROC_USERGROUP
+	inode->i_mode = S_IFDIR|S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP;
+	inode->i_gid = CONFIG_GRKERNSEC_PROC_GID;
+#else
 	inode->i_mode = S_IFDIR|S_IRUGO|S_IXUGO;
+#endif
 	inode->i_op = &proc_base_inode_operations;
 	inode->i_fop = &proc_base_operations;
 	inode->i_nlink = 3;
@@ -1124,6 +1167,18 @@
 		int pid = p->pid;
 		if (!pid)
 			continue;
+		if(gr_pid_is_chrooted(p))
+			continue;
+		if(gr_check_hidden_task(p)) 
+			continue;
+#ifdef CONFIG_GRKERNSEC_PROC
+		if (current->uid && (p->uid != current->uid)
+#ifdef CONFIG_GRKERNSEC_PROC_USERGROUP
+	    		&& !in_group_p(CONFIG_GRKERNSEC_PROC_GID)
+#endif
+		)
+			continue;	
+#endif
 		if (--index >= 0)
 			continue;
 		pids[nr_pids] = pid;
diff -urN --exclude-from=diff-exclude linux-2.4.25/fs/proc/generic.c linux-2.4.25-leo/fs/proc/generic.c
--- linux-2.4.25/fs/proc/generic.c	2003-11-28 18:26:21.000000000 +0000
+++ linux-2.4.25-leo/fs/proc/generic.c	2004-02-20 18:22:22.000000000 +0000
@@ -504,6 +504,32 @@
 	return ent;
 }
 
+#ifdef CONFIG_GRKERNSEC_PROC
+struct proc_dir_entry *proc_priv_mkdir(const char *name, struct proc_dir_entry *parent)
+{
+	struct proc_dir_entry *ent;
+	mode_t mode = 0;
+
+#ifdef CONFIG_GRKERNSEC_PROC_USER
+	mode = S_IFDIR | S_IRUSR | S_IXUSR;
+#elif CONFIG_GRKERNSEC_PROC_USERGROUP
+	mode = S_IFDIR | S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP;
+#endif
+
+	ent = proc_create(&parent, name, mode, 2);
+	if (ent) {
+		ent->proc_fops = &proc_dir_operations;
+		ent->proc_iops = &proc_dir_inode_operations;
+
+		if (proc_register(parent, ent) < 0) {
+			kfree(ent);
+			ent = NULL;
+		}
+	}
+	return ent;
+}
+#endif
+
 struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode,
 					 struct proc_dir_entry *parent)
 {
diff -urN --exclude-from=diff-exclude linux-2.4.25/fs/proc/inode.c linux-2.4.25-leo/fs/proc/inode.c
--- linux-2.4.25/fs/proc/inode.c	2003-11-28 18:26:21.000000000 +0000
+++ linux-2.4.25-leo/fs/proc/inode.c	2004-02-20 18:22:22.000000000 +0000
@@ -152,7 +152,11 @@
 		if (de->mode) {
 			inode->i_mode = de->mode;
 			inode->i_uid = de->uid;
+#ifdef CONFIG_GRKERNSEC_PROC_USERGROUP
+			inode->i_gid = CONFIG_GRKERNSEC_PROC_GID;
+#else
 			inode->i_gid = de->gid;
+#endif			
 		}
 		if (de->size)
 			inode->i_size = de->size;
diff -urN --exclude-from=diff-exclude linux-2.4.25/fs/proc/proc_misc.c linux-2.4.25-leo/fs/proc/proc_misc.c
--- linux-2.4.25/fs/proc/proc_misc.c	2003-11-28 18:26:21.000000000 +0000
+++ linux-2.4.25-leo/fs/proc/proc_misc.c	2004-02-20 18:22:22.000000000 +0000
@@ -590,6 +590,7 @@
 void __init proc_misc_init(void)
 {
 	struct proc_dir_entry *entry;
+	int gr_mode = 0;
 	static struct {
 		char *name;
 		int (*read_proc)(char*,char**,off_t,int,int*,void*);
@@ -604,17 +605,21 @@
 #ifdef CONFIG_STRAM_PROC
 		{"stram",	stram_read_proc},
 #endif
-#ifdef CONFIG_MODULES
+#if defined(CONFIG_MODULES) && !defined(CONFIG_GRKERNSEC_PROC)
 		{"modules",	modules_read_proc},
 #endif
 		{"stat",	kstat_read_proc},
+#ifndef CONFIG_GRKERNSEC_PROC_ADD
 		{"devices",	devices_read_proc},
-#if !defined(CONFIG_ARCH_S390) && !defined(CONFIG_X86)
+#endif
+#if !defined(CONFIG_ARCH_S390) && !defined(CONFIG_X86) && !defined(CONFIG_GRKERNSEC_PROC_ADD)
 		{"interrupts",	interrupts_read_proc},
 #endif
 		{"filesystems",	filesystems_read_proc},
+#ifndef CONFIG_GRKERNSEC_PROC_ADD
 		{"dma",		dma_read_proc},
 		{"cmdline",	cmdline_read_proc},
+#endif
 #ifdef CONFIG_SGI_DS1286
 		{"rtc",		ds1286_read_proc},
 #endif
@@ -626,29 +631,60 @@
 	for (p = simple_ones; p->name; p++)
 		create_proc_read_entry(p->name, 0, NULL, p->read_proc, NULL);
 
+#ifdef CONFIG_GRKERNSEC_PROC_USER
+	gr_mode = S_IRUSR;
+#elif CONFIG_GRKERNSEC_PROC_USERGROUP
+	gr_mode = S_IRUSR | S_IRGRP;
+#endif
+#if defined(CONFIG_GRKERNSEC_PROC) && defined(CONFIG_MODULES)
+	create_proc_read_entry("modules", gr_mode, NULL, &modules_read_proc, NULL);
+#endif
+#ifdef CONFIG_GRKERNSEC_PROC_ADD
+	create_proc_read_entry("devices", gr_mode, NULL, &devices_read_proc, NULL);
+	create_proc_read_entry("dma", gr_mode, NULL, &dma_read_proc, NULL);
+	create_proc_read_entry("cmdline", gr_mode, NULL, &cmdline_read_proc, NULL);
+#if !defined(CONFIG_ARCH_S390) && !defined(CONFIG_X86)
+	create_proc_read_entry("interrupts", gr_mode, NULL, &interrupts_read_proc, NULL);
+#endif
+#endif
+
 	proc_symlink("mounts", NULL, "self/mounts");
 
 	/* And now for trickier ones */
 	entry = create_proc_entry("kmsg", S_IRUSR, &proc_root);
 	if (entry)
 		entry->proc_fops = &proc_kmsg_operations;
+#ifdef CONFIG_GRKERNSEC_PROC_ADD
+	create_seq_entry("cpuinfo", gr_mode, &proc_cpuinfo_operations);
+#else
 	create_seq_entry("cpuinfo", 0, &proc_cpuinfo_operations);
-#if defined(CONFIG_X86)
+#endif
+#if defined(CONFIG_X86) && !defined(CONFIG_GRKERNSEC_PROC_ADD)
 	create_seq_entry("interrupts", 0, &proc_interrupts_operations);
+#elif defined(CONFIG_X86)
+	create_seq_entry("interrupts", gr_mode, &proc_interrupts_operations);
 #endif
+#ifdef CONFIG_GRKERNSEC_PROC_ADD
+	create_seq_entry("ioports", gr_mode, &proc_ioports_operations);
+	create_seq_entry("iomem", gr_mode, &proc_iomem_operations);
+	create_seq_entry("slabinfo",gr_mode,&proc_slabinfo_operations);
+#else
 	create_seq_entry("ioports", 0, &proc_ioports_operations);
 	create_seq_entry("iomem", 0, &proc_iomem_operations);
-	create_seq_entry("partitions", 0, &proc_partitions_operations);
 	create_seq_entry("slabinfo",S_IWUSR|S_IRUGO,&proc_slabinfo_operations);
+#endif
+	create_seq_entry("partitions", 0, &proc_partitions_operations);
 #ifdef CONFIG_MODULES
-	create_seq_entry("ksyms", 0, &proc_ksyms_operations);
+	create_seq_entry("ksyms", gr_mode, &proc_ksyms_operations);
 #endif
+#ifndef CONFIG_GRKERNSEC_PROC_ADD
 	proc_root_kcore = create_proc_entry("kcore", S_IRUSR, NULL);
 	if (proc_root_kcore) {
 		proc_root_kcore->proc_fops = &proc_kcore_operations;
 		proc_root_kcore->size =
 				(size_t)high_memory - PAGE_OFFSET + PAGE_SIZE;
 	}
+#endif
 	if (prof_shift) {
 		entry = create_proc_entry("profile", S_IWUSR | S_IRUGO, NULL);
 		if (entry) {
diff -urN --exclude-from=diff-exclude linux-2.4.25/fs/proc/proc_tty.c linux-2.4.25-leo/fs/proc/proc_tty.c
--- linux-2.4.25/fs/proc/proc_tty.c	2000-04-21 23:17:57.000000000 +0100
+++ linux-2.4.25-leo/fs/proc/proc_tty.c	2004-02-20 18:22:22.000000000 +0000
@@ -174,7 +174,11 @@
 	if (!proc_mkdir("tty", 0))
 		return;
 	proc_tty_ldisc = proc_mkdir("tty/ldisc", 0);
+#ifdef CONFIG_GRKERNSEC_PROC
+	proc_tty_driver = proc_priv_mkdir("tty/driver", 0);
+#else
 	proc_tty_driver = proc_mkdir("tty/driver", 0);
+#endif
 
 	create_proc_read_entry("tty/ldiscs", 0, 0, tty_ldiscs_read_proc,NULL);
 	create_proc_read_entry("tty/drivers", 0, 0, tty_drivers_read_proc,NULL);
diff -urN --exclude-from=diff-exclude linux-2.4.25/fs/proc/root.c linux-2.4.25-leo/fs/proc/root.c
--- linux-2.4.25/fs/proc/root.c	2002-08-03 01:39:45.000000000 +0100
+++ linux-2.4.25-leo/fs/proc/root.c	2004-02-20 18:22:22.000000000 +0000
@@ -37,13 +37,21 @@
 		return;
 	}
 	proc_misc_init();
+#ifdef CONFIG_GRKERNSEC_PROC
+	proc_net = proc_priv_mkdir("net", 0);
+#else
 	proc_net = proc_mkdir("net", 0);
+#endif
 #ifdef CONFIG_SYSVIPC
 	proc_mkdir("sysvipc", 0);
 #endif
 #ifdef CONFIG_SYSCTL
+#ifdef CONFIG_GRKERNSEC_PROC
+	proc_sys_root = proc_priv_mkdir("sys", 0);
+#else
 	proc_sys_root = proc_mkdir("sys", 0);
 #endif
+#endif
 #if defined(CONFIG_BINFMT_MISC) || defined(CONFIG_BINFMT_MISC_MODULE)
 	proc_mkdir("sys/fs", 0);
 	proc_mkdir("sys/fs/binfmt_misc", 0);
@@ -67,7 +75,12 @@
 #ifdef CONFIG_PPC_RTAS
 	proc_rtas_init();
 #endif
+
+#ifdef CONFIG_GRKERNSEC_PROC_ADD
+	proc_bus = proc_priv_mkdir("bus", 0);
+#else
 	proc_bus = proc_mkdir("bus", 0);
+#endif
 }
 
 static struct dentry *proc_root_lookup(struct inode * dir, struct dentry * dentry)
diff -urN --exclude-from=diff-exclude linux-2.4.25/fs/readdir.c linux-2.4.25-leo/fs/readdir.c
--- linux-2.4.25/fs/readdir.c	2004-02-20 14:11:44.000000000 +0000
+++ linux-2.4.25-leo/fs/readdir.c	2004-02-20 18:22:22.000000000 +0000
@@ -10,6 +10,7 @@
 #include <linux/stat.h>
 #include <linux/file.h>
 #include <linux/smp_lock.h>
+#include <linux/grsecurity.h>
 
 #include <asm/uaccess.h>
 
@@ -182,6 +183,7 @@
 struct readdir_callback {
 	struct old_linux_dirent * dirent;
 	int count;
+	struct nameidata nd;
 };
 
 static int fillonedir(void * __buf, const char * name, int namlen, loff_t offset,
@@ -192,6 +194,10 @@
 
 	if (buf->count)
 		return -EINVAL;
+
+	if (!gr_acl_handle_filldir(buf->nd.dentry, buf->nd.mnt, ino))
+		return 0;
+	    
 	buf->count++;
 	dirent = buf->dirent;
 	put_user(ino, &dirent->d_ino);
@@ -216,6 +222,9 @@
 	buf.count = 0;
 	buf.dirent = dirent;
 
+	buf.nd.dentry = file->f_dentry;
+	buf.nd.mnt = file->f_vfsmnt;
+
 	error = vfs_readdir(file, fillonedir, &buf);
 	if (error >= 0)
 		error = buf.count;
@@ -243,6 +252,7 @@
 	struct linux_dirent * previous;
 	int count;
 	int error;
+	struct nameidata nd;
 };
 
 static int filldir(void * __buf, const char * name, int namlen, loff_t offset,
@@ -255,6 +265,10 @@
 	buf->error = -EINVAL;	/* only used if we fail.. */
 	if (reclen > buf->count)
 		return -EINVAL;
+
+	if (!gr_acl_handle_filldir(buf->nd.dentry, buf->nd.mnt, ino))
+		return 0;
+
 	dirent = buf->previous;
 	if (dirent)
 		put_user(offset, &dirent->d_off);
@@ -287,6 +301,9 @@
 	buf.count = count;
 	buf.error = 0;
 
+	buf.nd.dentry = file->f_dentry;
+	buf.nd.mnt = file->f_vfsmnt;
+
 	error = vfs_readdir(file, filldir, &buf);
 	if (error < 0)
 		goto out_putf;
@@ -321,6 +338,7 @@
 	struct linux_dirent64 * previous;
 	int count;
 	int error;
+	struct nameidata nd;
 };
 
 static int filldir64(void * __buf, const char * name, int namlen, loff_t offset,
@@ -333,6 +351,10 @@
 	buf->error = -EINVAL;	/* only used if we fail.. */
 	if (reclen > buf->count)
 		return -EINVAL;
+
+	if (!gr_acl_handle_filldir(buf->nd.dentry, buf->nd.mnt, ino))
+		return 0;
+	
 	dirent = buf->previous;
 	if (dirent) {
 		d.d_off = offset;
@@ -370,6 +392,9 @@
 	buf.count = count;
 	buf.error = 0;
 
+	buf.nd.mnt = file->f_vfsmnt;
+	buf.nd.dentry = file->f_dentry;
+
 	error = vfs_readdir(file, filldir64, &buf);
 	if (error < 0)
 		goto out_putf;
diff -urN --exclude-from=diff-exclude linux-2.4.25/fs/reiserfs/super.c linux-2.4.25-leo/fs/reiserfs/super.c
--- linux-2.4.25/fs/reiserfs/super.c	2003-08-25 12:44:43.000000000 +0100
+++ linux-2.4.25-leo/fs/reiserfs/super.c	2004-02-20 18:21:57.000000000 +0000
@@ -73,7 +73,7 @@
     reiserfs_prepare_for_journal(s, SB_BUFFER_WITH_SB(s), 1);
     journal_mark_dirty(&th, s, SB_BUFFER_WITH_SB (s));
     reiserfs_block_writes(&th) ;
-    journal_end(&th, s, 1) ;
+    journal_end_sync(&th, s, 1) ;
   }
   s->s_dirt = dirty;
   unlock_kernel() ;
diff -urN --exclude-from=diff-exclude linux-2.4.25/fs/super.c linux-2.4.25-leo/fs/super.c
--- linux-2.4.25/fs/super.c	2003-08-25 12:44:43.000000000 +0100
+++ linux-2.4.25-leo/fs/super.c	2004-02-20 18:21:57.000000000 +0000
@@ -39,6 +39,12 @@
 spinlock_t sb_lock = SPIN_LOCK_UNLOCKED;
 
 /*
+ * stub of a filesystem used to make sure an FS isn't mounted
+ * in the middle of a lockfs call
+ */
+static DECLARE_FSTYPE_DEV(lockfs_fs_type, "lockfs", NULL);
+
+/*
  * Handling of filesystem drivers list.
  * Rules:
  *	Inclusion to/removals from/scanning of list are protected by spinlock.
@@ -436,6 +442,25 @@
 	put_super(sb);
 }
 
+static void write_super_lockfs(struct super_block *sb)
+{
+	lock_super(sb);
+	if (sb->s_root && sb->s_op) {
+		if (sb->s_dirt && sb->s_op->write_super)
+			sb->s_op->write_super(sb);
+		if (sb->s_op->write_super_lockfs)
+			sb->s_op->write_super_lockfs(sb);
+	}
+	unlock_super(sb);
+
+	/* 
+	 * if no lockfs call is provided, use the sync_fs call instead.
+	 * this must be done without the super lock held
+	 */
+	if (!sb->s_op->write_super_lockfs && sb->s_op->sync_fs)
+		sb->s_op->sync_fs(sb);
+}
+
 static inline void write_super(struct super_block *sb)
 {
 	lock_super(sb);
@@ -483,6 +508,119 @@
 	spin_unlock(&sb_lock);
 }
 
+static struct super_block *find_super_for_lockfs(kdev_t dev)
+{
+	struct super_block *lockfs_sb = alloc_super();
+	struct super_block * s;
+
+	if (!dev)
+		return NULL;
+restart:
+	spin_lock(&sb_lock);
+	s = find_super(dev);
+	if (s) {
+		spin_unlock(&sb_lock);
+		down_read(&s->s_umount);
+		if (s->s_root) {
+			destroy_super(lockfs_sb);
+			return s;
+		}
+		drop_super(s);
+		goto restart;
+	}
+	/* if (s) we either return or goto, so we know s == NULL here.
+	 * At this point, there are no mounted filesystems on this device,
+	 * so we pretend to mount one.
+	 */
+	if (!lockfs_sb) {
+		spin_unlock(&sb_lock);
+		return NULL;
+	}
+	s = lockfs_sb;
+	s->s_dev = dev;
+	if (lockfs_fs_type.fs_supers.prev == NULL)
+		INIT_LIST_HEAD(&lockfs_fs_type.fs_supers);
+	insert_super(s, &lockfs_fs_type);
+	s->s_root = (struct dentry *)1;
+	/* alloc_super gives us a write lock on s_umount, this
+	 * way we know there are no concurrent lockfs holders for this dev.  
+	 * It allows us to remove the temp super from the list of supers 
+	 * immediately when unlockfs is called
+	 */
+	return s;
+}
+/*
+ * Note: don't check the dirty flag before waiting, we want the lock
+ * to happen every time this is called.  dev must be non-zero
+ */
+void sync_supers_lockfs(kdev_t dev)
+{
+	struct super_block *sb;
+	sb = find_super_for_lockfs(dev);
+	if (sb) {
+		write_super_lockfs(sb);
+		/* the drop_super is done by unlockfs */
+	}
+}
+
+static void drop_super_lockfs(struct super_block *s)
+{
+	if (s->s_type == &lockfs_fs_type) {
+		struct file_system_type *fs = s->s_type;
+
+		/* 
+		 * nobody else is allowed to grab_super() on our temp
+		 */
+		if (!deactivate_super(s))
+			BUG();
+
+		spin_lock(&sb_lock);
+		s->s_root = NULL;
+		list_del(&s->s_list);
+		list_del(&s->s_instances);
+		spin_unlock(&sb_lock);
+
+		up_write(&s->s_umount);
+		put_super(s);
+		put_filesystem(fs);
+	} else
+		drop_super(s);
+}
+
+void unlockfs(kdev_t dev)
+{
+	struct super_block *s;
+	if (!dev)
+		return;
+
+	spin_lock(&sb_lock);
+	s = find_super(dev);
+	if (s) {
+		/* 
+		 * find_super and the original lockfs call both incremented
+		 * the reference count.  drop one of them
+		 */
+		s->s_count--;
+		spin_unlock(&sb_lock);
+		if (s->s_root) {
+			if (s->s_op->unlockfs)
+				s->s_op->unlockfs(s);
+			drop_super_lockfs(s);
+			goto out;
+		} else {
+			printk("unlockfs: no s_root, dev %s\n", kdevname(dev));
+			BUG();
+		}
+	} else {
+		printk("unlockfs: no super found, dev %s\n", kdevname(dev));
+		BUG();
+	}
+
+	spin_unlock(&sb_lock);
+out:
+	return;
+}
+
 /**
  *	get_super	-	get the superblock of a device
  *	@dev: device to get the superblock for
diff -urN --exclude-from=diff-exclude linux-2.4.25/grsecurity/Config.in linux-2.4.25-leo/grsecurity/Config.in
--- linux-2.4.25/grsecurity/Config.in	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.25-leo/grsecurity/Config.in	2004-02-20 18:22:48.000000000 +0000
@@ -0,0 +1,371 @@
+define_bool CONFIG_CRYPTO y
+define_bool CONFIG_CRYPTO_SHA256 y
+choice 'Security level' \
+        "Low		CONFIG_GRKERNSEC_LOW \
+         Medium		CONFIG_GRKERNSEC_MID \
+         High		CONFIG_GRKERNSEC_HI \
+	  Customized	CONFIG_GRKERNSEC_CUSTOM" Customized
+if [ "$CONFIG_GRKERNSEC_LOW" = "y" ]; then
+define_bool CONFIG_GRKERNSEC_RANDSRC n
+define_bool CONFIG_GRKERNSEC_RANDRPC n
+define_bool CONFIG_GRKERNSEC_FORKFAIL n
+define_bool CONFIG_GRKERNSEC_TIME n
+define_bool CONFIG_GRKERNSEC_SIGNAL n
+define_bool CONFIG_GRKERNSEC_CHROOT_SHMAT n
+define_bool CONFIG_GRKERNSEC_CHROOT_MOUNT n
+define_bool CONFIG_GRKERNSEC_CHROOT_FCHDIR n
+define_bool CONFIG_GRKERNSEC_CHROOT_DOUBLE n
+define_bool CONFIG_GRKERNSEC_CHROOT_PIVOT n
+define_bool CONFIG_GRKERNSEC_CHROOT_MKNOD n
+define_bool CONFIG_GRKERNSEC_PROC n
+define_bool CONFIG_GRKERNSEC_PROC_MEMMAP n
+define_bool CONFIG_GRKERNSEC_HIDESYM n
+define_bool CONFIG_GRKERNSEC_CHROOT_CAPS n
+define_bool CONFIG_GRKERNSEC_CHROOT_SYSCTL n
+define_bool CONFIG_GRKERNSEC_PROC_USERGROUP n
+define_bool CONFIG_GRKERNSEC_KMEM n
+define_bool CONFIG_GRKERNSEC_PORT n
+define_bool CONFIG_GRKERNSEC_PROC_ADD n
+define_bool CONFIG_GRKERNSEC_CHROOT_CHMOD n
+define_bool CONFIG_GRKERNSEC_CHROOT_NICE n
+define_bool CONFIG_GRKERNSEC_CHROOT_FINDTASK n
+define_bool CONFIG_GRKERNSEC_PAX_RANDUSTACK n
+define_bool CONFIG_GRKERNSEC_PAX_ASLR n
+define_bool CONFIG_GRKERNSEC_PAX_RANDMMAP n
+define_bool CONFIG_GRKERNSEC_PAX_NOEXEC n
+define_bool CONFIG_GRKERNSEC_PAX_PAGEEXEC n
+define_bool CONFIG_GRKERNSEC_PAX_NOELFRELOCS n
+define_bool CONFIG_GRKERNSEC_PAX_ETEXECRELOCS n
+define_bool CONFIG_GRKERNSEC_PAX_MPROTECT n
+if [ "$CONFIG_X86" = "y" ]; then
+define_bool CONFIG_GRKERNSEC_PAX_RANDKSTACK n
+define_bool CONFIG_GRKERNSEC_PAX_KERNEXEC n
+define_bool CONFIG_GRKERNSEC_IO n
+define_bool CONFIG_GRKERNSEC_PAX_RANDEXEC n
+define_bool CONFIG_GRKERNSEC_PAX_SEGMEXEC n
+define_bool CONFIG_GRKERNSEC_PAX_EMUTRAMP n
+define_bool CONFIG_GRKERNSEC_PAX_EMUSIGRT n
+fi
+define_bool CONFIG_GRKERNSEC_AUDIT_MOUNT n
+define_bool CONFIG_GRKERNSEC_ACL_HIDEKERN n
+define_bool CONFIG_GRKERNSEC_RESLOG n
+define_int CONFIG_GRKERNSEC_ACL_MAXTRIES 3
+define_int CONFIG_GRKERNSEC_ACL_TIMEOUT 30
+
+define_int  CONFIG_GRKERNSEC_FLOODTIME 10
+define_int  CONFIG_GRKERNSEC_FLOODBURST 4
+define_bool CONFIG_GRKERNSEC_LINK y
+define_bool CONFIG_GRKERNSEC_FIFO y
+define_bool CONFIG_GRKERNSEC_RANDPID y
+define_bool CONFIG_GRKERNSEC_EXECVE y
+define_bool CONFIG_GRKERNSEC_RANDNET y
+define_bool CONFIG_GRKERNSEC_RANDISN n
+define_bool CONFIG_GRKERNSEC_DMESG y
+define_bool CONFIG_GRKERNSEC_RANDID y
+define_bool CONFIG_GRKERNSEC_CHROOT_CHDIR y
+fi
+if [ "$CONFIG_GRKERNSEC_MID" = "y" ]; then
+define_bool CONFIG_GRKERNSEC_KMEM n
+define_bool CONFIG_GRKERNSEC_HIDESYM n
+define_bool CONFIG_GRKERNSEC_PROC_ADD n
+define_bool CONFIG_GRKERNSEC_CHROOT_CHMOD n
+define_bool CONFIG_GRKERNSEC_CHROOT_NICE n
+define_bool CONFIG_GRKERNSEC_CHROOT_FINDTASK n
+define_bool CONFIG_GRKERNSEC_PAX_NOEXEC n
+define_bool CONFIG_GRKERNSEC_PAX_PAGEEXEC n
+define_bool CONFIG_GRKERNSEC_PAX_NOELFRELOCS n
+define_bool CONFIG_GRKERNSEC_PAX_ETEXECRELOCS n
+define_bool CONFIG_GRKERNSEC_PAX_MPROTECT n
+if [ "$CONFIG_X86" = "y" ]; then
+define_bool CONFIG_GRKERNSEC_IO n
+define_bool CONFIG_GRKERNSEC_PAX_RANDEXEC n
+define_bool CONFIG_GRKERNSEC_PAX_SEGMEXEC n
+define_bool CONFIG_GRKERNSEC_PAX_EMUTRAMP n
+define_bool CONFIG_GRKERNSEC_PAX_EMUSIGRT n
+fi
+define_bool CONFIG_GRKERNSEC_AUDIT_MOUNT n
+define_bool CONFIG_GRKERNSEC_CHROOT_CAPS n
+define_bool CONFIG_GRKERNSEC_AUDIT_MOUNT n
+define_bool CONFIG_GRKERNSEC_CHROOT_FCHDIR n
+define_bool CONFIG_GRKERNSEC_ACL_HIDEKERN n
+define_bool CONFIG_GRKERNSEC_RESLOG n
+define_int CONFIG_GRKERNSEC_ACL_MAXTRIES 3
+define_int CONFIG_GRKERNSEC_ACL_TIMEOUT 30
+
+define_int  CONFIG_GRKERNSEC_FLOODTIME 10
+define_int  CONFIG_GRKERNSEC_FLOODBURST 4
+define_bool CONFIG_GRKERNSEC_CHROOT_SYSCTL y
+define_bool CONFIG_GRKERNSEC_PROC_MEMMAP y
+define_bool CONFIG_GRKERNSEC_LINK y
+define_bool CONFIG_GRKERNSEC_FIFO y
+define_bool CONFIG_GRKERNSEC_RANDPID y
+define_bool CONFIG_GRKERNSEC_EXECVE y
+define_bool CONFIG_GRKERNSEC_DMESG y
+define_bool CONFIG_GRKERNSEC_RANDID y
+define_bool CONFIG_GRKERNSEC_RANDNET y
+define_bool CONFIG_GRKERNSEC_RANDISN y
+define_bool CONFIG_GRKERNSEC_RANDSRC y
+define_bool CONFIG_GRKERNSEC_RANDRPC y
+define_bool CONFIG_GRKERNSEC_FORKFAIL y
+define_bool CONFIG_GRKERNSEC_TIME y
+define_bool CONFIG_GRKERNSEC_SIGNAL y
+define_bool CONFIG_GRKERNSEC_CHROOT y
+define_bool CONFIG_GRKERNSEC_CHROOT_SHMAT n
+define_bool CONFIG_GRKERNSEC_CHROOT_UNIX y
+define_bool CONFIG_GRKERNSEC_CHROOT_MOUNT y
+define_bool CONFIG_GRKERNSEC_CHROOT_PIVOT y
+define_bool CONFIG_GRKERNSEC_CHROOT_DOUBLE y
+define_bool CONFIG_GRKERNSEC_CHROOT_CHDIR y
+define_bool CONFIG_GRKERNSEC_CHROOT_MKNOD y
+define_bool CONFIG_GRKERNSEC_PROC y
+define_bool CONFIG_GRKERNSEC_PROC_USERGROUP y
+define_int  CONFIG_GRKERNSEC_PROC_GID 10
+define_bool CONFIG_GRKERNSEC_PAX_RANDUSTACK y
+define_bool CONFIG_GRKERNSEC_PAX_RANDKSTACK n
+define_bool CONFIG_GRKERNSEC_PAX_KERNEXEC n
+define_bool CONFIG_GRKERNSEC_PAX_ASLR y
+define_bool CONFIG_GRKERNSEC_PAX_RANDMMAP y
+fi
+if [ "$CONFIG_GRKERNSEC_HI" = "y" ]; then
+define_int CONFIG_GRKERNSEC_FLOODTIME 10
+define_int  CONFIG_GRKERNSEC_FLOODBURST 4
+define_bool CONFIG_GRKERNSEC_LINK y
+define_bool CONFIG_GRKERNSEC_FIFO y
+define_bool CONFIG_GRKERNSEC_RANDPID y
+define_bool CONFIG_GRKERNSEC_EXECVE y
+define_bool CONFIG_GRKERNSEC_DMESG y
+define_bool CONFIG_GRKERNSEC_RANDID y
+define_bool CONFIG_GRKERNSEC_RANDSRC y
+define_bool CONFIG_GRKERNSEC_RANDRPC y
+define_bool CONFIG_GRKERNSEC_FORKFAIL y
+define_bool CONFIG_GRKERNSEC_TIME y
+define_bool CONFIG_GRKERNSEC_SIGNAL y
+define_bool CONFIG_GRKERNSEC_CHROOT_SHMAT y
+define_bool CONFIG_GRKERNSEC_CHROOT_UNIX y
+define_bool CONFIG_GRKERNSEC_CHROOT_MOUNT y
+define_bool CONFIG_GRKERNSEC_CHROOT_FCHDIR y
+define_bool CONFIG_GRKERNSEC_CHROOT_PIVOT y
+define_bool CONFIG_GRKERNSEC_CHROOT_DOUBLE y
+define_bool CONFIG_GRKERNSEC_CHROOT_CHDIR y
+define_bool CONFIG_GRKERNSEC_CHROOT_MKNOD y
+define_bool CONFIG_GRKERNSEC_CHROOT_CAPS y
+define_bool CONFIG_GRKERNSEC_CHROOT_SYSCTL y
+define_bool CONFIG_GRKERNSEC_CHROOT_FINDTASK y
+define_bool CONFIG_GRKERNSEC_PROC y
+define_bool CONFIG_GRKERNSEC_PROC_MEMMAP y
+define_bool CONFIG_GRKERNSEC_HIDESYM y
+define_bool CONFIG_GRKERNSEC_PROC_USERGROUP y
+define_int  CONFIG_GRKERNSEC_PROC_GID 10
+define_bool CONFIG_GRKERNSEC_KMEM y
+define_bool CONFIG_GRKERNSEC_PORT y
+define_bool CONFIG_GRKERNSEC_RESLOG y
+define_bool CONFIG_GRKERNSEC_RANDNET y
+define_bool CONFIG_GRKERNSEC_RANDISN y
+
+define_bool CONFIG_GRKERNSEC_AUDIT_MOUNT n
+define_bool CONFIG_GRKERNSEC_ACL_HIDEKERN n
+define_int CONFIG_GRKERNSEC_ACL_MAXTRIES 3
+define_int CONFIG_GRKERNSEC_ACL_TIMEOUT 30
+
+define_bool CONFIG_GRKERNSEC_PROC_ADD y
+define_bool CONFIG_GRKERNSEC_CHROOT_CHMOD y
+define_bool CONFIG_GRKERNSEC_CHROOT_NICE y
+define_bool CONFIG_GRKERNSEC_PAX_RANDUSTACK y
+define_bool CONFIG_GRKERNSEC_PAX_ASLR y
+define_bool CONFIG_GRKERNSEC_PAX_RANDMMAP y
+define_bool CONFIG_GRKERNSEC_PAX_NOEXEC y
+define_bool CONFIG_GRKERNSEC_PAX_PAGEEXEC n
+define_bool CONFIG_GRKERNSEC_PAX_NOELFRELOCS n
+define_bool CONFIG_GRKERNSEC_PAX_MPROTECT y
+define_bool CONFIG_GRKERNSEC_PAX_ETEXECRELOCS n
+if [ "$CONFIG_X86" = "y" ]; then
+define_bool CONFIG_GRKERNSEC_IO n
+if [ "$CONFIG_MODULES" = "n" ]; then
+define_bool CONFIG_GRKERNSEC_PAX_KERNEXEC y
+fi
+define_bool CONFIG_GRKERNSEC_PAX_RANDKSTACK y
+define_bool CONFIG_GRKERNSEC_PAX_RANDEXEC y
+define_bool CONFIG_GRKERNSEC_PAX_SEGMEXEC y
+define_bool CONFIG_GRKERNSEC_PAX_EMUTRAMP n
+define_bool CONFIG_GRKERNSEC_PAX_EMUSIGRT n
+fi
+if [ "$CONFIG_PARISC" = "y" ]; then
+define_bool CONFIG_GRKERNSEC_PAX_EMUTRAMP y
+define_bool CONFIG_GRKERNSEC_PAX_EMUSIGRT y
+fi
+define_bool CONFIG_GRKERNSEC_AUDIT_MOUNT y
+fi
+if [ "$CONFIG_GRKERNSEC_CUSTOM" = "y" ]; then
+mainmenu_option next_comment
+comment 'Address Space Protection'
+bool 'Enforce non-executable pages' CONFIG_GRKERNSEC_PAX_NOEXEC
+if [ "$CONFIG_GRKERNSEC_PAX_NOEXEC" = "y" ]; then
+  bool 'Paging based non-executable pages' CONFIG_GRKERNSEC_PAX_PAGEEXEC
+  if [ "$CONFIG_X86" = "y" ]; then
+    bool 'Segmentation based non-executable pages' CONFIG_GRKERNSEC_PAX_SEGMEXEC
+  fi
+  if [ "$CONFIG_X86" = "y" -o "$CONFIG_PARISC" = "y" -o "$CONFIG_PPC32" = "y" ]; then
+    if [ "$CONFIG_GRKERNSEC_PAX_PAGEEXEC" = "y" -o "$CONFIG_GRKERNSEC_PAX_SEGMEXEC" = "y" ]; then
+      bool '   Emulate trampolines' CONFIG_GRKERNSEC_PAX_EMUTRAMP
+      if [ "$CONFIG_GRKERNSEC_PAX_EMUTRAMP" = "y" ]; then
+        bool '    Automatically emulate sigreturn trampolines' CONFIG_GRKERNSEC_PAX_EMUSIGRT
+      fi
+    fi
+  fi
+  bool '   Restrict mprotect()' CONFIG_GRKERNSEC_PAX_MPROTECT
+  if [ "$CONFIG_GRKERNSEC_PAX_MPROTECT" = "y" ]; then
+    if [ "$CONFIG_X86" = "y" ]; then
+      bool '    Disallow ELF text relocations (DANGEROUS)' CONFIG_GRKERNSEC_PAX_NOELFRELOCS
+    else
+    if [ "$CONFIG_ALPHA" = "y" -o "$CONFIG_PARISC" = "y" ]; then
+      bool '    Allow ELF ET_EXEC text relocations' CONFIG_GRKERNSEC_PAX_ETEXECRELOCS
+    fi
+    if [ "$CONFIG_X86" = "y" -a "$CONFIG_GRKERNSEC_PAX_MPROTECT" = "y" -a "$CONFIG_GRKERNSEC_PAX_EMUTRAMP" = "y" ]; then
+      bool '    Honor PT_GNU_STACK' CONFIG_GRKERNSEC_PAX_PT_GNU_STACK
+    fi
+    if [ "$CONFIG_X86" = "y" -a "$CONFIG_GRKERNSEC_PAX_MPROTECT" = "y" ]; then
+      bool '    Honor PT_GNU_HEAP' CONFIG_GRKERNSEC_PAX_PT_GNU_HEAP
+    fi
+    if [ "$CONFIG_PPC32" = "y" ]; then
+      define_bool CONFIG_GRKERNSEC_PAX_SYSCALL y
+    fi
+    if [ "$CONFIG_ALPHA" = "y" -o "$CONFIG_PARISC" = "y" -o "$CONFIG_SPARC32" = "y" -o "$CONFIG_SPARC64" = "y" -o "$CONFIG_PPC32" = "y" ]; then
+      bool '    Automatically emulate ELF PLT' CONFIG_GRKERNSEC_PAX_EMUPLT
+      if [ "$CONFIG_GRKERNSEC_PAX_EMUPLT" = "y" ]; then
+        if [ "$CONFIG_SPARC32" = "y" -o "$CONFIG_SPARC64" = "y" ]; then
+    	  define_bool CONFIG_GRKERNSEC_PAX_DLRESOLVE y
+        fi
+      fi
+    fi
+    fi
+  fi
+fi
+if [ "$CONFIG_X86" = "y" -a "$CONFIG_MODULES" = "n" ]; then
+  bool 'Enforce non-executable kernel pages' CONFIG_GRKERNSEC_PAX_KERNEXEC
+fi
+bool 'Address Space Layout Randomization' CONFIG_GRKERNSEC_PAX_ASLR
+if [ "$CONFIG_GRKERNSEC_PAX_ASLR" = "y" ]; then
+  if [ "$CONFIG_X86_TSC" = "y" ]; then
+    bool '  Randomize ker
