Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > fc11cd6e1c513a17304da94a5390f3cd > files > 3780

kernel-2.6.18-194.11.1.el5.src.rpm

Index: latest/arch/alpha/kernel/systbls.S
===================================================================
--- latest.orig/arch/alpha/kernel/systbls.S
+++ latest/arch/alpha/kernel/systbls.S
@@ -239,7 +239,15 @@ sys_call_table:
 	.quad alpha_ni_syscall
 	.quad alpha_ni_syscall			/* 220 */
 	.quad alpha_ni_syscall
+#ifdef CONFIG_TUX
+	.quad __sys_tux
+#else
+# ifdef CONFIG_TUX_MODULE
+	.quad sys_tux
+# else
 	.quad alpha_ni_syscall
+# endif
+#endif
 	.quad alpha_ni_syscall
 	.quad alpha_ni_syscall
 	.quad alpha_ni_syscall			/* 225 */
Index: latest/arch/i386/kernel/syscall_table.S
===================================================================
--- latest.orig/arch/i386/kernel/syscall_table.S
+++ latest/arch/i386/kernel/syscall_table.S
@@ -221,7 +221,15 @@ ENTRY(sys_call_table)
 	.long sys_madvise
 	.long sys_getdents64	/* 220 */
 	.long sys_fcntl64
-	.long sys_ni_syscall	/* reserved for TUX */
+#ifdef CONFIG_TUX
+	.long __sys_tux
+#else
+# ifdef CONFIG_TUX_MODULE
+	.long sys_tux
+# else
+	.long sys_ni_syscall
+# endif
+#endif
 	.long sys_ni_syscall
 	.long sys_gettid
 	.long sys_readahead	/* 225 */
Index: latest/arch/ia64/kernel/entry.S
===================================================================
--- latest.orig/arch/ia64/kernel/entry.S
+++ latest/arch/ia64/kernel/entry.S
@@ -1427,7 +1427,15 @@ sys_call_table:
 	data8 sys_syslog
 	data8 sys_setitimer
 	data8 sys_getitimer
+#ifdef CONFIG_TUX
+	data8 __sys_tux				// 1120		/* was: ia64_oldstat */
+#else
+# ifdef CONFIG_TUX_MODULE
+	data8 sys_tux				// 1120		/* was: ia64_oldstat */
+# else
 	data8 sys_ni_syscall			// 1120		/* was: ia64_oldstat */
+# endif
+#endif
 	data8 sys_ni_syscall					/* was: ia64_oldlstat */
 	data8 sys_ni_syscall					/* was: ia64_oldfstat */
 	data8 sys_vhangup
Index: latest/arch/ia64/kernel/ia64_ksyms.c
===================================================================
--- latest.orig/arch/ia64/kernel/ia64_ksyms.c
+++ latest/arch/ia64/kernel/ia64_ksyms.c
@@ -43,6 +43,11 @@ EXPORT_SYMBOL(__strlen_user);
 EXPORT_SYMBOL(__strncpy_from_user);
 EXPORT_SYMBOL(__strnlen_user);
 
+#define __KERNEL_SYSCALLS__
+#include <asm/unistd.h>
+EXPORT_SYMBOL_GPL(sys_execve);
+EXPORT_SYMBOL_GPL(clone);
+
 /* from arch/ia64/lib */
 extern void __divsi3(void);
 extern void __udivsi3(void);
Index: latest/arch/x86_64/ia32/ia32entry.S
===================================================================
--- latest.orig/arch/x86_64/ia32/ia32entry.S
+++ latest/arch/x86_64/ia32/ia32entry.S
@@ -617,7 +617,15 @@ ia32_sys_call_table:
 	.quad sys_madvise
 	.quad compat_sys_getdents64	/* 220 getdents64 */
 	.quad compat_sys_fcntl64	
-	.quad quiet_ni_syscall		/* tux */
+#ifdef CONFIG_TUX
+	.quad __sys_tux
+#else
+# ifdef CONFIG_TUX_MODULE
+	.quad sys_tux
+# else
+	.quad quiet_ni_syscall
+# endif
+#endif
 	.quad quiet_ni_syscall    	/* security */
 	.quad sys_gettid	
 	.quad sys_readahead	/* 225 */
Index: latest/fs/dcache.c
===================================================================
--- latest.orig/fs/dcache.c
+++ latest/fs/dcache.c
@@ -84,6 +84,10 @@ static void d_free(struct dentry *dentry
 {
 	if (dentry->d_op && dentry->d_op->d_release)
 		dentry->d_op->d_release(dentry);
+ 	if (dentry->d_extra_attributes) {
+ 		kfree(dentry->d_extra_attributes);
+ 		dentry->d_extra_attributes = NULL;
+ 	}
  	call_rcu(&dentry->d_u.d_rcu, d_callback);
 }
 
@@ -750,6 +754,7 @@ struct dentry *d_alloc(struct dentry * p
 	dentry->d_sb = NULL;
 	dentry->d_op = NULL;
 	dentry->d_fsdata = NULL;
+	dentry->d_extra_attributes = NULL;
 	dentry->d_mounted = 0;
 #ifdef CONFIG_PROFILING
 	dentry->d_cookie = NULL;
@@ -1358,6 +1363,16 @@ already_unhashed:
 	/* Unhash the target: dput() will then get rid of it */
 	__d_drop(target);
 
+ 	/* flush any possible attributes */
+ 	if (dentry->d_extra_attributes) {
+ 		kfree(dentry->d_extra_attributes);
+ 		dentry->d_extra_attributes = NULL;
+ 	}
+ 	if (target->d_extra_attributes) {
+ 		kfree(target->d_extra_attributes);
+ 		target->d_extra_attributes = NULL;
+ 	}
+
 	list_del(&dentry->d_u.d_child);
 	list_del(&target->d_u.d_child);
 
@@ -1402,7 +1417,7 @@ already_unhashed:
  *
  * "buflen" should be positive. Caller holds the dcache_lock.
  */
-static char * __d_path( struct dentry *dentry, struct vfsmount *vfsmnt,
+char * __d_path( struct dentry *dentry, struct vfsmount *vfsmnt,
 			struct dentry *root, struct vfsmount *rootmnt,
 			char *buffer, int buflen)
 {
@@ -1470,6 +1485,8 @@ Elong:
 	return ERR_PTR(-ENAMETOOLONG);
 }
 
+EXPORT_SYMBOL_GPL(__d_path);
+
 /* write full pathname into buffer and return start of pathname */
 char * d_path(struct dentry *dentry, struct vfsmount *vfsmnt,
 				char *buf, int buflen)
@@ -1698,6 +1715,23 @@ static void __init dcache_init_early(voi
 		INIT_HLIST_HEAD(&dentry_hashtable[loop]);
 }
 
+void flush_dentry_attributes (void)
+{
+	struct hlist_node *tmp;
+	struct dentry *dentry;
+	int i;
+
+	spin_lock(&dcache_lock);
+	for (i = 0; i <= d_hash_mask; i++)
+		hlist_for_each_entry(dentry, tmp, dentry_hashtable+i, d_hash) {
+			kfree(dentry->d_extra_attributes);
+			dentry->d_extra_attributes = NULL;
+		}
+	spin_unlock(&dcache_lock);
+}
+
+EXPORT_SYMBOL_GPL(flush_dentry_attributes);
+
 static void __init dcache_init(unsigned long mempages)
 {
 	int loop;
Index: latest/fs/exec.c
===================================================================
--- latest.orig/fs/exec.c
+++ latest/fs/exec.c
@@ -1466,6 +1466,8 @@ int do_coredump(long signr, int exit_cod
 	binfmt = current->binfmt;
 	if (!binfmt || !binfmt->core_dump)
 		goto fail;
+	if (current->tux_exit)
+		current->tux_exit();
 	down_write(&mm->mmap_sem);
 	if (!mm->dumpable) {
 		up_write(&mm->mmap_sem);
Index: latest/fs/fcntl.c
===================================================================
--- latest.orig/fs/fcntl.c
+++ latest/fs/fcntl.c
@@ -111,7 +111,7 @@ out:
 	return error;
 }
 
-static int dupfd(struct file *file, unsigned int start)
+int dupfd(struct file *file, unsigned int start)
 {
 	struct files_struct * files = current->files;
 	struct fdtable *fdt;
@@ -134,6 +134,8 @@ static int dupfd(struct file *file, unsi
 	return fd;
 }
 
+EXPORT_SYMBOL_GPL(dupfd);
+
 asmlinkage long sys_dup2(unsigned int oldfd, unsigned int newfd)
 {
 	int err = -EBADF;
Index: latest/fs/namei.c
===================================================================
--- latest.orig/fs/namei.c
+++ latest/fs/namei.c
@@ -745,7 +745,7 @@ static __always_inline void follow_dotdo
  *  It _is_ time-critical.
  */
 static int do_lookup(struct nameidata *nd, struct qstr *name,
-		     struct path *path)
+		     struct path *path, int atomic)
 {
 	struct vfsmount *mnt = nd->mnt;
 	struct dentry *dentry = __d_lookup(nd->dentry, name);
@@ -761,12 +761,16 @@ done:
 	return 0;
 
 need_lookup:
+	if (atomic)
+		return -EWOULDBLOCKIO;
 	dentry = real_lookup(nd->dentry, name, nd);
 	if (IS_ERR(dentry))
 		goto fail;
 	goto done;
 
 need_revalidate:
+	if (atomic)
+		return -EWOULDBLOCKIO;
 	if (dentry->d_op->d_revalidate(dentry, nd))
 		goto done;
 	if (d_invalidate(dentry))
@@ -790,9 +794,11 @@ static fastcall int __link_path_walk(con
 {
 	struct path next;
 	struct inode *inode;
-	int err;
+	int err, atomic;
 	unsigned int lookup_flags = nd->flags;
-	
+
+	atomic = (lookup_flags & LOOKUP_ATOMIC);
+
 	while (*name=='/')
 		name++;
 	if (!*name)
@@ -861,7 +867,7 @@ static fastcall int __link_path_walk(con
 				break;
 		}
 		/* This does the actual lookups.. */
-		err = do_lookup(nd, &this, &next);
+		err = do_lookup(nd, &this, &next, atomic);
 		if (err)
 			break;
 
@@ -916,7 +922,7 @@ last_component:
 			if (err < 0)
 				break;
 		}
-		err = do_lookup(nd, &this, &next);
+		err = do_lookup(nd, &this, &next, atomic);
 		if (err)
 			break;
 		inode = next.dentry->d_inode;
@@ -1418,6 +1424,8 @@ static inline int lookup_flags(unsigned 
 	
 	if (f & O_DIRECTORY)
 		retval |= LOOKUP_DIRECTORY;
+	if (f & O_ATOMICLOOKUP)
+		retval |= LOOKUP_ATOMIC;
 
 	return retval;
 }
Index: latest/fs/namespace.c
===================================================================
--- latest.orig/fs/namespace.c
+++ latest/fs/namespace.c
@@ -1609,6 +1609,8 @@ void set_fs_root(struct fs_struct *fs, s
 	}
 }
 
+EXPORT_SYMBOL_GPL(set_fs_root);
+
 /*
  * Replace the fs->{pwdmnt,pwd} with {mnt,dentry}. Put the old values.
  * It can block. Requires the big lock held.
Index: latest/fs/open.c
===================================================================
--- latest.orig/fs/open.c
+++ latest/fs/open.c
@@ -562,6 +562,8 @@ out:
 	return error;
 }
 
+EXPORT_SYMBOL_GPL(sys_chdir);
+
 asmlinkage long sys_fchdir(unsigned int fd)
 {
 	struct file *file;
@@ -618,6 +620,8 @@ out:
 	return error;
 }
 
+EXPORT_SYMBOL_GPL(sys_chroot);
+
 asmlinkage long sys_fchmod(unsigned int fd, mode_t mode)
 {
 	struct inode * inode;
Index: latest/fs/pipe.c
===================================================================
--- latest.orig/fs/pipe.c
+++ latest/fs/pipe.c
@@ -973,6 +973,8 @@ no_files:
 	return error;	
 }
 
+EXPORT_SYMBOL_GPL(do_pipe);
+
 /*
  * pipefs should _never_ be mounted by userland - too much of security hassle,
  * no real gain from having the whole whorehouse mounted. So we don't need
Index: latest/fs/read_write.c
===================================================================
--- latest.orig/fs/read_write.c
+++ latest/fs/read_write.c
@@ -374,6 +374,8 @@ asmlinkage ssize_t sys_write(unsigned in
 	return ret;
 }
 
+EXPORT_SYMBOL_GPL(sys_write);
+
 asmlinkage ssize_t sys_pread64(unsigned int fd, char __user *buf,
 			     size_t count, loff_t pos)
 {
Index: latest/include/asm-alpha/fcntl.h
===================================================================
--- latest.orig/include/asm-alpha/fcntl.h
+++ latest/include/asm-alpha/fcntl.h
@@ -14,6 +14,7 @@
 #define O_DIRECTORY	0100000	/* must be a directory */
 #define O_NOFOLLOW	0200000 /* don't follow links */
 #define O_LARGEFILE	0400000 /* will be set by the kernel on every open */
+#define O_ATOMICLOOKUP	01000000 /* do atomic file lookup */
 #define O_DIRECT	02000000 /* direct disk access - should check with OSF/1 */
 #define O_NOATIME	04000000
 
Index: latest/include/asm-generic/fcntl.h
===================================================================
--- latest.orig/include/asm-generic/fcntl.h
+++ latest/include/asm-generic/fcntl.h
@@ -48,6 +48,10 @@
 #ifndef O_NOATIME
 #define O_NOATIME	01000000
 #endif
+#ifndef O_ATOMICLOOKUP
+#define O_ATOMICLOOKUP	02000000	/* do atomic file lookup */
+#endif
+
 #ifndef O_NDELAY
 #define O_NDELAY	O_NONBLOCK
 #endif
Index: latest/include/asm-i386/unistd.h
===================================================================
--- latest.orig/include/asm-i386/unistd.h
+++ latest/include/asm-i386/unistd.h
@@ -328,6 +328,7 @@
 
 #define NR_syscalls 318
 
+#ifndef __KERNEL_SYSCALLS_NO_ERRNO__
 /*
  * user-visible error numbers are in the range -1 - -128: see
  * <asm-i386/errno.h>
@@ -341,6 +342,10 @@ do { \
 	return (type) (res); \
 } while (0)
 
+#else
+# define __syscall_return(type, res) return (type) (res)
+#endif
+
 /* XXX - _foo needs to be __foo, while __NR_bar could be _NR_bar. */
 #define _syscall0(type,name) \
 type name(void) \
Index: latest/include/asm-ia64/unistd.h
===================================================================
--- latest.orig/include/asm-ia64/unistd.h
+++ latest/include/asm-ia64/unistd.h
@@ -109,7 +109,7 @@
 #define __NR_syslog			1117
 #define __NR_setitimer			1118
 #define __NR_getitimer			1119
-/* 1120 was __NR_old_stat */
+#define __NR_tux			1120 /* was __NR_old_stat */
 /* 1121 was __NR_old_lstat */
 /* 1122 was __NR_old_fstat */
 #define __NR_vhangup			1123
Index: latest/include/asm-sparc/fcntl.h
===================================================================
--- latest.orig/include/asm-sparc/fcntl.h
+++ latest/include/asm-sparc/fcntl.h
@@ -14,6 +14,7 @@
 #define O_NDELAY	(0x0004 | O_NONBLOCK)
 #define O_NOCTTY	0x8000	/* not fcntl */
 #define O_LARGEFILE	0x40000
+#define O_ATOMICLOOKUP	0x80000 /* do atomic file lookup */
 #define O_DIRECT        0x100000 /* direct disk access hint */
 #define O_NOATIME	0x200000
 
Index: latest/include/asm-sparc64/fcntl.h
===================================================================
--- latest.orig/include/asm-sparc64/fcntl.h
+++ latest/include/asm-sparc64/fcntl.h
@@ -14,6 +14,7 @@
 #define O_NONBLOCK	0x4000
 #define O_NOCTTY	0x8000	/* not fcntl */
 #define O_LARGEFILE	0x40000
+#define O_ATOMICLOOKUP	0x80000 /* do atomic file lookup */
 #define O_DIRECT        0x100000 /* direct disk access hint */
 #define O_NOATIME	0x200000
 
Index: latest/include/asm-x86_64/unistd.h
===================================================================
--- latest.orig/include/asm-x86_64/unistd.h
+++ latest/include/asm-x86_64/unistd.h
@@ -425,7 +425,15 @@ __SYSCALL(__NR_putpmsg, sys_ni_syscall)
 __SYSCALL(__NR_afs_syscall, sys_ni_syscall)
 
 #define __NR_tuxcall      		184 /* reserved for tux */
-__SYSCALL(__NR_tuxcall, sys_ni_syscall)
+#ifdef CONFIG_TUX
+ __SYSCALL(__NR_tuxcall, __sys_tux)
+#else
+# ifdef CONFIG_TUX_MODULE
+  __SYSCALL(__NR_tuxcall, sys_tux)
+# else
+  __SYSCALL(__NR_tuxcall, sys_ni_syscall)
+# endif
+#endif
 
 #define __NR_security			185
 __SYSCALL(__NR_security, sys_ni_syscall)
Index: latest/include/linux/buffer_head.h
===================================================================
--- latest.orig/include/linux/buffer_head.h
+++ latest/include/linux/buffer_head.h
@@ -203,6 +203,7 @@ int generic_cont_expand(struct inode *in
 int generic_cont_expand_simple(struct inode *inode, loff_t size);
 int block_commit_write(struct page *page, unsigned from, unsigned to);
 void block_sync_page(struct page *);
+void flush_inode_pages (struct inode * inode);
 sector_t generic_block_bmap(struct address_space *, sector_t, get_block_t *);
 int generic_commit_write(struct file *, struct page *, unsigned, unsigned);
 int block_truncate_page(struct address_space *, loff_t, get_block_t *);
Index: latest/include/linux/dcache.h
===================================================================
--- latest.orig/include/linux/dcache.h
+++ latest/include/linux/dcache.h
@@ -107,6 +107,7 @@ struct dentry {
 	struct dentry_operations *d_op;
 	struct super_block *d_sb;	/* The root of the dentry tree */
 	void *d_fsdata;			/* fs-specific data */
+	void *d_extra_attributes;	/* TUX-specific data */
 #ifdef CONFIG_PROFILING
 	struct dcookie_struct *d_cookie; /* cookie, if any */
 #endif
@@ -230,6 +231,7 @@ extern struct dentry * d_splice_alias(st
 extern void shrink_dcache_sb(struct super_block *);
 extern void shrink_dcache_parent(struct dentry *);
 extern int d_invalidate(struct dentry *);
+extern void flush_dentry_attributes(void);
 
 /* only used at mount-time */
 extern struct dentry * d_alloc_root(struct inode *);
@@ -291,8 +293,12 @@ extern struct dentry * d_hash_and_lookup
 /* validate "insecure" dentry pointer */
 extern int d_validate(struct dentry *, struct dentry *);
 
+char * __d_path( struct dentry *dentry, struct vfsmount *vfsmnt,
+		 struct dentry *root, struct vfsmount *rootmnt,
+		 char *buffer, int buflen);
+
 extern char * d_path(struct dentry *, struct vfsmount *, char *, int);
-  
+
 /* Allocation counts.. */
 
 /**
Index: latest/include/linux/errno.h
===================================================================
--- latest.orig/include/linux/errno.h
+++ latest/include/linux/errno.h
@@ -24,6 +24,9 @@
 #define EIOCBQUEUED	529	/* iocb queued, will get completion event */
 #define EIOCBRETRY	530	/* iocb queued, will trigger a retry */
 
+/* Defined for TUX async IO */
+#define EWOULDBLOCKIO	530	/* Would block due to block-IO */
+
 #endif
 
 #endif
Index: latest/include/linux/file.h
===================================================================
--- latest.orig/include/linux/file.h
+++ latest/include/linux/file.h
@@ -113,4 +113,6 @@ struct task_struct;
 struct files_struct *get_files_struct(struct task_struct *);
 void FASTCALL(put_files_struct(struct files_struct *fs));
 
+extern int dupfd(struct file *file, unsigned int start);
+
 #endif /* __LINUX_FILE_H */
Index: latest/include/linux/fs.h
===================================================================
--- latest.orig/include/linux/fs.h
+++ latest/include/linux/fs.h
@@ -1659,7 +1659,7 @@ ssize_t generic_file_write_nolock(struct
 extern ssize_t generic_file_sendfile(struct file *, loff_t *, size_t, read_actor_t, void *);
 extern void do_generic_mapping_read(struct address_space *mapping,
 				    struct file_ra_state *, struct file *,
-				    loff_t *, read_descriptor_t *, read_actor_t);
+				    loff_t *, read_descriptor_t *, read_actor_t, int);
 
 /* fs/splice.c */
 extern ssize_t generic_file_splice_read(struct file *, loff_t *,
@@ -1702,14 +1702,15 @@ static inline int xip_truncate_page(stru
 
 static inline void do_generic_file_read(struct file * filp, loff_t *ppos,
 					read_descriptor_t * desc,
-					read_actor_t actor)
+					read_actor_t actor, int nonblock)
 {
 	do_generic_mapping_read(filp->f_mapping,
 				&filp->f_ra,
 				filp,
 				ppos,
 				desc,
-				actor);
+				actor,
+				nonblock);
 }
 
 ssize_t __blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode,
Index: latest/include/linux/kmod.h
===================================================================
--- latest.orig/include/linux/kmod.h
+++ latest/include/linux/kmod.h
@@ -46,5 +46,7 @@ call_usermodehelper(char *path, char **a
 }
 
 extern void usermodehelper_init(void);
+extern int __exec_usermodehelper(char *path, char **argv, char **envp,
+				 struct key *ring);
 
 #endif /* __LINUX_KMOD_H__ */
Index: latest/include/linux/namei.h
===================================================================
--- latest.orig/include/linux/namei.h
+++ latest/include/linux/namei.h
@@ -48,6 +48,8 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LA
 #define LOOKUP_PARENT		16
 #define LOOKUP_NOALT		32
 #define LOOKUP_REVAL		64
+#define LOOKUP_ATOMIC		128
+
 /*
  * Intent data
  */
Index: latest/include/linux/net.h
===================================================================
--- latest.orig/include/linux/net.h
+++ latest/include/linux/net.h
@@ -189,6 +189,7 @@ extern int	     sock_create_kern(int fam
 				      struct socket **res);
 extern int	     sock_create_lite(int family, int type, int proto,
 				      struct socket **res); 
+extern struct socket *sock_alloc(void);
 extern void	     sock_release(struct socket *sock);
 extern int   	     sock_sendmsg(struct socket *sock, struct msghdr *msg,
 				  size_t len);
Index: latest/include/linux/sched.h
===================================================================
--- latest.orig/include/linux/sched.h
+++ latest/include/linux/sched.h
@@ -895,6 +895,11 @@ struct task_struct {
 	int (*notifier)(void *priv);
 	void *notifier_data;
 	sigset_t *notifier_mask;
+
+	/* TUX state */
+	void *tux_info;
+	void (*tux_exit)(void);
+
 	
 	void *security;
 	struct audit_context *audit_context;
Index: latest/include/linux/skbuff.h
===================================================================
--- latest.orig/include/linux/skbuff.h
+++ latest/include/linux/skbuff.h
@@ -1422,6 +1422,8 @@ static inline unsigned int skb_checksum_
 		__skb_checksum_complete(skb);
 }
 
+struct tux_req_struct;
+
 #ifdef CONFIG_NETFILTER
 static inline void nf_conntrack_put(struct nf_conntrack *nfct)
 {
Index: latest/include/linux/socket.h
===================================================================
--- latest.orig/include/linux/socket.h
+++ latest/include/linux/socket.h
@@ -300,6 +300,10 @@ extern int move_addr_to_user(void *kaddr
 extern int move_addr_to_kernel(void __user *uaddr, int ulen, void *kaddr);
 extern int put_cmsg(struct msghdr*, int level, int type, int len, void *data);
 
+struct socket;
+extern int sock_map_fd(struct socket *sock);
+extern struct socket *sockfd_lookup(int fd, int *err);
+
 #endif
 #endif /* not kernel and not glibc */
 #endif /* _LINUX_SOCKET_H */
Index: latest/include/linux/sysctl.h
===================================================================
--- latest.orig/include/linux/sysctl.h
+++ latest/include/linux/sysctl.h
@@ -220,6 +220,7 @@ enum
 	NET_LLC=18,
 	NET_NETFILTER=19,
 	NET_DCCP=20,
+	NET_TUX=21,
 };
 
 /* /proc/sys/kernel/random */
@@ -774,6 +775,55 @@ enum {
 	NET_BRIDGE_NF_FILTER_VLAN_TAGGED = 4,
 };
 
+/* /proc/sys/net/tux/ */
+enum {
+	NET_TUX_DOCROOT			=  1,
+	NET_TUX_LOGFILE			=  2,
+	NET_TUX_EXTCGI			=  3,
+	NET_TUX_STOP			=  4,
+	NET_TUX_CLIENTPORT		=  5,
+	NET_TUX_LOGGING			=  6,
+	NET_TUX_SERVERPORT		=  7,
+	NET_TUX_THREADS			=  8,
+	NET_TUX_KEEPALIVE_TIMEOUT	=  9,
+	NET_TUX_MAX_KEEPALIVE_BW	= 10,
+	NET_TUX_DEFER_ACCEPT		= 11,
+	NET_TUX_MAX_FREE_REQUESTS	= 12,
+	NET_TUX_MAX_CONNECT		= 13,
+	NET_TUX_MAX_BACKLOG		= 14,
+	NET_TUX_MODE_FORBIDDEN		= 15,
+	NET_TUX_MODE_ALLOWED		= 16,
+	NET_TUX_MODE_USERSPACE		= 17,
+	NET_TUX_MODE_CGI		= 18,
+	NET_TUX_CGI_UID			= 19,
+	NET_TUX_CGI_GID			= 20,
+	NET_TUX_CGIROOT			= 21,
+	NET_TUX_LOGENTRY_ALIGN_ORDER	= 22,
+	NET_TUX_NONAGLE			= 23,
+	NET_TUX_ACK_PINGPONG		= 24,
+	NET_TUX_PUSH_ALL		= 25,
+	NET_TUX_ZEROCOPY_PARSE		= 26,
+	NET_CONFIG_TUX_DEBUG_BLOCKING	= 27,
+	NET_TUX_PAGE_AGE_START		= 28,
+	NET_TUX_PAGE_AGE_ADV		= 29,
+	NET_TUX_PAGE_AGE_MAX		= 30,
+	NET_TUX_VIRTUAL_SERVER		= 31,
+	NET_TUX_MAX_OBJECT_SIZE		= 32,
+	NET_TUX_COMPRESSION		= 33,
+	NET_TUX_NOID			= 34,
+	NET_TUX_CGI_INHERIT_CPU		= 35,
+	NET_TUX_CGI_CPU_MASK		= 36,
+	NET_TUX_ZEROCOPY_HEADER		= 37,
+	NET_TUX_ZEROCOPY_SENDFILE	= 38,
+	NET_TUX_ALL_USERSPACE		= 39,
+	NET_TUX_REDIRECT_LOGGING	= 40,
+	NET_TUX_REFERER_LOGGING		= 41,
+	NET_TUX_MAX_HEADER_LEN		= 42,
+	NET_TUX_404_PAGE		= 43,
+	NET_TUX_MAX_KEEPALIVES		= 44,
+	NET_TUX_IGNORE_QUERY		= 45,
+};
+
 /* CTL_FS names: */
 enum
 {
Index: latest/include/net/sock.h
===================================================================
--- latest.orig/include/net/sock.h
+++ latest/include/net/sock.h
@@ -62,7 +62,7 @@
  */
 
 /* Define this to get the SOCK_DBG debugging facility. */
-#define SOCK_DEBUGGING
+//#define SOCK_DEBUGGING
 #ifdef SOCK_DEBUGGING
 #define SOCK_DEBUG(sk, msg...) do { if ((sk) && sock_flag((sk), SOCK_DBG)) \
 					printk(KERN_DEBUG msg); } while (0)
@@ -166,7 +166,7 @@ struct sock_common {
   *	@sk_timer: sock cleanup timer
   *	@sk_stamp: time stamp of last packet received
   *	@sk_socket: Identd and reporting IO signals
-  *	@sk_user_data: RPC layer private data
+  *	@sk_user_data: RPC and Tux layer private data
   *	@sk_sndmsg_page: cached page for sendmsg
   *	@sk_sndmsg_off: cached offset for sendmsg
   *	@sk_send_head: front of stuff to transmit
@@ -176,6 +176,7 @@ struct sock_common {
   *	@sk_data_ready: callback to indicate there is data to be processed
   *	@sk_write_space: callback to indicate there is bf sending space available
   *	@sk_error_report: callback to indicate errors (e.g. %MSG_ERRQUEUE)
+  *	@sk_create_child - callback to get new socket events
   *	@sk_backlog_rcv: callback to process the backlog
   *	@sk_destruct: called at sock freeing time, i.e. when all refcnt == 0
  */
@@ -257,6 +258,7 @@ struct sock {
 	void			(*sk_error_report)(struct sock *sk);
   	int			(*sk_backlog_rcv)(struct sock *sk,
 						  struct sk_buff *skb);  
+	void			(*sk_create_child)(struct sock *sk, struct sock *newsk);
 	void                    (*sk_destruct)(struct sock *sk);
 };
 
@@ -760,7 +762,7 @@ extern struct sock		*sk_alloc(int family
 					  gfp_t priority,
 					  struct proto *prot, int zero_it);
 extern void			sk_free(struct sock *sk);
-extern struct sock		*sk_clone(const struct sock *sk,
+extern struct sock		*sk_clone(struct sock *sk,
 					  const gfp_t priority);
 
 extern struct sk_buff		*sock_wmalloc(struct sock *sk,
Index: latest/include/net/tcp.h
===================================================================
--- latest.orig/include/net/tcp.h
+++ latest/include/net/tcp.h
@@ -271,6 +271,8 @@ extern void			tcp_shutdown (struct sock 
 
 extern int			tcp_v4_rcv(struct sk_buff *skb);
 
+extern struct sock *		tcp_v4_lookup_listener(u32 daddr, unsigned short hnum, int dif);
+
 extern int			tcp_v4_remember_stamp(struct sock *sk);
 
 extern int		    	tcp_v4_tw_remember_stamp(struct inet_timewait_sock *tw);
@@ -435,6 +437,7 @@ extern int  tcp_send_synack(struct sock 
 extern void tcp_push_one(struct sock *, unsigned int mss_now);
 extern void tcp_send_ack(struct sock *sk);
 extern void tcp_send_delayed_ack(struct sock *sk);
+extern void tcp_cleanup_rbuf(struct sock *sk, int copied);
 
 /* tcp_input.c */
 extern void tcp_cwnd_application_limited(struct sock *sk);
Index: latest/include/net/tux.h
===================================================================
--- /dev/null
+++ latest/include/net/tux.h
@@ -0,0 +1,803 @@
+#ifndef _NET_TUX_H
+#define _NET_TUX_H
+
+/*
+ * TUX - Integrated Application Protocols Layer and Object Cache
+ *
+ * Copyright (C) 2000, 2001, Ingo Molnar <mingo@redhat.com>
+ *
+ * tux.h: main structure definitions and function prototypes
+ */
+
+#define __KERNEL_SYSCALLS__
+
+#include <linux/mm.h>
+#include <linux/net.h>
+#include <linux/wait.h>
+#include <linux/namei.h>
+#include <linux/file.h>
+#include <linux/mman.h>
+#include <linux/swap.h>
+#include <linux/ctype.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/unistd.h>
+#include <linux/sysctl.h>
+#include <linux/proc_fs.h>
+#include <linux/pagemap.h>
+#include <linux/vmalloc.h>
+#include <linux/utsname.h>
+#include <linux/smp_lock.h>
+#include <linux/kernel_stat.h>
+#include <linux/kernel_stat.h>
+#include <linux/time.h>
+#include <asm/div64.h>
+#include <asm/unaligned.h>
+#include <linux/compiler.h>
+#include <linux/mount.h>
+#include <linux/zlib.h>
+#include <linux/syscalls.h>
+#include <linux/cpumask.h>
+
+#include <net/tcp.h>
+#include <net/tux_u.h>
+
+/* Maximum number of threads: */
+#define CONFIG_TUX_NUMTHREADS 16
+
+/* Number of cachemiss/IO threads: */
+#define NR_IO_THREADS 64
+
+/* Maximum number of listen sockets per thread: */
+#define CONFIG_TUX_NUMSOCKETS 16
+
+extern spinlock_t tux_module_lock;
+extern struct module *tux_module;
+extern asmlinkage long (*sys_tux_ptr) (unsigned int action, user_req_t *u_info);
+
+#undef Dprintk
+
+extern int tux_TDprintk;
+extern int tux_Dprintk;
+
+#ifdef CONFIG_TUX_DEBUG
+# define TUX_BUG() BUG()
+
+# define TUX_DPRINTK 1
+# define TDprintk(x...) do { if (tux_TDprintk) { printk("<%ld:%s:%d>: ", jiffies, __FILE__, __LINE__); printk(x); } } while (0)
+# define Dprintk(x...) do { if (tux_Dprintk == 1) TDprintk(x); } while (0)
+#else
+# define TUX_DPRINTK 0
+# define Dprintk(x...) do { } while (0)
+# define TDprintk(x...) do { } while (0)
+//# define TUX_BUG() BUG()
+# define TUX_BUG() do { } while (0)
+#endif
+
+#if 1
+# define INC_STAT(x) do { } while (0)
+# define DEC_STAT(x) do { } while (0)
+# define ADD_STAT(x,y) do { } while (0)
+# define SUB_STAT(x,y) do { } while (0)
+#else
+# define INC_STAT(x) atomic_inc((atomic_t *)&kstat.x)
+# define DEC_STAT(x) atomic_dec((atomic_t *)&kstat.x)
+# define ADD_STAT(y,x) atomic_add(y,(atomic_t *)&kstat.x)
+# define SUB_STAT(y,x) atomic_sub(y,(atomic_t *)&kstat.x)
+#endif
+
+// lru needs this:
+
+# define DEBUG_DEL_LIST(x...) do { INIT_LIST_HEAD((x)); } while (0)
+
+
+#define LOG_LEN (8*1024*1024UL)
+
+struct tux_req_struct;
+typedef struct tux_req_struct tux_req_t;
+typedef struct tux_threadinfo threadinfo_t;
+
+extern struct address_space_operations url_aops;
+
+typedef struct tcapi_template_s {
+	char *vfs_name;
+	struct list_head modules;
+	int (*query) (tux_req_t *req);
+	struct module *mod;
+	unsigned int userspace_id;
+} tcapi_template_t;
+
+typedef struct mimetype_s {
+	struct list_head list;
+
+	char *ext;
+	unsigned int ext_len;
+	char *type;
+	unsigned int type_len;
+	char *expire_str;
+	unsigned int expire_str_len;
+
+	unsigned int special;
+} mimetype_t;
+
+typedef struct tux_attribute_s {
+	mimetype_t *mime;
+	tcapi_template_t *tcapi;
+} tux_attribute_t;
+
+#define MAX_TUX_ATOMS 8
+
+typedef void (atom_func_t)(tux_req_t *req, int cachemiss);
+
+typedef struct tux_proto_s
+{
+	unsigned int defer_accept;
+	unsigned int can_redirect;
+	void (*got_request) (tux_req_t *req);
+	int (*parse_message) (tux_req_t *req, const int total_len);
+	atom_func_t *illegal_request;
+	atom_func_t *request_timeout;
+	void (*pre_log) (tux_req_t *req);
+	int (*check_req_err) (tux_req_t *req, int cachemiss);
+	char * (*print_dir_line) (tux_req_t *req, char *tmp, char *d_name, int d_len, int d_type, struct dentry *dentry, struct inode *inode);
+	const char *name;
+	struct nameidata main_docroot;
+} tux_proto_t;
+
+typedef struct tux_socket_s {
+	tux_proto_t *proto;
+	unsigned int ip;
+	unsigned short port;
+	struct proc_dir_entry *entry;
+} tux_socket_t;
+
+extern tux_socket_t tux_listen [CONFIG_TUX_NUMTHREADS][CONFIG_TUX_NUMSOCKETS];
+
+
+typedef struct abuf_s {
+	struct page *page;
+	char *buf;
+	unsigned int size;
+	unsigned int max_len;
+	unsigned int offset;
+	unsigned int left;
+	unsigned long flags;
+} abuf_t;
+
+struct linux_dirent64 {
+	u64		d_ino;
+	s64		d_off;
+	unsigned short  d_reclen;
+	unsigned char   d_type;
+	char		d_name[0];
+};
+
+struct getdents_callback64 {
+	struct linux_dirent64 * current_dir;
+	struct linux_dirent64 * previous;
+	int count;
+	int error;
+};
+
+#define TUX_MAGIC 0x12457801
+
+#define MAX_TUX_ATOMS 8
+
+struct tux_req_struct
+{
+	tux_proto_t *proto;
+
+	int atom_idx;
+	atom_func_t *atoms [MAX_TUX_ATOMS];
+	struct list_head work;
+
+	struct list_head all;
+	struct list_head free;
+	struct list_head lru;
+
+	unsigned long idle_input;
+	unsigned long wait_output_space;
+
+	struct socket *sock;
+	struct dentry *dentry;
+	struct vfsmount *mnt;
+	struct dentry *docroot_dentry;
+	struct vfsmount *docroot_mnt;
+	struct dentry *cwd_dentry;
+	struct vfsmount *cwd_mnt;
+
+	struct file *in_file;
+	int fd;
+	read_descriptor_t desc;
+	u32 client_addr;
+	u32 client_port;
+	unsigned int virtual;
+
+	loff_t total_file_len;
+	unsigned int lendigits;
+	loff_t offset_start;
+	loff_t offset_end;
+	loff_t output_len;
+
+	loff_t ftp_offset_start;
+
+	time_t mtime;
+	unsigned int etaglen;
+	char etag [40];
+
+	char usermode;
+	unsigned int usermodule_idx;
+	struct dentry *module_dentry;
+	struct vfsmount *module_mnt;
+	char *userbuf;
+	unsigned int userlen;
+
+	tux_attribute_t *attr;
+
+	threadinfo_t *ti;
+	wait_queue_t sleep;
+	wait_queue_t ftp_sleep;
+
+	abuf_t abuf;
+	/*
+	 * Parsed request fields. In-line strings are zero-delimited.
+	 */
+	const char *headers;
+	unsigned int headers_len;
+
+	unsigned int parsed_len;
+
+	// FTP part
+	ftp_command_t ftp_command;
+	u32 ftp_user_addr;
+	u16 ftp_user_port;
+
+	struct socket *data_sock;
+	unsigned int prev_pos;
+
+	// ls handing:
+	struct linux_dirent64 *dirp0;
+	unsigned int curroff, total;
+
+#define MAX_USERNAME_LEN 16
+	char username[MAX_USERNAME_LEN];
+	unsigned int username_len;
+
+	// HTTP part
+	http_method_t method;
+	const char *method_str;
+	unsigned int method_len;
+
+	http_version_t version;
+	const char *version_str;
+	unsigned int version_len;
+
+	/* requested URI: */
+
+	const char *uri_str;
+	unsigned int uri_len;
+
+	/* Objectname (filename/scriptname) this URI refers to: */
+
+#define MAX_OBJECTNAME_LEN 256
+	char objectname[MAX_OBJECTNAME_LEN + 4]; // space for .gz as well
+	unsigned int objectname_len;
+
+	/* Query string within the URI: */
+
+	const char *query_str;
+	unsigned int query_len;
+
+	/* Cookies: */
+
+	const char *cookies_str;
+	unsigned int cookies_len;
+	unsigned int parse_cookies;
+
+	/* Content-TYpe */
+	const char *content_type_str;
+	unsigned int content_type_len;
+
+	/* Content-Length: */
+
+	const char *contentlen_str;
+	unsigned int contentlen_len;
+	unsigned int content_len;
+
+	/* User-Agent: */
+
+	const char *user_agent_str;
+	unsigned int user_agent_len;
+
+	/* Accept: */
+
+	const char *accept_str;
+	unsigned int accept_len;
+
+	/* Accept-Charset: */
+
+	const char *accept_charset_str;
+	unsigned int accept_charset_len;
+
+	/* Accept-Language: */
+
+	const char *accept_language_str;
+	unsigned int accept_language_len;
+
+	/* Cache-Control: */
+
+	const char *cache_control_str;
+	unsigned int cache_control_len;
+
+	/* If-Modified-Since: */
+
+	const char *if_modified_since_str;
+	unsigned int if_modified_since_len;
+
+	/* If-None-Match: */
+	const char *if_none_match_str;
+	unsigned int if_none_match_len;
+
+	/* If-Range: */
+
+	const char *if_range_str;
+	unsigned int if_range_len;
+
+	/* Negotiate: */
+
+	const char *negotiate_str;
+	unsigned int negotiate_len;
+
+	/* Pragma: */
+
+	const char *pragma_str;
+	unsigned int pragma_len;
+
+	/* Referer: */
+
+	const char *referer_str;
+	unsigned int referer_len;
+
+	/* Accept-Encoding: */
+
+	const char *accept_encoding_str;
+	unsigned int accept_encoding_len;
+	unsigned int may_send_gzip;
+	unsigned int content_gzipped;
+
+	/* Host */
+
+#define MAX_HOST_LEN 128
+	char host[MAX_HOST_LEN];
+	unsigned int host_len;
+
+	/* POSTed data: */
+
+	const char *post_data_str;
+	unsigned int post_data_len;
+
+	unsigned int status;
+
+	/* the file being sent */
+
+	unsigned int bytes_sent;
+#ifdef CONFIG_TUX_DEBUG
+	unsigned int bytes_expected;
+#endif
+	unsigned long first_timestamp;
+	unsigned int body_len;
+
+	unsigned int user_error;
+
+	char error;
+	char postponed;
+
+	char had_cachemiss;
+	char lookup_dir;
+	char lookup_404;
+
+	char keep_alive;
+	struct timer_list keepalive_timer;
+	unsigned int total_bytes;
+	struct timer_list output_timer;
+
+	unsigned int nr_keepalives;
+
+	unsigned int event;
+	u64 private;
+
+	unsigned int magic;
+	void (*real_data_ready)(struct sock *sk, int space);
+	void (*real_state_change)(struct sock *sk);
+	void (*real_write_space)(struct sock *sk);
+	void (*real_error_report)(struct sock *sk);
+	void (*real_destruct)(struct sock *sk);
+
+	void (*ftp_real_data_ready)(struct sock *sk, int space);
+	void (*ftp_real_state_change)(struct sock *sk);
+	void (*ftp_real_write_space)(struct sock *sk);
+	void (*ftp_real_error_report)(struct sock *sk);
+	void (*ftp_real_create_child)(struct sock *sk, struct sock *newsk);
+	void (*ftp_real_destruct)(struct sock *sk);
+
+#ifdef CONFIG_TUX_EXTENDED_LOG
+	unsigned long accept_timestamp;
+	unsigned long parse_timestamp;
+	unsigned long output_timestamp;
+	unsigned long flush_timestamp;
+# define SET_TIMESTAMP(x) do { (x) = jiffies; } while (0)
+#else
+# define SET_TIMESTAMP(x) do { } while (0)
+#endif
+
+};
+
+extern void add_tux_atom (tux_req_t *req, atom_func_t *event_done);
+extern void del_tux_atom (tux_req_t *req);
+extern void tux_schedule_atom (tux_req_t *req, int cachemiss);
+extern void add_req_to_workqueue (tux_req_t *req);
+
+
+typedef struct iothread_s
+{
+	spinlock_t async_lock;
+	threadinfo_t *ti;
+	struct list_head async_queue;
+	wait_queue_head_t async_sleep;
+	unsigned int nr_async_pending;
+	unsigned int threads;
+	unsigned int shutdown;
+	wait_queue_head_t wait_shutdown;
+} iothread_t;
+
+typedef struct tux_listen_s
+{
+	tux_proto_t *proto;
+	struct socket *sock;
+	unsigned int cloned;
+} tux_listen_t;
+
+struct tux_threadinfo
+{
+	tux_req_t *userspace_req;
+	unsigned int started;
+	struct task_struct *thread;
+	iothread_t *iot;
+	wait_queue_t wait_event [CONFIG_TUX_NUMSOCKETS];
+	wait_queue_t stop;
+	unsigned int pid;
+
+	struct page *header_cache;
+	unsigned int header_offset;
+
+	unsigned int nr_requests;
+	struct list_head all_requests;
+
+	unsigned int nr_free_requests;
+	spinlock_t free_requests_lock;
+	struct list_head free_requests;
+
+	spinlock_t work_lock;
+	struct list_head work_pending;
+	struct list_head lru;
+	unsigned int nr_lru;
+
+	unsigned int listen_error;
+	tux_listen_t listen[CONFIG_TUX_NUMSOCKETS];
+
+	struct semaphore gzip_sem;
+	z_stream gzip_state;
+
+	unsigned int cpu;
+	unsigned int __padding[16];
+};
+
+typedef enum special_mimetypes {
+	NORMAL_MIME_TYPE,
+	MIME_TYPE_REDIRECT,
+	MIME_TYPE_CGI,
+	MIME_TYPE_MODULE,
+} special_mimetypes_t;
+
+#ifdef CONFIG_TUX_DEBUG
+#if 0
+extern inline void url_hist_hit (int size)
+{
+	unsigned int idx = size/1024;
+
+	if (idx >= URL_HIST_SIZE)
+		idx = URL_HIST_SIZE-1;
+	kstat.url_hist_hits[idx]++;
+}
+extern inline void url_hist_miss (int size)
+{
+	unsigned int idx = size/1024;
+
+	if (idx >= URL_HIST_SIZE)
+		idx = URL_HIST_SIZE-1;
+	kstat.url_hist_misses[idx]++;
+}
+#endif
+extern void __check_req_list (tux_req_t *req, struct list_head *list);
+# define check_req_list __check_req_list
+#else
+# define check_req_list(req, list) do { } while (0)
+#endif
+
+#define url_hist_hit(size) do { } while (0)
+#define url_hist_miss(size) do { } while (0)
+
+extern char tux_common_docroot[200];
+extern char tux_http_subdocroot[200];
+extern char tux_ftp_subdocroot[200];
+extern char tux_logfile[200];
+extern char tux_cgiroot[200];
+extern char tux_404_page[200];
+extern char tux_default_vhost[200];
+extern char tux_extra_html_header[600];
+extern unsigned int tux_extra_html_header_size;
+extern int tux_cgi_uid;
+extern int tux_cgi_gid;
+extern unsigned int tux_clientport;
+extern unsigned int tux_logging;
+extern unsigned int tux_threads;
+extern unsigned int tux_keepalive_timeout;
+extern unsigned int tux_max_output_bandwidth;
+extern unsigned int tux_max_backlog;
+extern unsigned int tux_max_connect;
+extern unsigned int tux_mode_forbidden;
+extern unsigned int tux_mode_allowed;
+extern unsigned int tux_logentry_align_order;
+extern unsigned int tux_nonagle;
+extern unsigned int tux_ack_pingpong;
+extern unsigned int tux_push_all;
+extern unsigned int tux_zerocopy_parse;
+extern unsigned int tux_generate_etags;
+extern unsigned int tux_generate_last_mod;
+extern unsigned int tux_generate_cache_control;
+extern unsigned int tux_ip_logging;
+extern unsigned int tux_ftp_wait_close;
+extern unsigned int tux_ftp_log_retr_only;
+extern unsigned int tux_hide_unreadable;
+
+typedef enum virtual_server {
+	TUX_VHOST_NONE,
+	TUX_VHOST_HOST,
+	TUX_VHOST_IP,
+	TUX_VHOST_IP_HOST,
+} virtual_server_t;
+
+extern unsigned int tux_virtual_server;
+extern unsigned int mass_hosting_hash;
+extern unsigned int strip_host_tail;
+extern unsigned int tux_ftp_virtual_server;
+
+extern unsigned int tux_max_object_size;
+extern unsigned int tux_max_free_requests;
+extern unsigned int tux_defer_accept;
+
+extern struct socket * start_listening(tux_socket_t *listen, int nr);
+extern void stop_listening(struct socket **sock);
+extern void start_sysctl(void);
+extern void end_sysctl(void);
+extern void flush_request (tux_req_t *req, int cachemiss);
+extern void unlink_tux_socket (tux_req_t *req);
+extern void unlink_tux_data_socket (tux_req_t *req);
+extern void unlink_tux_listen_socket (tux_req_t *req);
+extern void link_tux_ftp_accept_socket (tux_req_t *req, struct socket *sock);
+extern void link_tux_data_socket (tux_req_t *req, struct socket *sock);
+extern void tux_push_req (tux_req_t *req);
+extern int send_sync_buf (tux_req_t *req, struct socket *sock, const char *buf, const size_t length, unsigned long flags);
+extern void __send_async_message (tux_req_t *req, const char *message, int status, unsigned int size, int push);
+#define send_async_message(req,str,status,push) \
+		__send_async_message(req,str,status,strlen(str),push)
+
+extern void send_success (tux_req_t *req, struct socket *sock);
+extern void send_async_err_not_found (tux_req_t *req);
+extern void send_async_timed_out (tux_req_t *req);
+
+extern void kfree_req (tux_req_t *req);
+extern int accept_requests (threadinfo_t *ti);
+extern int process_requests (threadinfo_t *ti, tux_req_t **user_req);
+extern int flush_freequeue (threadinfo_t * ti);
+extern int tux_flush_workqueue (threadinfo_t *ti);
+extern tux_req_t * pick_userspace_req (threadinfo_t *ti);
+extern atom_func_t redirect_request;
+extern atom_func_t parse_request;
+extern void queue_cachemiss (tux_req_t *req);
+extern int start_cachemiss_threads (threadinfo_t *ti);
+extern void stop_cachemiss_threads (threadinfo_t *ti);
+struct file * tux_open_file(char *filename, int mode);
+extern void start_log_thread (void);
+extern void stop_log_thread (void);
+extern void add_mimetype (char *new_ext, char *new_type, char *new_expire);
+extern void free_mimetypes (void);
+extern int lookup_object (tux_req_t *req, const unsigned int flag);
+extern int handle_gzip_req (tux_req_t *req, unsigned int flags);
+extern struct dentry * tux_lookup (tux_req_t *req, const char *filename, const unsigned int flag, struct vfsmount **mnt);
+extern tcapi_template_t * lookup_tuxmodule (const char *filename);
+extern int register_tuxmodule (tcapi_template_t *tcapi);
+extern tcapi_template_t * unregister_tuxmodule (char *vfs_name);
+extern tcapi_template_t * get_first_usermodule (void);
+extern int user_register_module (user_req_t *u_info);
+extern int user_unregister_module (user_req_t *u_info);
+extern void unregister_all_tuxmodules (void);
+
+typedef struct exec_param_s {
+	char *command;
+	char **argv;
+	char **envp;
+	unsigned int pipe_fds;
+} exec_param_t;
+
+extern pid_t tux_exec_process (char *command, char **argv, char **envp, int pipe_fds, exec_param_t *param, int wait);
+
+extern void start_external_cgi (tux_req_t *req);
+extern tcapi_template_t extcgi_tcapi;
+
+extern void queue_output_req (tux_req_t *req, threadinfo_t *ti);
+extern void queue_userspace_req (tux_req_t *req, threadinfo_t *ti);
+
+
+extern void __log_request (tux_req_t *req);
+extern inline void log_request (tux_req_t *req)
+{
+	if (tux_logging)
+		__log_request(req);
+}
+
+extern int __connection_too_fast (tux_req_t *req);
+
+#define connection_too_fast(req)				\
+	({							\
+		int __ret = 1;					\
+		if (unlikely(tux_max_output_bandwidth))		\
+			__ret = __connection_too_fast(req);	\
+		__ret;						\
+	})
+
+extern void trunc_headers (tux_req_t *req);
+extern int generic_send_file (tux_req_t *req, struct socket *sock, int cachemiss);
+extern int tux_fetch_file (tux_req_t *req, int nonblock);
+
+extern void postpone_request (tux_req_t *req);
+extern int continue_request (int fd);
+extern void tux_push_pending (struct sock *sk);
+extern void zap_request (tux_req_t *req, int cachemiss);
+extern int add_output_space_event (tux_req_t *req, struct socket *sock);
+
+extern void reap_kids (void);
+extern void unuse_frag (struct sk_buff *skb, skb_frag_t *frag);
+extern skb_frag_t * build_dynbuf_frag (tux_req_t *req, unsigned int size);
+extern int tux_permission (struct inode *inode);
+extern void flush_all_signals (void);
+
+#define D() Dprintk("{%s:%d}\n", __FILE__, __LINE__)
+
+extern int nr_async_io_pending (void);
+
+extern void __add_keepalive_timer (tux_req_t *req);
+#define add_keepalive_timer(req)					\
+do {									\
+	if (tux_keepalive_timeout) {					\
+		Dprintk("add_keepalive_timer(%p).\n", (req));		\
+		__add_keepalive_timer(req);				\
+	}								\
+} while (0)
+extern void __del_keepalive_timer (tux_req_t *req);
+#define del_keepalive_timer(req)					\
+do {									\
+	if (tux_keepalive_timeout) {					\
+		Dprintk("del_keepalive_timer(%p).\n", (req));		\
+		__del_keepalive_timer(req);				\
+	}								\
+} while (0)
+
+extern void del_output_timer (tux_req_t *req);
+extern void output_timeout (tux_req_t *req);
+
+extern void print_req (tux_req_t *req);
+
+extern char tux_date [DATE_LEN];
+
+
+extern int nr_async_io_pending (void);
+extern void tux_exit (void);
+extern char * get_abuf (tux_req_t *req, unsigned int max_size);
+extern void send_abuf (tux_req_t *req, unsigned int size, unsigned long flags);
+
+
+extern int idle_event (tux_req_t *req);
+extern int output_space_event (tux_req_t *req);
+extern cpumask_t tux_log_cpu_mask;
+extern unsigned int tux_compression;
+extern unsigned int tux_noid;
+extern unsigned int tux_cgi_inherit_cpu;
+extern unsigned int tux_zerocopy_header;
+extern unsigned int tux_zerocopy_sendfile;
+extern cpumask_t tux_cgi_cpu_mask;
+extern tux_proto_t tux_proto_http;
+extern tux_proto_t tux_proto_ftp;
+extern unsigned int tux_all_userspace;
+extern unsigned int tux_ignore_query;
+extern unsigned int tux_redirect_logging;
+extern unsigned int tux_referer_logging;
+extern unsigned int tux_log_incomplete;
+extern unsigned int tux_max_header_len;
+extern unsigned int tux_cpu_offset;
+extern unsigned int tux_ftp_login_message;
+
+extern void drop_permissions (void);
+extern int query_extcgi (tux_req_t *req);
+extern int tux_chroot (char *dir);
+
+extern void install_req_dentry (tux_req_t *req, struct dentry *dentry, struct vfsmount *mnt);
+extern void release_req_dentry (tux_req_t *req);
+extern void unidle_req (tux_req_t *req);
+extern int nr_requests_used (void);
+
+#define req_err(req) do { (req)->error = 1; Dprintk("request %p error at %s:%d.\n", req, __FILE__, __LINE__); } while (0)
+
+#define enough_wspace(sk) (sk_stream_wspace(sk) >= sk_stream_min_wspace(sk))
+#define clear_keepalive(req) do { (req)->keep_alive = 0; Dprintk("keepalive cleared for req %p.\n", req); } while (0)
+
+extern int print_all_requests (threadinfo_t *ti);
+extern unsigned int tux_max_keepalives;
+extern int time_unix2ls (time_t zulu, char *buf);
+extern void last_mod_time(char * curr, const time_t t);
+extern int mdtm_time(char * curr, const time_t t);
+extern time_t parse_time(const char *str, const int str_len);
+
+extern unsigned int nr_tux_threads;
+extern threadinfo_t threadinfo[CONFIG_TUX_NUMTHREADS];
+
+#define switch_docroot(req) do { if (((req)->docroot_dentry != current->fs->root) || ((req)->docroot_mnt != current->fs->rootmnt)) __switch_docroot(req); } while (0)
+extern void __switch_docroot(tux_req_t *req);
+extern void list_directory (tux_req_t *req, int cachemiss);
+extern char * tux_print_path (tux_req_t *req, struct dentry *dentry, struct vfsmount *mnt, char *buf, unsigned int max_len);
+
+extern unsigned int tux_http_dir_indexing;
+
+int tux_gzip_compress (tux_req_t *req, unsigned char *data_in, unsigned char *data_out, __u32 *in_len, __u32 *out_len);
+
+struct dentry * __tux_lookup (tux_req_t *req, const char *filename,
+                         struct nameidata *base, struct vfsmount **mnt);
+
+/* error codes for req->error */
+#define TUX_ERROR_REDIRECT     1
+#define TUX_ERROR_UNUSED       2
+#define TUX_ERROR_CONN_CLOSE   3
+#define TUX_ERROR_CONN_TIMEOUT 4
+
+extern void __put_data_sock (tux_req_t *req);
+
+static inline void put_data_sock (tux_req_t *req)
+{
+	if (req->data_sock)
+		__put_data_sock(req);
+}
+
+#define socket_input(sock) \
+	(!skb_queue_empty(&(sock)->sk->sk_receive_queue) || \
+		!skb_queue_empty(&(sock)->sk->sk_error_queue))
+
+#define tux_kmalloc(size)						\
+({									\
+	void *__ptr;							\
+									\
+	while (!(__ptr = kmalloc(size, GFP_KERNEL))) {			\
+		if (net_ratelimit())					\
+			printk(KERN_WARNING "tux: OOM at %s:%d (%d bytes).\n", \
+				__FILE__, __LINE__, size);		\
+		current->state = TASK_UNINTERRUPTIBLE;			\
+		schedule_timeout(1);					\
+	}								\
+	__ptr;								\
+})
+
+#define tux_close(fd) sys_close(fd)
+
+extern int init_tux_request_slabs(void);
+extern void free_tux_request_slabs(void);
+
+#endif
Index: latest/include/net/tux_u.h
===================================================================
--- /dev/null
+++ latest/include/net/tux_u.h
@@ -0,0 +1,163 @@
+#ifndef _NET_TUX_U_H
+#define _NET_TUX_U_H
+
+/*
+ * TUX - Integrated Application Protocols Layer and Object Cache
+ *
+ * Copyright (C) 2000, 2001, Ingo Molnar <mingo@redhat.com>
+ *
+ * tux_u.h: HTTP module API - HTTP interface to user-space
+ */
+
+/*
+ * Different major versions are not compatible.
+ * Different minor versions are only downward compatible.
+ * Different patchlevel versions are downward and upward compatible.
+ */
+#define TUX_MAJOR_VERSION		3
+#define TUX_MINOR_VERSION		0
+#define TUX_PATCHLEVEL_VERSION		0
+
+#define __KERNEL_SYSCALLS__
+
+typedef enum http_versions {
+        HTTP_1_0,
+        HTTP_1_1
+} http_version_t;
+
+/*
+ * Request methods known to HTTP:
+ */
+typedef enum http_methods {
+        METHOD_NONE,
+        METHOD_GET,
+        METHOD_HEAD,
+        METHOD_POST,
+        METHOD_PUT,
+	NR_METHODS
+} http_method_t;
+
+enum user_req {
+	TUX_ACTION_STARTUP = 1,
+	TUX_ACTION_SHUTDOWN = 2,
+	TUX_ACTION_STARTTHREAD = 3,
+	TUX_ACTION_STOPTHREAD = 4,
+	TUX_ACTION_EVENTLOOP = 5,
+	TUX_ACTION_GET_OBJECT = 6,
+	TUX_ACTION_SEND_OBJECT = 7,
+	TUX_ACTION_READ_OBJECT = 8,
+	TUX_ACTION_FINISH_REQ = 9,
+	TUX_ACTION_FINISH_CLOSE_REQ = 10,
+	TUX_ACTION_REGISTER_MODULE = 11,
+	TUX_ACTION_UNREGISTER_MODULE = 12,
+	TUX_ACTION_CURRENT_DATE = 13,
+	TUX_ACTION_REGISTER_MIMETYPE = 14,
+	TUX_ACTION_READ_HEADERS = 15,
+	TUX_ACTION_POSTPONE_REQ = 16,
+	TUX_ACTION_CONTINUE_REQ = 17,
+	TUX_ACTION_REDIRECT_REQ = 18,
+	TUX_ACTION_READ_POST_DATA = 19,
+	TUX_ACTION_SEND_BUFFER = 20,
+	TUX_ACTION_WATCH_PROXY_SOCKET = 21,
+	TUX_ACTION_WAIT_PROXY_SOCKET = 22,
+	TUX_ACTION_QUERY_VERSION = 23,
+	MAX_TUX_ACTION
+};
+
+enum tux_ret {
+	TUX_ERROR = -1,
+	TUX_RETURN_USERSPACE_REQUEST = 0,
+	TUX_RETURN_EXIT = 1,
+	TUX_RETURN_SIGNAL = 2,
+	TUX_CONTINUE_EVENTLOOP = 3,
+};
+
+#define MAX_URI_LEN 256
+#define MAX_COOKIE_LEN 128
+#define MAX_FIELD_LEN 64
+#define DATE_LEN 30
+
+typedef struct user_req_s {
+	u32 version_major;
+	u32 version_minor;
+	u32 version_patch;
+	u32 http_version;
+	u32 http_method;
+	u32 http_status;
+
+	u32 sock;
+	u32 event;
+	u32 error;
+	u32 thread_nr;
+	u32 bytes_sent;
+	u32 client_host;
+	u32 objectlen;
+	u32 module_index;
+	u32 keep_alive;
+	u32 cookies_len;
+
+	u64 id;
+	u64 priv;
+	u64 object_addr;
+
+	u8 query[MAX_URI_LEN];
+	u8 objectname[MAX_URI_LEN];
+	u8 cookies[MAX_COOKIE_LEN];
+	u8 content_type[MAX_FIELD_LEN];
+	u8 user_agent[MAX_FIELD_LEN];
+	u8 accept[MAX_FIELD_LEN];
+	u8 accept_charset[MAX_FIELD_LEN];
+	u8 accept_encoding[MAX_FIELD_LEN];
+	u8 accept_language[MAX_FIELD_LEN];
+	u8 cache_control[MAX_FIELD_LEN];
+	u8 if_modified_since[MAX_FIELD_LEN];
+	u8 negotiate[MAX_FIELD_LEN];
+	u8 pragma[MAX_FIELD_LEN];
+	u8 referer[MAX_FIELD_LEN];
+	u8 new_date[DATE_LEN];
+	u8 pad[2];
+
+} user_req_t;
+
+typedef enum ftp_commands {
+        FTP_COMM_NONE,
+        FTP_COMM_USER,
+        FTP_COMM_PASS,
+        FTP_COMM_ACCT,
+        FTP_COMM_CWD,
+        FTP_COMM_CDUP,
+        FTP_COMM_SMNT,
+        FTP_COMM_QUIT,
+        FTP_COMM_REIN,
+        FTP_COMM_PORT,
+        FTP_COMM_PASV,
+        FTP_COMM_TYPE,
+        FTP_COMM_STRU,
+        FTP_COMM_MODE,
+        FTP_COMM_RETR,
+        FTP_COMM_SIZE,
+        FTP_COMM_MDTM,
+        FTP_COMM_STOR,
+        FTP_COMM_STOU,
+        FTP_COMM_APPE,
+        FTP_COMM_ALLO,
+        FTP_COMM_REST,
+        FTP_COMM_RNFR,
+        FTP_COMM_RNTO,
+        FTP_COMM_ABOR,
+        FTP_COMM_DELE,
+        FTP_COMM_RMD,
+        FTP_COMM_MKD,
+        FTP_COMM_PWD,
+        FTP_COMM_LIST,
+        FTP_COMM_NLST,
+        FTP_COMM_SITE,
+        FTP_COMM_SYST,
+        FTP_COMM_STAT,
+        FTP_COMM_HELP,
+        FTP_COMM_NOOP,
+        FTP_COMM_FEAT,
+        FTP_COMM_CLNT,
+} ftp_command_t;
+
+#endif
Index: latest/kernel/exit.c
===================================================================
--- latest.orig/kernel/exit.c
+++ latest/kernel/exit.c
@@ -811,6 +811,15 @@ fastcall NORET_TYPE void do_exit(long co
  		hrtimer_cancel(&tsk->signal->real_timer);
 		exit_itimers(tsk->signal);
 	}
+
+	if (current->tux_info) {
+#ifdef CONFIG_TUX_DEBUG
+		printk("Possibly unexpected TUX-thread exit(%ld) at %p?\n",
+			code, __builtin_return_address(0));
+#endif
+		current->tux_exit();
+	}
+
 	acct_collect(code, group_dead);
 	if (unlikely(tsk->robust_list))
 		exit_robust_list(tsk);
Index: latest/kernel/fork.c
===================================================================
--- latest.orig/kernel/fork.c
+++ latest/kernel/fork.c
@@ -974,6 +974,8 @@ static struct task_struct *copy_process(
 	if (!p)
 		goto fork_out;
 
+	p->tux_info = NULL;
+
 #ifdef CONFIG_TRACE_IRQFLAGS
 	DEBUG_LOCKS_WARN_ON(!p->hardirqs_enabled);
 	DEBUG_LOCKS_WARN_ON(!p->softirqs_enabled);
Index: latest/kernel/kmod.c
===================================================================
--- latest.orig/kernel/kmod.c
+++ latest/kernel/kmod.c
@@ -127,14 +127,14 @@ struct subprocess_info {
 /*
  * This is the task which runs the usermode application
  */
-static int ____call_usermodehelper(void *data)
+int
+__exec_usermodehelper(char *path, char **argv, char **envp, struct key *ring)
 {
-	struct subprocess_info *sub_info = data;
 	struct key *new_session, *old_session;
 	int retval;
 
 	/* Unblock all signals and set the session keyring. */
-	new_session = key_get(sub_info->ring);
+	new_session = key_get(ring);
 	flush_signals(current);
 	spin_lock_irq(&current->sighand->siglock);
 	old_session = __install_session_keyring(current, new_session);
@@ -145,12 +145,28 @@ static int ____call_usermodehelper(void 
 
 	key_put(old_session);
 
+	retval = -EPERM;
+	if (current->fs->root)
+		retval = execve(path, argv, envp);
+
+	return retval;
+}
+
+EXPORT_SYMBOL_GPL(__exec_usermodehelper);
+
+/*
+ * This is the task which runs the usermode application
+ */
+static int ____call_usermodehelper(void *data)
+{
+	struct subprocess_info *sub_info = data;
+	int retval;
+
 	/* We can run anywhere, unlike our parent keventd(). */
 	set_cpus_allowed(current, CPU_MASK_ALL);
 
-	retval = -EPERM;
-	if (current->fs->root)
-		retval = execve(sub_info->path, sub_info->argv,sub_info->envp);
+	retval = __exec_usermodehelper(sub_info->path,
+			sub_info->argv, sub_info->envp, sub_info->ring);
 
 	/* Exec failed? */
 	sub_info->retval = retval;
Index: latest/kernel/signal.c
===================================================================
--- latest.orig/kernel/signal.c
+++ latest/kernel/signal.c
@@ -335,6 +335,7 @@ flush_signal_handlers(struct task_struct
 	}
 }
 
+EXPORT_SYMBOL_GPL(flush_signal_handlers);
 
 /* Notify the system that a driver wants to block all signals for this
  * process, and wants to be notified if any signals at all were to be
Index: latest/mm/filemap.c
===================================================================
--- latest.orig/mm/filemap.c
+++ latest/mm/filemap.c
@@ -874,7 +874,8 @@ void do_generic_mapping_read(struct addr
 			     struct file *filp,
 			     loff_t *ppos,
 			     read_descriptor_t *desc,
-			     read_actor_t actor)
+			     read_actor_t actor,
+			     int nonblock)
 {
 	struct inode *inode = mapping->host;
 	unsigned long index;
@@ -924,11 +925,21 @@ void do_generic_mapping_read(struct addr
 find_page:
 		page = find_get_page(mapping, index);
 		if (unlikely(page == NULL)) {
+			if (nonblock) {
+				desc->error = -EWOULDBLOCKIO;
+				break;
+			}
 			handle_ra_miss(mapping, &ra, index);
 			goto no_cached_page;
 		}
-		if (!PageUptodate(page))
+		if (!PageUptodate(page)) {
+			if (nonblock) {
+				page_cache_release(page);
+				desc->error = -EWOULDBLOCKIO;
+				break;
+			}
 			goto page_not_up_to_date;
+		}
 page_ok:
 
 		/* If users can be writing to this page using arbitrary
@@ -1195,7 +1206,7 @@ __generic_file_aio_read(struct kiocb *io
 			if (desc.count == 0)
 				continue;
 			desc.error = 0;
-			do_generic_file_read(filp,ppos,&desc,file_read_actor);
+			do_generic_file_read(filp,ppos,&desc,file_read_actor,0);
 			retval += desc.written;
 			if (desc.error) {
 				retval = retval ?: desc.error;
@@ -1266,7 +1277,7 @@ ssize_t generic_file_sendfile(struct fil
 	desc.arg.data = target;
 	desc.error = 0;
 
-	do_generic_file_read(in_file, ppos, &desc, actor);
+	do_generic_file_read(in_file, ppos, &desc, actor, 0);
 	if (desc.written)
 		return desc.written;
 	return desc.error;
Index: latest/mm/truncate.c
===================================================================
--- latest.orig/mm/truncate.c
+++ latest/mm/truncate.c
@@ -266,6 +266,8 @@ unlock:
 	return ret;
 }
 
+EXPORT_SYMBOL_GPL(invalidate_mapping_pages);
+
 unsigned long invalidate_inode_pages(struct address_space *mapping)
 {
 	return invalidate_mapping_pages(mapping, 0, ~0UL);
Index: latest/net/core/sock.c
===================================================================
--- latest.orig/net/core/sock.c
+++ latest/net/core/sock.c
@@ -904,7 +904,7 @@ void sk_free(struct sock *sk)
 	module_put(owner);
 }
 
-struct sock *sk_clone(const struct sock *sk, const gfp_t priority)
+struct sock *sk_clone(struct sock *sk, const gfp_t priority)
 {
 	struct sock *newsk = sk_alloc(sk->sk_family, priority, sk->sk_prot, 0);
 
@@ -946,6 +946,9 @@ struct sock *sk_clone(const struct sock 
 		if (filter != NULL)
 			sk_filter_charge(newsk, filter);
 
+		if (sk->sk_create_child)
+			sk->sk_create_child(sk, newsk);
+
 		if (unlikely(xfrm_sk_clone_policy(newsk))) {
 			/* It is still raw copy of parent, so invalidate
 			 * destructor and make plain sk_free() */
Index: latest/net/ipv4/tcp.c
===================================================================
--- latest.orig/net/ipv4/tcp.c
+++ latest/net/ipv4/tcp.c
@@ -2349,3 +2349,4 @@ EXPORT_SYMBOL(tcp_sendpage);
 EXPORT_SYMBOL(tcp_setsockopt);
 EXPORT_SYMBOL(tcp_shutdown);
 EXPORT_SYMBOL(tcp_statistics);
+EXPORT_SYMBOL_GPL(tcp_cleanup_rbuf);
Index: latest/net/ipv4/tcp_input.c
===================================================================
--- latest.orig/net/ipv4/tcp_input.c
+++ latest/net/ipv4/tcp_input.c
@@ -3534,6 +3534,7 @@ static int tcp_prune_queue(struct sock *
 	return -1;
 }
 
+EXPORT_SYMBOL_GPL(tcp_cwnd_application_limited);
 
 /* RFC2861, slow part. Adjust cwnd, after it was not full during one rto.
  * As additional protections, we do not touch cwnd in retransmission phases,
Index: latest/net/ipv4/tcp_output.c
===================================================================
--- latest.orig/net/ipv4/tcp_output.c
+++ latest/net/ipv4/tcp_output.c
@@ -858,6 +858,8 @@ unsigned int tcp_current_mss(struct sock
 	return mss_now;
 }
 
+EXPORT_SYMBOL_GPL(tcp_current_mss);
+
 /* Congestion window validation. (RFC2861) */
 
 static void tcp_cwnd_validate(struct sock *sk, struct tcp_sock *tp)
@@ -1371,6 +1373,7 @@ void __tcp_push_pending_frames(struct so
 			tcp_check_probe_timer(sk, tp);
 	}
 }
+EXPORT_SYMBOL_GPL(__tcp_push_pending_frames);
 
 /* Send _single_ skb sitting at the send head. This function requires
  * true push pending frames to setup probe timer etc.
Index: latest/net/Kconfig
===================================================================
--- latest.orig/net/Kconfig
+++ latest/net/Kconfig
@@ -245,6 +245,7 @@ source "net/ax25/Kconfig"
 source "net/irda/Kconfig"
 source "net/bluetooth/Kconfig"
 source "net/ieee80211/Kconfig"
+source "net/tux/Kconfig"
 
 config WIRELESS_EXT
 	bool
Index: latest/net/Makefile
===================================================================
--- latest.orig/net/Makefile
+++ latest/net/Makefile
@@ -22,6 +22,7 @@ obj-$(CONFIG_UNIX)		+= unix/
 ifneq ($(CONFIG_IPV6),)
 obj-y				+= ipv6/
 endif
+obj-$(CONFIG_TUX)		+= tux/
 obj-$(CONFIG_PACKET)		+= packet/
 obj-$(CONFIG_NET_KEY)		+= key/
 obj-$(CONFIG_NET_SCHED)		+= sched/
Index: latest/net/socket.c
===================================================================
--- latest.orig/net/socket.c
+++ latest/net/socket.c
@@ -67,6 +67,7 @@
 #include <linux/netdevice.h>
 #include <linux/proc_fs.h>
 #include <linux/seq_file.h>
+#include <net/tux.h>
 #include <linux/mutex.h>
 #include <linux/wanrouter.h>
 #include <linux/if_bridge.h>
@@ -123,7 +124,7 @@ static ssize_t sock_sendpage(struct file
  *	in the operation structures but are done directly via the socketcall() multiplexor.
  */
 
-static struct file_operations socket_file_ops = {
+struct file_operations socket_file_ops = {
 	.owner =	THIS_MODULE,
 	.llseek =	no_llseek,
 	.aio_read =	sock_aio_read,
@@ -511,7 +512,7 @@ static struct socket *sockfd_lookup_ligh
  *	NULL is returned.
  */
 
-static struct socket *sock_alloc(void)
+struct socket *sock_alloc(void)
 {
 	struct inode * inode;
 	struct socket * sock;
@@ -531,6 +532,8 @@ static struct socket *sock_alloc(void)
 	return sock;
 }
 
+EXPORT_SYMBOL_GPL(sock_alloc);
+
 /*
  *	In theory you can't get an open on this inode, but /proc provides
  *	a back door. Remember to keep it shut otherwise you'll let the
@@ -1090,6 +1093,8 @@ static int sock_fasync(int fd, struct fi
 	}
 
 out:
+	if (sock->sk != sk)
+		BUG();
 	release_sock(sock->sk);
 	return 0;
 }
@@ -2130,6 +2135,51 @@ static int __init sock_init(void)
 
 core_initcall(sock_init);	/* early initcall */
 
+int tux_Dprintk;
+int tux_TDprintk;
+
+struct module *tux_module = NULL;
+
+#ifdef CONFIG_TUX_MODULE
+
+asmlinkage long (*sys_tux_ptr) (unsigned int action, user_req_t *u_info) = NULL;
+spinlock_t tux_module_lock = SPIN_LOCK_UNLOCKED;
+
+asmlinkage long sys_tux (unsigned int action, user_req_t *u_info)
+{
+	int ret;
+
+	if (current->tux_info)
+		return sys_tux_ptr(action, u_info);
+
+	ret = -ENOSYS;
+	spin_lock(&tux_module_lock);
+	if (!tux_module)
+		goto out_unlock;
+	if (!try_module_get(tux_module))
+		goto out_unlock;
+	spin_unlock(&tux_module_lock);
+
+	if (!sys_tux_ptr)
+		TUX_BUG();
+	ret = sys_tux_ptr(action, u_info);
+
+	spin_lock(&tux_module_lock);
+	module_put(tux_module);
+out_unlock:
+	spin_unlock(&tux_module_lock);
+
+	return ret;
+}
+
+EXPORT_SYMBOL_GPL(tux_module);
+EXPORT_SYMBOL_GPL(tux_module_lock);
+EXPORT_SYMBOL_GPL(sys_tux_ptr);
+
+EXPORT_SYMBOL_GPL(tux_Dprintk);
+EXPORT_SYMBOL_GPL(tux_TDprintk);
+
+#endif
 #ifdef CONFIG_PROC_FS
 void socket_seq_show(struct seq_file *seq)
 {
Index: latest/net/tux/abuf.c
===================================================================
--- /dev/null
+++ latest/net/tux/abuf.c
@@ -0,0 +1,190 @@
+/*
+ * TUX - Integrated Application Protocols Layer and Object Cache
+ *
+ * Copyright (C) 2000, 2001, Ingo Molnar <mingo@redhat.com>
+ *
+ * abuf.c: async buffer-sending
+ */
+
+#include <net/tux.h>
+
+/****************************************************************
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2, or (at your option)
+ *      any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ ****************************************************************/
+
+char * get_abuf (tux_req_t *req, unsigned int max_size)
+{
+	threadinfo_t *ti = req->ti;
+	struct page *page;
+	char *buf;
+	unsigned int offset;
+	unsigned int left;
+
+	if (req->abuf.page || req->abuf.buf || req->abuf.size)
+		TUX_BUG();
+
+	if (max_size > PAGE_SIZE)
+		BUG();
+	offset = ti->header_offset;
+	if (offset > PAGE_SIZE)
+		TUX_BUG();
+	left = PAGE_SIZE - offset;
+	if (!max_size)
+		BUG();
+	page = ti->header_cache;
+	if ((left < max_size) || !page) {
+		while (!(page = alloc_pages(GFP_KERNEL, 0))) {
+			if (net_ratelimit())
+				printk(KERN_WARNING "tux: OOM in get_abuf()!\n");
+			current->state = TASK_UNINTERRUPTIBLE;
+			schedule_timeout(1);
+		}
+
+		if (ti->header_cache)
+			__free_page(ti->header_cache);
+		ti->header_cache = page;
+		ti->header_offset = 0;
+		offset = 0;
+	}
+	buf = page_address(page) + offset;
+
+	if (!page)
+		BUG();
+	req->abuf.page = page;
+	req->abuf.buf = buf;
+	req->abuf.size = 0;
+	req->abuf.offset = offset;
+	req->abuf.flags = 0;
+	get_page(req->abuf.page);
+
+	return buf;
+}
+
+static void do_send_abuf (tux_req_t *req, int cachemiss);
+
+void send_abuf (tux_req_t *req, unsigned int size, unsigned long flags)
+{
+	threadinfo_t *ti = req->ti;
+
+	Dprintk("send_abuf(req: %p, sock: %p): %p(%p), size:%d, off:%d, flags:%08lx\n", req, req->sock, req->abuf.page, req->abuf.buf, size, req->abuf.offset, flags);
+
+	ti->header_offset += size;
+	if (ti->header_offset > PAGE_SIZE)
+		TUX_BUG();
+	if (req->abuf.offset + req->abuf.size > PAGE_SIZE)
+		TUX_BUG();
+
+	req->abuf.flags = flags | MSG_NOSIGNAL;
+	req->abuf.size = size;
+
+	add_tux_atom(req, do_send_abuf);
+}
+
+static void do_send_abuf (tux_req_t *req, int cachemiss)
+{
+	int ret;
+
+	if (req->magic != TUX_MAGIC)
+		TUX_BUG();
+	if (!req->sock)
+		TUX_BUG();
+	tcp_sk(req->sock->sk)->nonagle = 2;
+
+repeat:
+	Dprintk("do_send_abuf(%p,%d): %p(%p), size:%d, off:%d, flags:%08lx\n",
+			req, cachemiss,
+			req->abuf.page, req->abuf.buf, req->abuf.size,
+			req->abuf.offset, req->abuf.flags);
+
+	if (tux_zerocopy_header)
+		ret = tcp_sendpage(req->sock, req->abuf.page,
+			req->abuf.offset, req->abuf.size, req->abuf.flags);
+	else {
+		mm_segment_t oldmm;
+		oldmm = get_fs(); set_fs(KERNEL_DS);
+		ret = send_sync_buf(req, req->sock, req->abuf.buf,
+			req->abuf.size, req->abuf.flags);
+		set_fs(oldmm);
+	}
+
+
+	Dprintk("do_send_abuf: ret: %d\n", ret);
+	if (!ret)
+		TUX_BUG();
+
+	if (ret < 0) {
+		if (ret != -EAGAIN) {
+			TDprintk("ret: %d, req->error = TUX_ERROR_CONN_CLOSE.\n", ret);
+			req->error = TUX_ERROR_CONN_CLOSE;
+			req->atom_idx = 0;
+			req->in_file->f_pos = 0;
+			__free_page(req->abuf.page);
+			memset(&req->abuf, 0, sizeof(req->abuf));
+			zap_request(req, cachemiss);
+			return;
+		}
+		add_tux_atom(req, do_send_abuf);
+		if (add_output_space_event(req, req->sock)) {
+			del_tux_atom(req);
+			goto repeat;
+		}
+		return;
+	}
+
+	req->abuf.buf += ret;
+	req->abuf.offset += ret;
+	req->abuf.size -= ret;
+
+	if ((int)req->abuf.size < 0)
+		TUX_BUG();
+	if (req->abuf.size > 0)
+		goto repeat;
+
+	Dprintk("DONE do_send_abuf: %p(%p), size:%d, off:%d, flags:%08lx\n",
+			req->abuf.page, req->abuf.buf, req->abuf.size,
+			req->abuf.offset, req->abuf.flags);
+
+	if (req->abuf.page)
+		__free_page(req->abuf.page);
+	else
+		if (printk_ratelimit())
+			WARN_ON(1);
+
+	memset(&req->abuf, 0, sizeof(req->abuf));
+
+	add_req_to_workqueue(req);
+}
+
+void __send_async_message (tux_req_t *req, const char *message,
+				int status, unsigned int size, int push)
+{
+	unsigned int flags;
+	char *buf;
+
+	Dprintk("TUX: sending %d reply (%d bytes)!\n", status, size);
+	Dprintk("request %p, reply: %s\n", req, message);
+	if (!size)
+		TUX_BUG();
+	buf = get_abuf(req, size);
+	memcpy(buf, message, size);
+
+	req->status = status;
+	flags = MSG_DONTWAIT;
+	if (!push)
+		flags |= MSG_MORE;
+	send_abuf(req, size, flags);
+	add_req_to_workqueue(req);
+}
Index: latest/net/tux/accept.c
===================================================================
--- /dev/null
+++ latest/net/tux/accept.c
@@ -0,0 +1,863 @@
+/*
+ * TUX - Integrated Application Protocols Layer and Object Cache
+ *
+ * Copyright (C) 2000, 2001, Ingo Molnar <mingo@redhat.com>
+ *
+ * accept.c: accept new connections, allocate requests
+ */
+
+#include <net/tux.h>
+
+/****************************************************************
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2, or (at your option)
+ *      any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ ****************************************************************/
+
+unsigned int tux_ack_pingpong = 1;
+unsigned int tux_push_all = 0;
+unsigned int tux_zerocopy_parse = 1;
+
+static int __idle_event (tux_req_t *req);
+static int __output_space_event (tux_req_t *req);
+
+struct socket * start_listening(tux_socket_t *listen, int nr)
+{
+	struct sockaddr_in sin;
+	struct socket *sock = NULL;
+	struct sock *sk;
+	struct tcp_sock *tp;
+	struct inet_connection_sock *icsk;
+	int err;
+	u16 port = listen->port;
+	u32 addr = listen->ip;
+	tux_proto_t *proto = listen->proto;
+
+	/* Create a listening socket: */
+
+	err = sock_create_kern(PF_INET, SOCK_STREAM, IPPROTO_TCP, &sock);
+	if (err) {
+		printk(KERN_ERR "TUX: error %d creating socket.\n", err);
+		goto error;
+	}
+
+	/* Bind the socket: */
+
+	sin.sin_family = AF_INET;
+	sin.sin_addr.s_addr = htonl(addr);
+	sin.sin_port = htons(port);
+
+	sk = sock->sk;
+	icsk = inet_csk(sk);
+	sk->sk_reuse = 1;
+	sock_set_flag(sk, SOCK_URGINLINE);
+
+	err = sock->ops->bind(sock, (struct sockaddr*)&sin, sizeof(sin));
+	if (err) {
+		printk(KERN_ERR "TUX: error %d binding socket. This means that probably some other process is (or was a short time ago) using addr %s://%d.%d.%d.%d:%d.\n",
+			err, proto->name, HIPQUAD(addr), port);
+		goto error;
+	}
+
+	tp = tcp_sk(sk);
+	Dprintk("listen sk accept_queue: %d.\n",
+		!reqsk_queue_empty(&icsk->icsk_accept_queue));
+	icsk->icsk_ack.pingpong = tux_ack_pingpong;
+
+	sock_reset_flag(sk, SOCK_LINGER);
+	sk->sk_lingertime = 0;
+	tp->linger2 = tux_keepalive_timeout * HZ;
+
+	if (proto->defer_accept && !tux_keepalive_timeout && tux_defer_accept)
+		icsk->icsk_accept_queue.rskq_defer_accept = 1;
+
+	/* Now, start listening on the socket */
+
+	err = sock->ops->listen(sock, tux_max_backlog);
+	if (err) {
+		printk(KERN_ERR "TUX: error %d listening on socket.\n", err);
+		goto error;
+	}
+
+	printk(KERN_NOTICE "TUX: thread %d listens on %s://%d.%d.%d.%d:%d.\n",
+		nr, proto->name, HIPQUAD(addr), port);
+	return sock;
+
+error:
+	if (sock)
+		sock_release(sock);
+	return NULL;
+}
+
+static inline void __kfree_req (tux_req_t *req, threadinfo_t * ti)
+{
+	list_del(&req->all);
+	DEBUG_DEL_LIST(&req->all);
+	ti->nr_requests--;
+	kfree(req);
+}
+
+int flush_freequeue (threadinfo_t * ti)
+{
+	struct list_head *tmp;
+	unsigned long flags;
+	tux_req_t *req;
+	int count = 0;
+
+	spin_lock_irqsave(&ti->free_requests_lock,flags);
+	while (ti->nr_free_requests) {
+		ti->nr_free_requests--;
+		tmp = ti->free_requests.next;
+		req = list_entry(tmp, tux_req_t, free);
+		list_del(tmp);
+		DEBUG_DEL_LIST(tmp);
+		DEC_STAT(nr_free_pending);
+		__kfree_req(req, ti);
+		count++;
+	}
+	spin_unlock_irqrestore(&ti->free_requests_lock,flags);
+
+	return count;
+}
+
+static tux_req_t * kmalloc_req (threadinfo_t * ti)
+{
+	struct list_head *tmp;
+	unsigned long flags;
+	tux_req_t *req;
+
+	spin_lock_irqsave(&ti->free_requests_lock, flags);
+	if (ti->nr_free_requests) {
+		ti->nr_free_requests--;
+		tmp = ti->free_requests.next;
+		req = list_entry(tmp, tux_req_t, free);
+		list_del(tmp);
+		DEBUG_DEL_LIST(tmp);
+		DEC_STAT(nr_free_pending);
+		req->magic = TUX_MAGIC;
+		spin_unlock_irqrestore(&ti->free_requests_lock, flags);
+	} else {
+		spin_unlock_irqrestore(&ti->free_requests_lock, flags);
+		req = tux_kmalloc(sizeof(*req));
+		ti->nr_requests++;
+		memset (req, 0, sizeof(*req));
+		list_add(&req->all, &ti->all_requests);
+	}
+	req->magic = TUX_MAGIC;
+	INC_STAT(nr_allocated);
+	init_waitqueue_entry(&req->sleep, current);
+	init_waitqueue_entry(&req->ftp_sleep, current);
+	INIT_LIST_HEAD(&req->work);
+	INIT_LIST_HEAD(&req->free);
+	INIT_LIST_HEAD(&req->lru);
+	req->ti = ti;
+	req->total_bytes = 0;
+	SET_TIMESTAMP(req->accept_timestamp);
+	req->first_timestamp = jiffies;
+	req->fd = -1;
+	init_timer(&req->keepalive_timer);
+	init_timer(&req->output_timer);
+
+	Dprintk("allocated NEW req %p.\n", req);
+	return req;
+}
+
+void kfree_req (tux_req_t *req)
+{
+	threadinfo_t * ti = req->ti;
+	unsigned long flags;
+
+	Dprintk("freeing req %p.\n", req);
+
+	if (req->magic != TUX_MAGIC)
+		TUX_BUG();
+	spin_lock_irqsave(&ti->free_requests_lock,flags);
+	req->magic = 0;
+	DEC_STAT(nr_allocated);
+	if (req->sock || req->dentry || req->private)
+		TUX_BUG();
+	if (ti->nr_free_requests > tux_max_free_requests)
+		__kfree_req(req, ti);
+	else {
+		req->error = 0;
+		ti->nr_free_requests++;
+
+		// the free requests queue is LIFO
+		list_add(&req->free, &ti->free_requests);
+		INC_STAT(nr_free_pending);
+	}
+	spin_unlock_irqrestore(&ti->free_requests_lock,flags);
+}
+
+static void __add_req_to_workqueue (tux_req_t *req)
+{
+	threadinfo_t *ti = req->ti;
+
+	if (!list_empty(&req->work))
+		TUX_BUG();
+	Dprintk("work-queueing request %p at %p/%p.\n", req, __builtin_return_address(0), __builtin_return_address(1));
+	if (connection_too_fast(req))
+		list_add_tail(&req->work, &ti->work_pending);
+	else
+		list_add(&req->work, &ti->work_pending);
+	INC_STAT(nr_work_pending);
+	wake_up_process(ti->thread);
+	return;
+}
+
+void add_req_to_workqueue (tux_req_t *req)
+{
+	unsigned long flags;
+	threadinfo_t *ti = req->ti;
+
+	spin_lock_irqsave(&ti->work_lock, flags);
+	__add_req_to_workqueue(req);
+	spin_unlock_irqrestore(&ti->work_lock, flags);
+}
+
+void del_output_timer (tux_req_t *req)
+{
+#ifdef CONFIG_SMP
+	if (!spin_is_locked(&req->ti->work_lock))
+		TUX_BUG();
+#endif
+	if (!list_empty(&req->lru)) {
+		list_del(&req->lru);
+		DEBUG_DEL_LIST(&req->lru);
+		req->ti->nr_lru--;
+	}
+	Dprintk("del output timeout for req %p.\n", req);
+	del_timer(&req->output_timer);
+}
+
+static void output_timeout_fn (unsigned long data);
+
+#define OUTPUT_TIMEOUT HZ
+
+static void add_output_timer (tux_req_t *req)
+{
+	struct timer_list *timer = &req->output_timer;
+
+	timer->data = (unsigned long) req;
+	timer->function = &output_timeout_fn;
+	mod_timer(timer, jiffies + OUTPUT_TIMEOUT);
+}
+
+static void output_timeout_fn (unsigned long data)
+{
+	tux_req_t *req = (tux_req_t *)data;
+
+	if (connection_too_fast(req)) {
+		add_output_timer(req);
+//		mod_timer(&req->output_timer, jiffies + OUTPUT_TIMEOUT);
+		return;
+	}
+	output_space_event(req);
+}
+
+void output_timeout (tux_req_t *req)
+{
+	Dprintk("output timeout for req %p.\n", req);
+	if (test_and_set_bit(0, &req->wait_output_space))
+		TUX_BUG();
+	INC_STAT(nr_output_space_pending);
+	add_output_timer(req);
+}
+
+void __del_keepalive_timer (tux_req_t *req)
+{
+#ifdef CONFIG_SMP
+	if (!spin_is_locked(&req->ti->work_lock))
+		TUX_BUG();
+#endif
+	if (!list_empty(&req->lru)) {
+		list_del(&req->lru);
+		DEBUG_DEL_LIST(&req->lru);
+		req->ti->nr_lru--;
+	}
+	Dprintk("del keepalive timeout for req %p.\n", req);
+	del_timer(&req->keepalive_timer);
+}
+
+static void keepalive_timeout_fn (unsigned long data)
+{
+	tux_req_t *req = (tux_req_t *)data;
+
+#ifdef CONFIG_TUX_DEBUG
+	Dprintk("req %p timed out after %d sec!\n", req, tux_keepalive_timeout);
+	if (tux_Dprintk)
+		print_req(req);
+#endif
+	Dprintk("req->error = TUX_ERROR_CONN_TIMEOUT!\n");
+	req->error = TUX_ERROR_CONN_TIMEOUT;
+	if (!idle_event(req))
+		output_space_event(req);
+}
+
+void __add_keepalive_timer (tux_req_t *req)
+{
+	struct timer_list *timer = &req->keepalive_timer;
+
+	if (!tux_keepalive_timeout)
+		TUX_BUG();
+#ifdef CONFIG_SMP
+	if (!spin_is_locked(&req->ti->work_lock))
+		TUX_BUG();
+#endif
+
+	if (!list_empty(&req->lru))
+		TUX_BUG();
+	if (req->ti->nr_lru > tux_max_keepalives) {
+		struct list_head *head, *last;
+		tux_req_t *last_req;
+
+		head = &req->ti->lru;
+		last = head->prev;
+		if (last == head)
+			TUX_BUG();
+		last_req = list_entry(last, tux_req_t, lru);
+		list_del(last);
+		DEBUG_DEL_LIST(last);
+		req->ti->nr_lru--;
+
+		Dprintk("LRU-aging req %p!\n", last_req);
+		last_req->error = TUX_ERROR_CONN_TIMEOUT;
+		if (!__idle_event(last_req))
+			__output_space_event(last_req);
+	}
+	list_add(&req->lru, &req->ti->lru);
+	req->ti->nr_lru++;
+
+	timer->expires = jiffies + tux_keepalive_timeout * HZ;
+	timer->data = (unsigned long) req;
+	timer->function = &keepalive_timeout_fn;
+	add_timer(timer);
+}
+
+static int __output_space_event (tux_req_t *req)
+{
+	if (!req || (req->magic != TUX_MAGIC))
+		TUX_BUG();
+
+	if (!test_and_clear_bit(0, &req->wait_output_space)) {
+		Dprintk("output space ready event at <%p>, on non-idle %p.\n", __builtin_return_address(0), req);
+		return 0;
+	}
+
+	Dprintk("output space ready event at <%p>, %p was waiting!\n", __builtin_return_address(0), req);
+	DEC_STAT(nr_output_space_pending);
+
+	del_keepalive_timer(req);
+	del_output_timer(req);
+
+	__add_req_to_workqueue(req);
+	return 1;
+}
+
+int output_space_event (tux_req_t *req)
+{
+	int ret;
+	unsigned long flags;
+
+	spin_lock_irqsave(&req->ti->work_lock, flags);
+	ret = __output_space_event(req);
+	spin_unlock_irqrestore(&req->ti->work_lock, flags);
+
+	return ret;
+}
+
+static int __idle_event (tux_req_t *req)
+{
+	struct inet_connection_sock *icsk;
+	threadinfo_t *ti;
+
+	if (!req || (req->magic != TUX_MAGIC))
+		TUX_BUG();
+	ti = req->ti;
+
+	if (!test_and_clear_bit(0, &req->idle_input)) {
+		Dprintk("data ready event at <%p>, on non-idle %p.\n", __builtin_return_address(0), req);
+		return 0;
+	}
+
+	Dprintk("data ready event at <%p>, %p was idle!\n", __builtin_return_address(0), req);
+	del_keepalive_timer(req);
+	del_output_timer(req);
+	DEC_STAT(nr_idle_input_pending);
+
+	icsk = inet_csk(req->sock->sk);
+
+	icsk->icsk_ack.pingpong = tux_ack_pingpong;
+	SET_TIMESTAMP(req->accept_timestamp);
+
+	__add_req_to_workqueue(req);
+
+	return 1;
+}
+
+int idle_event (tux_req_t *req)
+{
+	int ret;
+	unsigned long flags;
+
+	spin_lock_irqsave(&req->ti->work_lock, flags);
+	ret = __idle_event(req);
+	spin_unlock_irqrestore(&req->ti->work_lock, flags);
+
+	return ret;
+}
+
+#define HANDLE_CALLBACK_1(callback, tux_name, real_name, param...)	\
+	tux_req_t *req;					\
+							\
+	read_lock(&sk->sk_callback_lock);		\
+	req = sk->sk_user_data;				\
+							\
+	Dprintk("callback "#callback"(%p) req %p.\n",	\
+		sk->sk_##callback, req);		\
+							\
+	if (!req) {					\
+		if (sk->sk_##callback == tux_name) {	\
+			printk("BUG: "#callback" "#tux_name" "#real_name" no req!"); \
+			TUX_BUG();			\
+		}					\
+		read_unlock(&sk->sk_callback_lock);	\
+		if (sk->sk_##callback)			\
+			sk->sk_##callback(param);	\
+		return;					\
+	}						\
+
+#define HANDLE_CALLBACK_2(callback, tux_name, real_name, param...)	\
+	Dprintk(#tux_name"() on %p.\n", req);		\
+	if (req->magic != TUX_MAGIC)			\
+		TUX_BUG();				\
+	if (req->real_name)				\
+		req->real_name(param);
+
+#define HANDLE_CALLBACK(callback, tux_name, real_name, param...)	\
+	HANDLE_CALLBACK_1(callback,tux_name,real_name,param)	\
+	HANDLE_CALLBACK_2(callback,tux_name,real_name,param)
+
+static void tux_data_ready (struct sock *sk, int len)
+{
+	HANDLE_CALLBACK_1(data_ready, tux_data_ready, real_data_ready, sk, len);
+
+	if (!idle_event(req))
+		output_space_event(req);
+	read_unlock(&sk->sk_callback_lock);
+}
+
+static void tux_write_space (struct sock *sk)
+{
+	HANDLE_CALLBACK(write_space, tux_write_space, real_write_space, sk);
+
+	Dprintk("sk->sk_wmem_queued: %d, sk->sk_sndbuf: %d.\n",
+		sk->sk_wmem_queued, sk->sk_sndbuf);
+
+	if (sk_stream_wspace(sk) >= sk_stream_min_wspace(sk)) {
+		clear_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
+		if (!idle_event(req))
+			output_space_event(req);
+	}
+	read_unlock(&sk->sk_callback_lock);
+}
+
+static void tux_error_report (struct sock *sk)
+{
+	HANDLE_CALLBACK(error_report, tux_error_report, real_error_report, sk);
+
+	req->error = TUX_ERROR_CONN_CLOSE;
+	if (!idle_event(req))
+		output_space_event(req);
+	read_unlock(&sk->sk_callback_lock);
+}
+
+static void tux_state_change (struct sock *sk)
+{
+	HANDLE_CALLBACK(state_change, tux_state_change, real_state_change, sk);
+
+	if (req->sock && req->sock->sk &&
+				(req->sock->sk->sk_state > TCP_ESTABLISHED)) {
+		Dprintk("req %p changed to TCP non-established!\n", req);
+		Dprintk("req->sock: %p\n", req->sock);
+		if (req->sock)
+			Dprintk("req->sock->sk: %p\n", req->sock->sk);
+		if (req->sock && req->sock->sk)
+			Dprintk("TCP state: %d\n", req->sock->sk->sk_state);
+		Dprintk("req->error = TUX_ERROR_CONN_CLOSE!\n");
+		req->error = TUX_ERROR_CONN_CLOSE;
+	}
+	if (!idle_event(req))
+		output_space_event(req);
+	read_unlock(&sk->sk_callback_lock);
+}
+
+static void tux_destruct (struct sock *sk)
+{
+	BUG();
+}
+
+static void tux_ftp_data_ready (struct sock *sk, int len)
+{
+	HANDLE_CALLBACK_1(data_ready, tux_ftp_data_ready,
+				ftp_real_data_ready, sk, len);
+	if (!idle_event(req))
+		output_space_event(req);
+	read_unlock(&sk->sk_callback_lock);
+}
+
+static void tux_ftp_write_space (struct sock *sk)
+{
+	HANDLE_CALLBACK_1(write_space, tux_ftp_write_space,
+				ftp_real_write_space, sk);
+
+	Dprintk("sk->sk_wmem_queued: %d, sk->sk_sndbuf: %d.\n",
+		sk->sk_wmem_queued, sk->sk_sndbuf);
+
+	if (sk_stream_wspace(sk) >= sk->sk_sndbuf/10*8) {
+		clear_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
+		if (!idle_event(req))
+			output_space_event(req);
+	}
+	read_unlock(&sk->sk_callback_lock);
+}
+
+static void tux_ftp_error_report (struct sock *sk)
+{
+	HANDLE_CALLBACK(error_report, tux_ftp_error_report,
+		ftp_real_error_report, sk);
+
+	TDprintk("req %p sock %p got TCP errors on FTP data connection!\n", req, sk);
+	TDprintk("req->error = TUX_ERROR_CONN_CLOSE!\n");
+	req->error = TUX_ERROR_CONN_CLOSE;
+	if (!idle_event(req))
+		output_space_event(req);
+	read_unlock(&sk->sk_callback_lock);
+}
+
+static void tux_ftp_state_change (struct sock *sk)
+{
+	HANDLE_CALLBACK(state_change, tux_ftp_state_change,
+			ftp_real_state_change, sk);
+
+	if (req->sock && req->sock->sk &&
+			(req->sock->sk->sk_state > TCP_ESTABLISHED)) {
+		Dprintk("req %p FTP control sock changed to TCP non-established!\n", req);
+		Dprintk("req->sock: %p\n", req->sock);
+		TDprintk("req->error = TUX_ERROR_CONN_CLOSE!\n");
+
+		req->error = TUX_ERROR_CONN_CLOSE;
+	}
+	if (!idle_event(req))
+		output_space_event(req);
+	read_unlock(&sk->sk_callback_lock);
+}
+
+static void tux_ftp_create_child (struct sock *sk, struct sock *newsk)
+{
+	HANDLE_CALLBACK(create_child, tux_ftp_create_child,
+			ftp_real_create_child, sk, newsk);
+
+	newsk->sk_user_data = NULL;
+	newsk->sk_data_ready = req->ftp_real_data_ready;
+	newsk->sk_state_change = req->ftp_real_state_change;
+	newsk->sk_write_space = req->ftp_real_write_space;
+	newsk->sk_error_report = req->ftp_real_error_report;
+	newsk->sk_create_child = req->ftp_real_create_child;
+	newsk->sk_destruct = req->ftp_real_destruct;
+
+	if (!idle_event(req))
+		output_space_event(req);
+	read_unlock(&sk->sk_callback_lock);
+}
+
+static void tux_ftp_destruct (struct sock *sk)
+{
+	BUG();
+}
+
+static void link_tux_socket (tux_req_t *req, struct socket *sock)
+{
+	struct sock *sk = sock->sk;
+
+	if (req->sock)
+		TUX_BUG();
+	if (sk->sk_destruct == tux_destruct)
+		TUX_BUG();
+	/*
+	 * (No need to lock the socket, we just want to
+	 * make sure that events from now on go through
+	 * tux_data_ready())
+	 */
+	write_lock_irq(&sk->sk_callback_lock);
+
+	req->sock = sock;
+	sk->sk_user_data = req;
+
+	req->real_data_ready = sk->sk_data_ready;
+	req->real_state_change = sk->sk_state_change;
+	req->real_write_space = sk->sk_write_space;
+	req->real_error_report = sk->sk_error_report;
+	req->real_destruct = sk->sk_destruct;
+
+	sk->sk_data_ready = tux_data_ready;
+	sk->sk_state_change = tux_state_change;
+	sk->sk_write_space = tux_write_space;
+	sk->sk_error_report = tux_error_report;
+	sk->sk_destruct = tux_destruct;
+
+	write_unlock_irq(&sk->sk_callback_lock);
+
+	if (req->real_destruct == tux_destruct)
+		TUX_BUG();
+	req->client_addr = inet_sk(sk)->daddr;
+	req->client_port = inet_sk(sk)->dport;
+
+	add_wait_queue(sk->sk_sleep, &req->sleep);
+}
+
+void __link_data_socket (tux_req_t *req, struct socket *sock,
+						struct sock *sk)
+{
+	/*
+	 * (No need to lock the socket, we just want to
+	 * make sure that events from now on go through
+	 * tux_data_ready())
+	 */
+	write_lock_irq(&sk->sk_callback_lock);
+
+	req->data_sock = sock;
+	sk->sk_user_data = req;
+
+	req->ftp_real_data_ready = sk->sk_data_ready;
+	req->ftp_real_state_change = sk->sk_state_change;
+	req->ftp_real_write_space = sk->sk_write_space;
+	req->ftp_real_error_report = sk->sk_error_report;
+	req->ftp_real_create_child = sk->sk_create_child;
+	req->ftp_real_destruct = sk->sk_destruct;
+
+	sk->sk_data_ready = tux_ftp_data_ready;
+	sk->sk_state_change = tux_ftp_state_change;
+	sk->sk_write_space = tux_ftp_write_space;
+	sk->sk_error_report = tux_ftp_error_report;
+	sk->sk_create_child = tux_ftp_create_child;
+	sk->sk_destruct = tux_ftp_destruct;
+
+	if (req->ftp_real_destruct == tux_ftp_destruct)
+		TUX_BUG();
+
+	write_unlock_irq(&sk->sk_callback_lock);
+
+	add_wait_queue(sk->sk_sleep, &req->ftp_sleep);
+}
+
+void link_tux_data_socket (tux_req_t *req, struct socket *sock)
+{
+	struct sock *sk = sock->sk;
+
+	if (req->data_sock)
+		TUX_BUG();
+	if (sk->sk_destruct == tux_ftp_destruct)
+		TUX_BUG();
+	__link_data_socket(req, sock, sk);
+}
+
+void unlink_tux_socket (tux_req_t *req)
+{
+	struct sock *sk;
+
+	if (!req->sock || !req->sock->sk)
+		return;
+	sk = req->sock->sk;
+
+	write_lock_irq(&sk->sk_callback_lock);
+	if (!sk->sk_user_data)
+		TUX_BUG();
+	if (req->real_destruct == tux_destruct)
+		TUX_BUG();
+
+	sk->sk_user_data = NULL;
+
+	sk->sk_data_ready = req->real_data_ready;
+	sk->sk_state_change = req->real_state_change;
+	sk->sk_write_space = req->real_write_space;
+	sk->sk_error_report = req->real_error_report;
+	sk->sk_destruct = req->real_destruct;
+
+	if (sk->sk_destruct == tux_destruct)
+		TUX_BUG();
+
+	req->real_data_ready = NULL;
+	req->real_state_change = NULL;
+	req->real_write_space = NULL;
+	req->real_error_report = NULL;
+	req->real_destruct = NULL;
+
+	write_unlock_irq(&sk->sk_callback_lock);
+
+	remove_wait_queue(sk->sk_sleep, &req->sleep);
+}
+
+void unlink_tux_data_socket (tux_req_t *req)
+{
+	struct sock *sk;
+
+	if (!req->data_sock || !req->data_sock->sk)
+		return;
+	sk = req->data_sock->sk;
+
+	write_lock_irq(&sk->sk_callback_lock);
+
+	if (req->real_destruct == tux_ftp_destruct)
+		TUX_BUG();
+
+	sk->sk_user_data = NULL;
+	sk->sk_data_ready = req->ftp_real_data_ready;
+	sk->sk_state_change = req->ftp_real_state_change;
+	sk->sk_write_space = req->ftp_real_write_space;
+	sk->sk_error_report = req->ftp_real_error_report;
+	sk->sk_create_child = req->ftp_real_create_child;
+	sk->sk_destruct = req->ftp_real_destruct;
+
+	req->ftp_real_data_ready = NULL;
+	req->ftp_real_state_change = NULL;
+	req->ftp_real_write_space = NULL;
+	req->ftp_real_error_report = NULL;
+	req->ftp_real_create_child = NULL;
+	req->ftp_real_destruct = NULL;
+
+	write_unlock_irq(&sk->sk_callback_lock);
+
+	if (sk->sk_destruct == tux_ftp_destruct)
+		TUX_BUG();
+
+	remove_wait_queue(sk->sk_sleep, &req->ftp_sleep);
+}
+
+void add_tux_atom (tux_req_t *req, atom_func_t *atom)
+{
+	Dprintk("adding TUX atom %p to req %p, atom_idx: %d, at %p/%p.\n",
+		atom, req, req->atom_idx, __builtin_return_address(0), __builtin_return_address(1));
+	if (req->atom_idx == MAX_TUX_ATOMS)
+		TUX_BUG();
+	req->atoms[req->atom_idx] = atom;
+	req->atom_idx++;
+}
+
+void del_tux_atom (tux_req_t *req)
+{
+	if (!req->atom_idx)
+		TUX_BUG();
+	req->atom_idx--;
+	Dprintk("removing TUX atom %p to req %p, atom_idx: %d, at %p.\n",
+		req->atoms[req->atom_idx], req, req->atom_idx, __builtin_return_address(0));
+}
+
+void tux_schedule_atom (tux_req_t *req, int cachemiss)
+{
+	if (!list_empty(&req->work))
+		TUX_BUG();
+	if (!req->atom_idx)
+		TUX_BUG();
+	req->atom_idx--;
+	Dprintk("DOING TUX atom %p, req %p, atom_idx: %d, at %p.\n",
+		req->atoms[req->atom_idx], req, req->atom_idx, __builtin_return_address(0));
+	might_sleep();
+	req->atoms[req->atom_idx](req, cachemiss);
+	might_sleep();
+	Dprintk("DONE TUX atom %p, req %p, atom_idx: %d, at %p.\n",
+		req->atoms[req->atom_idx], req, req->atom_idx, __builtin_return_address(0));
+}
+
+/*
+ * Puts newly accepted connections into the inputqueue. This is the
+ * first step in the life of a TUX request.
+ */
+int accept_requests (threadinfo_t *ti)
+{
+	int count = 0, last_count = 0, error, socknr = 0;
+	struct socket *sock, *new_sock;
+	struct tcp_sock *tp2;
+	struct inet_connection_sock *icsk1, *icsk2;
+	tux_req_t *req;
+
+	if (ti->nr_requests > tux_max_connect)
+		goto out;
+
+repeat:
+	for (socknr = 0; socknr < CONFIG_TUX_NUMSOCKETS; socknr++) {
+		tux_listen_t *tux_listen;
+
+		tux_listen = ti->listen + socknr;
+		sock = tux_listen->sock;
+		if (!sock)
+			break;
+		if (unlikely(test_thread_flag(TIF_NEED_RESCHED)))
+			break;
+
+		icsk1 = inet_csk(sock->sk);
+		/*
+		 * Quick test to see if there are connections on the queue.
+		 * This is cheaper than accept() itself because this saves us
+		 * the allocation of a new socket. (Which doesn't seem to be
+		 * used anyway)
+		 */
+		if (!reqsk_queue_empty(&icsk1->icsk_accept_queue)) {
+			tux_proto_t *proto;
+
+			if (!count++)
+				__set_task_state(current, TASK_RUNNING);
+
+			new_sock = sock_alloc();
+			if (!new_sock)
+				goto out;
+
+			new_sock->type = sock->type;
+			new_sock->ops = sock->ops;
+
+			error = sock->ops->accept(sock, new_sock, O_NONBLOCK);
+			if (error < 0)
+				goto err;
+			if (new_sock->sk->sk_state != TCP_ESTABLISHED)
+				goto err;
+
+			tp2 = tcp_sk(new_sock->sk);
+			icsk2 = inet_csk(new_sock->sk);
+			tp2->nonagle = 2;
+			icsk2->icsk_ack.pingpong = tux_ack_pingpong;
+			new_sock->sk->sk_reuse = 1;
+			sock_set_flag(new_sock->sk, SOCK_URGINLINE);
+
+			/* Allocate a request-entry for the connection */
+			req = kmalloc_req(ti);
+			if (!req)
+				BUG();
+			link_tux_socket(req, new_sock);
+
+			proto = req->proto = tux_listen->proto;
+
+			proto->got_request(req);
+		}
+	}
+	if (count != last_count) {
+		last_count = count;
+		goto repeat;
+	}
+out:
+	return count;
+err:
+	sock_release(new_sock);
+	goto out;
+}
+
Index: latest/net/tux/cachemiss.c
===================================================================
--- /dev/null
+++ latest/net/tux/cachemiss.c
@@ -0,0 +1,265 @@
+/*
+ * TUX - Integrated Application Protocols Layer and Object Cache
+ *
+ * Copyright (C) 2000, 2001, Ingo Molnar <mingo@redhat.com>
+ *
+ * cachemiss.c: handle the 'slow IO path' by queueing not-yet-cached
+ * requests to the IO-thread pool. Dynamic load balancing is done
+ * between IO threads, based on the number of requests they have pending.
+ */
+
+#include <net/tux.h>
+#include <linux/delay.h>
+
+/****************************************************************
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2, or (at your option)
+ *      any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ ****************************************************************/
+
+void queue_cachemiss (tux_req_t *req)
+{
+	iothread_t *iot = req->ti->iot;
+
+	Dprintk("queueing_cachemiss(req:%p) (req->cwd_dentry: %p) at %p:%p.\n",
+		req, req->cwd_dentry, __builtin_return_address(0), __builtin_return_address(1));
+	if (req->idle_input || req->wait_output_space)
+		TUX_BUG();
+	req->had_cachemiss = 1;
+	if (!list_empty(&req->work))
+		TUX_BUG();
+	spin_lock(&iot->async_lock);
+	if (connection_too_fast(req))
+		list_add_tail(&req->work, &iot->async_queue);
+	else
+		list_add(&req->work, &iot->async_queue);
+	iot->nr_async_pending++;
+	INC_STAT(nr_cachemiss_pending);
+	spin_unlock(&iot->async_lock);
+
+	wake_up(&iot->async_sleep);
+}
+
+static tux_req_t * get_cachemiss (iothread_t *iot)
+{
+	struct list_head *tmp;
+	tux_req_t *req = NULL;
+
+	spin_lock(&iot->async_lock);
+	if (!list_empty(&iot->async_queue)) {
+
+		tmp = iot->async_queue.next;
+		req = list_entry(tmp, tux_req_t, work);
+
+		Dprintk("get_cachemiss(%p): got req %p.\n", iot, req);
+		list_del(tmp);
+		DEBUG_DEL_LIST(tmp);
+		iot->nr_async_pending--;
+		DEC_STAT(nr_cachemiss_pending);
+
+		if (req->ti->iot != iot)
+			TUX_BUG();
+	}
+	spin_unlock(&iot->async_lock);
+	return req;
+}
+
+struct file * tux_open_file (char *filename, int mode)
+{
+	struct file *filp;
+
+	if (!filename)
+		TUX_BUG();
+
+	/* Rule no. 3 -- Does the file exist ? */
+
+	filp = filp_open(filename, mode, 0600);
+
+	if (IS_ERR(filp) || !filp || !filp->f_dentry)
+		goto err;
+
+out:
+	return filp;
+err:
+	Dprintk("filp_open() error: %d.\n", (int)filp);
+	filp = NULL;
+	goto out;
+}
+
+static int cachemiss_thread (void *data)
+{
+	tux_req_t *req;
+	struct k_sigaction *ka;
+	DECLARE_WAITQUEUE(wait, current);
+	iothread_t *iot = data;
+	int nr = iot->ti->cpu, wake_up;
+
+	Dprintk("iot %p/%p got started.\n", iot, current);
+	drop_permissions();
+
+	spin_lock(&iot->async_lock);
+	iot->threads++;
+	sprintf(current->comm, "async IO %d/%d", nr, iot->threads);
+
+
+	spin_lock_irq(&current->sighand->siglock);
+	ka = current->sighand->action + SIGCHLD-1;
+	ka->sa.sa_handler = SIG_IGN;
+	siginitsetinv(&current->blocked, sigmask(SIGCHLD));
+	recalc_sigpending();
+	spin_unlock_irq(&current->sighand->siglock);
+
+	spin_unlock(&iot->async_lock);
+#ifdef CONFIG_SMP
+	{
+		cpumask_t mask;
+
+		if (cpu_isset(nr, cpu_online_map)) {
+			cpus_clear(mask);
+			cpu_set(nr, mask);
+			set_cpus_allowed(current, mask);
+		}
+
+	}
+#endif
+
+	add_wait_queue_exclusive(&iot->async_sleep, &wait);
+
+	for (;;) {
+		while (!list_empty(&iot->async_queue) &&
+				(req = get_cachemiss(iot))) {
+
+			if (!req->atom_idx) {
+				add_tux_atom(req, flush_request);
+				add_req_to_workqueue(req);
+				continue;
+			}
+			tux_schedule_atom(req, 1);
+			if (signal_pending(current))
+				flush_all_signals();
+		}
+		if (signal_pending(current))
+			flush_all_signals();
+		if (!list_empty(&iot->async_queue))
+			continue;
+		if (iot->shutdown) {
+			Dprintk("iot %p/%p got shutdown!\n", iot, current);
+			break;
+		}
+		__set_current_state(TASK_INTERRUPTIBLE);
+		if (list_empty(&iot->async_queue)) {
+			Dprintk("iot %p/%p going to sleep.\n", iot, current);
+			schedule();
+			Dprintk("iot %p/%p got woken up.\n", iot, current);
+		}
+		__set_current_state(TASK_RUNNING);
+	}
+
+	remove_wait_queue(&iot->async_sleep, &wait);
+
+	wake_up = 0;
+	spin_lock(&iot->async_lock);
+	if (!--iot->threads)
+		wake_up = 1;
+	spin_unlock(&iot->async_lock);
+	Dprintk("iot %p/%p has finished shutdown!\n", iot, current);
+	if (wake_up) {
+		Dprintk("iot %p/%p waking up master.\n", iot, current);
+		wake_up(&iot->wait_shutdown);
+	}
+
+	return 0;
+}
+
+static void __stop_cachemiss_threads (iothread_t *iot)
+{
+	DECLARE_WAITQUEUE(wait, current);
+
+	__set_current_state(TASK_UNINTERRUPTIBLE);
+
+	Dprintk("stopping async IO threads %p.\n", iot);
+	add_wait_queue(&iot->wait_shutdown, &wait);
+
+	spin_lock(&iot->async_lock);
+	if (iot->shutdown)
+		TUX_BUG();
+	if (!iot->threads)
+		TUX_BUG();
+	iot->shutdown = 1;
+	wake_up_all(&iot->async_sleep);
+	spin_unlock(&iot->async_lock);
+
+	Dprintk("waiting for async IO threads %p to exit.\n", iot);
+	schedule();
+	remove_wait_queue(&iot->wait_shutdown, &wait);
+
+	if (iot->threads)
+		TUX_BUG();
+	if (iot->nr_async_pending)
+		TUX_BUG();
+	Dprintk("stopped async IO threads %p.\n", iot);
+}
+
+void stop_cachemiss_threads (threadinfo_t *ti)
+{
+	iothread_t *iot = ti->iot;
+
+	if (!iot)
+		TUX_BUG();
+	if (iot->nr_async_pending)
+		TUX_BUG();
+	__stop_cachemiss_threads(iot);
+	ti->iot = NULL;
+	kfree(iot);
+}
+
+int start_cachemiss_threads (threadinfo_t *ti)
+{
+	int i, pid;
+
+	iothread_t *iot;
+
+	iot = kmalloc(sizeof(*iot), GFP_KERNEL);
+	if (!iot)
+		return -ENOMEM;
+	memset(iot, 0, sizeof(*iot));
+
+	iot->ti = ti;
+	spin_lock_init(&iot->async_lock);
+	iot->nr_async_pending = 0;
+	INIT_LIST_HEAD(&iot->async_queue);
+	init_waitqueue_head(&iot->async_sleep);
+	init_waitqueue_head(&iot->wait_shutdown);
+
+	for (i = 0; i < NR_IO_THREADS; i++) {
+		pid = kernel_thread(cachemiss_thread, (void *)iot, 0);
+		if (pid < 0) {
+			printk(KERN_ERR "TUX: error %d creating IO thread!\n",
+					pid);
+			__stop_cachemiss_threads(iot);
+			kfree(iot);
+			return pid;
+		}
+	}
+	ti->iot = iot;
+	/*
+	 * Wait for all cachemiss threads to start up:
+	 */
+	while (iot->threads != NR_IO_THREADS) {
+		__set_current_state(TASK_INTERRUPTIBLE);
+		schedule_timeout(HZ/10);
+	}
+	return 0;
+}
+
Index: latest/net/tux/cgi.c
===================================================================
--- /dev/null
+++ latest/net/tux/cgi.c
@@ -0,0 +1,171 @@
+/*
+ * TUX - Integrated Application Protocols Layer and Object Cache
+ *
+ * Copyright (C) 2000, 2001, Ingo Molnar <mingo@redhat.com>
+ *
+ * cgi.c: user-space CGI (and other) code execution.
+ */
+
+#define __KERNEL_SYSCALLS__
+#define __KERNEL_SYSCALLS_NO_ERRNO__
+
+#include <net/tux.h>
+
+/****************************************************************
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2, or (at your option)
+ *      any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ ****************************************************************/
+
+static int exec_usermode(char *program_path, char *argv[], char *envp[])
+{
+	struct files_struct *files = current->files;
+	struct fdtable *fdt;
+	int i, err;
+
+	err = tux_chroot(tux_cgiroot);
+	if (err) {
+		printk(KERN_ERR "TUX: CGI chroot returned %d, /proc/sys/net/tux/cgiroot is probably set up incorrectly! Aborting CGI execution.\n", err);
+		return err;
+	}
+
+	/* Allow execve args to be in kernel space. */
+	set_fs(KERNEL_DS);
+
+	// TODO: is this RCU-safe?
+	spin_lock(&files->file_lock);
+	fdt = files_fdtable(files);
+	spin_unlock(&files->file_lock);
+
+	for (i = 3; i < fdt->max_fds; i++ )
+		if (fdt->fd[i])
+			tux_close(i);
+
+	err = __exec_usermodehelper(program_path, argv, envp, NULL);
+	if (err < 0)
+		return err;
+	return 0;
+}
+
+static inline long tux_dup(unsigned int fildes)
+{
+	int ret = -EBADF;
+	struct file * file = fget(fildes);
+
+	if (file)
+		ret = dupfd(file, 0);
+	return ret;
+}
+
+static int exec_helper (void * data)
+{
+	exec_param_t *param = data;
+	char **tmp;
+	int ret;
+
+	sprintf(current->comm,"doexec - %d", current->pid);
+#ifdef CONFIG_SMP
+	if (!tux_cgi_inherit_cpu) {
+		cpumask_t map;
+
+		cpus_and(map, cpu_online_map, tux_cgi_cpu_mask);
+
+		if (!(cpus_empty(map)))
+			set_cpus_allowed(current, map);
+		else
+			set_cpus_allowed(current, cpu_online_map);
+	}
+#endif
+
+	if (!param)
+		TUX_BUG();
+	Dprintk("doing exec(%s).\n", param->command);
+
+	Dprintk("argv: ");
+	tmp = param->argv;
+	while (*tmp) {
+		Dprintk("{%s} ", *tmp);
+		tmp++;
+	}
+	Dprintk("\n");
+	Dprintk("envp: ");
+	tmp = param->envp;
+	while (*tmp) {
+		Dprintk("{%s} ", *tmp);
+		tmp++;
+	}
+	Dprintk("\n");
+	/*
+	 * Set up stdin, stdout and stderr of the external
+	 * CGI application.
+	 */
+	if (param->pipe_fds) {
+		struct files_struct *files = current->files;
+		struct fdtable *fdt;
+
+		tux_close(1);
+		tux_close(2);
+		tux_close(4);
+		if (tux_dup(3) != 1)
+			TUX_BUG();
+		if (tux_dup(5) != 2)
+			TUX_BUG();
+		tux_close(3);
+		tux_close(5);
+		// do not close on exec.
+		spin_lock(&files->file_lock);
+		fdt = files_fdtable(files);
+		FD_CLR(0, fdt->close_on_exec);
+		FD_CLR(1, fdt->close_on_exec);
+		FD_CLR(2, fdt->close_on_exec);
+		spin_unlock(&files->file_lock);
+	}
+	ret = exec_usermode(param->command, param->argv, param->envp);
+	if (ret < 0)
+		Dprintk("bug: exec() returned %d.\n", ret);
+	else
+		Dprintk("exec()-ed successfully!\n");
+	return 0;
+}
+
+pid_t tux_exec_process (char *command, char **argv,
+			char **envp, int pipe_fds,
+				exec_param_t *param, int wait)
+{
+	exec_param_t param_local;
+	pid_t pid;
+	struct k_sigaction *ka;
+
+	ka = current->sighand->action + SIGCHLD-1;
+	ka->sa.sa_handler = SIG_IGN;
+
+	if (!param && wait)
+		param = &param_local;
+
+	param->command = command;
+	param->argv = argv;
+	param->envp = envp;
+	param->pipe_fds = pipe_fds;
+
+repeat_fork:
+	pid = kernel_thread(exec_helper, (void*) param, CLONE_SIGHAND|SIGCHLD);
+	Dprintk("kernel thread created PID %d.\n", pid);
+	if (pid < 0) {
+		printk(KERN_ERR "TUX: could not create new CGI kernel thread due to %d... retrying.\n", pid);
+		current->state = TASK_UNINTERRUPTIBLE;
+		schedule_timeout(HZ);
+		goto repeat_fork;
+	}
+	return pid;
+}
Index: latest/net/tux/directory.c
===================================================================
--- /dev/null
+++ latest/net/tux/directory.c
@@ -0,0 +1,302 @@
+/*
+ * TUX - Integrated Application Protocols Layer and Object Cache
+ *
+ * Copyright (C) 2000, 2001, Ingo Molnar <mingo@redhat.com>
+ *
+ * directory.c: directory listing support
+ */
+
+#define __KERNEL_SYSCALLS__
+#include <net/tux.h>
+
+/****************************************************************
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2, or (at your option)
+ *      any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ ****************************************************************/
+
+char * tux_print_path (tux_req_t *req, struct dentry *dentry, struct vfsmount *mnt, char *buf, unsigned int max_len)
+{
+	char *res;
+	struct dentry *cwd, *root;
+	struct vfsmount *cwd_mnt, *rootmnt;
+
+	cwd = dget(dentry);
+	cwd_mnt = mntget(mnt);
+	root = dget(req->docroot_dentry);
+	rootmnt = mntget(req->docroot_mnt);
+
+	spin_lock(&dcache_lock);
+	res = __d_path(cwd, cwd_mnt, root, rootmnt, buf, max_len);
+	spin_unlock(&dcache_lock);
+
+	dput(cwd);
+	mntput(cwd_mnt);
+	dput(root);
+	mntput(rootmnt);
+
+	return res;
+}
+
+/*
+ * There are filesystems that do not fill in ->d_type correctly.
+ * Determine file-type.
+ */
+static int get_d_type (struct dentry *dentry)
+{
+	unsigned int mode = dentry->d_inode->i_mode;
+
+	if (S_ISREG(mode))
+		return DT_REG;
+	if (S_ISDIR(mode))
+		return DT_DIR;
+	if (S_ISLNK(mode))
+		return DT_LNK;
+	if (S_ISFIFO(mode))
+		return DT_FIFO;
+	if (S_ISSOCK(mode))
+		return DT_SOCK;
+	if (S_ISCHR(mode))
+		return DT_CHR;
+	if (S_ISBLK(mode))
+		return DT_BLK;
+	return 0;
+}
+
+static void do_dir_line (tux_req_t *req, int cachemiss)
+{
+	struct linux_dirent64 *dirp, *dirp0;
+	char string0[MAX_OBJECTNAME_LEN+200], *tmp;
+	int len, curroff, total, str_len = 0;
+	int err, flag = cachemiss ? 0 : LOOKUP_ATOMIC;
+	struct nameidata base = { };
+	struct dentry *dentry = NULL;
+	struct inode *inode = NULL;
+	struct vfsmount *mnt = NULL;
+
+	if (req->proto->check_req_err(req, cachemiss))
+		return;
+
+	tmp = NULL;
+	dirp0 = req->dirp0;
+	curroff = req->curroff;
+	total = req->total;
+
+	dirp = (struct linux_dirent64 *)((char *)dirp0 + curroff);
+	if (!dirp->d_name || !dirp->d_name[0])
+		goto next_dir;
+	/*
+	 * Hide .xxxxx files:
+	 */
+	if (dirp->d_name[0] == '.')
+		goto next_dir;
+	Dprintk("<%s T:%d (off:%Ld) (len:%d)>\n", dirp->d_name, dirp->d_type, dirp->d_off, dirp->d_reclen);
+	if (tux_hide_unreadable) {
+		switch (dirp->d_type) {
+			default:
+				goto next_dir;
+			case DT_UNKNOWN:
+			case DT_REG:
+			case DT_DIR:
+			case DT_LNK:
+			/* valid entries - fall through. */
+				;
+		}
+	}
+	len = strlen(dirp->d_name);
+	if (len >= MAX_OBJECTNAME_LEN) {
+		dirp->d_name[MAX_OBJECTNAME_LEN] = 0;
+		len = MAX_OBJECTNAME_LEN-1;
+	}
+
+	if (!req->dentry)
+		TUX_BUG();
+
+	base.flags = flag;
+	base.last_type = LAST_ROOT;
+	base.dentry = dget(req->dentry);
+	base.mnt = mntget(req->cwd_mnt);
+
+	switch_docroot(req);
+	err = path_walk(dirp->d_name, &base);
+
+	Dprintk("path_walk() returned %d.\n", err);
+
+	if (err) {
+		if (err == -EWOULDBLOCKIO) {
+			add_tux_atom(req, do_dir_line);
+			queue_cachemiss(req);
+			return;
+		}
+		goto next_dir;
+	}
+
+	dentry = base.dentry;
+	mnt = base.mnt;
+	if (!dentry)
+		TUX_BUG();
+	if (IS_ERR(dentry))
+		TUX_BUG();
+	inode = dentry->d_inode;
+	if (!inode)
+		TUX_BUG();
+	if (!dirp->d_type)
+		dirp->d_type = get_d_type(dentry);
+	if (tux_hide_unreadable) {
+		umode_t mode;
+
+		mode = inode->i_mode;
+		if (mode & tux_mode_forbidden)
+			goto out_dput;
+		if (!(mode & tux_mode_allowed))
+			goto out_dput;
+
+		err = permission(inode, MAY_READ, NULL);
+		if (err)
+			goto out_dput;
+		if (dirp->d_type == DT_DIR) {
+			err = permission(inode, MAY_EXEC, NULL);
+			if (err)
+				goto out_dput;
+		}
+	}
+
+	tmp = req->proto->print_dir_line(req, string0, dirp->d_name, len, dirp->d_type, dentry, inode);
+	if (tmp)
+		str_len = tmp-string0;
+out_dput:
+	dput(dentry);
+	mntput(mnt);
+next_dir:
+	curroff += dirp->d_reclen;
+
+	if (tmp && (tmp != string0))
+		Dprintk("writing line (len: %d): <%s>\n", strlen(string0), string0);
+
+	if (curroff < total) {
+		req->dirp0 = dirp0;
+		req->curroff = curroff;
+		add_tux_atom(req, do_dir_line);
+	} else {
+		kfree(dirp0);
+		req->dirp0 = NULL;
+		req->curroff = 0;
+		// falls back to the list_directory atom
+	}
+	if (tmp && (tmp != string0))
+		__send_async_message(req, string0, 200, str_len, 0);
+	else
+		add_req_to_workqueue(req);
+}
+
+#define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de)))
+#define ROUND_UP(x) (((x)+sizeof(long)-1) & ~(sizeof(long)-1))
+#define ROUND_UP64(x) (((x)+sizeof(u64)-1) & ~(sizeof(u64)-1))
+
+static int filldir64(void * __buf, const char * name, int namlen, loff_t offset,
+		     u64 ino, unsigned int d_type)
+{
+	struct linux_dirent64 * dirent, d;
+	struct getdents_callback64 * buf = (struct getdents_callback64 *) __buf;
+	int reclen = ROUND_UP64(NAME_OFFSET(dirent) + namlen + 1);
+	int err;
+
+	buf->error = -EINVAL;	/* only used if we fail.. */
+	if (reclen > buf->count)
+		return -EINVAL;
+	dirent = buf->previous;
+	if (dirent) {
+		d.d_off = offset;
+		err = copy_to_user(&dirent->d_off, &d.d_off, sizeof(d.d_off));
+		BUG_ON(err);
+	}
+	dirent = buf->current_dir;
+	buf->previous = dirent;
+	memset(&d, 0, NAME_OFFSET(&d));
+	d.d_ino = ino;
+	d.d_reclen = reclen;
+	d.d_type = d_type;
+	err = copy_to_user(dirent, &d, NAME_OFFSET(&d));
+	BUG_ON(err);
+	err = copy_to_user(dirent->d_name, name, namlen);
+	BUG_ON(err);
+	err = put_user(0, dirent->d_name + namlen);
+	BUG_ON(err);
+	dirent = (void *)dirent + reclen;
+	buf->current_dir = dirent;
+	buf->count -= reclen;
+	return 0;
+}
+#define DIRENT_SIZE 3000
+
+void list_directory (tux_req_t *req, int cachemiss)
+{
+	struct getdents_callback64 buf;
+	struct linux_dirent64 *dirp0;
+	mm_segment_t oldmm;
+	int total;
+
+	Dprintk("list_directory(%p, %d), dentry: %p.\n", req, cachemiss, req->dentry);
+	if (!req->cwd_dentry)
+		TUX_BUG();
+
+	if (!cachemiss) {
+		add_tux_atom(req, list_directory);
+		queue_cachemiss(req);
+		return;
+	}
+
+	dirp0 = tux_kmalloc(DIRENT_SIZE);
+
+	buf.current_dir = dirp0;
+	buf.previous = NULL;
+	buf.count = DIRENT_SIZE;
+	buf.error = 0;
+
+	oldmm = get_fs(); set_fs(KERNEL_DS);
+	set_fs(KERNEL_DS);
+	total = vfs_readdir(req->in_file, filldir64, &buf);
+	set_fs(oldmm);
+
+	if (buf.previous)
+		total = DIRENT_SIZE - buf.count;
+
+	Dprintk("total: %d (buf.error: %d, buf.previous %p)\n",
+		total, buf.error, buf.previous);
+
+	if (total < 0) {
+		kfree(dirp0);
+		req_err(req);
+		add_req_to_workqueue(req);
+		return;
+	}
+	if (!total) {
+		kfree(dirp0);
+		req->in_file->f_pos = 0;
+		add_req_to_workqueue(req);
+		return;
+	}
+
+	if (!req->cwd_dentry)
+		TUX_BUG();
+	add_tux_atom(req, list_directory);
+
+	req->dirp0 = dirp0;
+	req->curroff = 0;
+	req->total = total;
+	add_tux_atom(req, do_dir_line);
+
+	add_req_to_workqueue(req);
+}
+
Index: latest/net/tux/extcgi.c
===================================================================
--- /dev/null
+++ latest/net/tux/extcgi.c
@@ -0,0 +1,329 @@
+/*
+ * TUX - Integrated Application Protocols Layer and Object Cache
+ *
+ * Copyright (C) 2000, 2001, Ingo Molnar <mingo@redhat.com>
+ *
+ * extcgi.c: dynamic TUX module which forks and starts an external CGI
+ */
+
+#define __KERNEL_SYSCALLS__
+#define __KERNEL_SYSCALLS_NO_ERRNO__
+
+#include <net/tux.h>
+#include "parser.h"
+
+/****************************************************************
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2, or (at your option)
+ *      any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ ****************************************************************/
+
+#define MAX_ENVLEN 1000
+#define MAX_CGI_METAVARIABLES 32
+#define CGI_CHUNK_SIZE 1024
+#define MAX_CGI_COMMAND_LEN 256
+
+#ifdef CONFIG_TUX_DEBUG
+#define PRINT_MESSAGE_LEFT \
+	Dprintk("CGI message left at %s:%d:\n--->{%s}<---\n", \
+		__FILE__, __LINE__, curr)
+#else
+#define PRINT_MESSAGE_LEFT do {} while(0)
+#endif
+
+#define GOTO_INCOMPLETE do { Dprintk("invalid CGI reply at %s:%d.\n", __FILE__, __LINE__); goto invalid; } while (0)
+
+/*
+ * Please acknowledge our hard work by not changing this define, or
+ * at least please acknowledge us by leaving "TUX/2.0 (Linux)" in
+ * the ID string. Thanks! :-)
+ */
+#define CGI_SUCCESS2 "HTTP/1.1 200 OK\r\nConnection: close\r\nServer: TUX/2.0 (Linux)\r\n"
+
+static int handle_cgi_reply (tux_req_t *req)
+{
+	int first = 1;
+	int len, left, total;
+	char *buf, *tmp;
+	mm_segment_t oldmm;
+
+	buf = tux_kmalloc(CGI_CHUNK_SIZE+1);
+	tux_close(3);
+	tux_close(4);
+	tux_close(5);
+	oldmm = get_fs(); set_fs(KERNEL_DS);
+	send_sync_buf(NULL, req->sock, CGI_SUCCESS2, sizeof(CGI_SUCCESS2)-1, MSG_MORE);
+	set_fs(oldmm);
+
+	req->bytes_sent = 0;
+	/*
+	 * The new process is the new owner of the socket, it will
+	 * close it.
+	 */
+repeat:
+	left = CGI_CHUNK_SIZE;
+	len = 0;
+	total = 0;
+	tmp = buf;
+	do {
+		mm_segment_t oldmm;
+
+		tmp += len;
+		total += len;
+		left -= len;
+		if (!left)
+			break;
+repeat_read:
+		Dprintk("reading %d bytes via read().\n", left);
+		oldmm = get_fs(); set_fs(KERNEL_DS);
+		len = sys_read(2, tmp, left);
+		set_fs(oldmm);
+		Dprintk("got %d bytes from read() (total: %d).\n", len, total);
+		if (len > 0)
+			tmp[len] = 0;
+		Dprintk("CGI reply: (%d bytes, total %d).\n", len, total);
+		if (len == -ERESTARTSYS) {
+			flush_all_signals();
+			goto repeat_read;
+		}
+	} while (len > 0);
+	if (total > CGI_CHUNK_SIZE) {
+		printk(KERN_ERR "TUX: CGI weirdness. total: %d, len: %d, left: %d.\n", total, len, left);
+		TUX_BUG();
+	}
+	Dprintk("CGI done reply chunk: (%d bytes last, total %d).\n", len, total);
+	if (total) {
+		mm_segment_t oldmm;
+
+		oldmm = get_fs(); set_fs(KERNEL_DS);
+		if (!len)
+			send_sync_buf(NULL, req->sock, buf, total, 0);
+		else
+			send_sync_buf(NULL, req->sock, buf, total, MSG_MORE);
+		set_fs(oldmm);
+		req->bytes_sent += total;
+	}
+
+	Dprintk("bytes_sent: %d\n", req->bytes_sent);
+	if ((total > 0) && first) {
+		first = 0;
+
+		if (buf[total])
+			TUX_BUG();
+		tmp = strstr(buf, "\n\n");
+		if (tmp) {
+			req->bytes_sent -= (tmp-buf) + 2;
+			Dprintk("new bytes_sent: %d\n", req->bytes_sent);
+		} else {
+			req->bytes_sent = 0;
+			req_err(req);
+		}
+	}
+	if (len < 0)
+		Dprintk("sys_read returned with %d.\n", len);
+	else {
+		if (total > 0)
+			goto repeat;
+	}
+	tux_close(2);
+
+	req->status = 200;
+	add_req_to_workqueue(req);
+	kfree(buf);
+
+	return -1;
+}
+
+static int exec_external_cgi (void *data)
+{
+	exec_param_t param;
+	tux_req_t *req = data;
+	char *envp[MAX_CGI_METAVARIABLES+1], **envp_p;
+	char *argv[] = { "extcgi", NULL};
+	char *envstr, *tmp;
+	unsigned int host;
+	struct k_sigaction *ka;
+	int in_pipe_fds[2], out_pipe_fds[2], err_pipe_fds[2], len, err;
+	char *command;
+	pid_t pid;
+
+	len = strlen(tux_common_docroot);
+	if (req->objectname_len + len + 12 > MAX_CGI_COMMAND_LEN)
+		return -ENOMEM;
+	sprintf(current->comm,"cgimain - %d", current->pid);
+	host = inet_sk(req->sock->sk)->daddr;
+
+	envstr = tux_kmalloc(MAX_ENVLEN);
+	command = tux_kmalloc(MAX_CGI_COMMAND_LEN);
+
+	tmp = envstr;
+	envp_p = envp;
+
+#define WRITE_ENV(str...) \
+	if (envp_p >= envp + MAX_CGI_METAVARIABLES) \
+		TUX_BUG(); \
+	len = sprintf(tmp, str); \
+	*envp_p++ = tmp; \
+	tmp += len + 1; \
+	if (tmp >= envstr + MAX_ENVLEN) \
+		TUX_BUG();
+
+	#define WRITE_ENV_STR(str,field,len)			\
+	do {							\
+		int offset;					\
+								\
+		offset = sizeof(str)-1;				\
+		err = -EFAULT;					\
+		if (tmp - envstr + offset + len >= MAX_ENVLEN)	\
+			goto out;				\
+		if (envp_p >= envp + MAX_CGI_METAVARIABLES) 	\
+			TUX_BUG(); 				\
+		memcpy(tmp, str, offset);			\
+		memcpy(tmp + offset, field, len);		\
+		offset += len;					\
+		tmp[offset] = 0;				\
+		*envp_p++ = tmp;				\
+		tmp += offset + 1;				\
+	} while (0)
+
+	WRITE_ENV("GATEWAY_INTERFACE=CGI/1.1");
+	WRITE_ENV("CONTENT_LENGTH=%d", req->post_data_len);
+	WRITE_ENV("REMOTE_ADDR=%d.%d.%d.%d", NIPQUAD(host));
+	WRITE_ENV("SERVER_PORT=%d", 80);
+	WRITE_ENV("SERVER_SOFTWARE=TUX/2.0 (Linux)");
+
+#if 1
+	WRITE_ENV("DOCUMENT_ROOT=/");
+	WRITE_ENV("PATH_INFO=/");
+#else
+	WRITE_ENV_STR("DOCUMENT_ROOT=", tux_common_docroot, len);
+	WRITE_ENV_STR("PATH_INFO=", tux_common_docroot, len);
+#endif
+	WRITE_ENV_STR("QUERY_STRING=", req->query_str, req->query_len);
+	WRITE_ENV_STR("REQUEST_METHOD=", req->method_str, req->method_len);
+	WRITE_ENV_STR("SCRIPT_NAME=", req->objectname, req->objectname_len);
+	WRITE_ENV_STR("SERVER_PROTOCOL=", req->version_str, req->version_len);
+
+	if (req->content_type_len)
+		WRITE_ENV_STR("CONTENT_TYPE=",
+			req->content_type_str, req->content_type_len);
+	if (req->cookies_len)
+		WRITE_ENV_STR("HTTP_COOKIE=",
+			req->cookies_str, req->cookies_len);
+
+	if (req->host_len)
+		WRITE_ENV_STR("SERVER_NAME=", req->host, req->host_len);
+	else {
+		const char *host = "localhost";
+		WRITE_ENV_STR("SERVER_NAME=", host, strlen(host));
+	}
+
+	*envp_p = NULL;
+
+	spin_lock_irq(&current->sighand->siglock);
+	ka = current->sighand->action + SIGPIPE-1;
+	ka->sa.sa_handler = SIG_IGN;
+	siginitsetinv(&current->blocked, sigmask(SIGCHLD));
+	recalc_sigpending();
+	spin_unlock_irq(&current->sighand->siglock);
+
+	tux_close(0); tux_close(1);
+	tux_close(2); tux_close(3);
+	tux_close(4); tux_close(5);
+
+	in_pipe_fds[0] = in_pipe_fds[1] = -1;
+	out_pipe_fds[0] = out_pipe_fds[1] = -1;
+	err_pipe_fds[0] = err_pipe_fds[1] = -1;
+
+	err = -ENFILE;
+	if (do_pipe(in_pipe_fds))
+		goto out;
+	if (do_pipe(out_pipe_fds))
+		goto out;
+	if (do_pipe(err_pipe_fds))
+		goto out;
+
+	if (in_pipe_fds[0] != 0) TUX_BUG();
+	if (in_pipe_fds[1] != 1) TUX_BUG();
+	if (out_pipe_fds[0] != 2) TUX_BUG();
+	if (out_pipe_fds[1] != 3) TUX_BUG();
+	if (err_pipe_fds[0] != 4) TUX_BUG();
+	if (err_pipe_fds[1] != 5) TUX_BUG();
+
+	if (req->virtual && req->host_len)
+		sprintf(command, "/%s/cgi-bin/%s", req->host, req->objectname);
+	else
+		sprintf(command, "/cgi-bin/%s", req->objectname);
+	Dprintk("before CGI exec.\n");
+	pid = tux_exec_process(command, argv, envp, 1, &param, 0);
+	Dprintk("after CGI exec.\n");
+
+	if (req->post_data_len) {
+		mm_segment_t oldmm;
+		int ret;
+
+		Dprintk("POST data to CGI:\n");
+		oldmm = get_fs(); set_fs(KERNEL_DS);
+		ret = sys_write(1, req->post_data_str, req->post_data_len);
+		set_fs(oldmm);
+		Dprintk("write() returned: %d.\n", ret);
+		if (ret != req->post_data_len)
+			Dprintk("write() returned: %d.\n", ret);
+	}
+
+	tux_close(0);
+	tux_close(1);
+
+	handle_cgi_reply(req);
+	err = 0;
+
+out:
+	kfree(envstr);
+	kfree(command);
+
+	return err;
+}
+
+void start_external_cgi (tux_req_t *req)
+{
+	int pid;
+
+repeat:
+	pid = kernel_thread(exec_external_cgi, (void*) req, SIGCHLD);
+	if (pid == -1)
+		return;
+	if (pid < 0) {
+		printk(KERN_INFO "TUX: Could not fork external CGI process due to %d, retrying!\n", pid);
+		current->state = TASK_UNINTERRUPTIBLE;
+		schedule_timeout(HZ);
+		goto repeat;
+	}
+}
+
+int query_extcgi (tux_req_t *req)
+{
+	clear_keepalive(req);
+	start_external_cgi(req);
+	return -1;
+}
+
+#define EXTCGI_INVALID_HEADER \
+	"HTTP/1.1 503 Service Unavailable\r\n" \
+	"Content-Length: 23\r\n\r\n"
+
+#define EXTCGI_INVALID_BODY \
+	"TUX: invalid CGI reply."
+
+#define EXTCGI_INVALID EXTCGI_INVALID_HEADER EXTCGI_INVALID_BODY
+
Index: latest/net/tux/gzip.c
===================================================================
--- /dev/null
+++ latest/net/tux/gzip.c
@@ -0,0 +1,40 @@
+/*	$Id: zlib.h,v 1.2 1997/12/23 10:47:44 paulus Exp $	*/
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/zlib.h>
+#include <net/tux.h>
+
+#define STREAM_END_SPACE 12
+
+int tux_gzip_compress (tux_req_t *req, unsigned char *data_in, unsigned char *data_out, __u32 *in_len, __u32 *out_len)
+{
+	z_stream *s = &req->ti->gzip_state;
+	int ret, left;
+
+	down(&req->ti->gzip_sem);
+	if (zlib_deflateReset(s) != Z_OK)
+		BUG();
+
+	s->next_in = data_in;
+	s->next_out = data_out;
+	s->avail_in = *in_len;
+	s->avail_out = *out_len;
+
+	Dprintk("calling zlib_deflate with avail_in %d, avail_out %d\n", s->avail_in, s->avail_out);
+	ret = zlib_deflate(s, Z_FINISH);
+	Dprintk("deflate returned with avail_in %d, avail_out %d, total_in %ld, total_out %ld\n", s->avail_in, s->avail_out, s->total_in, s->total_out);
+
+	if (ret != Z_STREAM_END) {
+		printk("bad: deflate returned with %d! avail_in %d, avail_out %d, total_in %ld, total_out %ld\n", ret, s->avail_in, s->avail_out, s->total_in, s->total_out);
+		BUG();
+	}
+	*in_len = s->avail_in;
+	*out_len = s->avail_out;
+	left = s->avail_in;
+
+	up(&req->ti->gzip_sem);
+
+	return left;
+}
+
Index: latest/net/tux/input.c
===================================================================
--- /dev/null
+++ latest/net/tux/input.c
@@ -0,0 +1,641 @@
+/*
+ * TUX - Integrated Application Protocols Layer and Object Cache
+ *
+ * Copyright (C) 2000, 2001, Ingo Molnar <mingo@redhat.com>
+ *
+ * input.c: handle requests arriving on accepted connections
+ */
+
+#include <net/tux.h>
+#include <linux/kmod.h>
+
+/****************************************************************
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2, or (at your option)
+ *      any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ ****************************************************************/
+
+void zap_request (tux_req_t *req, int cachemiss)
+{
+	if (!req->error)
+		TUX_BUG();
+	if (req->error == TUX_ERROR_CONN_TIMEOUT) {
+		if (req->proto->request_timeout) {
+			clear_keepalive(req);
+			req->proto->request_timeout(req, cachemiss);
+		} else {
+			clear_keepalive(req);
+			if (!cachemiss)
+				flush_request(req, 0);
+			else {
+				add_tux_atom(req, flush_request);
+				add_req_to_workqueue(req);
+			}
+		}
+		return;
+	}
+
+	if (!cachemiss && (req->error == TUX_ERROR_CONN_CLOSE)) {
+		/*
+		 * Zap connection as fast as possible, there is
+		 * no valid client connection anymore:
+		 */
+		clear_keepalive(req);
+		flush_request(req, 0);
+	} else {
+		if (req->error == TUX_ERROR_CONN_CLOSE) {
+			clear_keepalive(req);
+			add_tux_atom(req, flush_request);
+		} else
+			/*
+			 * Potentially redirect to the secondary server:
+			 */
+			add_tux_atom(req, redirect_request);
+		add_req_to_workqueue(req);
+	}
+}
+
+void __switch_docroot(tux_req_t *req)
+{
+	if (!req->docroot_dentry || !req->docroot_mnt)
+		TUX_BUG();
+	set_fs_root(current->fs, req->docroot_mnt, req->docroot_dentry);
+}
+
+struct dentry * __tux_lookup (tux_req_t *req, const char *filename,
+			 struct nameidata *base, struct vfsmount **mnt)
+{
+	int err;
+
+	err = path_walk(filename, base);
+	if (err) {
+		Dprintk("path_walk() returned with %d!\n", err);
+		return ERR_PTR(err);
+	}
+	if (*mnt)
+		TUX_BUG();
+	*mnt = base->mnt;
+
+	return base->dentry;
+}
+
+int tux_permission (struct inode *inode)
+{
+	umode_t mode;
+	int err;
+
+	mode = inode->i_mode;
+	Dprintk("URL inode mode: %08x.\n", mode);
+
+	if (mode & tux_mode_forbidden)
+		return -2;
+	/*
+	 * at least one bit in the 'allowed' set has to
+	 * be present to allow access.
+	 */
+	if (!(mode & tux_mode_allowed))
+		return -3;
+	err = permission(inode,MAY_READ,NULL);
+	return err;
+}
+
+struct dentry * tux_lookup (tux_req_t *req, const char *filename,
+			const unsigned int flag, struct vfsmount **mnt)
+{
+	struct dentry *dentry;
+	struct nameidata base = { };
+
+	Dprintk("tux_lookup(%p, %s, %d, virtual: %d, host: %s (%d).)\n", req, filename, flag, req->virtual, req->host, req->host_len);
+
+	base.flags = LOOKUP_FOLLOW|flag;
+	base.last_type = LAST_ROOT;
+	if (req->objectname[0] == '/') {
+		base.dentry = dget(req->docroot_dentry);
+		base.mnt = mntget(req->docroot_mnt);
+	} else {
+		if (!req->cwd_dentry) {
+			req->cwd_dentry = dget(req->docroot_dentry);
+			req->cwd_mnt = mntget(req->docroot_mnt);
+		}
+		base.dentry = req->cwd_dentry;
+		dget(base.dentry);
+		base.mnt = mntget(req->cwd_mnt);
+	}
+
+	switch_docroot(req);
+	dentry = __tux_lookup (req, filename, &base, mnt);
+
+	Dprintk("looked up {%s} == dentry %p.\n", filename, dentry);
+
+	if (dentry && !IS_ERR(dentry) && !dentry->d_inode)
+		TUX_BUG();
+	return dentry;
+}
+
+int lookup_object (tux_req_t *req, const unsigned int flag)
+{
+	struct vfsmount *mnt = NULL;
+	struct dentry *dentry = NULL;
+	int perm;
+
+	dentry = tux_lookup(req, req->objectname, flag, &mnt);
+	if (!dentry || IS_ERR(dentry)) {
+		if (PTR_ERR(dentry) == -EWOULDBLOCKIO)
+			goto cachemiss;
+		goto abort;
+	}
+	perm = tux_permission(dentry->d_inode);
+	/*
+	 * Only regular files allowed.
+	 */
+	if ((perm < 0) || !S_ISREG(dentry->d_inode->i_mode)) {
+		req->status = 403;
+		goto abort;
+	}
+	req->total_file_len = dentry->d_inode->i_size;
+out:
+	install_req_dentry(req, dentry, mnt);
+	return 0;
+cachemiss:
+	return 1;
+abort:
+	if (dentry) {
+		if (!IS_ERR(dentry))
+			dput(dentry);
+		dentry = NULL;
+	}
+	if (mnt) {
+		if (!IS_ERR(mnt))
+			mntput(mnt);
+		mnt = NULL;
+	}
+	req_err(req);
+	goto out;
+}
+
+void install_req_dentry (tux_req_t *req, struct dentry *dentry, struct vfsmount *mnt)
+{
+	if (req->dentry)
+		TUX_BUG();
+	req->dentry = dentry;
+	if (req->mnt)
+		TUX_BUG();
+	req->mnt = mnt;
+	if (req->in_file && req->in_file->f_dentry)
+		TUX_BUG();
+	if (dentry)
+		req->in_file = dentry_open(dget(dentry), NULL, O_RDONLY);
+}
+
+void release_req_dentry (tux_req_t *req)
+{
+	if (!req->dentry) {
+		if (req->in_file && req->in_file->f_dentry)
+			TUX_BUG();
+		return;
+	}
+
+	fput(req->in_file);
+	req->in_file = NULL;
+	dput(req->dentry);
+	req->dentry = NULL;
+	mntput(req->mnt);
+	req->mnt = NULL;
+}
+
+int __connection_too_fast (tux_req_t *req)
+{
+	unsigned long curr_bw, delta, bytes;
+
+	bytes = req->total_bytes + req->bytes_sent;
+	if (!bytes)
+		return 1;
+
+	delta = jiffies - req->first_timestamp;
+	if (!delta)
+		delta++;
+	curr_bw = bytes * HZ / delta;
+
+	if (curr_bw > tux_max_output_bandwidth)
+		return 2;
+	return 0;
+}
+
+void unidle_req (tux_req_t *req)
+{
+	threadinfo_t *ti = req->ti;
+
+	Dprintk("UNIDLE req %p <%p> (sock %p, sk %p) (keepalive: %d, status: %d)\n", req, __builtin_return_address(0), req->sock, req->sock->sk, req->keep_alive, req->status);
+	spin_lock_irq(&ti->work_lock);
+	if (req->magic != TUX_MAGIC)
+		TUX_BUG();
+	if (!test_and_clear_bit(0, &req->idle_input)) {
+		Dprintk("unidling %p, wasnt idle!\n", req);
+		if (list_empty(&req->work))
+			TUX_BUG();
+		list_del(&req->work);
+		DEBUG_DEL_LIST(&req->work);
+		DEC_STAT(nr_work_pending);
+	} else {
+		del_keepalive_timer(req);
+		DEC_STAT(nr_idle_input_pending);
+		Dprintk("unidled %p.\n", req);
+	}
+	if (req->idle_input)
+		TUX_BUG();
+	spin_unlock_irq(&ti->work_lock);
+}
+
+#define GOTO_INCOMPLETE do { Dprintk("incomplete at %s:%d.\n", __FILE__, __LINE__); goto incomplete; } while (0)
+#define GOTO_REDIRECT do { TDprintk("redirect at %s:%d.\n", __FILE__, __LINE__); goto redirect; } while (0)
+#define GOTO_REDIRECT_NONIDLE do { TDprintk("redirect at %s:%d.\n", __FILE__, __LINE__); goto redirect_nonidle; } while (0)
+
+static int read_request (struct socket *sock, char *buf, int max_size)
+{
+	mm_segment_t oldmm;
+	struct kiocb iocb;
+	struct msghdr msg;
+	struct iovec iov;
+
+	int len;
+
+	msg.msg_name     = NULL;
+	msg.msg_namelen  = 0;
+	msg.msg_iov	 = &iov;
+	msg.msg_iovlen   = 1;
+	msg.msg_control  = NULL;
+	msg.msg_controllen = 0;
+	msg.msg_flags    = 0;
+
+	msg.msg_iov->iov_base = buf;
+	msg.msg_iov->iov_len  = max_size;
+
+	oldmm = get_fs(); set_fs(KERNEL_DS);
+
+read_again:
+	init_sync_kiocb(&iocb, NULL);
+	len = sock->sk->sk_prot->recvmsg(&iocb, sock->sk, &msg, max_size,
+						MSG_DONTWAIT, MSG_PEEK, NULL);
+	if (-EIOCBQUEUED == len)
+		len = wait_on_sync_kiocb(&iocb);
+
+	/*
+	 * We must not get a signal inbetween
+	 */
+	if ((len == -EAGAIN) || (len == -ERESTARTSYS)) {
+		if (!signal_pending(current)) {
+			len = 0;
+			goto out;
+		}
+		flush_all_signals();
+		goto read_again;
+	}
+out:
+	set_fs(oldmm);
+	return len;
+}
+
+/*
+ * We inline URG data so it's at the head of the normal receive queue.
+ */
+static int zap_urg_data (struct socket *sock)
+{
+	mm_segment_t oldmm;
+	struct msghdr msg;
+	struct iovec iov;
+	struct kiocb iocb;
+	int len;
+	char buf[10];
+
+	oldmm = get_fs(); set_fs(KERNEL_DS);
+
+	msg.msg_name		= NULL;
+	msg.msg_namelen		= 0;
+	msg.msg_iov		= &iov;
+	msg.msg_iovlen		= 1;
+	msg.msg_control		= NULL;
+	msg.msg_controllen	= 0;
+	msg.msg_flags		= 0;
+
+	msg.msg_iov->iov_base = buf;
+	msg.msg_iov->iov_len  = 2;
+
+read_again:
+	init_sync_kiocb(&iocb, NULL);
+	len = sock->sk->sk_prot->recvmsg(&iocb, sock->sk, &msg, 2,
+						MSG_DONTWAIT, 0, NULL);
+	if (-EIOCBQUEUED == len)
+		len = wait_on_sync_kiocb(&iocb);
+	Dprintk("recvmsg(MSG_OOB) returned %d.\n", len);
+
+	/*
+	 * We must not get a signal inbetween
+	 */
+	if ((len == -EAGAIN) || (len == -ERESTARTSYS)) {
+		if (!signal_pending(current)) {
+			len = 0;
+			goto out;
+		}
+		flush_all_signals();
+		goto read_again;
+	}
+out:
+	set_fs(oldmm);
+
+	Dprintk("in out:.. and will return %d.!\n", len);
+
+	return len;
+}
+
+void trunc_headers (tux_req_t *req)
+{
+	struct sock *sk = req->sock->sk;
+	int len, addr_len = 0;
+	struct kiocb iocb;
+
+	if (!req->parsed_len)
+		TUX_BUG();
+repeat_trunc:
+	init_sync_kiocb(&iocb, NULL);
+	len = sk->sk_prot->recvmsg(&iocb, sk, NULL, req->parsed_len, 1, MSG_TRUNC, &addr_len);
+	if (-EIOCBQUEUED == len)
+		len = wait_on_sync_kiocb(&iocb);
+	if ((len == -ERESTARTSYS) || (len == -EAGAIN)) {
+		flush_all_signals();
+		goto repeat_trunc;
+	}
+	Dprintk("truncated (TRUNC) %d bytes at %p. (wanted: %d.)\n", len, __builtin_return_address(0), req->parsed_len);
+
+
+
+	req->parsed_len = 0;
+}
+
+void print_req (tux_req_t *req)
+{
+	struct sock *sk;
+
+	printk("PRINT req %p <%p>, sock %p\n",
+			req, __builtin_return_address(0), req->sock);
+	printk("... idx: %d\n", req->atom_idx);
+	if (req->sock) {
+		sk = req->sock->sk;
+		printk("... sock %p, sk %p, sk->state: %d, sk->err: %d\n", req->sock, sk, sk->sk_state, sk->sk_err);
+		printk("... write_queue: %d, receive_queue: %d, error_queue: %d, keepalive: %d, status: %d\n", !skb_queue_empty(&sk->sk_write_queue), !skb_queue_empty(&sk->sk_receive_queue), !skb_queue_empty(&sk->sk_error_queue), req->keep_alive, req->status);
+		printk("...tp->send_head: %p\n", sk->sk_send_head);
+		printk("...tp->snd_una: %08x\n", tcp_sk(sk)->snd_una);
+		printk("...tp->snd_nxt: %08x\n", tcp_sk(sk)->snd_nxt);
+		printk("...tp->packets_out: %08x\n", tcp_sk(sk)->packets_out);
+	}
+	printk("... meth:{%s}, uri:{%s}, query:{%s}, ver:{%s}\n", req->method_str ? req->method_str : "<null>", req->uri_str ? req->uri_str : "<null>", req->query_str ? req->query_str : "<null>", req->version_str ? req->version_str : "<null>");
+	printk("... post_data:{%s}(%d).\n", req->post_data_str, req->post_data_len);
+	printk("... headers: {%s}\n", req->headers);
+}
+/*
+ * parse_request() reads all available TCP/IP data and prepares
+ * the request if the TUX request is complete. (we can get TUX
+ * requests in several packets.) Invalid requests are redirected
+ * to the secondary server.
+ */
+
+void parse_request (tux_req_t *req, int cachemiss)
+{
+	int len, parsed_len;
+	struct sock *sk = req->sock->sk;
+	struct tcp_sock *tp = tcp_sk(sk);
+	struct inet_connection_sock *icsk = inet_csk(sk);
+	int was_keepalive = req->keep_alive;
+
+	if (req->magic != TUX_MAGIC)
+		TUX_BUG();
+
+	SET_TIMESTAMP(req->parse_timestamp);
+
+	spin_lock_irq(&req->ti->work_lock);
+	add_keepalive_timer(req);
+	if (test_and_set_bit(0, &req->idle_input))
+		TUX_BUG();
+	INC_STAT(nr_idle_input_pending);
+	spin_unlock_irq(&req->ti->work_lock);
+
+	Dprintk("idled request %p.\n", req);
+
+restart:
+
+	if (tp->urg_data && !(tp->urg_data & TCP_URG_READ)) {
+		len = zap_urg_data(req->sock);
+		if (tp->urg_data && !(tp->urg_data & TCP_URG_READ)) {
+			req->error = TUX_ERROR_CONN_CLOSE;
+			goto redirect_error;
+		}
+	}
+
+	INC_STAT(input_slowpath);
+
+	if (!req->headers)
+		req->headers = tux_kmalloc(tux_max_header_len);
+
+	/* First, read the data */
+	len = read_request(req->sock, (char *)req->headers, tux_max_header_len-1);
+	if (len < 0) {
+		req->error = TUX_ERROR_CONN_CLOSE;
+		goto redirect_error;
+	}
+	if (!len)
+		GOTO_INCOMPLETE;
+
+	/*
+	 * Make it a zero-delimited string to automatically get
+	 * protection against various buffer overflow situations.
+	 * Then pass it to the TUX application protocol stack.
+	 */
+	((char *)req->headers)[len] = 0;
+	req->headers_len = len;
+
+	parsed_len = req->proto->parse_message(req, len);
+
+	/*
+	 * Is the request fully read? (or is there any error)
+	 */
+	if (parsed_len < 0)
+		GOTO_REDIRECT;
+	if (!parsed_len) {
+		/*
+		 * Push pending ACK which was delayed due to the
+		 * pingpong optimization:
+		 */
+		if (was_keepalive) {
+			lock_sock(sk);
+			icsk->icsk_ack.pingpong = 0;
+			icsk->icsk_ack.pending |= ICSK_ACK_PUSHED;
+			tcp_cleanup_rbuf(sk, 1);
+			release_sock(sk);
+		}
+		if (len >= tux_max_header_len-1)
+			GOTO_REDIRECT;
+		GOTO_INCOMPLETE;
+	}
+	unidle_req(req);
+
+	tp->nonagle = 2;
+
+	add_req_to_workqueue(req);
+	return;
+
+redirect:
+	TDprintk("req %p will be redirected!\n", req);
+	req_err(req);
+
+redirect_error:
+	unidle_req(req);
+
+	if (len < 0)
+		req->parsed_len = 0;
+	else
+		req->parsed_len = len;
+
+	INC_STAT(parse_static_redirect);
+	if (req->headers)
+		kfree(req->headers);
+	req->headers = NULL;
+	if (req->error)
+		zap_request(req, cachemiss);
+	return;
+
+incomplete:
+	if (req->error)
+		goto redirect_error;
+	if (tp->urg_data && !(tp->urg_data & TCP_URG_READ))
+		goto restart;
+
+	add_tux_atom(req, parse_request);
+	INC_STAT(parse_static_incomplete);
+	tux_push_req(req);
+}
+
+int process_requests (threadinfo_t *ti, tux_req_t **user_req)
+{
+	struct list_head *head, *curr;
+	int count = 0;
+	tux_req_t *req;
+
+	*user_req = NULL;
+
+restart_loop:
+	spin_lock_irq(&ti->work_lock);
+	head = &ti->work_pending;
+	curr = head->next;
+
+	if (curr != head) {
+		int i;
+
+		req = list_entry(curr, tux_req_t, work);
+		Dprintk("PROCESS req %p <%p>.\n",
+			req, __builtin_return_address(0));
+		for (i = 0; i < req->atom_idx; i++)
+			Dprintk("... atom %d: %p\n", i, req->atoms[i]);
+
+		if (req->ti != ti)
+			TUX_BUG();
+		if (req->magic != TUX_MAGIC)
+			TUX_BUG();
+
+		if (list_empty(&req->work))
+			TUX_BUG();
+		list_del(curr);
+		DEBUG_DEL_LIST(&req->work);
+		spin_unlock_irq(&ti->work_lock);
+
+		if (!req->atom_idx) {
+			if (req->usermode) {
+				*user_req = req;
+				return count;
+			}
+			/*
+			 * idx == 0 requests are flushed automatically.
+			 */
+			flush_request(req, 0);
+		} else
+			tux_schedule_atom(req, 0);
+		count++;
+		goto restart_loop;
+	}
+	spin_unlock_irq(&ti->work_lock);
+
+	return count;
+}
+
+int tux_flush_workqueue (threadinfo_t *ti)
+{
+	struct list_head *head, *curr, *next;
+	tux_req_t *req;
+	int count = 0;
+
+restart:
+	spin_lock_irq(&ti->work_lock);
+	head = &ti->work_pending;
+	curr = head->next;
+
+	if (curr != head) {
+		req = list_entry(curr, tux_req_t, work);
+		next = curr->next;
+		clear_bit(0, &req->idle_input);
+		clear_bit(0, &req->wait_output_space);
+		if (list_empty(&req->work))
+			TUX_BUG();
+		list_del(curr);
+		DEBUG_DEL_LIST(curr);
+		DEC_STAT(nr_input_pending);
+		spin_unlock_irq(&ti->work_lock);
+#ifdef CONFIG_TUX_DEBUG
+		req->bytes_expected = 0;
+#endif
+		req->in_file->f_pos = 0;
+		req->atom_idx = 0;
+		clear_keepalive(req);
+		req->status = -1;
+		if (req->usermode) {
+			req->usermode = 0;
+			req->private = 0;
+		}
+		flush_request(req, 0);
+		count++;
+		goto restart;
+	}
+	spin_unlock_irq(&ti->work_lock);
+
+	return count;
+}
+
+int print_all_requests (threadinfo_t *ti)
+{
+	struct list_head *head, *curr;
+	tux_req_t *req;
+	int count = 0;
+
+	spin_lock_irq(&ti->work_lock);
+	head = &ti->all_requests;
+	curr = head->next;
+
+	while (curr != head) {
+		req = list_entry(curr, tux_req_t, all);
+		curr = curr->next;
+		print_req(req);
+		count++;
+	}
+	spin_unlock_irq(&ti->work_lock);
+
+	return count;
+}
+
Index: latest/net/tux/Kconfig
===================================================================
--- /dev/null
+++ latest/net/tux/Kconfig
@@ -0,0 +1,25 @@
+
+config TUX
+	tristate "TUX: Threaded linUX application protocol accelerator layer"
+	default y if INET=y
+	select ZLIB_DEFLATE
+	help
+	  This is the TUX content-accelerator/server
+
+menu "TUX options"
+	depends on TUX
+
+config TUX_EXTCGI
+	bool "External CGI module"
+	default y
+
+config TUX_EXTENDED_LOG
+	bool "extended TUX logging format"
+	default n
+
+config TUX_DEBUG
+	bool "debug TUX"
+	default n
+
+endmenu
+
Index: latest/net/tux/logger.c
===================================================================
--- /dev/null
+++ latest/net/tux/logger.c
@@ -0,0 +1,841 @@
+/*
+ * TUX - Integrated Application Protocols Layer and Object Cache
+ *
+ * Copyright (C) 2000, 2001, Ingo Molnar <mingo@redhat.com>
+ *
+ * Cleaned up logger output for Alpha.
+ * -- Phil Ezolt (Phillip.Ezolt@compaq.com) & Bill Carr (wcarr92@yahoo.com)
+ *
+ * logger.c: log requests finished by TUX.
+ */
+
+#define __KERNEL_SYSCALLS__
+#include <net/tux.h>
+
+/****************************************************************
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2, or (at your option)
+ *      any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ ****************************************************************/
+
+static DEFINE_SPINLOCK(log_lock);
+static unsigned int log_head, log_tail;
+static char * log_buffer = NULL;
+static DECLARE_WAIT_QUEUE_HEAD(log_wait);
+static DECLARE_WAIT_QUEUE_HEAD(log_full);
+static int logger_pid = 0;
+
+/*
+ * High-speed TUX logging architecture:
+ *
+ * All fast threads share a common log-ringbuffer. (default size 1MB)
+ * Log entries are binary and are padded to be cacheline aligned, this
+ * ensures that there is no cache-pingpong between fast threads.
+ *
+ * The logger thread writes out pending log entries within 1 second
+ * (buffer-cache writes data out within 5 seconds). The logger thread
+ * gets activated once we have more than 25% of the log ringbuffer
+ * filled - or the 1 second log timeout expires. Fast threads block
+ * if if more than 95% of the ringbuffer is filled and unblock only
+ * if used logbuffer space drops below 90%.
+ *
+ * This architecture guarantees that 1) logging is reliable (no
+ * log entry is ever lost), 2) timely (touches disk within 6 seconds),
+ * 3) in the log-contention case the saturation behavior is still
+ * write-clustered, but 4) if the logger thread can keep up then
+ * the coupling is completely asynchron and parallel.
+ *
+ * The binary log format gives us about 50% saved IO/memory bandwith
+ * and 50% less on-disk used log space than the traditional W3C ASCII
+ * format.
+ *
+ * (We might switch to raw IO though to write the logfile.)
+ */
+
+#define SOFT_LIMIT		(LOG_LEN*25/100)
+#define HARD_LIMIT		(LOG_LEN*95/100)
+#define HARD_RELAX_LIMIT	(LOG_LEN*90/100)
+
+unsigned int tux_logentry_align_order = 5;
+
+#if SMP_CACHE_BYTES == 8
+# define TUX_LOGENTRY_ALIGN 3
+#else
+#if SMP_CACHE_BYTES == 16
+# define TUX_LOGENTRY_ALIGN 4
+#else
+#if SMP_CACHE_BYTES == 32
+# define TUX_LOGENTRY_ALIGN 5
+#else
+#if SMP_CACHE_BYTES == 64
+# define TUX_LOGENTRY_ALIGN 6
+#else
+#if SMP_CACHE_BYTES == 128
+# define TUX_LOGENTRY_ALIGN 7
+#else
+#if SMP_CACHE_BYTES == 256
+# define TUX_LOGENTRY_ALIGN 8
+#else
+#error Add entry!
+#endif
+#endif
+#endif
+#endif
+#endif
+#endif
+
+#define ROUND_UP(x) (((((x)-1) >> TUX_LOGENTRY_ALIGN) + 1) \
+					<< TUX_LOGENTRY_ALIGN)
+
+static void __throttle_logging (void)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	int pending;
+
+	add_wait_queue(&log_full, &wait);
+	for (;;) {
+		static unsigned long last_warning = 0;
+
+		if (jiffies - last_warning > 10*HZ) {
+			last_warning = jiffies;
+			printk(KERN_NOTICE "TUX: log buffer overflow, have to throttle TUX thread!\n");
+		}
+
+		current->state = TASK_INTERRUPTIBLE;
+
+		spin_lock(&log_lock);
+		pending = log_head-log_tail;
+		spin_unlock(&log_lock);
+
+		if ((pending % LOG_LEN) < HARD_LIMIT)
+			break;
+
+		schedule();
+	}
+	current->state = TASK_RUNNING;
+	remove_wait_queue(&log_full, &wait);
+}
+
+#ifdef CONFIG_TUX_DEBUG
+#define CHECK_LOGPTR(ptr) \
+do { \
+	if ((ptr < log_buffer) || (ptr > log_buffer + LOG_LEN)) { \
+		printk(KERN_ERR "TUX: ouch: log ptr %p > %p + %ld!\n", \
+			ptr, log_buffer, LOG_LEN); \
+		TUX_BUG(); \
+	} \
+} while (0)
+#else
+#define CHECK_LOGPTR(ptr) do { } while (0)
+#endif
+
+void __log_request (tux_req_t *req)
+{
+	char *str, *next;
+	const char *uri_str;
+	unsigned int inc, len, uri_len, pending, next_head, def_vhost_len = 0;
+	unsigned long flags;
+
+	if (req->proto->pre_log)
+		req->proto->pre_log(req);
+	/*
+	 * Log the reply status (success, or type of failure)
+	 */
+	if (!tux_log_incomplete && (!req->status || (req->bytes_sent == -1))) {
+
+		Dprintk("not logging req %p: {%s} [%d/%d]\n", req, req->uri_str, req->status, req->bytes_sent);
+		return;
+	}
+	Dprintk("uri: {%s} [%d]\n", req->uri_str, req->uri_len);
+
+#define NO_URI "<none>"
+	if (req->uri_len) {
+		uri_len = req->uri_len;
+		uri_str = req->uri_str;
+	} else {
+		uri_str = NO_URI;
+		uri_len = sizeof(NO_URI)-1;
+	}
+	len = uri_len + 1;
+
+	if (req->virtual) {
+		if (req->host_len)
+			len += req->host_len;
+		else {
+			def_vhost_len = strlen(tux_default_vhost);
+			len += def_vhost_len;
+		}
+	}
+
+	Dprintk("method_str: {%s} [%d]\n", req->method_str, req->method_len);
+	len += req->method_len + 1;
+
+	Dprintk("version_str: {%s} [%d]\n", req->version_str, req->version_len);
+	len += req->version_len + 1;
+
+#ifdef CONFIG_TUX_EXTENDED_LOG
+	Dprintk("user_agent_str: {%s} [%d]\n", req->user_agent_str, req->user_agent_len);
+	len += req->user_agent_len + 1;
+#endif
+	if (tux_referer_logging) {
+		Dprintk("referer_str: {%s} [%d]\n", req->referer_str, req->referer_len);
+		len += req->referer_len;
+	}
+	len++;
+
+	inc = 5*sizeof(u32) + len;
+#ifdef CONFIG_TUX_EXTENDED_LOG
+	inc += 7*sizeof(u32);
+#endif
+
+	spin_lock_irqsave(&log_lock, flags);
+
+	next_head = ROUND_UP(log_head + inc);
+
+	if (next_head < LOG_LEN) {
+		str = log_buffer + log_head;
+		if (str > log_buffer + LOG_LEN)
+			TUX_BUG();
+		log_head = next_head;
+	} else {
+		if (log_head < LOG_LEN)
+			memset(log_buffer+log_head, 0, LOG_LEN-log_head);
+		str = log_buffer;
+		log_head = ROUND_UP(inc);
+	}
+
+	if (str < log_buffer || str+inc >= log_buffer+LOG_LEN)
+		TUX_BUG();
+
+	/*
+	 * Log record signature - this makes finding the next entry
+	 * easier (since record length is variable), and makes the
+	 * binary logfile more robust against potential data corruption
+	 * and other damage. The signature also servers as a log format
+	 * version identifier.
+	 */
+#ifdef CONFIG_TUX_EXTENDED_LOG
+	*(u32 *)str = 0x2223beef;
+#else
+	*(u32 *)str = 0x1112beef;
+#endif
+	str += sizeof(u32);
+	CHECK_LOGPTR(str);
+
+	*(u32 *)str = 0;
+	/*
+	 * Log the client IP address:
+	 */
+	if (tux_ip_logging)
+		*(u32 *)str = req->client_addr;
+	str += sizeof(u32);
+	CHECK_LOGPTR(str);
+
+#ifdef CONFIG_TUX_EXTENDED_LOG
+	/*
+	 * Log the client port number:
+	 */
+	*(u32 *)str = 0;
+	if (tux_ip_logging)
+		*(u32 *)str = req->client_port;
+	str += sizeof(u32);
+	CHECK_LOGPTR(str);
+#endif
+
+	/*
+	 * Log the request timestamp, in units of 'seconds since 1970'.
+	 */
+	*(u32 *)str = CURRENT_TIME.tv_sec;
+	str += sizeof(u32);
+	CHECK_LOGPTR(str);
+
+#ifdef CONFIG_TUX_EXTENDED_LOG
+	*(u32 *)str = req->accept_timestamp; str += sizeof(u32);
+	*(u32 *)str = req->parse_timestamp; str += sizeof(u32);
+	*(u32 *)str = req->output_timestamp; str += sizeof(u32);
+	*(u32 *)str = req->flush_timestamp; str += sizeof(u32);
+	*(u32 *)str = req->had_cachemiss; str += sizeof(u32);
+	*(u32 *)str = req->keep_alive; str += sizeof(u32);
+#endif
+	/*
+	 * Log the requested file size (in fact, log actual bytes sent.)
+	 */
+	*(u32 *)str = req->bytes_sent;
+	str += sizeof(u32);
+	CHECK_LOGPTR(str);
+
+	*(u32 *)str = req->status;
+	str += sizeof(u32);
+	CHECK_LOGPTR(str);
+
+	/*
+	 * Zero-terminated method, (base) URI, query and version string.
+	 */
+	if (req->method_len) {
+		memcpy(str, req->method_str, req->method_len);
+		str += req->method_len;
+		CHECK_LOGPTR(str);
+	}
+	*str++ = 0;
+
+	if (req->virtual) {
+		if (req->host_len) {
+			memcpy(str, req->host, req->host_len);
+			str += req->host_len;
+		} else {
+			memcpy(str, tux_default_vhost, def_vhost_len);
+			str += def_vhost_len;
+		}
+		CHECK_LOGPTR(str);
+	}
+
+	memcpy(str, uri_str, uri_len);
+	str += uri_len;
+	*str++ = 0;
+
+	CHECK_LOGPTR(str);
+
+	if (req->version_len) {
+		memcpy(str, req->version_str, req->version_len);
+		str += req->version_len;
+		CHECK_LOGPTR(str);
+	}
+	*str++ = 0;
+#ifdef CONFIG_TUX_EXTENDED_LOG
+	if (req->user_agent_len) {
+		memcpy(str, req->user_agent_str, req->user_agent_len);
+		str += req->user_agent_len;
+		CHECK_LOGPTR(str);
+	}
+	*str++ = 0;
+#endif
+	CHECK_LOGPTR(str);
+
+	if (tux_referer_logging && req->referer_len) {
+		memcpy(str, req->referer_str, req->referer_len);
+		str += req->referer_len;
+		CHECK_LOGPTR(str);
+	}
+	*str++ = 0;
+	CHECK_LOGPTR(str);
+	/*
+	 * pad with spaces to next cacheline, with an ending newline.
+	 * (not needed for the user-space log utility, but results in
+	 * a more readable binary log file, and reduces the amount
+	 * of cache pingpong.)
+	 */
+	next = (char *)ROUND_UP((unsigned long)str);
+
+	CHECK_LOGPTR(next);
+	len = next-str;
+	memset(str, ' ', len);
+
+	pending = (log_head-log_tail) % LOG_LEN;
+	spin_unlock_irqrestore(&log_lock, flags);
+
+	if (pending >= SOFT_LIMIT)
+		wake_up(&log_wait);
+
+	if (pending >= HARD_LIMIT)
+		__throttle_logging();
+}
+
+void tux_push_pending (struct sock *sk)
+{
+	struct tcp_sock *tp = tcp_sk(sk);
+	struct inet_connection_sock *icsk = inet_csk(sk);
+
+	Dprintk("pushing pending frames on sock %p.\n", sk);
+	lock_sock(sk);
+	if ((sk->sk_state == TCP_ESTABLISHED) && !sk->sk_err) {
+		icsk->icsk_ack.pingpong = tux_ack_pingpong;
+		tp->nonagle = 1;
+		__tcp_push_pending_frames(sk, tp, tcp_current_mss(sk, 0), TCP_NAGLE_OFF);
+	}
+	release_sock(sk);
+}
+
+inline void tux_push_req (tux_req_t *req)
+{
+	if (req->sock)
+		tux_push_pending(req->sock->sk);
+	if (req->data_sock)
+		tux_push_pending(req->data_sock->sk);
+}
+
+void __put_data_sock (tux_req_t *req)
+{
+	unlink_tux_data_socket(req);
+	if (req->data_sock->file)
+		fput(req->data_sock->file);
+	else
+		sock_release(req->data_sock);
+	req->data_sock = NULL;
+}
+
+void flush_request (tux_req_t *req, int cachemiss)
+{
+	struct socket *sock;
+	struct sock *sk;
+	int keep_alive;
+
+	if (cachemiss)
+		TUX_BUG();
+	__set_task_state(current, TASK_RUNNING);
+
+	if (req->magic != TUX_MAGIC)
+		TUX_BUG();
+	if (req->ti->thread != current)
+		TUX_BUG();
+#ifdef CONFIG_TUX_DEBUG
+	if (req->bytes_expected && (req->bytes_sent != req->bytes_expected)) {
+		printk("hm, bytes_expected: %d != bytes_sent: %d!\n",
+			req->bytes_expected, req->bytes_sent);
+		TUX_BUG();
+	}
+#endif
+	SET_TIMESTAMP(req->flush_timestamp);
+
+	log_request(req);
+	sock = req->sock;
+	sk = NULL;
+	if (sock)
+		sk = sock->sk;
+	Dprintk("FLUSHING req %p <%p> (sock %p, sk %p) (keepalive: %d, status: %d)\n", req, __builtin_return_address(0), sock, sk, req->keep_alive, req->status);
+	if (req->in_file->f_pos)
+		/*TUX_BUG()*/;
+	release_req_dentry(req);
+	req->private = 0;
+
+	if (req->docroot_dentry) {
+		dput(req->docroot_dentry);
+		req->docroot_dentry = NULL;
+		if (!req->docroot_mnt)
+			TUX_BUG();
+	}
+	if (req->docroot_mnt) {
+		mntput(req->docroot_mnt);
+		req->docroot_mnt = NULL;
+	}
+
+	req->offset_start = 0;
+	req->offset_end = 0;
+	req->output_len = 0;
+	req->total_file_len = 0;
+	req->lendigits = 0;
+	req->mtime = 0;
+	req->etaglen = 0;
+	req->etag[0] = 0;
+	req->ftp_command = 0;
+
+	if (req->postponed)
+		TUX_BUG();
+	if (test_bit(0, &req->idle_input))
+		TUX_BUG();
+	if (test_bit(0, &req->wait_output_space))
+		TUX_BUG();
+	if (req->parsed_len)
+		trunc_headers(req);
+	if (req->parsed_len)
+		TUX_BUG();
+	req->attr = NULL;
+	req->usermode = 0;
+	req->usermodule_idx = 0;
+	req->atom_idx = 0;
+	if (req->module_dentry) {
+		dput(req->module_dentry);
+		req->module_dentry = NULL;
+	}
+	if (req->headers)
+		kfree(req->headers);
+	req->headers = NULL;
+	req->headers_len = 0;
+
+	req->method = METHOD_NONE;
+	req->method_len = 0;
+	req->method_str = NULL;
+	req->version = 0;
+	req->version_str = NULL;
+	req->version_len = 0;
+
+	req->uri_str = NULL;
+	req->uri_len = 0;
+
+	req->objectname[0] = 0;
+	req->objectname_len = 0;
+
+	req->query_str = NULL;
+	req->query_len = 0;
+
+	req->cookies_str = NULL;
+	req->cookies_len = 0;
+	req->parse_cookies = 0;
+
+	req->contentlen_str = NULL;
+	req->contentlen_len = 0;
+	req->content_len = 0;
+
+	req->user_agent_str = NULL;
+	req->user_agent_len = 0;
+
+	req->may_send_gzip = 0;
+	req->content_gzipped = 0;
+
+	req->content_type_str = NULL;
+	req->content_type_len = 0;
+
+	req->accept_str = NULL;
+	req->accept_len = 0;
+
+	req->accept_charset_str = NULL;
+	req->accept_charset_len = 0;
+
+	req->accept_encoding_str = NULL;
+	req->accept_encoding_len = 0;
+
+	req->accept_language_str = NULL;
+	req->accept_language_len = 0;
+
+	req->cache_control_str = NULL;
+	req->cache_control_len = 0;
+
+	req->if_modified_since_str = NULL;
+	req->if_modified_since_len = 0;
+
+	req->if_none_match_str = NULL;
+	req->if_none_match_len = 0;
+
+	req->if_range_str = NULL;
+	req->if_range_len = 0;
+
+	req->negotiate_str = NULL;
+	req->negotiate_len = 0;
+
+	req->pragma_str = NULL;
+	req->pragma_len = 0;
+
+	req->referer_str = NULL;
+	req->referer_len = 0;
+
+	req->post_data_str = NULL;
+	req->post_data_len = 0;
+
+	SET_TIMESTAMP(req->accept_timestamp);
+#ifdef CONFIG_TUX_EXTENDED_LOG
+	req->parse_timestamp = 0;
+	req->output_timestamp = 0;
+	req->flush_timestamp = 0;
+#endif
+	req->status = 0;
+
+	req->total_bytes += req->bytes_sent;
+	req->bytes_sent = 0;
+#ifdef CONFIG_TUX_DEBUG
+	req->bytes_expected = 0;
+#endif
+	req->body_len = 0;
+	keep_alive = req->keep_alive;
+	clear_keepalive(req);
+	req->had_cachemiss = 0;
+	// first_timestamp and total_bytes is kept!
+	req->event = 0;
+	req->lookup_dir = 0;
+	req->lookup_404 = 0;
+
+	req->error = 0;
+	req->user_error = 0;
+
+	if (req->abuf.page)
+		__free_page(req->abuf.page);
+	memset(&req->abuf, 0, sizeof(req->abuf));
+
+	if (sk && keep_alive) {
+		add_tux_atom(req, parse_request);
+		if (skb_queue_empty(&sk->sk_receive_queue)) {
+			spin_lock_irq(&req->ti->work_lock);
+			add_keepalive_timer(req);
+			if (test_and_set_bit(0, &req->idle_input))
+				TUX_BUG();
+			/*
+			 * Avoid the race with the event callback:
+			 */
+			if (skb_queue_empty(&sk->sk_receive_queue) ||
+				   !test_and_clear_bit(0, &req->idle_input)) {
+				INC_STAT(nr_idle_input_pending);
+				spin_unlock_irq(&req->ti->work_lock);
+				tux_push_req(req);
+				goto out;
+			}
+			del_keepalive_timer(req);
+			spin_unlock_irq(&req->ti->work_lock);
+		}
+		Dprintk("KEEPALIVE PENDING req %p <%p> (sock %p, sk %p) (keepalive: %d, status: %d)\n", req, __builtin_return_address(0), req->sock, req->sock->sk, req->keep_alive, req->status);
+		add_req_to_workqueue(req);
+		INC_STAT(nr_keepalive_optimized);
+		goto out;
+	}
+
+	del_timer_sync(&req->keepalive_timer);
+	del_timer_sync(&req->output_timer);
+
+	if (timer_pending(&req->keepalive_timer))
+		TUX_BUG();
+	if (timer_pending(&req->output_timer))
+		TUX_BUG();
+	if (!list_empty(&req->lru))
+		TUX_BUG();
+	req->nr_keepalives = 0;
+	req->client_addr = 0;
+	req->client_port = 0;
+	req->virtual = 0;
+	req->ftp_offset_start = 0;
+
+	req->host[0] = 0;
+	req->host_len = 0;
+
+	if (req->cwd_dentry) {
+		dput(req->cwd_dentry);
+		req->cwd_dentry = NULL;
+		if (!req->cwd_mnt)
+			TUX_BUG();
+	}
+	if (req->cwd_mnt) {
+		mntput(req->cwd_mnt);
+		req->cwd_mnt = NULL;
+	}
+	put_data_sock(req);
+	req->prev_pos = 0;
+	req->curroff = 0;
+	req->total = 0;
+	if (req->dirp0) {
+		kfree(req->dirp0);
+		req->dirp0 = NULL;
+	}
+
+	if (sk)
+		unlink_tux_socket(req);
+	req->sock = NULL;
+	/*
+	 * Close potential user-space file descriptors.
+	 */
+	{
+		int fd = req->fd, ret;
+
+		if (fd != -1) {
+			Dprintk("closing req->fd: %d\n", fd);
+			req->fd = -1;
+			ret = tux_close(fd);
+			if (ret)
+				TUX_BUG();
+		} else
+			if (sock)
+				sock_release(sock);
+	}
+	kfree_req(req);
+out:
+	;
+}
+
+static int warn_once = 1;
+
+static loff_t log_filp_last_index;
+
+static unsigned int writeout_log (void)
+{
+	unsigned int len, pending, next_log_tail;
+	mm_segment_t oldmm = get_fs();
+	struct file *log_filp;
+	char * str;
+	unsigned int ret;
+	struct inode *inode;
+	struct address_space *mapping;
+
+	if (tux_logging)
+		Dprintk("TUX logger: opening log file {%s}.\n", tux_logfile);
+	log_filp = tux_open_file(tux_logfile, O_CREAT|O_APPEND|O_WRONLY|O_LARGEFILE);
+	if (!log_filp) {
+		if (warn_once) {
+			printk(KERN_ERR "TUX: could not open log file {%s}!\n",
+				tux_logfile);
+			warn_once = 0;
+		}
+		__set_current_state(TASK_INTERRUPTIBLE);
+		schedule_timeout(HZ);
+		return 0;
+	}
+	spin_lock(&log_lock);
+	str = log_buffer + log_tail;
+	if (log_head < log_tail) {
+		len = LOG_LEN-log_tail;
+		next_log_tail = 0;
+	} else {
+		len = log_head-log_tail;
+		next_log_tail = log_head;
+	}
+	if (!len)
+		goto out;
+	spin_unlock(&log_lock);
+
+	set_fs(KERNEL_DS);
+	ret = log_filp->f_op->write(log_filp, str, len, &log_filp->f_pos);
+	set_fs(oldmm);
+
+	if (len != ret) {
+		if (ret == -ENOSPC) {
+			printk(KERN_ERR "TUX: trying to write TUX logfile %s, but filesystem is full! Lost %d bytes of log data.\n", tux_logfile, len);
+		} else {
+			printk(KERN_ERR "TUX: log write %d != %d.\n", ret, len);
+			printk(KERN_ERR "TUX: log_filp: %p, str: %p, len: %d str[len-1]: %d.\n", log_filp, str, len, str[len-1]);
+		}
+		goto out_lock;
+	}
+
+	/*
+	 * Sync log data to disk:
+	 */
+	inode = log_filp->f_dentry->d_inode;
+	mapping = inode->i_mapping;
+	if (mapping->nrpages > 256) {   /* batch stuff up */
+		mutex_lock(&inode->i_mutex);
+		filemap_fdatawrite(inode->i_mapping);
+
+		/*
+		 * Now nuke old pagecache up to the place where we just
+		 * started the I/O.   There's no point in trying to invalidate
+		 * pages after that, because they're currently in-flight.
+		 */
+		invalidate_mapping_pages(mapping, 0, log_filp_last_index);
+		log_filp_last_index = log_filp->f_pos >> PAGE_CACHE_SHIFT;
+		mutex_unlock(&inode->i_mutex);
+	}
+
+out_lock:
+	spin_lock(&log_lock);
+out:
+	log_tail = next_log_tail;
+	pending = (log_head-log_tail) % LOG_LEN;
+	spin_unlock(&log_lock);
+
+	if (pending < HARD_LIMIT)
+		wake_up(&log_full);
+
+	fput(log_filp);
+	return pending;
+}
+
+static DECLARE_WAIT_QUEUE_HEAD(stop_logger_wait);
+static int stop_logger = 0;
+
+static int logger_thread (void *data)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	mm_segment_t oldmm;
+
+	daemonize("TUX logger");
+
+	oldmm = get_fs();
+	set_fs(KERNEL_DS);
+	printk(KERN_NOTICE "TUX: logger thread started.\n");
+#ifdef CONFIG_SMP
+	{
+		cpumask_t map;
+
+		cpus_and(map, cpu_online_map, tux_log_cpu_mask);
+		if (!(cpus_empty(map)))
+			set_cpus_allowed(current, map);
+
+	}
+#endif
+
+
+	spin_lock_irq(&current->sighand->siglock);
+	siginitsetinv(&current->blocked, 0);
+	recalc_sigpending();
+	spin_unlock_irq(&current->sighand->siglock);
+
+	if (log_buffer)
+		TUX_BUG();
+	log_buffer = vmalloc(LOG_LEN);
+	if (!log_buffer) {
+		TUX_BUG();
+		goto out;
+	}
+	memset(log_buffer, 0, LOG_LEN);
+	log_head = log_tail = 0;
+
+	current->signal->rlim[RLIMIT_FSIZE].rlim_cur = RLIM_INFINITY;
+
+	add_wait_queue(&log_wait, &wait);
+	for (;;) {
+		if (tux_logging)
+			Dprintk("logger does writeout - stop:%d.\n", stop_logger);
+
+		while (writeout_log() >= SOFT_LIMIT) {
+			if (stop_logger)
+				break;
+		}
+		if (stop_logger)
+			break;
+			/* nothing */;
+
+		if (tux_logging)
+			Dprintk("logger does sleep - stop:%d.\n", stop_logger);
+		__set_current_state(TASK_INTERRUPTIBLE);
+		if (log_head != log_tail) {
+			__set_current_state(TASK_RUNNING);
+			continue;
+		}
+		schedule_timeout(HZ);
+		if (tux_logging)
+			Dprintk("logger back from sleep - stop:%d.\n", stop_logger);
+		if (signal_pending(current))
+			flush_all_signals();
+	}
+	remove_wait_queue(&log_wait, &wait);
+
+	vfree(log_buffer);
+	log_buffer = NULL;
+	stop_logger = 0;
+	wake_up(&stop_logger_wait);
+out:
+	set_fs(oldmm);
+
+	return 0;
+}
+
+void start_log_thread (void)
+{
+	warn_once = 1;
+
+	logger_pid = kernel_thread(logger_thread, NULL, 0);
+	if (logger_pid < 0)
+		TUX_BUG();
+}
+
+void stop_log_thread (void)
+{
+	DECLARE_WAITQUEUE(wait, current);
+
+	Dprintk("stopping logger thread %d ...\n", logger_pid);
+
+	__set_current_state(TASK_UNINTERRUPTIBLE);
+	add_wait_queue(&stop_logger_wait, &wait);
+	stop_logger = 1;
+	wake_up(&log_wait);
+	schedule();
+	__set_current_state(TASK_RUNNING);
+	remove_wait_queue(&stop_logger_wait, &wait);
+
+	Dprintk("logger thread stopped!\n");
+}
Index: latest/net/tux/main.c
===================================================================
--- /dev/null
+++ latest/net/tux/main.c
@@ -0,0 +1,1417 @@
+/*
+ * TUX - Integrated Application Protocols Layer and Object Cache
+ *
+ * Copyright (C) 2000, 2001, Ingo Molnar <mingo@redhat.com>
+ *
+ * main.c: main management and initialization routines
+ */
+
+#define __KERNEL_SYSCALLS__
+#define __KERNEL_SYSCALLS_NO_ERRNO__
+
+#include <net/tux.h>
+
+/****************************************************************
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2, or (at your option)
+ *      any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ ****************************************************************/
+
+/*
+ * Threads information.
+ */
+unsigned int nr_tux_threads;
+static atomic_t nr_tux_threads_running = ATOMIC_INIT(0);
+static int stop_threads = 0;
+
+threadinfo_t threadinfo[CONFIG_TUX_NUMTHREADS];
+
+static void flush_all_requests (threadinfo_t *ti);
+
+void flush_all_signals (void)
+{
+	flush_signals(current);
+	spin_lock_irq(&current->sighand->siglock);
+	recalc_sigpending();
+	spin_unlock_irq(&current->sighand->siglock);
+}
+
+int nr_requests_used (void)
+{
+	unsigned int i, nr = 0;
+
+	for (i = 0; i < nr_tux_threads; i++) {
+		threadinfo_t *ti = threadinfo + i;
+		nr += ti->nr_requests - ti->nr_free_requests;
+	}
+
+	return nr;
+}
+
+static inline int accept_pending (threadinfo_t *ti)
+{
+	int j;
+
+	for (j = 0; j < CONFIG_TUX_NUMSOCKETS; j++) {
+		if (!ti->listen[j].proto)
+			break;
+		if (!ti->listen[j].sock)
+			break;
+		if (!reqsk_queue_empty(&inet_csk(ti->listen[j].sock->sk)->icsk_accept_queue))
+			return 1;
+	}
+	return 0;
+}
+
+static inline int requests_pending (threadinfo_t *ti)
+{
+	if (!list_empty(&ti->work_pending))
+		return 1;
+	return 0;
+}
+
+static int event_loop (threadinfo_t *ti)
+{
+	tux_req_t *req;
+	int work_done;
+
+repeat_accept:
+	if (ti->thread != current)
+		TUX_BUG();
+
+	/*
+	 * Any (relevant) event on the socket will change this
+	 * thread to TASK_RUNNING because we add it to both
+	 * the main listening and the connection request socket
+	 * waitqueues. Thus we can do 'lazy checking' of work
+	 * to be done and schedule away only if the thread is
+	 * still TASK_INTERRUPTIBLE. This makes TUX fully
+	 * event driven.
+	 */
+	set_task_state(current, TASK_INTERRUPTIBLE);
+	current->flags |= PF_MEMALLOC;
+	work_done = 0;
+	if (accept_pending(ti))
+		work_done = accept_requests(ti);
+
+	if (requests_pending(ti)) {
+		work_done = process_requests(ti, &req);
+		if (req)
+			goto handle_userspace_req;
+	}
+
+	/*
+	 * Be nice to other processes:
+	 */
+	if (unlikely(test_thread_flag(TIF_NEED_RESCHED))) {
+		__set_task_state(current, TASK_RUNNING);
+		schedule();
+		goto repeat_accept;
+	}
+
+	if (ti->userspace_req)
+		TUX_BUG();
+	if (unlikely(stop_threads))
+		goto handle_stop;
+
+	/* Any signals? */
+	if (unlikely(signal_pending(current)))
+		goto handle_signal;
+
+	if (work_done)
+		goto repeat_accept;
+	/*
+	 * Any socket event either on the listen socket
+	 * or on the request sockets will wake us up:
+	 */
+	if ((current->state != TASK_RUNNING) &&
+			!requests_pending(ti) && !accept_pending(ti)) {
+		Dprintk("fast thread: no work to be done, sleeping.\n");
+		schedule();
+		Dprintk("fast thread: back from sleep!\n");
+		goto repeat_accept;
+	}
+	goto repeat_accept;
+
+handle_userspace_req:
+	if (req->attr)
+		TUX_BUG();
+	switch_docroot(req);
+	ti->userspace_req = req;
+	__set_task_state(current, TASK_RUNNING);
+	return TUX_RETURN_USERSPACE_REQUEST;
+
+handle_signal:
+	__set_task_state(current, TASK_RUNNING);
+	return TUX_RETURN_SIGNAL;
+
+handle_stop:
+	__set_task_state(current, TASK_RUNNING);
+	return TUX_RETURN_EXIT;
+}
+
+static int init_queues (int nr_tux_threads)
+{
+	int i;
+
+	for (i = 0; i < nr_tux_threads; i++) {
+		threadinfo_t *ti = threadinfo + i;
+
+		INIT_LIST_HEAD(&ti->all_requests);
+
+		spin_lock_init(&ti->free_requests_lock);
+		INIT_LIST_HEAD(&ti->free_requests);
+
+		spin_lock_init(&ti->work_lock);
+		INIT_LIST_HEAD(&ti->work_pending);
+		INIT_LIST_HEAD(&ti->lru);
+
+	}
+	return 0;
+}
+
+int tux_chroot (char *dir)
+{
+	kernel_cap_t saved_cap = current->cap_effective;
+	mm_segment_t oldmm;
+	int err;
+
+	/* Allow chroot dir to be in kernel space. */
+	oldmm = get_fs(); set_fs(KERNEL_DS);
+	set_fs(KERNEL_DS);
+	cap_raise (current->cap_effective, CAP_SYS_CHROOT);
+
+	err = sys_chroot(dir);
+	if (!err)
+		sys_chdir("/");
+
+	current->cap_effective = saved_cap;
+	set_fs(oldmm);
+
+	return err;
+}
+
+/*
+ * Right now this is not fully SMP-safe against multiple TUX
+ * managers. It's just a rudimentary protection against typical
+ * mistakes.
+ */
+static int initialized = 0;
+
+#define MAX_DOCROOTLEN 500
+
+static int lookup_docroot(struct nameidata *docroot, const char *name)
+{
+	int err;
+
+	docroot->mnt = mntget(current->fs->rootmnt);
+	docroot->dentry = dget(current->fs->root);
+	docroot->last.len = 0;
+	docroot->flags = LOOKUP_FOLLOW;
+
+	err = path_walk(name, docroot);
+	if (err) {
+		mntput(docroot->mnt);
+		docroot->mnt = NULL;
+		return err;
+	}
+	return 0;
+}
+
+static int user_req_startup (void)
+{
+	char name[MAX_DOCROOTLEN];
+	struct nameidata *docroot;
+	unsigned int i;
+	int err;
+
+	if (initialized)
+		return -EINVAL;
+	initialized = 1;
+
+	/*
+	 * Look up the HTTP and FTP document root.
+	 * (typically they are shared, but can be
+	 * different directories.)
+	 */
+	docroot = &tux_proto_http.main_docroot;
+	if (docroot->mnt)
+		TUX_BUG();
+	strcpy(name, tux_common_docroot);
+	strcat(name, tux_http_subdocroot);
+
+	err = lookup_docroot(docroot, name);
+	if (err) {
+		initialized = 0;
+		printk(KERN_ERR "TUX: could not look up HTTP documentroot: \"%s\"\n", name);
+		return err;
+	}
+
+	docroot = &tux_proto_ftp.main_docroot;
+	if (docroot->mnt)
+		TUX_BUG();
+	strcpy(name, tux_common_docroot);
+	strcat(name, tux_ftp_subdocroot);
+
+	err = lookup_docroot(docroot, name);
+	if (err) {
+abort:
+		docroot = &tux_proto_http.main_docroot;
+		path_release(docroot);
+		memset(docroot, 0, sizeof(*docroot));
+		initialized = 0;
+		printk(KERN_ERR "TUX: could not look up FTP documentroot: \"%s\"\n", name);
+		return err;
+	}
+
+	/*
+	 * Start up the logger thread. (which opens the logfile)
+	 */
+	start_log_thread();
+
+	nr_tux_threads = tux_threads;
+	if (nr_tux_threads < 1)
+		nr_tux_threads = 1;
+	if (nr_tux_threads > CONFIG_TUX_NUMTHREADS)
+		nr_tux_threads = CONFIG_TUX_NUMTHREADS;
+	tux_threads = nr_tux_threads;
+
+	/*
+	 * Set up per-thread work-queues:
+	 */
+	memset(threadinfo, 0, CONFIG_TUX_NUMTHREADS*sizeof(threadinfo_t));
+	init_queues(nr_tux_threads);
+
+	/*
+	 * Prepare the worker thread structures.
+	 */
+	for (i = 0; i < nr_tux_threads; i++) {
+		threadinfo_t *ti = threadinfo + i;
+		ti->cpu = i;
+		ti->gzip_state.workspace =
+			vmalloc(zlib_deflate_workspacesize());
+		if (!ti->gzip_state.workspace ||
+			    (zlib_deflateInit(&ti->gzip_state, 6) != Z_OK)) {
+			stop_log_thread();
+			goto abort;
+		}
+		init_MUTEX(&ti->gzip_sem);
+	}
+
+	__module_get(tux_module);
+
+	return 0;
+}
+
+static DECLARE_WAIT_QUEUE_HEAD(wait_stop);
+static DECLARE_WAIT_QUEUE_HEAD(thread_stopped);
+
+static int user_req_shutdown (void)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	struct nameidata *docroot;
+	int i, err = -EINVAL;
+
+	lock_kernel();
+	if (!initialized) {
+		Dprintk("TUX is not up - cannot shut down.\n");
+		goto err;
+	}
+	initialized = 0;
+	stop_threads = 1;
+	add_wait_queue(&thread_stopped, &wait);
+
+wait_more:
+	/*
+	 * Wake up all the worker threads so they notice
+	 * that we are being stopped.
+	 */
+	set_task_state(current, TASK_UNINTERRUPTIBLE);
+	if (atomic_read(&nr_tux_threads_running)) {
+		Dprintk("TUX: shutdown, %d threads still running.\n",
+			atomic_read(&nr_tux_threads_running));
+		wake_up(&wait_stop);
+		schedule();
+		goto wait_more;
+	}
+	set_task_state(current, TASK_RUNNING);
+	stop_threads = 0;
+	remove_wait_queue(&thread_stopped, &wait);
+
+	if (nr_async_io_pending())
+		TUX_BUG();
+
+	stop_log_thread();
+
+	docroot = &tux_proto_http.main_docroot;
+	path_release(docroot);
+	memset(docroot, 0, sizeof(*docroot));
+	docroot = &tux_proto_ftp.main_docroot;
+	path_release(docroot);
+	memset(docroot, 0, sizeof(*docroot));
+	err = 0;
+
+	flush_dentry_attributes();
+	free_mimetypes();
+	unregister_all_tuxmodules();
+
+	for (i = 0; i < nr_tux_threads; i++) {
+		threadinfo_t *ti = threadinfo + i;
+		vfree(ti->gzip_state.workspace);
+	}
+
+	module_put(tux_module);
+
+err:
+	unlock_kernel();
+	return err;
+}
+
+void drop_permissions (void)
+{
+	/*
+	 * Userspace drops privileges already, and group
+	 * membership is important to keep.
+	 */
+	/* Give the new process no privileges.. */
+	current->uid = current->euid =
+		current->suid = current->fsuid = tux_cgi_uid;
+	current->gid = current->egid =
+		current->sgid = current->fsgid = tux_cgi_gid;
+	cap_clear(current->cap_permitted);
+	cap_clear(current->cap_inheritable);
+	cap_clear(current->cap_effective);
+}
+
+static int wait_for_others (void)
+{
+	threadinfo_t *ti;
+	unsigned int cpu;
+
+repeat:
+	if (signal_pending(current))
+		return -1;
+	set_current_state(TASK_INTERRUPTIBLE);
+	schedule_timeout(HZ/10);
+
+	for (cpu = 0; cpu < nr_tux_threads; cpu++) {
+		ti = threadinfo + cpu;
+		if (ti->listen_error)
+			return -1;
+		if (!ti->started)
+			goto repeat;
+	}
+	/* ok, all threads have started up. */
+	return 0;
+}
+
+static void zap_listen_sockets (threadinfo_t *ti)
+{
+	struct socket *sock;
+	int i;
+
+	for (i = 0; i < CONFIG_TUX_NUMSOCKETS; i++) {
+		if (!ti->listen[i].proto)
+			break;
+		sock = ti->listen[i].sock;
+		if (!ti->listen[i].cloned && sock) {
+			while (waitqueue_active(sock->sk->sk_sleep))
+				yield();
+			sock_release(sock);
+		}
+		ti->listen[i].sock = NULL;
+		ti->listen[i].proto = NULL;
+		ti->listen[i].cloned = 0;
+	}
+}
+
+static DECLARE_MUTEX(serialize_startup);
+
+static int user_req_start_thread (threadinfo_t *ti)
+{
+	unsigned int err, cpu, i, j, k;
+	struct k_sigaction *ka;
+
+	cpu = ti->cpu;
+#ifdef CONFIG_SMP
+	{
+		unsigned int home_cpu;
+		cpumask_t map;
+
+		home_cpu = (cpu + tux_cpu_offset) % num_online_cpus();
+		map = cpumask_of_cpu(home_cpu);
+
+		cpus_and(map, map, cpu_online_map);
+		if (!(cpus_empty(map)))
+			set_cpus_allowed(current, map);
+	}
+#endif
+	ti->thread = current;
+	atomic_inc(&nr_tux_threads_running);
+
+	err = start_cachemiss_threads(ti);
+	if (err)
+		goto out;
+
+	init_waitqueue_entry(&ti->stop, current);
+	for (j = 0; j < CONFIG_TUX_NUMSOCKETS; j++)
+		init_waitqueue_entry(ti->wait_event + j, current);
+
+	ka = current->sighand->action + SIGCHLD-1;
+	ka->sa.sa_handler = SIG_IGN;
+
+	/* Block all signals except SIGKILL, SIGSTOP, SIGHUP and SIGCHLD */
+	spin_lock_irq(&current->sighand->siglock);
+	siginitsetinv(&current->blocked, sigmask(SIGKILL) |
+			sigmask(SIGSTOP)| sigmask(SIGHUP) | sigmask(SIGCHLD));
+	recalc_sigpending();
+	spin_unlock_irq(&current->sighand->siglock);
+
+	if (!tux_listen[cpu][0].proto) {
+		printk(KERN_ERR "no listen socket specified for TUX thread %d, in /proc/net/tux/%d/listen/, aborting.\n", cpu, cpu);
+		goto error;
+	}
+
+	/*
+	 * Serialize startup so that listen sockets can be
+	 * created race-free.
+	 */
+	down(&serialize_startup);
+
+	Dprintk("thread %d initializing sockets.\n", cpu);
+
+	for (k = 0; k < CONFIG_TUX_NUMSOCKETS; k++) {
+		tux_socket_t *e1, *e2;
+
+		e1 = tux_listen[cpu] + k;
+		if (!e1->proto)
+			break;
+		for (i = 0; i < CONFIG_TUX_NUMTHREADS; i++) {
+			if (i == cpu)
+				continue;
+			for (j = 0; j < CONFIG_TUX_NUMSOCKETS; j++) {
+				e2 = tux_listen[i] + j;
+				if (!e2->proto)
+					continue;
+				if ((e1->ip == e2->ip) && (e1->port == e2->port) && (e1->proto == e2->proto) && threadinfo[i].listen[j].proto) {
+					ti->listen[k] = threadinfo[i].listen[j];
+					ti->listen[k].cloned = 1;
+					Dprintk("cloned socket %d from thread %d's socket %d.\n", k, i, j);
+					goto next_socket;
+				}
+			}
+		}
+
+		ti->listen[k].sock = start_listening(tux_listen[cpu] + k, cpu);
+		if (!ti->listen[k].sock)
+			goto error_unlock;
+		ti->listen[k].cloned = 0;
+		ti->listen[k].proto = tux_listen[cpu][k].proto;
+		Dprintk("thread %d got sock %p (%d), proto %s.\n", cpu, ti->listen[k].sock, k, ti->listen[k].proto->name);
+next_socket:
+		;
+	}
+	Dprintk("thread %d done initializing sockets.\n", cpu);
+	up(&serialize_startup);
+
+	if (wait_for_others())
+		goto error_nomsg;
+
+	if (!ti->listen[0].proto) {
+		printk("hm, socket 0 has no protocol.\n");
+		goto error;
+	}
+
+	add_wait_queue(&wait_stop, &ti->stop);
+	for (j = 0; j < CONFIG_TUX_NUMSOCKETS; j++)
+		if (ti->listen[j].proto)
+			add_wait_queue_exclusive(ti->listen[j].sock->sk->sk_sleep,
+				ti->wait_event + j);
+	drop_permissions();
+
+	__module_get(tux_module);
+	return 0;
+
+error_unlock:
+	up(&serialize_startup);
+error:
+	printk(KERN_NOTICE "TUX: could not start worker thread %d.\n", ti->cpu);
+
+error_nomsg:
+	ti->listen_error = 1;
+	ti->started = 0;
+
+	zap_listen_sockets(ti);
+	flush_all_requests(ti);
+	stop_cachemiss_threads(ti);
+
+	err = -EINVAL;
+
+out:
+	/*
+	 * Last thread close the door:
+	 */
+	if (atomic_dec_and_test(&nr_tux_threads_running))
+		user_req_shutdown();
+
+	return -err;
+}
+
+static int flush_idleinput (threadinfo_t * ti)
+{
+	struct list_head *head, *tmp;
+	tux_req_t *req;
+	int count = 0;
+
+	head = &ti->all_requests;
+	tmp = head->next;
+
+	while (tmp != head) {
+		req = list_entry(tmp, tux_req_t, all);
+		tmp = tmp->next;
+		if (test_bit(0, &req->idle_input)) {
+			idle_event(req);
+			count++;
+		}
+	}
+	return count;
+}
+
+static int flush_waitoutput (threadinfo_t * ti)
+{
+	struct list_head *head, *tmp;
+	tux_req_t *req;
+	int count = 0;
+
+	head = &ti->all_requests;
+	tmp = head->next;
+
+	while (tmp != head) {
+		req = list_entry(tmp, tux_req_t, all);
+		tmp = tmp->next;
+		if (test_bit(0, &req->wait_output_space)) {
+			output_space_event(req);
+			count++;
+		}
+	}
+	return count;
+}
+
+static void flush_all_requests (threadinfo_t *ti)
+{
+	for (;;) {
+		int count;
+
+		count = flush_idleinput(ti);
+		count += flush_waitoutput(ti);
+		count += tux_flush_workqueue(ti);
+		count += flush_freequeue(ti);
+		if (!ti->nr_requests)
+			break;
+		/*
+		 * Go through again if we advanced:
+		 */
+		if (count)
+			continue;
+		Dprintk("flush_all_requests: %d requests still waiting.\n", ti->nr_requests);
+#ifdef CONFIG_TUX_DEBUG
+		count = print_all_requests(ti);
+		Dprintk("flush_all_requests: printed %d requests.\n", count);
+#endif
+		current->state = TASK_UNINTERRUPTIBLE;
+		schedule_timeout(HZ/10);
+	}
+}
+
+int nr_async_io_pending (void)
+{
+	unsigned int i, sum = 0;
+
+	for (i = 0; i < nr_tux_threads; i++) {
+		threadinfo_t *ti = threadinfo + i;
+		if (ti->iot)
+			sum += ti->iot->nr_async_pending;
+	}
+	return sum;
+}
+
+static int user_req_stop_thread (threadinfo_t *ti)
+{
+	int j;
+
+	printk(KERN_NOTICE "TUX: thread %d stopping ...\n",
+		(int)(ti-threadinfo));
+
+	if (!ti->started)
+		TUX_BUG();
+	for (j = 0; j < CONFIG_TUX_NUMSOCKETS; j++)
+		if (ti->listen[j].proto)
+			remove_wait_queue(ti->listen[j].sock->sk->sk_sleep,
+				ti->wait_event + j);
+	remove_wait_queue(&wait_stop, &ti->stop);
+
+	Dprintk(KERN_NOTICE "TUX: thread %d waiting for sockets to go inactive ...\n", (int)(ti-threadinfo));
+	zap_listen_sockets(ti);
+
+	Dprintk(KERN_NOTICE "TUX: thread %d has all sockets inactive.\n", (int)(ti-threadinfo));
+
+	flush_all_requests(ti);
+	stop_cachemiss_threads(ti);
+
+	if (ti->nr_requests)
+		TUX_BUG();
+	ti->started = 0;
+
+	printk(KERN_INFO "TUX: thread %d stopped.\n", ti->cpu);
+
+	ti->thread = NULL;
+	current->tux_info = NULL;
+	current->tux_exit = NULL;
+	atomic_dec(&nr_tux_threads_running);
+	wake_up(&thread_stopped);
+
+	module_put(tux_module);
+
+	return 0;
+}
+
+#define COPY_INT(u_field, k_field)					\
+do {									\
+	if (__copy_to_user(&u_info->u_field, &req->k_field,		\
+					sizeof(req->k_field)))		\
+		return_EFAULT;						\
+} while (0)
+
+#define GETLEN(k_field, maxlen)						\
+		((req->k_field##_len < maxlen) ?			\
+		req->k_field##_len : maxlen-1)
+
+#define COPY_STR(u_field, k_field, maxlen)				\
+do {									\
+	if (__copy_to_user(u_info->u_field, req->k_field##_str,		\
+		GETLEN(k_field, maxlen)))				\
+			return_EFAULT;					\
+} while (0)
+
+#define COPY_COND_STR(u_field,k_field,maxlen)				\
+do {									\
+	if (req->k_field##_len)						\
+		COPY_STR(u_field, k_field, maxlen);			\
+	if (__put_user((char)0, u_info->u_field +			\
+			GETLEN(k_field, maxlen)))			\
+		return_EFAULT;						\
+} while (0)
+
+static void finish_userspace_req (tux_req_t *req)
+{
+	threadinfo_t *ti = req->ti;
+
+	ti->userspace_req = NULL;
+	req->usermode = 0;
+	req->private = 0;
+	req->error = 0;
+	DEC_STAT(nr_userspace_pending);
+	flush_request(req, 0);
+}
+
+static void zap_userspace_req (tux_req_t *req)
+{
+	clear_keepalive(req);
+	finish_userspace_req(req);
+}
+
+/*
+ * Fills in the user-space request structure:
+ */
+static int prepare_userspace_req (threadinfo_t *ti, user_req_t *u_info)
+{
+	u64 u_req;
+	tux_req_t *req = ti->userspace_req;
+	unsigned int tmp;
+	int filelen;
+	int fd;
+
+	Dprintk("prepare_userspace_req(%p).\n", req);
+	if (!req)
+		TUX_BUG();
+	if (req->error) {
+		TDprintk("userspace request has error %d.\n", req->error);
+		return -1;
+	}
+	fd = req->fd;
+	if (fd == -1) {
+		fd = sock_map_fd(req->sock);
+		Dprintk("sock_map_fd(%p) :%d.\n", req, fd);
+		if (fd < 0) {
+			Dprintk("sock_map_fd() returned %d.\n", fd);
+			return -EMFILE;
+		}
+		req->fd = fd;
+	}
+
+#define return_EFAULT do { Dprintk("-EFAULT at %d:%s.\n", __LINE__, __FILE__); return -EFAULT; } while (0)
+
+	if (!access_ok(VERIFY_WRITE, u_info, sizeof(*u_info)))
+		return_EFAULT;
+	if (__copy_to_user(&u_info->sock, &fd, sizeof(fd)))
+		return_EFAULT;
+	if (req->attr)
+		TUX_BUG();
+
+	COPY_INT(module_index, usermodule_idx);
+
+	COPY_COND_STR(query, query, MAX_URI_LEN);
+
+	COPY_INT(event, event);
+	Dprintk("prepare userspace, user error: %d, event %d.\n", req->user_error, req->event);
+	COPY_INT(error, user_error);
+	req->user_error = 0;
+
+	filelen = req->total_file_len;
+	if (filelen < 0)
+		filelen = 0;
+	if (__copy_to_user(&u_info->objectlen, &filelen, sizeof(filelen)))
+		return_EFAULT;
+	if ((req->method == METHOD_POST) && !filelen)
+		if (__copy_to_user(&u_info->objectlen,
+			&req->content_len, sizeof(filelen)))
+		return_EFAULT;
+	if (req->objectname_len) {
+		if (req->objectname[req->objectname_len])
+			TUX_BUG();
+		if (__copy_to_user(u_info->objectname, req->objectname,
+				req->objectname_len + 1))
+			return_EFAULT;
+	} else
+		if (__put_user((char)0, u_info->objectname))
+			return_EFAULT;
+
+	COPY_INT(http_version, version);
+	COPY_INT(http_method, method);
+	COPY_INT(keep_alive, keep_alive);
+
+	COPY_INT(cookies_len, cookies_len);
+	if (req->cookies_len)
+		COPY_STR(cookies, cookies, MAX_COOKIE_LEN);
+	if (__put_user((char)0, u_info->cookies + req->cookies_len))
+		return_EFAULT;
+
+	u_req = (u64)(unsigned long)req;
+	if (__copy_to_user(&u_info->id, &u_req, sizeof(u_req)))
+		return_EFAULT;
+	COPY_INT(priv, private);
+	COPY_INT(bytes_sent, bytes_sent);
+
+	tmp = inet_sk(req->sock->sk)->daddr;
+	if (__copy_to_user(&u_info->client_host, &tmp, sizeof(tmp)))
+		return_EFAULT;
+
+	COPY_COND_STR(content_type, content_type, MAX_FIELD_LEN);
+	COPY_COND_STR(user_agent, user_agent, MAX_FIELD_LEN);
+	COPY_COND_STR(accept, accept, MAX_FIELD_LEN);
+	COPY_COND_STR(accept_charset, accept_charset, MAX_FIELD_LEN);
+	COPY_COND_STR(accept_encoding, accept_encoding, MAX_FIELD_LEN);
+	COPY_COND_STR(accept_language, accept_language, MAX_FIELD_LEN);
+	COPY_COND_STR(cache_control, cache_control, MAX_FIELD_LEN);
+	COPY_COND_STR(if_modified_since, if_modified_since, MAX_FIELD_LEN);
+	COPY_COND_STR(negotiate, negotiate, MAX_FIELD_LEN);
+	COPY_COND_STR(pragma, pragma, MAX_FIELD_LEN);
+	COPY_COND_STR(referer, referer, MAX_FIELD_LEN);
+
+	return TUX_RETURN_USERSPACE_REQUEST;
+}
+
+#define GOTO_ERR_no_unlock do { Dprintk("sys_tux() ERR at %s:%d.\n", __FILE__, __LINE__); goto err_no_unlock; } while (0)
+#define GOTO_ERR_unlock do { Dprintk("sys_tux() ERR at %s:%d.\n", __FILE__, __LINE__); goto err_unlock; } while (0)
+
+static int register_mimetype(user_req_t *u_info)
+{
+	char extension[MAX_URI_LEN], mimetype[MAX_URI_LEN], expires[MAX_URI_LEN];
+	u64 u_addr;
+	char *addr;
+	int ret;
+
+	ret = strncpy_from_user(extension, u_info->objectname, MAX_URI_LEN);
+	if (ret <= 0)
+		GOTO_ERR_no_unlock;
+	extension[ret] = 0;
+	Dprintk("got MIME extension: %s.\n", extension);
+	ret = copy_from_user(&u_addr, &u_info->object_addr, sizeof(u_addr));
+	if (ret)
+		GOTO_ERR_no_unlock;
+	addr = (char *)(unsigned long)u_addr;
+	ret = strncpy_from_user(mimetype, addr, MAX_URI_LEN);
+	if (ret <= 0)
+		GOTO_ERR_no_unlock;
+	mimetype[ret] = 0;
+	Dprintk("got MIME type: %s.\n", mimetype);
+       ret = strncpy_from_user(expires, u_info->cache_control, MAX_URI_LEN);
+       if (ret >= 0)
+		expires[ret] = 0;
+	else
+		expires[0] = 0;
+       Dprintk("got expires header: %s.\n", expires);
+
+	add_mimetype(extension, mimetype, expires);
+	ret = 0;
+err_no_unlock:
+	return ret;
+}
+
+void user_send_buffer (tux_req_t *req, int cachemiss)
+{
+	int ret;
+
+
+	SET_TIMESTAMP(req->output_timestamp);
+
+repeat:
+	ret = send_sync_buf(req, req->sock, req->userbuf, req->userlen, MSG_DONTWAIT | MSG_MORE);
+	switch (ret) {
+		case -EAGAIN:
+			add_tux_atom(req, user_send_buffer);
+			if (add_output_space_event(req, req->sock)) {
+				del_tux_atom(req);
+				goto repeat;
+			}
+			INC_STAT(user_sendbuf_write_misses);
+			break;
+		default:
+			if (ret <= 0) {
+				req_err(req);
+				req->usermode = 0;
+				req->private = 0;
+				add_req_to_workqueue(req);
+				break;
+			}
+			req->userbuf += ret;
+			req->userlen -= ret;
+			if ((int)req->userlen < 0)
+				TUX_BUG();
+			if (req->userlen)
+				goto repeat;
+			add_req_to_workqueue(req);
+			break;
+	}
+}
+
+void user_send_object (tux_req_t *req, int cachemiss)
+{
+	int ret;
+
+
+	SET_TIMESTAMP(req->output_timestamp);
+
+repeat:
+	ret = generic_send_file(req, req->sock, cachemiss);
+	switch (ret) {
+		case -5:
+			add_tux_atom(req, user_send_object);
+			output_timeout(req);
+			break;
+		case -4:
+			add_tux_atom(req, user_send_object);
+			if (add_output_space_event(req, req->sock)) {
+				del_tux_atom(req);
+				goto repeat;
+			}
+			INC_STAT(user_sendobject_write_misses);
+			break;
+		case -3:
+			INC_STAT(user_sendobject_cachemisses);
+			add_tux_atom(req, user_send_object);
+			queue_cachemiss(req);
+			break;
+		case -1:
+			break;
+		default:
+			req->in_file->f_pos = 0;
+			add_req_to_workqueue(req);
+			break;
+	}
+}
+
+void user_get_object (tux_req_t *req, int cachemiss)
+{
+	int missed;
+
+	if (!req->dentry) {
+		req->usermode = 0;
+		missed = lookup_object(req, cachemiss ? 0 : LOOKUP_ATOMIC);
+		if (req->usermode)
+			TUX_BUG();
+		req->usermode = 1;
+		if (!missed && !req->dentry) {
+			req->error = 0;
+			req->user_error = -ENOENT;
+			add_req_to_workqueue(req);
+			return;
+		}
+		if (missed) {
+			if (cachemiss)
+				TUX_BUG();
+			INC_STAT(user_lookup_cachemisses);
+fetch_missed:
+			req->ti->userspace_req = NULL;
+			DEC_STAT(nr_userspace_pending);
+			add_tux_atom(req, user_get_object);
+			queue_cachemiss(req);
+			return;
+		}
+	}
+	req->total_file_len = req->dentry->d_inode->i_size;
+	if (!req->output_len)
+		req->output_len = req->total_file_len;
+	if (tux_fetch_file(req, !cachemiss)) {
+		INC_STAT(user_fetch_cachemisses);
+		goto fetch_missed;
+	}
+	req->in_file->f_pos = 0;
+	add_req_to_workqueue(req);
+}
+
+asmlinkage long __sys_tux (unsigned int action, user_req_t *u_info)
+{
+	int ret = -1;
+	threadinfo_t *ti;
+	tux_req_t *req;
+
+	if (action != TUX_ACTION_CURRENT_DATE)
+		Dprintk("got sys_tux(%d, %p).\n", action, u_info);
+
+	if (action >= MAX_TUX_ACTION)
+		GOTO_ERR_no_unlock;
+
+	ti = (threadinfo_t *) current->tux_info;
+	if (ti)
+		if (ti->thread != current)
+			TUX_BUG();
+
+	if (!capable(CAP_SYS_ADMIN)
+			&& (action != TUX_ACTION_CONTINUE_REQ) &&
+				(action != TUX_ACTION_STOPTHREAD))
+		goto userspace_actions;
+
+	switch (action) {
+		case TUX_ACTION_CONTINUE_REQ:
+			ret = continue_request((int)(long)u_info);
+			goto out;
+
+		case TUX_ACTION_STARTUP:
+			lock_kernel();
+			ret = user_req_startup();
+			unlock_kernel();
+			goto out;
+
+		case TUX_ACTION_SHUTDOWN:
+			lock_kernel();
+			ret = user_req_shutdown();
+			unlock_kernel();
+			goto out;
+
+		case TUX_ACTION_REGISTER_MODULE:
+			ret = user_register_module(u_info);
+			goto out;
+
+		case TUX_ACTION_UNREGISTER_MODULE:
+			ret = user_unregister_module(u_info);
+			goto out;
+
+		case TUX_ACTION_STARTTHREAD:
+		{
+			unsigned int nr;
+
+			ret = copy_from_user(&nr, &u_info->thread_nr,
+						sizeof(int));
+			if (ret)
+				GOTO_ERR_no_unlock;
+			if (nr >= nr_tux_threads)
+				GOTO_ERR_no_unlock;
+			ti = threadinfo + nr;
+			if (ti->started)
+				GOTO_ERR_unlock;
+			ti->started = 1;
+			current->tux_info = ti;
+			current->tux_exit = tux_exit;
+			if (ti->thread)
+				TUX_BUG();
+			Dprintk("TUX: current open files limit for TUX%d: %ld.\n", nr, current->signal->rlim[RLIMIT_NOFILE].rlim_cur);
+			lock_kernel();
+			ret = user_req_start_thread(ti);
+			unlock_kernel();
+			if (ret) {
+				current->tux_info = NULL;
+				current->tux_exit = NULL;
+			} else {
+				if (ti->thread != current)
+					TUX_BUG();
+			}
+			goto out_userreq;
+		}
+
+		case TUX_ACTION_STOPTHREAD:
+			if (!ti)
+				GOTO_ERR_no_unlock;
+			if (!ti->started)
+				GOTO_ERR_unlock;
+			req = ti->userspace_req;
+			if (req)
+				zap_userspace_req(req);
+
+			lock_kernel();
+			ret = user_req_stop_thread(ti);
+			unlock_kernel();
+			goto out_userreq;
+
+		case TUX_ACTION_CURRENT_DATE:
+			ret = strncpy_from_user(tux_date, u_info->new_date,
+				DATE_LEN);
+			if (ret <= 0)
+				GOTO_ERR_no_unlock;
+			goto out;
+
+		case TUX_ACTION_REGISTER_MIMETYPE:
+			ret = register_mimetype(u_info);
+			if (ret)
+				GOTO_ERR_no_unlock;
+			goto out;
+
+		case TUX_ACTION_QUERY_VERSION:
+			ret = (TUX_MAJOR_VERSION << 24) | (TUX_MINOR_VERSION << 16) | TUX_PATCHLEVEL_VERSION;
+			goto out;
+		default:
+			;
+	}
+
+userspace_actions:
+
+	if (!ti)
+		GOTO_ERR_no_unlock;
+
+	if (!ti->started)
+		GOTO_ERR_unlock;
+
+	req = ti->userspace_req;
+	if (!req) {
+		if (action == TUX_ACTION_EVENTLOOP)
+			goto eventloop;
+		GOTO_ERR_unlock;
+	}
+	if (!req->usermode)
+		TUX_BUG();
+
+	ret = copy_from_user(&req->event, &u_info->event, sizeof(int));
+	if (ret)
+		GOTO_ERR_unlock;
+	ret = copy_from_user(&req->status, &u_info->http_status, sizeof(int));
+	if (ret)
+		GOTO_ERR_unlock;
+	ret = copy_from_user(&req->bytes_sent, &u_info->bytes_sent, sizeof(int));
+	if (ret)
+		GOTO_ERR_unlock;
+	ret = copy_from_user(&req->private, &u_info->priv, sizeof(req->private));
+	if (ret)
+		GOTO_ERR_unlock;
+
+	switch (action) {
+
+		case TUX_ACTION_EVENTLOOP:
+eventloop:
+			req = ti->userspace_req;
+			if (req)
+				zap_userspace_req(req);
+			ret = event_loop(ti);
+			goto out_userreq;
+
+		/*
+		 * Module forces keepalive off, server will close
+		 * the connection.
+		 */
+		case TUX_ACTION_FINISH_CLOSE_REQ:
+			clear_keepalive(req);
+
+		case TUX_ACTION_FINISH_REQ:
+			finish_userspace_req(req);
+			goto eventloop;
+
+		case TUX_ACTION_REDIRECT_REQ:
+
+			ti->userspace_req = NULL;
+			req->usermode = 0;
+			req->private = 0;
+			req->error = TUX_ERROR_REDIRECT;
+			DEC_STAT(nr_userspace_pending);
+			add_tux_atom(req, redirect_request);
+			add_req_to_workqueue(req);
+
+			goto eventloop;
+
+		case TUX_ACTION_POSTPONE_REQ:
+
+			postpone_request(req);
+			ti->userspace_req = NULL;
+			ret = TUX_RETURN_USERSPACE_REQUEST;
+			break;
+
+		case TUX_ACTION_GET_OBJECT:
+			release_req_dentry(req);
+			ret = strncpy_from_user(req->objectname,
+				u_info->objectname, MAX_URI_LEN-1);
+			if (ret <= 0) {
+				req->objectname[0] = 0;
+				req->objectname_len = 0;
+				GOTO_ERR_unlock;
+			}
+			req->objectname[ret] = 0; // string delimit
+			req->objectname_len = ret;
+
+			Dprintk("got objectname {%s} (%d) from user-space req %p (req: %p).\n", req->objectname, req->objectname_len, u_info, req);
+			req->ti->userspace_req = NULL;
+			DEC_STAT(nr_userspace_pending);
+			user_get_object(req, 0);
+			goto eventloop;
+
+		case TUX_ACTION_READ_OBJECT:
+		{
+			u64 u_addr;
+			char *addr;
+			loff_t ppos = 0;
+			struct file *filp;
+
+			if (!req->dentry)
+				GOTO_ERR_unlock;
+
+			ret = copy_from_user(&u_addr, &u_info->object_addr,
+					sizeof(u_addr));
+			if (ret)
+				GOTO_ERR_unlock;
+			addr = (char *)(unsigned long)u_addr;
+			filp = dentry_open(req->dentry, NULL, O_RDONLY);
+			dget(req->dentry);
+			generic_file_read(filp, addr, req->total_file_len, &ppos);
+			fput(filp);
+			ret = TUX_RETURN_USERSPACE_REQUEST;
+			break;
+		}
+
+		case TUX_ACTION_SEND_OBJECT:
+			if (!req->dentry)
+				GOTO_ERR_unlock;
+			req->ti->userspace_req = NULL;
+			DEC_STAT(nr_userspace_pending);
+			user_send_object(req, 0);
+			goto eventloop;
+
+		case TUX_ACTION_SEND_BUFFER:
+		{
+			u64 u_addr;
+			char *addr;
+			unsigned int len;
+
+			ret = copy_from_user(&u_addr,
+					&u_info->object_addr, sizeof(u_addr));
+			if (ret)
+				GOTO_ERR_unlock;
+			addr = (char *)(unsigned long)u_addr;
+			ret = copy_from_user(&len,
+					&u_info->objectlen, sizeof(addr));
+			if (ret)
+				GOTO_ERR_unlock;
+			if ((int)len <= 0)
+				GOTO_ERR_unlock;
+
+			ret = -EFAULT;
+			if (!access_ok(VERIFY_READ, addr, len))
+				GOTO_ERR_unlock;
+			req->userbuf = addr;
+			req->userlen = len;
+
+			req->ti->userspace_req = NULL;
+			DEC_STAT(nr_userspace_pending);
+			user_send_buffer(req, 0);
+			ret = 0;
+			goto eventloop;
+		}
+
+		case TUX_ACTION_READ_HEADERS:
+		{
+			char *addr;
+			u64 u_addr;
+
+			ret = copy_from_user(&u_addr, &u_info->object_addr,
+					sizeof(u_addr));
+			if (ret)
+				GOTO_ERR_unlock;
+			addr = (char *)(unsigned long)u_addr;
+			ret = copy_to_user(&u_info->objectlen,
+				 &req->headers_len, sizeof(req->headers_len));
+			if (ret)
+				GOTO_ERR_unlock;
+			ret = copy_to_user(addr,req->headers, req->headers_len);
+			if (ret)
+				GOTO_ERR_unlock;
+			break;
+		}
+
+		case TUX_ACTION_READ_POST_DATA:
+		{
+			char *addr;
+			unsigned int size;
+			u64 u_addr;
+
+			ret = copy_from_user(&u_addr, &u_info->object_addr,
+					sizeof(u_addr));
+			if (ret)
+				GOTO_ERR_unlock;
+			addr = (char *)(unsigned long)u_addr;
+
+			ret = copy_from_user(&size, &u_info->objectlen,
+					sizeof(size));
+			if (ret)
+				GOTO_ERR_unlock;
+			Dprintk("READ_POST_DATA: got %p(%d).\n", addr, size);
+			if (req->post_data_len < size)
+				size = req->post_data_len;
+			Dprintk("READ_POST_DATA: writing %d.\n", size);
+			ret = copy_to_user(&u_info->objectlen,
+						&size, sizeof(size));
+			if (ret)
+				GOTO_ERR_unlock;
+			ret = copy_to_user(addr, req->post_data_str, size);
+			if (ret)
+				GOTO_ERR_unlock;
+			goto out;
+		}
+
+		case TUX_ACTION_WATCH_PROXY_SOCKET:
+		{
+			struct socket *sock;
+			int err;
+			long fd;
+			u64 u_addr;
+
+			ret = copy_from_user(&u_addr, &u_info->object_addr,
+					sizeof(u_addr));
+			if (ret)
+				GOTO_ERR_unlock;
+			fd = (int)(unsigned long)u_addr;
+
+			sock = sockfd_lookup(fd, &err);
+			if (!sock)
+				GOTO_ERR_unlock;
+			put_data_sock(req);
+			link_tux_data_socket(req, sock);
+
+			ret = 0;
+			goto out;
+		}
+
+		case TUX_ACTION_WAIT_PROXY_SOCKET:
+		{
+			if (!req->data_sock)
+				GOTO_ERR_unlock;
+			if (socket_input(req->data_sock)) {
+				ret = TUX_RETURN_USERSPACE_REQUEST;
+				goto out_userreq;
+			}
+			spin_lock_irq(&req->ti->work_lock);
+			add_keepalive_timer(req);
+			if (test_and_set_bit(0, &req->idle_input))
+				TUX_BUG();
+			spin_unlock_irq(&req->ti->work_lock);
+			if (socket_input(req->data_sock)) {
+				unidle_req(req);
+				ret = TUX_RETURN_USERSPACE_REQUEST;
+				goto out_userreq;
+			}
+			req->ti->userspace_req = NULL;
+			goto eventloop;
+		}
+
+		default:
+			GOTO_ERR_unlock;
+	}
+
+out_userreq:
+	req = ti->userspace_req;
+	if (req) {
+		ret = prepare_userspace_req(ti, u_info);
+		if (ret < 0) {
+			TDprintk("hm, user req %p returned %d, zapping.\n",
+				req, ret);
+			zap_userspace_req(req);
+			goto eventloop;
+		}
+	}
+out:
+	if (action != TUX_ACTION_CURRENT_DATE)
+		Dprintk("sys_tux(%d, %p) returning %d.\n", action, u_info, ret);
+	while (unlikely(test_thread_flag(TIF_NEED_RESCHED))) {
+		__set_task_state(current, TASK_RUNNING);
+		schedule();
+	}
+	return ret;
+err_unlock:
+err_no_unlock:
+	Dprintk("sys_tux(%d, %p) returning -EINVAL (ret:%d)!\n", action, u_info, ret);
+	while (unlikely(test_thread_flag(TIF_NEED_RESCHED))) {
+		__set_task_state(current, TASK_RUNNING);
+		schedule();
+	}
+	return -EINVAL;
+}
+
+/*
+ * This gets called if a TUX thread does an exit().
+ */
+void tux_exit (void)
+{
+	__sys_tux(TUX_ACTION_STOPTHREAD, NULL);
+}
+
+int tux_init(void)
+{
+	if (init_tux_request_slabs())
+		return -ENOMEM;
+
+	start_sysctl();
+
+#ifdef CONFIG_TUX_MODULE
+	spin_lock(&tux_module_lock);
+	sys_tux_ptr = __sys_tux;
+	tux_module = THIS_MODULE;
+	spin_unlock(&tux_module_lock);
+#endif
+
+	return 0;
+}
+
+void tux_cleanup (void)
+{
+#ifdef CONFIG_TUX_MODULE
+	spin_lock(&tux_module_lock);
+	tux_module = NULL;
+	sys_tux_ptr = NULL;
+	spin_unlock(&tux_module_lock);
+#endif
+	end_sysctl();
+
+	free_tux_request_slabs();
+}
+
+module_init(tux_init)
+module_exit(tux_cleanup)
+
+MODULE_LICENSE("GPL");
+
Index: latest/net/tux/Makefile
===================================================================
--- /dev/null
+++ latest/net/tux/Makefile
@@ -0,0 +1,12 @@
+#
+# Makefile for TUX
+#
+
+obj-$(CONFIG_TUX) += tux.o
+
+tux-y := accept.o input.o userspace.o cachemiss.o output.o \
+	redirect.o postpone.o logger.o proto_http.o proto_ftp.o \
+	proc.o main.o mod.o abuf.o times.o directory.o gzip.o
+
+tux-$(subst m,y,$(CONFIG_TUX_EXTCGI)) += cgi.o extcgi.o
+
Index: latest/net/tux/mod.c
===================================================================
--- /dev/null
+++ latest/net/tux/mod.c
@@ -0,0 +1,262 @@
+/*
+ * TUX - Integrated Application Protocols Layer and Object Cache
+ *
+ * Copyright (C) 2000, 2001, Ingo Molnar <mingo@redhat.com>
+ *
+ * mod.c: loading/registering of dynamic TUX modules
+ */
+
+#include <net/tux.h>
+#include <linux/kmod.h>
+
+/****************************************************************
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2, or (at your option)
+ *      any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ ****************************************************************/
+
+DEFINE_SPINLOCK(tuxmodules_lock);
+static LIST_HEAD(tuxmodules_list);
+
+tcapi_template_t * get_first_usermodule (void)
+{
+	tcapi_template_t *tcapi;
+	struct list_head *head, *curr, *next;
+
+	spin_lock(&tuxmodules_lock);
+	head = &tuxmodules_list;
+	next = head->next;
+
+	while ((curr = next) != head) {
+		tcapi = list_entry(curr, tcapi_template_t, modules);
+		next = curr->next;
+		if (tcapi->userspace_id) {
+			spin_unlock(&tuxmodules_lock);
+			return tcapi;
+		}
+	}
+	spin_unlock(&tuxmodules_lock);
+	return NULL;
+}
+
+static tcapi_template_t * lookup_module (const char *vfs_name)
+{
+	tcapi_template_t *tcapi;
+	struct list_head *head, *curr, *next;
+
+	while (*vfs_name == '/')
+		vfs_name++;
+	Dprintk("looking up TUX module {%s}.\n", vfs_name);
+	head = &tuxmodules_list;
+	next = head->next;
+
+	while ((curr = next) != head) {
+		tcapi = list_entry(curr, tcapi_template_t, modules);
+		next = curr->next;
+		Dprintk("checking module {%s} == {%s}?\n", vfs_name, tcapi->vfs_name);
+		if (!strcmp(tcapi->vfs_name, vfs_name))
+			return tcapi;
+	}
+	return NULL;
+}
+
+/*
+ * Attempt to load a TUX application module.
+ * This is the slow path, we cache ('link') the module's
+ * API vector to the inode.
+ * The module loading path is serialized, and we handshake
+ * with the loaded module and fetch its API vector.
+ */
+tcapi_template_t * lookup_tuxmodule (const char *filename)
+{
+	tcapi_template_t *tcapi;
+
+	spin_lock(&tuxmodules_lock);
+	tcapi = lookup_module(filename);
+	if (!tcapi)
+		Dprintk("did not find module vfs:{%s}\n", filename);
+	spin_unlock(&tuxmodules_lock);
+	return tcapi;
+}
+
+
+int register_tuxmodule (tcapi_template_t *tcapi)
+{
+	int ret = -EEXIST;
+
+	spin_lock(&tuxmodules_lock);
+
+	if (lookup_module(tcapi->vfs_name)) {
+		Dprintk("module with VFS binding '%s' already registered!\n",
+						 tcapi->vfs_name);
+		goto out;
+	}
+
+	list_add(&tcapi->modules, &tuxmodules_list);
+	ret = 0;
+	Dprintk("TUX module %s registered.\n", tcapi->vfs_name);
+out:
+	spin_unlock(&tuxmodules_lock);
+
+	return ret;
+}
+
+void unregister_all_tuxmodules (void)
+{
+	tcapi_template_t *tcapi;
+	struct list_head *curr;
+
+	spin_lock(&tuxmodules_lock);
+	while (((curr = tuxmodules_list.next)) != &tuxmodules_list) {
+		tcapi = list_entry(curr, tcapi_template_t, modules);
+		list_del(curr);
+		kfree(tcapi->vfs_name);
+		kfree(tcapi);
+	}
+	spin_unlock(&tuxmodules_lock);
+}
+
+tcapi_template_t * unregister_tuxmodule (char *vfs_name)
+{
+	tcapi_template_t *tcapi;
+	int err = 0;
+
+	spin_lock(&tuxmodules_lock);
+	tcapi = lookup_module(vfs_name);
+	if (!tcapi) {
+		Dprintk("huh, module %s not registered??\n", vfs_name);
+		err = -1;
+	} else {
+		list_del(&tcapi->modules);
+		Dprintk("TUX module %s unregistered.\n", vfs_name);
+	}
+	spin_unlock(&tuxmodules_lock);
+
+	return tcapi;
+}
+
+static int check_module_version (user_req_t *u_info)
+{
+	int major, minor, patch, ret;
+
+	ret = copy_from_user(&major, &u_info->version_major, sizeof(int));
+	ret += copy_from_user(&minor, &u_info->version_minor, sizeof(int));
+	ret += copy_from_user(&patch, &u_info->version_patch, sizeof(int));
+	if (ret)
+		return -EFAULT;
+
+	if ((major != TUX_MAJOR_VERSION) || (minor > TUX_MINOR_VERSION)) {
+
+		printk(KERN_ERR "TUX: module version %d:%d incompatible with kernel version %d:%d!\n", major, minor, TUX_MAJOR_VERSION, TUX_MINOR_VERSION);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+int user_register_module (user_req_t *u_info)
+{
+	int idx, len, ret;
+	tcapi_template_t *tcapi;
+	char modulename [MAX_URI_LEN+1];
+
+	ret = check_module_version(u_info);
+	if (ret)
+		return ret;
+
+	/*
+	 * Check module name length.
+	 */
+	ret = strnlen_user(u_info->objectname, MAX_URI_LEN+2);
+	if (ret < 0)
+		goto out;
+	ret = -EINVAL;
+	if (ret >= MAX_URI_LEN)
+		goto out;
+
+	Dprintk("register user-module, %p.\n", u_info);
+	ret = strncpy_from_user(modulename, u_info->objectname, MAX_URI_LEN);
+	if (ret < 0)
+		goto out;
+	modulename[ret] = 0;
+	Dprintk("... user-module is: {%s}.\n", modulename);
+	len = strlen(modulename);
+	if (!len)
+		printk(KERN_ERR "no module name provided: please upgrade your TUX user-space utilities!\n");
+	if (!len || (len > MAX_URI_LEN))
+		return -EINVAL;
+	Dprintk("... user-module len is: %d.\n", len);
+
+	ret = copy_from_user(&idx, &u_info->module_index, sizeof(int));
+	if (ret || !idx)
+		goto out;
+	Dprintk("... user-module index is: %d.\n", idx);
+
+	ret = -ENOMEM;
+	tcapi = (tcapi_template_t *) kmalloc(sizeof(*tcapi), GFP_KERNEL);
+	if (!tcapi)
+		goto out;
+	memset(tcapi, 0, sizeof(*tcapi));
+
+	tcapi->vfs_name = (char *) kmalloc(len+1, GFP_KERNEL);
+	if (!tcapi->vfs_name) {
+		kfree(tcapi);
+		goto out;
+	}
+	strcpy(tcapi->vfs_name, modulename);
+	tcapi->userspace_id = idx;
+
+	Dprintk("... registering module {%s}.\n", tcapi->vfs_name);
+	ret = register_tuxmodule(tcapi);
+out:
+	return ret;
+}
+
+int user_unregister_module (user_req_t *u_info)
+{
+	int len, ret;
+	tcapi_template_t *tcapi;
+	char modulename [MAX_URI_LEN+1];
+
+	/*
+	 * Check module name length.
+	 */
+	ret = strnlen_user(u_info->objectname, MAX_URI_LEN+2);
+	if (ret < 0)
+		goto out;
+	ret = -EINVAL;
+	if (ret >= MAX_URI_LEN)
+		goto out;
+	Dprintk("unregister user-module, %p.\n", u_info);
+	ret = strncpy_from_user(modulename, u_info->objectname, MAX_URI_LEN);
+	if (ret <= 0)
+		goto out;
+	modulename[ret] = 0;
+	Dprintk("... user-module is: {%s}.\n", modulename);
+	len = strlen(modulename);
+	if (!len || (len > MAX_URI_LEN))
+		return -EINVAL;
+	Dprintk("... user-module len is: %d.\n", len);
+
+	Dprintk("... unregistering module {%s}.\n", modulename);
+	tcapi = unregister_tuxmodule(modulename);
+	ret = -EINVAL;
+	if (tcapi) {
+		ret = 0;
+		kfree(tcapi->vfs_name);
+		kfree(tcapi);
+	}
+out:
+	return ret;
+}
+
Index: latest/net/tux/output.c
===================================================================
--- /dev/null
+++ latest/net/tux/output.c
@@ -0,0 +1,352 @@
+/*
+ * TUX - Integrated Application Protocols Layer and Object Cache
+ *
+ * Copyright (C) 2000, 2001, Ingo Molnar <mingo@redhat.com>
+ *
+ * output.c: Send data to clients
+ */
+
+#include <net/tux.h>
+
+/****************************************************************
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2, or (at your option)
+ *      any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ ****************************************************************/
+
+int send_sync_buf (tux_req_t *req, struct socket *sock, const char *buf, const size_t length, unsigned long flags)
+{
+	struct msghdr msg;
+	struct iovec iov;
+	int len, written = 0, left = length;
+	struct tcp_sock *tp = tcp_sk(sock->sk);
+
+	tp->nonagle = 2;
+
+	msg.msg_name     = NULL;
+	msg.msg_namelen  = 0;
+	msg.msg_iov	 = &iov;
+	msg.msg_iovlen   = 1;
+	msg.msg_control  = NULL;
+	msg.msg_controllen = 0;
+	msg.msg_flags    = flags | MSG_NOSIGNAL;
+repeat_send:
+	msg.msg_iov->iov_len = left;
+	msg.msg_iov->iov_base = (char *) buf + written;
+
+	len = sock_sendmsg(sock, &msg, left);
+
+	Dprintk("sendmsg ret: %d, written: %d, left: %d.\n", len,written,left);
+	if ((len == -ERESTARTSYS) || (!(flags & MSG_DONTWAIT) &&
+			 (len == -EAGAIN))) {
+		flush_all_signals();
+		goto repeat_send;
+	}
+	if (len > 0) {
+		written += len;
+		left -= len;
+		if (left)
+			goto repeat_send;
+	}
+	if (len >= 0) {
+		if (written != length)
+			TUX_BUG();
+		if (left)
+			TUX_BUG();
+	}
+	if (req && (written > 0))
+		req->bytes_sent += written;
+	Dprintk("sendmsg FINAL ret: %d, written: %d, left: %d.\n", len,written,left);
+	return written ? written : len;
+}
+
+unsigned int tux_zerocopy_sendfile = 1;
+
+typedef struct sock_send_desc
+{
+	struct socket *sock;
+	tux_req_t *req;
+} sock_send_desc_t;
+
+static int sock_send_actor (read_descriptor_t * desc, struct page *page,
+				unsigned long offset, unsigned long orig_size)
+{
+	sock_send_desc_t *sock_desc = (sock_send_desc_t *)desc->arg.buf;
+	struct socket *sock = sock_desc->sock;
+	tux_req_t *req = sock_desc->req;
+	unsigned int flags;
+	ssize_t written;
+	char *buf = NULL;
+	unsigned int size;
+
+	flags = MSG_DONTWAIT | MSG_NOSIGNAL;
+	if (desc->count < orig_size)
+		orig_size = desc->count;
+	if (desc->count > orig_size)
+		flags |= MSG_MORE;
+	Dprintk("sock_send_actor(), page: %p, offset: %ld, orig_size: %ld, sock: %p, desc->count: %d, desc->written: %d, MSG_MORE: %d.\n", page, offset, orig_size, sock, desc->count, desc->written, flags & MSG_MORE);
+
+	if (req->content_gzipped >= 2) {
+		unsigned int gzip_left;
+		struct msghdr msg;
+		struct iovec iov;
+		mm_segment_t oldmm;
+		char *kaddr = kmap(page);
+		__u32 in_len, out_len;
+		out_len = orig_size*101/100 + 12;
+		buf = tux_kmalloc(out_len);
+		in_len = orig_size;
+		size = out_len;
+		gzip_left = 0;
+// 8b1f 0808 fdc4 3bd8 0300 79
+buf[1] = 0x8b; buf[0] = 0x1f; buf[3] = 0x08; buf[2] = 0x08;
+buf[5] = 0xfd; buf[4] = 0xc4; buf[7] = 0x3b; buf[6] = 0xd8;
+buf[9] = 0x03; buf[8] = 0x00; buf[10] = 0x79;
+		size += 11;
+		Dprintk("pre-compress: in_len: %d, out_len: %d, gzip_left: %d, uncompressed size: %d.\n", in_len, out_len, gzip_left, size);
+		gzip_left = tux_gzip_compress(req, kaddr, buf+11, &in_len, &out_len);
+		size -= out_len;
+ buf[11] = 0x79; buf[12] = 0x00;
+
+		Dprintk("post-compress: in_len: %d, out_len: %d, gzip_left: %d, compressed size: %d.\n", in_len, out_len, gzip_left, size);
+		kunmap(page);
+		msg.msg_name = NULL;
+		msg.msg_namelen = 0;
+		msg.msg_iov = &iov;
+		msg.msg_iovlen = 1;
+		msg.msg_control = NULL;
+		msg.msg_controllen = 0;
+		flags &= ~MSG_DONTWAIT;
+		msg.msg_flags = flags;
+		iov.iov_base = buf;
+		iov.iov_len = size;
+
+		oldmm = get_fs(); set_fs(KERNEL_DS);
+		written = sock_sendmsg(sock, &msg, size);
+		set_fs(oldmm);
+
+		Dprintk("buf: %p, offset: %ld, size: %d, written: %d.\n", buf, offset, size, written);
+		if (written == size)
+			written = orig_size;
+		else
+			written = size;
+
+	} else {
+		size = orig_size;
+		if (tux_zerocopy_sendfile && sock->ops->sendpage &&
+		    (sock->sk->sk_route_caps&NETIF_F_SG)) {
+			written = sock->ops->sendpage(sock, page, offset, size, flags);
+		} else {
+			struct msghdr msg;
+			struct iovec iov;
+			char *kaddr;
+			mm_segment_t oldmm;
+
+			if (offset+size > PAGE_SIZE)
+				return -EFAULT;
+
+			kaddr = kmap(page);
+
+			msg.msg_name = NULL;
+			msg.msg_namelen = 0;
+			msg.msg_iov = &iov;
+			msg.msg_iovlen = 1;
+			msg.msg_control = NULL;
+			msg.msg_controllen = 0;
+			msg.msg_flags = flags;
+			iov.iov_base = kaddr + offset;
+			iov.iov_len = size;
+
+			oldmm = get_fs(); set_fs(KERNEL_DS);
+			written = sock_sendmsg(sock, &msg, size);
+			set_fs(oldmm);
+
+			Dprintk("kaddr: %p, offset: %ld, size: %d, written: %d.\n", kaddr, offset, size, written);
+			kunmap(page);
+		}
+	}
+	if (written < 0) {
+		desc->error = written;
+		written = 0;
+	}
+	Dprintk("desc->count: %d, desc->written: %d, written: %d.\n", desc->count, desc->written, written);
+	desc->count -= written;
+	if ((int)desc->count < 0)
+		TUX_BUG();
+	desc->written += written;
+
+	if (buf)
+		kfree(buf);
+
+	return written;
+}
+
+/*
+ * Return 1 if the output space condition went away
+ * before adding the handler.
+ */
+int add_output_space_event (tux_req_t *req, struct socket *sock)
+{
+	struct sock *sk = sock->sk;
+	/*
+	 * blocked due to socket IO?
+	 */
+	spin_lock_irq(&req->ti->work_lock);
+	add_keepalive_timer(req);
+	if (test_and_set_bit(0,&req->wait_output_space))
+		TUX_BUG();
+	INC_STAT(nr_output_space_pending);
+
+	if ((sk->sk_state == TCP_ESTABLISHED) && enough_wspace(sk)) {
+		if (test_and_clear_bit(0, &req->wait_output_space)) {
+			DEC_STAT(nr_output_space_pending);
+			del_keepalive_timer(req);
+			spin_unlock_irq(&req->ti->work_lock);
+			return 1;
+		}
+	}
+	spin_unlock_irq(&req->ti->work_lock);
+
+	return 0;
+}
+
+#define SEND_BLOCKSIZE (164*1024)
+
+int generic_send_file (tux_req_t *req, struct socket *sock, int cachemiss)
+{
+	sock_send_desc_t sock_desc;
+	int len, want, nonblock = !cachemiss;
+	struct tcp_sock *tp = tcp_sk(sock->sk);
+
+	tp->nonagle = 2;
+
+	sock_desc.sock = sock;
+	sock_desc.req = req;
+
+repeat:
+	Dprintk("generic_send_file(%p,%d,%p) called, f_pos: %Ld, output_len: %Ld.\n", req, nonblock, sock, req->in_file->f_pos, req->output_len);
+
+	if (req->proto->check_req_err(req, cachemiss))
+		return -1;
+	if (connection_too_fast(req) == 2) {
+		len = -5;
+		goto out;
+	}
+	if (req->total_file_len < req->in_file->f_pos)
+		TUX_BUG();
+
+	req->desc.written = 0;
+	/*
+	 * Careful, output_len can be 64-bit, while 'want' can be 32-bit.
+	 */
+	if (req->output_len > SEND_BLOCKSIZE)
+		want = SEND_BLOCKSIZE;
+	else
+		want = req->output_len;
+	req->desc.count = want;
+	req->desc.arg.buf = (char *) &sock_desc;
+	req->desc.error = 0;
+	Dprintk("sendfile(), desc.count: %d.\n", req->desc.count);
+	do_generic_file_read(req->in_file, &req->in_file->f_pos, &req->desc, sock_send_actor, nonblock);
+	if (req->desc.written > 0) {
+		req->bytes_sent += req->desc.written;
+		req->output_len -= req->desc.written;
+	}
+	if (!nonblock && (req->desc.error == -EWOULDBLOCKIO))
+		TUX_BUG();
+	Dprintk("sendfile() wrote: %d bytes.\n", req->desc.written);
+	if (req->output_len && !req->desc.written && !req->desc.error) {
+#ifdef CONFIG_TUX_DEBUG
+		req->bytes_expected = 0;
+#endif
+		req->in_file->f_pos = 0;
+		req->error = TUX_ERROR_CONN_CLOSE;
+		zap_request(req, cachemiss);
+		return -1;
+	}
+
+	switch (req->desc.error) {
+
+	case -EWOULDBLOCKIO:
+		len = -3;
+		break;
+	case -EAGAIN:
+no_write_space:
+		Dprintk("sk->wmem_queued: %d, sk->sndbuf: %d.\n",
+			sock->sk->sk_wmem_queued, sock->sk->sk_sndbuf);
+		len = -4;
+		break;
+	default:
+		len = req->desc.written;
+#ifdef CONFIG_TUX_DEBUG
+		if (req->desc.error)
+			TDprintk("TUX: sendfile() returned error %d (signals pending: %08lx)!\n", req->desc.error, current->pending.signal.sig[0]);
+#endif
+		if (!req->desc.error) {
+			if (req->output_len < 0)
+				BUG();
+			if (req->output_len) {
+				if (test_bit(SOCK_NOSPACE, &sock->flags))
+					goto no_write_space;
+				goto repeat;
+			}
+		}
+#ifdef CONFIG_TUX_DEBUG
+		if (req->desc.written != want)
+			TDprintk("TUX: sendfile() wrote %d bytes, wanted %d! (pos %Ld) (signals pending: %08lx).\n", req->desc.written, want, req->in_file->f_pos, current->pending.signal.sig[0]);
+		else
+			Dprintk("TUX: sendfile() FINISHED for req %p, wrote %d bytes.\n", req, req->desc.written);
+		req->bytes_expected = 0;
+#endif
+		break;
+	}
+
+out:
+	Dprintk("sendfile() wrote %d bytes.\n", len);
+
+	return len;
+}
+
+static int file_fetch_actor (read_descriptor_t * desc, struct page *page,
+				unsigned long offset, unsigned long size)
+{
+	if (desc->count < size)
+		size = desc->count;
+
+	desc->count -= size;
+	desc->written += size;
+
+	return size;
+}
+
+int tux_fetch_file (tux_req_t *req, int nonblock)
+{
+	int len;
+
+	req->desc.written = 0;
+	req->desc.count = req->output_len;
+	req->desc.arg.buf = NULL;
+	req->desc.error = 0;
+
+	do_generic_file_read(req->in_file, &req->in_file->f_pos, &req->desc,
+					file_fetch_actor, nonblock);
+	if (nonblock && (req->desc.error == -EWOULDBLOCKIO))
+		return 1;
+	len = req->desc.written;
+	if (req->desc.error)
+		Dprintk("fetchfile() returned %d error!\n", req->desc.error);
+	Dprintk("fetchfile() fetched %d bytes.\n", len);
+	return 0;
+}
+
Index: latest/net/tux/parser.h
===================================================================
--- /dev/null
+++ latest/net/tux/parser.h
@@ -0,0 +1,102 @@
+/*
+ * TUX - Integrated Application Protocols Layer and Object Cache
+ *
+ * Copyright (C) 2000, Ingo Molnar <mingo@redhat.com>
+ *
+ * parser.h: generic parsing routines
+ */
+
+#define get_c(ptr,left)						\
+({								\
+	char __ret;					\
+								\
+	if (!left)						\
+		GOTO_INCOMPLETE;				\
+	left--;							\
+	__ret = *((ptr)++);					\
+	if (!__ret)						\
+		GOTO_REDIR;					\
+	__ret;							\
+})
+
+#define PARSE_TOKEN(ptr,str,left)				\
+	({							\
+		int __ret;					\
+								\
+		if (!left)					\
+			GOTO_INCOMPLETE;			\
+		if (sizeof(str)-1 > left) {			\
+			if (memcmp(ptr, str, left))		\
+				GOTO_REDIR;			\
+			GOTO_INCOMPLETE;			\
+		}						\
+								\
+		if (memcmp(ptr, str, sizeof(str)-1))		\
+			__ret = 0;				\
+		else {						\
+			ptr += sizeof(str)-1;			\
+			left -= sizeof(str)-1;			\
+			__ret = 1;				\
+		}						\
+		__ret;						\
+	})
+
+#define PARSE_METHOD(req,ptr,name,left)				\
+	({							\
+		int __ret;					\
+								\
+		if (PARSE_TOKEN(ptr,#name" ",left)) {		\
+			req->method = METHOD_##name;		\
+			__ret = 1;				\
+		} else						\
+			__ret = 0;				\
+		__ret;						\
+	})
+
+#define COPY_LINE(ptr,target,left)				\
+	do {							\
+		char prev_c = 0, c;				\
+		while (((c = get_c(ptr,left))) != '\n')	\
+			*target++ = prev_c = c;			\
+		if (prev_c != '\r')				\
+			GOTO_REDIR;				\
+	} while (0)
+
+#define COPY_LINE_TOLOWER(ptr,target,left,limit)		\
+	do {							\
+		char prev_c = 0, c;				\
+		while (((c = get_c(ptr,left))) != '\n') {	\
+			if ((c >= 'A') && (c <= 'Z'))		\
+				c -= 'A'-'a';			\
+			*target++ = prev_c = c;			\
+			if (target == (limit))			\
+				GOTO_REDIR;			\
+		}						\
+		if (prev_c != '\r')				\
+			GOTO_REDIR;				\
+	} while (0)
+
+#define COPY_FIELD(ptr,target,left)				\
+	do {							\
+		char c;						\
+		while ((c = get_c(ptr,left)) != ' ')		\
+			*target++ = c;				\
+	} while (0)
+
+#define SKIP_LINE(ptr,left)					\
+	do {							\
+		char prev_c = 0, c;				\
+		while (((c = get_c(ptr,left))) != '\n')		\
+			prev_c = c;				\
+		if (prev_c != '\r')				\
+			GOTO_REDIR;				\
+	} while (0)
+
+#define SKIP_WHITESPACE(curr,left)		\
+do {						\
+	while ((left) && (*(curr) == ' '))	\
+		(curr)++, (left)--;		\
+	if (!(left))				\
+		GOTO_REDIR;			\
+} while (0)
+
Index: latest/net/tux/postpone.c
===================================================================
--- /dev/null
+++ latest/net/tux/postpone.c
@@ -0,0 +1,77 @@
+/*
+ * TUX - Integrated Application Protocols Layer and Object Cache
+ *
+ * Copyright (C) 2000, 2001, Ingo Molnar <mingo@redhat.com>
+ *
+ * postpone.c: postpone/continue userspace requests
+ */
+
+#include <net/tux.h>
+
+/****************************************************************
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2, or (at your option)
+ *      any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ ****************************************************************/
+
+void postpone_request (tux_req_t *req)
+{
+	if (!req->usermode)
+		TUX_BUG();
+	INC_STAT(nr_postpone_pending);
+	req->postponed = 1;
+}
+
+/*
+ * Continue a postponed request. The request will show up in the
+ * userspace queue and will be handled by the fast thread.
+ * A request can only be postponed in a TUX process, but can be
+ * continued from any process that has access to the socket file
+ * descriptor.
+ */
+int continue_request (int fd)
+{
+	threadinfo_t *ti;
+	struct socket *sock;
+	tux_req_t *req;
+	int err;
+
+	sock = sockfd_lookup(fd, &err);
+	if (!sock || !sock->sk)
+		goto out;
+	req = sock->sk->sk_user_data;
+
+	err = -EINVAL;
+	if (!req)
+		goto out_put;
+	ti = req->ti;
+	if (!req->postponed)
+		goto out_unlock_put;
+	if (!req->usermode)
+		TUX_BUG();
+
+	req->postponed = 0;
+	DEC_STAT(nr_postpone_pending);
+
+	Dprintk("continuing postponed req %p.\n", req);
+	add_req_to_workqueue(req);
+
+out_unlock_put:
+	err = 0;
+out_put:
+	fput(sock->file);
+out:
+	return err;
+}
+
Index: latest/net/tux/proc.c
===================================================================
--- /dev/null
+++ latest/net/tux/proc.c
@@ -0,0 +1,1149 @@
+/*
+ * TUX - Integrated Application Protocols Layer and Object Cache
+ *
+ * Copyright (C) 2000, 2001, Ingo Molnar <mingo@redhat.com>
+ *
+ * proc.c: /proc/sys/tux handling
+ */
+
+#include <net/tux.h>
+
+/****************************************************************
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2, or (at your option)
+ *      any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ ****************************************************************/
+
+char tux_common_docroot[200] = "/var/www/tux/";
+char tux_http_subdocroot[200] = "";
+char tux_ftp_subdocroot[200] = "";
+char tux_logfile[200] = "/var/log/tux";
+char tux_cgiroot[200] = "/var/www/tux/cgiroot/";
+char tux_404_page[200] = "404.html";
+char tux_default_vhost[200] = "default";
+char tux_extra_html_header[600];
+unsigned int tux_extra_html_header_size = 0;
+
+int tux_cgi_uid = -1;
+int tux_cgi_gid = -1;
+unsigned int tux_clientport = 8080;
+unsigned int tux_logging = 0;
+unsigned int tux_threads = 2;
+unsigned int tux_max_connect = 10000;
+unsigned int tux_max_keepalives = 10000;
+unsigned int tux_max_backlog = 2048;
+unsigned int tux_keepalive_timeout = 0;
+unsigned int tux_max_output_bandwidth = 0;
+unsigned int tux_defer_accept = 1;
+unsigned int tux_mode_forbidden = 0 /*S_IXUGO*/; /* do not allow executable (CGI) files */
+unsigned int tux_mode_allowed = S_IROTH; /* allow access if read-other is set */
+unsigned int tux_virtual_server = 0;
+unsigned int tux_ftp_virtual_server = 0;
+unsigned int mass_hosting_hash = 0;
+unsigned int strip_host_tail = 0;
+unsigned int tux_max_object_size = 0;
+cpumask_t tux_log_cpu_mask = CPU_MASK_ALL;
+unsigned int tux_compression = 0;
+unsigned int tux_noid = 0;
+unsigned int tux_cgi_inherit_cpu = 0;
+cpumask_t tux_cgi_cpu_mask = CPU_MASK_ALL;
+unsigned int tux_zerocopy_header = 1;
+unsigned int tux_max_free_requests = 1000;
+unsigned int tux_ignore_query = 0;
+unsigned int tux_all_userspace = 0;
+unsigned int tux_redirect_logging = 1;
+unsigned int tux_max_header_len = 3000;
+unsigned int tux_referer_logging = 0;
+unsigned int tux_generate_etags = 1;
+unsigned int tux_generate_last_mod = 1;
+unsigned int tux_generate_cache_control = 1;
+unsigned int tux_ip_logging = 1;
+unsigned int tux_ftp_wait_close = 1;
+unsigned int tux_ftp_log_retr_only = 0;
+unsigned int tux_hide_unreadable = 1;
+unsigned int tux_http_dir_indexing = 0;
+unsigned int tux_log_incomplete = 0;
+unsigned int tux_cpu_offset = 0;
+unsigned int tux_ftp_login_message = 0;
+
+static struct ctl_table_header *tux_table_header;
+
+static ctl_table tux_table[] = {
+	{	NET_TUX_DOCROOT,
+		"documentroot",
+		&tux_common_docroot,
+		sizeof(tux_common_docroot),
+		0644,
+		NULL,
+		proc_dostring,
+		&sysctl_string,
+		NULL,
+		NULL,
+		NULL
+	},
+	{	NET_TUX_DOCROOT,
+		"http_subdocroot",
+		&tux_http_subdocroot,
+		sizeof(tux_http_subdocroot),
+		0644,
+		NULL,
+		proc_dostring,
+		&sysctl_string,
+		NULL,
+		NULL,
+		NULL
+	},
+	{	NET_TUX_DOCROOT,
+		"ftp_subdocroot",
+		&tux_ftp_subdocroot,
+		sizeof(tux_ftp_subdocroot),
+		0644,
+		NULL,
+		proc_dostring,
+		&sysctl_string,
+		NULL,
+		NULL,
+		NULL
+	},
+	{	NET_TUX_LOGFILE,
+		"logfile",
+		&tux_logfile,
+		sizeof(tux_logfile),
+		0644,
+		NULL,
+		proc_dostring,
+		&sysctl_string,
+		NULL,
+		NULL,
+		NULL
+	},
+	{	NET_TUX_THREADS,
+		"threads",
+		&tux_threads,
+		sizeof(int),
+		0644,
+		NULL,
+		proc_dointvec,
+		&sysctl_intvec,
+		NULL,
+		NULL,
+		NULL
+	},
+	{	NET_TUX_KEEPALIVE_TIMEOUT,
+		"keepalive_timeout",
+		&tux_keepalive_timeout,
+		sizeof(int),
+		0644,
+		NULL,
+		proc_dointvec,
+		&sysctl_intvec,
+		NULL,
+		NULL,
+		NULL
+	},
+	{	NET_TUX_MAX_KEEPALIVE_BW,
+		"max_output_bandwidth",
+		&tux_max_output_bandwidth,
+		sizeof(int),
+		0644,
+		NULL,
+		proc_dointvec,
+		&sysctl_intvec,
+		NULL,
+		NULL,
+		NULL
+	},
+	{	NET_TUX_DEFER_ACCEPT,
+		"defer_accept",
+		&tux_defer_accept,
+		sizeof(int),
+		0644,
+		NULL,
+		proc_dointvec,
+		&sysctl_intvec,
+		NULL,
+		NULL,
+		NULL
+	},
+	{	NET_TUX_MAX_BACKLOG,
+		"max_backlog",
+		&tux_max_backlog,
+		sizeof(int),
+		0644,
+		NULL,
+		proc_dointvec,
+		&sysctl_intvec,
+		NULL,
+		NULL,
+		NULL
+	},
+	{	NET_TUX_MAX_CONNECT,
+		"max_connect",
+		&tux_max_connect,
+		sizeof(int),
+		0644,
+		NULL,
+		proc_dointvec,
+		&sysctl_intvec,
+		NULL,
+		NULL,
+		NULL
+	},
+	{	NET_TUX_MAX_KEEPALIVES,
+		"max_keepalives",
+		&tux_max_keepalives,
+		sizeof(int),
+		0644,
+		NULL,
+		proc_dointvec,
+		&sysctl_intvec,
+		NULL,
+		NULL,
+		NULL
+	},
+	{	NET_TUX_MODE_FORBIDDEN,
+		"mode_forbidden",
+		&tux_mode_forbidden,
+		sizeof(int),
+		0644,
+		NULL,
+		proc_dointvec,
+		&sysctl_intvec,
+		NULL,
+		NULL,
+		NULL
+	},
+	{	NET_TUX_MODE_ALLOWED,
+		"mode_allowed",
+		&tux_mode_allowed,
+		sizeof(int),
+		0644,
+		NULL,
+		proc_dointvec,
+		&sysctl_intvec,
+		NULL,
+		NULL,
+		NULL
+	},
+	{	NET_TUX_CGI_UID,
+		"cgi_uid",
+		&tux_cgi_uid,
+		sizeof(int),
+		0644,
+		NULL,
+		proc_dointvec,
+		&sysctl_intvec,
+		NULL,
+		NULL,
+		NULL
+	},
+	{	NET_TUX_CGI_GID,
+		"cgi_gid",
+		&tux_cgi_gid,
+		sizeof(int),
+		0644,
+		NULL,
+		proc_dointvec,
+		&sysctl_intvec,
+		NULL,
+		NULL,
+		NULL
+	},
+	{	NET_TUX_CGIROOT,
+		"cgiroot",
+		&tux_cgiroot,
+		sizeof(tux_cgiroot),
+		0644,
+		NULL,
+		proc_dostring,
+		&sysctl_string,
+		NULL,
+		NULL,
+		NULL
+	},
+	{	NET_TUX_404_PAGE,
+		"404_page",
+		&tux_404_page,
+		sizeof(tux_404_page),
+		0644,
+		NULL,
+		proc_dostring,
+		&sysctl_string,
+		NULL,
+		NULL,
+		NULL
+	},
+	{	NET_TUX_404_PAGE,
+		"default_vhost",
+		&tux_default_vhost,
+		sizeof(tux_default_vhost),
+		0644,
+		NULL,
+		proc_dostring,
+		&sysctl_string,
+		NULL,
+		NULL,
+		NULL
+	},
+	{	NET_TUX_404_PAGE,
+		"extra_html_header",
+		&tux_extra_html_header,
+		sizeof(tux_extra_html_header),
+		0644,
+		NULL,
+		proc_dostring,
+		&sysctl_string,
+		NULL,
+		NULL,
+		NULL
+	},
+	{	NET_TUX_CLIENTPORT,
+		"extra_html_header_size",
+		&tux_extra_html_header_size,
+		sizeof(int),
+		0644,
+		NULL,
+		proc_dointvec,
+		&sysctl_intvec,
+		NULL,
+		NULL,
+		NULL
+	},
+	{	NET_TUX_CLIENTPORT,
+		"clientport",
+		&tux_clientport,
+		sizeof(int),
+		0644,
+		NULL,
+		proc_dointvec,
+		&sysctl_intvec,
+		NULL,
+		NULL,
+		NULL
+	},
+	{	NET_TUX_CLIENTPORT,
+		"generate_etags",
+		&tux_generate_etags,
+		sizeof(int),
+		0644,
+		NULL,
+		proc_dointvec,
+		&sysctl_intvec,
+		NULL,
+		NULL,
+		NULL
+	},
+        {       NET_TUX_CLIENTPORT,
+                "generate_last_mod",
+                &tux_generate_last_mod,
+                sizeof(int),
+                0644,
+                NULL,
+                proc_dointvec,
+                &sysctl_intvec,
+                NULL,
+                NULL,
+                NULL
+        },
+        {       NET_TUX_CLIENTPORT,
+                "generate_cache_control",
+                &tux_generate_cache_control,
+                sizeof(int),
+                0644,
+                NULL,
+                proc_dointvec,
+                &sysctl_intvec,
+                NULL,
+                NULL,
+                NULL
+        },
+	{	NET_TUX_CLIENTPORT,
+		"ip_logging",
+		&tux_ip_logging,
+		sizeof(int),
+		0644,
+		NULL,
+		proc_dointvec,
+		&sysctl_intvec,
+		NULL,
+		NULL,
+		NULL
+	},
+	{	NET_TUX_CLIENTPORT,
+		"ftp_wait_close",
+		&tux_ftp_wait_close,
+		sizeof(int),
+		0644,
+		NULL,
+		proc_dointvec,
+		&sysctl_intvec,
+		NULL,
+		NULL,
+		NULL
+	},
+	{	NET_TUX_CLIENTPORT,
+		"ftp_log_retr_only",
+		&tux_ftp_log_retr_only,
+		sizeof(int),
+		0644,
+		NULL,
+		proc_dointvec,
+		&sysctl_intvec,
+		NULL,
+		NULL,
+		NULL
+	},
+	{	NET_TUX_CLIENTPORT,
+		"http_dir_indexing",
+		&tux_http_dir_indexing,
+		sizeof(int),
+		0644,
+		NULL,
+		proc_dointvec,
+		&sysctl_intvec,
+		NULL,
+		NULL,
+		NULL
+	},
+	{	NET_TUX_CLIENTPORT,
+		"hide_unreadable",
+		&tux_hide_unreadable,
+		sizeof(int),
+		0644,
+		NULL,
+		proc_dointvec,
+		&sysctl_intvec,
+		NULL,
+		NULL,
+		NULL
+	},
+	{	NET_TUX_CLIENTPORT,
+		"log_incomplete",
+		&tux_log_incomplete,
+		sizeof(int),
+		0644,
+		NULL,
+		proc_dointvec,
+		&sysctl_intvec,
+		NULL,
+		NULL,
+		NULL
+	},
+	{	NET_TUX_LOGGING,
+		"TDprintk",
+		&tux_TDprintk,
+		sizeof(int),
+		0644,
+		NULL,
+		proc_dointvec,
+		&sysctl_intvec,
+		NULL,
+		NULL,
+		NULL
+	},
+	{	NET_TUX_LOGGING,
+		"Dprintk",
+		&tux_Dprintk,
+		sizeof(int),
+		0644,
+		NULL,
+		proc_dointvec,
+		&sysctl_intvec,
+		NULL,
+		NULL,
+		NULL
+	},
+#ifdef TUX_DPRINTK
+#endif
+	{	NET_TUX_LOGGING,
+		"logging",
+		&tux_logging,
+		sizeof(int),
+		0644,
+		NULL,
+		proc_dointvec,
+		&sysctl_intvec,
+		NULL,
+		NULL,
+		NULL
+	},
+	{	NET_TUX_LOGENTRY_ALIGN_ORDER,
+		"logentry_align_order",
+		&tux_logentry_align_order,
+		sizeof(int),
+		0644,
+		NULL,
+		proc_dointvec,
+		&sysctl_intvec,
+		NULL,
+		NULL,
+		NULL
+	},
+	{	NET_TUX_ACK_PINGPONG,
+		"ack_pingpong",
+		&tux_ack_pingpong,
+		sizeof(int),
+		0644,
+		NULL,
+		proc_dointvec,
+		&sysctl_intvec,
+		NULL,
+		NULL,
+		NULL
+	},
+	{	NET_TUX_PUSH_ALL,
+		"push_all",
+		&tux_push_all,
+		sizeof(int),
+		0644,
+		NULL,
+		proc_dointvec,
+		&sysctl_intvec,
+		NULL,
+		NULL,
+		NULL
+	},
+	{	NET_TUX_ZEROCOPY_PARSE,
+		"zerocopy_parse",
+		&tux_zerocopy_parse,
+		sizeof(int),
+		0644,
+		NULL,
+		proc_dointvec,
+		&sysctl_intvec,
+		NULL,
+		NULL,
+		NULL
+	},
+	{	NET_TUX_VIRTUAL_SERVER,
+		"virtual_server",
+		&tux_virtual_server,
+		sizeof(int),
+		0644,
+		NULL,
+		proc_dointvec,
+		&sysctl_intvec,
+		NULL,
+		NULL,
+		NULL
+	},
+	{	NET_TUX_VIRTUAL_SERVER,
+		"mass_hosting_hash",
+		&mass_hosting_hash,
+		sizeof(int),
+		0644,
+		NULL,
+		proc_dointvec,
+		&sysctl_intvec,
+		NULL,
+		NULL,
+		NULL
+	},
+	{	NET_TUX_VIRTUAL_SERVER,
+		"strip_host_tail",
+		&strip_host_tail,
+		sizeof(int),
+		0644,
+		NULL,
+		proc_dointvec,
+		&sysctl_intvec,
+		NULL,
+		NULL,
+		NULL
+	},
+	{	NET_TUX_VIRTUAL_SERVER,
+		"ftp_virtual_server",
+		&tux_ftp_virtual_server,
+		sizeof(int),
+		0644,
+		NULL,
+		proc_dointvec,
+		&sysctl_intvec,
+		NULL,
+		NULL,
+		NULL
+	},
+	{	NET_TUX_MAX_OBJECT_SIZE,
+		"max_object_size",
+		&tux_max_object_size,
+		sizeof(int),
+		0644,
+		NULL,
+		proc_dointvec,
+		&sysctl_intvec,
+		NULL,
+		NULL,
+		NULL
+	},
+	{	NET_TUX_COMPRESSION,
+		"compression",
+		&tux_compression,
+		sizeof(int),
+		0644,
+		NULL,
+		proc_dointvec,
+		&sysctl_intvec,
+		NULL,
+		NULL,
+		NULL
+	},
+	{	NET_TUX_NOID,
+		"noid",
+		&tux_noid,
+		sizeof(int),
+		0644,
+		NULL,
+		proc_dointvec,
+		&sysctl_intvec,
+		NULL,
+		NULL,
+		NULL
+	},
+	{	NET_TUX_CGI_INHERIT_CPU,
+		"cgi_inherit_cpu",
+		&tux_cgi_inherit_cpu,
+		sizeof(int),
+		0644,
+		NULL,
+		proc_dointvec,
+		&sysctl_intvec,
+		NULL,
+		NULL,
+		NULL
+	},
+	{	NET_TUX_ZEROCOPY_HEADER,
+		"zerocopy_header",
+		&tux_zerocopy_header,
+		sizeof(int),
+		0644,
+		NULL,
+		proc_dointvec,
+		&sysctl_intvec,
+		NULL,
+		NULL,
+		NULL
+	},
+	{	NET_TUX_ZEROCOPY_SENDFILE,
+		"zerocopy_sendfile",
+		&tux_zerocopy_sendfile,
+		sizeof(int),
+		0644,
+		NULL,
+		proc_dointvec,
+		&sysctl_intvec,
+		NULL,
+		NULL,
+		NULL
+	},
+	{	NET_TUX_MAX_FREE_REQUESTS,
+		"max_free_requests",
+		&tux_max_free_requests,
+		sizeof(int),
+		0644,
+		NULL,
+		proc_dointvec,
+		&sysctl_intvec,
+		NULL,
+		NULL,
+		NULL
+	},
+	{	NET_TUX_ALL_USERSPACE,
+		"all_userspace",
+		&tux_all_userspace,
+		sizeof(int),
+		0644,
+		NULL,
+		proc_dointvec,
+		&sysctl_intvec,
+		NULL,
+		NULL,
+		NULL
+	},
+	{	NET_TUX_REDIRECT_LOGGING,
+		"redirect_logging",
+		&tux_redirect_logging,
+		sizeof(int),
+		0644,
+		NULL,
+		proc_dointvec,
+		&sysctl_intvec,
+		NULL,
+		NULL,
+		NULL
+	},
+	{	NET_TUX_IGNORE_QUERY,
+		"ignore_query",
+		&tux_ignore_query,
+		sizeof(int),
+		0644,
+		NULL,
+		proc_dointvec,
+		&sysctl_intvec,
+		NULL,
+		NULL,
+		NULL
+	},
+	{	NET_TUX_REFERER_LOGGING,
+		"referer_logging",
+		&tux_referer_logging,
+		sizeof(int),
+		0644,
+		NULL,
+		proc_dointvec,
+		&sysctl_intvec,
+		NULL,
+		NULL,
+		NULL
+	},
+	{	NET_TUX_REFERER_LOGGING,
+		"cpu_offset",
+		&tux_cpu_offset,
+		sizeof(int),
+		0644,
+		NULL,
+		proc_dointvec,
+		&sysctl_intvec,
+		NULL,
+		NULL,
+		NULL
+	},
+	{	NET_TUX_REFERER_LOGGING,
+		"ftp_login_message",
+		&tux_ftp_login_message,
+		sizeof(int),
+		0644,
+		NULL,
+		proc_dointvec,
+		&sysctl_intvec,
+		NULL,
+		NULL,
+		NULL
+	},
+	{	NET_TUX_MAX_HEADER_LEN,
+		"max_header_len",
+		&tux_max_header_len,
+		sizeof(int),
+		0644,
+		NULL,
+		proc_dointvec,
+		&sysctl_intvec,
+		NULL,
+		NULL,
+		NULL
+	},
+	{0, NULL, NULL, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL} };
+
+
+static ctl_table tux_dir_table[] = {
+	{NET_TUX, "tux", NULL, 0, 0555, tux_table, NULL, NULL, NULL, NULL, NULL},
+	{0, NULL, NULL, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL}
+};
+
+static ctl_table tux_root_table[] = {
+	{CTL_NET, "net", NULL, 0, 0555, tux_dir_table, NULL, NULL, NULL, NULL, NULL},
+	{0, NULL, NULL, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL}
+};
+
+
+static struct proc_dir_entry * root_tux_dir;
+static struct proc_dir_entry * log_cpu_mask_entry;
+static struct proc_dir_entry * cgi_cpu_mask_entry;
+static struct proc_dir_entry * stat_entry;
+static struct proc_dir_entry * tux_dir [CONFIG_TUX_NUMTHREADS];
+static struct proc_dir_entry * listen_dir [CONFIG_TUX_NUMTHREADS];
+
+tux_socket_t tux_listen [CONFIG_TUX_NUMTHREADS][CONFIG_TUX_NUMSOCKETS] =
+ { [0 ... CONFIG_TUX_NUMTHREADS-1] = { {&tux_proto_http, 0, 80, NULL}, } };
+
+static int cpu_mask_read_proc (char *page, char **start, off_t off,
+					int count, int *eof, void *data)
+{
+	int len = cpumask_scnprintf(page, count, *(cpumask_t *)data);
+	if (count - len < 2)
+		return -EINVAL;
+	len += sprintf(page + len, "\n");
+	return len;
+}
+
+static int cpu_mask_write_proc (struct file *file,
+					const char __user *buffer,
+					unsigned long count, void *data)
+{
+	cpumask_t *mask = (cpumask_t *)data;
+	unsigned long full_count = count, err;
+	cpumask_t new_value;
+
+	err = cpumask_parse(buffer, count, new_value);
+	if (err)
+		return err;
+
+	*mask = new_value;
+	return full_count;
+}
+
+#define LINE_SIZE 1024
+#define LINE_MASK (LINE_SIZE-1)
+
+static int print_request_stats (threadinfo_t *ti, char *page, unsigned int skip_count, unsigned int max_count)
+{
+	struct list_head *head, *curr;
+	tux_req_t *req;
+	unsigned int count = 0, size, line_off, len;
+	char stat_line [LINE_SIZE];
+
+	if (!max_count)
+		BUG();
+
+	head = &ti->all_requests;
+	curr = head->next;
+
+	while (curr != head) {
+		req = list_entry(curr, tux_req_t, all);
+		curr = curr->next;
+		count++;
+		if (count <= skip_count)
+			continue;
+		line_off = 0;
+#define SP(x...) \
+	line_off += sprintf(stat_line + line_off, x)
+
+		if (req->proto == &tux_proto_http)
+			SP("0 ");
+		else
+			SP("1 ");
+
+		SP("%p ", req);
+		SP("%d ", req->atom_idx);
+		if (req->atom_idx >= 1)
+			SP("%p ", req->atoms[0]);
+		else
+			SP("........ ");
+		if (req->atom_idx >= 2)
+			SP("%p ", req->atoms[1]);
+		else
+			SP("........ ");
+		if (!list_empty(&req->work))	SP("W");	else SP(".");
+		if (!list_empty(&req->free))	SP("F");	else SP(".");
+		if (!list_empty(&req->lru))	SP("L");	else SP(".");
+		if (req->keep_alive)		SP("K");	else SP(".");
+		if (req->idle_input)		SP("I");	else SP(".");
+		if (timer_pending(&req->keepalive_timer))
+						SP("T(%lu/%lu)",jiffies,req->keepalive_timer.expires);	else SP(".");
+		if (req->wait_output_space)	SP("O");	else SP(".");
+		if (timer_pending(&req->output_timer))
+						SP("T");	else SP(".");
+		SP(" %d ", req->error);
+		SP(" %d ", req->status);
+
+#define SP_HOST(ip,port) \
+		SP("%d.%d.%d.%d:%d ",NIPQUAD(ip),port)
+
+		if (req->sock) {
+			if (req->sock->sk)
+				SP("%d:", req->sock->sk->sk_state);
+			else
+				SP("-2:");
+		} else
+			SP("-1:");
+		SP_HOST(req->client_addr, req->client_port);
+
+		SP("%Ld ", req->total_file_len);
+		SP("%Ld ", req->in_file ? req->in_file->f_pos : -1);
+		if (req->proto == &tux_proto_http) {
+			SP("%d ", req->method);
+			SP("%d ", req->version);
+		}
+		if (req->proto == &tux_proto_ftp) {
+			SP("%d ", req->ftp_command);
+			if (req->data_sock) {
+				if (req->data_sock->sk)
+					SP("%d:",req->data_sock->sk->sk_state);
+				else
+					SP("-2:");
+				if (req->data_sock->sk)
+					SP_HOST(inet_sk(req->data_sock->sk)->daddr,
+						inet_sk(req->data_sock->sk)->dport);
+				else
+					SP("-1:-1 ");
+			} else
+				SP("-1 ");
+		}
+		SP("%p/%p %p/%p ", req->sock, req->sock ? req->sock->sk : (void *)-1, req->data_sock, req->data_sock ? req->data_sock->sk : (void *)-1);
+
+		SP("%d\n", req->parsed_len);
+		len = req->headers_len;
+		if (len > 500)
+			len = 500;
+		SP("\n%d\n", len);
+		memcpy(stat_line + line_off, req->headers, len);
+		line_off += len;
+		len = req->objectname_len;
+		if (len > 100)
+			len = 100;
+		SP("\n%d\n", len);
+		memcpy(stat_line + line_off, req->objectname, len);
+		line_off += len;
+		SP("\n\n<END>");
+		if (line_off >= LINE_SIZE)
+			BUG();
+		Dprintk("printing req %p, count %d, page %p: {%s}.\n", req, count, page, stat_line);
+		size = sprintf(page, "%-*s\n", LINE_SIZE-1, stat_line);
+		if (size != LINE_SIZE)
+			BUG();
+		page += LINE_SIZE;
+		if (count-skip_count >= max_count)
+			break;
+	}
+
+	Dprintk("count: %d.\n", count-skip_count);
+	return count - skip_count;
+}
+
+static int stat_read_proc (char *page, char **start, off_t off,
+			int max_size, int *eof, void *data)
+{
+	unsigned int i, nr_total = 0, nr, nr_off, nr_skip, size = 0, nr_wanted;
+
+	Dprintk("START, page: %p, max_size: %d, off: %ld.\n", page, max_size, off);
+	*eof = 1;
+	if (max_size & LINE_MASK)
+		return 0;
+	if (off & LINE_MASK)
+		return 0;
+	if (!max_size)
+		return 0;
+
+	nr_off = off/LINE_SIZE;
+
+	for (i = 0; i < nr_tux_threads; i++) {
+		threadinfo_t *ti = threadinfo + i;
+		spin_lock_irq(&ti->work_lock);
+		nr = ti->nr_requests;
+		Dprintk("ti: %p, nr: %d, nr_total: %d, nr_off: %d.\n", ti, nr, nr_total, nr_off);
+		nr_total += nr;
+		if (nr_off >= nr_total) {
+			spin_unlock_irq(&ti->work_lock);
+			continue;
+		}
+		nr_skip = nr_off - (nr_total - nr);
+		nr_wanted = (max_size-size) / LINE_SIZE;
+		Dprintk("nr_skip: %d, nr_wanted: %d.\n", nr_skip, nr_wanted);
+		nr = print_request_stats(ti, page + size, nr_skip, nr_wanted);
+		spin_unlock_irq(&ti->work_lock);
+		nr_off += nr;
+		size += nr * LINE_SIZE;
+		Dprintk("ret: %d requests, size: %d.\n", nr, size);
+		if (size > max_size)
+			BUG();
+		if (size == max_size)
+			break;
+	}
+	Dprintk("DONE: size: %d.\n", size);
+
+	*start = page;
+
+	if (size)
+		*eof = 0;
+	return size;
+}
+
+static int stat_write_proc (struct file *file, const char *buffer,
+					unsigned long count, void *data)
+{
+	return -EINVAL;
+}
+
+#define MAX_STRING "http://255.255.255.255:65535"
+#define MAX_STRINGLEN (sizeof(MAX_STRING))
+
+#define INACTIVE_1 "[inactive]\n"
+#define INACTIVE_2 "0\n"
+
+static int listen_read_proc (char *page, char **start, off_t off,
+			int count, int *eof, void *data)
+{
+	tux_socket_t *listen = data;
+
+	if (count < MAX_STRINGLEN)
+		return -EINVAL;
+
+	if (!listen->proto)
+		return sprintf(page, INACTIVE_1);
+
+	return sprintf (page, "%s://%u.%u.%u.%u:%hu\n", listen->proto->name,
+			HIPQUAD(listen->ip), listen->port);
+}
+
+static int listen_write_proc (struct file *file, const char *buffer,
+					unsigned long count, void *data)
+{
+	char string [MAX_STRINGLEN];
+	unsigned int d1, d2, d3, d4;
+	unsigned short port;
+	tux_socket_t *listen = data;
+
+	if (!count)
+		return -EINVAL;
+	if (count > MAX_STRINGLEN)
+		count = MAX_STRINGLEN;
+	if (copy_from_user(string, buffer, count))
+		return -EFAULT;
+	string[count] = 0;
+
+	if (!strcmp(string, INACTIVE_1) || !strcmp(string, INACTIVE_2)) {
+		listen->proto = NULL;
+		listen->ip = 0;
+		listen->port = 0;
+		return count;
+	}
+
+#define MK_IP(a,b,c,d) ((a << 24) | (b << 16) | (c << 8) | d)
+
+        if (sscanf(string, "http://%u.%u.%u.%u:%hu\n",
+					&d1, &d2, &d3, &d4, &port) == 5) {
+		listen->ip = MK_IP(d1,d2,d3,d4);
+		listen->port = port;
+		listen->proto = &tux_proto_http;
+		return count;
+	}
+
+        if (sscanf(string, "ftp://%u.%u.%u.%u:%hu\n",
+					&d1, &d2, &d3, &d4, &port) == 5) {
+		listen->ip = MK_IP(d1,d2,d3,d4);
+		listen->port = port;
+		listen->proto = &tux_proto_ftp;
+		return count;
+	}
+	printk(KERN_ERR "tux: invalid listen-socket parameters: %s\n", string);
+	return -EINVAL;
+}
+
+#define MAX_NAMELEN 10
+
+static void register_tux_proc (unsigned int nr)
+{
+	struct proc_dir_entry *entry;
+	char name [MAX_NAMELEN];
+	int i;
+
+	if (!root_tux_dir)
+		TUX_BUG();
+
+	sprintf(name, "%d", nr);
+
+	/* create /proc/net/tux/1234/ */
+	tux_dir[nr] = proc_mkdir(name, root_tux_dir);
+
+	/* create /proc/net/tux/1234/listen/ */
+	listen_dir[nr] = proc_mkdir("listen", tux_dir[nr]);
+
+	/* create /proc/net/tux/1234/listen/ */
+	for (i = 0; i < CONFIG_TUX_NUMSOCKETS; i++) {
+		sprintf(name, "%d", i);
+		entry = create_proc_entry(name, 0700, listen_dir[nr]);
+
+		entry->nlink = 1;
+		entry->data = (void *)(tux_listen[nr] + i);
+		entry->read_proc = listen_read_proc;
+		entry->write_proc = listen_write_proc;
+		tux_listen[nr][i].entry = entry;
+	}
+}
+
+static void unregister_tux_proc (unsigned int nr)
+{
+	int i;
+
+	for (i = 0; i < CONFIG_TUX_NUMSOCKETS; i++) {
+		remove_proc_entry(tux_listen[nr][i].entry->name,listen_dir[nr]);
+		tux_listen[nr][i].entry = NULL;
+	}
+
+	remove_proc_entry(listen_dir[nr]->name, tux_dir[nr]);
+
+	remove_proc_entry(tux_dir[nr]->name, root_tux_dir);
+}
+
+static void cleanup_tux_proc (void)
+{
+	int i;
+
+	Dprintk("cleaning up /proc/net/tux/\n");
+
+	for (i = 0; i < CONFIG_TUX_NUMTHREADS; i++)
+		unregister_tux_proc(i);
+	remove_proc_entry(stat_entry->name, root_tux_dir);
+	remove_proc_entry(log_cpu_mask_entry->name, root_tux_dir);
+	remove_proc_entry(cgi_cpu_mask_entry->name, root_tux_dir);
+	remove_proc_entry(root_tux_dir->name, proc_net);
+}
+
+static void init_tux_proc (void)
+{
+	struct proc_dir_entry *entry;
+	int i;
+
+	if (root_tux_dir)
+		return;
+
+	/* create /proc/net/tux */
+	root_tux_dir = proc_mkdir("tux", proc_net);
+
+	entry = create_proc_entry("log_cpu_mask", 0700, root_tux_dir);
+
+	entry->nlink = 1;
+	entry->data = (void *)&tux_log_cpu_mask;
+	entry->read_proc = cpu_mask_read_proc;
+	entry->write_proc = cpu_mask_write_proc;
+
+	log_cpu_mask_entry = entry;
+
+	entry = create_proc_entry("cgi_cpu_mask", 0700, root_tux_dir);
+
+	entry->nlink = 1;
+	entry->data = (void *)&tux_cgi_cpu_mask;
+	entry->read_proc = cpu_mask_read_proc;
+	entry->write_proc = cpu_mask_write_proc;
+
+	cgi_cpu_mask_entry = entry;
+
+	entry = create_proc_entry("stat", 0700, root_tux_dir);
+
+	entry->nlink = 1;
+	entry->data = NULL;
+	entry->read_proc = stat_read_proc;
+	entry->write_proc = stat_write_proc;
+
+	stat_entry = entry;
+
+	/*
+	 * Create entries for all existing threads.
+	 */
+	for (i = 0; i < CONFIG_TUX_NUMTHREADS; i++)
+		register_tux_proc(i);
+}
+
+void start_sysctl(void)
+{
+	init_tux_proc();
+	tux_table_header = register_sysctl_table(tux_root_table,1);
+}
+
+void end_sysctl(void)
+{
+	cleanup_tux_proc();
+	unregister_sysctl_table(tux_table_header);
+}
+
+
Index: latest/net/tux/proto_ftp.c
===================================================================
--- /dev/null
+++ latest/net/tux/proto_ftp.c
@@ -0,0 +1,1555 @@
+/*
+ * TUX - Integrated Application Protocols Layer and Object Cache
+ *
+ * Copyright (C) 2000, 2001, Ingo Molnar <mingo@redhat.com>
+ *
+ * ftp_proto.c: FTP application protocol support
+ */
+
+#define __KERNEL_SYSCALLS__
+#include <net/tux.h>
+
+/****************************************************************
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2, or (at your option)
+ *      any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ ****************************************************************/
+
+#define HELLO		"220 Linux/TUX 3.0 FTP server welcomes you!\r\n"
+#define WRITE_DONE	"226 Transfer complete.\r\n"
+#define BAD_FILENAME	"550 No such file or directory.\r\n"
+#define GOOD_DIR	"250 CWD command successful.\r\n"
+#define LIST_ERR	"503 LIST without PORT! Closing connection.\r\n"
+#define LIST_ERR_MEM	"503 LIST could not allocate memory! Closing connection.\r\n"
+#define WRITE_FILE	"150 Opening BINARY mode data connection.\r\n"
+#define WRITE_LIST	"150 Opening ASCII mode data connection.\r\n"
+#define RETR_ERR	"503 RETR without PORT! Closing connection.\r\n"
+#define PORT_OK		"200 PORT command successful.\r\n"
+#define LOGIN_OK	"230-There are currently %d users logged in, out of %d maximum.\r\n230-Bandwidth served by TUX currently: %d KB/sec\r\n230 TUX Guest login ok.\r\n"
+#define LOGIN_OK_ONE	"230-There is currently 1 user logged in, out of %d maximum.\r\n230-Bandwidth served by TUX currently: %d KB/sec\r\n230 TUX Guest login ok.\r\n"
+#define LOGIN_OK_PASS	"230 TUX Guest login ok.\r\n"
+#define LOGIN_FORBIDDEN	"530 Sorry, Login Denied!\r\n"
+#define TYPE_OK		"200 Type set to I.\r\n"
+#define BYE		"221 Thank You for using TUX!\r\n"
+#define NOT_IMPLEMENTED	"502 Command not implemented.\r\n"
+#define CLOSE_2		"221 Cannot handle request, closing connection!\r\n"
+#define CLOSE		"500 Unknown command.\r\n"
+#define CLOSE_TIMEOUT	"421 Timeout, closing connection!\r\n"
+#define LINUX_SYST	"215 UNIX Type: L8, Linux/TUX/3.0\r\n"
+#define COMMAND_OK	"200 Command OK.\r\n"
+#define REST_OK		"350 Restart offset OK.\r\n"
+#define WRITE_ABORTED	"426 Transfer aborted, data connection closed.\r\n"
+#define SITE		"214 No SITE commands are recognized.\r\n"
+
+#define INTERVAL 10
+
+unsigned long last_measurement;
+unsigned int ftp_bytes_sent;
+unsigned int ftp_bandwidth;
+
+static void __update_bandwidth (tux_req_t *req, unsigned int bytes)
+{
+	/*
+	 * Bandwidth measurement. Not completely accurate,
+	 * but it's good enough and lightweight enough.
+	 */
+	if (jiffies >= last_measurement + INTERVAL*HZ) {
+		ftp_bandwidth = (ftp_bytes_sent + 1023)/INTERVAL/1024;
+		ftp_bytes_sent = 0;
+		last_measurement = jiffies;
+	}
+	if (bytes)
+		atomic_add(bytes, (atomic_t *)&ftp_bytes_sent);
+	Dprintk("update_bandwidth(%p,%d), bytes_sent: %d, bandwidth: %d.\n",
+		req, bytes, ftp_bytes_sent, ftp_bandwidth);
+}
+
+#define update_bandwidth(req,bytes)				\
+	do {							\
+		if (unlikely(tux_ftp_login_message))		\
+			__update_bandwidth(req, bytes);		\
+	} while (0)
+
+static inline void __ftp_send_async_message (tux_req_t *req,
+		 const char *message, int status, unsigned int size)
+{
+	update_bandwidth(req, size);
+	__send_async_message(req, message, status, size, 1);
+}
+
+#define ftp_send_async_message(req,str,status) \
+		__ftp_send_async_message(req,str,status,sizeof(str)-1)
+
+
+static void ftp_flush_req (tux_req_t *req, int cachemiss)
+{
+	tux_push_pending(req->sock->sk);
+	add_req_to_workqueue(req);
+}
+
+static void ftp_execute_command (tux_req_t *req, int cachemiss);
+
+static void ftp_lookup_vhost (tux_req_t *req, int cachemiss)
+{
+	struct dentry *dentry;
+	struct nameidata base = { };
+	struct vfsmount *mnt = NULL;
+	unsigned int flag = cachemiss ? 0 : LOOKUP_ATOMIC;
+	char ip[3+1+3+1+3+1+3 + 2];
+
+	sprintf(ip, "%d.%d.%d.%d", NIPQUAD(inet_sk(req->sock->sk)->rcv_saddr));
+	Dprintk("ftp_lookup_vhost(%p, %d, virtual: %d, host: %s.)\n",
+		req, flag, req->virtual, ip);
+
+	base.flags = LOOKUP_FOLLOW|flag;
+	base.last_type = LAST_ROOT;
+	base.dentry = dget(req->proto->main_docroot.dentry);
+	base.mnt = mntget(req->proto->main_docroot.mnt);
+
+	dentry = __tux_lookup(req, ip, &base, &mnt);
+
+	Dprintk("looked up dentry %p.\n", dentry);
+	if (dentry && !IS_ERR(dentry) && !dentry->d_inode)
+		TUX_BUG();
+
+	if (!dentry || IS_ERR(dentry)) {
+		if (PTR_ERR(dentry) == -EWOULDBLOCKIO) {
+			add_tux_atom(req, ftp_lookup_vhost);
+			queue_cachemiss(req);
+			return;
+		}
+		goto abort;
+	}
+
+	req->docroot_dentry = dentry;
+	req->docroot_mnt = mnt;
+
+	add_tux_atom(req, ftp_execute_command);
+	add_req_to_workqueue(req);
+	return;
+abort:
+	if (dentry) {
+		if (!IS_ERR(dentry))
+			dput(dentry);
+		dentry = NULL;
+	}
+	if (mnt) {
+		if (!IS_ERR(mnt))
+			mntput(mnt);
+		mnt = NULL;
+	}
+	req_err(req);
+	add_req_to_workqueue(req);
+}
+
+static void ftp_got_request (tux_req_t *req)
+{
+	add_tux_atom(req, parse_request);
+	add_tux_atom(req, ftp_flush_req);
+	ftp_send_async_message(req, HELLO, 220);
+}
+
+#define GOTO_ERR { TDprintk("FTP protocol error at: %s:%d\n", \
+			__FILE__, __LINE__); goto error; }
+
+static void zap_data_socket (tux_req_t *req)
+{
+	if (!req->data_sock)
+		return;
+	Dprintk("zapping req %p's data socket %p.\n", req, req->data_sock);
+
+	unlink_tux_data_socket(req);
+	sock_release(req->data_sock);
+	req->data_sock = NULL;
+}
+
+static int parse_ftp_message (tux_req_t *req, const int total_len)
+{
+	int comm, comm1 = 0, comm2 = 0, comm3 = 0, comm4 = 0;
+	int newline_pos, i;
+	const char *mess, *curr;
+
+	curr = mess = req->headers;
+
+	Dprintk("FTP parser got %d bytes: --->{%s}<---\n", total_len, curr);
+
+	newline_pos = -1;
+	for (i = 0; i < total_len; i++, curr++) {
+		if (!*curr)
+			GOTO_ERR;
+		if (!(*curr == '\r') || !(*(curr+1) == '\n'))
+			continue;
+		newline_pos = i;
+		break;
+	}
+	Dprintk("Newline pos: %d\n", newline_pos);
+	if (newline_pos == -1) {
+		Dprintk("incomplete mess on req %p!\n", req);
+		return 0;
+	}
+	if (newline_pos < 3)
+		GOTO_ERR;
+
+#define toup(c) ((((c) >= 'a') && ((c) <= 'z')) ? ((c) + 'A' - 'a') : (c))
+
+#define STRING_VAL(c1,c2,c3,c4) \
+	(toup(c1) + (toup(c2) << 8) + (toup(c3) << 16) + (toup(c4) << 24))
+
+#define STRING_VAL_STR(str) \
+		STRING_VAL(str[0], str[1], str[2], str[3])
+
+	Dprintk("string val (%c%c%c%c): %08x\n",
+		mess[0], mess[1], mess[2], mess[3],
+		STRING_VAL_STR(mess));
+
+#define PARSE_FTP_COMM(c1,c2,c3,c4,name,num)			\
+	if (STRING_VAL_STR(mess) == STRING_VAL(c1,c2,c3,c4))	\
+	{							\
+		Dprintk("parsed "#name".\n");			\
+		comm##num = FTP_COMM_##name;			\
+	}
+
+	PARSE_FTP_COMM('A','C','C','T', ACCT,2);
+	PARSE_FTP_COMM('C','D','U','P', CDUP,3);
+	PARSE_FTP_COMM('S','M','N','T', SMNT,4);
+	PARSE_FTP_COMM('Q','U','I','T', QUIT,1);
+	PARSE_FTP_COMM('R','E','I','N', REIN,2);
+	PARSE_FTP_COMM('P','A','S','V', PASV,3);
+	PARSE_FTP_COMM('S','T','R','U', STRU,4);
+	PARSE_FTP_COMM('S','T','O','R', STOR,2);
+	PARSE_FTP_COMM('S','T','O','U', STOU,3);
+	PARSE_FTP_COMM('A','P','P','E', APPE,4);
+	PARSE_FTP_COMM('A','L','L','O', ALLO,1);
+	PARSE_FTP_COMM('R','N','F','R', RNFR,2);
+	PARSE_FTP_COMM('R','N','T','O', RNTO,3);
+	PARSE_FTP_COMM('A','B','O','R', ABOR,4);
+	PARSE_FTP_COMM('D','E','L','E', DELE,1);
+	PARSE_FTP_COMM('R','M','D',' ', RMD, 2);
+	PARSE_FTP_COMM('M','K','D',' ', MKD, 3);
+	PARSE_FTP_COMM('P','W','D',' ', PWD, 4);
+	PARSE_FTP_COMM('S','Y','S','T', SYST,2);
+	PARSE_FTP_COMM('N','O','O','P', NOOP,3);
+	PARSE_FTP_COMM('F','E','A','T', FEAT,4);
+
+	comm = comm1 | comm2 | comm3 | comm4;
+
+	if (comm) {
+		if (newline_pos != 4)
+			GOTO_ERR;
+		req->ftp_command = comm;
+		goto out;
+	}
+
+	switch (STRING_VAL(mess[0], mess[1], mess[2], mess[3])) {
+
+#define PARSE_FTP_COMM_3CHAR(c1,c2,c3,name)				\
+		case STRING_VAL(c1,c2,c3,'\r'):				\
+		{							\
+			Dprintk("parsed "#name".\n");			\
+			req->ftp_command = FTP_COMM_##name;		\
+			if (newline_pos != 3)				\
+				GOTO_ERR;				\
+		}
+
+#define PARSE_FTP_3CHAR_COMM_IGNORE(c1,c2,c3,name)			\
+		case STRING_VAL(c1,c2,c3,' '):				\
+		{							\
+			Dprintk("parsed "#name".\n");			\
+			req->ftp_command = FTP_COMM_##name;		\
+		}
+
+#define PARSE_FTP_COMM_IGNORE(c1,c2,c3,c4,name)				\
+		case STRING_VAL(c1,c2,c3,c4):				\
+		{							\
+			Dprintk("parsed "#name".\n");			\
+			req->ftp_command = FTP_COMM_##name;		\
+		}
+
+#define PARSE_FTP_3CHAR_COMM_1_FIELD(c1,c2,c3,name,field,field_len,max)	\
+		case STRING_VAL(c1,c2,c3,' '):				\
+		{							\
+			Dprintk("parsed "#name".\n");			\
+			req->ftp_command = FTP_COMM_##name;		\
+			if (newline_pos == 4)				\
+				GOTO_ERR;				\
+			if (newline_pos >= 5) {				\
+				curr = mess + 3;			\
+				if (*curr++ != ' ')			\
+					GOTO_ERR;			\
+				*(field_len) = newline_pos-4;		\
+				if (*(field_len) >= max)		\
+					GOTO_ERR;			\
+				memcpy(field, curr, *(field_len));	\
+				(field)[*(field_len)] = 0;		\
+			}						\
+		}
+
+#define PARSE_FTP_COMM_1_FIELD(c1,c2,c3,c4,name,field,field_len,max)	\
+		case STRING_VAL(c1,c2,c3,c4):				\
+		{							\
+			Dprintk("parsed "#name".\n");			\
+			req->ftp_command = FTP_COMM_##name;		\
+			if (newline_pos < 4)				\
+				GOTO_ERR;				\
+			if (newline_pos == 4)				\
+				*(field_len) = 0;			\
+			else {						\
+				curr = mess + 4;			\
+				if (*curr++ != ' ')			\
+					GOTO_ERR;			\
+				*(field_len) = newline_pos-5;		\
+				if (*(field_len) >= max)		\
+					GOTO_ERR;			\
+				memcpy(field, curr, *(field_len));	\
+				(field)[*(field_len)] = 0;		\
+			}						\
+		}
+
+		PARSE_FTP_COMM_1_FIELD('U','S','E','R', USER,
+			req->username, &req->username_len,
+			MAX_USERNAME_LEN-1);
+		if (!req->username_len)
+			GOTO_ERR;
+		break;
+
+		{
+			#define MAX_PASS_LEN 100
+			char pass[MAX_PASS_LEN];
+			unsigned int pass_len;
+			PARSE_FTP_COMM_1_FIELD('P','A','S','S', PASS,
+				pass, &pass_len,
+				MAX_PASS_LEN-1);
+			if (!pass_len)
+				GOTO_ERR;
+			break;
+		}
+
+		PARSE_FTP_3CHAR_COMM_1_FIELD('C','W','D', CWD,
+			req->objectname, &req->objectname_len,
+			MAX_OBJECTNAME_LEN-1);
+		if (!req->objectname_len)
+			GOTO_ERR;
+		req->uri_str = req->objectname;
+		req->uri_len = req->objectname_len;
+		break;
+
+		PARSE_FTP_COMM_3CHAR('P','W','D', PWD); break;
+
+		{
+			char type[3];
+			unsigned int type_len;
+
+			PARSE_FTP_COMM_1_FIELD('T','Y','P','E', TYPE,
+				type, &type_len, 2);
+			if (!type_len)
+				GOTO_ERR;
+			if ((type[0] != 'I') && (type[0] != 'A'))
+				GOTO_ERR;
+		}
+		break;
+
+		PARSE_FTP_COMM_1_FIELD('R','E','T','R', RETR,
+			req->objectname, &req->objectname_len,
+			MAX_OBJECTNAME_LEN-1);
+		if (!req->objectname_len) {
+			zap_data_socket(req);
+			req->ftp_command = FTP_COMM_NONE;
+		}
+		req->uri_str = req->objectname;
+		req->uri_len = req->objectname_len;
+		break;
+
+		PARSE_FTP_COMM_1_FIELD('S','I','Z','E', SIZE,
+			req->objectname, &req->objectname_len,
+			MAX_OBJECTNAME_LEN-1);
+		if (!req->objectname_len)
+			req->ftp_command = FTP_COMM_NONE;
+		req->uri_str = req->objectname;
+		req->uri_len = req->objectname_len;
+		break;
+
+		PARSE_FTP_COMM_1_FIELD('M','D','T','M', MDTM,
+			req->objectname, &req->objectname_len,
+			MAX_OBJECTNAME_LEN-1);
+		if (!req->objectname_len)
+			req->ftp_command = FTP_COMM_NONE;
+		req->uri_str = req->objectname;
+		req->uri_len = req->objectname_len;
+		break;
+
+		PARSE_FTP_COMM_IGNORE('M','O','D','E', MODE);
+		break;
+
+		PARSE_FTP_COMM_IGNORE('S','T','A','T', STAT);
+		break;
+
+		PARSE_FTP_COMM_IGNORE('S','I','T','E', SITE);
+		break;
+
+		PARSE_FTP_COMM_1_FIELD('L','I','S','T', LIST,
+			req->objectname, &req->objectname_len,
+			MAX_OBJECTNAME_LEN-1);
+		if (req->objectname[0] == '-') {
+			req->objectname_len = 0;
+			req->objectname[0] = 0;
+		}
+		if (req->objectname_len) {
+			req->uri_str = req->objectname;
+			req->uri_len = req->objectname_len;
+		}
+		break;
+
+		PARSE_FTP_COMM_1_FIELD('N','L','S','T', NLST,
+			req->objectname, &req->objectname_len,
+			MAX_OBJECTNAME_LEN-1);
+		if (req->objectname[0] == '-') {
+			req->objectname_len = 0;
+			req->objectname[0] = 0;
+		}
+		if (req->objectname_len) {
+			req->uri_str = req->objectname;
+			req->uri_len = req->objectname_len;
+		}
+		break;
+
+		PARSE_FTP_COMM_IGNORE('H','E','L','P', HELP);
+		break;
+
+		PARSE_FTP_COMM_IGNORE('C','L','N','T', CLNT);
+		break;
+
+#define IS_NUM(n) (((n) >= '0') && ((n) <= '9'))
+
+#define GET_DIGIT(curr,n)				\
+	n += (*curr) - '0';				\
+	curr++;						\
+	if (IS_NUM(*curr)) {				\
+		n *= 10;
+
+#define PARSE_PORTNUM(curr,n)				\
+do {							\
+	Dprintk("PORT NUM parser:--->{%s}<---\n", curr);\
+	if (!IS_NUM(*curr))				\
+		GOTO_ERR;				\
+	n = 0;						\
+	GET_DIGIT(curr,n);				\
+	GET_DIGIT(curr,n);				\
+	GET_DIGIT(curr,n);				\
+	}}}						\
+	if (n > 255)					\
+		GOTO_ERR;				\
+	Dprintk("PORT NUM parser:--->{%s}<---\n", curr);\
+	Dprintk("PORT NUM parser parsed %d.\n", n);	\
+} while (0)
+
+#define PARSE_NUM(curr,n)				\
+do {							\
+	Dprintk("NUM parser:--->{%s}<---\n", curr);	\
+	if (!IS_NUM(*curr))				\
+		GOTO_ERR;				\
+	n = 0;						\
+	GET_DIGIT(curr,n);				\
+	GET_DIGIT(curr,n);				\
+	GET_DIGIT(curr,n);				\
+	GET_DIGIT(curr,n);				\
+	GET_DIGIT(curr,n);				\
+	GET_DIGIT(curr,n);				\
+	GET_DIGIT(curr,n);				\
+	GET_DIGIT(curr,n);				\
+	GET_DIGIT(curr,n);				\
+	GET_DIGIT(curr,n);				\
+	}}}}}}}}}}					\
+	Dprintk("NUM parser:--->{%s}<---\n", curr);	\
+	Dprintk("NUM parser parsed %d.\n", n);		\
+} while (0)
+
+		case STRING_VAL('P','O','R','T'):
+		{
+			unsigned int h1, h2, h3, h4, p1, p2;
+			if (req->data_sock)
+				zap_data_socket(req);
+			/*
+			 * Minimum size: "PORT 0,0,0,0,0,0", 16 bytes.
+			 */
+			if (newline_pos < 16)
+				GOTO_ERR;
+			Dprintk("parsed PORT.\n");
+			if (req->data_sock)
+				GOTO_ERR;
+			curr = mess + 4;
+			if (*curr++ != ' ')
+				GOTO_ERR;
+			PARSE_PORTNUM(curr,h1);
+			if (*curr++ != ',')
+				GOTO_ERR;
+			PARSE_PORTNUM(curr,h2);
+			if (*curr++ != ',')
+				GOTO_ERR;
+			PARSE_PORTNUM(curr,h3);
+			if (*curr++ != ',')
+				GOTO_ERR;
+			PARSE_PORTNUM(curr,h4);
+			if (*curr++ != ',')
+				GOTO_ERR;
+			PARSE_PORTNUM(curr,p1);
+			if (*curr++ != ',')
+				GOTO_ERR;
+			PARSE_PORTNUM(curr,p2);
+			if (curr-mess != newline_pos)
+				GOTO_ERR;
+			req->ftp_command = FTP_COMM_PORT;
+			req->ftp_user_addr = (h1<<24) + (h2<<16) + (h3<<8) + h4;
+			req->ftp_user_port = (p1<<8) + p2;
+			Dprintk("FTP PORT got: %d.%d.%d.%d:%d.\n",
+				h1, h2, h3, h4, req->ftp_user_port);
+			Dprintk("FTP user-addr: %08x (htonl: %08x), socket: %08x.\n",
+				req->ftp_user_addr, htonl(req->ftp_user_addr),
+				inet_sk(req->sock->sk)->daddr);
+			/*
+			 * Do not allow redirection of connections, and do
+			 * not allow reserved ports to be accessed.
+			 */
+			if (inet_sk(req->sock->sk)->daddr != htonl(req->ftp_user_addr))
+				GOTO_ERR;
+			if (req->ftp_user_port < 1024)
+				GOTO_ERR;
+			break;
+		}
+		case STRING_VAL('R','E','S','T'):
+		{
+			unsigned int offset;
+
+			/*
+			 * Minimum size: "REST 0", 6 bytes.
+			 */
+			if (newline_pos < 6)
+				GOTO_ERR;
+			Dprintk("parsed REST.\n");
+			curr = mess + 4;
+			if (*curr++ != ' ')
+				GOTO_ERR;
+			PARSE_NUM(curr,offset);
+			if (curr-mess != newline_pos)
+				GOTO_ERR;
+			req->ftp_command = FTP_COMM_REST;
+			req->ftp_offset_start = offset;
+			Dprintk("FTP REST got: %d bytes offset.\n", offset);
+
+			break;
+		}
+		default:
+			req->ftp_command = FTP_COMM_NONE;
+			break;
+	}
+
+out:
+	req->parsed_len = newline_pos + 2;
+
+	req->virtual = tux_ftp_virtual_server;
+	if (req->virtual)
+		add_tux_atom(req, ftp_lookup_vhost);
+	else {
+		req->docroot_dentry = dget(req->proto->main_docroot.dentry);
+		req->docroot_mnt = mntget(req->proto->main_docroot.mnt);
+		add_tux_atom(req, ftp_execute_command);
+	}
+
+	return req->parsed_len;
+error:
+	clear_keepalive(req);
+	TDprintk("rejecting FTP session!\n");
+	TDprintk("mess     :--->{%s}<---\n", mess);
+	TDprintk("mess left:--->{%s}<---\n", curr);
+	req_err(req);
+	return -1;
+}
+
+static void ftp_wait_close (tux_req_t *req, int cachemiss);
+static void ftp_wait_syn (tux_req_t *req, int cachemiss);
+
+static int ftp_check_req_err (tux_req_t *req, int cachemiss)
+{
+	int state = req->sock->sk->sk_state;
+	int err = req->sock->sk->sk_err | req->error;
+	int urg = tcp_sk(req->sock->sk)->urg_data;
+
+	if (req->data_sock) {
+		urg |= tcp_sk(req->data_sock->sk)->urg_data;
+		state |= req->data_sock->sk->sk_state;
+		err |= req->data_sock->sk->sk_err;
+	}
+
+	if ((state <= TCP_SYN_RECV) && !err) {
+		if (!urg)
+			return 0;
+		req->in_file->f_pos = 0;
+		add_tux_atom(req, flush_request);
+		zap_data_socket(req);
+		ftp_send_async_message(req, WRITE_ABORTED, 426);
+		return 1;
+	}
+#ifdef CONFIG_TUX_DEBUG
+	req->bytes_expected = 0;
+	if (tux_TDprintk)
+		dump_stack();
+#endif
+	req->in_file->f_pos = 0;
+	TDprintk("zapping, data sock state: %d (err: %d, urg: %d)\n",
+		state, err, urg);
+	/*
+	 * We are in the middle of a file transfer,
+	 * zap it immediately:
+	 */
+	req->error = TUX_ERROR_CONN_CLOSE;
+	zap_request(req, cachemiss);
+	return 1;
+}
+
+void ftp_send_file (tux_req_t *req, int cachemiss)
+{
+	int ret;
+
+	SET_TIMESTAMP(req->output_timestamp);
+repeat:
+	ret = generic_send_file(req, req->data_sock, cachemiss);
+	update_bandwidth(req, req->in_file->f_pos - req->prev_pos);
+	req->prev_pos = req->in_file->f_pos;
+
+	switch (ret) {
+		case -5:
+			add_tux_atom(req, ftp_send_file);
+			output_timeout(req);
+			break;
+		case -4:
+			add_tux_atom(req, ftp_send_file);
+			if (add_output_space_event(req, req->data_sock)) {
+				del_tux_atom(req);
+				goto repeat;
+			}
+			break;
+		case -3:
+			add_tux_atom(req, ftp_send_file);
+			queue_cachemiss(req);
+			break;
+		case -1:
+			break;
+		default:
+			req->in_file->f_pos = 0;
+
+			if (tux_ftp_wait_close) {
+				req->data_sock->ops->shutdown(req->data_sock, SEND_SHUTDOWN);
+				add_tux_atom(req, ftp_wait_close);
+				add_req_to_workqueue(req);
+				return;
+			}
+			Dprintk("FTP send file req %p finished!\n", req);
+			zap_data_socket(req);
+			add_tux_atom(req, ftp_flush_req);
+			if (req->error)
+				ftp_send_async_message(req, BAD_FILENAME, 200);
+			else
+				ftp_send_async_message(req, WRITE_DONE, 200);
+			break;
+	}
+}
+
+#define sk_syn(sk) \
+	(!(sk)->sk_err && ((1 << (sk)->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV)))
+#define req_syn(req) \
+	(!(req)->error && sk_syn((req)->data_sock->sk))
+
+static void ftp_wait_syn (tux_req_t *req, int cachemiss)
+{
+	Dprintk("ftp_wait_syn in: data socket state %d.\n", req->data_sock->state);
+	if (req_syn(req)) {
+		spin_lock_irq(&req->ti->work_lock);
+		add_keepalive_timer(req);
+		if (test_and_set_bit(0, &req->idle_input))
+			TUX_BUG();
+		spin_unlock_irq(&req->ti->work_lock);
+		if (req_syn(req)) {
+			add_tux_atom(req, ftp_wait_syn);
+			return;
+		}
+		unidle_req(req);
+	}
+	Dprintk("ftp_wait_syn out: data socket state %d.\n", req->data_sock->state);
+	add_req_to_workqueue(req);
+}
+
+static void ftp_wait_close (tux_req_t *req, int cachemiss)
+{
+	struct sock *sk = req->data_sock->sk;
+
+	Dprintk("ftp_wait_close: data socket state %d.\n", sk->sk_state);
+
+	if (!req->error && (sk->sk_state <= TCP_FIN_WAIT1) && !sk->sk_err) {
+		spin_lock_irq(&req->ti->work_lock);
+		add_keepalive_timer(req);
+		if (test_and_set_bit(0, &req->idle_input))
+			TUX_BUG();
+		spin_unlock_irq(&req->ti->work_lock);
+		if (!req->error && (sk->sk_state <= TCP_FIN_WAIT1) && !sk->sk_err) {
+			add_tux_atom(req, ftp_wait_close);
+			return;
+		}
+		unidle_req(req);
+	}
+	zap_data_socket(req);
+	add_tux_atom(req, ftp_flush_req);
+	if (req->error)
+		ftp_send_async_message(req, BAD_FILENAME, 200);
+	else
+		ftp_send_async_message(req, WRITE_DONE, 200);
+}
+
+void ftp_get_size (tux_req_t *req, int cachemiss)
+{
+	char file_size[200];
+	int missed, len;
+
+	if (!req->dentry) {
+		missed = lookup_object(req, cachemiss ? 0 : LOOKUP_ATOMIC);
+		if (!missed && !req->dentry) {
+			ftp_send_async_message(req, BAD_FILENAME, 200);
+			return;
+		}
+		if (missed) {
+			if (cachemiss)
+				TUX_BUG();
+			add_tux_atom(req, ftp_get_size);
+			queue_cachemiss(req);
+			return;
+		}
+	}
+	req->in_file->f_pos = 0;
+	len = sprintf(file_size, "213 %Li\r\n", req->dentry->d_inode->i_size);
+	__ftp_send_async_message(req, file_size, 200, len);
+}
+
+void ftp_get_mdtm (tux_req_t *req, int cachemiss)
+{
+	unsigned int flag = cachemiss ? 0 : LOOKUP_ATOMIC;
+	struct dentry *dentry;
+	struct vfsmount *mnt = NULL;
+	char file_mdtm[200];
+	unsigned int len;
+	int err;
+
+	dentry = tux_lookup(req, req->objectname, flag, &mnt);
+	if (!dentry || IS_ERR(dentry)) {
+		if (PTR_ERR(dentry) == -EWOULDBLOCKIO) {
+			if (cachemiss)
+				TUX_BUG();
+			add_tux_atom(req, ftp_get_mdtm);
+			queue_cachemiss(req);
+			return;
+		}
+		goto out_err;
+	}
+	err = permission(dentry->d_inode, MAY_READ, NULL);
+	if (err)
+		goto out_err_put;
+
+	req->in_file->f_pos = 0;
+	len = mdtm_time (file_mdtm, dentry->d_inode->i_mtime.tv_sec);
+	dput(dentry);
+	mntput(mnt);
+	__ftp_send_async_message(req, file_mdtm, 200, len);
+	return;
+
+out_err_put:
+	dput(dentry);
+	mntput(mnt);
+out_err:
+	ftp_send_async_message(req, BAD_FILENAME, 550);
+}
+
+static void ftp_get_file (tux_req_t *req, int cachemiss)
+{
+	int missed;
+
+	if (!req->dentry) {
+		missed = lookup_object(req, cachemiss ? 0 : LOOKUP_ATOMIC);
+		if (!missed && !req->dentry) {
+			ftp_send_async_message(req, BAD_FILENAME, 200);
+			return;
+		}
+		if (missed) {
+			if (cachemiss)
+				TUX_BUG();
+			add_tux_atom(req, ftp_get_file);
+			queue_cachemiss(req);
+			return;
+		}
+	}
+	Dprintk("ftp_send_file %p, ftp_offset: %Ld, total_len: %Ld.\n", req, req->ftp_offset_start, req->total_file_len);
+	req->in_file->f_pos = 0;
+	if (req->ftp_offset_start) {
+		if (req->ftp_offset_start <= req->total_file_len) {
+			req->offset_start = req->ftp_offset_start;
+			req->in_file->f_pos = req->offset_start;
+		}
+		req->ftp_offset_start = 0;
+	}
+	req->output_len = req->total_file_len - req->offset_start;
+	req->prev_pos = req->in_file->f_pos;
+	Dprintk("ftp_send_file %p, f_pos: %Ld (out_len: %Ld).\n", req, req->in_file->f_pos, req->output_len);
+	add_tux_atom(req, ftp_send_file);
+	add_tux_atom(req, ftp_wait_syn);
+	add_tux_atom(req, ftp_flush_req);
+	ftp_send_async_message(req, WRITE_FILE, 200);
+}
+
+static void __exchange_sockets (tux_req_t *req)
+{
+	struct socket *tmp;
+
+	tmp = req->data_sock;
+	req->data_sock = req->sock;
+	req->sock = tmp;
+
+	req->in_file->f_pos = 0;
+}
+
+static void ftp_do_ls_start (tux_req_t *req, int cachemiss)
+{
+	Dprintk("ftp_do_ls_start(%p, %d).\n", req, cachemiss);
+	if (!req->cwd_dentry)
+		TUX_BUG();
+	__exchange_sockets(req);
+	queue_cachemiss(req);
+}
+
+static void ftp_do_ls_end (tux_req_t *req, int cachemiss)
+{
+	Dprintk("ftp_do_ls_end(%p, %d).\n", req, cachemiss);
+	__exchange_sockets(req);
+	if (tux_ftp_wait_close) {
+		req->data_sock->ops->shutdown(req->data_sock, SEND_SHUTDOWN);
+		add_tux_atom(req, ftp_wait_close);
+		add_req_to_workqueue(req);
+		return;
+	}
+	zap_data_socket(req);
+	add_tux_atom(req, ftp_flush_req);
+	if (req->error)
+		ftp_send_async_message(req, BAD_FILENAME, 200);
+	else
+		ftp_send_async_message(req, WRITE_DONE, 200);
+}
+
+static void ftp_chdir (tux_req_t *req, int cachemiss)
+{
+	unsigned int flag = cachemiss ? 0 : LOOKUP_ATOMIC;
+	struct dentry *dentry;
+	struct vfsmount *mnt = NULL;
+	int err;
+
+	Dprintk("ftp_chdir(%p, %d, {%s})\n", req, cachemiss, req->objectname);
+	dentry = tux_lookup(req, req->objectname, flag, &mnt);
+	if (!dentry || IS_ERR(dentry)) {
+		if (PTR_ERR(dentry) == -EWOULDBLOCKIO) {
+			if (cachemiss)
+				TUX_BUG();
+			add_tux_atom(req, ftp_chdir);
+			queue_cachemiss(req);
+			return;
+		}
+		goto out_err;
+	}
+	err = permission(dentry->d_inode, MAY_EXEC, NULL);
+	if (err)
+		goto out_err_put;
+	req->cwd_dentry = dentry;
+	req->cwd_mnt = mnt;
+	ftp_send_async_message(req, GOOD_DIR, 200);
+	return;
+
+out_err_put:
+	dput(dentry);
+	mntput(mnt);
+out_err:
+	ftp_send_async_message(req, BAD_FILENAME, 550);
+}
+
+void ftp_accept_pasv (tux_req_t *req, int cachemiss)
+{
+	struct socket *sock, *new_sock = NULL;
+	struct inet_connection_sock *icsk1, *icsk2;
+	struct tcp_sock *tp1, *tp2;
+	int err;
+
+	tp1 = tcp_sk(req->data_sock->sk);
+	icsk1 = inet_csk(req->data_sock->sk);
+
+	Dprintk("PASV accept on req %p, accept_queue: %p.\n",
+			req, &icsk1->icsk_accept_queue);
+	if (req->error || (req->data_sock->sk->sk_state != TCP_LISTEN))
+		goto error;
+new_socket:
+	if (reqsk_queue_empty(&icsk1->icsk_accept_queue)) {
+		spin_lock_irq(&req->ti->work_lock);
+		add_keepalive_timer(req);
+		if (test_and_set_bit(0, &req->idle_input))
+			TUX_BUG();
+		spin_unlock_irq(&req->ti->work_lock);
+		if (reqsk_queue_empty(&icsk1->icsk_accept_queue)) {
+			add_tux_atom(req, ftp_accept_pasv);
+			return;
+		}
+		unidle_req(req);
+	}
+	new_sock = sock_alloc();
+	if (!new_sock)
+		goto error;
+	sock = req->data_sock;
+	new_sock->type = sock->type;
+	new_sock->ops = sock->ops;
+
+	err = sock->ops->accept(sock, new_sock, O_NONBLOCK);
+	Dprintk("PASV accept() returned %d (state %d).\n", err, new_sock->sk->sk_state);
+	if (err < 0)
+		goto error;
+	if (new_sock->sk->sk_state != TCP_ESTABLISHED)
+		goto error;
+	/*
+	 * Do not allow other clients to steal the FTP connection!
+	 */
+	if (inet_sk(new_sock->sk)->daddr != inet_sk(req->sock->sk)->daddr) {
+		Dprintk("PASV: ugh, unauthorized connect?\n");
+		sock_release(new_sock);
+		new_sock = NULL;
+		goto new_socket;
+	}
+	/*
+	 * Zap the listen socket:
+	 */
+	zap_data_socket(req);
+
+	tp2 = tcp_sk(new_sock->sk);
+	icsk2 = inet_csk(new_sock->sk);
+	tp2->nonagle = 2;
+	icsk2->icsk_ack.pingpong = tux_ack_pingpong;
+	new_sock->sk->sk_reuse = 1;
+	sock_set_flag(new_sock->sk, SOCK_URGINLINE);
+	sock_reset_flag(new_sock->sk, SOCK_LINGER);
+
+	link_tux_data_socket(req, new_sock);
+	add_req_to_workqueue(req);
+	return;
+
+error:
+	if (new_sock)
+		sock_release(new_sock);
+	req_err(req);
+	zap_data_socket(req);
+	ftp_send_async_message(req, CLOSE, 500);
+}
+
+static char * ftp_print_dir_line (tux_req_t *req, char *tmp, char *d_name, int d_len, int d_type, struct dentry *dentry, struct inode *inode)
+{
+	char *string0 = tmp;
+	unsigned int size;
+
+	if (req->ftp_command == FTP_COMM_NLST) {
+		memcpy(tmp, d_name, d_len);
+		tmp += d_len;
+		*tmp++ = '\r';
+		*tmp++ = '\n';
+		*tmp = 0;
+		return tmp;
+	}
+	switch (d_type) {
+		default:
+		case DT_UNKNOWN:
+		case DT_WHT:
+			if (tux_hide_unreadable)
+				goto out_dput;
+			*tmp++ = '?';
+			break;
+
+		case DT_FIFO:
+			if (tux_hide_unreadable)
+				goto out_dput;
+			*tmp++ = 'p';
+			break;
+
+		case DT_CHR:
+			if (tux_hide_unreadable)
+				goto out_dput;
+			*tmp++ = 'c';
+			break;
+
+		case DT_DIR:
+			*tmp++ = 'd';
+			break;
+
+		case DT_BLK:
+			if (tux_hide_unreadable)
+				goto out_dput;
+			*tmp++ = 'b';
+			break;
+
+		case DT_REG:
+			*tmp++ = '-';
+			break;
+
+		case DT_LNK:
+			*tmp++ = 'l';
+			break;
+
+		case DT_SOCK:
+			if (tux_hide_unreadable)
+				goto out_dput;
+			*tmp++ = 's';
+			break;
+	}
+
+	if (inode->i_mode & S_IRUSR) *tmp++ = 'r'; else *tmp++ = '-';
+	if (inode->i_mode & S_IWUSR) *tmp++ = 'w'; else *tmp++ = '-';
+	if (inode->i_mode & S_IXUSR) *tmp++ = 'x'; else *tmp++ = '-';
+	if (inode->i_mode & S_IRGRP) *tmp++ = 'r'; else *tmp++ = '-';
+	if (inode->i_mode & S_IWGRP) *tmp++ = 'w'; else *tmp++ = '-';
+	if (inode->i_mode & S_IXGRP) *tmp++ = 'x'; else *tmp++ = '-';
+	if (inode->i_mode & S_IROTH) *tmp++ = 'r'; else *tmp++ = '-';
+	if (inode->i_mode & S_IWOTH) *tmp++ = 'w'; else *tmp++ = '-';
+	if (inode->i_mode & S_IXOTH) *tmp++ = 'x'; else *tmp++ = '-';
+
+	*tmp++ = ' ';
+
+	size = sprintf(tmp, "%4i %d", inode->i_nlink, inode->i_uid);
+	tmp += size;
+
+	size = 14 - size;
+	if (size <= 0)
+		size = 1;
+	memset(tmp, ' ', size);
+	tmp += size;
+
+	size = sprintf(tmp, "%d", inode->i_gid);
+	tmp += size;
+
+	size = 9 - size;
+	if (size <= 0)
+		size = 1;
+	memset(tmp, ' ', size);
+	tmp += size;
+
+	tmp += sprintf(tmp, "%8Li", inode->i_size);
+	*tmp++ = ' ';
+
+	tmp += time_unix2ls(inode->i_mtime.tv_sec, tmp);
+	*tmp++ = ' ';
+
+	memcpy(tmp, d_name, d_len);
+	tmp += d_len;
+
+	if (d_type == DT_LNK) {
+		int len = 0, max_len;
+		#define ARROW " -> "
+
+		memcpy(tmp, ARROW, sizeof(ARROW)-1);
+		tmp += sizeof(ARROW)-1;
+		max_len = MAX_OBJECTNAME_LEN-(tmp-string0);
+		if (inode->i_op && inode->i_op->readlink) {
+			mm_segment_t oldmm;
+
+			oldmm = get_fs(); set_fs(KERNEL_DS);
+			set_fs(KERNEL_DS);
+			len = inode->i_op->readlink(dentry, tmp, max_len);
+			set_fs(oldmm);
+		}
+		if (len > 0)
+			tmp += len;
+		else
+			Dprintk("hm, readlink() returned %d.\n", len);
+	}
+	*tmp++ = '\r';
+	*tmp++ = '\n';
+	*tmp = 0;
+
+	return tmp;
+out_dput:
+	return NULL;
+}
+
+static void ftp_do_ls_onefile (tux_req_t *req, int cachemiss)
+{
+	char string0[MAX_OBJECTNAME_LEN+200], *tmp;
+
+	tmp = ftp_print_dir_line(req, string0, req->objectname, req->objectname_len,
+DT_REG, req->dentry, req->dentry->d_inode);
+	if (!tmp) {
+		req_err(req);
+		add_req_to_workqueue(req);
+		return;
+	}
+	if (tmp - string0 >= MAX_OBJECTNAME_LEN+200)
+		BUG();
+	__ftp_send_async_message(req, string0, 200, tmp - string0);
+}
+
+static void ftp_lookup_listfile (tux_req_t *req, int cachemiss)
+{
+	unsigned int flag = cachemiss ? 0 : LOOKUP_ATOMIC;
+	struct dentry *dentry;
+	struct vfsmount *mnt = NULL;
+	int err;
+
+	Dprintk("ftp_lookup_listfile(%p, %d, {%s})\n", req, cachemiss, req->objectname);
+	dentry = tux_lookup(req, req->objectname, flag, &mnt);
+	if (!dentry || IS_ERR(dentry)) {
+		if (PTR_ERR(dentry) == -EWOULDBLOCKIO) {
+			if (cachemiss)
+				TUX_BUG();
+			add_tux_atom(req, ftp_lookup_listfile);
+			queue_cachemiss(req);
+			return;
+		}
+		goto out_err;
+	}
+
+	if (S_ISDIR(dentry->d_inode->i_mode)) {
+		err = permission(dentry->d_inode, MAY_EXEC, NULL);
+		if (err) {
+			Dprintk("Directory permission error: %d.\n", err);
+			goto out_err_put;
+		}
+		install_req_dentry(req, dentry, mnt);
+
+		add_tux_atom(req, ftp_do_ls_end);
+		if (!req->cwd_dentry)
+			TUX_BUG();
+		add_tux_atom(req, list_directory);
+	} else {
+		install_req_dentry(req, dentry, mnt);
+
+		add_tux_atom(req, ftp_do_ls_end);
+		add_tux_atom(req, ftp_do_ls_onefile);
+	}
+
+	add_tux_atom(req, ftp_do_ls_start);
+	add_tux_atom(req, ftp_wait_syn);
+	add_tux_atom(req, ftp_flush_req);
+	ftp_send_async_message(req, WRITE_LIST, 200);
+	return;
+
+out_err_put:
+	dput(dentry);
+	mntput(mnt);
+out_err:
+	ftp_send_async_message(req, BAD_FILENAME, 550);
+}
+
+static void ftp_execute_command (tux_req_t *req, int cachemiss)
+{
+	if (!req->parsed_len)
+		TUX_BUG();
+	trunc_headers(req);
+	req->keep_alive = 1;
+
+	switch (req->ftp_command) {
+
+#define ABORTED \
+	"226 Abort successful.\r\n"
+
+	case FTP_COMM_ABOR:
+	{
+		zap_data_socket(req);
+		ftp_send_async_message(req, ABORTED, 226);
+		break;
+	}
+
+	case FTP_COMM_PWD:
+	{
+		unsigned int str_len;
+		char *buf, *path;
+
+		buf = (char *)__get_free_page(GFP_KERNEL);
+		if (!buf) {
+			req_err(req);
+			ftp_send_async_message(req, LIST_ERR_MEM, 200);
+			GOTO_ERR;
+		}
+
+		if (!req->cwd_dentry) {
+			req->cwd_dentry = dget(req->docroot_dentry);
+			req->cwd_mnt = mntget(req->docroot_mnt);
+		}
+
+// "257 "/" is current directory.\r\n"
+
+#define PART_1 "257 \""
+#define PART_1_LEN (sizeof(PART_1)-1)
+
+#define PART_3 "\" is current directory.\r\n"
+#define PART_3_LEN sizeof(PART_3)
+
+		path = tux_print_path(req, req->cwd_dentry, req->cwd_mnt,
+			buf+PART_1_LEN, PAGE_SIZE - PART_3_LEN - PART_1_LEN);
+
+		if (path < buf + PART_1_LEN)
+			BUG();
+
+		memcpy(path - PART_1_LEN, PART_1, PART_1_LEN);
+		memcpy(buf + PAGE_SIZE-PART_3_LEN-1, PART_3, PART_3_LEN);
+		str_len = buf + PAGE_SIZE-1 - (path - PART_1_LEN) - 1;
+
+		__ftp_send_async_message(req, path - PART_1_LEN, 226, str_len);
+		free_page((unsigned long)buf);
+		break;
+	}
+
+	case FTP_COMM_CDUP:
+	{
+		memcpy(req->objectname, "..", 3);
+		req->objectname_len = 2;
+		req->uri_str = req->objectname;
+		req->uri_len = req->objectname_len;
+
+		// fall through to CWD:
+	}
+	case FTP_COMM_CWD:
+	{
+		ftp_chdir(req, cachemiss);
+		break;
+	}
+
+	case FTP_COMM_NLST:
+	case FTP_COMM_LIST:
+	{
+		if (!req->data_sock) {
+			req_err(req);
+			ftp_send_async_message(req, LIST_ERR, 200);
+			GOTO_ERR;
+		}
+		if (req->dentry)
+			TUX_BUG();
+		if (!req->cwd_dentry) {
+			req->cwd_dentry = dget(req->docroot_dentry);
+			req->cwd_mnt = mntget(req->docroot_mnt);
+		}
+		if (req->objectname_len)
+			ftp_lookup_listfile(req, cachemiss);
+		else {
+			dget(req->cwd_dentry);
+			mntget(req->cwd_mnt);
+			install_req_dentry(req, req->cwd_dentry, req->cwd_mnt);
+			if (!req->dentry)
+				TUX_BUG();
+			add_tux_atom(req, ftp_do_ls_end);
+			if (!req->cwd_dentry)
+				TUX_BUG();
+			add_tux_atom(req, list_directory);
+			add_tux_atom(req, ftp_do_ls_start);
+			add_tux_atom(req, ftp_wait_syn);
+			add_tux_atom(req, ftp_flush_req);
+			ftp_send_async_message(req, WRITE_LIST, 200);
+		}
+		break;
+	}
+
+	case FTP_COMM_RETR:
+	{
+		if (!req->data_sock) {
+			req_err(req);
+			ftp_send_async_message(req, RETR_ERR, 200);
+			GOTO_ERR;
+		}
+		ftp_get_file(req, cachemiss);
+		break;
+	}
+
+	case FTP_COMM_SIZE:
+	{
+		ftp_get_size(req, cachemiss);
+		break;
+	}
+
+	case FTP_COMM_MDTM:
+	{
+		ftp_get_mdtm(req, cachemiss);
+		break;
+	}
+
+	case FTP_COMM_PASV:
+	{
+		char buf [36 + 4*3 + 5 + 10];
+		struct socket *data_sock;
+		struct sockaddr_in addr;
+		unsigned int str_len;
+		struct tcp_sock *tp;
+		struct inet_connection_sock *icsk;
+		u32 local_addr;
+		int err;
+
+		if (req->data_sock)
+			zap_data_socket(req);
+		/*
+		 * Create FTP data connection to client:
+		 */
+		err = sock_create_kern(AF_INET, SOCK_STREAM, IPPROTO_IP, &data_sock);
+		if (err < 0) {
+			Dprintk("sock create err: %d\n", err);
+			req_err(req);
+			ftp_send_async_message(req, CLOSE, 500);
+			GOTO_ERR;
+		}
+
+		local_addr = inet_sk(req->sock->sk)->rcv_saddr;
+		addr.sin_family = AF_INET;
+		addr.sin_port = 0;
+		addr.sin_addr.s_addr = local_addr;
+		Dprintk("client address: (%d,%d,%d,%d).\n",
+			NIPQUAD(inet_sk(req->sock->sk)->daddr));
+
+		data_sock->sk->sk_reuse = 1;
+		sock_set_flag(data_sock->sk, SOCK_URGINLINE);
+		sock_reset_flag(data_sock->sk, SOCK_LINGER);
+
+		err = data_sock->ops->bind(data_sock,
+				(struct sockaddr*)&addr, sizeof(addr));
+		tp = tcp_sk(data_sock->sk);
+		icsk = inet_csk(data_sock->sk);
+
+		tp->nonagle = 2;
+		Dprintk("PASV bind() ret: %d.\n", err);
+		if (err < 0) {
+			req_err(req);
+			sock_release(data_sock);
+			ftp_send_async_message(req, CLOSE, 500);
+			GOTO_ERR;
+		}
+
+		icsk->icsk_ack.pingpong = tux_ack_pingpong;
+
+		if (!tux_keepalive_timeout)
+			tp->linger2 = 0;
+		else
+			tp->linger2 = tux_keepalive_timeout * HZ;
+
+		err = data_sock->ops->listen(data_sock, 1);
+		Dprintk("PASV listen() ret: %d\n", err);
+		if (err) {
+			req_err(req);
+			sock_release(data_sock);
+			ftp_send_async_message(req, CLOSE, 500);
+			GOTO_ERR;
+		}
+		link_tux_data_socket(req, data_sock);
+
+		Dprintk("FTP PASV listen sock state: %d, sk state: %d\n",
+			data_sock->state, data_sock->sk->sk_state);
+
+		str_len = sprintf(buf,
+			"227 Entering Passive Mode (%d,%d,%d,%d,%d,%d)\r\n",
+				NIPQUAD(local_addr),
+				ntohs(inet_sk(data_sock->sk)->sport) / 256,
+				ntohs(inet_sk(data_sock->sk)->sport) & 255 );
+		Dprintk("PASV mess: {%s}\n", buf);
+
+		add_tux_atom(req, ftp_accept_pasv);
+		add_tux_atom(req, ftp_flush_req);
+		__ftp_send_async_message(req, buf, 227, str_len);
+		break;
+	}
+
+	case FTP_COMM_PORT:
+	{
+		struct socket *data_sock;
+		struct sockaddr_in addr;
+		kernel_cap_t saved_cap;
+		u32 local_addr;
+		int err;
+
+		/*
+		 * Create FTP data connection to client:
+		 */
+		err = sock_create_kern(AF_INET, SOCK_STREAM, IPPROTO_IP, &data_sock);
+		if (err < 0) {
+			Dprintk("sock create err: %d\n", err);
+			req_err(req);
+			ftp_send_async_message(req, CLOSE, 500);
+			GOTO_ERR;
+		}
+
+		local_addr = inet_sk(req->sock->sk)->rcv_saddr;
+		addr.sin_family = AF_INET;
+		addr.sin_port = htons(20);
+		addr.sin_addr.s_addr = local_addr;
+
+		Dprintk("data socket address: (%d,%d,%d,%d).\n",
+			NIPQUAD(local_addr));
+
+		data_sock->sk->sk_reuse = 1;
+		sock_set_flag(data_sock->sk, SOCK_URGINLINE);
+		sock_reset_flag(data_sock->sk, SOCK_LINGER);
+
+		saved_cap = current->cap_effective;
+		cap_raise (current->cap_effective, CAP_NET_BIND_SERVICE);
+		err = data_sock->ops->bind(data_sock,
+				(struct sockaddr*)&addr, sizeof(addr));
+		current->cap_effective = saved_cap;
+
+		Dprintk("ACTIVE bind() ret: %d.\n", err);
+		if (err) {
+			sock_release(data_sock);
+			req_err(req);
+			ftp_send_async_message(req, CLOSE, 500);
+			GOTO_ERR;
+		}
+		tcp_sk(data_sock->sk)->nonagle = 2;
+
+		link_tux_data_socket(req, data_sock);
+
+		addr.sin_family = AF_INET;
+		addr.sin_port = htons(req->ftp_user_port);
+		addr.sin_addr.s_addr = htonl(req->ftp_user_addr);
+
+		err = data_sock->ops->connect(data_sock, (struct sockaddr *) &addr, sizeof(addr), O_RDWR|O_NONBLOCK);
+		if (err && (err != -EINPROGRESS)) {
+			Dprintk("connect error: %d\n", err);
+			zap_data_socket(req);
+			req_err(req);
+			ftp_send_async_message(req, CLOSE, 500);
+			GOTO_ERR;
+		}
+		Dprintk("FTP data sock state: %d, sk state: %d\n", data_sock->state, data_sock->sk->sk_state);
+		ftp_send_async_message(req, PORT_OK, 200);
+		break;
+	}
+
+	case FTP_COMM_USER:
+	{
+		if (!strcmp(req->username, "ftp")
+			 || !strcmp(req->username, "FTP")
+			 || !strcmp(req->username, "anonymous")
+			 || !strcmp(req->username, "ANONYMOUS")) {
+			unsigned int str_len;
+			char login_ok [200];
+
+			if (!tux_ftp_login_message) {
+				ftp_send_async_message(req, LOGIN_OK_PASS, 230);
+				break;
+			}
+			update_bandwidth(req, 0); /* get current bandwidth */
+			if (nr_requests_used() == 1)
+				str_len = sprintf(login_ok, LOGIN_OK_ONE,
+					tux_max_connect, ftp_bandwidth);
+			else
+				str_len = sprintf(login_ok, LOGIN_OK,
+					nr_requests_used(), tux_max_connect, ftp_bandwidth);
+			__ftp_send_async_message(req, login_ok, 200, str_len);
+		} else {
+			clear_keepalive(req);
+			ftp_send_async_message(req, LOGIN_FORBIDDEN, 530);
+		}
+		break;
+	}
+	case FTP_COMM_PASS:
+	{
+		ftp_send_async_message(req, LOGIN_OK_PASS, 230);
+		break;
+	}
+	case FTP_COMM_SITE:
+	{
+		ftp_send_async_message(req, SITE, 214);
+		break;
+	}
+	case FTP_COMM_SYST:
+	{
+		ftp_send_async_message(req, LINUX_SYST, 200);
+		break;
+	}
+	case FTP_COMM_TYPE:
+	{
+		ftp_send_async_message(req, TYPE_OK, 200);
+		break;
+	}
+#define EXTRA_FEATURES "211-Extensions supported:\r\n SIZE\r\n MDTM\r\n211 End\r\n"
+
+	case FTP_COMM_FEAT:
+	{
+		ftp_send_async_message(req, EXTRA_FEATURES, 211);
+		break;
+	}
+	case FTP_COMM_HELP:
+	case FTP_COMM_CLNT:
+	case FTP_COMM_NOOP:
+	{
+		ftp_send_async_message(req, COMMAND_OK, 200);
+		break;
+	}
+	case FTP_COMM_REST:
+	{
+		ftp_send_async_message(req, REST_OK, 200);
+		break;
+	}
+	case FTP_COMM_QUIT:
+	{
+		clear_keepalive(req);
+		ftp_send_async_message(req, BYE, 200);
+		break;
+	}
+
+	default:
+	{
+		req->keep_alive = 1;
+		ftp_send_async_message(req, CLOSE, 500);
+		break;
+	}
+	}
+	return;
+error:
+	Dprintk("rejecting FTP session!\n");
+	return;
+}
+
+
+static void ftp_timeout (tux_req_t *req, int cachemiss)
+{
+	Dprintk("called ftp_timeout(%p)\n", req);
+	if (req->error != TUX_ERROR_CONN_TIMEOUT)
+		TUX_BUG();
+	ftp_send_async_message(req, CLOSE_TIMEOUT, 421);
+}
+
+static void ftp_close (tux_req_t *req, int cachemiss)
+{
+	Dprintk("called ftp_close(%p)\n", req);
+	ftp_send_async_message(req, CLOSE, 500);
+}
+
+static void ftp_pre_log (tux_req_t *req)
+{
+	if (tux_ftp_log_retr_only && (req->ftp_command != FTP_COMM_RETR))
+		req->status = 0;
+	else
+		req->status = req->ftp_command;
+}
+
+tux_proto_t tux_proto_ftp = {
+	.defer_accept = 0,
+	.can_redirect = 0,
+	.got_request = ftp_got_request,
+	.parse_message = parse_ftp_message,
+	.illegal_request = ftp_close,
+	.request_timeout = ftp_timeout,
+	.pre_log = ftp_pre_log,
+	.check_req_err = ftp_check_req_err,
+	.print_dir_line = ftp_print_dir_line,
+	.name = "ftp",
+};
+
Index: latest/net/tux/proto_http.c
===================================================================
--- /dev/null
+++ latest/net/tux/proto_http.c
@@ -0,0 +1,2197 @@
+/*
+ * TUX - Integrated Application Protocols Layer and Object Cache
+ *
+ * Copyright (C) 2000, 2001, Ingo Molnar <mingo@redhat.com>
+ *
+ * proto_http.c: HTTP application protocol support
+ *
+ * Right now we detect simple GET headers, anything more
+ * subtle gets redirected to secondary server port.
+ */
+
+#include <net/tux.h>
+#include "parser.h"
+
+/****************************************************************
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2, or (at your option)
+ *      any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ ****************************************************************/
+
+/*
+ * Parse the HTTP message and put results into the request structure.
+ * CISAPI extensions do not see the actual message buffer.
+ *
+ * Any perceived irregularity is honored with a redirect to the
+ * secondary server - which in most cases should be Apache. So
+ * if TUX gets confused by some strange request we fall back
+ * to Apache to be RFC-correct.
+ *
+ * The parser is 'optimistic', ie. it's optimized for the case where
+ * the whole message is available and correct. The parser is also
+ * supposed to be 'robust', ie. it can be called multiple times with
+ * an incomplete message, as new packets arrive.
+ */
+
+static inline int TOHEX (char c)
+{
+	switch (c) {
+		case '0' ... '9': c -= '0'; break;
+		case 'a' ... 'f': c -= 'a'-10; break;
+		case 'A' ... 'F': c -= 'A'-10; break;
+	default:
+		c = -1;
+	}
+	return c;
+}
+
+/*
+ * This function determines whether the client supports
+ * gzip-type content-encoding.
+ */
+static int may_gzip (const char *str, int len)
+{
+	const char *tmp, *curr;
+	int i;
+
+	if (len <= 4)
+		return 0;
+	tmp = str;
+	for (i = 0; i <= len-6; i++) {
+		Dprintk("gzip-checking: {%s}\n", tmp);
+		if (memcmp(tmp, " gzip", 5)) {
+			tmp++;
+			continue;
+		}
+		curr = tmp + 5;
+
+		if (*curr == ',' || *curr == '\r')
+			return 1;
+		if (memcmp(curr, ";q=", 3))
+			return 0;
+		curr += 3;
+		/*
+		 * Every qvalue except explicitly zero is accepted.
+		 * Zero values are "q=0.0", "q=0.00", "q=0.000".
+		 * Parsing is optimized.
+		 */
+		if (*curr == '0') {
+			curr += 2;
+			if (*curr == '0') {
+				curr++;
+				if (*curr == ' ' || *curr == '\r')
+					return 0;
+				if (*curr == '0') {
+					curr++;
+					if (*curr == ' ' || *curr == '\r')
+						return 0;
+					if (*curr == '0') {
+						curr++;
+						if (*curr == ' ' ||
+								*curr == '\r')
+							return 0;
+					}
+				}
+			}
+		}
+		return 1;
+	}
+	return 0;
+}
+
+/*
+ * This function strips off 'strip_host_tail' number of hostname
+ * components from the tail of the hostname.
+ *
+ * Eg. with a value of '1', the "somesite.hosting.com" hostname gets
+ * transformed into the "somesite" string.
+ */
+static void strip_hostname(tux_req_t *req)
+{
+	int strip = strip_host_tail;
+	int left = req->host_len;
+	int component = 0;
+
+	if (!strip || !left)
+		return;
+
+	while (--left) {
+		if (req->host[left] != '.')
+			continue;
+		if (++component == strip)
+			break;
+	}
+	if (!left)
+		return;
+	req->host[left] = 0;
+	req->host_len = left;
+}
+
+static void http_lookup_vhost (tux_req_t *req, int cachemiss);
+static void http_process_message (tux_req_t *req, int cachemiss);
+
+int parse_http_message (tux_req_t *req, const int total_len)
+{
+	int hexhex = 0, hex_val_0 = 0, hex_val_1 = 0;
+	const char *curr, *uri, *message;
+	unsigned int objectname_len, left;
+	unsigned int have_r = 0;
+	char c;
+
+	left = total_len;
+	message = req->headers;
+	Dprintk("parsing request:\n---\n%s\n---\n", message);
+/*
+ * RFC 2616, 5.1:
+ *
+ *	 Request-Line   = Method SP Request-URI SP HTTP-Version CRLF
+ */
+
+	if (!total_len)
+		TUX_BUG();
+
+	curr = message;
+
+#define GOTO_INCOMPLETE do { Dprintk("incomplete at %s:%d.\n", __FILE__, __LINE__); goto incomplete_message; } while (0)
+#define GOTO_REDIR do { TDprintk("redirect secondary at %s:%d.\n", __FILE__, __LINE__); goto error; } while (0)
+
+#define PRINT_MESSAGE_LEFT \
+    Dprintk("message left (%d) at %s:%d:\n--->{%s}<---\n", left, __FILE__, __LINE__, curr)
+
+	switch (*curr) {
+		case 'G':
+			if (PARSE_METHOD(req,curr,GET,left))
+				break;
+			GOTO_REDIR;
+
+		case 'H':
+			if (PARSE_METHOD(req,curr,HEAD,left))
+				break;
+			GOTO_REDIR;
+
+		case 'P':
+			if (PARSE_METHOD(req,curr,POST,left))
+				break;
+			if (PARSE_METHOD(req,curr,PUT,left))
+				break;
+			GOTO_REDIR;
+
+		default:
+			GOTO_REDIR;
+	}
+
+	req->method_str = message;
+	req->method_len = curr-message-1;
+
+	Dprintk("got method %d\n", req->method);
+
+	PRINT_MESSAGE_LEFT;
+
+	/*
+	 * Ok, we got one of the methods we can handle, parse
+	 * the URI:
+	 */
+
+	{
+		// Do not allow leading "../" and intermediate "/../"
+		int dotdot = 1;
+		char *tmp = req->objectname;
+		int slashcheck = 1;
+
+		req->uri_str = uri = curr;
+
+		for (;;) {
+			c = get_c(curr,left);
+			if (slashcheck) {
+				if (c == '/')
+					continue;
+				slashcheck = 0;
+			}
+
+			PRINT_MESSAGE_LEFT;
+			if (c == ' ' || ((c == '?') && (tux_ignore_query != 1)) || c == '\r' || c == '\n')
+				break;
+			if (c == '#')
+				GOTO_REDIR;
+
+			Dprintk("hexhex: %d.\n", hexhex);
+			/*
+			 * First handle HEX HEX encoding
+			 */
+			switch (hexhex) {
+				case 0:
+					if (c == '%') {
+						hexhex = 1;
+						goto continue_parsing;
+					}
+					break;
+				case 1:
+					hex_val_0 = TOHEX(c);
+					if (hex_val_0 < 0)
+						GOTO_REDIR;
+					hexhex = 2;
+					goto continue_parsing;
+				case 2:
+					hex_val_1 = TOHEX(c);
+					if (hex_val_1 < 0)
+						GOTO_REDIR;
+					c = (hex_val_0 << 4) | hex_val_1;
+					if (!c)
+						GOTO_REDIR;
+					hexhex = 0;
+					break;
+				default:
+					TUX_BUG();
+			}
+			if (hexhex)
+				TUX_BUG();
+
+			switch (dotdot) {
+				case 0:
+					break;
+				case 1:
+					if (c == '.')
+						dotdot = 2;
+					else
+						dotdot = 0;
+					break;
+				case 2:
+					if (c == '.')
+						dotdot = 3;
+					else
+						dotdot = 0;
+					break;
+				case 3:
+					if (c == '/')
+						GOTO_REDIR;
+					else
+						dotdot = 0;
+					break;
+				default:
+					TUX_BUG();
+			}
+			if (!dotdot && (c == '/'))
+				dotdot = 1;
+
+			*(tmp++) = c;
+continue_parsing:
+			if (curr - uri >= MAX_OBJECTNAME_LEN)
+				GOTO_REDIR;
+		}
+		PRINT_MESSAGE_LEFT;
+		*tmp = 0;
+
+		// handle trailing "/.."
+		if (dotdot == 3)
+			GOTO_REDIR;
+
+		objectname_len = tmp - req->objectname;
+		req->objectname_len = objectname_len;
+	}
+	Dprintk("got filename %s (%d)\n", req->objectname, req->objectname_len);
+
+	PRINT_MESSAGE_LEFT;
+
+	/*
+	 * Parse optional query string. Copy until end-of-string or space.
+	 */
+	if (c == '?') {
+		int query_len;
+		const char *query;
+
+		req->query_str = query = curr;
+
+		for (;;) {
+			c = get_c(curr,left);
+			if (c == ' ')
+				break;
+			if (c == '#')
+				GOTO_REDIR;
+		}
+		if (unlikely(tux_ignore_query == 2))
+			req->query_str = NULL;
+		else {
+			query_len = curr-query-1;
+			req->query_len = query_len;
+		}
+	}
+	if (req->query_len)
+		Dprintk("got query string %s (%d)\n", req->query_str, req->query_len);
+	req->uri_len = curr-uri-1;
+	if (!req->uri_len)
+		GOTO_REDIR;
+	Dprintk("got URI %s (%d)\n", req->uri_str, req->uri_len);
+
+	PRINT_MESSAGE_LEFT;
+	/*
+	 * Parse the HTTP version field:
+	 */
+	req->version_str = curr;
+	if (!PARSE_TOKEN(curr,"HTTP/1.",left))
+		GOTO_REDIR;
+
+	switch (get_c(curr,left)) {
+		case '0':
+			req->version = HTTP_1_0;
+			break;
+		case '1':
+			req->version = HTTP_1_1;
+			break;
+		default:
+			GOTO_REDIR;
+	}
+	/*
+	 * We default to keepalive in the HTTP/1.1 case and default
+	 * to non-keepalive in the HTTP/1.0 case. If max_keepalives
+	 * is 0 then we do no keepalives.
+	 */
+	clear_keepalive(req);
+	if (tux_max_keepalives && (req->version == HTTP_1_1))
+		req->keep_alive = 1;
+	req->version_len = curr - req->version_str;
+
+	if (get_c(curr,left) != '\r')
+		GOTO_REDIR;
+	if (get_c(curr,left) != '\n')
+		GOTO_REDIR;
+
+	Dprintk("got version %d [%d]\n", req->version, req->version_len);
+	PRINT_MESSAGE_LEFT;
+
+	/*
+	 * Now parse (optional) request header fields:
+	 */
+	for (;;) {
+		char c;
+
+		c = get_c(curr,left);
+		switch (c) {
+		case '\r':
+			if (have_r)
+				GOTO_REDIR;
+			have_r = 1;
+			continue;
+		case '\n':
+			if (!have_r)
+				GOTO_REDIR;
+			goto out;
+		default:
+			if (have_r)
+				GOTO_REDIR;
+		}
+
+#define PARSE_STR_FIELD(char,field,str,len)				\
+	if (PARSE_TOKEN(curr,field,left)) {				\
+		req->str = curr;					\
+		SKIP_LINE(curr,left);					\
+		req->len = curr - req->str - 2;				\
+		Dprintk(char field "field: %s.\n", req->str);		\
+		break;							\
+	}
+
+#define ALLOW_UNKNOWN_FIELDS 1
+#ifdef ALLOW_UNKNOWN_FIELDS
+# define UNKNOWN_FIELD { SKIP_LINE(curr,left); break; }
+#else
+# define UNKNOWN_FIELD GOTO_REDIR
+#endif
+
+		switch (c) {
+		case 'A':
+			PARSE_STR_FIELD("A","ccept: ",
+				accept_str,accept_len);
+			if (PARSE_TOKEN(curr,"ccept-Encoding: ",left)) {
+				const char *str = curr-1;
+
+				req->accept_encoding_str = curr;
+				SKIP_LINE(curr,left);
+				req->accept_encoding_len = curr - req->accept_encoding_str - 2;
+				Dprintk("Accept-Encoding field: {%s}.\n", str);
+
+				if (tux_compression && may_gzip(str,curr-str)) {
+					Dprintk("client accepts gzip!.\n");
+					req->may_send_gzip = 1;
+				}
+				break;
+			}
+			PARSE_STR_FIELD("A","ccept-Charset: ",
+				accept_charset_str,accept_charset_len);
+			PARSE_STR_FIELD("A","ccept-Language: ",
+				accept_language_str,accept_language_len);
+			UNKNOWN_FIELD;
+
+		case 'C':
+			if (PARSE_TOKEN(curr,"onnection: ",left)) {
+next_token:
+			switch (get_c(curr,left)) {
+			case 'K':
+				if (!PARSE_TOKEN(curr,"eep-Alive",left))
+					GOTO_REDIR;
+				if (tux_max_keepalives)
+					req->keep_alive = 1;
+				break;
+
+			case 'C':
+			case 'c':
+				if (!PARSE_TOKEN(curr,"lose",left))
+					GOTO_REDIR;
+				clear_keepalive(req);
+				break;
+
+			case 'k':
+				if (!PARSE_TOKEN(curr,"eep-alive",left))
+					GOTO_REDIR;
+				if (tux_max_keepalives)
+					req->keep_alive = 1;
+				break;
+			case 'T':
+				if (PARSE_TOKEN(curr,"E",left))
+					break;
+				if (PARSE_TOKEN(curr,"railers",left))
+					break;
+				if (PARSE_TOKEN(curr,"ransfer-Encoding",left))
+					break;
+				GOTO_REDIR;
+			case 'P':
+				if (PARSE_TOKEN(curr,"roxy-Authenticate",left))
+					break;
+				if (PARSE_TOKEN(curr,"roxy-Authorization",left))
+					break;
+				GOTO_REDIR;
+			case 'U':
+				if (!PARSE_TOKEN(curr,"pgrade",left))
+					GOTO_REDIR;
+				break;
+			case ' ':
+				PRINT_MESSAGE_LEFT;
+				goto next_token;
+			case ',':
+				PRINT_MESSAGE_LEFT;
+				goto next_token;
+			default:
+				GOTO_REDIR;
+			}
+			PRINT_MESSAGE_LEFT;
+			if (*curr != '\r')
+				goto next_token;
+			// allow other tokens.
+			SKIP_LINE(curr,left);
+			break;
+			}
+
+			PARSE_STR_FIELD("C","ookie: ",
+				cookies_str,cookies_len);
+			PARSE_STR_FIELD("C","ontent-Type: ",
+				content_type_str,content_type_len);
+
+			if (PARSE_TOKEN(curr,"ontent-Length: ",left) ||
+			    PARSE_TOKEN(curr,"ontent-length: ",left)) {
+				const char *tmp;
+				req->contentlen_str = curr;
+				SKIP_LINE(curr,left);
+				req->contentlen_len = curr - req->contentlen_str - 2;
+				if (req->contentlen_len) {
+					tmp = req->contentlen_str;
+					req->content_len = simple_strtoul(tmp, NULL, 10);
+				}
+				Dprintk("Content-Length field: %s [%d].\n", req->contentlen_str, req->contentlen_len);
+				Dprintk("Content-Length value: %d.\n", req->content_len);
+				break;
+			}
+			PARSE_STR_FIELD("C","ache-Control: ",
+				cache_control_str,cache_control_len);
+			UNKNOWN_FIELD;
+
+		case 'H':
+			if (PARSE_TOKEN(curr,"ost: ",left)) {
+				const char *tmp = curr;
+				char *tmp2 = req->host;
+
+				/*
+				 * canonize the hostname:
+				 *
+				 * 1) strip off preceding 'www.' variants,
+				 * 2) transform it to lowercase.
+				 * 3) strip trailing dots
+				 * 4) potentially strip off tail
+				 */
+
+#define is_w(n) ((curr[n] == 'w') || (curr[n] == 'W'))
+
+				if ((left > 4) && is_w(0) && is_w(1) &&
+						is_w(2) && curr[3] == '.') {
+					curr += 4;
+					left -= 4;
+					tmp = curr;
+				}
+
+				COPY_LINE_TOLOWER(curr, tmp2, left, req->host+MAX_HOST_LEN-2);
+				req->host_len = curr - tmp - 2;
+				while (req->host[req->host_len] == '.') {
+					if (!req->host_len)
+						break;
+					req->host_len--;
+				}
+				req->host[req->host_len] = 0;
+				if (strip_host_tail)
+					strip_hostname(req);
+				Dprintk("Host field: %s [%d].\n", req->host, req->host_len);
+				break;
+			}
+			UNKNOWN_FIELD;
+
+		case 'I':
+			PARSE_STR_FIELD("I","f-None-Match: ",
+				if_none_match_str,if_none_match_len);
+			PARSE_STR_FIELD("I","f-Modified-Since: ",
+				if_modified_since_str,if_modified_since_len);
+			PARSE_STR_FIELD("I","f-Range: ",
+				if_range_str,if_range_len);
+			UNKNOWN_FIELD;
+
+		case 'N':
+			PARSE_STR_FIELD("N","egotiate: ",
+				negotiate_str,negotiate_len);
+			UNKNOWN_FIELD;
+
+		case 'P':
+			PARSE_STR_FIELD("P","ragma: ",
+				pragma_str,pragma_len);
+			UNKNOWN_FIELD;
+
+		case 'R':
+
+			PARSE_STR_FIELD("R","eferer: ",
+				referer_str,referer_len);
+			if (!PARSE_TOKEN(curr,"ange: bytes=",left))
+				UNKNOWN_FIELD;
+		{
+			const char *tmp = curr;
+			char *tmp2 = (char *)curr;
+			unsigned int offset_start = 0, offset_end = 0;
+
+			if (*tmp2 != '-')
+				offset_start = simple_strtoul(tmp2, &tmp2, 10);
+			if (*tmp2 == '-') {
+				tmp2++;
+				if (*tmp2 != '\r')
+					offset_end = simple_strtoul(tmp2, &tmp2, 10) +1;
+			}
+			curr = tmp2;
+			left -= tmp2-tmp;
+
+			req->offset_start = offset_start;
+			req->offset_end = offset_end;
+
+			SKIP_LINE(curr,left);
+			Dprintk("Range field: %s [%d] (%d-%d).\n", tmp, curr-tmp, offset_start, offset_end);
+			break;
+		}
+
+		case 'U':
+			PARSE_STR_FIELD("U","ser-Agent: ",
+				user_agent_str,user_agent_len);
+			UNKNOWN_FIELD;
+
+		default:
+			UNKNOWN_FIELD;
+		}
+		PRINT_MESSAGE_LEFT;
+	}
+out:
+	/*
+	 * POST data.
+	 */
+	if ((req->method == METHOD_POST) && req->content_len) {
+		PRINT_MESSAGE_LEFT;
+		if (curr + req->content_len > message + total_len)
+			GOTO_INCOMPLETE;
+		req->post_data_str = curr;
+		req->post_data_len = req->content_len;
+		curr += req->content_len;
+		left -= req->content_len;
+		Dprintk("POST-ed data: {%s}\n", req->post_data_str);
+	}
+
+	switch (req->method) {
+		default:
+			GOTO_REDIR;
+		case METHOD_GET:
+		case METHOD_HEAD:
+		case METHOD_POST:
+		case METHOD_PUT:
+			;
+	}
+
+#define TUX_SCHEME "http://"
+#define TUX_SCHEME_LEN (sizeof(TUX_SCHEME)-1)
+
+	if (!memcmp(req->objectname, TUX_SCHEME, TUX_SCHEME_LEN)) {
+
+		/* http://user:password@host:port/object */
+
+		const char *head, *tail, *end, *host, *port;
+		int host_len, objectname_len;
+
+		head = req->objectname + TUX_SCHEME_LEN;
+		end = req->objectname + req->objectname_len;
+
+		tail = memchr(head, '/', end - head);
+		if (!tail)
+			GOTO_REDIR;
+		host = memchr(head, '@', tail - head);
+		if (!host)
+			host = head;
+		else
+			host++;
+		if (!*host)
+			GOTO_REDIR;
+		port = memchr(host, ':', tail - host);
+		if (port)
+			host_len = port - host;
+		else
+			host_len = tail - host;
+		if (host_len >= MAX_HOST_LEN)
+			GOTO_REDIR;
+		memcpy(req->host, host, host_len);
+		req->host_len = host_len;
+		req->host[host_len] = 0;
+
+		if (*tail != '/')
+			TUX_BUG();
+
+		req->uri_str = tail;
+		req->uri_len = end - tail;
+
+		tail++;
+		while (*tail == '/')
+			tail++;
+
+		objectname_len = end - tail;
+		memcpy(req->objectname, tail, objectname_len);
+		req->objectname_len = objectname_len;
+		req->objectname[objectname_len] = 0;
+	} else
+		if (req->uri_str[0] != '/')
+			GOTO_REDIR;
+
+	if ((req->version == HTTP_1_1) && !req->host_len)
+		GOTO_REDIR;
+	if (req->objectname[0] == '/')
+		GOTO_REDIR;
+	/*
+	 * Lets make sure nobody plays games with the host
+	 * header in a virtual hosting environment:
+	 */
+	if (req->virtual && req->host_len) {
+		if (memchr(req->host, '/', req->host_len))
+			GOTO_REDIR;
+		if (req->host[0] == '.') {
+			if (req->host_len == 1)
+				GOTO_REDIR;
+			if ((req->host_len == 2) && (req->host[0] == '.'))
+				GOTO_REDIR;
+		}
+	}
+	/*
+	 * From this point on the request is for the main TUX engine:
+	 */
+	Dprintk("ok, request accepted.\n");
+
+	if (req->keep_alive) {
+		req->nr_keepalives++;
+		if (req->nr_keepalives == -1)
+			req->nr_keepalives--;
+		INC_STAT(nr_keepalive_reqs);
+	} else
+		INC_STAT(nr_nonkeepalive_reqs);
+	INC_STAT(keepalive_hist[req->nr_keepalives]);
+
+	PRINT_MESSAGE_LEFT;
+	req->parsed_len = curr-message;
+	if (req->dentry)
+		TUX_BUG();
+	req->virtual = tux_virtual_server;
+	if (req->virtual)
+		add_tux_atom(req, http_lookup_vhost);
+	else {
+		req->docroot_dentry = dget(req->proto->main_docroot.dentry);
+		req->docroot_mnt = mntget(req->proto->main_docroot.mnt);
+		add_tux_atom(req, http_process_message);
+	}
+
+	return req->parsed_len;
+
+incomplete_message:
+	Dprintk("incomplete message!\n");
+	PRINT_MESSAGE_LEFT;
+
+	return 0;
+
+error:
+	if (total_len > 0)
+		req->parsed_len = total_len;
+	else
+		req->parsed_len = 0;
+	PRINT_MESSAGE_LEFT;
+	if (tux_TDprintk) {
+		TDprintk("redirecting message to secondary server.\n");
+		print_req(req);
+	}
+	return -1;
+}
+
+static int lookup_url (tux_req_t *req, const unsigned int flag)
+{
+	/*
+	 * -1 : no previous checks made
+	 *  0 : previous check failed, do not check farther,
+	 *  1 : previous check successed, check farther
+	 */
+	int not_modified = -1;
+	int perm = 0;
+	struct dentry *dentry = NULL;
+	struct vfsmount *mnt = NULL;
+	struct inode *inode;
+	const char *filename;
+
+	/*
+	 * Do not do any etag or last_modified header checking
+	 * if both unset.
+	 */
+	if (!tux_generate_etags && !tux_generate_last_mod)
+		not_modified = 0;
+
+repeat_lookup:
+	if (req->dentry)
+		TUX_BUG();
+
+	filename = req->objectname;
+	Dprintk("will look up {%s} (%d)\n", filename, req->objectname_len);
+	Dprintk("current->fsuid: %d, current->fsgid: %d, ngroups: %d\n",
+		current->fsuid, current->fsgid, current->group_info->ngroups);
+
+	dentry = tux_lookup(req, filename, flag, &mnt);
+
+#define INDEX "/index.html"
+
+	if (!dentry || IS_ERR(dentry)) {
+		if (PTR_ERR(dentry) == -EWOULDBLOCKIO)
+			goto cachemiss;
+
+		if (tux_http_dir_indexing && (req->lookup_dir == 1)) {
+			// undo the index.html appending:
+			req->objectname_len -= sizeof(INDEX)-1;
+			req->objectname[req->objectname_len] = 0;
+			req->lookup_dir = 2;
+			goto repeat_lookup;
+		}
+		if (!req->lookup_404) {
+			int len = strlen(tux_404_page);
+			memcpy(req->objectname, tux_404_page, len);
+			req->objectname[len] = 0;
+			req->objectname_len = len;
+			req->lookup_404 = 1;
+			req->status = 404;
+			goto repeat_lookup;
+		}
+		TDprintk("abort - lookup error.\n");
+		goto abort;
+	}
+
+	Dprintk("SUCCESS, looked up {%s} == dentry %p (inode %p, count %d.)\n", filename, dentry, dentry->d_inode, atomic_read(&dentry->d_count));
+	inode = dentry->d_inode;
+
+	/*
+	 * At this point we have a real, non-negative dentry.
+	 */
+	perm = tux_permission(inode);
+
+	if ((perm < 0) || (!S_ISDIR(dentry->d_inode->i_mode)
+				&& !S_ISREG(dentry->d_inode->i_mode))) {
+		Dprintk("FAILED trusted dentry %p permission %d.\n", dentry, perm);
+		req->status = 403;
+		goto abort;
+	}
+	if ((req->lookup_dir != 2) && S_ISDIR(dentry->d_inode->i_mode)) {
+		if (req->lookup_dir || (req->objectname_len +
+				 sizeof(INDEX) >= MAX_OBJECTNAME_LEN)) {
+			req->status = 403;
+			goto abort;
+		}
+		if (req->objectname_len && (req->objectname[req->objectname_len-1] != '/')) {
+			dput(dentry);
+			mntput(mnt);
+			req->lookup_dir = 0;
+			return 2;
+		}
+		memcpy(req->objectname + req->objectname_len,
+						INDEX, sizeof(INDEX));
+		req->objectname_len += sizeof(INDEX)-1;
+		req->lookup_dir = 1;
+		dput(dentry);
+		mntput(mnt);
+		mnt = NULL;
+		dentry = NULL;
+		goto repeat_lookup;
+	}
+	if (tux_max_object_size && (inode->i_size > tux_max_object_size)) {
+		TDprintk("too big object, %Ld bytes.\n", inode->i_size);
+		req->status = 403;
+		goto abort;
+	}
+	req->total_file_len = inode->i_size;
+	req->mtime = inode->i_mtime.tv_sec;
+
+	{
+		loff_t num = req->total_file_len;
+		int nr_digits = 0;
+		unsigned long modulo;
+		char * etag_p = req->etag;
+		char digits [30];
+
+		do {
+			modulo = do_div(num, 10);
+			digits[nr_digits++] = '0' + modulo;
+		} while (num);
+
+		req->lendigits = nr_digits;
+		req->etaglen = nr_digits;
+
+		while (nr_digits)
+			*etag_p++ = digits[--nr_digits];
+
+		*etag_p++ = '-';
+		num = req->mtime;
+		nr_digits = 0;
+
+		do {
+			digits[nr_digits++] = 'a' + num % 16;
+				num /= 16;
+		} while (num);
+		req->etaglen += nr_digits+1;
+		while (nr_digits)
+			*etag_p++ = digits[--nr_digits];
+		*etag_p = 0;
+	}
+
+	if ((req->if_none_match_len >= req->etaglen) && (abs(not_modified) == 1)) {
+
+		char * etag_p = req->etag;
+		const char * match_p = req->if_none_match_str;
+		int pos = req->etaglen - 1;
+		int matchpos = req->etaglen - 1;
+
+		do {
+			while (etag_p[matchpos--] == match_p[pos--])
+				if (matchpos < 0)
+					break;
+			if (matchpos < 0)
+				pos = req->if_none_match_len;
+			else {
+				if (match_p[pos+1] == ',')
+					pos += req->etaglen + 2;
+				else
+					pos += req->etaglen-matchpos;
+				matchpos = req->etaglen - 1;
+			}
+		} while (pos < req->if_none_match_len);
+
+		if (matchpos < 0) {
+			not_modified = 1;
+			TDprintk("Etag matched.\n");
+		} else
+			not_modified = 0;
+	}
+
+        if ((req->if_modified_since_len >= 24) && (abs(not_modified) == 1)) {
+                if (parse_time(req->if_modified_since_str, req->if_modified_since_len) >= req->mtime ) {
+			not_modified = 1;
+                        Dprintk("Last-Modified matched.\n");
+                } else
+			not_modified = 0;
+        }
+
+	if (not_modified == 1) {
+		req->status = 304;
+		goto abort;
+	}
+
+	Dprintk("looked up cached dentry %p, (count %d.)\n", dentry, dentry ? atomic_read(&dentry->d_count) : -1 );
+
+	url_hist_hit(req->total_file_len);
+out:
+	install_req_dentry(req, dentry, mnt);
+	req->lookup_dir = 0;
+	return 0;
+
+cachemiss:
+	return 1;
+
+abort:
+	if (dentry) {
+		if (!IS_ERR(dentry))
+			dput(dentry);
+		dentry = NULL;
+	}
+	if (mnt) {
+		if (!IS_ERR(mnt))
+			mntput(mnt);
+		mnt = NULL;
+	}
+#ifdef CONFIG_TUX_DEBUG
+	if (!not_modified) {
+		TDprintk("req %p has lookup errors!\n", req);
+		if (tux_TDprintk)
+			print_req(req);
+	}
+#endif
+	req_err(req);
+	goto out;
+}
+
+int handle_gzip_req (tux_req_t *req, unsigned int flags)
+{
+	char *curr = req->objectname + req->objectname_len;
+	struct dentry *dentry;
+	struct vfsmount *mnt = NULL;
+	struct inode *inode, *orig_inode;
+	loff_t size, orig_size;
+
+	*curr++ = '.';
+	*curr++ = 'g';
+	*curr++ = 'z';
+	*curr++ = 0;
+	req->objectname_len += 3;
+
+	dentry = tux_lookup(req, req->objectname, flags, &mnt);
+
+	req->objectname_len -= 3;
+	req->objectname[req->objectname_len] = 0;
+
+	if (!dentry)
+		return 0;
+	if (IS_ERR(dentry)) {
+		if (PTR_ERR(dentry) == -EWOULDBLOCKIO) {
+			release_req_dentry(req);
+			return 1;
+		}
+		return 0;
+	}
+
+	inode = dentry->d_inode;
+	size = inode->i_size;
+	orig_inode = req->dentry->d_inode;
+	orig_size = orig_inode->i_size;
+
+	if (!tux_permission(inode)
+			&& (size < orig_size)
+			&& (inode->i_mtime.tv_sec >= orig_inode->i_mtime.tv_sec)) {
+
+		release_req_dentry(req);
+		install_req_dentry(req, dentry, mnt);
+		req->total_file_len = req->output_len = size;
+		Dprintk("content WILL be gzipped!\n");
+		req->content_gzipped = 1;
+	} else {
+		dput(dentry);
+		mntput(mnt);
+	}
+
+	return 0;
+}
+
+static DEFINE_SPINLOCK(mimetypes_lock);
+
+static LIST_HEAD(mimetypes_head);
+
+static mimetype_t default_mimetype = { .type = "text/plain", .type_len = 10, .expire_str = "", .expire_str_len = 0 };
+
+#define MAX_MIMETYPE_LEN 128
+#define MAX_CACHE_CONTROL_AGE_LEN 30
+
+void add_mimetype (char *new_ext, char *new_type, char *new_expire)
+{
+	int type_len = strlen(new_type);
+	int ext_len = strlen(new_ext);
+	int expire_len = strlen(new_expire);
+	mimetype_t *mime;
+	char *ext, *type, *expire;
+
+        if (type_len > MAX_MIMETYPE_LEN)
+                type_len = MAX_MIMETYPE_LEN;
+        if (ext_len > MAX_URI_LEN)
+                ext_len = MAX_URI_LEN;
+        if (expire_len > MAX_CACHE_CONTROL_AGE_LEN)
+                expire_len = MAX_CACHE_CONTROL_AGE_LEN;
+
+	mime = tux_kmalloc(sizeof(*mime));
+	memset(mime, 0, sizeof(*mime));
+	ext = tux_kmalloc(ext_len + 1);
+	type = tux_kmalloc(type_len + 1);
+	expire = tux_kmalloc(expire_len + 1);
+
+	strncpy(ext, new_ext, ext_len);
+	strncpy(type, new_type, type_len);
+	strncpy(expire, new_expire, expire_len);
+
+	// in case one of the above parameters was too long :
+
+	ext[ext_len] = '\0';
+	type[type_len] = '\0';
+	expire[expire_len] = '\0';
+
+	mime->ext = ext;
+	mime->ext_len = ext_len;
+
+	mime->type = type;
+	mime->type_len = type_len;
+
+	mime->expire_str = expire;
+	mime->expire_str_len = expire_len;
+
+	mime->special = NORMAL_MIME_TYPE;
+	if (!strcmp(type, "TUX/redirect"))
+		mime->special = MIME_TYPE_REDIRECT;
+	if (!strcmp(type, "TUX/CGI"))
+		mime->special = MIME_TYPE_CGI;
+	if (!strcmp(type, "TUX/module"))
+		mime->special = MIME_TYPE_MODULE;
+
+	spin_lock(&mimetypes_lock);
+	list_add(&mime->list, &mimetypes_head);
+	spin_unlock(&mimetypes_lock);
+}
+
+static inline int ext_matches (char *file, int len, char *ext, int extlen)
+{
+	int i;
+	char *tmp = file + len-1;
+	char *tmp2 = ext + extlen-1;
+
+	if (len < extlen)
+		return 0;
+
+	for (i = 0; i < extlen; i++) {
+		if (*tmp != *tmp2)
+			return 0;
+		tmp--;
+		tmp2--;
+	}
+	return 1;
+}
+
+/*
+ * Overhead is not a problem, we cache the MIME type
+ * in the dentry.
+ */
+static mimetype_t * lookup_mimetype (tux_req_t *req)
+{
+	char *objectname = req->objectname;
+	int len = req->objectname_len;
+	mimetype_t *mime = NULL;
+	struct list_head *head, *tmp, *tmp1, *tmp2, *tmp3;
+
+	if (!memchr(objectname, '.', len))
+		goto out;
+
+	spin_lock(&mimetypes_lock);
+	head = &mimetypes_head;
+	tmp = head->next;
+
+	while (tmp != head) {
+		mime = list_entry(tmp, mimetype_t, list);
+		if (ext_matches(objectname, len, mime->ext, mime->ext_len)) {
+			/*
+			 * Percolate often-used mimetypes up:
+			 */
+			if (tmp->prev != &mimetypes_head) {
+				tmp1 = tmp;
+				tmp2 = tmp->prev;
+				tmp3 = tmp->prev->prev;
+				list_del(tmp1);
+				list_del(tmp2);
+				list_add(tmp, tmp3);
+				list_add(tmp2, tmp);
+			}
+			break;
+		} else
+			mime = NULL;
+		tmp = tmp->next;
+	}
+	spin_unlock(&mimetypes_lock);
+
+out:
+	if (!mime)
+		mime = &default_mimetype;
+	return mime;
+}
+
+void free_mimetypes (void)
+{
+	struct list_head *head, *tmp, *next;
+	mimetype_t *mime;
+
+	spin_lock(&mimetypes_lock);
+	head = &mimetypes_head;
+	tmp = head->next;
+
+	while (tmp != head) {
+		next = tmp->next;
+		mime = list_entry(tmp, mimetype_t, list);
+		list_del(tmp);
+
+		kfree(mime->ext);
+		mime->ext = NULL;
+		kfree(mime->type);
+		mime->type = NULL;
+		kfree(mime);
+
+		tmp = next;
+	}
+	spin_unlock(&mimetypes_lock);
+}
+
+/*
+ * Various constant HTTP responses:
+ */
+
+static const char forbidden[] =
+	"HTTP/1.1 403 Forbidden\r\n"
+	"Connection: Keep-Alive\r\n" \
+	"Content-Length: 24\r\n\r\n"
+	"<HTML> Forbidden </HTML>";
+
+static const char not_found[] =
+	"HTTP/1.1 404 Not Found\r\n"
+	"Connection: Keep-Alive\r\n" \
+	"Content-Length: 29\r\n\r\n"
+	"<HTML> Page Not Found </HTML>";
+
+#define NOTMODIFIED_1 \
+	"HTTP/1.1 304 Not Modified\r\n" \
+	"Connection: Keep-Alive\r\n" \
+	"Date: "
+
+#define NOTMODIFIED_1_LEN (sizeof(NOTMODIFIED_1) - 1)
+
+#define NOTMODIFIED_2 \
+	"\r\nETag: \""
+
+#define NOTMODIFIED_2_LEN (sizeof(NOTMODIFIED_2) - 1)
+
+#define NOTMODIFIED_3 \
+	"\"\r\n\r\n"
+
+#define NOTMODIFIED_3_LEN (sizeof(NOTMODIFIED_3) - 1)
+
+#define REDIRECT_1 \
+	"HTTP/1.1 301 Moved Permanently\r\n" \
+	"Location: http://"
+
+#define REDIRECT_1_LEN (sizeof(REDIRECT_1) - 1)
+
+#define REDIRECT_2 \
+	"/\r\nContent-Length: 36\r\n" \
+	"Connection: Keep-Alive\r\n" \
+	"Content-Type: text/html\r\n\r\n" \
+	"<HTML> 301 Moved Permanently </HTML>"
+
+#define REDIRECT_2_LEN (sizeof(REDIRECT_2) - 1)
+
+void send_async_err_forbidden (tux_req_t *req)
+{
+	send_async_message(req, forbidden, 403, 1);
+}
+
+void send_async_err_not_found (tux_req_t *req)
+{
+	send_async_message(req, not_found, 404, 1);
+}
+
+static void send_ret_notmodified (tux_req_t *req)
+{
+	char *buf;
+	int size;
+
+	size = NOTMODIFIED_1_LEN + DATE_LEN - 1 + NOTMODIFIED_2_LEN + req->etaglen + NOTMODIFIED_3_LEN;
+	buf = get_abuf(req, size);
+	memcpy(buf, NOTMODIFIED_1, NOTMODIFIED_1_LEN);
+	buf += NOTMODIFIED_1_LEN;
+	memcpy(buf, tux_date, DATE_LEN-1);
+	buf += DATE_LEN-1;
+	memcpy(buf, NOTMODIFIED_2, NOTMODIFIED_2_LEN);
+	buf += NOTMODIFIED_2_LEN;
+	memcpy(buf, &req->etag, req->etaglen);
+	buf += req->etaglen;
+	memcpy(buf, NOTMODIFIED_3, NOTMODIFIED_3_LEN);
+	buf += NOTMODIFIED_3_LEN;
+
+	req->status = 304;
+	send_abuf(req, size, MSG_DONTWAIT);
+	add_req_to_workqueue(req);
+}
+
+static void send_ret_redirect (tux_req_t *req, int cachemiss)
+{
+	char *buf;
+	unsigned int size;
+	unsigned int uts_len = 0;
+
+	size = REDIRECT_1_LEN;
+	if (req->host_len)
+		size += req->host_len;
+	else {
+		down_read(&uts_sem);
+		uts_len = strlen(system_utsname.nodename);
+		size += uts_len;
+	}
+	if (req->objectname[0] != '/')
+		size++;
+	size += req->objectname_len;
+	size += REDIRECT_2_LEN;
+
+	if (size > PAGE_SIZE) {
+		req->error = TUX_ERROR_CONN_CLOSE;
+		zap_request(req, cachemiss);
+		return;
+	}
+
+	buf = get_abuf(req, size);
+
+	memcpy(buf, REDIRECT_1, REDIRECT_1_LEN);
+	buf += REDIRECT_1_LEN;
+
+	Dprintk("req %p, host: %s, host_len: %d.\n", req, req->host, req->host_len);
+	if (req->host_len) {
+		memcpy(buf, req->host, req->host_len);
+		buf += req->host_len;
+	} else {
+		memcpy(buf, system_utsname.nodename, uts_len);
+		up_read(&uts_sem);
+		buf += uts_len;
+	}
+	if (req->objectname[0] != '/') {
+		buf[0] = '/';
+		buf++;
+	}
+
+	memcpy(buf, req->objectname, req->objectname_len);
+	buf += req->objectname_len;
+
+	memcpy(buf, REDIRECT_2, REDIRECT_2_LEN);
+	buf += REDIRECT_2_LEN;
+
+	req->status = 301;
+	send_abuf(req, size, MSG_DONTWAIT);
+	add_req_to_workqueue(req);
+}
+
+static void http_got_request (tux_req_t *req)
+{
+	req->host[0] = 0;
+	req->host_len = 0;
+	add_tux_atom(req, parse_request);
+	add_req_to_workqueue(req);
+}
+
+
+tux_attribute_t * lookup_tux_attribute (tux_req_t *req)
+{
+	tux_attribute_t *attr;
+	struct inode *inode;
+	mimetype_t *mime;
+
+	attr = tux_kmalloc(sizeof(*attr));
+	memset(attr, 0, sizeof(*attr));
+
+	mime = lookup_mimetype(req);
+
+	inode = req->dentry->d_inode;
+	if (!inode->i_uid && !inode->i_gid) {
+		if (mime->special == MIME_TYPE_MODULE) {
+			attr->tcapi = lookup_tuxmodule(req->objectname);
+			if (!attr->tcapi) {
+				req_err(req);
+				mime = &default_mimetype;
+			}
+		}
+	} else {
+		if (mime->special && (mime->special != MIME_TYPE_REDIRECT))
+			mime = &default_mimetype;
+	}
+	attr->mime = mime;
+
+	return attr;
+}
+
+static void handle_range(tux_req_t *req)
+{
+	if (req->if_range_len) {
+		time_t range_time;
+
+		range_time = parse_time(req->if_range_str, req->if_range_len);
+
+		/*
+		 * If the file is newer then we send the whole file.
+		 */
+		if (range_time < req->mtime )
+			goto out_no_range;
+	}
+	/* if no offset_end was specified then default to 'end of file': */
+	if (!req->offset_end)
+		req->offset_end = req->total_file_len;
+	/*
+	 * Sanity checks:
+	 *
+	 *  - is the range between 0...file_len-1 ?
+	 *  - is offset_end after offset_start?
+	 *
+	 * (note that offset_end is higher by 1)
+	 */
+	if ((req->offset_end > req->total_file_len) ||
+			(req->offset_start >= req->total_file_len) ||
+			(req->offset_end <= req->offset_start))
+		goto out_no_range;
+	/*
+	 * If the range is 0...file_len-1 then send the whole file:
+	 */
+	if (!req->offset_start && (req->offset_end == req->total_file_len))
+		goto out_no_range;
+
+	/* ok, the range is valid, use it: */
+
+	req->output_len = req->offset_end - req->offset_start;
+	req->in_file->f_pos = req->offset_start;
+	return;
+
+out_no_range:
+	req->offset_start = 0;
+	req->offset_end = 0;
+}
+
+static void http_pre_header (tux_req_t *req, int push);
+static void http_post_header (tux_req_t *req, int cachemiss);
+static void http_send_body (tux_req_t *req, int cachemiss);
+
+#define DIRLIST_HEAD_1 "\
+<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\
+<HTML><HEAD><TITLE>Index of %s</TITLE></HEAD><BODY>\
+<H1>Index of %s </H1><PRE><HR>\n%s"
+
+#define DIRLIST_HEAD_2 "\
+<IMG SRC=\"/icons/back.gif\"ALT=\"[DIR]\"> <A HREF=\"../\">Parent Directory</A>\n"
+
+#define DIRLIST_HEAD_SIZE (sizeof(DIRLIST_HEAD_1) + sizeof(DIRLIST_HEAD_2))
+
+static void http_dirlist_head (tux_req_t *req, int cachemiss)
+{
+	char *buf1, *buf2, *path;
+	int len;
+
+	buf1 = (char *)__get_free_page(GFP_KERNEL);
+	buf2 = (char *)__get_free_page(GFP_KERNEL);
+	if (!buf1 || !buf2)
+		goto out;
+	path = tux_print_path(req, req->dentry, req->mnt, buf1, PAGE_SIZE);
+	if (path[0] == '/' && path[1] == '/' && !path[3])
+		path = "/";
+	if (2*strlen(path) + DIRLIST_HEAD_SIZE >= PAGE_SIZE)
+		goto out;
+	len = sprintf(buf2, DIRLIST_HEAD_1, path, path, req->dentry == req->docroot_dentry ? "" : DIRLIST_HEAD_2);
+	__send_async_message(req, buf2, 200, len, 0);
+
+out:
+	if (buf1)
+		free_page((unsigned long)buf1);
+	if (buf2)
+		free_page((unsigned long)buf2);
+}
+
+#define DIRLIST_TAIL "\
+</PRE><HR><ADDRESS><IMG SRC=\"/icons/tuxlogo.gif\"ALIGN=\"MIDDLE\"ALT=\"[TUX]\">Powered by Linux/TUX 3.0</ADDRESS>\n</BODY></HTML>"
+
+static void http_dirlist_tail (tux_req_t *req, int cachemiss)
+{
+	__send_async_message(req, DIRLIST_TAIL, 200, sizeof(DIRLIST_TAIL)-1, 1);
+}
+
+static void http_dirlist (tux_req_t *req, int cachemiss)
+{
+	int head = (req->method == METHOD_HEAD);
+
+	req->lookup_dir = 3;
+	clear_keepalive(req);
+	if (!head) {
+		add_tux_atom(req, http_dirlist_tail);
+		add_tux_atom(req, list_directory);
+		add_tux_atom(req, http_dirlist_head);
+	}
+	http_pre_header(req, head);
+	add_req_to_workqueue(req);
+}
+
+static char *host_path_hash(tux_req_t *req, char *tmp)
+{
+	if (req->host_len < 2)
+		return NULL;
+
+	switch (mass_hosting_hash) {
+		default:
+		case 0:
+			return req->host;
+		case 1:
+
+			// www.ABCDEFG.com => A/ABCDEFG.com
+
+			tmp[0] = req->host[0];
+			tmp[1] = '/';
+			memcpy(tmp + 2, req->host, req->host_len);
+			tmp[req->host_len + 2] = 0;
+
+			return tmp;
+		case 2:
+			// www.ABCDEFG.com => A/AB/ABCDEFG.com
+
+			tmp[0] = req->host[0];
+			tmp[1] = '/';
+			tmp[2] = req->host[0];
+			tmp[3] = req->host[1];
+			tmp[4] = '/';
+			memcpy(tmp + 5, req->host, req->host_len);
+			tmp[req->host_len + 5] = 0;
+
+			return tmp;
+		case 3:
+			// www.ABCDEFG.com => A/AB/ABC/ABCDEFG.com
+
+			tmp[0] = req->host[0];
+			tmp[1] = '/';
+			tmp[2] = req->host[0];
+			tmp[3] = req->host[1];
+			tmp[4] = '/';
+			tmp[5] = req->host[0];
+			tmp[6] = req->host[1];
+			tmp[7] = req->host[2];
+			tmp[8] = '/';
+			memcpy(tmp + 9, req->host, req->host_len);
+			tmp[req->host_len + 9] = 0;
+
+			return tmp;
+	}
+}
+
+static struct dentry * vhost_lookup (tux_req_t *req, struct nameidata* base, struct vfsmount **mnt)
+{
+	struct dentry *dentry = NULL;
+	// 255.255.255.255
+	char ip [3+1+3+1+3+1+3 + 2];
+
+	if (req->virtual >= TUX_VHOST_IP) {
+		sprintf(ip, "%d.%d.%d.%d",
+				NIPQUAD(inet_sk(req->sock->sk)->rcv_saddr));
+		dentry = __tux_lookup (req, ip, base, mnt);
+		if (!dentry || IS_ERR(dentry)) {
+			if (PTR_ERR(dentry) == -EWOULDBLOCKIO)
+				return dentry;
+			base->dentry = dget(req->proto->main_docroot.dentry);
+			base->mnt = mntget(req->proto->main_docroot.mnt);
+			goto lookup_default;
+		}
+		if (req->virtual == TUX_VHOST_IP)
+			goto done;
+
+		// fall through in mixed mode:
+	}
+
+	if (!req->host_len) {
+lookup_default:
+		*mnt = NULL;
+		dentry = __tux_lookup (req, tux_default_vhost, base, mnt);
+	} else {
+		char tmp [MAX_HOST_LEN*2];
+		char *host_path;
+
+		host_path = host_path_hash(req, tmp);
+		Dprintk("host path hash returned: {%s}\n", host_path);
+
+		dentry = NULL;
+		if (host_path) {
+			*mnt = NULL;
+			dentry = __tux_lookup (req, host_path, base, mnt);
+		}
+		if (!dentry || IS_ERR(dentry)) {
+			if (PTR_ERR(dentry) == -EWOULDBLOCKIO)
+				return dentry;
+			base->dentry = dget(req->proto->main_docroot.dentry);
+			base->mnt = mntget(req->proto->main_docroot.mnt);
+			if (req->virtual >= TUX_VHOST_IP) {
+				*mnt = NULL;
+				dentry = __tux_lookup (req, ip, base, mnt);
+				if (!dentry || IS_ERR(dentry)) {
+					if (PTR_ERR(dentry) == -EWOULDBLOCKIO)
+						return dentry;
+					base->dentry = dget(req->proto->main_docroot.dentry);
+					base->mnt = mntget(req->proto->main_docroot.mnt);
+				}
+			}
+			goto lookup_default;
+		}
+	}
+done:
+	return dentry;
+}
+
+static void http_lookup_vhost (tux_req_t *req, int cachemiss)
+{
+	struct dentry *dentry;
+	struct nameidata base = { };
+	struct vfsmount *mnt = NULL;
+	unsigned int flag = cachemiss ? 0 : LOOKUP_ATOMIC;
+
+	Dprintk("http_lookup_vhost(%p, %d, virtual: %d, host: %s (%d).)\n", req, flag, req->virtual, req->host, req->host_len);
+
+	base.flags = LOOKUP_FOLLOW|flag;
+	base.last_type = LAST_ROOT;
+	base.dentry = dget(req->proto->main_docroot.dentry);
+	base.mnt = mntget(req->proto->main_docroot.mnt);
+
+	dentry = vhost_lookup(req, &base, &mnt);
+
+	Dprintk("looked up dentry %p.\n", dentry);
+
+	if (dentry && !IS_ERR(dentry) && !dentry->d_inode)
+		TUX_BUG();
+
+	if (!dentry || IS_ERR(dentry)) {
+		if (PTR_ERR(dentry) == -EWOULDBLOCKIO) {
+			add_tux_atom(req, http_lookup_vhost);
+			queue_cachemiss(req);
+			return;
+		}
+		goto abort;
+	}
+
+	req->docroot_dentry = dentry;
+	req->docroot_mnt = mnt;
+
+	add_tux_atom(req, http_process_message);
+	add_req_to_workqueue(req);
+	return;
+abort:
+	if (dentry) {
+		if (!IS_ERR(dentry))
+			dput(dentry);
+		dentry = NULL;
+	}
+	if (mnt) {
+		if (!IS_ERR(mnt))
+			mntput(mnt);
+		mnt = NULL;
+	}
+	req_err(req);
+	add_req_to_workqueue(req);
+}
+
+static void http_process_message (tux_req_t *req, int cachemiss)
+{
+	tux_attribute_t *attr;
+	int missed;
+	unsigned int lookup_flag = cachemiss ? 0 : LOOKUP_ATOMIC;
+
+	Dprintk("handling req %p, cachemiss: %d.\n", req, cachemiss);
+
+	/*
+	 * URL redirection support - redirect all valid requests
+	 * to the first userspace module.
+	 */
+	if (tux_all_userspace) {
+		tcapi_template_t *tcapi = get_first_usermodule();
+		if (tcapi) {
+			req->usermode = 1;
+			req->usermodule_idx = tcapi->userspace_id;
+			goto usermode;
+		}
+	}
+	missed = lookup_url(req, lookup_flag);
+	if (missed == 2) {
+		if (req->query_str) {
+			req->error = TUX_ERROR_REDIRECT;
+			goto error;
+		}
+		send_ret_redirect(req, cachemiss);
+		return;
+	}
+	if (req->error)
+		goto error;
+	if (missed) {
+cachemiss:
+		if (cachemiss)
+			TUX_BUG();
+		Dprintk("uncached request.\n");
+		INC_STAT(static_lookup_cachemisses);
+		if (req->dentry)
+			TUX_BUG();
+		add_tux_atom(req, http_process_message);
+		queue_cachemiss(req);
+		return;
+	}
+	/*
+	 * HTML directory indexing.
+	 */
+	if (S_ISDIR(req->dentry->d_inode->i_mode))
+		return http_dirlist(req, cachemiss);
+	if (!S_ISREG(req->dentry->d_inode->i_mode))
+		TUX_BUG();
+
+
+	attr = req->dentry->d_extra_attributes;
+	if (!attr) {
+		attr = lookup_tux_attribute(req);
+		if (!attr)
+			TUX_BUG();
+		req->dentry->d_extra_attributes = attr;
+	}
+	if (attr->mime)
+		Dprintk("using MIME type %s:%s, %d.\n", attr->mime->type, attr->mime->ext, attr->mime->special);
+	if (attr->tcapi) {
+		req->usermode = 1;
+		req->usermodule_idx = attr->tcapi->userspace_id;
+		if (req->module_dentry)
+			TUX_BUG();
+		req->module_dentry = dget(req->dentry);
+		release_req_dentry(req);
+		goto usermode;
+	}
+
+	switch (attr->mime->special) {
+		case MIME_TYPE_MODULE:
+			req->usermode = 1;
+			goto usermode;
+
+		case MIME_TYPE_REDIRECT:
+			req->error = TUX_ERROR_REDIRECT;
+			goto error;
+
+		case MIME_TYPE_CGI:
+#ifdef CONFIG_TUX_EXTCGI
+			Dprintk("CGI request %p.\n", req);
+			query_extcgi(req);
+			return;
+#endif
+
+		default:
+			if (req->query_str) {
+				req->error = TUX_ERROR_REDIRECT;
+				goto error;
+			}
+	}
+	req->attr = attr;
+	switch (req->method) {
+		case METHOD_GET:
+		case METHOD_HEAD:
+			break;
+		default:
+			req->error = TUX_ERROR_REDIRECT;
+			goto error;
+	}
+	if (req->usermode)
+		TUX_BUG();
+
+	req->output_len = req->total_file_len;
+	/*
+	 * Do range calculations.
+	 */
+	if (req->offset_end || req->offset_start)
+		handle_range(req);
+
+	if (req->may_send_gzip && !req->offset_start && !req->offset_end) {
+		if (handle_gzip_req(req, lookup_flag))
+			goto cachemiss;
+		if ((tux_compression >= 2) && !req->content_gzipped)
+			req->content_gzipped = 2;
+	}
+	if (req->parsed_len)
+		trunc_headers(req);
+
+	if (req->error)
+		goto error;
+
+	add_tux_atom(req, http_send_body);
+	add_tux_atom(req, http_post_header);
+
+	http_pre_header(req, req->method == METHOD_HEAD);
+
+	add_req_to_workqueue(req);
+	return;
+
+error:
+	if (req->error)
+		zap_request(req, cachemiss);
+	return;
+
+usermode:
+	add_req_to_workqueue(req);
+}
+
+static void http_post_header (tux_req_t *req, int cachemiss)
+{
+#ifdef CONFIG_TUX_DEBUG
+	req->bytes_expected = req->output_len;
+#endif
+	req->bytes_sent = 0; // data comes now.
+
+	add_req_to_workqueue(req);
+}
+
+static void http_send_body (tux_req_t *req, int cachemiss)
+{
+	int ret;
+
+	Dprintk("SEND req %p <%p> (sock %p, sk %p) (keepalive: %d, status: %d)\n", req, __builtin_return_address(0), req->sock, req->sock->sk, req->keep_alive, req->status);
+
+	SET_TIMESTAMP(req->output_timestamp);
+
+	if (req->error) {
+#ifdef CONFIG_TUX_DEBUG
+		req->bytes_expected = 0;
+#endif
+		req->in_file->f_pos = 0;
+		/*
+		 * We are in the middle of a file transfer,
+		 * zap it immediately:
+		 */
+		TDprintk("req->error = TUX_ERROR_CONN_CLOSE.\n");
+		req->error = TUX_ERROR_CONN_CLOSE;
+		zap_request(req, cachemiss);
+		return;
+	}
+
+repeat:
+	ret = 0;
+	if (!req->status)
+		req->status = 200;
+	if (req->method != METHOD_HEAD) {
+		ret = generic_send_file(req, req->sock, cachemiss);
+		Dprintk("body send-file returned: %d.\n", ret);
+	} else {
+#ifdef CONFIG_TUX_DEBUG
+		req->bytes_expected = 0;
+#endif
+	}
+
+	switch (ret) {
+		case -5:
+			add_tux_atom(req, http_send_body);
+			output_timeout(req);
+			break;
+		case -4:
+			add_tux_atom(req, http_send_body);
+			if (add_output_space_event(req, req->sock)) {
+				del_tux_atom(req);
+				goto repeat;
+			}
+			break;
+		case -3:
+			INC_STAT(static_sendfile_cachemisses);
+			add_tux_atom(req, http_send_body);
+			queue_cachemiss(req);
+			break;
+		case -1:
+			break;
+		default:
+			req->in_file->f_pos = 0;
+			add_req_to_workqueue(req);
+			break;
+	}
+}
+
+#define DEFAULT_DATE "Wed, 01 Jan 1970 00:00:01 GMT"
+
+char tux_date [DATE_LEN] = DEFAULT_DATE;
+
+/*
+ * HTTP header
+ */
+
+#define HEADER_PART1A \
+		"HTTP/1.1 200 OK\r\n" \
+		"Content-Type: "
+
+#define HEADER_PART1B \
+		"HTTP/1.1 200 OK"
+
+#define HEADER_PART1AP \
+		"HTTP/1.1 206 Partial Content\r\n" \
+		"Content-Type: "
+
+#define HEADER_PART1BP \
+		"HTTP/1.1 206 Partial Content"
+
+#define HEADER_PART1C \
+		"HTTP/1.1 404 Page Not Found\r\n" \
+		"Content-Type: "
+
+#define HEADER_PART1D \
+		"HTTP/1.1 200 OK\r\n" \
+		"Content-Type: text/html\r\n" \
+		"Connection: close\r\n"
+
+#define HEADER_PART2_keepalive "\r\nConnection: Keep-Alive\r\nDate: "
+
+#define HEADER_PART2_close "\r\nConnection: close\r\nDate: "
+
+#define HEADER_PART2_none "\r\nDate: "
+
+// date "%s"
+
+#define HEADER_PART3A "\r\nContent-Encoding: gzip"
+#define HEADER_PART3BX "\r\nContent-Length: "
+
+/*
+ * Please acknowledge our hard work by not changing this define, or
+ * at least please acknowledge us by leaving "TUX/2.0 (Linux)" in
+ * the ID string. Thanks! :-)
+ */
+#define HEADER_PART3BY "\r\nServer: TUX/2.0 (Linux)\r\nContent-Length: "
+#define HEADER_PART3C "\r\nETag: \""
+#define HEADER_PART3ACC "\r\nAccept-Ranges: bytes"
+#define HEADER_PART3L "\r\nLast-Modified: "
+#define HEADER_PART3P "\r\nContent-Range: bytes "
+#define HEADER_PART3CA "\r\nCache-Control: max-age="
+#define HEADER_PART4 "\r\n\r\n"
+
+#define MAX_OUT_HEADER_LEN (sizeof(HEADER_PART1AP) + MAX_MIMETYPE_LEN + \
+		sizeof(HEADER_PART2_keepalive) + DATE_LEN + \
+		sizeof(HEADER_PART3A) + sizeof(HEADER_PART3BY) + \
+		12 + sizeof(HEADER_PART3C) + 21 + sizeof(HEADER_PART3L) + \
+		sizeof(HEADER_PART3P) + 32 + \
+		DATE_LEN + sizeof(HEADER_PART4) + sizeof(tux_extra_html_header) \
+		+ sizeof(HEADER_PART3CA) + MAX_CACHE_CONTROL_AGE_LEN)
+
+static void http_pre_header (tux_req_t *req, int head)
+{
+	int partial = req->offset_start | req->offset_end;
+	unsigned long flags;
+	char *buf, *curr;
+	mimetype_t *mime = NULL;
+	int size;
+
+
+	if (MAX_OUT_HEADER_LEN > PAGE_SIZE)
+		TUX_BUG();
+	if ((req->attr && req->attr->tcapi) || req->usermode)
+		TUX_BUG();
+
+#define COPY_STATIC_PART(nr,curr)					\
+	do {	\
+		memcpy(curr, HEADER_PART##nr, sizeof(HEADER_PART##nr)-1); \
+		curr += sizeof(HEADER_PART##nr)-1;			\
+	} while (0)
+
+	buf = curr = get_abuf(req, MAX_OUT_HEADER_LEN);
+
+	if (req->lookup_dir) {
+		COPY_STATIC_PART(1D, curr);
+		goto dir_next;
+	}
+	mime = req->attr->mime;
+	if (!mime)
+		TUX_BUG();
+
+	if (req->status == 404) {
+		COPY_STATIC_PART(1C, curr);
+		memcpy(curr, mime->type, mime->type_len);
+		curr += mime->type_len;
+	} else {
+		if (tux_noid && (mime == &default_mimetype)) {
+			if (partial)
+				COPY_STATIC_PART(1BP, curr);
+			else
+				COPY_STATIC_PART(1B, curr);
+		} else {
+			if (partial)
+				COPY_STATIC_PART(1AP, curr);
+			else
+				COPY_STATIC_PART(1A, curr);
+			memcpy(curr, mime->type, mime->type_len);
+			curr += mime->type_len;
+		}
+	}
+
+	if (tux_generate_cache_control && mime->expire_str_len) {
+		COPY_STATIC_PART(3CA, curr);
+		memcpy(curr, mime->expire_str, mime->expire_str_len);
+		curr += mime->expire_str_len;
+	}
+
+	if (req->keep_alive /* && (req->version == HTTP_1_0) */)
+		COPY_STATIC_PART(2_keepalive, curr);
+	else if (!req->keep_alive && (req->version == HTTP_1_1))
+		COPY_STATIC_PART(2_close, curr);
+	else
+		// HTTP/1.0 default means close
+		COPY_STATIC_PART(2_none, curr);
+
+dir_next:
+	memcpy(curr, tux_date, DATE_LEN-1);
+	curr += DATE_LEN-1;
+
+	if (req->content_gzipped)
+		COPY_STATIC_PART(3A, curr);
+
+	/*
+	 * Content-Length:
+	 */
+	if (!req->lookup_dir) {
+		if (tux_noid)
+			COPY_STATIC_PART(3BX, curr);
+		else
+			COPY_STATIC_PART(3BY, curr);
+
+		if (partial)
+			curr += sprintf(curr, "%Ld", req->output_len);
+		else {
+			if (req->content_gzipped)
+				curr += sprintf(curr, "%Ld",
+							req->total_file_len);
+			else {
+				memcpy(curr, &req->etag, req->lendigits);
+				curr += req->lendigits;
+			}
+		}
+		if (tux_generate_etags && (req->status != 404)) {
+			COPY_STATIC_PART(3C, curr);
+			memcpy(curr, &req->etag, req->etaglen);
+			curr += req->etaglen;
+			curr[0] = '"';
+			curr++;
+		}
+		if (tux_generate_last_mod || tux_generate_etags)
+			COPY_STATIC_PART(3ACC, curr);
+	}
+        if (tux_generate_last_mod && (req->status != 404)) {
+                COPY_STATIC_PART(3L, curr);
+		last_mod_time(curr, req->mtime);
+		curr += DATE_LEN-1;
+        }
+	if (partial) {
+		COPY_STATIC_PART(3P, curr);
+		curr += sprintf(curr, "%Ld-%Ld/%Ld", req->offset_start,
+				req->offset_end-1, req->total_file_len);
+	}
+	COPY_STATIC_PART(4, curr);
+	/*
+	 * Possibly add an extra HTML header:
+	 */
+	if (tux_extra_html_header_size && mime && !strcmp(mime->type, "text/html")) {
+		unsigned int len = tux_extra_html_header_size;
+
+		memcpy(curr, tux_extra_html_header, len);
+		curr += len;
+	}
+
+	size = curr-buf;
+
+#ifdef CONFIG_TUX_DEBUG
+	*curr = 0;
+	Dprintk("{%s} [%d/%d]\n", buf, size, strlen(buf));
+#endif
+
+	flags = MSG_DONTWAIT;
+	if (!head)
+		flags |= MSG_MORE;
+	send_abuf(req, size, flags);
+}
+
+void http_illegal_request (tux_req_t *req, int cachemiss)
+{
+	if (req->status == 304)
+		send_ret_notmodified(req);
+	else {
+		if (req->status == 403)
+			send_async_err_forbidden(req);
+		else
+			send_async_err_not_found(req);
+	}
+}
+
+static int http_check_req_err (tux_req_t *req, int cachemiss)
+{
+	if ((req->sock->sk->sk_state <= TCP_SYN_RECV) &&
+		!tcp_sk(req->sock->sk)->urg_data)
+			return 0;
+	Dprintk("http_check_req_err(%p,%d): 1 (state: %d, urg: %d)\n",
+		req, cachemiss, req->sock->sk->sk_state,
+		tcp_sk(req->sock->sk)->urg_data);
+#ifdef CONFIG_TUX_DEBUG
+	req->bytes_expected = 0;
+#endif
+	req->in_file->f_pos = 0;
+	req->error = TUX_ERROR_CONN_CLOSE;
+	zap_request(req, cachemiss);
+
+	return 1;
+}
+
+#define COPY_STR(str) \
+	do { memcpy(tmp, str, sizeof(str)-1); \
+	tmp += sizeof(str)-1; } while (0)
+
+static char * http_print_dir_line (tux_req_t *req, char *tmp, char *d_name, int d_len, int d_type, struct dentry *dentry, struct inode *inode)
+{
+	int len, spaces;
+	loff_t size;
+
+	switch (d_type) {
+	case DT_DIR:
+		COPY_STR("<IMG SRC=\"/icons/dir.gif\" ALT=\"[DIR]\">");
+		break;
+	case DT_REG:
+		if ((d_len >= 3) &&
+			(d_name[d_len-3] == '.') &&
+			(d_name[d_len-2] == 'g') &&
+			(d_name[d_len-1] == 'z'))
+			COPY_STR("<IMG SRC=\"/icons/compressed.gif\" ALT=\"[   ]\">");
+		else
+		if ((d_len >= 4) &&
+			(d_name[d_len-4] == '.') &&
+			(d_name[d_len-3] == 't') &&
+			(d_name[d_len-2] == 'g') &&
+			(d_name[d_len-1] == 'z'))
+			COPY_STR("<IMG SRC=\"/icons/compressed.gif\" ALT=\"[   ]\">");
+		else
+		if ((d_len >= 4) &&
+			(d_name[d_len-4] == '.') &&
+			(d_name[d_len-3] == 't') &&
+			(d_name[d_len-2] == 'x') &&
+			(d_name[d_len-1] == 't'))
+			COPY_STR("<IMG SRC=\"/icons/text.gif\" ALT=\"[   ]\">");
+		else
+		if ((d_len >= 4) &&
+			(d_name[d_len-4] == '.') &&
+			(d_name[d_len-3] == 'b') &&
+			(d_name[d_len-2] == 'z') &&
+			(d_name[d_len-1] == '2'))
+			COPY_STR("<IMG SRC=\"/icons/compressed.gif\" ALT=\"[   ]\">");
+		else
+		if ((d_len >= 4) &&
+			(d_name[d_len-4] == '.') &&
+			(d_name[d_len-3] == 'z') &&
+			(d_name[d_len-2] == 'i') &&
+			(d_name[d_len-1] == 'p'))
+			COPY_STR("<IMG SRC=\"/icons/compressed.gif\" ALT=\"[   ]\">");
+		else
+			COPY_STR("<IMG SRC=\"/icons/file.gif\" ALT=\"[   ]\">");
+		break;
+	case DT_LNK:
+		COPY_STR("<IMG SRC=\"/icons/link.gif\" ALT=\"[LNK]\">");
+		break;
+	default:
+		if (tux_hide_unreadable)
+			goto out_dput;
+		COPY_STR("<IMG SRC=\"/icons/unknown.gif\" ALT=\"[   ]\">");
+		break;
+	}
+
+#define LIST_1 " <A HREF=\""
+#define LIST_2 "\">"
+#define LIST_2_DIR "/\">"
+#define LIST_3 "</A> "
+
+	COPY_STR(LIST_1);
+	memcpy(tmp, d_name, d_len);
+	tmp += d_len;
+	if (d_type == DT_DIR)
+		COPY_STR(LIST_2_DIR);
+	else
+		COPY_STR(LIST_2);
+	spaces = 0;
+	len = d_len;
+
+	if (len > 25)
+		len = 25;
+	memcpy(tmp, d_name, len);
+	tmp += len;
+	if (len != d_len) {
+		*tmp++ = '.';
+		*tmp++ = '.';
+	} else {
+		if (d_type == DT_DIR)
+			*tmp++ = '/';
+		else
+			spaces++;
+		spaces++;
+	}
+	COPY_STR(LIST_3);
+	while (spaces) {
+		*tmp++ = ' ';
+		spaces--;
+	}
+#define FILL 25
+	if (d_len < FILL) {
+		memset(tmp, ' ', FILL-d_len);
+		tmp += FILL-d_len;
+	}
+
+	tmp += time_unix2ls(inode->i_mtime.tv_sec, tmp);
+	*tmp++ = ' ';
+
+	if (d_type != DT_REG) {
+		COPY_STR("        - ");
+		goto out_size;
+	}
+	size = inode->i_size >> 10;
+	if (size < 1024) {
+		tmp += sprintf(tmp, "%8Lik ", size);
+		goto out_size;
+	}
+	size >>= 10;
+	if (size < 1024) {
+		tmp += sprintf(tmp, "%8LiM ", size);
+		goto out_size;
+	}
+	size >>= 10;
+	if (size < 1024) {
+		tmp += sprintf(tmp, "%8LiG ", size);
+		goto out_size;
+	}
+	size >>= 10;
+	if (size < 1024) {
+		tmp += sprintf(tmp, "%8LiT ", size);
+		goto out_size;
+	}
+	size >>= 10;
+	tmp += sprintf(tmp, "%8LiT ", size);
+
+out_size:
+	*tmp++ = '\n';
+	*tmp = 0;
+
+	return tmp;
+out_dput:
+	return NULL;
+}
+
+tux_proto_t tux_proto_http = {
+	.defer_accept = 1,
+	.can_redirect = 1,
+	.got_request = http_got_request,
+	.parse_message = parse_http_message,
+	.illegal_request = http_illegal_request,
+	.check_req_err = http_check_req_err,
+	.print_dir_line = http_print_dir_line,
+	.name = "http",
+};
+
Index: latest/net/tux/redirect.c
===================================================================
--- /dev/null
+++ latest/net/tux/redirect.c
@@ -0,0 +1,172 @@
+/*
+ * TUX - Integrated Application Protocols Layer and Object Cache
+ *
+ * Copyright (C) 2000, 2001, Ingo Molnar <mingo@redhat.com>
+ *
+ * redirect.c: redirect requests to other server sockets (such as Apache).
+ */
+
+#include <net/tux.h>
+#include <linux/module.h>
+#include <linux/init.h>
+
+/****************************************************************
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2, or (at your option)
+ *      any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ ****************************************************************/
+
+static void nop_destructor(struct request_sock *req)
+{
+}
+
+static struct request_sock_ops tux_req =
+{
+	.destructor = &nop_destructor,
+};
+
+static int redirect_sock (tux_req_t *req, const int port)
+{
+	struct socket *sock = req->sock;
+	struct request_sock *tcpreq;
+	struct sock *sk, *oldsk;
+	int err = -1;
+
+	/*
+	 * Look up (optional) listening user-space socket.
+	 */
+	local_bh_disable();
+	sk = inet_lookup_listener(&tcp_hashinfo, INADDR_ANY, port, 0);
+	/*
+	 * Look up localhost listeners as well.
+	 */
+	if (!sk) {
+		u32 daddr;
+		((unsigned char *)&daddr)[0] = 127;
+		((unsigned char *)&daddr)[1] = 0;
+		((unsigned char *)&daddr)[2] = 0;
+		((unsigned char *)&daddr)[3] = 1;
+		sk = inet_lookup_listener(&tcp_hashinfo, daddr, port, 0);
+	}
+	local_bh_enable();
+
+	/* No secondary server found */
+	if (!sk)
+		goto out;
+	if (sk->sk_family != AF_INET) {
+		sock_put(sk);
+		goto out;
+	}
+
+	/*
+	 * Requeue the 'old' socket as an accept-socket of
+	 * the listening socket. This way we can shuffle
+	 * a socket around. Since we've read the input data
+	 * via the non-destructive MSG_PEEK, the secondary
+	 * server can be used transparently.
+	 */
+	oldsk = sock->sk;
+	lock_sock(sk);
+
+	if (sk->sk_state != TCP_LISTEN)
+		goto out_unlock;
+
+	tcpreq = reqsk_alloc(&tux_req);
+	if (!tcpreq)
+		goto out_unlock;
+
+	unlink_tux_socket(req);
+
+	sock->sk = NULL;
+	sock->state = SS_UNCONNECTED;
+
+	write_lock_irq(&oldsk->sk_callback_lock);
+	oldsk->sk_socket = NULL;
+        oldsk->sk_sleep = NULL;
+	write_unlock_irq(&oldsk->sk_callback_lock);
+
+	tcp_sk(oldsk)->nonagle = 0;
+
+	inet_csk_reqsk_queue_add(sk, tcpreq, oldsk);
+
+	sk->sk_data_ready(sk, 0);
+
+	/*
+	 * It's now completely up to the secondary
+	 * server to handle this request.
+	 */
+	if (req->fd != -1) {
+		tux_close(req->fd);
+		req->fd = -1;
+	} else
+		sock_release(req->sock);
+	req->sock = NULL;
+	req->parsed_len = 0;
+	err = 0;
+	Dprintk("req %p redirected to secondary server!\n", req);
+
+out_unlock:
+	release_sock(sk);
+	sock_put(sk);
+out:
+	if (err)
+		Dprintk("NO secondary server for req %p!\n", req);
+	return err;
+}
+
+void redirect_request (tux_req_t *req, int cachemiss)
+{
+	if (tux_TDprintk && (req->status != 304)) {
+		TDprintk("trying to redirect req %p, req->error: %d, req->status: %d.\n", req, req->error, req->status);
+		print_req(req);
+	}
+
+	if (cachemiss)
+		TUX_BUG();
+	if (req->error == TUX_ERROR_CONN_CLOSE)
+		goto out_flush;
+	if (!req->sock)
+		TUX_BUG();
+
+	if (!req->status)
+		req->status = -1;
+	if (!req->proto->can_redirect || (req->status == 304) || redirect_sock(req, tux_clientport)) {
+		if (req->parsed_len)
+			trunc_headers(req);
+		req->proto->illegal_request(req, cachemiss);
+		return;
+	} else {
+		if (req->data_sock)
+			BUG();
+	}
+out_flush:
+	clear_keepalive(req);
+	if (!tux_redirect_logging)
+		req->status = 0;
+	flush_request(req, cachemiss);
+}
+
+int init_tux_request_slabs(void)
+{
+	tux_req.slab = kmem_cache_create("tux-request",
+			sizeof(struct request_sock), 0, SLAB_HWCACHE_ALIGN,
+			NULL, NULL);
+
+	return tux_req.slab == NULL;
+}
+
+void free_tux_request_slabs(void)
+{
+	kmem_cache_destroy(tux_req.slab);
+}
Index: latest/net/tux/times.c
===================================================================
--- /dev/null
+++ latest/net/tux/times.c
@@ -0,0 +1,392 @@
+/*
+ * TUX - Integrated Application Protocols Layer and Object Cache
+ *
+ * Copyright (C) 2000, 2001, Ingo Molnar <mingo@redhat.com>
+ *
+ * times.c: time conversion routines.
+ *
+ * Original time convserion code Copyright (C) 1999 by Arjan van de Ven
+ */
+
+/****************************************************************
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2, or (at your option)
+ *	any later version.
+ *
+ *	This program is distributed in the hope that it will be useful,
+ *	but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *	GNU General Public License for more details.
+ *
+ *	You should have received a copy of the GNU General Public License
+ *	along with this program; if not, write to the Free Software
+ *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ ****************************************************************/
+
+#include <linux/time.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+
+
+#include "times.h"
+
+char *dayName[7] = {
+	"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
+};
+
+static char *monthName[12] = {
+	"Jan", "Feb", "Mar", "Apr", "May", "Jun",
+	"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+};
+
+char itoa_h[60]={'0','0','0','0','0','0','0','0','0','0',
+		 '1','1','1','1','1','1','1','1','1','1',
+		 '2','2','2','2','2','2','2','2','2','2',
+		 '3','3','3','3','3','3','3','3','3','3',
+		 '4','4','4','4','4','4','4','4','4','4',
+		 '5','5','5','5','5','5','5','5','5','5'};
+
+char itoa_l[60]={'0','1','2','3','4','5','6','7','8','9',
+		 '0','1','2','3','4','5','6','7','8','9',
+		 '0','1','2','3','4','5','6','7','8','9',
+		 '0','1','2','3','4','5','6','7','8','9',
+		 '0','1','2','3','4','5','6','7','8','9',
+		 '0','1','2','3','4','5','6','7','8','9'};
+
+int time_unix2ls(time_t zulu, char *buf)
+{
+	int Y=0,M=0,D=0;
+	int H=0,Min=0,S=0,WD=0;
+	int I,I2;
+	time_t rest, delta;
+
+	if (zulu > xtime.tv_sec)
+		zulu = xtime.tv_sec;
+
+	I=0;
+	while (I<TUX_NUMYEARS) {
+		if (TimeDays[I][0]>zulu)
+		   break;
+		I++;
+	}
+
+	Y=--I;
+	if (I<0) {
+		Y=0;
+		goto BuildYear;
+	}
+	I2=0;
+	while (I2<=12) {
+		if (TimeDays[I][I2]>zulu)
+		   break;
+		I2++;
+	}
+
+	M=I2-1;
+
+	rest=zulu - TimeDays[Y][M];
+	WD=WeekDays[Y][M];
+	D=rest/86400;
+	rest=rest%86400;
+	WD+=D;
+	WD=WD%7;
+	H=rest/3600;
+	rest=rest%3600;
+	Min=rest/60;
+	rest=rest%60;
+	S=rest;
+
+BuildYear:
+	Y+=TUX_YEAROFFSET;
+
+
+	/* Format:  Day, 01 Mon 1999 01:01:01 GMT */
+
+	delta = xtime.tv_sec - zulu;
+	if (delta > 6*30*24*60)
+		//               "May 23   2000"
+		return sprintf( buf, "%s %02i  %04i", monthName[M], D+1, Y);
+	else
+		//                "May 23 10:14"
+		return sprintf( buf, "%s %02i %02i:%02i",
+			monthName[M], D+1, H, Min);
+}
+
+static int MonthHash[32] =
+	{0,0,7,0,0,0,0,0,0,0,0,3,0,0,0,2,6,0,5,0,9,8,4,0,0,11,1,10,0,0,0,0};
+
+#define is_digit(c)	((c) >= '0' && (c) <= '9')
+
+static inline int skip_atoi(char **s)
+{
+	int i=0;
+
+	while (is_digit(**s))
+		i = i*10 + *((*s)++) - '0';
+	return i;
+}
+
+time_t mimetime_to_unixtime(char *Q)
+{
+	int Y,M,D,H,Min,S;
+	unsigned int Hash;
+	time_t Temp;
+	char *s,**s2;
+
+	s=Q;
+	s2=&s;
+
+	if (strlen(s)<30) return 0;
+	if (s[3]!=',') return 0;
+	if (s[19]!=':') return 0;
+
+	s+=5; /* Skip day of week */
+	D = skip_atoi(s2);  /*  Day of month */
+	s++;
+	Hash = (char)s[0]+(char)s[2];
+	Hash = (Hash<<1) + (char)s[1];
+	Hash = (Hash&63)>>1;
+	M = MonthHash[Hash];
+	s+=4;
+	Y = skip_atoi(s2); /* Year */
+	s++;
+	H = skip_atoi(s2); /* Hour */
+	s++;
+	Min = skip_atoi(s2); /* Minutes */
+	s++;
+	S = skip_atoi(s2); /* Seconds */
+	s++;
+	if ((s[0]!='G')||(s[1]!='M')||(s[2]!='T'))
+	{
+		return 0; /* No GMT */
+	}
+
+	if (Y<TUX_YEAROFFSET) Y = TUX_YEAROFFSET;
+	if (Y>TUX_YEAROFFSET+9) Y = TUX_YEAROFFSET+9;
+
+	Temp = TimeDays[Y-TUX_YEAROFFSET][M];
+	Temp += D*86400+H*3600+Min*60+S;
+
+	return Temp;
+}
+
+// writes the full http date, corresponding to time_t received
+
+void last_mod_time(char * curr, const time_t t)
+{
+	int day, tod, year, wday, mon, hour, min, sec;
+
+	tod = t % 86400;
+	day = t / 86400;
+	if (tod < 0) {
+		tod += 86400;
+		--day;
+	}
+
+	hour = tod / 3600;
+	tod %= 3600;
+	min = tod / 60;
+	sec = tod % 60;
+
+	wday = (day + 4) % 7;
+	if (wday < 0)
+		wday += 7;
+
+	day -= 11017;
+	/* day 0 is march 1, 2000 */
+	year = 5 + day / 146097;
+	day = day % 146097;
+	if (day < 0) {
+		day += 146097;
+		--year;
+	}
+	/* from now on, day is nonnegative */
+	year *= 4;
+	if (day == 146096) {
+		year += 3;
+		day = 36524;
+	} else {
+		year += day / 36524;
+		day %= 36524;
+	}
+	year *= 25;
+	year += day / 1461;
+	day %= 1461;
+	year *= 4;
+	if (day == 1460) {
+		year += 3;
+		day = 365;
+	} else {
+		year += day / 365;
+		day %= 365;
+	}
+
+	day *= 10;
+	mon = (day + 5) / 306;
+	day = day + 5 - 306 * mon;
+	day /= 10;
+	if (mon >= 10) {
+		++year;
+		mon -= 10;
+	} else
+		mon += 2;
+
+	sprintf(curr, "%s, %.2d %s %d %.2d:%.2d:%.2d GMT", dayName[wday],
+		day+1, monthName[mon], year, hour, min, sec);
+}
+
+// writes the full date in ISO8601 format,
+// corresponding to time_t received
+// example: 20011126224910
+
+int mdtm_time(char * curr, const time_t t)
+{
+	int day, tod, year, wday, mon, hour, min, sec;
+
+	tod = t % 86400;
+	day = t / 86400;
+	if (tod < 0) {
+		tod += 86400;
+		--day;
+	}
+
+	hour = tod / 3600;
+	tod %= 3600;
+	min = tod / 60;
+	sec = tod % 60;
+
+	wday = (day + 4) % 7;
+	if (wday < 0)
+		wday += 7;
+
+	day -= 11017;
+	/* day 0 is march 1, 2000 */
+	year = 5 + day / 146097;
+	day = day % 146097;
+	if (day < 0) {
+		day += 146097;
+		--year;
+	}
+	/* from now on, day is nonnegative */
+	year *= 4;
+	if (day == 146096) {
+		year += 3;
+		day = 36524;
+	} else {
+		year += day / 36524;
+		day %= 36524;
+	}
+	year *= 25;
+	year += day / 1461;
+	day %= 1461;
+	year *= 4;
+	if (day == 1460) {
+		year += 3;
+		day = 365;
+	} else {
+		year += day / 365;
+		day %= 365;
+	}
+
+	day *= 10;
+	mon = (day + 5) / 306;
+	day = day + 5 - 306 * mon;
+	day /= 10;
+	if (mon >= 10) {
+		++year;
+		mon -= 10;
+	} else
+		mon += 2;
+
+	return sprintf(curr, "213 %.4d%.2d%.2d%.2d%.2d%.2d\r\n",
+		year, mon+1, day+1, hour, min, sec);
+}
+
+static inline int make_num(const char *s)
+{
+	if (*s >= '0' && *s <= '9')
+		return 10 * (*s - '0') + *(s + 1) - '0';
+	else
+		return *(s + 1) - '0';
+}
+
+static inline int make_month(const char *s)
+{
+	int i;
+
+	for (i = 0; i < 12; i++)
+		if (!strncmp(monthName[i], s, 3))
+			return i+1;
+	return 0;
+}
+
+time_t parse_time(const char *str, const int str_len)
+{
+	int hour;
+	int min;
+	int sec;
+	int mday;
+	int mon;
+	int year;
+
+	if (str[3] == ',') {
+		/* Thu, 09 Jan 1993 01:29:59 GMT */
+
+		if (str_len < 29)
+			return -1;
+
+		mday = make_num(str+5);
+		mon = make_month(str + 8);
+		year = 100 * make_num(str + 12) + make_num(str + 14);
+		hour = make_num(str + 17);
+		min = make_num(str + 20);
+		sec = make_num(str + 23);
+	}
+	else {
+		const char *s;
+		s = strchr(str, ',');
+		if (!s || (str_len - (s - str) < 24)) {
+			/* Wed Jun  9 01:29:59 1993 */
+
+			if (str_len < 24)
+				return -1;
+
+			mon = make_month(str+4);
+			mday = make_num(str+8);
+			hour = make_num(str+11);
+			min = make_num(str+14);
+			sec = make_num(str+17);
+			year = make_num(str+20)*100 + make_num(str+22);
+		}
+		else {
+			/* Thursday, 10-Jun-93 01:29:59 GMT */
+
+			mday = make_num(s + 2);
+			mon = make_month(s + 5);
+			year = make_num(s + 9) + 1900;
+			if (year < 1970)
+				year += 100;
+			hour = make_num(s + 12);
+			min = make_num(s + 15);
+			sec = make_num(s + 18);
+		}
+	}
+
+	if (sec < 0 || sec > 59)
+		return -1;
+	if (min < 0 || min > 59)
+		return -1;
+	if (hour < 0 || hour > 23)
+		return -1;
+	if (mday < 1 || mday > 31)
+		return -1;
+	if (mon < 1 || mon > 12)
+		return -1;
+	if (year < 1970 || year > 2020)
+		return -1;
+
+	return mktime(year, mon, mday, hour, min, sec);
+}
Index: latest/net/tux/userspace.c
===================================================================
--- /dev/null
+++ latest/net/tux/userspace.c
@@ -0,0 +1,27 @@
+/*
+ * TUX - Integrated Application Protocols Layer and Object Cache
+ *
+ * Copyright (C) 2000, 2001, Ingo Molnar <mingo@redhat.com>
+ *
+ * userspace.c: handle userspace-module requests
+ */
+
+#include <net/tux.h>
+
+/****************************************************************
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2, or (at your option)
+ *      any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ ****************************************************************/
+
Index: latest/net/tux/times.h
===================================================================
--- /dev/null
+++ latest/net/tux/times.h
@@ -0,0 +1,26 @@
+static time_t TimeDays[10][13] = {
+ { 852073200,	854751600,	857170800,	859849200,	862441200,	865119600,	867711600,	870390000,	873068400,	875660400,	878338800,	880930800,	883609200 } ,
+ { 883609200,	886287600,	888706800,	891385200,	893977200,	896655600,	899247600,	901926000,	904604400,	907196400,	909874800,	912466800,	915145200 } ,
+ { 915145200,	917823600,	920242800,	922921200,	925513200,	928191600,	930783600,	933462000,	936140400,	938732400,	941410800,	944002800,	946681200 } ,
+ { 946681200,	949359600,	951865200,	954543600,	957135600,	959814000,	962406000,	965084400,	967762800,	970354800,	973033200,	975625200,	978303600 } ,
+ { 978303600,	980982000,	983401200,	986079600,	988671600,	991350000,	993942000,	996620400,	999298800,	1001890800,	1004569200,	1007161200,	1009839600 } ,
+ { 1009839600,	1012518000,	1014937200,	1017615600,	1020207600,	1022886000,	1025478000,	1028156400,	1030834800,	1033426800,	1036105200,	1038697200,	1041375600 } ,
+ { 1041375600,	1044054000,	1046473200,	1049151600,	1051743600,	1054422000,	1057014000,	1059692400,	1062370800,	1064962800,	1067641200,	1070233200,	1072911600 } ,
+ { 1072911600,	1075590000,	1078095600,	1080774000,	1083366000,	1086044400,	1088636400,	1091314800,	1093993200,	1096585200,	1099263600,	1101855600,	1104534000 } ,
+ { 1104534000,	1107212400,	1109631600,	1112310000,	1114902000,	1117580400,	1120172400,	1122850800,	1125529200,	1128121200,	1130799600,	1133391600,	1136070000 } ,
+ { 1136070000,	1138748400,	1141167600,	1143846000,	1146438000,	1149116400,	1151708400,	1154386800,	1157065200,	1159657200,	1162335600,	1164927600,	1167606000 }
+};
+static int WeekDays[10][13] = {
+ { 3,	6,	6,	2,	4,	0,	2,	5,	1,	3,	6,	1,	4 } ,
+ { 4,	0,	0,	3,	5,	1,	3,	6,	2,	4,	0,	2,	5 } ,
+ { 5,	1,	1,	4,	6,	2,	4,	0,	3,	5,	1,	3,	6 } ,
+ { 6,	2,	3,	6,	1,	4,	6,	2,	5,	0,	3,	5,	1 } ,
+ { 1,	4,	4,	0,	2,	5,	0,	3,	6,	1,	4,	6,	2 } ,
+ { 2,	5,	5,	1,	3,	6,	1,	4,	0,	2,	5,	0,	3 } ,
+ { 3,	6,	6,	2,	4,	0,	2,	5,	1,	3,	6,	1,	4 } ,
+ { 4,	0,	1,	4,	6,	2,	4,	0,	3,	5,	1,	3,	6 } ,
+ { 6,	2,	2,	5,	0,	3,	5,	1,	4,	6,	2,	4,	0 } ,
+ { 0,	3,	3,	6,	1,	4,	6,	2,	5,	0,	3,	5,	1 }
+};
+#define TUX_YEAROFFSET   1997
+#define TUX_NUMYEARS     10