---------------------
PatchSet 3729 
Date: 2002/03/14 20:23:41
Author: serassio
Branch: nt-2_5
Tag: (none) 
Log:
Imported Windows patches from NT-2_3 and Cygwin-svc

Members: 
	include/config.h:1.5->1.5.18.1 
	include/radix.h:1.7->1.7.14.1 
	include/util.h:1.10->1.10.14.1 
	lib/ntlmauth.c:1.6->1.6.14.1 
	lib/util.c:1.12->1.12.14.1 
	snmplib/parse.c:1.2->1.2.14.1 
	src/cachemgr.c:1.6->1.6.14.1 
	src/comm.c:1.18.6.1->1.18.6.1.2.1 
	src/disk.c:1.8->1.8.22.1 
	src/fd.c:1.7->1.7.16.1 
	src/globals.h:1.14->1.14.14.1 
	src/helper.c:1.16->1.16.10.1 
	src/ipc.c:1.7->1.7.10.1 
	src/main.c:1.28.6.1->1.28.6.1.4.1 
	src/protos.h:1.41.6.3->1.41.6.3.4.1 
	src/squid.h:1.13.6.2->1.13.6.2.4.1 
	src/stat.c:1.13->1.13.10.1 
	src/store_dir.c:1.19->1.19.14.1 
	src/store_io.c:1.3->1.3.14.1 
	src/structs.h:1.48->1.48.6.1 
	src/tools.c:1.19->1.19.14.1 
	src/win32.c:1.5->1.5.14.1 
	src/fs/awin32/Makefile:1.1->1.1.2.1 
	src/fs/awin32/Makefile.am:1.1->1.1.18.1 
	src/fs/awin32/Makefile.in:1.1->1.1.62.1 
	src/fs/awin32/aiops.c:1.1->1.1.62.1 
	src/fs/awin32/async_io.c:1.1->1.1.62.1 
	src/fs/awin32/store_asyncufs.h:1.1->1.1.62.1 
	src/fs/awin32/store_dir_aufs.c:1.1->1.1.62.1 
	src/fs/awin32/store_io_aufs.c:1.1->1.1.62.1 
	src/fs/ufs/store_dir_ufs.c:1.21.6.1->1.21.6.1.4.1 

Index: squid/include/config.h
===================================================================
RCS file: /cvsroot/squid-sf//squid/include/config.h,v
retrieving revision 1.5
retrieving revision 1.5.18.1
diff -u -r1.5 -r1.5.18.1
--- squid/include/config.h	13 Nov 2001 22:19:32 -0000	1.5
+++ squid/include/config.h	14 Mar 2002 20:23:41 -0000	1.5.18.1
@@ -105,7 +105,8 @@
 #define _SQUID_CYGWIN_
 
 #elif defined(WIN32) || defined(WINNT) || defined(__WIN32__) || defined(__WIN32)
-#define _SQUID_MSWIN_
+#define _SQUID_MSWIN_			      /* MS Compilers */
+#include "squid-mswin.h"
 
 #elif defined(__APPLE__)
 #define _SQUID_APPLE_
Index: squid/include/radix.h
===================================================================
RCS file: /cvsroot/squid-sf//squid/include/radix.h,v
retrieving revision 1.7
retrieving revision 1.7.14.1
diff -u -r1.7 -r1.7.14.1
--- squid/include/radix.h	13 Nov 2001 22:19:33 -0000	1.7
+++ squid/include/radix.h	14 Mar 2002 20:23:41 -0000	1.7.14.1
@@ -1,5 +1,5 @@
 /*
- * $Id: radix.h,v 1.7 2001/11/13 22:19:33 squidadm Exp $
+ * $Id: radix.h,v 1.7.14.1 2002/03/14 20:23:41 serassio Exp $
  */
 
 #ifndef SQUID_RADIX_H
@@ -153,6 +153,8 @@
 extern struct squid_radix_node *squid_rn_search(void *, struct squid_radix_node *);
 extern struct squid_radix_node *squid_rn_search_m(void *, struct squid_radix_node *, void *);
 extern struct squid_radix_node *squid_rn_lookup(void *, void *, struct squid_radix_node_head *);
+#ifndef min
 #define min(x,y) ((x)<(y)? (x) : (y))
+#endif
 
 #endif /* SQUID_RADIX_H */
Index: squid/include/util.h
===================================================================
RCS file: /cvsroot/squid-sf//squid/include/util.h,v
retrieving revision 1.10
retrieving revision 1.10.14.1
diff -u -r1.10 -r1.10.14.1
--- squid/include/util.h	17 Oct 2001 12:30:51 -0000	1.10
+++ squid/include/util.h	14 Mar 2002 20:23:41 -0000	1.10.14.1
@@ -1,5 +1,5 @@
 /*
- * $Id: util.h,v 1.10 2001/10/17 12:30:51 squidadm Exp $
+ * $Id: util.h,v 1.10.14.1 2002/03/14 20:23:41 serassio Exp $
  *
  * AUTHOR: Harvest Derived
  *
@@ -44,11 +44,13 @@
 #include <sys/time.h>
 #endif
 
-#if !defined(SQUIDHOSTNAMELEN)
+#if !defined(SQUIDHOSTNAMELEN)
+#ifndef _SQUID_MSWIN_
 #include <sys/param.h>
 #ifndef _SQUID_NETDB_H_		/* need protection on NEXTSTEP */
 #define _SQUID_NETDB_H_
 #include <netdb.h>
+#endif
 #endif
 #if !defined(MAXHOSTNAMELEN) || (MAXHOSTNAMELEN < 128)
 #define SQUIDHOSTNAMELEN 128
@@ -131,5 +133,19 @@
  * Returns the amount of known allocated memory
  */
 int statMemoryAccounted(void);
+
+/* CygWin & Windows NT Port */ 
+/* win32lib.c */
+#ifdef _SQUID_MSWIN_
+extern int chroot (const char *);
+extern int ftruncate(int, off_t);
+extern int gettimeofday(struct timeval * ,void *);
+extern int kill(pid_t, int);
+extern int statfs(const char *, struct statfs *);
+extern int truncate(const char *, off_t);
+extern const char * wsastrerror(int);
+extern struct passwd *getpwnam(char *);
+extern struct group *getgrnam(char *);
+#endif
 
 #endif /* SQUID_UTIL_H */
Index: squid/lib/ntlmauth.c
===================================================================
RCS file: /cvsroot/squid-sf//squid/lib/ntlmauth.c,v
retrieving revision 1.6
retrieving revision 1.6.14.1
diff -u -r1.6 -r1.6.14.1
--- squid/lib/ntlmauth.c	17 Oct 2001 12:42:58 -0000	1.6
+++ squid/lib/ntlmauth.c	14 Mar 2002 20:23:42 -0000	1.6.14.1
@@ -21,8 +21,8 @@
  *
  */
 
-#include "ntlmauth.h"
 #include "util.h"		/* for base64-related stuff */
+#include "ntlmauth.h"
 
 #ifdef HAVE_STRING_H
 #include <string.h>
Index: squid/lib/util.c
===================================================================
RCS file: /cvsroot/squid-sf//squid/lib/util.c,v
retrieving revision 1.12
retrieving revision 1.12.14.1
diff -u -r1.12 -r1.12.14.1
--- squid/lib/util.c	18 Oct 2001 20:52:10 -0000	1.12
+++ squid/lib/util.c	14 Mar 2002 20:23:42 -0000	1.12.14.1
@@ -1,6 +1,6 @@
 
 /*
- * $Id: util.c,v 1.12 2001/10/18 20:52:10 squidadm Exp $
+ * $Id: util.c,v 1.12.14.1 2002/03/14 20:23:42 serassio Exp $
  *
  * DEBUG: 
  * AUTHOR: Harvest Derived
@@ -32,6 +32,7 @@
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
  *
  */
+#define UTIL_C
 
 #define _etext etext
 
Index: squid/snmplib/parse.c
===================================================================
RCS file: /cvsroot/squid-sf//squid/snmplib/parse.c,v
retrieving revision 1.2
retrieving revision 1.2.14.1
diff -u -r1.2 -r1.2.14.1
--- squid/snmplib/parse.c	18 Oct 2001 20:52:10 -0000	1.2
+++ squid/snmplib/parse.c	14 Mar 2002 20:23:42 -0000	1.2.14.1
@@ -107,6 +107,11 @@
 };
 
 int Line = 1;
+
+#ifdef _SQUID_MSWIN_
+#undef OPAQUE
+#undef OPTIONAL
+#endif
 
 /* types of tokens */
 #define	CONTINUE    -1
Index: squid/src/cachemgr.c
===================================================================
RCS file: /cvsroot/squid-sf//squid/src/Attic/cachemgr.c,v
retrieving revision 1.6
retrieving revision 1.6.14.1
diff -u -r1.6 -r1.6.14.1
--- squid/src/cachemgr.c	18 Oct 2001 20:52:11 -0000	1.6
+++ squid/src/cachemgr.c	14 Mar 2002 20:23:43 -0000	1.6.14.1
@@ -1,6 +1,6 @@
 
 /*
- * $Id: cachemgr.c,v 1.6 2001/10/18 20:52:11 squidadm Exp $
+ * $Id: cachemgr.c,v 1.6.14.1 2002/03/14 20:23:43 serassio Exp $
  *
  * DEBUG: section 0     CGI Cache Manager
  * AUTHOR: Duane Wessels
@@ -169,7 +169,11 @@
 static void error_html(const char *msg);
 static char *menu_url(cachemgr_request * req, const char *action);
 static int parse_status_line(const char *sline, const char **statusStr);
+#ifdef _SQUID_MSWIN_
+static cachemgr_request *read_request(char *);
+#else
 static cachemgr_request *read_request(void);
+#endif
 static char *read_get_request(void);
 static char *read_post_request(void);
 
@@ -178,6 +182,45 @@
 static void reset_auth(cachemgr_request * req);
 static const char *make_auth_header(const cachemgr_request * req);
 
+#ifdef _SQUID_MSWIN_
+static int s_iInitCount = 0;
+int Win32SockInit(void)
+{
+    int iVersionRequested;
+    WSADATA wsaData;
+    int err;
+
+    if (s_iInitCount > 0) {
+	s_iInitCount++;
+	return (0);
+    }
+    else if (s_iInitCount < 0)
+	return (s_iInitCount);
+
+    /* s_iInitCount == 0. Do the initailization */
+    iVersionRequested = MAKEWORD(2, 0);
+    err = WSAStartup((WORD) iVersionRequested, &wsaData);
+    if (err) {
+	s_iInitCount = -1;
+	return (s_iInitCount);
+    }
+    if (LOBYTE(wsaData.wVersion) != 2 ||
+	HIBYTE(wsaData.wVersion) != 0) {
+	s_iInitCount = -2;
+	WSACleanup();
+	return (s_iInitCount);
+    }
+    s_iInitCount++;
+    return (s_iInitCount);
+}
+
+void Win32SockCleanup(void)
+{
+    if (--s_iInitCount == 0)
+	WSACleanup();
+    return;
+}
+#endif /* ifdef _SQUID_MSWIN_ */
 
 static const char *
 safe_str(const char *str)
@@ -398,7 +441,12 @@
 read_reply(int s, cachemgr_request * req)
 {
     char buf[4 * 1024];
+#ifdef _SQUID_MSWIN_
+    int reply;
+    FILE *fp = tmpfile();
+#else
     FILE *fp = fdopen(s, "r");
+#endif
     /* interpretation states */
     enum {
 	isStatusLine, isHeaders, isBodyStart, isBody, isForward, isEof, isForwardEof, isSuccess, isError
@@ -415,6 +463,11 @@
 	perror("fdopen");
 	return 1;
     }
+#ifdef _SQUID_MSWIN_
+    while ((reply=recv(s,buf,sizeof(buf),0))>0)
+	fwrite(buf,1,reply,fp);
+    rewind(fp);
+#endif
     if (parse_menu)
 	action = "menu";
     /* read reply interpreting one line at a time depending on state */
@@ -507,6 +560,9 @@
     static struct sockaddr_in S;
     int s;
     int l;
+#ifdef _SQUID_MSWIN_
+    int answer;
+#endif
     static char buf[2 * 1024];
     if (req == NULL) {
 	auth_html(CACHEMGR_HOSTNAME, CACHE_HTTP_PORT, "");
@@ -555,9 +611,19 @@
 	req->hostname,
 	req->action,
 	make_auth_header(req));
+#ifdef _SQUID_MSWIN_
+    send(s, buf, l,0);
+#else
     write(s, buf, l);
+#endif
     debug(1) fprintf(stderr, "wrote request: '%s'\n", buf);
+#ifdef _SQUID_MSWIN_
+    answer=read_reply(s, req);
+    close(s);
+    return answer;
+#else
     return read_reply(s, req);
+#endif
 }
 
 int
@@ -565,16 +631,31 @@
 {
     char *s;
     cachemgr_request *req;
+#ifdef _SQUID_MSWIN_
+    int answer;
+#endif
     safe_inet_addr("255.255.255.255", &no_addr);
     now = time(NULL);
+#ifdef _SQUID_MSWIN_
+    Win32SockInit();
+    if ((s = strrchr(argv[0], '\\')))
+#else
     if ((s = strrchr(argv[0], '/')))
+#endif
 	progname = xstrdup(s + 1);
     else
 	progname = xstrdup(argv[0]);
     if ((s = getenv("SCRIPT_NAME")) != NULL)
 	script_name = xstrdup(s);
+#ifdef _SQUID_MSWIN_
+    req = read_request(NULL);
+    answer=process_request(req);
+    Win32SockCleanup();
+    return answer;
+#else
     req = read_request();
     return process_request(req);
+#endif
 }
 
 static char *
@@ -606,10 +687,16 @@
     return xstrdup(s);
 }
 
+#ifdef _SQUID_MSWIN_
+static cachemgr_request *
+read_request(char* buf)
+{
+#else
 static cachemgr_request *
 read_request(void)
 {
     char *buf;
+#endif
     cachemgr_request *req;
     char *s;
     char *t;
@@ -620,7 +707,11 @@
 	(void) 0;
     else
 	return NULL;
+#ifdef _SQUID_MSWIN_
+    if (strlen(buf) == 0 || strlen(buf) == 4000)
+#else
     if (strlen(buf) == 0)
+#endif
 	return NULL;
     req = xcalloc(1, sizeof(cachemgr_request));
     for (s = strtok(buf, "&"); s != NULL; s = strtok(NULL, "&")) {
Index: squid/src/comm.c
===================================================================
RCS file: /cvsroot/squid-sf//squid/src/comm.c,v
retrieving revision 1.18.6.1
retrieving revision 1.18.6.1.2.1
diff -u -r1.18.6.1 -r1.18.6.1.2.1
--- squid/src/comm.c	27 Feb 2002 09:18:08 -0000	1.18.6.1
+++ squid/src/comm.c	14 Mar 2002 20:23:43 -0000	1.18.6.1.2.1
@@ -1,6 +1,6 @@
 
 /*
- * $Id: comm.c,v 1.18.6.1 2002/02/27 09:18:08 squidadm Exp $
+ * $Id: comm.c,v 1.18.6.1.2.1 2002/03/14 20:23:43 serassio Exp $
  *
  * DEBUG: section 5     Socket Functions
  * AUTHOR: Harvest Derived
@@ -32,7 +32,8 @@
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
  *
  */
-
+#ifndef COMM_C
+#define COMM_C
 #include "squid.h"
 
 #if defined(_SQUID_CYGWIN_)
@@ -173,7 +174,7 @@
     const char *note)
 {
     int new_socket;
-    int tos = 0;
+    int tos;
     fde *F = NULL;
 
     /* Create socket for accepting new connections. */
@@ -774,15 +775,20 @@
 {
     int flags;
     int dummy = 0;
-#ifdef _SQUID_CYGWIN_
+#if defined (_SQUID_CYGWIN_) || defined (_SQUID_MSWIN_)
     int nonblocking = TRUE;
+#ifdef _SQUID_CYGWIN_    
     if (fd_table[fd].type != FD_PIPE) {
+#endif
 	if (ioctl(fd, FIONBIO, &nonblocking) < 0) {
 	    debug(50, 0) ("commSetNonBlocking: FD %d: %s %D\n", fd, xstrerror(), fd_table[fd].type);
 	    return COMM_ERROR;
 	}
+#ifdef _SQUID_CYGWIN_    
     } else {
 #endif
+#endif
+#ifndef _SQUID_MSWIN_
 	if ((flags = fcntl(fd, F_GETFL, dummy)) < 0) {
 	    debug(50, 0) ("FD %d: fcntl F_GETFL: %s\n", fd, xstrerror());
 	    return COMM_ERROR;
@@ -791,6 +797,7 @@
 	    debug(50, 0) ("commSetNonBlocking: FD %d: %s\n", fd, xstrerror());
 	    return COMM_ERROR;
 	}
+#endif
 #ifdef _SQUID_CYGWIN_
     }
 #endif
@@ -801,13 +808,18 @@
 int
 commUnsetNonBlocking(int fd)
 {
-    int flags;
     int dummy = 0;
+#ifdef _SQUID_MSWIN_
+    int nonblocking = FALSE;
+    if (ioctlsocket(fd, FIONBIO, (unsigned long *) &nonblocking) < 0) {
+#else
+    int flags;
     if ((flags = fcntl(fd, F_GETFL, dummy)) < 0) {
 	debug(50, 0) ("FD %d: fcntl F_GETFL: %s\n", fd, xstrerror());
 	return COMM_ERROR;
     }
     if (fcntl(fd, F_SETFL, flags & (~SQUID_NONBLOCK)) < 0) {
+#endif
 	debug(50, 0) ("commUnsetNonBlocking: FD %d: %s\n", fd, xstrerror());
 	return COMM_ERROR;
     }
@@ -999,3 +1011,4 @@
 	}
     }
 }
+#endif /* COMM_C */
Index: squid/src/disk.c
===================================================================
RCS file: /cvsroot/squid-sf//squid/src/disk.c,v
retrieving revision 1.8
retrieving revision 1.8.22.1
diff -u -r1.8 -r1.8.22.1
--- squid/src/disk.c	16 Aug 2001 07:39:03 -0000	1.8
+++ squid/src/disk.c	14 Mar 2002 20:23:43 -0000	1.8.22.1
@@ -1,6 +1,6 @@
 
 /*
- * $Id: disk.c,v 1.8 2001/08/16 07:39:03 squidadm Exp $
+ * $Id: disk.c,v 1.8.22.1 2002/03/14 20:23:43 serassio Exp $
  *
  * DEBUG: section 6     Disk I/O Routines
  * AUTHOR: Harvest Derived
@@ -60,17 +60,39 @@
 file_open(const char *path, int mode)
 {
     int fd;
+#ifdef _SQUID_MSWIN_ 
+    char fmode[4]="";
+    FILE * fbuf;
+#endif
     if (mode & O_WRONLY)
 	mode |= O_APPEND;
+#ifdef _SQUID_MSWIN_ 
+    mode |= _O_BINARY;
+    errno = 0;
+    if (mode & O_WRONLY) {
+	if (mode & O_TRUNC)
+	    strcpy(fmode,"wb");
+	else
+    	    strcpy(fmode,"a+b");
+	}
+	else
+	    strcpy(fmode,"rb");
+	fbuf=fopen(path,fmode);
+    fd = (fbuf != NULL) ? fileno(fbuf) : -1;
+#else
     mode |= SQUID_NONBLOCK;
     errno = 0;
     fd = open(path, mode, 0644);
+#endif
     statCounter.syscalls.disk.opens++;
     if (fd < 0) {
 	debug(50, 3) ("file_open: error opening file %s: %s\n", path,
 	    xstrerror());
 	fd = DISK_ERROR;
     } else {
+#ifdef _SQUID_MSWIN_ 
+	fd_table[fd].fbuf=fbuf;
+#endif
 	debug(6, 5) ("file_open: FD %d\n", fd);
 	commSetCloseOnExec(fd);
 	fd_open(fd, FD_FILE, path);
@@ -83,6 +105,9 @@
 void
 file_close(int fd)
 {
+#ifdef _SQUID_MSWIN_
+    FILE *fbuf;
+#endif
     fde *F = &fd_table[fd];
     PF *read_callback;
     assert(fd >= 0);
@@ -114,7 +139,15 @@
 #if CALL_FSYNC_BEFORE_CLOSE
     fsync(fd);
 #endif
+#ifdef _SQUID_MSWIN_
+    fbuf = fd_table[fd].fbuf;
+    if (fbuf == NULL)
+	close(fd);
+    else 
+	fclose(fbuf);
+#else
     close(fd);
+#endif
     debug(6, F->flags.close_request ? 2 : 5)
 	("file_close: FD %d, really closing\n", fd);
     fd_close(fd);
Index: squid/src/fd.c
===================================================================
RCS file: /cvsroot/squid-sf//squid/src/fd.c,v
retrieving revision 1.7
retrieving revision 1.7.16.1
diff -u -r1.7 -r1.7.16.1
--- squid/src/fd.c	26 Aug 2001 22:26:29 -0000	1.7
+++ squid/src/fd.c	14 Mar 2002 20:23:43 -0000	1.7.16.1
@@ -1,6 +1,6 @@
 
 /*
- * $Id: fd.c,v 1.7 2001/08/26 22:26:29 squidadm Exp $
+ * $Id: fd.c,v 1.7.16.1 2002/03/14 20:23:43 serassio Exp $
  *
  * DEBUG: section 51    Filedescriptor Functions
  * AUTHOR: Duane Wessels
@@ -96,13 +96,21 @@
 int
 default_read_method(int fd, char *buf, int len)
 {
+#ifdef _SQUID_MSWIN_
+    return (fread(buf, 1, len, fd_table[fd].fbuf));
+#else
     return (read(fd, buf, len));
+#endif
 }
 
 int
 default_write_method(int fd, const char *buf, int len)
 {
-    return (write(fd, buf, len));
+#ifdef _SQUID_MSWIN_
+    return (fwrite(buf, 1, len, fd_table[fd].fbuf));
+#else
+    return (write(fd, buf, len));
+#endif
 }
 
 void
Index: squid/src/globals.h
===================================================================
RCS file: /cvsroot/squid-sf//squid/src/globals.h,v
retrieving revision 1.14
retrieving revision 1.14.14.1
diff -u -r1.14 -r1.14.14.1
--- squid/src/globals.h	18 Oct 2001 20:52:11 -0000	1.14
+++ squid/src/globals.h	14 Mar 2002 20:23:44 -0000	1.14.14.1
@@ -1,6 +1,6 @@
 
 /*
- * $Id: globals.h,v 1.14 2001/10/18 20:52:11 squidadm Exp $
+ * $Id: globals.h,v 1.14.14.1 2002/03/14 20:23:44 serassio Exp $
  *
  *
  * SQUID Web Proxy Cache          http://www.squid-cache.org/
@@ -159,7 +159,14 @@
 extern int incoming_sockets_accepted;
 #if defined(_SQUID_MSWIN_) || defined(_SQUID_CYGWIN_)
 extern unsigned int WIN32_OS_version;	/* 0 */
-extern char *WIN32_OS_string;
+extern char *WIN32_OS_string;    /* NULL */	
+extern char *WIN32_Service_name; /* NULL */
+extern char *WIN32_Command_Line; /* NULL */
+extern char *WIN32_Service_Command_Line; /* NULL */
+extern unsigned int WIN32_run_mode;	/* _WIN_SQUID_RUN_MODE_INTERACTIVE */
+#if defined(_SQUID_MSWIN_) && defined(_DEBUG)
+extern int do_debug;	/* 0 */
+#endif
 #endif
 
 #endif /* SQUID_GLOBALS_H */
Index: squid/src/helper.c
===================================================================
RCS file: /cvsroot/squid-sf//squid/src/helper.c,v
retrieving revision 1.16
retrieving revision 1.16.10.1
diff -u -r1.16 -r1.16.10.1
--- squid/src/helper.c	28 Nov 2001 23:29:52 -0000	1.16
+++ squid/src/helper.c	14 Mar 2002 20:23:44 -0000	1.16.10.1
@@ -1,6 +1,6 @@
 
 /*
- * $Id: helper.c,v 1.16 2001/11/28 23:29:52 squidadm Exp $
+ * $Id: helper.c,v 1.16.10.1 2002/03/14 20:23:44 serassio Exp $
  *
  * DEBUG: section 29    Helper process maintenance
  * AUTHOR: Harvest Derived?
@@ -32,6 +32,8 @@
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
  *
  */
+#ifndef HELPER_C
+#define HELPER_C
 
 #include "squid.h"
 
@@ -73,6 +75,11 @@
     int rfd;
     int wfd;
     wordlist *w;
+#ifdef _SQUID_MSWIN_
+    HANDLE hIpc;
+    pid_t pid;
+#endif
+
     if (hlp->cmdline == NULL)
 	return;
     progname = hlp->cmdline->key;
@@ -97,7 +104,11 @@
 	    args,
 	    shortname,
 	    &rfd,
-	    &wfd);
+	    &wfd
+#ifdef _SQUID_MSWIN_
+	    , &hIpc, &pid
+#endif
+	    );
 	if (x < 0) {
 	    debug(29, 1) ("WARNING: Cannot run '%s' process.\n", progname);
 	    continue;
@@ -112,6 +123,10 @@
 	srv->buf_sz = 8192;
 	srv->offset = 0;
 	srv->parent = hlp;
+#ifdef _SQUID_MSWIN_
+	srv->hIpc = hIpc;
+	srv->pid = pid;
+#endif
 	cbdataLock(hlp);	/* lock because of the parent backlink */
 	dlinkAddTail(srv, &srv->link, &hlp->servers);
 	if (rfd == wfd) {
@@ -149,6 +164,11 @@
     int rfd;
     int wfd;
     wordlist *w;
+#ifdef _SQUID_MSWIN_
+    HANDLE hIpc;
+    pid_t pid;
+#endif
+
     if (hlp->cmdline == NULL)
 	return;
     progname = hlp->cmdline->key;
@@ -173,7 +193,11 @@
 	    args,
 	    shortname,
 	    &rfd,
-	    &wfd);
+	    &wfd
+#ifdef _SQUID_MSWIN_
+	    , &hIpc, &pid
+#endif
+	    );
 	if (x < 0) {
 	    debug(29, 1) ("WARNING: Cannot run '%s' process.\n", progname);
 	    continue;
@@ -507,6 +531,11 @@
 {
     dlink_node *link = hlp->servers.head;
     helper_server *srv;
+#ifdef _SQUID_MSWIN_
+    HANDLE hIpc;
+    pid_t pid;
+    int no;
+#endif
     while (link) {
 	srv = link->data;
 	link = link->next;
@@ -527,8 +556,25 @@
 	    continue;
 	}
 	srv->flags.closing = 1;
+#ifdef _SQUID_MSWIN_
+	hIpc = srv->hIpc;
+	pid = srv->pid;
+	no = srv->index + 1;
+	shutdown(srv->rfd, SD_BOTH);
+#endif
 	comm_close(srv->wfd);
 	srv->wfd = -1;
+#ifdef _SQUID_MSWIN_
+	if (hIpc) {
+	    if (WaitForSingleObject(hIpc, 5000) != WAIT_OBJECT_0) {
+		getCurrentTime();
+		debug(34, 1) ("helperShutdown: WARNING: %s #%d (%s,%ld) "
+		    "didn't exit in 5 seconds\n",
+		    hlp->id_name, no, hlp->cmdline->key, pid);
+	    }
+	    CloseHandle(hIpc);
+	}
+#endif
     }
 }
 
@@ -1115,3 +1161,4 @@
     xfree(r->buf);
     memFree(r, MEM_HELPER_STATEFUL_REQUEST);
 }
+#endif /* HELPER_C */
Index: squid/src/ipc.c
===================================================================
RCS file: /cvsroot/squid-sf//squid/src/ipc.c,v
retrieving revision 1.7
retrieving revision 1.7.10.1
diff -u -r1.7 -r1.7.10.1
--- squid/src/ipc.c	18 Oct 2001 20:52:11 -0000	1.7
+++ squid/src/ipc.c	14 Mar 2002 20:23:44 -0000	1.7.10.1
@@ -1,6 +1,6 @@
 
 /*
- * $Id: ipc.c,v 1.7 2001/10/18 20:52:11 squidadm Exp $
+ * $Id: ipc.c,v 1.7.10.1 2002/03/14 20:23:44 serassio Exp $
  *
  * DEBUG: section 54    Interprocess Communication
  * AUTHOR: Duane Wessels
@@ -35,6 +35,35 @@
 
 #include "squid.h"
 
+#ifdef _SQUID_MSWIN_
+#include <process.h>
+
+struct ipc_params
+{
+    int type;
+    int crfd;
+    int cwfd;
+    struct sockaddr_in PS;
+    const char *prog;
+    char **args;
+};
+
+static struct thread_params
+{
+    int type;
+    int rfd;
+    int send_fd;
+    const char *prog;
+    pid_t pid;
+};
+static int __stdcall ipc_thread_1(struct ipc_params *params);
+static int __stdcall ipc_thread_2(struct thread_params *params);
+
+static const char *ok_string = "OK\n";
+static const char *err_string = "ERR\n";
+static const char *shutdown_string = "$shutdown\n";
+#endif
+
 static const char *hello_string = "hi there\n";
 #define HELLO_BUF_SZ 32
 static char hello_buf[HELLO_BUF_SZ];
@@ -56,8 +85,16 @@
 }
 
 int
+#ifdef _SQUID_MSWIN_
+ipcCreate(int type, const char *prog, const char *const args[], const char *name, int *rfd, int *wfd, HANDLE * hIpc, pid_t * ppid)
+#else
 ipcCreate(int type, const char *prog, const char *const args[], const char *name, int *rfd, int *wfd)
+#endif
 {
+#ifdef _SQUID_MSWIN_
+    unsigned long thread;
+    struct ipc_params params;
+#endif
     pid_t pid;
     struct sockaddr_in CS;
     struct sockaddr_in PS;
@@ -65,13 +102,15 @@
     int prfd = -1;
     int cwfd = -1;
     int pwfd = -1;
+    socklen_t len;
+#ifndef _SQUID_MSWIN_
     int fd;
     int t1, t2, t3;
-    socklen_t len;
     int tmp_s;
 #if HAVE_PUTENV
     char *env_str;
 #endif
+#endif
     int x;
 
 #if HAVE_POLL && defined(_SQUID_OSF_)
@@ -82,6 +121,12 @@
 	*rfd = -1;
     if (wfd)
 	*wfd = -1;
+#ifdef _SQUID_MSWIN_
+    if (hIpc)
+	*hIpc = NULL;
+    if (ppid)
+	*ppid = -1;
+#endif
     if (type == IPC_TCP_SOCKET) {
 	crfd = cwfd = comm_open(SOCK_STREAM,
 	    0,
@@ -109,6 +154,12 @@
 	    0,
 	    name);
     } else if (type == IPC_FIFO) {
+#ifdef _SQUID_MSWIN_
+	debug(54, 0)
+	    ("ipcCreate: %s: use IPC_TCP_SOCKET instead of IP_FIFO on Windows\n",
+	    prog);
+	assert(0);
+#else
 	int p2c[2];
 	int c2p[2];
 	if (pipe(p2c) < 0) {
@@ -123,6 +174,7 @@
 	fd_open(cwfd = p2c[1], FD_PIPE, "IPC FIFO Child Write");
 	fd_open(crfd = c2p[0], FD_PIPE, "IPC FIFO Child Read");
 	fd_open(pwfd = c2p[1], FD_PIPE, "IPC FIFO Parent Write");
+#endif
     } else {
 	assert(IPC_NONE);
     }
@@ -139,7 +191,11 @@
 	debug(54, 0) ("ipcCreate: Failed to create server FD.\n");
 	return ipcCloseAllFD(prfd, pwfd, crfd, cwfd);
     }
+#ifdef _SQUID_MSWIN_
+    {
+#else
     if (type == IPC_TCP_SOCKET || type == IPC_UDP_SOCKET) {
+#endif
 	len = sizeof(PS);
 	memset(&PS, '\0', len);
 	if (getsockname(pwfd, (struct sockaddr *) &PS, &len) < 0) {
@@ -166,10 +222,25 @@
     }
     /* flush or else we get dup data if unbuffered_logs is set */
     logsFlush();
+#ifdef _SQUID_MSWIN_
+    params.type = type;
+    params.crfd = crfd;
+    params.cwfd = cwfd;
+    params.PS = PS;
+    params.prog = prog;
+    params.args = (char **) args;
+
+    thread = _beginthreadex(NULL, 0, ipc_thread_1, &params, 0, NULL);
+
+    if (thread == 0) {
+	debug(50, 1) ("ipcCreate: _beginthread: %s\n", xstrerror());
+#else
     if ((pid = fork()) < 0) {
 	debug(50, 1) ("ipcCreate: fork: %s\n", xstrerror());
+#endif
 	return ipcCloseAllFD(prfd, pwfd, crfd, cwfd);
     }
+#ifndef _SQUID_MSWIN_
     if (pid > 0) {		/* parent */
 	/* close shared socket with child */
 	comm_close(crfd);
@@ -177,14 +248,23 @@
 	    comm_close(cwfd);
 	cwfd = crfd = -1;
 	if (type == IPC_TCP_SOCKET || type == IPC_UDP_SOCKET) {
+#else
+#undef  ipcCloseAllFD
+#define ipcCloseAllFD(w,x,y,z) (CloseHandle((HANDLE)thread), ipcCloseAllFD((w),(x),-1,-1))
+    {
+#endif
 	    if (comm_connect_addr(pwfd, &CS) == COMM_ERROR)
 		return ipcCloseAllFD(prfd, pwfd, crfd, cwfd);
 	}
 	memset(hello_buf, '\0', HELLO_BUF_SZ);
-	if (type == IPC_UDP_SOCKET)
+#ifndef _SQUID_MSWIN_
+    if (type == IPC_UDP_SOCKET)
+#endif
 	    x = recv(prfd, hello_buf, HELLO_BUF_SZ - 1, 0);
-	else
-	    x = read(prfd, hello_buf, HELLO_BUF_SZ - 1);
+#ifndef _SQUID_MSWIN_
+    else
+	x = read(prfd, hello_buf, HELLO_BUF_SZ - 1);
+#endif
 	if (x < 0) {
 	    debug(50, 0) ("ipcCreate: PARENT: hello read test failed\n");
 	    debug(50, 0) ("--> read: %s\n", xstrerror());
@@ -195,6 +275,28 @@
 	    debug(54, 0) ("--> got '%s'\n", rfc1738_escape(hello_buf));
 	    return ipcCloseAllFD(prfd, pwfd, crfd, cwfd);
 	}
+#ifdef _SQUID_MSWIN_
+    x = send(pwfd, ok_string, strlen(ok_string), 0);
+    if (x < 0) {
+	debug(50, 0) ("ipcCreate: PARENT: OK write test failed\n");
+	debug(50, 0) ("--> read: %s\n", xstrerror());
+	return ipcCloseAllFD(prfd, pwfd, crfd, cwfd);
+    }
+    memset(hello_buf, '\0', HELLO_BUF_SZ);
+    x = recv(prfd, hello_buf, HELLO_BUF_SZ - 1, 0);
+    if (x < 0) {
+	debug(50, 0) ("ipcCreate: PARENT: OK read test failed\n");
+	debug(50, 0) ("--> read: %s\n", xstrerror());
+	return ipcCloseAllFD(prfd, pwfd, crfd, cwfd);
+    } else if (!strcmp(hello_buf, err_string)) {
+	debug(54, 0) ("ipcCreate: PARENT: OK read test failed\n");
+	debug(54, 0) ("--> read returned %d\n", x);
+	debug(54, 0) ("--> got '%s'\n", rfc1738_escape(hello_buf));
+	return ipcCloseAllFD(prfd, pwfd, crfd, cwfd);
+    }
+    hello_buf[x] = '\0';
+    pid = atol(hello_buf);
+#endif
 	commSetTimeout(prfd, -1, NULL, NULL);
 	commSetNonBlocking(prfd);
 	commSetNonBlocking(pwfd);
@@ -202,6 +304,23 @@
 	    *rfd = prfd;
 	if (wfd)
 	    *wfd = pwfd;
+#ifdef _SQUID_MSWIN_
+    fd_table[prfd].flags.ipc = 1;
+    fd_table[pwfd].flags.ipc = 1;
+    fd_table[crfd].flags.ipc = 1;
+    fd_table[cwfd].flags.ipc = 1;
+    {
+	DWORD ecode = 0;
+	if (GetExitCodeThread((HANDLE) thread, &ecode) && ecode == STILL_ACTIVE) {
+	    if (hIpc)
+		*hIpc = (HANDLE) thread;
+	    if (ppid)
+		*ppid = pid;
+	    return pwfd;
+	} else
+	    return ipcCloseAllFD(prfd, pwfd, crfd, cwfd);
+    }
+#else
 	fd_table[prfd].flags.ipc = 1;
 	fd_table[pwfd].flags.ipc = 1;
 	if (Config.sleep_after_fork) {
@@ -212,7 +331,63 @@
 	    select(0, NULL, NULL, NULL, &sl);
 	}
 	return pid;
+#endif
     }
+
+#ifdef _SQUID_MSWIN_
+#undef ipcCloseAllFD
+
+static int
+ipcSend(int cwfd, const char *buf, int len)
+{
+    int x;
+
+    x = send(cwfd, buf, len, 0);
+    if (x < 0) {
+	debug(50, 0) ("sendto FD %d: %s\n", cwfd, xstrerror());
+	debug(50, 0) ("ipcCreate: CHILD: hello write test failed\n");
+    }
+    return x;
+}
+
+static int __stdcall
+ipc_thread_1(struct ipc_params *params)
+{
+    int retval = -1;
+    int t1, t2, t3;
+    int p2c[2] = { -1, -1 };
+    int c2p[2] = { -1, -1 };
+    HANDLE hProcess = NULL;
+    pid_t pid;
+    HANDLE thread = NULL;
+    struct thread_params thread_params;
+    int fd;
+    char *str;
+    int tmp_s;
+#if HAVE_PUTENV
+    char *env_str = NULL;
+#endif
+    int x;
+    int type = params->type;
+    int crfd = params->crfd;
+    int cwfd = params->cwfd;
+    int prfd_ipc = -1, pwfd_ipc = -1, crfd_ipc = -1, cwfd_ipc = -1;
+    char *prog = NULL;
+    char **args = params->args;
+    struct sockaddr_in PS = params->PS;
+    struct sockaddr_in CS_ipc, PS_ipc;
+    char *buf1 = NULL;
+
+    buf1 = xcalloc(1, 8192);
+    strcpy(buf1, params->prog);
+    prog = strtok(buf1, w_space);
+    if (str = strrchr(prog, '/'))
+	prog = ++str;
+    if (str = strrchr(prog, '\\'))
+	prog = ++str;
+    prog = xstrdup(prog);
+
+#else
     /* child */
     no_suid();			/* give up extra priviliges */
     /* close shared socket with parent */
@@ -220,25 +395,47 @@
     if (pwfd != prfd)
 	close(pwfd);
     pwfd = prfd = -1;
+#endif
 
     if (type == IPC_TCP_SOCKET) {
 	debug(54, 3) ("ipcCreate: calling accept on FD %d\n", crfd);
 	if ((fd = accept(crfd, NULL, NULL)) < 0) {
 	    debug(50, 0) ("ipcCreate: FD %d accept: %s\n", crfd, xstrerror());
-	    _exit(1);
+#ifdef _SQUID_MSWIN_
+	goto cleanup;
+#else
+	_exit(1);
+#endif
 	}
 	debug(54, 3) ("ipcCreate: CHILD accepted new FD %d\n", fd);
-	close(crfd);
+#ifdef _SQUID_MSWIN_
+    	comm_close(crfd);
+    	snprintf(buf1, 8191, "%s CHILD socket", prog);
+    	fd_open(fd, FD_SOCKET, buf1);
+    	fd_table[fd].flags.ipc = 1;
+#else
+    	close(crfd);
+#endif
 	cwfd = crfd = fd;
     } else if (type == IPC_UDP_SOCKET) {
 	if (comm_connect_addr(crfd, &PS) == COMM_ERROR)
+#ifdef _SQUID_MSWIN_
+	goto cleanup;
+#else
 	    return ipcCloseAllFD(prfd, pwfd, crfd, cwfd);
+#endif
     }
+#ifndef _SQUID_MSWIN_
     if (type == IPC_UDP_SOCKET) {
+#endif
 	x = send(cwfd, hello_string, strlen(hello_string) + 1, 0);
 	if (x < 0) {
 	    debug(50, 0) ("sendto FD %d: %s\n", cwfd, xstrerror());
 	    debug(50, 0) ("ipcCreate: CHILD: hello write test failed\n");
+#ifdef _SQUID_MSWIN_
+	goto cleanup;
+    }
+#else
 	    _exit(1);
 	}
     } else {
@@ -248,11 +445,13 @@
 	    _exit(1);
 	}
     }
+#endif
 #if HAVE_PUTENV
     env_str = xcalloc((tmp_s = strlen(Config.debugOptions) + 32), 1);
     snprintf(env_str, tmp_s, "SQUID_DEBUG=%s", Config.debugOptions);
     putenv(env_str);
 #endif
+#ifndef _SQUID_MSWIN_
     /*
      * This double-dup stuff avoids problems when one of 
      *  crfd, cwfd, or debug_log are in the rage 0-2.
@@ -286,4 +485,359 @@
     debug(50, 0) ("ipcCreate: %s: %s\n", prog, xstrerror());
     _exit(1);
     return 0;
+#else
+    memset(buf1, '\0', sizeof(buf1));
+    x = recv(crfd, buf1, 8191, 0);
+    if (x < 0) {
+	debug(50, 0) ("ipcCreate: CHILD: OK read test failed\n");
+	debug(50, 0) ("--> read: %s\n", xstrerror());
+	goto cleanup;
+    } else if (strcmp(buf1, ok_string)) {
+	debug(54, 0) ("ipcCreate: CHILD: OK read test failed\n");
+	debug(54, 0) ("--> read returned %d\n", x);
+	debug(54, 0) ("--> got '%s'\n", rfc1738_escape(hello_buf));
+	goto cleanup;
+    }
+    /* assign file descriptors to child process */
+    if (_pipe(p2c, 1024, _O_BINARY | _O_NOINHERIT) < 0) {
+	debug(50, 0) ("ipcCreate: CHILD: pipe: %s\n", xstrerror());
+	ipcSend(cwfd, err_string, strlen(err_string));
+	goto cleanup;
+    }
+    if (_pipe(c2p, 1024, _O_BINARY | _O_NOINHERIT) < 0) {
+	debug(50, 0) ("ipcCreate: CHILD: pipe: %s\n", xstrerror());
+	ipcSend(cwfd, err_string, strlen(err_string));
+	goto cleanup;
+    }
+
+    if (type == IPC_UDP_SOCKET) {
+	snprintf(buf1, 8192, "%s(%ld) <-> ipc CHILD socket", prog, -1);
+	crfd_ipc = cwfd_ipc = comm_open(SOCK_DGRAM, 0, local_addr, 0, 0, buf1);
+
+	if (crfd_ipc < 0) {
+	    debug(54,
+		0) ("ipcCreate: CHILD: Failed to create child FD for %s.\n",
+		prog);
+	    ipcSend(cwfd, err_string, strlen(err_string));
+	    goto cleanup;
+	}
+
+	snprintf(buf1, 8192, "%s(%ld) <-> ipc PARENT socket", prog, -1);
+	prfd_ipc = pwfd_ipc = comm_open(SOCK_DGRAM, 0, local_addr, 0, 0, buf1);
+	if (pwfd_ipc < 0) {
+	    debug(54,
+		0) ("ipcCreate: CHILD: Failed to create server FD for %s.\n",
+		prog);
+	    ipcSend(cwfd, err_string, strlen(err_string));
+	    goto cleanup;
+	}
+
+	tmp_s = sizeof(PS_ipc);
+	memset(&PS_ipc, '\0', tmp_s);
+	if (getsockname(pwfd_ipc, (struct sockaddr *) &PS_ipc, &tmp_s) < 0) {
+	    debug(50, 0) ("ipcCreate: getsockname: %s\n", xstrerror());
+	    ipcSend(cwfd, err_string, strlen(err_string));
+	    goto cleanup;
+	}
+	debug(54, 3) ("ipcCreate: FD %d sockaddr %s:%d\n",
+	    pwfd_ipc, inet_ntoa(PS_ipc.sin_addr), ntohs(PS_ipc.sin_port));
+	tmp_s = sizeof(CS_ipc);
+	memset(&CS_ipc, '\0', tmp_s);
+	if (getsockname(crfd_ipc, (struct sockaddr *) &CS_ipc, &tmp_s) < 0) {
+	    debug(50, 0) ("ipcCreate: getsockname: %s\n", xstrerror());
+	    ipcSend(cwfd, err_string, strlen(err_string));
+	    goto cleanup;
+	}
+	debug(54, 3) ("ipcCreate: FD %d sockaddr %s:%d\n",
+	    crfd_ipc, inet_ntoa(CS_ipc.sin_addr), ntohs(CS_ipc.sin_port));
+
+	if (comm_connect_addr(pwfd_ipc, &CS_ipc) == COMM_ERROR) {
+	    ipcSend(cwfd, err_string, strlen(err_string));
+	    goto cleanup;
+	}
+
+	fd = crfd;
+
+	if (comm_connect_addr(crfd_ipc, &PS_ipc) == COMM_ERROR) {
+	    ipcSend(cwfd, err_string, strlen(err_string));
+	    goto cleanup;
+	}
+    }				/* IPC_UDP_SOCKET */
+    t1 = dup(0);
+    t2 = dup(1);
+    t3 = dup(2);
+    dup2(c2p[0], 0);
+    dup2(p2c[1], 1);
+    dup2(fileno(debug_log), 2);
+    close(c2p[0]);
+    close(p2c[1]);
+
+    commUnsetNonBlocking(fd);
+
+    {
+	STARTUPINFO si;
+	PROCESS_INFORMATION pi;
+
+	memset(&si, 0, sizeof(STARTUPINFO));
+	si.cb = sizeof(STARTUPINFO);
+	si.hStdInput = (HANDLE) _get_osfhandle(0);
+	si.hStdOutput = (HANDLE) _get_osfhandle(1);
+	si.hStdError = (HANDLE) _get_osfhandle(2);
+	si.dwFlags = STARTF_USESTDHANDLES;
+
+	*buf1 = '\0';
+	strcpy(buf1 + 4096, params->prog);
+	str = strtok(buf1 + 4096, w_space);
+	do {
+	    strcat(buf1, str);
+	    strcat(buf1, " ");
+	} while ((str = strtok(NULL, w_space)));
+	x = 1;
+	while (args[x]) {
+	    strcat(buf1, args[x++]);
+	    strcat(buf1, " ");
+	}
+
+	if (CreateProcess(buf1 + 4096, buf1, NULL, NULL, TRUE, CREATE_NO_WINDOW,
+		NULL, NULL, &si, &pi)) {
+	    pid = pi.dwProcessId;
+	    hProcess = pi.hProcess;
+	} else {
+	    pid = -1;
+	    _dosmaperr(GetLastError());
+	    x = errno;
+	}
+    }
+    dup2(t1, 0);
+    dup2(t2, 1);
+    dup2(t3, 2);
+    close(t1);
+    close(t2);
+    close(t3);
+
+    if (pid == -1) {
+	errno = x;
+	debug(50, 0) ("ipcCreate: CHILD: %s: %s\n", params->prog, xstrerror());
+	ipcSend(cwfd, err_string, strlen(err_string));
+	goto cleanup;
+    }
+
+    if (type == IPC_UDP_SOCKET) {
+	WSAPROTOCOL_INFO wpi;
+
+	memset(&wpi, 0, sizeof(wpi));
+	if (SOCKET_ERROR == WSADuplicateSocket(crfd_ipc, pid, &wpi)) {
+	    debug(50, 0) ("ipcCreate: CHILD: WSADuplicateSocket: %s\n",
+		xstrerror());
+	    ipcSend(cwfd, err_string, strlen(err_string));
+	    goto cleanup;
+	}
+	x = write(c2p[1], &wpi, sizeof(wpi));
+	if (x < sizeof(wpi)) {
+	    debug(50, 0) ("ipcCreate: CHILD: write FD %d: %s\n", c2p[1],
+		xstrerror());
+	    debug(54, 0) ("ipcCreate: CHILD: %s: socket exchange failed\n",
+		prog);
+	    ipcSend(cwfd, err_string, strlen(err_string));
+	    goto cleanup;
+	}
+	x = read(p2c[0], buf1, 8192);
+	if (x < 0) {
+	    debug(50, 0) ("ipcCreate: CHILD: read FD %d: %s\n", p2c[0],
+		xstrerror());
+	    debug(54, 0) ("ipcCreate: CHILD: %s: socket exchange failed\n",
+		prog);
+	    ipcSend(cwfd, err_string, strlen(err_string));
+	    goto cleanup;
+	} else if (strncmp(buf1, ok_string, strlen(ok_string))) {
+	    debug(54, 0) ("ipcCreate: CHILD: %s: socket exchange failed\n",
+		prog);
+	    debug(54, 0) ("--> read returned %d\n", x);
+	    buf1[x] = '\0';
+	    debug(54, 0) ("--> got '%s'\n", rfc1738_escape(buf1));
+	    ipcSend(cwfd, err_string, strlen(err_string));
+	    goto cleanup;
+	}
+	x = write(c2p[1], &PS_ipc, sizeof(PS_ipc));
+	if (x < sizeof(PS_ipc)) {
+	    debug(50, 0) ("ipcCreate: CHILD: write FD %d: %s\n", c2p[1],
+		xstrerror());
+	    debug(54, 0) ("ipcCreate: CHILD: %s: socket exchange failed\n",
+		prog);
+	    ipcSend(cwfd, err_string, strlen(err_string));
+	    goto cleanup;
+	}
+	x = read(p2c[0], buf1, 8192);
+	if (x < 0) {
+	    debug(50, 0) ("ipcCreate: CHILD: read FD %d: %s\n", p2c[0],
+		xstrerror());
+	    debug(54, 0) ("ipcCreate: CHILD: %s: socket exchange failed\n",
+		prog);
+	    ipcSend(cwfd, err_string, strlen(err_string));
+	    goto cleanup;
+	} else if (strncmp(buf1, ok_string, strlen(ok_string))) {
+	    debug(54, 0) ("ipcCreate: CHILD: %s: socket exchange failed\n");
+	    debug(54, 0) ("--> read returned %d\n", x);
+	    buf1[x] = '\0';
+	    debug(54, 0) ("--> got '%s'\n", rfc1738_escape(buf1));
+	    ipcSend(cwfd, err_string, strlen(err_string));
+	    goto cleanup;
+	}
+	x = send(pwfd_ipc, ok_string, strlen(ok_string), 0);
+	x = recv(prfd_ipc, buf1 + 200, 8191 - 200, 0);
+	assert((size_t) x == strlen(ok_string)
+	    && !strncmp(ok_string, buf1 + 200, strlen(ok_string)));
+    }				/* IPC_UDP_SOCKET */
+    snprintf(buf1, 8191, "%s(%ld) CHILD socket", prog, pid);
+    fd_note(fd, buf1);
+    if (prfd_ipc != -1) {
+	snprintf(buf1, 8191, "%s(%ld) <-> ipc CHILD socket", prog, pid);
+	fd_note(crfd_ipc, buf1);
+	snprintf(buf1, 8191, "%s(%ld) <-> ipc PARENT socket", prog, pid);
+	fd_note(prfd_ipc, buf1);
+    }				/* else {                      // IPC_TCP_SOCKET
+				 * commSetNoLinger(fd);
+				 * }
+				 */
+    thread_params.prog = prog;
+    thread_params.send_fd = cwfd;
+    thread_params.pid = pid;
+    if ((thread_params.type = type) == IPC_TCP_SOCKET)
+	thread_params.rfd = p2c[0];
+    else
+	thread_params.rfd = prfd_ipc;
+
+    thread =
+	(HANDLE) _beginthreadex(NULL, 0, ipc_thread_2, &thread_params, 0, NULL);
+    if (!thread) {
+	debug(50, 0) ("ipcCreate: CHILD: _beginthreadex: %s\n", xstrerror());
+	ipcSend(cwfd, err_string, strlen(err_string));
+	goto cleanup;
+    }
+
+    snprintf(buf1, 8191, "%ld\n", pid);
+    if (-1 == ipcSend(cwfd, buf1, strlen(buf1)))
+	goto cleanup;
+
+    debug(54, 2) ("ipc(%s,%d): started successfully\n", prog, pid);
+    /* cycle */
+    for (;;) {
+	x = recv(crfd, buf1, 8192, 0);
+	if (x <= 0) {
+	    debug(54,
+		3) ("ipc(%s,%d): %d bytes received from parent. Exiting...\n",
+		prog, pid, x);
+	    break;
+	}
+	buf1[x] = '\0';
+	if (type == 2 && !strcmp(buf1, shutdown_string)) {
+	    debug(54, 3)
+		
+		("ipc(%s,%d): request for shutdown received from parent. Exiting...\n",
+		prog, pid);
+	    break;
+	}
+	debug(54, 5) ("ipc(%s,%d): received from parent: %s\n", prog, pid,
+	    rfc1738_escape_unescaped(buf1));
+	if (type == IPC_TCP_SOCKET)
+	    x = write(c2p[1], buf1, x);
+	else
+	    x = send(pwfd_ipc, buf1, x, 0);
+	if (x <= 0) {
+	    debug(54, 3) ("ipc(%s,%d): %d bytes written to %s. Exiting...\n",
+		prog, pid, x, prog);
+	    break;
+	}
+    }
+
+    retval = 0;
+
+  cleanup:
+    if (c2p[1] != -1)
+	close(c2p[1]);
+    if (fd_table[crfd].flags.open)
+	ipcCloseAllFD(-1, -1, crfd, cwfd);
+    if (prfd_ipc != -1) {
+	send(crfd_ipc, shutdown_string, strlen(shutdown_string), 0);
+	shutdown(crfd_ipc, SD_BOTH);
+	shutdown(prfd_ipc, SD_BOTH);
+    }
+    ipcCloseAllFD(prfd_ipc, pwfd_ipc, crfd_ipc, cwfd_ipc);
+    if (hProcess && WAIT_OBJECT_0 !=
+	WaitForSingleObject(hProcess, type == IPC_UDP_SOCKET ? 12000 : 5000)) {
+
+	getCurrentTime();
+	debug(54, 0) ("ipc(%s,%d): WARNING: %s didn't exit in %d seconds.\n",
+	    prog, pid, prog, type == IPC_UDP_SOCKET ? 12 : 5);
+    }
+    if (thread && WAIT_OBJECT_0 != WaitForSingleObject(thread, 3000)) {
+	getCurrentTime();
+	debug(54, 0)
+	    ("ipc(%s,%d): WARNING: ipc_thread_2 didn't exit in 3 seconds.\n",
+	    prog, pid, prog);
+    }
+    getCurrentTime();
+    if (!retval)
+	debug(54, 2) ("ipc(%s,%d): normal exit\n", prog, pid);
+
+    if (buf1)
+	xfree(buf1);
+    if (prog)
+	xfree(prog);
+    if (env_str)
+	xfree(env_str);
+    if (thread)
+	CloseHandle(thread);
+    if (hProcess)
+	CloseHandle(hProcess);
+    if (p2c[0] != -1)
+	close(p2c[0]);
+    return retval;
+#endif
+}
+
+#ifdef _SQUID_MSWIN_
+static int __stdcall
+ipc_thread_2(struct thread_params *params)
+{
+    int type = params->type;
+    int rfd = params->rfd;
+    int send_fd = params->send_fd;
+    int x;
+    char *prog = xstrdup(params->prog);
+    pid_t pid = params->pid;
+    char *buf2 = xcalloc(1, 8192);
+
+    for (;;) {
+	if (type == IPC_TCP_SOCKET)
+	    x = read(rfd, buf2, 8192);
+	else
+	    x = recv(rfd, buf2, 8192, 0);
+	if ((x <= 0 && type == IPC_TCP_SOCKET) || (x < 0
+		&& type == IPC_UDP_SOCKET)) {
+	    debug(54, 3) ("ipc(%s,%d): %d bytes read from %s. Exiting...\n",
+		prog, pid, x, prog);
+	    break;
+	}
+	buf2[x] = '\0';
+	if (type == 2 && !strcmp(buf2, shutdown_string)) {
+	    debug(54,
+		3) ("ipc(%s,%d): request for shutdown received. Exiting...\n",
+		prog, pid);
+	    break;
+	}
+	debug(54, 5) ("ipc(%s,%d): received from child : %s\n", prog, pid,
+	    rfc1738_escape_unescaped(buf2));
+	x = send(send_fd, buf2, x, 0);
+	if ((x <= 0 && type == IPC_TCP_SOCKET) || (x < 0
+		&& type == IPC_UDP_SOCKET)) {
+	    debug(54, 3) ("ipc(%s,%d): %d bytes sent to parent. Exiting...\n",
+		prog, pid, x);
+	    break;
+	}
+    }
+    xfree(prog);
+    xfree(buf2);
+    return 0;
 }
+#endif
Index: squid/src/main.c
===================================================================
RCS file: /cvsroot/squid-sf//squid/src/main.c,v
retrieving revision 1.28.6.1
retrieving revision 1.28.6.1.4.1
diff -u -r1.28.6.1 -r1.28.6.1.4.1
--- squid/src/main.c	16 Jan 2002 09:32:28 -0000	1.28.6.1
+++ squid/src/main.c	14 Mar 2002 20:23:44 -0000	1.28.6.1.4.1
@@ -1,6 +1,6 @@
 
 /*
- * $Id: main.c,v 1.28.6.1 2002/01/16 09:32:28 squidadm Exp $
+ * $Id: main.c,v 1.28.6.1.4.1 2002/03/14 20:23:44 serassio Exp $
  *
  * DEBUG: section 1     Startup and Main Loop
  * AUTHOR: Harvest Derived
@@ -35,11 +35,26 @@
 
 #include "squid.h"
 
+#if defined(USE_WIN32_SERVICE) && (defined(_SQUID_MSWIN_) || defined(_SQUID_CYGWIN_))
+#include <windows.h>
+#include <process.h>
+static int opt_install_service = FALSE;
+static int opt_remove_service = FALSE;
+static int opt_signal_service = FALSE;
+static int opt_command_line = FALSE;
+extern void WIN32_svcstatusupdate(DWORD);
+void WINAPI WIN32_svcHandler(DWORD);
+#endif
+
 /* for error reporting from xmalloc and friends */
 extern void (*failure_notify) (const char *);
 
 static int opt_send_signal = -1;
+#if defined(USE_WIN32_SERVICE) && (defined(_SQUID_MSWIN_) || defined(_SQUID_CYGWIN_))
+static int opt_no_daemon = 1;
+#else
 static int opt_no_daemon = 0;
+#endif
 static int opt_parse_cfg_only = 0;
 static int httpPortNumOverride = 1;
 static int icpPortNumOverride = 1;	/* Want to detect "-u 0" */
@@ -53,8 +68,6 @@
 
 static void mainRotate(void);
 static void mainReconfigure(void);
-static SIGHDLR rotate_logs;
-static SIGHDLR reconfigure;
 #if ALARM_UPDATES_TIME
 static SIGHDLR time_tick;
 #endif
@@ -83,15 +96,27 @@
 usage(void)
 {
     fprintf(stderr,
+#if defined(USE_WIN32_SERVICE) && (defined(_SQUID_MSWIN_) || defined(_SQUID_CYGWIN_))
+	"Usage: %s [-dhirsvzCDFRVYX] [-f config-file] [-[au] port] [-k signal] [-n name] [-O CommandLine]\n"
+#else
 	"Usage: %s [-dhsvzCDFNRVYX] [-f config-file] [-[au] port] [-k signal]\n"
+#endif
 	"       -a port   Specify HTTP port number (default: %d).\n"
 	"       -d level  Write debugging to stderr also.\n"
 	"       -f file   Use given config-file instead of\n"
 	"                 %s\n"
 	"       -h        Print help message.\n"
+#if defined(USE_WIN32_SERVICE) && (defined(_SQUID_MSWIN_) || defined(_SQUID_CYGWIN_))
+	"       -i        Installs as a Windows Service (see -n option).\n"
+#endif
 	"       -k reconfigure|rotate|shutdown|interrupt|kill|debug|check|parse\n"
 	"                 Parse configuration file, then send signal to \n"
 	"                 running copy (except -k parse) and exit.\n"
+#if defined(USE_WIN32_SERVICE) && (defined(_SQUID_MSWIN_) || defined(_SQUID_CYGWIN_))
+	"       -n name   Specify Windows Service name to use for service operations\n"
+	"                 default is: " _WIN_SQUID_DEFAULT_SERVICE_NAME ".\n"
+	"       -r        Removes a Windows Service (see -n option).\n"
+#endif
 	"       -s        Enable logging to syslog.\n"
 	"       -u port   Specify ICP port number (default: %d), disable with 0.\n"
 	"       -v        Print version.\n"
@@ -99,7 +124,12 @@
 	"       -C        Do not catch fatal signals.\n"
 	"       -D        Disable initial DNS tests.\n"
 	"       -F        Don't serve any requests until store is rebuilt.\n"
+#if !(defined(USE_WIN32_SERVICE) && (defined(_SQUID_MSWIN_) || defined(_SQUID_CYGWIN_)))
 	"       -N        No daemon mode.\n"
+#else
+	"       -O options\n"
+       	"                 Set Windows Service Command line options in Registry.\n"
+#endif
 	"       -R        Do not set REUSEADDR on port.\n"
 	"       -S        Double-check swap during rebuild.\n"
 	"       -V        Virtual host httpd-accelerator.\n"
@@ -115,7 +145,11 @@
     extern char *optarg;
     int c;
 
+#if defined(USE_WIN32_SERVICE) && (defined(_SQUID_MSWIN_) || defined(_SQUID_CYGWIN_))
+    while ((c = getopt(argc, argv, "CDFO:RSVYXa:d:f:hik:m::n:rsu:vz?")) != -1) {
+#else
     while ((c = getopt(argc, argv, "CDFNRSVYXa:d:f:hk:m::su:vz?")) != -1) {
+#endif
 	switch (c) {
 	case 'C':
 	    opt_catch_signals = 0;
@@ -126,9 +160,16 @@
 	case 'F':
 	    opt_foreground_rebuild = 1;
 	    break;
+#if !(defined(USE_WIN32_SERVICE) && (defined(_SQUID_MSWIN_) || defined(_SQUID_CYGWIN_)))
 	case 'N':
 	    opt_no_daemon = 1;
 	    break;
+#else
+	case 'O':
+	    opt_command_line = 1;
+	    WIN32_Command_Line = xstrdup(optarg);
+	    break;
+#endif
 	case 'R':
 	    opt_reuseaddr = 0;
 	    break;
@@ -158,6 +199,11 @@
 	case 'h':
 	    usage();
 	    break;
+#if defined(USE_WIN32_SERVICE) && (defined(_SQUID_MSWIN_) || defined(_SQUID_CYGWIN_))
+	case 'i':
+	    opt_install_service = TRUE;
+	    break;
+#endif
 	case 'k':
 	    if ((int) strlen(optarg) < 1)
 		usage();
@@ -205,6 +251,16 @@
 		fatal("Need to configure --enable-xmalloc-debug-trace to use -m option");
 #endif
 	    }
+#if defined(USE_WIN32_SERVICE) && (defined(_SQUID_MSWIN_) || defined(_SQUID_CYGWIN_))
+	case 'n':
+	    xfree(WIN32_Service_name);
+	    WIN32_Service_name = xstrdup(optarg);
+	    opt_signal_service = TRUE;
+	    break;
+	case 'r':
+	    opt_remove_service = TRUE;
+	    break;
+#endif
 	case 's':
 	    opt_syslog_enable = 1;
 	    break;
@@ -215,6 +271,9 @@
 	    break;
 	case 'v':
 	    printf("Squid Cache: Version %s\nconfigure options: %s\n", version_string, SQUID_CONFIGURE_OPTIONS);
+#if defined(USE_WIN32_SERVICE) && (defined(_SQUID_MSWIN_) || defined(_SQUID_CYGWIN_))
+	    printf("Compiled as Windows System Service.\n");
+#endif
 	    exit(0);
 	    /* NOTREACHED */
 	case 'z':
@@ -229,13 +288,15 @@
 }
 
 /* ARGSUSED */
-static void
+void
 rotate_logs(int sig)
 {
     do_rotate = 1;
+#ifndef _SQUID_MSWIN_
 #if !HAVE_SIGACTION
     signal(sig, rotate_logs);
 #endif
+#endif
 }
 
 #if ALARM_UPDATES_TIME
@@ -252,19 +313,22 @@
 #endif
 
 /* ARGSUSED */
-static void
+void
 reconfigure(int sig)
 {
     do_reconfigure = 1;
+#ifndef _SQUID_MSWIN_
 #if !HAVE_SIGACTION
     signal(sig, reconfigure);
 #endif
+#endif
 }
 
 void
 shut_down(int sig)
 {
     do_shutdown = sig == SIGINT ? -1 : 1;
+#ifndef _SQUID_MSWIN_
 #ifdef KILL_PARENT_OPT
     if (getppid() > 1) {
 	debug(1, 1) ("Killing RunCache, pid %d\n", getppid());
@@ -275,6 +339,7 @@
     signal(SIGTERM, SIG_DFL);
     signal(SIGINT, SIG_DFL);
 #endif
+#endif
 }
 
 static void
@@ -292,7 +357,9 @@
     wccpConnectionOpen();
 #endif
     clientdbInit();
+#ifndef _SQUID_MSWIN_
     icmpOpen();
+#endif
     netdbInit();
     asnInit();
     peerSelectInit();
@@ -411,7 +478,7 @@
     leave_suid();		/* Run as non privilegied user */
 #ifdef _SQUID_OS2_
     return;
-#endif
+#else
     if (geteuid() == 0) {
 	debug(0, 0) ("Squid is not safe to run as root!  If you must\n");
 	debug(0, 0) ("start Squid as root, then you must configure\n");
@@ -419,6 +486,7 @@
 	debug(0, 0) ("'cache_effective_user' option in the config file.\n");
 	fatal("Don't run Squid as root, set 'cache_effective_user'!");
     }
+#endif
 }
 
 static void
@@ -472,6 +540,14 @@
     debug(1, 0) ("Starting Squid Cache version %s for %s...\n",
 	version_string,
 	CONFIG_HOST_TYPE);
+#if defined(_SQUID_MSWIN_) || defined(_SQUID_CYGWIN_)
+    if (WIN32_run_mode == _WIN_SQUID_RUN_MODE_SERVICE) {
+	debug(1, 0) ("Running as %s Windows System Service on %s\n", WIN32_Service_name, WIN32_OS_string);
+	debug(1, 0) ("Service command line is: %s\n", WIN32_Service_Command_Line);
+    }
+    else
+	debug(1, 0) ("Running on %s\n",WIN32_OS_string);
+#endif
     debug(1, 1) ("Process ID %d\n", (int) getpid());
     debug(1, 1) ("With %d file descriptors available\n", Squid_MaxFD);
 
@@ -563,24 +639,35 @@
     configured_once = 1;
 }
 
+#if defined(USE_WIN32_SERVICE) && (defined(_SQUID_MSWIN_) || defined(_SQUID_CYGWIN_))
+/* When USE_WIN32_SERVICE is defined, the main function is placed in win32.c */
+void WINAPI
+SquidMain(int argc, char **argv)
+#else
 int
 main(int argc, char **argv)
+#endif
 {
     int errcount = 0;
     int n;			/* # of GC'd objects */
     time_t loop_delay;
     mode_t oldmask;
-#if defined(_SQUID_MSWIN_) || defined(_SQUID_CYGWIN_)
-    int WIN32_init_err;
-#endif
 
     debug_log = stderr;
     if (FD_SETSIZE < Squid_MaxFD)
 	Squid_MaxFD = FD_SETSIZE;
 
 #if defined(_SQUID_MSWIN_) || defined(_SQUID_CYGWIN_)
-    if ((WIN32_init_err = WIN32_Subsystem_Init()))
-	return WIN32_init_err;
+#ifdef USE_WIN32_SERVICE
+    if (WIN32_Subsystem_Init(&argc, &argv))
+	return;
+#else
+    {
+	int WIN32_init_err;	
+	if ((WIN32_init_err = WIN32_Subsystem_Init()))
+	    return WIN32_init_err;
+    }
+#endif
 #endif
 
     /* call mallopt() before anything else */
@@ -621,8 +708,26 @@
     squid_start = current_time;
     failure_notify = fatal_dump;
 
+#if defined(USE_WIN32_SERVICE) && (defined(_SQUID_MSWIN_) || defined(_SQUID_CYGWIN_))
+    WIN32_svcstatusupdate(SERVICE_START_PENDING);
+#endif
     mainParseOptions(argc, argv);
 
+#if defined(USE_WIN32_SERVICE) && (defined(_SQUID_MSWIN_) || defined(_SQUID_CYGWIN_))
+    if (opt_install_service) {
+	WIN32_InstallService();
+	return;
+    }
+    if (opt_remove_service) {
+	WIN32_RemoveService();
+	return;
+    }
+    if (opt_command_line) {
+	WIN32_SetServiceCommandLine();
+	return;
+    }
+#endif
+
     /* parse configuration file
      * note: in "normal" case this used to be called from mainInitialize() */
     {
@@ -641,7 +746,11 @@
 	parse_err = parseConfigFile(ConfigFile);
 
 	if (opt_parse_cfg_only)
+#if defined(USE_WIN32_SERVICE) && (defined(_SQUID_MSWIN_) || defined(_SQUID_CYGWIN_))
+	    return;
+#else
 	    return parse_err;
+#endif
     }
     if (-1 == opt_send_signal)
 	if (checkRunningPid())
@@ -672,7 +781,11 @@
 	setEffectiveUser();
 	debug(0, 0) ("Creating Swap Directories\n");
 	storeCreateSwapDirectories();
+#if defined(USE_WIN32_SERVICE) && (defined(_SQUID_MSWIN_) || defined(_SQUID_CYGWIN_))
+	return;
+#else
 	return 0;
+#endif
     }
     if (!opt_no_daemon)
 	watch_child(argv);
@@ -686,14 +799,25 @@
     comm_init();
     comm_select_init();
 
+#if defined(_SQUID_MSWIN_) || defined(_SQUID_CYGWIN_)
+    if ((WIN32_run_mode == _WIN_SQUID_RUN_MODE_INTERACTIVE) && (opt_no_daemon)) {
+#else
     if (opt_no_daemon) {
+#endif
 	/* we have to init fdstat here. */
 	fd_open(0, FD_LOG, "stdin");
 	fd_open(1, FD_LOG, "stdout");
 	fd_open(2, FD_LOG, "stderr");
     }
+#if defined(USE_WIN32_SERVICE) && (defined(_SQUID_MSWIN_) || defined(_SQUID_CYGWIN_))
+    WIN32_svcstatusupdate(SERVICE_START_PENDING);
+#endif
     mainInitialize();
 
+#if defined(USE_WIN32_SERVICE) && (defined(_SQUID_MSWIN_) || defined(_SQUID_CYGWIN_))
+    WIN32_svcstatusupdate(SERVICE_RUNNING);
+#endif
+
     /* main loop */
     for (;;) {
 	if (do_reconfigure) {
@@ -711,6 +835,9 @@
 	    do_shutdown = 0;
 	    shutting_down = 1;
 	    serverConnectionsClose();
+#if defined(USE_WIN32_SERVICE) && (defined(_SQUID_MSWIN_) || defined(_SQUID_CYGWIN_))
+	    WIN32_svcstatusupdate(SERVICE_STOP_PENDING);
+#endif
 #if USE_DNSSERVERS
 	    dnsShutdown();
 #else
@@ -747,7 +874,11 @@
 	}
     }
     /* NOTREACHED */
+#if defined(USE_WIN32_SERVICE) && (defined(_SQUID_MSWIN_) || defined(_SQUID_CYGWIN_))
+    return;
+#else
     return 0;
+#endif
 }
 
 static void
@@ -757,6 +888,11 @@
     debug_log = stderr;
     pid = readPidFile();
     if (pid > 1) {
+#if defined(USE_WIN32_SERVICE) && (defined(_SQUID_MSWIN_) || defined(_SQUID_CYGWIN_))
+	if (opt_signal_service)
+	    WIN32_sendSignal(opt_send_signal);
+	else {
+#endif
 	if (kill(pid, opt_send_signal) &&
 	/* ignore permissions if just running check */
 	    !(opt_send_signal == 0 && errno == EPERM)) {
@@ -765,6 +901,9 @@
 		opt_send_signal, (int) pid, xstrerror());
 	    exit(1);
 	}
+#if defined(USE_WIN32_SERVICE) && (defined(_SQUID_MSWIN_) || defined(_SQUID_CYGWIN_))
+	}
+#endif
     } else {
 	fprintf(stderr, "%s: ERROR: No running copy\n", appname);
 	exit(1);
@@ -773,6 +912,7 @@
     exit(0);
 }
 
+#ifndef _SQUID_MSWIN_
 /*
  * This function is run when Squid is in daemon mode, just
  * before the parent forks and starts up the child process.
@@ -809,6 +949,7 @@
 	} while (rpid != cpid);
     }
 }
+#endif
 
 static int
 checkRunningPid(void)
@@ -827,6 +968,7 @@
 static void
 watch_child(char *argv[])
 {
+#ifndef _SQUID_MSWIN_
     char *prog;
     int failcount = 0;
     time_t start;
@@ -928,6 +1070,7 @@
 	sleep(3);
     }
     /* NOTREACHED */
+#endif
 }
 
 static void
@@ -984,7 +1127,11 @@
     errorClean();
 #endif
 #if !XMALLOC_TRACE
+#if defined(_SQUID_MSWIN_) || defined(_SQUID_CYGWIN_)
+    if ((WIN32_run_mode == _WIN_SQUID_RUN_MODE_INTERACTIVE) && (opt_no_daemon)) {
+#else
     if (opt_no_daemon) {
+#endif
 	file_close(0);
 	file_close(1);
 	file_close(2);
Index: squid/src/protos.h
===================================================================
RCS file: /cvsroot/squid-sf//squid/src/protos.h,v
retrieving revision 1.41.6.3
retrieving revision 1.41.6.3.4.1
diff -u -r1.41.6.3 -r1.41.6.3.4.1
--- squid/src/protos.h	18 Feb 2002 15:01:19 -0000	1.41.6.3
+++ squid/src/protos.h	14 Mar 2002 20:23:44 -0000	1.41.6.3.4.1
@@ -1,6 +1,6 @@
 
 /*
- * $Id: protos.h,v 1.41.6.3 2002/02/18 15:01:19 squidadm Exp $
+ * $Id: protos.h,v 1.41.6.3.4.1 2002/03/14 20:23:44 serassio Exp $
  *
  *
  * SQUID Web Proxy Cache          http://www.squid-cache.org/
@@ -787,6 +787,8 @@
 
 extern void serverConnectionsClose(void);
 extern void shut_down(int);
+extern void rotate_logs(int);
+extern void reconfigure(int);
 
 
 extern void start_announce(void *unused);
@@ -1202,7 +1204,11 @@
     const char *const args[],
     const char *name,
     int *rfd,
-    int *wfd);
+    int *wfd
+#ifdef _SQUID_MSWIN_
+    , HANDLE * hIpc, pid_t * ppid
+#endif
+);
 
 /* CacheDigest */
 extern CacheDigest *cacheDigestCreate(int capacity, int bpe);
@@ -1324,8 +1330,25 @@
 /* CygWin & Windows NT Port */
 /* win32.c */
 #if defined(_SQUID_MSWIN_) || defined(_SQUID_CYGWIN_)
+#ifdef USE_WIN32_SERVICE
+extern int WIN32_Subsystem_Init(int *, char ***);
+#else
 extern int WIN32_Subsystem_Init(void);
+#endif
+extern void WIN32_sendSignal(int);
+extern void WIN32_Abort(int);
 extern void WIN32_Exit(void);
+extern void WIN32_SetServiceCommandLine(void);
+extern void WIN32_InstallService(void);
+extern void WIN32_RemoveService(void);
+#if defined(_SQUID_MSWIN_) && !defined(COMM_C)
+#define comm_open(sock_type,proto,addr,port,flags,note) \
+	comm_open((sock_type), \
+		(sock_type) == SOCK_DGRAM ? IPPROTO_UDP : IPPROTO_TCP, \
+		(addr),(port),(flags),(note))
+#endif
+extern int WIN32_getrusage(int, struct rusage *);
+extern int WIN32_is_interface_down(const char *);
 #endif
 
 #endif /* SQUID_PROTOS_H */
Index: squid/src/squid.h
===================================================================
RCS file: /cvsroot/squid-sf//squid/src/squid.h,v
retrieving revision 1.13.6.2
retrieving revision 1.13.6.2.4.1
diff -u -r1.13.6.2 -r1.13.6.2.4.1
--- squid/src/squid.h	8 Jan 2002 05:19:18 -0000	1.13.6.2
+++ squid/src/squid.h	14 Mar 2002 20:23:45 -0000	1.13.6.2.4.1
@@ -1,6 +1,6 @@
 
 /*
- * $Id: squid.h,v 1.13.6.2 2002/01/08 05:19:18 squidadm Exp $
+ * $Id: squid.h,v 1.13.6.2.4.1 2002/03/14 20:23:45 serassio Exp $
  *
  * AUTHOR: Duane Wessels
  *
@@ -211,7 +211,7 @@
 #if HAVE_LIMITS_H
 #include <limits.h>
 #endif
-#if defined(_SQUID_CYGWIN_)
+#if defined(_SQUID_MSWIN_) || defined(_SQUID_CYGWIN_)
 #include <io.h>
 #endif
 
@@ -479,5 +479,13 @@
 
 #define FD_READ_METHOD(fd, buf, len) (*fd_table[fd].read_method)(fd, buf, len)
 #define FD_WRITE_METHOD(fd, buf, len) (*fd_table[fd].write_method)(fd, buf, len)
+
+#ifndef IPPROTO_UDP
+#define IPPROTO_UDP 0
+#endif
+
+#ifndef IPPROTO_TCP
+#define IPPROTO_TCP 0
+#endif
 
 #endif /* SQUID_H */
Index: squid/src/stat.c
===================================================================
RCS file: /cvsroot/squid-sf//squid/src/stat.c,v
retrieving revision 1.13
retrieving revision 1.13.10.1
diff -u -r1.13 -r1.13.10.1
--- squid/src/stat.c	24 Oct 2001 09:42:13 -0000	1.13
+++ squid/src/stat.c	14 Mar 2002 20:23:45 -0000	1.13.10.1
@@ -1,6 +1,6 @@
 
 /*
- * $Id: stat.c,v 1.13 2001/10/24 09:42:13 squidadm Exp $
+ * $Id: stat.c,v 1.13.10.1 2002/03/14 20:23:45 serassio Exp $
  *
  * DEBUG: section 18    Cache Manager Statistics
  * AUTHOR: Harvest Derived
@@ -459,6 +459,15 @@
 	runtime = 1.0;
     storeAppendPrintf(sentry, "Squid Object Cache: Version %s\n",
 	version_string);
+#if defined(_SQUID_MSWIN_) || defined(_SQUID_CYGWIN_)
+    if (WIN32_run_mode == _WIN_SQUID_RUN_MODE_SERVICE) {
+	storeAppendPrintf(sentry,"\nRunning as %s Windows System Service on %s\n",
+	WIN32_Service_name, WIN32_OS_string);
+	storeAppendPrintf(sentry,"Service command line is: %s\n", WIN32_Service_Command_Line);
+    }
+    else
+	storeAppendPrintf(sentry,"Running on %s\n",WIN32_OS_string);
+#endif
     storeAppendPrintf(sentry, "Start Time:\t%s\n",
 	mkrfc1123(squid_start.tv_sec));
     storeAppendPrintf(sentry, "Current Time:\t%s\n",
Index: squid/src/store_dir.c
===================================================================
RCS file: /cvsroot/squid-sf//squid/src/store_dir.c,v
retrieving revision 1.19
retrieving revision 1.19.14.1
diff -u -r1.19 -r1.19.14.1
--- squid/src/store_dir.c	24 Oct 2001 09:42:14 -0000	1.19
+++ squid/src/store_dir.c	14 Mar 2002 20:23:45 -0000	1.19.14.1
@@ -1,6 +1,6 @@
 
 /*
- * $Id: store_dir.c,v 1.19 2001/10/24 09:42:14 squidadm Exp $
+ * $Id: store_dir.c,v 1.19.14.1 2002/03/14 20:23:45 serassio Exp $
  *
  * DEBUG: section 47    Store Directory Routines
  * AUTHOR: Duane Wessels
@@ -32,7 +32,8 @@
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
  *
  */
-
+#ifndef STORE_DIR_C
+#define STORE_DIR_C
 #include "squid.h"
 
 #if HAVE_STATVFS
@@ -78,13 +79,18 @@
 {
     int i;
     SwapDir *sd;
+#ifndef _SQUID_MSWIN_
     pid_t pid;
     int status;
+#endif
     for (i = 0; i < Config.cacheSwap.n_configured; i++) {
+#ifndef _SQUID_MSWIN_
 	if (fork())
 	    continue;
+#endif
 	sd = &Config.cacheSwap.swapDirs[i];
 	sd->newfs(sd);
+#ifndef _SQUID_MSWIN_
 	exit(0);
     }
     do {
@@ -94,6 +100,9 @@
 	pid = waitpid(-1, &status, 0);
 #endif
     } while (pid > 0 || (pid < 0 && errno == EINTR));
+#else
+    }
+#endif
 }
 
 /*
@@ -542,3 +551,4 @@
 #endif
     return 0;
 }
+#endif
Index: squid/src/store_io.c
===================================================================
RCS file: /cvsroot/squid-sf//squid/src/store_io.c,v
retrieving revision 1.3
retrieving revision 1.3.14.1
diff -u -r1.3 -r1.3.14.1
--- squid/src/store_io.c	24 Oct 2001 09:42:14 -0000	1.3
+++ squid/src/store_io.c	14 Mar 2002 20:23:45 -0000	1.3.14.1
@@ -1,3 +1,5 @@
+#ifndef STORE_IO_C
+#define STORE_IO_C
 #include "squid.h"
 
 static struct {
@@ -115,3 +117,4 @@
     storeAppendPrintf(sentry, "create.create_fail %d\n", store_io_stats.create.create_fail);
     storeAppendPrintf(sentry, "create.success %d\n", store_io_stats.create.success);
 }
+#endif
Index: squid/src/structs.h
===================================================================
RCS file: /cvsroot/squid-sf//squid/src/structs.h,v
retrieving revision 1.48
retrieving revision 1.48.6.1
diff -u -r1.48 -r1.48.6.1
--- squid/src/structs.h	1 Dec 2001 20:27:54 -0000	1.48
+++ squid/src/structs.h	14 Mar 2002 20:23:45 -0000	1.48.6.1
@@ -1,6 +1,6 @@
 
 /*
- * $Id: structs.h,v 1.48 2001/12/01 20:27:54 squidadm Exp $
+ * $Id: structs.h,v 1.48.6.1 2002/03/14 20:23:45 serassio Exp $
  *
  *
  * SQUID Web Proxy Cache          http://www.squid-cache.org/
@@ -748,6 +748,9 @@
     unsigned int type;
     u_short local_port;
     u_short remote_port;
+#ifdef _SQUID_MSWIN_
+    FILE * fbuf;
+#endif
     struct in_addr local_addr;
     unsigned char tos;
     char ipaddr[16];		/* dotted decimal address of peer */
@@ -2051,6 +2054,10 @@
     struct {
 	int uses;
     } stats;
+#ifdef _SQUID_MSWIN_
+    HANDLE hIpc;
+    pid_t pid;
+#endif
 };
 
 
Index: squid/src/tools.c
===================================================================
RCS file: /cvsroot/squid-sf//squid/src/tools.c,v
retrieving revision 1.19
retrieving revision 1.19.14.1
diff -u -r1.19 -r1.19.14.1
--- squid/src/tools.c	18 Nov 2001 01:15:42 -0000	1.19
+++ squid/src/tools.c	14 Mar 2002 20:23:46 -0000	1.19.14.1
@@ -1,6 +1,6 @@
 
 /*
- * $Id: tools.c,v 1.19 2001/11/18 01:15:42 squidadm Exp $
+ * $Id: tools.c,v 1.19.14.1 2002/03/14 20:23:46 serassio Exp $
  *
  * DEBUG: section 21    Misc Functions
  * AUTHOR: Harvest Derived
@@ -305,6 +305,10 @@
 void
 sigusr2_handle(int sig)
 {
+#if defined(_SQUID_MSWIN_) && defined(_DEBUG)
+    do_debug = 1;
+#endif
+#if (defined(_SQUID_MSWIN_) && !defined(_DEBUG)) || !defined(_SQUID_MSWIN_)
     static int state = 0;
     /* no debug() here; bad things happen if the signal is delivered during _db_print() */
     if (state == 0) {
@@ -322,6 +326,7 @@
 #endif
 	state = 0;
     }
+#endif
 #if !HAVE_SIGACTION
     signal(sig, sigusr2_handle);	/* reinstall */
 #endif
@@ -415,6 +420,7 @@
 void
 sig_child(int sig)
 {
+#ifndef _SQUID_MSWIN_
 #ifdef _SQUID_NEXT_
     union wait status;
 #else
@@ -435,6 +441,7 @@
     } while (pid > 0 || (pid < 0 && errno == EINTR));
     signal(sig, sig_child);
 #endif
+#endif
 }
 
 const char *
@@ -924,6 +931,9 @@
 xrename(const char *from, const char *to)
 {
     debug(21, 2) ("xrename: renaming %s to %s\n", from, to);
+#ifdef _SQUID_MSWIN_
+    remove(to);
+#endif
     if (0 == rename(from, to))
 	return 0;
     debug(21, errno == ENOENT ? 2 : 1) ("xrename: Cannot rename %s to %s: %s\n",
@@ -966,9 +976,6 @@
     char buf2[512];
     char *nt = buf;
     char *lt = buf;
-#if defined(_SQUID_MSWIN_) || defined(_SQUID_CYGWIN_)
-    char *systemroot = NULL;
-#endif
     if (NULL == Config.etcHostsPath)
 	return;
     if (0 == strcmp(Config.etcHostsPath, "none"))
Index: squid/src/win32.c
===================================================================
RCS file: /cvsroot/squid-sf//squid/src/win32.c,v
retrieving revision 1.5
retrieving revision 1.5.14.1
diff -u -r1.5 -r1.5.14.1
--- squid/src/win32.c	18 Nov 2001 01:15:42 -0000	1.5
+++ squid/src/win32.c	14 Mar 2002 20:23:46 -0000	1.5.14.1
@@ -22,18 +22,189 @@
  *
  */
 
+#ifndef WIN32_C
+#define WIN32_C
+
 #include "squid.h"
 
 /* This code compiles only CygWin & Windows NT Port */
 #if defined(_SQUID_MSWIN_) || defined(_SQUID_CYGWIN_)
 #include <windows.h>
+#ifdef _SQUID_MSWIN_
+#if HAVE_WIN32_PSAPI
+#include <psapi.h>
+#endif
+#include <process.h>
+#endif
 
 static unsigned int GetOSVersion();
+void WIN32_svcstatusupdate(DWORD);
+void WINAPI WIN32_svcHandler(DWORD);
+static int WIN32_StoreKey(const char *, DWORD, unsigned char *, int);
+static int WIN32_create_key(void);
+static void WIN32_build_argv (char *);
+void WINAPI SquidMain(DWORD, char **);
+
+/* The following code section is part of an EXPERIMENTAL native */
+/* Windows NT/2000 Squid port - Compiles only on MS Visual C++  */
+#if defined(_SQUID_MSWIN_)
+static int Win32SockInit(void);
+static void Win32SockCleanup(void);
+#endif /* End native Windows NT EXPERIMENTAL PORT */
+
+static SERVICE_STATUS svcStatus;
+static SERVICE_STATUS_HANDLE svcHandle;
+static int WIN32_argc;
+static char ** WIN32_argv;
+static char * WIN32_module_name;
+
+#define VENDOR   "GNU"
+#if defined(_SQUID_MSWIN_)
+#define SOFTWARENAME "SquidNT"
+#else
+#define SOFTWARENAME "Squid"
+#endif
+#define WIN32_VERSION  "2.5"
+#define COMMANDLINE "CommandLine"
+#define CONFIGFILE  "ConfigFile"
+#undef ChangeServiceConfig2
+typedef BOOL (WINAPI * PFChangeServiceConfig2) (SC_HANDLE, DWORD, LPVOID);
+#ifdef UNICODE
+#define CHANGESERVICECONFIG2 "ChangeServiceConfig2W"
+#else
+#define CHANGESERVICECONFIG2 "ChangeServiceConfig2A"
+#endif
+static SC_ACTION Squid_SCAction[] = { { SC_ACTION_RESTART, 60000 } };
+static SERVICE_DESCRIPTION Squid_ServiceDescription = { SOFTWARENAME " " VERSION " WWW Proxy Server" };
+static SERVICE_FAILURE_ACTIONS Squid_ServiceFailureActions = { 0, NULL, NULL, 1, Squid_SCAction };
+static char REGKEY[256]="SOFTWARE\\"VENDOR"\\"SOFTWARENAME"\\"WIN32_VERSION"\\";
+static char *keys[] = { 
+    "SOFTWARE",	    /* key[0] */
+    VENDOR,	    /* key[1] */
+    SOFTWARENAME,   /* key[2] */
+    WIN32_VERSION,  /* key[3] */
+    NULL,	    /* key[4] */
+    NULL	    /* key[5] */
+};
 
 /* ====================================================================== */
 /* LOCAL FUNCTIONS */
 /* ====================================================================== */
 
+static int
+WIN32_create_key(void)
+{
+    int index;
+    HKEY hKey;
+    HKEY hKeyNext;
+    int retval;
+    long rv;
+
+    hKey = HKEY_LOCAL_MACHINE;
+    index = 0;
+    retval = 0;
+
+    /* Walk the tree, creating at each stage if necessary */
+    while (keys[index]) {
+	unsigned long result;
+	rv = RegCreateKeyEx(hKey, keys[index],	/* subkey */
+	    0,			/* reserved */
+	    NULL,		/* class */
+	    REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKeyNext, &result);
+	if (rv != ERROR_SUCCESS) {
+	    debug(1, 1) ("RegCreateKeyEx(%s),%d\n", keys[index], (int) rv);
+	    retval = -4;
+	}
+	/* Close the old key */
+	rv = RegCloseKey(hKey);
+	if (rv != ERROR_SUCCESS) {
+	    debug(1, 1) ("RegCloseKey %d\n", (int) rv);
+	    if (retval == 0) {
+		/* Keep error status from RegCreateKeyEx, if any */
+		retval = -4;
+	    }
+	}
+	if (retval) {
+	    break;
+	}
+	hKey = hKeyNext;
+	index++;
+    }
+    if (keys[index] == NULL) {
+	/* Close the final key we opened, if we walked the entire
+	 * tree
+	 */
+	rv = RegCloseKey(hKey);
+	if (rv != ERROR_SUCCESS) {
+	    debug(1, 1) ("RegCloseKey %d\n", (int) rv);
+	    if (retval == 0) {
+		/* Keep error status from RegCreateKeyEx, if any */
+		retval = -4;
+	    }
+	}
+    }
+    return retval;
+}
+
+static int
+WIN32_StoreKey(const char *key, DWORD type, unsigned char *value,
+    int value_size)
+{
+    long rv;
+    HKEY hKey;
+    int retval;
+
+    rv = RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGKEY, 0, KEY_WRITE, &hKey);
+    if (rv == ERROR_FILE_NOT_FOUND) {
+	/* Key could not be opened -- try to create it 
+	 */
+	if (WIN32_create_key() < 0) {
+	    /* Creation failed (error already reported) */
+	    return -4;
+	}
+	/* Now it has been created we should be able to open it
+	 */
+	rv = RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGKEY, 0, KEY_WRITE, &hKey);
+	if (rv == ERROR_FILE_NOT_FOUND) {
+	    debug(1, 1) ("Registry does not contain key %s after creation",
+		REGKEY);
+	    return -1;
+	}
+    }
+    if (rv != ERROR_SUCCESS) {
+	debug(1, 1) ("RegOpenKeyEx HKLM\\%s, %d\n", REGKEY, (int) rv);
+	return -4;
+    }
+    /* Now set the value and data */
+    rv = RegSetValueEx(hKey, key,	/* value key name */
+	0,			/* reserved */
+	type,			/* type */
+	value,			/* value data */
+	(DWORD) value_size);	/* for size of "value" */
+    retval = 0;			/* Return value */
+    if (rv != ERROR_SUCCESS) {
+	debug(1, 1) ("RegQueryValueEx(key %s),%d\n", key, (int) rv);
+	retval = -4;
+    } else {
+	debug(1, 1) ("Registry stored HKLM\\%s\\%s value %s\n",
+	    REGKEY,
+	    key,
+	    type == REG_SZ ? value : (unsigned char *) "(not displayable)");
+    }
+    /* Make sure we close the key even if there was an error storing
+     * the data
+     */
+    rv = RegCloseKey(hKey);
+    if (rv != ERROR_SUCCESS) {
+	debug(1, 1) ("RegCloseKey HKLM\\%s, %d\n", REGKEY, (int) rv);
+	if (retval == 0) {
+	    /* Keep error status from RegQueryValueEx, if any */
+	    retval = -4;
+	}
+    }
+    return retval;
+}
+
 static unsigned int
 GetOSVersion()
 {
@@ -83,24 +254,546 @@
     return _WIN_OS_UNKNOWN;
 }
 
+/* Build argv, argc from string passed from Windows.  */
+static void WIN32_build_argv(char *cmd)
+{
+    int argvlen = 0;
+    char *word;
+
+    WIN32_argc = 1;
+    WIN32_argv = (char **) xmalloc ((WIN32_argc+1) * sizeof (char *));
+    WIN32_argv[0]=xstrdup(WIN32_module_name);    
+/* Scan command line until there is nothing left. */
+    while (*cmd) {
+	/* Ignore spaces */
+	if (xisspace(*cmd)) {
+	    cmd++;
+	    continue;
+	}
+	/* Found the beginning of an argument. */
+	word = cmd;
+	while (*cmd) {
+	    cmd++;		/* Skip over this character */
+	    if (xisspace(*cmd))	/* End of argument if space */
+		break;
+	}
+	if (*cmd)
+	    *cmd++ = '\0';		/* Terminate `word' */
+	/* See if we need to allocate more space for argv */
+	if (WIN32_argc >= argvlen) {
+	    argvlen = WIN32_argc + 1;
+	    WIN32_argv = (char **) xrealloc (WIN32_argv, (1 + argvlen) * sizeof (char *));
+	}
+	/* Add word to argv file. */
+	WIN32_argv[WIN32_argc++] = word;
+    }
+    WIN32_argv[WIN32_argc] = NULL;
+}
+
 /* ====================================================================== */
 /* PUBLIC FUNCTIONS */
 /* ====================================================================== */
 
 void
+WIN32_Abort(int sig)
+{
+#ifdef USE_WIN32_SERVICE
+    svcStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
+    svcStatus.dwServiceSpecificExitCode = 1;
+#endif
+    WIN32_Exit();
+}
+
+void
 WIN32_Exit()
 {
+#ifdef _SQUID_MSWIN_
+    Win32SockCleanup();
+#endif
+#ifdef USE_WIN32_SERVICE
+    svcStatus.dwCurrentState = SERVICE_STOPPED;
+    SetServiceStatus(svcHandle, &svcStatus);
+#endif
     _exit(0);
 }
 
-int
-WIN32_Subsystem_Init()
+#ifdef USE_WIN32_SERVICE
+int WIN32_Subsystem_Init(int * argc, char *** argv)
+#else
+int WIN32_Subsystem_Init()
+#endif
 {
     WIN32_OS_version = GetOSVersion();
     if ((WIN32_OS_version == _WIN_OS_UNKNOWN) || (WIN32_OS_version == _WIN_OS_WIN32S))
 	return 1;
     if (atexit(WIN32_Exit) != 0)
 	return 1;
+#ifdef USE_WIN32_SERVICE
+    if (WIN32_run_mode == _WIN_SQUID_RUN_MODE_SERVICE) {
+	char path[512];
+	HKEY hndKey;
+	if (signal(SIGABRT, WIN32_Abort) == SIG_ERR)
+	    return 1;
+	/* Register the service Handler function */
+	svcHandle =
+	    RegisterServiceCtrlHandler(WIN32_Service_name,
+	    WIN32_svcHandler);
+	if (svcHandle == 0)
+	    return 1;
+	/* Set Service Staus to SERVICE_START_PENDING */
+	svcStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
+	svcStatus.dwCurrentState = SERVICE_START_PENDING;
+	svcStatus.dwControlsAccepted =
+	    SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
+	svcStatus.dwWin32ExitCode = 0;
+	svcStatus.dwServiceSpecificExitCode = 0;
+	svcStatus.dwCheckPoint = 0;
+	svcStatus.dwWaitHint = 10000;
+	SetServiceStatus(svcHandle, &svcStatus);
+	/* Set Process work dir to directory cointaining squid.exe */
+	GetModuleFileName(NULL, path, 512);
+	WIN32_module_name=xstrdup(path);
+	path[strlen(path) - 10] = '\0';
+	if (SetCurrentDirectory(path) == 0)
+	    return 1;
+	safe_free(ConfigFile);
+	/* get config file from Windows Registry */
+	if (RegOpenKey(HKEY_LOCAL_MACHINE, REGKEY, &hndKey) == ERROR_SUCCESS) {
+	    DWORD Type = 0;
+	    DWORD Size = 0;
+	    LONG Result;
+	    Result =
+		RegQueryValueEx(hndKey, CONFIGFILE, NULL, &Type, NULL, &Size);
+	    if (Result == ERROR_SUCCESS && Size) {
+		ConfigFile = xmalloc(Size);
+		RegQueryValueEx(hndKey, CONFIGFILE, NULL, &Type, ConfigFile,
+		    &Size);
+	    } else
+		ConfigFile = xstrdup(DefaultConfigFile);
+	    Size = 0;
+	    Type = 0;
+	    Result =
+		RegQueryValueEx(hndKey, COMMANDLINE, NULL, &Type, NULL, &Size);
+	    if (Result == ERROR_SUCCESS && Size) {
+		WIN32_Service_Command_Line = xmalloc(Size);
+		RegQueryValueEx(hndKey, COMMANDLINE, NULL, &Type, WIN32_Service_Command_Line,
+		    &Size);
+	    } else
+		WIN32_Service_Command_Line = xstrdup("");
+	    RegCloseKey(hndKey);
+	} else {
+	    ConfigFile = xstrdup(DefaultConfigFile);
+	    WIN32_Service_Command_Line = xstrdup("");
+	}
+	WIN32_build_argv(WIN32_Service_Command_Line);
+	*argc = WIN32_argc;
+	*argv = WIN32_argv;
+    }
+#endif
+#ifdef _SQUID_MSWIN_
+    Win32SockInit();
+#endif
+    return 0;
+}
+
+#ifdef USE_WIN32_SERVICE
+void
+WIN32_svcstatusupdate(DWORD svcstate)
+{
+    if (WIN32_run_mode == _WIN_SQUID_RUN_MODE_SERVICE) {
+	svcStatus.dwCheckPoint++;
+	svcStatus.dwCurrentState = svcstate;
+	SetServiceStatus(svcHandle, &svcStatus);
+    }
+}
+
+VOID WINAPI
+WIN32_svcHandler(DWORD Opcode)
+{
+    DWORD status;
+
+    switch (Opcode) {
+    case _WIN_SQUID_SERVICE_CONTROL_STOP:
+    case _WIN_SQUID_SERVICE_CONTROL_SHUTDOWN:
+/* Do whatever it takes to stop here. */
+	svcStatus.dwWin32ExitCode = 0;
+	svcStatus.dwCurrentState = SERVICE_STOP_PENDING;
+	svcStatus.dwCheckPoint = 0;
+	svcStatus.dwWaitHint = 10000;
+	shut_down(SIGTERM);
+	if (!SetServiceStatus(svcHandle, &svcStatus)) {
+	    status = GetLastError();
+	    debug(1, 1) (" SetServiceStatus error  %ld\n", status);
+	}
+	debug(1, 1) ("Leaving Squid service \n");
+	return;
+    case _WIN_SQUID_SERVICE_CONTROL_INTERROGATE:
+/* Fall through to send current status. */
+	if (!SetServiceStatus(svcHandle, &svcStatus)) {
+	    status = GetLastError();
+	    debug(1, 1) (" SetServiceStatus error  %ld\n", status);
+	}
+	break;
+    case _WIN_SQUID_SERVICE_CONTROL_ROTATE:
+	rotate_logs(SIGUSR1);
+	break;
+    case _WIN_SQUID_SERVICE_CONTROL_RECONFIGURE:
+	reconfigure(SIGHUP);
+	break;
+    case _WIN_SQUID_SERVICE_CONTROL_DEBUG:
+	sigusr2_handle(SIGUSR2);
+	break;
+    case _WIN_SQUID_SERVICE_CONTROL_INTERRUPT:
+/* Do whatever it takes to stop here. */
+	svcStatus.dwWin32ExitCode = 0;
+	svcStatus.dwCurrentState = SERVICE_STOP_PENDING;
+	svcStatus.dwCheckPoint = 0;
+	svcStatus.dwWaitHint = 10000;
+	shut_down(SIGINT);
+	if (!SetServiceStatus(svcHandle, &svcStatus)) {
+	    status = GetLastError();
+	    debug(1, 1) (" SetServiceStatus error  %ld\n", status);
+	}
+	debug(1, 1) ("Leaving Squid service \n");
+	break;
+    default:
+	debug(1, 1) ("Unrecognized opcode %ld\n", Opcode);
+    }
+    return;
+}
+
+void
+WIN32_RemoveService()
+{
+    SC_HANDLE schService;
+    SC_HANDLE schSCManager;
+
+    if (!WIN32_Service_name)
+	WIN32_Service_name = xstrdup(_WIN_SQUID_DEFAULT_SERVICE_NAME);
+    strcat(REGKEY, WIN32_Service_name);
+    keys[4] = WIN32_Service_name;
+    schSCManager = OpenSCManager(NULL,	/* machine (NULL == local)    */
+	NULL,			/* database (NULL == default) */
+	SC_MANAGER_ALL_ACCESS	/* access required            */
+	);
+    if (!schSCManager)
+	debug(1, 1) ("OpenSCManager failed");
+    else {
+	schService = OpenService(schSCManager, WIN32_Service_name, SERVICE_ALL_ACCESS);
+	if (schService == NULL)
+	    debug(1, 1) ("OpenService failed");
+	/* Could not open the service */
+	else {
+	    /* try to stop the service */
+	    if (ControlService(schService, _WIN_SQUID_SERVICE_CONTROL_STOP,
+		    &svcStatus)) {
+		sleep(1);
+		while (QueryServiceStatus(schService, &svcStatus)) {
+		    if (svcStatus.dwCurrentState == SERVICE_STOP_PENDING)
+			sleep(1);
+		    else
+			break;
+		}
+	    }
+	    /* now remove the service */
+	    if (DeleteService(schService) == 0)
+		fprintf(stderr,"DeleteService failed.\n");
+	    else
+		printf("Service %s deleted successfully.\n",
+		    WIN32_Service_name);
+	    CloseServiceHandle(schService);
+	}
+	CloseServiceHandle(schSCManager);
+    }
+}
+
+void
+WIN32_SetServiceCommandLine()
+{
+    if (!WIN32_Service_name)
+	WIN32_Service_name = xstrdup(_WIN_SQUID_DEFAULT_SERVICE_NAME);
+    strcat(REGKEY, WIN32_Service_name);
+    keys[4] = WIN32_Service_name;
+    /* Now store the Service Command Line in the registry */
+    WIN32_StoreKey(COMMANDLINE, REG_SZ, (unsigned char *) WIN32_Command_Line, strlen(WIN32_Command_Line) + 1);
+}
+
+void
+WIN32_InstallService()
+{
+    SC_HANDLE schService;
+    SC_HANDLE schSCManager;
+    char ServicePath[512];
+    char szPath[512];
+    int lenpath;
+    
+    if (!WIN32_Service_name)
+	WIN32_Service_name = xstrdup(_WIN_SQUID_DEFAULT_SERVICE_NAME);
+    strcat(REGKEY, WIN32_Service_name);
+    keys[4] = WIN32_Service_name;
+    if ((lenpath = GetModuleFileName(NULL, ServicePath, 512)) == 0) {
+	debug(1, 1) ("Can't get executable path");
+	exit(1);
+    }
+    snprintf(szPath, sizeof(szPath), "%s %s:%s", ServicePath, _WIN_SQUID_SERVICE_OPTION, WIN32_Service_name);
+    schSCManager = OpenSCManager(NULL,	/* machine (NULL == local)    */
+	NULL,			/* database (NULL == default) */
+	SC_MANAGER_ALL_ACCESS	/* access required            */
+	);
+    if (!schSCManager) {    
+	debug(1, 1) ("OpenSCManager failed");
+	exit(1);
+    }
+    else {
+	schService = CreateService(schSCManager,    /* SCManager database     */
+	    WIN32_Service_name,			    /* name of service        */
+	    WIN32_Service_name,			    /* name to display        */
+	    SERVICE_ALL_ACCESS,			    /* desired access         */
+	    SERVICE_WIN32_OWN_PROCESS,		    /* service type           */
+	    SERVICE_AUTO_START,			    /* start type             */
+	    SERVICE_ERROR_NORMAL,		    /* error control type     */
+	    (const char *) szPath,		    /* service's binary       */
+	    NULL,				    /* no load ordering group */
+	    NULL,				    /* no tag identifier      */
+	    "Tcpip\0AFD\0",			    /* dependencies           */
+	    NULL,				    /* LocalSystem account    */
+	    NULL);				    /* no password            */
+	if (schService) {
+	    if ((WIN32_OS_version == _WIN_OS_WIN2K) || (WIN32_OS_version == _WIN_OS_WINXP))
+	    {
+		HMODULE ADVAPI32Handle;
+		PFChangeServiceConfig2 ChangeServiceConfig2;
+		DWORD dwInfoLevel = SERVICE_CONFIG_DESCRIPTION;
+
+		ADVAPI32Handle = GetModuleHandle("advapi32");
+		ChangeServiceConfig2 = (PFChangeServiceConfig2) GetProcAddress(ADVAPI32Handle, CHANGESERVICECONFIG2);
+		ChangeServiceConfig2(schService, dwInfoLevel, &Squid_ServiceDescription);
+		dwInfoLevel = SERVICE_CONFIG_FAILURE_ACTIONS;
+		ChangeServiceConfig2(schService, dwInfoLevel, &Squid_ServiceFailureActions);
+	    } 
+	    CloseServiceHandle(schService);
+	    /* Now store the config file location in the registry */
+	    if (!ConfigFile)
+		ConfigFile = xstrdup(DefaultConfigFile);
+	    WIN32_StoreKey(CONFIGFILE, REG_SZ, (unsigned char *) ConfigFile, strlen(ConfigFile) + 1);
+	    printf("Squid Cache version %s for %s\n", version_string,
+		CONFIG_HOST_TYPE);
+	    printf("installed successfully as %s Windows System Service.\n",
+		WIN32_Service_name);
+	    printf
+		("To run, start it from the Services Applet of Control Panel.\n");
+	    printf("Don't forget to edit squid.conf before starting it.\n\n");
+	} else {
+	    debug(1, 1) ("CreateService failed");
+	    exit(1);
+	}
+	CloseServiceHandle(schSCManager);
+    }
+}
+
+void
+WIN32_sendSignal(int WIN32_signal)
+{
+    SERVICE_STATUS ssStatus;
+    DWORD fdwAccess, fdwControl;
+    SC_HANDLE schService;
+    SC_HANDLE schSCManager;
+
+    if (!WIN32_Service_name)
+	WIN32_Service_name = xstrdup(_WIN_SQUID_DEFAULT_SERVICE_NAME);
+    schSCManager = OpenSCManager(NULL,	/* machine (NULL == local)    */
+	NULL,			/* database (NULL == default) */
+	SC_MANAGER_ALL_ACCESS	/* access required            */
+	);
+    if (!schSCManager) {
+	debug(1, 1) ("OpenSCManager failed");
+	exit(1);
+    }
+    /* The required service object access depends on the control. */
+    switch (WIN32_signal) {
+    case 0:			/* SIGNULL */
+	fdwAccess = SERVICE_INTERROGATE;
+	fdwControl = _WIN_SQUID_SERVICE_CONTROL_INTERROGATE;
+	break;
+    case SIGUSR1:
+	fdwAccess = SERVICE_USER_DEFINED_CONTROL;
+	fdwControl = _WIN_SQUID_SERVICE_CONTROL_ROTATE;
+	break;
+    case SIGUSR2:
+	fdwAccess = SERVICE_USER_DEFINED_CONTROL;
+	fdwControl = _WIN_SQUID_SERVICE_CONTROL_DEBUG;
+	break;
+    case SIGHUP:
+	fdwAccess = SERVICE_USER_DEFINED_CONTROL;
+	fdwControl = _WIN_SQUID_SERVICE_CONTROL_RECONFIGURE;
+	break;
+    case SIGTERM:
+	fdwAccess = SERVICE_STOP;
+	fdwControl = _WIN_SQUID_SERVICE_CONTROL_STOP;
+	break;
+    case SIGINT:
+    case SIGKILL:
+	fdwAccess = SERVICE_USER_DEFINED_CONTROL;
+	fdwControl = _WIN_SQUID_SERVICE_CONTROL_INTERRUPT;
+	break;
+    default:
+	exit(1);
+    }
+    /* Open a handle to the service. */
+    schService = OpenService(schSCManager,	/* SCManager database */
+	WIN32_Service_name,	/* name of service    */
+	fdwAccess);		/* specify access     */
+    if (schService == NULL) {
+	fprintf(stderr, "%s: ERROR: Could not open Service %s\n", appname,
+	    WIN32_Service_name);
+	exit(1);
+    } else {
+	/* Send a control value to the service. */
+	if (!ControlService(schService,	/* handle of service      */
+		fdwControl,	/* control value to send  */
+		&ssStatus)) {	/* address of status info */
+	    fprintf(stderr, "%s: ERROR: Could not Control Service %s\n",
+		appname, WIN32_Service_name);
+	    exit(1);
+	} else {
+	    /* Print the service status. */
+	    printf("\nStatus of %s Service:\n", WIN32_Service_name);
+	    printf("  Service Type: 0x%lx\n", ssStatus.dwServiceType);
+	    printf("  Current State: 0x%lx\n", ssStatus.dwCurrentState);
+	    printf("  Controls Accepted: 0x%lx\n", ssStatus.dwControlsAccepted);
+	    printf("  Exit Code: %ld\n", ssStatus.dwWin32ExitCode);
+	    printf("  Service Specific Exit Code: %ld\n",
+		ssStatus.dwServiceSpecificExitCode);
+	    printf("  Check Point: %ld\n", ssStatus.dwCheckPoint);
+	    printf("  Wait Hint: %ld\n", ssStatus.dwWaitHint);
+	}
+	CloseServiceHandle(schService);
+    }
+    CloseServiceHandle(schSCManager);
+}
+
+int main(int argc, char **argv)
+{
+    SERVICE_TABLE_ENTRY DispatchTable[] = {
+	{NULL, SquidMain},
+	{NULL, NULL}
+    };
+    char *c;
+    
+    if ((argc == 2) && strstr(argv[1], _WIN_SQUID_SERVICE_OPTION)){
+	WIN32_run_mode = _WIN_SQUID_RUN_MODE_SERVICE;
+	if (!(c=strchr(argv[1],':'))){
+	    fprintf(stderr, "Bad Service Parameter: %s\n", argv[1]);
+	    return 1;
+	}
+	WIN32_Service_name = xstrdup(c+1);
+	DispatchTable[0].lpServiceName=WIN32_Service_name;
+	strcat(REGKEY, WIN32_Service_name);
+	keys[4] = WIN32_Service_name;
+    	if (!StartServiceCtrlDispatcher(DispatchTable)) {
+	    fprintf(stderr, "StartServiceCtrlDispatcher error = %ld\n",
+		GetLastError());
+	    return 1;
+	}
+    } else {
+    	WIN32_run_mode = _WIN_SQUID_RUN_MODE_INTERACTIVE;
+	SquidMain(argc, argv);
+    }
     return 0;
 }
 #endif
+
+/* The following code section is part of an EXPERIMENTAL native */
+/* Windows NT/2000 Squid port - Compiles only on MS Visual C++  */
+
+#if defined(_SQUID_MSWIN_)
+static int s_iInitCount = 0;
+
+int WIN32_getrusage(int who, struct rusage *usage)
+{
+#if HAVE_WIN32_PSAPI
+    if ((WIN32_OS_version == _WIN_OS_WINNT) || (WIN32_OS_version == _WIN_OS_WIN2K)
+	|| (WIN32_OS_version == _WIN_OS_WINXP))
+    {
+    	/* On Windows NT/2000 call PSAPI.DLL for process Memory */
+    	/* informations -- Guido Serassio                       */
+    	HANDLE hProcess;
+    	PROCESS_MEMORY_COUNTERS pmc;
+	hProcess = OpenProcess(PROCESS_QUERY_INFORMATION |
+	    PROCESS_VM_READ,
+	    FALSE, GetCurrentProcessId());
+	{
+	/* Microsoft Visual C++ doesn't have getrusage function,  */
+	/* so we get process CPU time information from PSAPI.DLL. */
+	    FILETIME ftCreate, ftExit, ftKernel, ftUser;
+	    if (GetProcessTimes(hProcess, &ftCreate, &ftExit, &ftKernel, &ftUser))
+	    {
+		LONGLONG tUser64 = (*(LONGLONG *)&ftUser / 10);
+		LONGLONG tKernel64 = (*(LONGLONG *)&ftKernel / 10);
+		usage->ru_utime.tv_sec =(long)(tUser64 / 1000000);
+		usage->ru_stime.tv_sec =(long)(tKernel64 / 1000000);
+		usage->ru_utime.tv_usec =(long)(tUser64 % 1000000);
+		usage->ru_stime.tv_usec =(long)(tKernel64 % 1000000);
+	    }
+	    else
+	    {
+		CloseHandle( hProcess );
+		return -1;
+	    }
+	}
+	if (GetProcessMemoryInfo( hProcess, &pmc, sizeof(pmc)))
+	{
+	    usage->ru_maxrss=(DWORD)(pmc.WorkingSetSize /1024);
+	    usage->ru_majflt=pmc.PageFaultCount;
+	}
+	else
+	{
+	    CloseHandle( hProcess );
+	    return -1;
+	}
+	CloseHandle( hProcess );
+    }
+#endif
+    return 0;
+}
+
+static int Win32SockInit(void)
+{
+    int iVersionRequested;
+    WSADATA wsaData;
+    int err;
+
+    if (s_iInitCount > 0) {
+	s_iInitCount++;
+	return (0);
+    }
+    else if (s_iInitCount < 0)
+	return (s_iInitCount);
+    /* s_iInitCount == 0. Do the initailization */
+    iVersionRequested = MAKEWORD(2, 0);
+    err = WSAStartup((WORD) iVersionRequested, &wsaData);
+    if (err) {
+	s_iInitCount = -1;
+	return (s_iInitCount);
+    }
+    if (LOBYTE(wsaData.wVersion) != 2 ||
+	HIBYTE(wsaData.wVersion) != 0) {
+	s_iInitCount = -2;
+	WSACleanup();
+	return (s_iInitCount);
+    }
+    debug(1,1)("Windows sockets initialised");
+    s_iInitCount++;
+    return (s_iInitCount);
+}
+
+static void Win32SockCleanup(void)
+{
+    if (--s_iInitCount == 0)
+	WSACleanup();
+    return;
+}
+#endif /* End native Windows NT EXPERIMENTAL PORT */
+#endif
+
+#endif /* WIN32_C */
--- /dev/null	Wed Feb 14 00:55:47 2007
+++ squid/src/fs/awin32/Makefile	Wed Feb 14 00:57:26 2007
@@ -0,0 +1,241 @@
+# Makefile.in generated automatically by automake 1.5 from Makefile.am.
+
+# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
+# Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+
+
+SHELL = /bin/sh
+
+srcdir = .
+top_srcdir = ../../..
+
+prefix = c:/squid
+exec_prefix = ${prefix}
+
+bindir = ${exec_prefix}/bin
+sbindir = ${exec_prefix}/sbin
+libexecdir = ${exec_prefix}/libexec
+datadir = ${prefix}/share
+sysconfdir = ${prefix}/etc
+sharedstatedir = ${prefix}/com
+localstatedir = ${prefix}/var
+libdir = ${exec_prefix}/lib
+infodir = ${prefix}/info
+mandir = ${prefix}/man
+includedir = ${prefix}/include
+oldincludedir = /usr/include
+pkgdatadir = $(datadir)/squid
+pkglibdir = $(libdir)/squid
+pkgincludedir = $(includedir)/squid
+top_builddir = ../../..
+
+ACLOCAL = ${SHELL} /cygdrive/c/work/cygwin-svc/cfgaux/missing --run aclocal
+AUTOCONF = ${SHELL} /cygdrive/c/work/cygwin-svc/cfgaux/missing --run autoconf
+AUTOMAKE = ${SHELL} /cygdrive/c/work/cygwin-svc/cfgaux/missing --run automake
+AUTOHEADER = ${SHELL} /cygdrive/c/work/cygwin-svc/cfgaux/missing --run autoheader
+
+INSTALL = /usr/bin/install -c
+INSTALL_PROGRAM = ${INSTALL}
+INSTALL_DATA = ${INSTALL} -m 644
+INSTALL_SCRIPT = ${INSTALL}
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = s,x,x,
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+host_alias = 
+host_triplet = i686-pc-cygwin
+AIOLIB = 
+AMTAR = ${SHELL} /cygdrive/c/work/cygwin-svc/cfgaux/missing --run tar
+AR = /usr/bin/ar
+AR_R = /usr/bin/ar r
+AUTH_LIBS = libbasic.a libntlm.a
+AUTH_MODULES = basic ntlm
+AUTH_OBJS = auth/libbasic.a auth/libntlm.a
+AWK = gawk
+BASIC_AUTH_HELPERS = win32_locallogon
+CC = gcc
+CPP = gcc -E
+CRYPTLIB = -lcrypt
+CXX = @CXX@
+DEPDIR = .deps
+DIGEST_AUTH_HELPERS = 
+DLLIB = 
+ERR_DEFAULT_LANGUAGE = English
+ERR_LANGUAGES =  Bulgarian Czech Danish Dutch English Estonian Finnish French German Hungarian Italian Japanese Korean Polish Portuguese Romanian Russian-1251 Russian-koi8-r Serbian Simplify_Chinese Slovak Spanish Swedish Traditional_Chinese Turkish
+EXEEXT = .exe
+FALSE = /usr/bin/false
+INSTALL_STRIP_PROGRAM = ${SHELL} $(install_sh) -c -s
+LIBDLMALLOC = 
+LIBREGEX = 
+LIB_MALLOC = 
+LN = /usr/bin/ln
+LN_S = ln -s
+MAINT = #
+MKDIR = /usr/bin/mkdir
+MV = /usr/bin/mv
+NTLM_AUTH_HELPERS = NTLMSSP
+OBJEXT = o
+OPT_DISKD_EXE = 
+OPT_DNSSERVER_EXE = 
+OPT_PINGER = 
+OPT_PINGER_EXE = 
+PACKAGE = squid
+PERL = /usr/bin/perl
+PTHREADLIB = -lpthread
+RANLIB = ranlib
+REGEXLIB = -lregex
+REPL_LIBS = liblru.a
+REPL_OBJS = repl/liblru.a
+REPL_POLICIES = lru
+RM = /usr/bin/rm
+SH = /usr/bin/sh
+SNMPLIB = -L../snmplib -lsnmp
+SQUID_AIO_LIB = 
+SQUID_PTHREAD_LIB = 
+SSLLIB = 
+STORE_LIBS = libufs.a
+STORE_MODULES = ufs
+STORE_MODULE_SUBDIRS = 
+STORE_OBJS = fs/libufs.a
+TRUE = /usr/bin/true
+VERSION = 2.6-DEVEL
+XTRA_LIBS = -lm
+XTRA_OBJS = 
+am__include = include
+am__quote = 
+install_sh = /cygdrive/c/work/cygwin-svc/cfgaux/install-sh
+makesnmplib = snmplib
+subdir = src/fs/awin32
+mkinstalldirs = $(SHELL) $(top_srcdir)/cfgaux/mkinstalldirs
+CONFIG_HEADER = $(top_builddir)/include/autoconf.h
+CONFIG_CLEAN_FILES =
+DIST_SOURCES =
+DIST_COMMON = Makefile.am Makefile.in
+all: all-am
+
+.SUFFIXES:
+$(srcdir)/Makefile.in: # Makefile.am  $(top_srcdir)/configure.in $(ACLOCAL_M4)
+	cd $(top_srcdir) && \
+	  $(AUTOMAKE) --foreign  src/fs/awin32/Makefile
+Makefile: # $(srcdir)/Makefile.in  $(top_builddir)/config.status
+	cd $(top_builddir) && \
+	  CONFIG_HEADERS= CONFIG_LINKS= \
+	  CONFIG_FILES=$(subdir)/$@ $(SHELL) ./config.status
+uninstall-info-am:
+tags: TAGS
+TAGS:
+
+
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+
+top_distdir = ../../..
+distdir = $(top_distdir)/$(PACKAGE)-$(VERSION)
+
+distdir: $(DISTFILES)
+	@for file in $(DISTFILES); do \
+	  if test -f $$file; then d=.; else d=$(srcdir); fi; \
+	  dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
+	  if test "$$dir" != "$$file" && test "$$dir" != "."; then \
+	    $(mkinstalldirs) "$(distdir)/$$dir"; \
+	  fi; \
+	  if test -d $$d/$$file; then \
+	    cp -pR $$d/$$file $(distdir) \
+	    || exit 1; \
+	  else \
+	    test -f $(distdir)/$$file \
+	    || cp -p $$d/$$file $(distdir)/$$file \
+	    || exit 1; \
+	  fi; \
+	done
+check-am: all-am
+check: check-am
+all-am: Makefile
+
+installdirs:
+
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+	$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	  `test -z '$(STRIP)' || \
+	    echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+	-rm -f Makefile $(CONFIG_CLEAN_FILES) stamp-h stamp-h[0-9]*
+
+maintainer-clean-generic:
+	@echo "This command is intended for maintainers to use"
+	@echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic mostlyclean-am
+
+distclean: distclean-am
+
+distclean-am: clean-am distclean-generic
+
+dvi: dvi-am
+
+dvi-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-exec-am:
+
+install-info: install-info-am
+
+install-man:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-generic
+
+uninstall-am: uninstall-info-am
+
+.PHONY: all all-am check check-am clean clean-generic distclean \
+	distclean-generic distdir dvi dvi-am info info-am install \
+	install-am install-data install-data-am install-exec \
+	install-exec-am install-info install-info-am install-man \
+	install-strip installcheck installcheck-am installdirs \
+	maintainer-clean maintainer-clean-generic mostlyclean \
+	mostlyclean-generic uninstall uninstall-am uninstall-info-am
+
+
+all clean:
+	@cd .. && $(MAKE) $(MFLAGS) awin32/$@
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
--- /dev/null	Wed Feb 14 00:55:47 2007
+++ squid/src/fs/awin32/Makefile.am	Wed Feb 14 00:57:26 2007
@@ -0,0 +1,2 @@
+all clean:
+	@cd .. && $(MAKE) $(MFLAGS) awin32/$@
--- /dev/null	Wed Feb 14 00:55:47 2007
+++ squid/src/fs/awin32/Makefile.in	Wed Feb 14 00:57:26 2007
@@ -0,0 +1,241 @@
+# Makefile.in generated automatically by automake 1.5 from Makefile.am.
+
+# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
+# Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+SHELL = @SHELL@
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+
+bindir = @bindir@
+sbindir = @sbindir@
+libexecdir = @libexecdir@
+datadir = @datadir@
+sysconfdir = @sysconfdir@
+sharedstatedir = @sharedstatedir@
+localstatedir = @localstatedir@
+libdir = @libdir@
+infodir = @infodir@
+mandir = @mandir@
+includedir = @includedir@
+oldincludedir = /usr/include
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+top_builddir = ../../..
+
+ACLOCAL = @ACLOCAL@
+AUTOCONF = @AUTOCONF@
+AUTOMAKE = @AUTOMAKE@
+AUTOHEADER = @AUTOHEADER@
+
+INSTALL = @INSTALL@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = @program_transform_name@
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+host_alias = @host_alias@
+host_triplet = @host@
+AIOLIB = @AIOLIB@
+AMTAR = @AMTAR@
+AR = @AR@
+AR_R = @AR_R@
+AUTH_LIBS = @AUTH_LIBS@
+AUTH_MODULES = @AUTH_MODULES@
+AUTH_OBJS = @AUTH_OBJS@
+AWK = @AWK@
+BASIC_AUTH_HELPERS = @BASIC_AUTH_HELPERS@
+CC = @CC@
+CPP = @CPP@
+CRYPTLIB = @CRYPTLIB@
+CXX = @CXX@
+DEPDIR = @DEPDIR@
+DIGEST_AUTH_HELPERS = @DIGEST_AUTH_HELPERS@
+DLLIB = @DLLIB@
+ERR_DEFAULT_LANGUAGE = @ERR_DEFAULT_LANGUAGE@
+ERR_LANGUAGES = @ERR_LANGUAGES@
+EXEEXT = @EXEEXT@
+FALSE = @FALSE@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LIBDLMALLOC = @LIBDLMALLOC@
+LIBREGEX = @LIBREGEX@
+LIB_MALLOC = @LIB_MALLOC@
+LN = @LN@
+LN_S = @LN_S@
+MAINT = @MAINT@
+MKDIR = @MKDIR@
+MV = @MV@
+NTLM_AUTH_HELPERS = @NTLM_AUTH_HELPERS@
+OBJEXT = @OBJEXT@
+OPT_DISKD_EXE = @OPT_DISKD_EXE@
+OPT_DNSSERVER_EXE = @OPT_DNSSERVER_EXE@
+OPT_PINGER = @OPT_PINGER@
+OPT_PINGER_EXE = @OPT_PINGER_EXE@
+PACKAGE = @PACKAGE@
+PERL = @PERL@
+PTHREADLIB = @PTHREADLIB@
+RANLIB = @RANLIB@
+REGEXLIB = @REGEXLIB@
+REPL_LIBS = @REPL_LIBS@
+REPL_OBJS = @REPL_OBJS@
+REPL_POLICIES = @REPL_POLICIES@
+RM = @RM@
+SH = @SH@
+SNMPLIB = @SNMPLIB@
+SQUID_AIO_LIB = @SQUID_AIO_LIB@
+SQUID_PTHREAD_LIB = @SQUID_PTHREAD_LIB@
+SSLLIB = @SSLLIB@
+STORE_LIBS = @STORE_LIBS@
+STORE_MODULES = @STORE_MODULES@
+STORE_MODULE_SUBDIRS = @STORE_MODULE_SUBDIRS@
+STORE_OBJS = @STORE_OBJS@
+TRUE = @TRUE@
+VERSION = @VERSION@
+XTRA_LIBS = @XTRA_LIBS@
+XTRA_OBJS = @XTRA_OBJS@
+am__include = @am__include@
+am__quote = @am__quote@
+install_sh = @install_sh@
+makesnmplib = @makesnmplib@
+subdir = src/fs/awin32
+mkinstalldirs = $(SHELL) $(top_srcdir)/cfgaux/mkinstalldirs
+CONFIG_HEADER = $(top_builddir)/include/autoconf.h
+CONFIG_CLEAN_FILES =
+DIST_SOURCES =
+DIST_COMMON = Makefile.am Makefile.in
+all: all-am
+
+.SUFFIXES:
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ Makefile.am  $(top_srcdir)/configure.in $(ACLOCAL_M4)
+	cd $(top_srcdir) && \
+	  $(AUTOMAKE) --foreign  src/fs/awin32/Makefile
+Makefile: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.in  $(top_builddir)/config.status
+	cd $(top_builddir) && \
+	  CONFIG_HEADERS= CONFIG_LINKS= \
+	  CONFIG_FILES=$(subdir)/$@ $(SHELL) ./config.status
+uninstall-info-am:
+tags: TAGS
+TAGS:
+
+
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+
+top_distdir = ../../..
+distdir = $(top_distdir)/$(PACKAGE)-$(VERSION)
+
+distdir: $(DISTFILES)
+	@for file in $(DISTFILES); do \
+	  if test -f $$file; then d=.; else d=$(srcdir); fi; \
+	  dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
+	  if test "$$dir" != "$$file" && test "$$dir" != "."; then \
+	    $(mkinstalldirs) "$(distdir)/$$dir"; \
+	  fi; \
+	  if test -d $$d/$$file; then \
+	    cp -pR $$d/$$file $(distdir) \
+	    || exit 1; \
+	  else \
+	    test -f $(distdir)/$$file \
+	    || cp -p $$d/$$file $(distdir)/$$file \
+	    || exit 1; \
+	  fi; \
+	done
+check-am: all-am
+check: check-am
+all-am: Makefile
+
+installdirs:
+
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+	$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	  `test -z '$(STRIP)' || \
+	    echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+	-rm -f Makefile $(CONFIG_CLEAN_FILES) stamp-h stamp-h[0-9]*
+
+maintainer-clean-generic:
+	@echo "This command is intended for maintainers to use"
+	@echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic mostlyclean-am
+
+distclean: distclean-am
+
+distclean-am: clean-am distclean-generic
+
+dvi: dvi-am
+
+dvi-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-exec-am:
+
+install-info: install-info-am
+
+install-man:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-generic
+
+uninstall-am: uninstall-info-am
+
+.PHONY: all all-am check check-am clean clean-generic distclean \
+	distclean-generic distdir dvi dvi-am info info-am install \
+	install-am install-data install-data-am install-exec \
+	install-exec-am install-info install-info-am install-man \
+	install-strip installcheck installcheck-am installdirs \
+	maintainer-clean maintainer-clean-generic mostlyclean \
+	mostlyclean-generic uninstall uninstall-am uninstall-info-am
+
+
+all clean:
+	@cd .. && $(MAKE) $(MFLAGS) awin32/$@
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
--- /dev/null	Wed Feb 14 00:55:47 2007
+++ squid/src/fs/awin32/aiops.c	Wed Feb 14 00:57:26 2007
@@ -0,0 +1,1021 @@
+/*
+ * $Id: aiops.c,v 1.1.62.1 2002/03/14 20:23:47 serassio Exp $
+ *
+ * DEBUG: section 43    AIOPS
+ * AUTHOR: Stewart Forster <slf@connect.com.au>
+ *
+ * SQUID Web Proxy Cache          http://www.squid-cache.org/
+ * ----------------------------------------------------------
+ *
+ *  Squid is the result of efforts by numerous individuals from
+ *  the Internet community; see the CONTRIBUTORS file for full
+ *  details.   Many organizations have provided support for Squid's
+ *  development; see the SPONSORS file for full details.  Squid is
+ *  Copyrighted (C) 2001 by the Regents of the University of
+ *  California; see the COPYRIGHT file for full details.  Squid
+ *  incorporates software developed and/or copyrighted by other
+ *  sources; see the CREDITS file for full details.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *  
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *  
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
+ *
+ */
+
+#include "squid.h"
+#include <windows.h>
+#include "store_asyncufs.h"
+
+
+#include	<stdio.h>
+#include	<sys/types.h>
+#include	<sys/stat.h>
+#include	<fcntl.h>
+#include	<errno.h>
+#include	<dirent.h>
+#include	<signal.h>
+
+#define RIDICULOUS_LENGTH	4096
+
+enum _squidaio_thread_status {
+    _THREAD_STARTING = 0,
+    _THREAD_WAITING,
+    _THREAD_BUSY,
+    _THREAD_FAILED,
+    _THREAD_DONE
+};
+typedef enum _squidaio_thread_status squidaio_thread_status;
+
+enum _squidaio_request_type {
+    _AIO_OP_NONE = 0,
+    _AIO_OP_OPEN,
+    _AIO_OP_READ,
+    _AIO_OP_WRITE,
+    _AIO_OP_CLOSE,
+    _AIO_OP_UNLINK,
+    _AIO_OP_TRUNCATE,
+    _AIO_OP_OPENDIR,
+    _AIO_OP_STAT
+};
+typedef enum _squidaio_request_type squidaio_request_type;
+
+typedef struct squidaio_request_t {
+    struct squidaio_request_t *next;
+    squidaio_request_type request_type;
+    int cancelled;
+    char *path;
+    int oflag;
+    mode_t mode;
+    int fd;
+    char *bufferp;
+    char *tmpbufp;
+    int buflen;
+    off_t offset;
+    int whence;
+    int ret;
+    int err;
+    struct stat *tmpstatp;
+    struct stat *statp;
+    squidaio_result_t *resultp;
+} squidaio_request_t;
+
+typedef struct squidaio_request_queue_t {
+    HANDLE mutex; /* pthread_mutex_t mutex; */
+    HANDLE cond; /*pthread_cond_t cond; */ /* See Event objects */
+    squidaio_request_t *volatile head;
+    squidaio_request_t *volatile *volatile tailp;
+    unsigned long requests;
+    unsigned long blocked;	/* main failed to lock the queue */
+} squidaio_request_queue_t;
+
+typedef struct squidaio_thread_t squidaio_thread_t;
+struct squidaio_thread_t {
+    squidaio_thread_t *next;
+    HANDLE thread; /* pthread_t thread; */
+    DWORD dwThreadId; /* thread ID */
+    squidaio_thread_status status;
+    struct squidaio_request_t *current_req;
+    unsigned long requests;
+};
+
+int squidaio_cancel(squidaio_result_t *);
+int squidaio_open(const char *, int, mode_t, squidaio_result_t *);
+int squidaio_read(int, char *, int, off_t, int, squidaio_result_t *);
+int squidaio_write(int, char *, int, off_t, int, squidaio_result_t *);
+int squidaio_close(int, squidaio_result_t *);
+int squidaio_unlink(const char *, squidaio_result_t *);
+int squidaio_truncate(const char *, off_t length, squidaio_result_t *);
+int squidaio_opendir(const char *, squidaio_result_t *);
+squidaio_result_t *squidaio_poll_done();
+int squidaio_sync(void);
+
+static void squidaio_init(void);
+static void squidaio_queue_request(squidaio_request_t *);
+static void squidaio_cleanup_request(squidaio_request_t *);
+/* static void *squidaio_thread_loop(void *); */
+static DWORD WINAPI squidaio_thread_loop( LPVOID lpParam );
+static void squidaio_do_open(squidaio_request_t *);
+static void squidaio_do_read(squidaio_request_t *);
+static void squidaio_do_write(squidaio_request_t *);
+static void squidaio_do_close(squidaio_request_t *);
+static void squidaio_do_stat(squidaio_request_t *);
+static void squidaio_do_unlink(squidaio_request_t *);
+static void squidaio_do_truncate(squidaio_request_t *);
+#if AIO_OPENDIR
+static void *squidaio_do_opendir(squidaio_request_t *);
+#endif
+static void squidaio_debug(squidaio_request_t *);
+static void squidaio_poll_queues(void);
+
+static squidaio_thread_t *threads = NULL;
+static int squidaio_initialised = 0;
+
+
+#define AIO_LARGE_BUFS  16384
+#define AIO_MEDIUM_BUFS	AIO_LARGE_BUFS >> 1
+#define AIO_SMALL_BUFS	AIO_LARGE_BUFS >> 2
+#define AIO_TINY_BUFS	AIO_LARGE_BUFS >> 3
+#define AIO_MICRO_BUFS	128
+
+static MemPool *squidaio_large_bufs = NULL;	/* 16K */
+static MemPool *squidaio_medium_bufs = NULL;		/* 8K */
+static MemPool *squidaio_small_bufs = NULL;	/* 4K */
+static MemPool *squidaio_tiny_bufs = NULL;	/* 2K */
+static MemPool *squidaio_micro_bufs = NULL;	/* 128K */
+
+static int request_queue_len = 0;
+static MemPool *squidaio_request_pool = NULL;
+static MemPool *squidaio_thread_pool = NULL;
+static squidaio_request_queue_t request_queue;
+static struct {
+    squidaio_request_t *head, **tailp;
+} request_queue2 = {
+
+    NULL, &request_queue2.head
+};
+static squidaio_request_queue_t done_queue;
+static struct {
+    squidaio_request_t *head, **tailp;
+} done_requests = {
+
+    NULL, &done_requests.head
+};
+/* static pthread_attr_t globattr; The attributes of a created thread */
+/*static struct sched_param globsched; */
+/* static pthread_t main_thread; */
+static HANDLE main_thread;
+
+static MemPool *
+squidaio_get_pool(int size)
+{
+    MemPool *p;
+    if (size <= AIO_LARGE_BUFS) {
+	if (size <= AIO_MICRO_BUFS)
+	    p = squidaio_micro_bufs;
+	else if (size <= AIO_TINY_BUFS)
+	    p = squidaio_tiny_bufs;
+	else if (size <= AIO_SMALL_BUFS)
+	    p = squidaio_small_bufs;
+	else if (size <= AIO_MEDIUM_BUFS)
+	    p = squidaio_medium_bufs;
+	else
+	    p = squidaio_large_bufs;
+    } else
+	p = NULL;
+    return p;
+}
+
+static void *
+squidaio_xmalloc(int size)
+{
+    void *p;
+    MemPool *pool;
+
+    if ((pool = squidaio_get_pool(size)) != NULL) {
+	p = memPoolAlloc(pool);
+    } else
+	p = xmalloc(size);
+
+    return p;
+}
+
+static char *
+squidaio_xstrdup(const char *str)
+{
+    char *p;
+    int len = strlen(str) + 1;
+
+    p = squidaio_xmalloc(len);
+    strncpy(p, str, len);
+
+    return p;
+}
+
+static void
+squidaio_xfree(void *p, int size)
+{
+    MemPool *pool;
+
+    if ((pool = squidaio_get_pool(size)) != NULL) {
+	memPoolFree(pool, p);
+    } else
+	xfree(p);
+}
+
+static void
+squidaio_xstrfree(char *str)
+{
+    MemPool *pool;
+    int len = strlen(str) + 1;
+
+    if ((pool = squidaio_get_pool(len)) != NULL) {
+	memPoolFree(pool, str);
+    } else
+	xfree(str);
+}
+
+static void
+squidaio_init(void)
+{
+    int i;
+    squidaio_thread_t *threadp;
+
+    if (squidaio_initialised)
+	return;
+
+    if (!DuplicateHandle(GetCurrentProcess(), /* pseudo handle, don't close */
+	GetCurrentThread(), /* pseudo handle to copy */
+	GetCurrentProcess(), /* pseudo handle, don't close */
+	&main_thread,
+	0, /* required access */
+	FALSE, /* child process's don't inherit the handle */
+	DUPLICATE_SAME_ACCESS)) {
+	/* spit errors */
+	fatal("couldn't get current thread handle\n");
+    }
+	/* FIXME: where do we CloseHandle the main_thread ?*/
+#if 0
+/* FIXME set the current thread to priority 1 */
+#if HAVE_PTHREAD_SETSCHEDPARAM
+    pthread_setschedparam(main_thread, SCHED_OTHER, &globsched);
+#endif
+/* FIXME set new threads to priority 2 
+    globsched.sched_priority = 2;
+#if HAVE_PTHREAD_ATTR_SETSCHEDPARAM
+    pthread_attr_setschedparam(&globattr, &globsched);
+#endif
+*/
+#endif
+
+    /* Initialize request queue */
+    if ((request_queue.mutex=CreateMutex( NULL, /* no inheritance */
+	FALSE, /* start unowned (as per mutex_init) */
+	NULL /* no name */ )
+	)==NULL) { 
+	fatal("failed to create mutex\n");
+    }
+    if ((request_queue.cond=CreateEvent( NULL, /* no inheritance */
+	FALSE, /* auto signal reset - which I think is pthreads like ? */
+	FALSE, /* start non signaled */
+	NULL /* no name */ )
+	)==NULL) {
+        fatal("failed to create condition event variable.\n");
+    }
+    request_queue.head = NULL;
+    request_queue.tailp = &request_queue.head;
+    request_queue.requests = 0;
+    request_queue.blocked = 0;
+
+    /* Initialize done queue */
+    if ((done_queue.mutex=CreateMutex( NULL, /* no inheritance */
+        FALSE, /* start unowned (as per mutex_init) */
+        NULL /* no name */ )
+        )==NULL) {
+        fatal("failed to create mutex\n");
+    }
+    if ((done_queue.cond=CreateEvent( NULL, /* no inheritance */
+        TRUE, /* manually signaled - which I think is pthreads like ? */
+        FALSE, /* start non signaled */
+        NULL /* no name */ )
+        )==NULL) {
+        fatal("failed to create condition event variable.\n");
+    }
+    done_queue.head = NULL;
+    done_queue.tailp = &done_queue.head;
+    done_queue.requests = 0;
+    done_queue.blocked = 0;
+
+    /* Create threads and get them to sit in their wait loop */
+    squidaio_thread_pool = memPoolCreate("aio_thread", sizeof(squidaio_thread_t));
+    for (i = 0; i < NUMTHREADS; i++) {
+	threadp = memPoolAlloc(squidaio_thread_pool);
+	threadp->status = _THREAD_STARTING;
+	threadp->current_req = NULL;
+	threadp->requests = 0;
+	threadp->next = threads;
+	threads = threadp;
+	if ((threadp->thread = CreateThread(
+	    NULL,                        // no security attributes
+	    0,                           // use default stack size
+	    squidaio_thread_loop,                  // thread function
+	    threadp,                // argument to thread function
+	    0,                           // use default creation flags
+	    &(threadp->dwThreadId))                // returns the thread identifier
+	    )==NULL) {
+	    fprintf(stderr, "Thread creation failed\n");
+	    threadp->status = _THREAD_FAILED;
+	    continue;
+	}
+    }
+
+    /* Create request pool */
+    squidaio_request_pool = memPoolCreate("aio_request", sizeof(squidaio_request_t));
+    squidaio_large_bufs = memPoolCreate("squidaio_large_bufs", AIO_LARGE_BUFS);
+    squidaio_medium_bufs = memPoolCreate("squidaio_medium_bufs", AIO_MEDIUM_BUFS);
+    squidaio_small_bufs = memPoolCreate("squidaio_small_bufs", AIO_SMALL_BUFS);
+    squidaio_tiny_bufs = memPoolCreate("squidaio_tiny_bufs", AIO_TINY_BUFS);
+    squidaio_micro_bufs = memPoolCreate("squidaio_micro_bufs", AIO_MICRO_BUFS);
+
+    squidaio_initialised = 1;
+}
+
+
+static DWORD WINAPI 
+squidaio_thread_loop( LPVOID lpParam )
+{
+    squidaio_thread_t *threadp = lpParam;
+    squidaio_request_t *request;
+    HANDLE cond; /* local copy of the event queue because win32 event handles
+		  * don't atomically release the mutex as cond variables do. */
+
+#if 0
+/*    sigset_t new; */
+    /*
+     * Does WIN32 have this problem? 
+     */
+
+    /*
+     * Make sure to ignore signals which may possibly get sent to
+     * the parent squid thread.  Causes havoc with mutex's and
+     * condition waits otherwise
+     */
+
+    sigemptyset(&new);
+    sigaddset(&new, SIGPIPE);
+    sigaddset(&new, SIGCHLD);
+#ifdef _SQUID_LINUX_THREADS_
+    sigaddset(&new, SIGQUIT);
+    sigaddset(&new, SIGTRAP);
+#else
+    sigaddset(&new, SIGUSR1);
+    sigaddset(&new, SIGUSR2);
+#endif
+    sigaddset(&new, SIGHUP);
+    sigaddset(&new, SIGTERM);
+    sigaddset(&new, SIGINT);
+    sigaddset(&new, SIGALRM);
+    pthread_sigmask(SIG_BLOCK, &new, NULL);
+#endif /* 0 */
+
+    /* lock the thread info */
+    if (WAIT_FAILED == WaitForSingleObject(
+	  request_queue.mutex,        // handle to object
+	  INFINITE   // time-out interval
+	)) {
+	    /* wait failed.... what to do ? */
+    }
+    /* duplicate the handle */
+    if (!DuplicateHandle(GetCurrentProcess(), /* pseudo handle, don't close */
+        request_queue.cond, /* handle to copy */
+        GetCurrentProcess(), /* pseudo handle, don't close */
+        &cond,
+        0, /* required access */
+        FALSE, /* child process's don't inherit the handle */
+        DUPLICATE_SAME_ACCESS)) 
+        exit(1);
+   if (!ReleaseMutex(request_queue.mutex)) {
+	CloseHandle(cond);
+	exit(1);
+    }
+
+
+    while (1) {
+	DWORD rv;
+	threadp->current_req = request = NULL;
+	request = NULL;
+	/* Get a request to process */
+	threadp->status = _THREAD_WAITING;
+	rv = WaitForSingleObject(
+	  request_queue.mutex,        // handle to object
+	  INFINITE   // time-out interval
+	);
+	if (rv == WAIT_FAILED) {
+	    CloseHandle(cond);
+	    return 1;
+	}
+
+
+	while (!request_queue.head) {
+	if (!ReleaseMutex(request_queue.mutex)) {
+		CloseHandle(cond);
+		threadp->status = _THREAD_FAILED;
+		return 1;
+	    }
+	    rv = WaitForSingleObject(
+		cond,        // handle to object
+		INFINITE   // time-out interval
+		);
+	    if (rv == WAIT_FAILED) {
+		CloseHandle(cond);
+		return 1;
+	    }
+	    rv = WaitForSingleObject(
+		request_queue.mutex,        // handle to object
+		INFINITE   // time-out interval
+		);
+	    if (rv == WAIT_FAILED) {
+		CloseHandle(cond);
+		return 1;
+	    }
+	}
+	request = request_queue.head;
+	if (request)
+	    request_queue.head = request->next;
+	if (!request_queue.head)
+	    request_queue.tailp = &request_queue.head;
+	if (!ReleaseMutex(request_queue.mutex)) {
+	    CloseHandle(cond);
+	    return 1;
+	}
+	/* process the request */
+	threadp->status = _THREAD_BUSY;
+	request->next = NULL;
+	threadp->current_req = request;
+	errno = 0;
+	if (!request->cancelled) {
+	    switch (request->request_type) {
+	    case _AIO_OP_OPEN:
+		squidaio_do_open(request);
+		break;
+	    case _AIO_OP_READ:
+		squidaio_do_read(request);
+		break;
+	    case _AIO_OP_WRITE:
+		squidaio_do_write(request);
+		break;
+	    case _AIO_OP_CLOSE:
+		squidaio_do_close(request);
+		break;
+	    case _AIO_OP_UNLINK:
+		squidaio_do_unlink(request);
+		break;
+	    case _AIO_OP_TRUNCATE:
+		squidaio_do_truncate(request);
+		break;
+#if AIO_OPENDIR			/* Opendir not implemented yet */
+	    case _AIO_OP_OPENDIR:
+		squidaio_do_opendir(request);
+		break;
+#endif
+	    case _AIO_OP_STAT:
+		squidaio_do_stat(request);
+		break;
+	    default:
+		request->ret = -1;
+		request->err = EINVAL;
+		break;
+	    }
+	} else {		/* cancelled */
+	    request->ret = -1;
+	    request->err = EINTR;
+	}
+	threadp->status = _THREAD_DONE;
+	/* put the request in the done queue */
+	rv = WaitForSingleObject(
+	  done_queue.mutex,        // handle to object
+	  INFINITE   // time-out interval
+	);
+	if (rv == WAIT_FAILED) {
+	    CloseHandle(cond);
+	    return 1;
+	}
+	*done_queue.tailp = request;
+	done_queue.tailp = &request->next;
+	if (!ReleaseMutex(done_queue.mutex)) {
+	    CloseHandle(cond);
+	    return 1;
+	}
+	threadp->requests++;
+    }				/* while forever */
+    CloseHandle(cond);
+    return 0;
+}				/* squidaio_thread_loop */
+
+static void
+squidaio_queue_request(squidaio_request_t * request)
+{
+    static int high_start = 0;
+    debug(41, 9) ("squidaio_queue_request: %p type=%d result=%p\n",
+	request, request->request_type, request->resultp);
+    /* Mark it as not executed (failing result, no error) */
+    request->ret = -1;
+    request->err = 0;
+    /* Internal housekeeping */
+    request_queue_len += 1;
+    request->resultp->_data = request;
+    /* Play some tricks with the request_queue2 queue */
+    request->next = NULL;
+    if (!request_queue2.head) {
+	/* If queue2 is empty, insert into queue 1 and try to "push" the queue
+	   If the "push" fails, queue it via queue2 */
+	if (WaitForSingleObject(request_queue.mutex, 0)==WAIT_OBJECT_0) {
+	    /* Normal path */
+	    *request_queue.tailp = request;
+	    request_queue.tailp = &request->next;
+	   if (!SetEvent(request_queue.cond))
+		fatal("couldn't push queue\n");
+	    if (!ReleaseMutex(request_queue.mutex)) {
+		/* unexpected error */
+		fatal("couldn't push queue\n");
+	    }
+	} else {
+	    /* Oops, the request queue is blocked, use request_queue2 */
+	    *request_queue2.tailp = request;
+	    request_queue2.tailp = &request->next;
+	}
+    } else {
+	/* Secondary path. We have blocked requests to deal with */
+	/* add the request to the chain */
+	*request_queue2.tailp = request;
+	if (WaitForSingleObject(request_queue.mutex, 0)==WAIT_OBJECT_0) {
+	    /* Ok, the queue is no longer blocked */
+	    *request_queue.tailp = request_queue2.head;
+	    request_queue.tailp = &request->next;
+	    if (!SetEvent(request_queue.cond))
+		fatal("couldn't push queue\n");
+	    if (!ReleaseMutex(request_queue.mutex)) {
+		/* unexpected error */
+		fatal("couldn't push queue\n");
+	    }
+	    request_queue2.head = NULL;
+	    request_queue2.tailp = &request_queue2.head;
+	} else {
+	    /* still blocked, bump the blocked request chain */
+	    request_queue2.tailp = &request->next;
+	}
+    }
+    if (request_queue2.head) {
+	static int filter = 0;
+	static int filter_limit = 8;
+	if (++filter >= filter_limit) {
+	    filter_limit += filter;
+	    filter = 0;
+	    debug(43, 1) ("squidaio_queue_request: WARNING - Queue congestion\n");
+	}
+    }
+    /* Warn if out of threads */
+    if (request_queue_len > MAGIC1) {
+	static int last_warn = 0;
+	static int queue_high, queue_low;
+	if (high_start == 0) {
+	    high_start = squid_curtime;
+	    queue_high = request_queue_len;
+	    queue_low = request_queue_len;
+	}
+	if (request_queue_len > queue_high)
+	    queue_high = request_queue_len;
+	if (request_queue_len < queue_low)
+	    queue_low = request_queue_len;
+	if (squid_curtime >= (last_warn + 15) &&
+	    squid_curtime >= (high_start + 5)) {
+	    debug(43, 1) ("squidaio_queue_request: WARNING - Disk I/O overloading\n");
+	    if (squid_curtime >= (high_start + 15))
+		debug(43, 1) ("squidaio_queue_request: Queue Length: current=%d, high=%d, low=%d, duration=%ld\n",
+		    request_queue_len, queue_high, queue_low, (long int) (squid_curtime - high_start));
+	    last_warn = squid_curtime;
+	}
+    } else {
+	high_start = 0;
+    }
+    /* Warn if seriously overloaded */
+    if (request_queue_len > RIDICULOUS_LENGTH) {
+	debug(43, 0) ("squidaio_queue_request: Async request queue growing uncontrollably!\n");
+	debug(43, 0) ("squidaio_queue_request: Syncing pending I/O operations.. (blocking)\n");
+	squidaio_sync();
+	debug(43, 0) ("squidaio_queue_request: Synced\n");
+    }
+}				/* squidaio_queue_request */
+
+static void
+squidaio_cleanup_request(squidaio_request_t * requestp)
+{
+    squidaio_result_t *resultp = requestp->resultp;
+    int cancelled = requestp->cancelled;
+
+    /* Free allocated structures and copy data back to user space if the */
+    /* request hasn't been cancelled */
+    switch (requestp->request_type) {
+    case _AIO_OP_STAT:
+	if (!cancelled && requestp->ret == 0)
+	    xmemcpy(requestp->statp, requestp->tmpstatp, sizeof(struct stat));
+	squidaio_xfree(requestp->tmpstatp, sizeof(struct stat));
+	squidaio_xstrfree(requestp->path);
+	break;
+    case _AIO_OP_OPEN:
+	if (cancelled && requestp->ret >= 0)
+	    /* The open() was cancelled but completed */
+	    close(requestp->ret);
+	squidaio_xstrfree(requestp->path);
+	break;
+    case _AIO_OP_CLOSE:
+	if (cancelled && requestp->ret < 0)
+	    /* The close() was cancelled and never got executed */
+	    close(requestp->fd);
+	break;
+    case _AIO_OP_UNLINK:
+    case _AIO_OP_TRUNCATE:
+    case _AIO_OP_OPENDIR:
+	squidaio_xstrfree(requestp->path);
+	break;
+    case _AIO_OP_READ:
+	if (!cancelled && requestp->ret > 0)
+	    xmemcpy(requestp->bufferp, requestp->tmpbufp, requestp->ret);
+	squidaio_xfree(requestp->tmpbufp, requestp->buflen);
+	break;
+    case _AIO_OP_WRITE:
+	squidaio_xfree(requestp->tmpbufp, requestp->buflen);
+	break;
+    default:
+	break;
+    }
+    if (resultp != NULL && !cancelled) {
+	resultp->aio_return = requestp->ret;
+	resultp->aio_errno = requestp->err;
+    }
+    memPoolFree(squidaio_request_pool, requestp);
+}				/* squidaio_cleanup_request */
+
+
+int
+squidaio_cancel(squidaio_result_t * resultp)
+{
+    squidaio_request_t *request = resultp->_data;
+
+    if (request && request->resultp == resultp) {
+	debug(41, 9) ("squidaio_cancel: %p type=%d result=%p\n",
+	    request, request->request_type, request->resultp);
+	request->cancelled = 1;
+	request->resultp = NULL;
+	resultp->_data = NULL;
+	return 0;
+    }
+    return 1;
+}				/* squidaio_cancel */
+
+
+int
+squidaio_open(const char *path, int oflag, mode_t mode, squidaio_result_t * resultp)
+{
+    squidaio_request_t *requestp;
+
+    if (!squidaio_initialised)
+	squidaio_init();
+    requestp = memPoolAlloc(squidaio_request_pool);
+    requestp->path = (char *) squidaio_xstrdup(path);
+    requestp->oflag = oflag;
+    requestp->mode = mode;
+    requestp->resultp = resultp;
+    requestp->request_type = _AIO_OP_OPEN;
+    requestp->cancelled = 0;
+
+    squidaio_queue_request(requestp);
+    return 0;
+}
+
+
+static void
+squidaio_do_open(squidaio_request_t * requestp)
+{
+    requestp->ret = open(requestp->path, requestp->oflag, requestp->mode);
+    requestp->err = errno;
+}
+
+
+int
+squidaio_read(int fd, char *bufp, int bufs, off_t offset, int whence, squidaio_result_t * resultp)
+{
+    squidaio_request_t *requestp;
+
+    if (!squidaio_initialised)
+	squidaio_init();
+    requestp = memPoolAlloc(squidaio_request_pool);
+    requestp->fd = fd;
+    requestp->bufferp = bufp;
+    requestp->tmpbufp = (char *) squidaio_xmalloc(bufs);
+    requestp->buflen = bufs;
+    requestp->offset = offset;
+    requestp->whence = whence;
+    requestp->resultp = resultp;
+    requestp->request_type = _AIO_OP_READ;
+    requestp->cancelled = 0;
+
+    squidaio_queue_request(requestp);
+    return 0;
+}
+
+
+static void
+squidaio_do_read(squidaio_request_t * requestp)
+{
+    lseek(requestp->fd, requestp->offset, requestp->whence);
+    requestp->ret = read(requestp->fd, requestp->tmpbufp, requestp->buflen);
+    requestp->err = errno;
+}
+
+
+int
+squidaio_write(int fd, char *bufp, int bufs, off_t offset, int whence, squidaio_result_t * resultp)
+{
+    squidaio_request_t *requestp;
+
+    if (!squidaio_initialised)
+	squidaio_init();
+    requestp = memPoolAlloc(squidaio_request_pool);
+    requestp->fd = fd;
+    requestp->tmpbufp = (char *) squidaio_xmalloc(bufs);
+    xmemcpy(requestp->tmpbufp, bufp, bufs);
+    requestp->buflen = bufs;
+    requestp->offset = offset;
+    requestp->whence = whence;
+    requestp->resultp = resultp;
+    requestp->request_type = _AIO_OP_WRITE;
+    requestp->cancelled = 0;
+
+    squidaio_queue_request(requestp);
+    return 0;
+}
+
+
+static void
+squidaio_do_write(squidaio_request_t * requestp)
+{
+    requestp->ret = write(requestp->fd, requestp->tmpbufp, requestp->buflen);
+    requestp->err = errno;
+}
+
+
+int
+squidaio_close(int fd, squidaio_result_t * resultp)
+{
+    squidaio_request_t *requestp;
+
+    if (!squidaio_initialised)
+	squidaio_init();
+    requestp = memPoolAlloc(squidaio_request_pool);
+    requestp->fd = fd;
+    requestp->resultp = resultp;
+    requestp->request_type = _AIO_OP_CLOSE;
+    requestp->cancelled = 0;
+
+    squidaio_queue_request(requestp);
+    return 0;
+}
+
+
+static void
+squidaio_do_close(squidaio_request_t * requestp)
+{
+    requestp->ret = close(requestp->fd);
+    requestp->err = errno;
+}
+
+
+int
+squidaio_stat(const char *path, struct stat *sb, squidaio_result_t * resultp)
+{
+    squidaio_request_t *requestp;
+
+    if (!squidaio_initialised)
+	squidaio_init();
+    requestp = memPoolAlloc(squidaio_request_pool);
+    requestp->path = (char *) squidaio_xstrdup(path);
+    requestp->statp = sb;
+    requestp->tmpstatp = (struct stat *) squidaio_xmalloc(sizeof(struct stat));
+    requestp->resultp = resultp;
+    requestp->request_type = _AIO_OP_STAT;
+    requestp->cancelled = 0;
+
+    squidaio_queue_request(requestp);
+    return 0;
+}
+
+
+static void
+squidaio_do_stat(squidaio_request_t * requestp)
+{
+    requestp->ret = stat(requestp->path, requestp->tmpstatp);
+    requestp->err = errno;
+}
+
+
+int
+squidaio_unlink(const char *path, squidaio_result_t * resultp)
+{
+    squidaio_request_t *requestp;
+
+    if (!squidaio_initialised)
+	squidaio_init();
+    requestp = memPoolAlloc(squidaio_request_pool);
+    requestp->path = squidaio_xstrdup(path);
+    requestp->resultp = resultp;
+    requestp->request_type = _AIO_OP_UNLINK;
+    requestp->cancelled = 0;
+
+    squidaio_queue_request(requestp);
+    return 0;
+}
+
+
+static void
+squidaio_do_unlink(squidaio_request_t * requestp)
+{
+    requestp->ret = unlink(requestp->path);
+    requestp->err = errno;
+}
+
+int
+squidaio_truncate(const char *path, off_t length, squidaio_result_t * resultp)
+{
+    squidaio_request_t *requestp;
+
+    if (!squidaio_initialised)
+	squidaio_init();
+    requestp = memPoolAlloc(squidaio_request_pool);
+    requestp->path = (char *) squidaio_xstrdup(path);
+    requestp->offset = length;
+    requestp->resultp = resultp;
+    requestp->request_type = _AIO_OP_TRUNCATE;
+    requestp->cancelled = 0;
+
+    squidaio_queue_request(requestp);
+    return 0;
+}
+
+
+static void
+squidaio_do_truncate(squidaio_request_t * requestp)
+{
+    requestp->ret = truncate(requestp->path, requestp->offset);
+    requestp->err = errno;
+}
+
+
+#if AIO_OPENDIR
+/* XXX squidaio_opendir NOT implemented yet.. */
+
+int
+squidaio_opendir(const char *path, squidaio_result_t * resultp)
+{
+    squidaio_request_t *requestp;
+    int len;
+
+    if (!squidaio_initialised)
+	squidaio_init();
+    requestp = memPoolAlloc(squidaio_request_pool);
+    return -1;
+}
+
+static void
+squidaio_do_opendir(squidaio_request_t * requestp)
+{
+    /* NOT IMPLEMENTED */
+}
+
+#endif
+
+static void
+squidaio_poll_queues(void)
+{
+    /* kick "overflow" request queue */
+    if (request_queue2.head &&
+	(WaitForSingleObject(request_queue.mutex, 0)==WAIT_OBJECT_0)) {
+	*request_queue.tailp = request_queue2.head;
+	request_queue.tailp = request_queue2.tailp;
+	if (!SetEvent(request_queue.cond))
+		fatal("couldn't push queue\n");
+	if (!ReleaseMutex(request_queue.mutex)) {
+	    /* unexpected error */
+	}
+	request_queue2.head = NULL;
+	request_queue2.tailp = &request_queue2.head;
+    }
+    /* poll done queue */
+    if (done_queue.head && 
+	(WaitForSingleObject(done_queue.mutex, 0)==WAIT_OBJECT_0)) {
+	struct squidaio_request_t *requests = done_queue.head;
+	done_queue.head = NULL;
+	done_queue.tailp = &done_queue.head;
+	if (!ReleaseMutex(done_queue.mutex)) {
+	    /* unexpected error */
+	}
+	*done_requests.tailp = requests;
+	request_queue_len -= 1;
+	while (requests->next) {
+	    requests = requests->next;
+	    request_queue_len -= 1;
+	}
+	done_requests.tailp = &requests->next;
+    }
+}
+
+squidaio_result_t *
+squidaio_poll_done(void)
+{
+    squidaio_request_t *request;
+    squidaio_result_t *resultp;
+    int cancelled;
+    int polled = 0;
+
+  AIO_REPOLL:
+    request = done_requests.head;
+    if (request == NULL && !polled) {
+	squidaio_poll_queues();
+	polled = 1;
+	request = done_requests.head;
+    }
+    if (!request) {
+	return NULL;
+    }
+    debug(41, 9) ("squidaio_poll_done: %p type=%d result=%p\n",
+	request, request->request_type, request->resultp);
+    done_requests.head = request->next;
+    if (!done_requests.head)
+	done_requests.tailp = &done_requests.head;
+    resultp = request->resultp;
+    cancelled = request->cancelled;
+    squidaio_debug(request);
+    debug(43, 5) ("DONE: %d -> %d\n", request->ret, request->err);
+    squidaio_cleanup_request(request);
+    if (cancelled)
+	goto AIO_REPOLL;
+    return resultp;
+}				/* squidaio_poll_done */
+
+int
+squidaio_operations_pending(void)
+{
+    return request_queue_len + (done_requests.head ? 1 : 0);
+}
+
+int
+squidaio_sync(void)
+{
+    /* XXX This might take a while if the queue is large.. */
+    do {
+	squidaio_poll_queues();
+    } while (request_queue_len > 0);
+    return squidaio_operations_pending();
+}
+
+int
+squidaio_get_queue_len(void)
+{
+    return request_queue_len;
+}
+
+static void
+squidaio_debug(squidaio_request_t * request)
+{
+    switch (request->request_type) {
+    case _AIO_OP_OPEN:
+	debug(43, 5) ("OPEN of %s to FD %d\n", request->path, request->ret);
+	break;
+    case _AIO_OP_READ:
+	debug(43, 5) ("READ on fd: %d\n", request->fd);
+	break;
+    case _AIO_OP_WRITE:
+	debug(43, 5) ("WRITE on fd: %d\n", request->fd);
+	break;
+    case _AIO_OP_CLOSE:
+	debug(43, 5) ("CLOSE of fd: %d\n", request->fd);
+	break;
+    case _AIO_OP_UNLINK:
+	debug(43, 5) ("UNLINK of %s\n", request->path);
+	break;
+    case _AIO_OP_TRUNCATE:
+	debug(43, 5) ("UNLINK of %s\n", request->path);
+	break;
+    default:
+	break;
+    }
+}
--- /dev/null	Wed Feb 14 00:55:47 2007
+++ squid/src/fs/awin32/async_io.c	Wed Feb 14 00:57:26 2007
@@ -0,0 +1,360 @@
+
+/*
+ * $Id: async_io.c,v 1.1.62.1 2002/03/14 20:23:47 serassio Exp $
+ *
+ * DEBUG: section 32    Asynchronous Disk I/O
+ * AUTHOR: Pete Bentley <pete@demon.net>
+ * AUTHOR: Stewart Forster <slf@connect.com.au>
+ *
+ * SQUID Web Proxy Cache          http://www.squid-cache.org/
+ * ----------------------------------------------------------
+ *
+ *  Squid is the result of efforts by numerous individuals from
+ *  the Internet community; see the CONTRIBUTORS file for full
+ *  details.   Many organizations have provided support for Squid's
+ *  development; see the SPONSORS file for full details.  Squid is
+ *  Copyrighted (C) 2001 by the Regents of the University of
+ *  California; see the COPYRIGHT file for full details.  Squid
+ *  incorporates software developed and/or copyrighted by other
+ *  sources; see the CREDITS file for full details.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *  
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *  
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
+ *
+ */
+
+#include "squid.h"
+#include "store_asyncufs.h"
+
+#define _AIO_OPEN	0
+#define _AIO_READ	1
+#define _AIO_WRITE	2
+#define _AIO_CLOSE	3
+#define _AIO_UNLINK	4
+#define _AIO_TRUNCATE	4
+#define _AIO_OPENDIR	5
+#define _AIO_STAT	6
+
+typedef struct squidaio_ctrl_t {
+    struct squidaio_ctrl_t *next;
+    int fd;
+    int operation;
+    AIOCB *done_handler;
+    void *done_handler_data;
+    squidaio_result_t result;
+    char *bufp;
+    FREE *free_func;
+    dlink_node node;
+} squidaio_ctrl_t;
+
+static struct {
+    int open;
+    int close;
+    int cancel;
+    int write;
+    int read;
+    int stat;
+    int unlink;
+    int check_callback;
+} squidaio_counts;
+
+typedef struct squidaio_unlinkq_t {
+    char *path;
+    struct squidaio_unlinkq_t *next;
+} squidaio_unlinkq_t;
+
+static dlink_list used_list;
+static int initialised = 0;
+static OBJH aioStats;
+static MemPool *squidaio_ctrl_pool;
+static void aioFDWasClosed(int fd);
+
+static void
+aioFDWasClosed(int fd)
+{
+    if (fd_table[fd].flags.closing)
+	fd_close(fd);
+}
+
+void
+aioInit(void)
+{
+    if (initialised)
+	return;
+    squidaio_ctrl_pool = memPoolCreate("aio_ctrl", sizeof(squidaio_ctrl_t));
+    cachemgrRegister("squidaio_counts", "Async IO Function Counters",
+	aioStats, 0, 1);
+    initialised = 1;
+    comm_quick_poll_required();
+}
+
+void
+aioDone(void)
+{
+    memPoolDestroy(squidaio_ctrl_pool);
+    initialised = 0;
+}
+
+void
+aioOpen(const char *path, int oflag, mode_t mode, AIOCB * callback, void *callback_data)
+{
+    squidaio_ctrl_t *ctrlp;
+
+    assert(initialised);
+    squidaio_counts.open++;
+    ctrlp = memPoolAlloc(squidaio_ctrl_pool);
+    ctrlp->fd = -2;
+    ctrlp->done_handler = callback;
+    ctrlp->done_handler_data = callback_data;
+    ctrlp->operation = _AIO_OPEN;
+    cbdataLock(callback_data);
+    ctrlp->result.data = ctrlp;
+    squidaio_open(path, oflag, mode, &ctrlp->result);
+    dlinkAdd(ctrlp, &ctrlp->node, &used_list);
+    return;
+}
+
+void
+aioClose(int fd)
+{
+    squidaio_ctrl_t *ctrlp;
+
+    assert(initialised);
+    squidaio_counts.close++;
+    aioCancel(fd);
+    ctrlp = memPoolAlloc(squidaio_ctrl_pool);
+    ctrlp->fd = fd;
+    ctrlp->done_handler = NULL;
+    ctrlp->done_handler_data = NULL;
+    ctrlp->operation = _AIO_CLOSE;
+    ctrlp->result.data = ctrlp;
+    squidaio_close(fd, &ctrlp->result);
+    dlinkAdd(ctrlp, &ctrlp->node, &used_list);
+    return;
+}
+
+void
+aioCancel(int fd)
+{
+    squidaio_ctrl_t *curr;
+    AIOCB *done_handler;
+    void *their_data;
+    dlink_node *m, *next;
+
+    assert(initialised);
+    squidaio_counts.cancel++;
+    for (m = used_list.head; m; m = next) {
+	next = m->next;
+	curr = m->data;
+	if (curr->fd != fd)
+	    continue;
+
+	squidaio_cancel(&curr->result);
+
+	if ((done_handler = curr->done_handler)) {
+	    their_data = curr->done_handler_data;
+	    curr->done_handler = NULL;
+	    curr->done_handler_data = NULL;
+	    debug(32, 2) ("this be aioCancel\n");
+	    if (cbdataValid(their_data))
+		done_handler(fd, their_data, -2, -2);
+	    cbdataUnlock(their_data);
+	}
+	dlinkDelete(m, &used_list);
+	memPoolFree(squidaio_ctrl_pool, curr);
+    }
+}
+
+
+void
+aioWrite(int fd, int offset, char *bufp, int len, AIOCB * callback, void *callback_data, FREE * free_func)
+{
+    squidaio_ctrl_t *ctrlp;
+    int seekmode;
+
+    assert(initialised);
+    squidaio_counts.write++;
+    ctrlp = memPoolAlloc(squidaio_ctrl_pool);
+    ctrlp->fd = fd;
+    ctrlp->done_handler = callback;
+    ctrlp->done_handler_data = callback_data;
+    ctrlp->operation = _AIO_WRITE;
+    ctrlp->bufp = bufp;
+    ctrlp->free_func = free_func;
+    if (offset >= 0)
+	seekmode = SEEK_SET;
+    else {
+	seekmode = SEEK_END;
+	offset = 0;
+    }
+    cbdataLock(callback_data);
+    ctrlp->result.data = ctrlp;
+    squidaio_write(fd, bufp, len, offset, seekmode, &ctrlp->result);
+    dlinkAdd(ctrlp, &ctrlp->node, &used_list);
+}				/* aioWrite */
+
+
+void
+aioRead(int fd, int offset, char *bufp, int len, AIOCB * callback, void *callback_data)
+{
+    squidaio_ctrl_t *ctrlp;
+    int seekmode;
+
+    assert(initialised);
+    squidaio_counts.read++;
+    ctrlp = memPoolAlloc(squidaio_ctrl_pool);
+    ctrlp->fd = fd;
+    ctrlp->done_handler = callback;
+    ctrlp->done_handler_data = callback_data;
+    ctrlp->operation = _AIO_READ;
+    if (offset >= 0)
+	seekmode = SEEK_SET;
+    else {
+	seekmode = SEEK_CUR;
+	offset = 0;
+    }
+    cbdataLock(callback_data);
+    ctrlp->result.data = ctrlp;
+    squidaio_read(fd, bufp, len, offset, seekmode, &ctrlp->result);
+    dlinkAdd(ctrlp, &ctrlp->node, &used_list);
+    return;
+}				/* aioRead */
+
+void
+aioStat(char *path, struct stat *sb, AIOCB * callback, void *callback_data)
+{
+    squidaio_ctrl_t *ctrlp;
+
+    assert(initialised);
+    squidaio_counts.stat++;
+    ctrlp = memPoolAlloc(squidaio_ctrl_pool);
+    ctrlp->fd = -2;
+    ctrlp->done_handler = callback;
+    ctrlp->done_handler_data = callback_data;
+    ctrlp->operation = _AIO_STAT;
+    cbdataLock(callback_data);
+    ctrlp->result.data = ctrlp;
+    squidaio_stat(path, sb, &ctrlp->result);
+    dlinkAdd(ctrlp, &ctrlp->node, &used_list);
+    return;
+}				/* aioStat */
+
+void
+aioUnlink(const char *path, AIOCB * callback, void *callback_data)
+{
+    squidaio_ctrl_t *ctrlp;
+    assert(initialised);
+    squidaio_counts.unlink++;
+    ctrlp = memPoolAlloc(squidaio_ctrl_pool);
+    ctrlp->fd = -2;
+    ctrlp->done_handler = callback;
+    ctrlp->done_handler_data = callback_data;
+    ctrlp->operation = _AIO_UNLINK;
+    cbdataLock(callback_data);
+    ctrlp->result.data = ctrlp;
+    squidaio_unlink(path, &ctrlp->result);
+    dlinkAdd(ctrlp, &ctrlp->node, &used_list);
+}				/* aioUnlink */
+
+void
+aioTruncate(const char *path, off_t length, AIOCB * callback, void *callback_data)
+{
+    squidaio_ctrl_t *ctrlp;
+    assert(initialised);
+    squidaio_counts.unlink++;
+    ctrlp = memPoolAlloc(squidaio_ctrl_pool);
+    ctrlp->fd = -2;
+    ctrlp->done_handler = callback;
+    ctrlp->done_handler_data = callback_data;
+    ctrlp->operation = _AIO_TRUNCATE;
+    cbdataLock(callback_data);
+    ctrlp->result.data = ctrlp;
+    squidaio_truncate(path, length, &ctrlp->result);
+    dlinkAdd(ctrlp, &ctrlp->node, &used_list);
+}				/* aioTruncate */
+
+
+int
+aioCheckCallbacks(SwapDir * SD)
+{
+    squidaio_result_t *resultp;
+    squidaio_ctrl_t *ctrlp;
+    AIOCB *done_handler;
+    void *their_data;
+    int retval = 0;
+
+    assert(initialised);
+    squidaio_counts.check_callback++;
+    for (;;) {
+	if ((resultp = squidaio_poll_done()) == NULL)
+	    break;
+	ctrlp = (squidaio_ctrl_t *) resultp->data;
+	if (ctrlp == NULL)
+	    continue;		/* XXX Should not happen */
+	dlinkDelete(&ctrlp->node, &used_list);
+	if ((done_handler = ctrlp->done_handler)) {
+	    their_data = ctrlp->done_handler_data;
+	    ctrlp->done_handler = NULL;
+	    ctrlp->done_handler_data = NULL;
+	    if (cbdataValid(their_data)) {
+		retval = 1;	/* Return that we've actually done some work */
+		done_handler(ctrlp->fd, their_data,
+		    ctrlp->result.aio_return, ctrlp->result.aio_errno);
+	    }
+	    cbdataUnlock(their_data);
+	}
+	/* free data if requested to aioWrite() */
+	if (ctrlp->free_func)
+	    ctrlp->free_func(ctrlp->bufp);
+	if (ctrlp->operation == _AIO_CLOSE)
+	    aioFDWasClosed(ctrlp->fd);
+	memPoolFree(squidaio_ctrl_pool, ctrlp);
+    }
+    return retval;
+}
+
+void
+aioStats(StoreEntry * sentry)
+{
+    storeAppendPrintf(sentry, "ASYNC IO Counters:\n");
+    storeAppendPrintf(sentry, "open\t%d\n", squidaio_counts.open);
+    storeAppendPrintf(sentry, "close\t%d\n", squidaio_counts.close);
+    storeAppendPrintf(sentry, "cancel\t%d\n", squidaio_counts.cancel);
+    storeAppendPrintf(sentry, "write\t%d\n", squidaio_counts.write);
+    storeAppendPrintf(sentry, "read\t%d\n", squidaio_counts.read);
+    storeAppendPrintf(sentry, "stat\t%d\n", squidaio_counts.stat);
+    storeAppendPrintf(sentry, "unlink\t%d\n", squidaio_counts.unlink);
+    storeAppendPrintf(sentry, "check_callback\t%d\n", squidaio_counts.check_callback);
+    storeAppendPrintf(sentry, "queue\t%d\n", squidaio_get_queue_len());
+}
+
+/* Flush all pending I/O */
+void
+aioSync(SwapDir * SD)
+{
+    if (!initialised)
+	return;			/* nothing to do then */
+    /* Flush all pending operations */
+    debug(32, 1) ("aioSync: flushing pending I/O operations\n");
+    do {
+	aioCheckCallbacks(SD);
+    } while (squidaio_sync());
+    debug(32, 1) ("aioSync: done\n");
+}
+
+int
+aioQueueSize(void)
+{
+    return memPoolInUseCount(squidaio_ctrl_pool);
+}
--- /dev/null	Wed Feb 14 00:55:47 2007
+++ squid/src/fs/awin32/store_asyncufs.h	Wed Feb 14 00:57:27 2007
@@ -0,0 +1,132 @@
+/*
+ * store_aufs.h
+ *
+ * Internal declarations for the aufs routines
+ */
+
+#ifndef __STORE_ASYNCUFS_H__
+#define __STORE_ASYNCUFS_H__
+
+#ifdef AUFS_IO_THREADS
+#define NUMTHREADS AUFS_IO_THREADS
+#else
+#define NUMTHREADS (Config.cacheSwap.n_configured*16)
+#endif
+
+/* Queue limit where swapouts are deferred (load calculation) */
+#define MAGIC1 (NUMTHREADS*Config.cacheSwap.n_configured*5)
+/* Queue limit where swapins are deferred (open/create fails) */
+#define MAGIC2 (NUMTHREADS*Config.cacheSwap.n_configured*20)
+
+/* Which operations to run async */
+#define ASYNC_OPEN 1
+#define ASYNC_CLOSE 0
+#define ASYNC_CREATE 1
+#define ASYNC_WRITE 0
+#define ASYNC_READ 1
+
+struct _squidaio_result_t {
+    int aio_return;
+    int aio_errno;
+    void *_data;		/* Internal housekeeping */
+    void *data;			/* Available to the caller */
+};
+
+typedef struct _squidaio_result_t squidaio_result_t;
+
+typedef void AIOCB(int fd, void *, int aio_return, int aio_errno);
+
+int squidaio_cancel(squidaio_result_t *);
+int squidaio_open(const char *, int, mode_t, squidaio_result_t *);
+int squidaio_read(int, char *, int, off_t, int, squidaio_result_t *);
+int squidaio_write(int, char *, int, off_t, int, squidaio_result_t *);
+int squidaio_close(int, squidaio_result_t *);
+int squidaio_stat(const char *, struct stat *, squidaio_result_t *);
+int squidaio_unlink(const char *, squidaio_result_t *);
+int squidaio_truncate(const char *, off_t length, squidaio_result_t *);
+int squidaio_opendir(const char *, squidaio_result_t *);
+squidaio_result_t *squidaio_poll_done(void);
+int squidaio_operations_pending(void);
+int squidaio_sync(void);
+int squidaio_get_queue_len(void);
+
+void aioInit(void);
+void aioDone(void);
+void aioCancel(int);
+void aioOpen(const char *, int, mode_t, AIOCB *, void *);
+void aioClose(int);
+void aioWrite(int, int offset, char *, int size, AIOCB *, void *, FREE *);
+void aioRead(int, int offset, char *, int size, AIOCB *, void *);
+void aioStat(char *, struct stat *, AIOCB *, void *);
+void aioUnlink(const char *, AIOCB *, void *);
+void aioTruncate(const char *, off_t length, AIOCB *, void *);
+int aioCheckCallbacks(SwapDir *);
+void aioSync(SwapDir *);
+int aioQueueSize(void);
+
+struct _squidaioinfo_t {
+    int swaplog_fd;
+    int l1;
+    int l2;
+    fileMap *map;
+    int suggest;
+};
+
+struct _squidaiostate_t {
+    int fd;
+    struct {
+	unsigned int close_request:1;
+	unsigned int reading:1;
+	unsigned int writing:1;
+	unsigned int opening:1;
+	unsigned int write_kicking:1;
+	unsigned int read_kicking:1;
+	unsigned int inreaddone:1;
+    } flags;
+    const char *read_buf;
+    link_list *pending_writes;
+    link_list *pending_reads;
+};
+
+struct _queued_write {
+    char *buf;
+    size_t size;
+    off_t offset;
+    FREE *free_func;
+};
+
+struct _queued_read {
+    char *buf;
+    size_t size;
+    off_t offset;
+    STRCB *callback;
+    void *callback_data;
+};
+
+typedef struct _squidaioinfo_t squidaioinfo_t;
+typedef struct _squidaiostate_t squidaiostate_t;
+
+/* The squidaio_state memory pools */
+extern MemPool *squidaio_state_pool;
+extern MemPool *aufs_qread_pool;
+extern MemPool *aufs_qwrite_pool;
+
+extern void storeAufsDirMapBitReset(SwapDir *, sfileno);
+extern int storeAufsDirMapBitAllocate(SwapDir *);
+
+extern char *storeAufsDirFullPath(SwapDir * SD, sfileno filn, char *fullpath);
+extern void storeAufsDirUnlinkFile(SwapDir *, sfileno);
+extern void storeAufsDirReplAdd(SwapDir * SD, StoreEntry *);
+extern void storeAufsDirReplRemove(StoreEntry *);
+
+/*
+ * Store IO stuff
+ */
+extern STOBJCREATE storeAufsCreate;
+extern STOBJOPEN storeAufsOpen;
+extern STOBJCLOSE storeAufsClose;
+extern STOBJREAD storeAufsRead;
+extern STOBJWRITE storeAufsWrite;
+extern STOBJUNLINK storeAufsUnlink;
+
+#endif
--- /dev/null	Wed Feb 14 00:55:47 2007
+++ squid/src/fs/awin32/store_dir_aufs.c	Wed Feb 14 00:57:27 2007
@@ -0,0 +1,1712 @@
+
+/*
+ * $Id: store_dir_aufs.c,v 1.1.62.1 2002/03/14 20:23:47 serassio Exp $
+ *
+ * DEBUG: section 47    Store Directory Routines
+ * AUTHOR: Duane Wessels
+ *
+ * SQUID Web Proxy Cache          http://www.squid-cache.org/
+ * ----------------------------------------------------------
+ *
+ *  Squid is the result of efforts by numerous individuals from
+ *  the Internet community; see the CONTRIBUTORS file for full
+ *  details.   Many organizations have provided support for Squid's
+ *  development; see the SPONSORS file for full details.  Squid is
+ *  Copyrighted (C) 2001 by the Regents of the University of
+ *  California; see the COPYRIGHT file for full details.  Squid
+ *  incorporates software developed and/or copyrighted by other
+ *  sources; see the CREDITS file for full details.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *  
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *  
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
+ *
+ */
+
+#include "squid.h"
+
+#include "store_asyncufs.h"
+
+#define DefaultLevelOneDirs     16
+#define DefaultLevelTwoDirs     256
+#define STORE_META_BUFSZ 4096
+
+typedef struct _RebuildState RebuildState;
+struct _RebuildState {
+    SwapDir *sd;
+    int n_read;
+    FILE *log;
+    int speed;
+    int curlvl1;
+    int curlvl2;
+    struct {
+	unsigned int need_to_validate:1;
+	unsigned int clean:1;
+	unsigned int init:1;
+    } flags;
+    int done;
+    int in_dir;
+    int fn;
+    struct dirent *entry;
+    DIR *td;
+    char fullpath[SQUID_MAXPATHLEN];
+    char fullfilename[SQUID_MAXPATHLEN];
+    struct _store_rebuild_data counts;
+};
+
+static int n_asyncufs_dirs = 0;
+static int *asyncufs_dir_index = NULL;
+MemPool *squidaio_state_pool = NULL;
+MemPool *aufs_qread_pool = NULL;
+MemPool *aufs_qwrite_pool = NULL;
+static int asyncufs_initialised = 0;
+
+static char *storeAufsDirSwapSubDir(SwapDir *, int subdirn);
+static int storeAufsDirCreateDirectory(const char *path, int);
+static int storeAufsDirVerifyCacheDirs(SwapDir *);
+static int storeAufsDirVerifyDirectory(const char *path);
+static void storeAufsDirCreateSwapSubDirs(SwapDir *);
+static char *storeAufsDirSwapLogFile(SwapDir *, const char *);
+static EVH storeAufsDirRebuildFromDirectory;
+static EVH storeAufsDirRebuildFromSwapLog;
+static int storeAufsDirGetNextFile(RebuildState *, sfileno *, int *size);
+static StoreEntry *storeAufsDirAddDiskRestore(SwapDir * SD, const cache_key * key,
+    sfileno file_number,
+    size_t swap_file_sz,
+    time_t expires,
+    time_t timestamp,
+    time_t lastref,
+    time_t lastmod,
+    u_num32 refcount,
+    u_short flags,
+    int clean);
+static void storeAufsDirRebuild(SwapDir * sd);
+static void storeAufsDirCloseTmpSwapLog(SwapDir * sd);
+static FILE *storeAufsDirOpenTmpSwapLog(SwapDir *, int *, int *);
+static STLOGOPEN storeAufsDirOpenSwapLog;
+static STINIT storeAufsDirInit;
+static STFREE storeAufsDirFree;
+static STLOGCLEANSTART storeAufsDirWriteCleanStart;
+static STLOGCLEANNEXTENTRY storeAufsDirCleanLogNextEntry;
+static STLOGCLEANWRITE storeAufsDirWriteCleanEntry;
+static STLOGCLEANDONE storeAufsDirWriteCleanDone;
+static STLOGCLOSE storeAufsDirCloseSwapLog;
+static STLOGWRITE storeAufsDirSwapLog;
+static STNEWFS storeAufsDirNewfs;
+static STDUMP storeAufsDirDump;
+static STMAINTAINFS storeAufsDirMaintain;
+static STCHECKOBJ storeAufsDirCheckObj;
+static STREFOBJ storeAufsDirRefObj;
+static STUNREFOBJ storeAufsDirUnrefObj;
+static QS rev_int_sort;
+static int storeAufsDirClean(int swap_index);
+static EVH storeAufsDirCleanEvent;
+static int storeAufsDirIs(SwapDir * sd);
+static int storeAufsFilenoBelongsHere(int fn, int F0, int F1, int F2);
+static int storeAufsCleanupDoubleCheck(SwapDir *, StoreEntry *);
+static void storeAufsDirStats(SwapDir *, StoreEntry *);
+static void storeAufsDirInitBitmap(SwapDir *);
+static int storeAufsDirValidFileno(SwapDir *, sfileno, int);
+
+/* The MAIN externally visible function */
+STSETUP storeFsSetup_awin32;
+
+/*
+ * These functions were ripped straight out of the heart of store_dir.c.
+ * They assume that the given filenum is on a asyncufs partiton, which may or
+ * may not be true.. 
+ * XXX this evilness should be tidied up at a later date!
+ */
+
+static int
+storeAufsDirMapBitTest(SwapDir * SD, sfileno filn)
+{
+    squidaioinfo_t *aioinfo;
+    aioinfo = (squidaioinfo_t *) SD->fsdata;
+    return file_map_bit_test(aioinfo->map, filn);
+}
+
+static void
+storeAufsDirMapBitSet(SwapDir * SD, sfileno filn)
+{
+    squidaioinfo_t *aioinfo;
+    aioinfo = (squidaioinfo_t *) SD->fsdata;
+    file_map_bit_set(aioinfo->map, filn);
+}
+
+void
+storeAufsDirMapBitReset(SwapDir * SD, sfileno filn)
+{
+    squidaioinfo_t *aioinfo;
+    aioinfo = (squidaioinfo_t *) SD->fsdata;
+    /*
+     * We have to test the bit before calling file_map_bit_reset.
+     * file_map_bit_reset doesn't do bounds checking.  It assumes
+     * filn is a valid file number, but it might not be because
+     * the map is dynamic in size.  Also clearing an already clear
+     * bit puts the map counter of-of-whack.
+     */
+    if (file_map_bit_test(aioinfo->map, filn))
+	file_map_bit_reset(aioinfo->map, filn);
+}
+
+int
+storeAufsDirMapBitAllocate(SwapDir * SD)
+{
+    squidaioinfo_t *aioinfo = (squidaioinfo_t *) SD->fsdata;
+    int fn;
+    fn = file_map_allocate(aioinfo->map, aioinfo->suggest);
+    file_map_bit_set(aioinfo->map, fn);
+    aioinfo->suggest = fn + 1;
+    return fn;
+}
+
+/*
+ * Initialise the asyncufs bitmap
+ *
+ * If there already is a bitmap, and the numobjects is larger than currently
+ * configured, we allocate a new bitmap and 'grow' the old one into it.
+ */
+static void
+storeAufsDirInitBitmap(SwapDir * sd)
+{
+    squidaioinfo_t *aioinfo = (squidaioinfo_t *) sd->fsdata;
+
+    if (aioinfo->map == NULL) {
+	/* First time */
+	aioinfo->map = file_map_create();
+    } else if (aioinfo->map->max_n_files) {
+	/* it grew, need to expand */
+	/* XXX We don't need it anymore .. */
+    }
+    /* else it shrunk, and we leave the old one in place */
+}
+
+static char *
+storeAufsDirSwapSubDir(SwapDir * sd, int subdirn)
+{
+    squidaioinfo_t *aioinfo = (squidaioinfo_t *) sd->fsdata;
+
+    LOCAL_ARRAY(char, fullfilename, SQUID_MAXPATHLEN);
+    assert(0 <= subdirn && subdirn < aioinfo->l1);
+    snprintf(fullfilename, SQUID_MAXPATHLEN, "%s/%02X", sd->path, subdirn);
+    return fullfilename;
+}
+
+static int
+storeAufsDirCreateDirectory(const char *path, int should_exist)
+{
+    int created = 0;
+    struct stat st;
+    getCurrentTime();
+    if (0 == stat(path, &st)) {
+	if (S_ISDIR(st.st_mode)) {
+	    debug(20, should_exist ? 3 : 1) ("%s exists\n", path);
+	} else {
+	    fatalf("Swap directory %s is not a directory.", path);
+	}
+    } else if (0 == mkdir(path, 0755)) {
+	debug(20, should_exist ? 1 : 3) ("%s created\n", path);
+	created = 1;
+    } else {
+	fatalf("Failed to make swap directory %s: %s",
+	    path, xstrerror());
+    }
+    return created;
+}
+
+static int
+storeAufsDirVerifyDirectory(const char *path)
+{
+    struct stat sb;
+    if (stat(path, &sb) < 0) {
+	debug(20, 0) ("%s: %s\n", path, xstrerror());
+	return -1;
+    }
+    if (S_ISDIR(sb.st_mode) == 0) {
+	debug(20, 0) ("%s is not a directory\n", path);
+	return -1;
+    }
+    return 0;
+}
+
+/*
+ * This function is called by storeAufsDirInit().  If this returns < 0,
+ * then Squid exits, complains about swap directories not
+ * existing, and instructs the admin to run 'squid -z'
+ */
+static int
+storeAufsDirVerifyCacheDirs(SwapDir * sd)
+{
+    squidaioinfo_t *aioinfo = (squidaioinfo_t *) sd->fsdata;
+    int j;
+    const char *path = sd->path;
+
+    if (storeAufsDirVerifyDirectory(path) < 0)
+	return -1;
+    for (j = 0; j < aioinfo->l1; j++) {
+	path = storeAufsDirSwapSubDir(sd, j);
+	if (storeAufsDirVerifyDirectory(path) < 0)
+	    return -1;
+    }
+    return 0;
+}
+
+static void
+storeAufsDirCreateSwapSubDirs(SwapDir * sd)
+{
+    squidaioinfo_t *aioinfo = (squidaioinfo_t *) sd->fsdata;
+    int i, k;
+    int should_exist;
+    LOCAL_ARRAY(char, name, MAXPATHLEN);
+    for (i = 0; i < aioinfo->l1; i++) {
+	snprintf(name, MAXPATHLEN, "%s/%02X", sd->path, i);
+	if (storeAufsDirCreateDirectory(name, 0))
+	    should_exist = 0;
+	else
+	    should_exist = 1;
+	debug(47, 1) ("Making directories in %s\n", name);
+	for (k = 0; k < aioinfo->l2; k++) {
+	    snprintf(name, MAXPATHLEN, "%s/%02X/%02X", sd->path, i, k);
+	    storeAufsDirCreateDirectory(name, should_exist);
+	}
+    }
+}
+
+static char *
+storeAufsDirSwapLogFile(SwapDir * sd, const char *ext)
+{
+    LOCAL_ARRAY(char, path, SQUID_MAXPATHLEN);
+    LOCAL_ARRAY(char, pathtmp, SQUID_MAXPATHLEN);
+    LOCAL_ARRAY(char, digit, 32);
+    char *pathtmp2;
+    if (Config.Log.swap) {
+	xstrncpy(pathtmp, sd->path, SQUID_MAXPATHLEN - 64);
+	pathtmp2 = pathtmp;
+	while ((pathtmp2 = strchr(pathtmp2, '/')) != NULL)
+	    *pathtmp2 = '.';
+	while (strlen(pathtmp) && pathtmp[strlen(pathtmp) - 1] == '.')
+	    pathtmp[strlen(pathtmp) - 1] = '\0';
+	for (pathtmp2 = pathtmp; *pathtmp2 == '.'; pathtmp2++);
+	snprintf(path, SQUID_MAXPATHLEN - 64, Config.Log.swap, pathtmp2);
+	if (strncmp(path, Config.Log.swap, SQUID_MAXPATHLEN - 64) == 0) {
+	    strcat(path, ".");
+	    snprintf(digit, 32, "%02d", sd->index);
+	    strncat(path, digit, 3);
+	}
+    } else {
+	xstrncpy(path, sd->path, SQUID_MAXPATHLEN - 64);
+	strcat(path, "/swap.state");
+    }
+    if (ext)
+	strncat(path, ext, 16);
+    return path;
+}
+
+static void
+storeAufsDirOpenSwapLog(SwapDir * sd)
+{
+    squidaioinfo_t *aioinfo = (squidaioinfo_t *) sd->fsdata;
+    char *path;
+    int fd;
+    path = storeAufsDirSwapLogFile(sd, NULL);
+    fd = file_open(path, O_WRONLY | O_CREAT | O_BINARY);
+    if (fd < 0) {
+	debug(50, 1) ("%s: %s\n", path, xstrerror());
+	fatal("storeAufsDirOpenSwapLog: Failed to open swap log.");
+    }
+    debug(47, 3) ("Cache Dir #%d log opened on FD %d\n", sd->index, fd);
+    aioinfo->swaplog_fd = fd;
+    if (0 == n_asyncufs_dirs)
+	assert(NULL == asyncufs_dir_index);
+    n_asyncufs_dirs++;
+    assert(n_asyncufs_dirs <= Config.cacheSwap.n_configured);
+}
+
+static void
+storeAufsDirCloseSwapLog(SwapDir * sd)
+{
+    squidaioinfo_t *aioinfo = (squidaioinfo_t *) sd->fsdata;
+    if (aioinfo->swaplog_fd < 0)	/* not open */
+	return;
+    file_close(aioinfo->swaplog_fd);
+    debug(47, 3) ("Cache Dir #%d log closed on FD %d\n",
+	sd->index, aioinfo->swaplog_fd);
+    aioinfo->swaplog_fd = -1;
+    n_asyncufs_dirs--;
+    assert(n_asyncufs_dirs >= 0);
+    if (0 == n_asyncufs_dirs)
+	safe_free(asyncufs_dir_index);
+}
+
+static void
+storeAufsDirInit(SwapDir * sd)
+{
+    static int started_clean_event = 0;
+    static const char *errmsg =
+    "\tFailed to verify one of the swap directories, Check cache.log\n"
+    "\tfor details.  Run 'squid -z' to create swap directories\n"
+    "\tif needed, or if running Squid for the first time.";
+    storeAufsDirInitBitmap(sd);
+    if (storeAufsDirVerifyCacheDirs(sd) < 0)
+	fatal(errmsg);
+    storeAufsDirOpenSwapLog(sd);
+    storeAufsDirRebuild(sd);
+    if (!started_clean_event) {
+	eventAdd("storeDirClean", storeAufsDirCleanEvent, NULL, 15.0, 1);
+	started_clean_event = 1;
+    }
+    (void) storeDirGetBlkSize(sd->path, &sd->fs.blksize);
+}
+
+static void
+storeAufsDirRebuildFromDirectory(void *data)
+{
+    RebuildState *rb = data;
+    SwapDir *SD = rb->sd;
+    LOCAL_ARRAY(char, hdr_buf, SM_PAGE_SIZE);
+    StoreEntry *e = NULL;
+    StoreEntry tmpe;
+    cache_key key[MD5_DIGEST_CHARS];
+    sfileno filn = 0;
+    int count;
+    int size;
+    struct stat sb;
+    int swap_hdr_len;
+    int fd = -1;
+    tlv *tlv_list;
+    tlv *t;
+    assert(rb != NULL);
+    debug(20, 3) ("storeAufsDirRebuildFromDirectory: DIR #%d\n", rb->sd->index);
+    for (count = 0; count < rb->speed; count++) {
+	assert(fd == -1);
+	fd = storeAufsDirGetNextFile(rb, &filn, &size);
+	if (fd == -2) {
+	    debug(20, 1) ("Done scanning %s swaplog (%d entries)\n",
+		rb->sd->path, rb->n_read);
+	    store_dirs_rebuilding--;
+	    storeAufsDirCloseTmpSwapLog(rb->sd);
+	    storeRebuildComplete(&rb->counts);
+	    cbdataFree(rb);
+	    return;
+	} else if (fd < 0) {
+	    continue;
+	}
+	assert(fd > -1);
+	/* lets get file stats here */
+	if (fstat(fd, &sb) < 0) {
+	    debug(20, 1) ("storeAufsDirRebuildFromDirectory: fstat(FD %d): %s\n",
+		fd, xstrerror());
+	    file_close(fd);
+	    store_open_disk_fd--;
+	    fd = -1;
+	    continue;
+	}
+	if ((++rb->counts.scancount & 0xFFFF) == 0)
+	    debug(20, 3) ("  %s %7d files opened so far.\n",
+		rb->sd->path, rb->counts.scancount);
+	debug(20, 9) ("file_in: fd=%d %08X\n", fd, filn);
+	statCounter.syscalls.disk.reads++;
+	if (read(fd, hdr_buf, SM_PAGE_SIZE) < 0) {
+	    debug(20, 1) ("storeAufsDirRebuildFromDirectory: read(FD %d): %s\n",
+		fd, xstrerror());
+	    file_close(fd);
+	    store_open_disk_fd--;
+	    fd = -1;
+	    continue;
+	}
+	file_close(fd);
+	store_open_disk_fd--;
+	fd = -1;
+	swap_hdr_len = 0;
+#if USE_TRUNCATE
+	if (sb.st_size == 0)
+	    continue;
+#endif
+	tlv_list = storeSwapMetaUnpack(hdr_buf, &swap_hdr_len);
+	if (tlv_list == NULL) {
+	    debug(20, 1) ("storeAufsDirRebuildFromDirectory: failed to get meta data\n");
+	    /* XXX shouldn't this be a call to storeAufsUnlink ? */
+	    storeAufsDirUnlinkFile(SD, filn);
+	    continue;
+	}
+	debug(20, 3) ("storeAufsDirRebuildFromDirectory: successful swap meta unpacking\n");
+	memset(key, '\0', MD5_DIGEST_CHARS);
+	memset(&tmpe, '\0', sizeof(StoreEntry));
+	for (t = tlv_list; t; t = t->next) {
+	    switch (t->type) {
+	    case STORE_META_KEY:
+		assert(t->length == MD5_DIGEST_CHARS);
+		xmemcpy(key, t->value, MD5_DIGEST_CHARS);
+		break;
+	    case STORE_META_STD:
+		assert(t->length == STORE_HDR_METASIZE);
+		xmemcpy(&tmpe.timestamp, t->value, STORE_HDR_METASIZE);
+		break;
+	    default:
+		break;
+	    }
+	}
+	storeSwapTLVFree(tlv_list);
+	tlv_list = NULL;
+	if (storeKeyNull(key)) {
+	    debug(20, 1) ("storeAufsDirRebuildFromDirectory: NULL key\n");
+	    storeAufsDirUnlinkFile(SD, filn);
+	    continue;
+	}
+	tmpe.hash.key = key;
+	/* check sizes */
+	if (tmpe.swap_file_sz == 0) {
+	    tmpe.swap_file_sz = sb.st_size;
+	} else if (tmpe.swap_file_sz == sb.st_size - swap_hdr_len) {
+	    tmpe.swap_file_sz = sb.st_size;
+	} else if (tmpe.swap_file_sz != sb.st_size) {
+	    debug(20, 1) ("storeAufsDirRebuildFromDirectory: SIZE MISMATCH %ld!=%ld\n",
+		(long int) tmpe.swap_file_sz, (long int) sb.st_size);
+	    storeAufsDirUnlinkFile(SD, filn);
+	    continue;
+	}
+	if (EBIT_TEST(tmpe.flags, KEY_PRIVATE)) {
+	    storeAufsDirUnlinkFile(SD, filn);
+	    rb->counts.badflags++;
+	    continue;
+	}
+	e = storeGet(key);
+	if (e && e->lastref >= tmpe.lastref) {
+	    /* key already exists, current entry is newer */
+	    /* keep old, ignore new */
+	    rb->counts.dupcount++;
+	    continue;
+	} else if (NULL != e) {
+	    /* URL already exists, this swapfile not being used */
+	    /* junk old, load new */
+	    storeRelease(e);	/* release old entry */
+	    rb->counts.dupcount++;
+	}
+	rb->counts.objcount++;
+	storeEntryDump(&tmpe, 5);
+	e = storeAufsDirAddDiskRestore(SD, key,
+	    filn,
+	    tmpe.swap_file_sz,
+	    tmpe.expires,
+	    tmpe.timestamp,
+	    tmpe.lastref,
+	    tmpe.lastmod,
+	    tmpe.refcount,	/* refcount */
+	    tmpe.flags,		/* flags */
+	    (int) rb->flags.clean);
+	storeDirSwapLog(e, SWAP_LOG_ADD);
+    }
+    eventAdd("storeRebuild", storeAufsDirRebuildFromDirectory, rb, 0.0, 1);
+}
+
+static void
+storeAufsDirRebuildFromSwapLog(void *data)
+{
+    RebuildState *rb = data;
+    SwapDir *SD = rb->sd;
+    StoreEntry *e = NULL;
+    storeSwapLogData s;
+    size_t ss = sizeof(storeSwapLogData);
+    int count;
+    int used;			/* is swapfile already in use? */
+    int disk_entry_newer;	/* is the log entry newer than current entry? */
+    double x;
+    assert(rb != NULL);
+    /* load a number of objects per invocation */
+    for (count = 0; count < rb->speed; count++) {
+	if (fread(&s, ss, 1, rb->log) != 1) {
+	    debug(20, 1) ("Done reading %s swaplog (%d entries)\n",
+		rb->sd->path, rb->n_read);
+	    fclose(rb->log);
+	    rb->log = NULL;
+	    store_dirs_rebuilding--;
+	    storeAufsDirCloseTmpSwapLog(rb->sd);
+	    storeRebuildComplete(&rb->counts);
+	    cbdataFree(rb);
+	    return;
+	}
+	rb->n_read++;
+	if (s.op <= SWAP_LOG_NOP)
+	    continue;
+	if (s.op >= SWAP_LOG_MAX)
+	    continue;
+	/*
+	 * BC: during 2.4 development, we changed the way swap file
+	 * numbers are assigned and stored.  The high 16 bits used
+	 * to encode the SD index number.  There used to be a call
+	 * to storeDirProperFileno here that re-assigned the index 
+	 * bits.  Now, for backwards compatibility, we just need
+	 * to mask it off.
+	 */
+	s.swap_filen &= 0x00FFFFFF;
+	debug(20, 3) ("storeAufsDirRebuildFromSwapLog: %s %s %08X\n",
+	    swap_log_op_str[(int) s.op],
+	    storeKeyText(s.key),
+	    s.swap_filen);
+	if (s.op == SWAP_LOG_ADD) {
+	    (void) 0;
+	} else if (s.op == SWAP_LOG_DEL) {
+	    if ((e = storeGet(s.key)) != NULL) {
+		/*
+		 * Make sure we don't unlink the file, it might be
+		 * in use by a subsequent entry.  Also note that
+		 * we don't have to subtract from store_swap_size
+		 * because adding to store_swap_size happens in
+		 * the cleanup procedure.
+		 */
+		storeExpireNow(e);
+		storeReleaseRequest(e);
+		if (e->swap_filen > -1) {
+		    storeAufsDirReplRemove(e);
+		    storeAufsDirMapBitReset(SD, e->swap_filen);
+		    e->swap_filen = -1;
+		    e->swap_dirn = -1;
+		}
+		storeRelease(e);
+		rb->counts.objcount--;
+		rb->counts.cancelcount++;
+	    }
+	    continue;
+	} else {
+	    x = log(++rb->counts.bad_log_op) / log(10.0);
+	    if (0.0 == x - (double) (int) x)
+		debug(20, 1) ("WARNING: %d invalid swap log entries found\n",
+		    rb->counts.bad_log_op);
+	    rb->counts.invalid++;
+	    continue;
+	}
+	if ((++rb->counts.scancount & 0xFFF) == 0) {
+	    struct stat sb;
+	    if (0 == fstat(fileno(rb->log), &sb))
+		storeRebuildProgress(SD->index,
+		    (int) sb.st_size / ss, rb->n_read);
+	}
+	if (!storeAufsDirValidFileno(SD, s.swap_filen, 0)) {
+	    rb->counts.invalid++;
+	    continue;
+	}
+	if (EBIT_TEST(s.flags, KEY_PRIVATE)) {
+	    rb->counts.badflags++;
+	    continue;
+	}
+	e = storeGet(s.key);
+	used = storeAufsDirMapBitTest(SD, s.swap_filen);
+	/* If this URL already exists in the cache, does the swap log
+	 * appear to have a newer entry?  Compare 'lastref' from the
+	 * swap log to e->lastref. */
+	disk_entry_newer = e ? (s.lastref > e->lastref ? 1 : 0) : 0;
+	if (used && !disk_entry_newer) {
+	    /* log entry is old, ignore it */
+	    rb->counts.clashcount++;
+	    continue;
+	} else if (used && e && e->swap_filen == s.swap_filen && e->swap_dirn == SD->index) {
+	    /* swapfile taken, same URL, newer, update meta */
+	    if (e->store_status == STORE_OK) {
+		e->lastref = s.timestamp;
+		e->timestamp = s.timestamp;
+		e->expires = s.expires;
+		e->lastmod = s.lastmod;
+		e->flags = s.flags;
+		e->refcount += s.refcount;
+		storeAufsDirUnrefObj(SD, e);
+	    } else {
+		debug_trap("storeAufsDirRebuildFromSwapLog: bad condition");
+		debug(20, 1) ("\tSee %s:%d\n", __FILE__, __LINE__);
+	    }
+	    continue;
+	} else if (used) {
+	    /* swapfile in use, not by this URL, log entry is newer */
+	    /* This is sorta bad: the log entry should NOT be newer at this
+	     * point.  If the log is dirty, the filesize check should have
+	     * caught this.  If the log is clean, there should never be a
+	     * newer entry. */
+	    debug(20, 1) ("WARNING: newer swaplog entry for dirno %d, fileno %08X\n",
+		SD->index, s.swap_filen);
+	    /* I'm tempted to remove the swapfile here just to be safe,
+	     * but there is a bad race condition in the NOVM version if
+	     * the swapfile has recently been opened for writing, but
+	     * not yet opened for reading.  Because we can't map
+	     * swapfiles back to StoreEntrys, we don't know the state
+	     * of the entry using that file.  */
+	    /* We'll assume the existing entry is valid, probably because
+	     * were in a slow rebuild and the the swap file number got taken
+	     * and the validation procedure hasn't run. */
+	    assert(rb->flags.need_to_validate);
+	    rb->counts.clashcount++;
+	    continue;
+	} else if (e && !disk_entry_newer) {
+	    /* key already exists, current entry is newer */
+	    /* keep old, ignore new */
+	    rb->counts.dupcount++;
+	    continue;
+	} else if (e) {
+	    /* key already exists, this swapfile not being used */
+	    /* junk old, load new */
+	    storeExpireNow(e);
+	    storeReleaseRequest(e);
+	    if (e->swap_filen > -1) {
+		storeAufsDirReplRemove(e);
+		/* Make sure we don't actually unlink the file */
+		storeAufsDirMapBitReset(SD, e->swap_filen);
+		e->swap_filen = -1;
+		e->swap_dirn = -1;
+	    }
+	    storeRelease(e);
+	    rb->counts.dupcount++;
+	} else {
+	    /* URL doesnt exist, swapfile not in use */
+	    /* load new */
+	    (void) 0;
+	}
+	/* update store_swap_size */
+	rb->counts.objcount++;
+	e = storeAufsDirAddDiskRestore(SD, s.key,
+	    s.swap_filen,
+	    s.swap_file_sz,
+	    s.expires,
+	    s.timestamp,
+	    s.lastref,
+	    s.lastmod,
+	    s.refcount,
+	    s.flags,
+	    (int) rb->flags.clean);
+	storeDirSwapLog(e, SWAP_LOG_ADD);
+    }
+    eventAdd("storeRebuild", storeAufsDirRebuildFromSwapLog, rb, 0.0, 1);
+}
+
+static int
+storeAufsDirGetNextFile(RebuildState * rb, sfileno * filn_p, int *size)
+{
+    SwapDir *SD = rb->sd;
+    squidaioinfo_t *aioinfo = (squidaioinfo_t *) SD->fsdata;
+    int fd = -1;
+    int used = 0;
+    int dirs_opened = 0;
+    debug(20, 3) ("storeAufsDirGetNextFile: flag=%d, %d: /%02X/%02X\n",
+	rb->flags.init,
+	rb->sd->index,
+	rb->curlvl1,
+	rb->curlvl2);
+    if (rb->done)
+	return -2;
+    while (fd < 0 && rb->done == 0) {
+	fd = -1;
+	if (0 == rb->flags.init) {	/* initialize, open first file */
+	    rb->done = 0;
+	    rb->curlvl1 = 0;
+	    rb->curlvl2 = 0;
+	    rb->in_dir = 0;
+	    rb->flags.init = 1;
+	    assert(Config.cacheSwap.n_configured > 0);
+	}
+	if (0 == rb->in_dir) {	/* we need to read in a new directory */
+	    snprintf(rb->fullpath, SQUID_MAXPATHLEN, "%s/%02X/%02X",
+		rb->sd->path,
+		rb->curlvl1, rb->curlvl2);
+	    if (rb->flags.init && rb->td != NULL)
+		closedir(rb->td);
+	    rb->td = NULL;
+	    if (dirs_opened)
+		return -1;
+	    rb->td = opendir(rb->fullpath);
+	    dirs_opened++;
+	    if (rb->td == NULL) {
+		debug(50, 1) ("storeAufsDirGetNextFile: opendir: %s: %s\n",
+		    rb->fullpath, xstrerror());
+	    } else {
+		rb->entry = readdir(rb->td);	/* skip . and .. */
+		rb->entry = readdir(rb->td);
+		if (rb->entry == NULL && errno == ENOENT)
+		    debug(20, 1) ("storeAufsDirGetNextFile: directory does not exist!.\n");
+		debug(20, 3) ("storeAufsDirGetNextFile: Directory %s\n", rb->fullpath);
+	    }
+	}
+	if (rb->td != NULL && (rb->entry = readdir(rb->td)) != NULL) {
+	    rb->in_dir++;
+	    if (sscanf(rb->entry->d_name, "%x", &rb->fn) != 1) {
+		debug(20, 3) ("storeAufsDirGetNextFile: invalid %s\n",
+		    rb->entry->d_name);
+		continue;
+	    }
+	    if (!storeAufsFilenoBelongsHere(rb->fn, rb->sd->index, rb->curlvl1, rb->curlvl2)) {
+		debug(20, 3) ("storeAufsDirGetNextFile: %08X does not belong in %d/%d/%d\n",
+		    rb->fn, rb->sd->index, rb->curlvl1, rb->curlvl2);
+		continue;
+	    }
+	    used = storeAufsDirMapBitTest(SD, rb->fn);
+	    if (used) {
+		debug(20, 3) ("storeAufsDirGetNextFile: Locked, continuing with next.\n");
+		continue;
+	    }
+	    snprintf(rb->fullfilename, SQUID_MAXPATHLEN, "%s/%s",
+		rb->fullpath, rb->entry->d_name);
+	    debug(20, 3) ("storeAufsDirGetNextFile: Opening %s\n", rb->fullfilename);
+	    fd = file_open(rb->fullfilename, O_RDONLY | O_BINARY);
+	    if (fd < 0)
+		debug(50, 1) ("storeAufsDirGetNextFile: %s: %s\n", rb->fullfilename, xstrerror());
+	    else
+		store_open_disk_fd++;
+	    continue;
+	}
+	rb->in_dir = 0;
+	if (++rb->curlvl2 < aioinfo->l2)
+	    continue;
+	rb->curlvl2 = 0;
+	if (++rb->curlvl1 < aioinfo->l1)
+	    continue;
+	rb->curlvl1 = 0;
+	rb->done = 1;
+    }
+    *filn_p = rb->fn;
+    return fd;
+}
+
+/* Add a new object to the cache with empty memory copy and pointer to disk
+ * use to rebuild store from disk. */
+static StoreEntry *
+storeAufsDirAddDiskRestore(SwapDir * SD, const cache_key * key,
+    sfileno file_number,
+    size_t swap_file_sz,
+    time_t expires,
+    time_t timestamp,
+    time_t lastref,
+    time_t lastmod,
+    u_num32 refcount,
+    u_short flags,
+    int clean)
+{
+    StoreEntry *e = NULL;
+    debug(20, 5) ("storeAufsAddDiskRestore: %s, fileno=%08X\n", storeKeyText(key), file_number);
+    /* if you call this you'd better be sure file_number is not 
+     * already in use! */
+    e = new_StoreEntry(STORE_ENTRY_WITHOUT_MEMOBJ, NULL, NULL);
+    e->store_status = STORE_OK;
+    storeSetMemStatus(e, NOT_IN_MEMORY);
+    e->swap_status = SWAPOUT_DONE;
+    e->swap_filen = file_number;
+    e->swap_dirn = SD->index;
+    e->swap_file_sz = swap_file_sz;
+    e->lock_count = 0;
+    e->lastref = lastref;
+    e->timestamp = timestamp;
+    e->expires = expires;
+    e->lastmod = lastmod;
+    e->refcount = refcount;
+    e->flags = flags;
+    EBIT_SET(e->flags, ENTRY_CACHABLE);
+    EBIT_CLR(e->flags, RELEASE_REQUEST);
+    EBIT_CLR(e->flags, KEY_PRIVATE);
+    e->ping_status = PING_NONE;
+    EBIT_CLR(e->flags, ENTRY_VALIDATED);
+    storeAufsDirMapBitSet(SD, e->swap_filen);
+    storeHashInsert(e, key);	/* do it after we clear KEY_PRIVATE */
+    storeAufsDirReplAdd(SD, e);
+    return e;
+}
+
+CBDATA_TYPE(RebuildState);
+
+static void
+storeAufsDirRebuild(SwapDir * sd)
+{
+    RebuildState *rb;
+    int clean = 0;
+    int zero = 0;
+    FILE *fp;
+    EVH *func = NULL;
+    CBDATA_INIT_TYPE(RebuildState);
+    rb = cbdataAlloc(RebuildState);
+    rb->sd = sd;
+    rb->speed = opt_foreground_rebuild ? 1 << 30 : 50;
+    /*
+     * If the swap.state file exists in the cache_dir, then
+     * we'll use storeAufsDirRebuildFromSwapLog(), otherwise we'll
+     * use storeAufsDirRebuildFromDirectory() to open up each file
+     * and suck in the meta data.
+     */
+    fp = storeAufsDirOpenTmpSwapLog(sd, &clean, &zero);
+    if (fp == NULL || zero) {
+	if (fp != NULL)
+	    fclose(fp);
+	func = storeAufsDirRebuildFromDirectory;
+    } else {
+	func = storeAufsDirRebuildFromSwapLog;
+	rb->log = fp;
+	rb->flags.clean = (unsigned int) clean;
+    }
+    if (!clean)
+	rb->flags.need_to_validate = 1;
+    debug(20, 1) ("Rebuilding storage in %s (%s)\n",
+	sd->path, clean ? "CLEAN" : "DIRTY");
+    store_dirs_rebuilding++;
+    eventAdd("storeRebuild", func, rb, 0.0, 1);
+}
+
+static void
+storeAufsDirCloseTmpSwapLog(SwapDir * sd)
+{
+    squidaioinfo_t *aioinfo = (squidaioinfo_t *) sd->fsdata;
+    char *swaplog_path = xstrdup(storeAufsDirSwapLogFile(sd, NULL));
+    char *new_path = xstrdup(storeAufsDirSwapLogFile(sd, ".new"));
+    int fd;
+    file_close(aioinfo->swaplog_fd);
+#if defined (_SQUID_OS2_) || defined (_SQUID_CYGWIN_) || defined (_SQUID_MSWIN_)
+    if (unlink(swaplog_path) < 0) {
+	debug(50, 0) ("%s: %s\n", swaplog_path, xstrerror());
+	fatal("storeAufsDirCloseTmpSwapLog: unlink failed");
+    }
+#endif
+    if (xrename(new_path, swaplog_path) < 0) {
+	fatal("storeAufsDirCloseTmpSwapLog: rename failed");
+    }
+    fd = file_open(swaplog_path, O_WRONLY | O_CREAT | O_BINARY);
+    if (fd < 0) {
+	debug(50, 1) ("%s: %s\n", swaplog_path, xstrerror());
+	fatal("storeAufsDirCloseTmpSwapLog: Failed to open swap log.");
+    }
+    safe_free(swaplog_path);
+    safe_free(new_path);
+    aioinfo->swaplog_fd = fd;
+    debug(47, 3) ("Cache Dir #%d log opened on FD %d\n", sd->index, fd);
+}
+
+static FILE *
+storeAufsDirOpenTmpSwapLog(SwapDir * sd, int *clean_flag, int *zero_flag)
+{
+    squidaioinfo_t *aioinfo = (squidaioinfo_t *) sd->fsdata;
+    char *swaplog_path = xstrdup(storeAufsDirSwapLogFile(sd, NULL));
+    char *clean_path = xstrdup(storeAufsDirSwapLogFile(sd, ".last-clean"));
+    char *new_path = xstrdup(storeAufsDirSwapLogFile(sd, ".new"));
+    struct stat log_sb;
+    struct stat clean_sb;
+    FILE *fp;
+    int fd;
+    if (stat(swaplog_path, &log_sb) < 0) {
+	debug(47, 1) ("Cache Dir #%d: No log file\n", sd->index);
+	safe_free(swaplog_path);
+	safe_free(clean_path);
+	safe_free(new_path);
+	return NULL;
+    }
+    *zero_flag = log_sb.st_size == 0 ? 1 : 0;
+    /* close the existing write-only FD */
+    if (aioinfo->swaplog_fd >= 0)
+	file_close(aioinfo->swaplog_fd);
+    /* open a write-only FD for the new log */
+    fd = file_open(new_path, O_WRONLY | O_CREAT | O_TRUNC);
+    if (fd < 0) {
+	debug(50, 1) ("%s: %s\n", new_path, xstrerror());
+	fatal("storeDirOpenTmpSwapLog: Failed to open swap log.");
+    }
+    aioinfo->swaplog_fd = fd;
+    /* open a read-only stream of the old log */
+    fp = fopen(swaplog_path, "r");
+    if (fp == NULL) {
+	debug(50, 0) ("%s: %s\n", swaplog_path, xstrerror());
+	fatal("Failed to open swap log for reading");
+    }
+    memset(&clean_sb, '\0', sizeof(struct stat));
+    if (stat(clean_path, &clean_sb) < 0)
+	*clean_flag = 0;
+    else if (clean_sb.st_mtime < log_sb.st_mtime)
+	*clean_flag = 0;
+    else
+	*clean_flag = 1;
+    safeunlink(clean_path, 1);
+    safe_free(swaplog_path);
+    safe_free(clean_path);
+    safe_free(new_path);
+    return fp;
+}
+
+struct _clean_state {
+    char *cur;
+    char *new;
+    char *cln;
+    char *outbuf;
+    off_t outbuf_offset;
+    int fd;
+    RemovalPolicyWalker *walker;
+};
+
+#define CLEAN_BUF_SZ 16384
+/*
+ * Begin the process to write clean cache state.  For AUFS this means
+ * opening some log files and allocating write buffers.  Return 0 if
+ * we succeed, and assign the 'func' and 'data' return pointers.
+ */
+static int
+storeAufsDirWriteCleanStart(SwapDir * sd)
+{
+    struct _clean_state *state = xcalloc(1, sizeof(*state));
+#if HAVE_FCHMOD
+    struct stat sb;
+#endif
+    sd->log.clean.write = NULL;
+    sd->log.clean.state = NULL;
+    state->new = xstrdup(storeAufsDirSwapLogFile(sd, ".clean"));
+    state->fd = file_open(state->new, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY);
+    if (state->fd < 0) {
+	xfree(state->new);
+	xfree(state);
+	return -1;
+    }
+    state->cur = xstrdup(storeAufsDirSwapLogFile(sd, NULL));
+    state->cln = xstrdup(storeAufsDirSwapLogFile(sd, ".last-clean"));
+    state->outbuf = xcalloc(CLEAN_BUF_SZ, 1);
+    state->outbuf_offset = 0;
+    state->walker = sd->repl->WalkInit(sd->repl);
+    unlink(state->new);
+    unlink(state->cln);
+    debug(20, 3) ("storeDirWriteCleanLogs: opened %s, FD %d\n",
+	state->new, state->fd);
+#if HAVE_FCHMOD
+    if (stat(state->cur, &sb) == 0)
+	fchmod(state->fd, sb.st_mode);
+#endif
+    sd->log.clean.write = storeAufsDirWriteCleanEntry;
+    sd->log.clean.state = state;
+    return 0;
+}
+
+/*
+ * Get the next entry that is a candidate for clean log writing
+ */
+const StoreEntry *
+storeAufsDirCleanLogNextEntry(SwapDir * sd)
+{
+    const StoreEntry *entry = NULL;
+    struct _clean_state *state = sd->log.clean.state;
+    if (state->walker)
+	entry = state->walker->Next(state->walker);
+    return entry;
+}
+
+/*
+ * "write" an entry to the clean log file.
+ */
+static void
+storeAufsDirWriteCleanEntry(SwapDir * sd, const StoreEntry * e)
+{
+    storeSwapLogData s;
+    static size_t ss = sizeof(storeSwapLogData);
+    struct _clean_state *state = sd->log.clean.state;
+    memset(&s, '\0', ss);
+    s.op = (char) SWAP_LOG_ADD;
+    s.swap_filen = e->swap_filen;
+    s.timestamp = e->timestamp;
+    s.lastref = e->lastref;
+    s.expires = e->expires;
+    s.lastmod = e->lastmod;
+    s.swap_file_sz = e->swap_file_sz;
+    s.refcount = e->refcount;
+    s.flags = e->flags;
+    xmemcpy(&s.key, e->hash.key, MD5_DIGEST_CHARS);
+    xmemcpy(state->outbuf + state->outbuf_offset, &s, ss);
+    state->outbuf_offset += ss;
+    /* buffered write */
+    if (state->outbuf_offset + ss > CLEAN_BUF_SZ) {
+	if (write(state->fd, state->outbuf, state->outbuf_offset) < 0) {
+	    debug(50, 0) ("storeDirWriteCleanLogs: %s: write: %s\n",
+		state->new, xstrerror());
+	    debug(20, 0) ("storeDirWriteCleanLogs: Current swap logfile not replaced.\n");
+	    file_close(state->fd);
+	    state->fd = -1;
+	    unlink(state->new);
+	    safe_free(state);
+	    sd->log.clean.state = NULL;
+	    sd->log.clean.write = NULL;
+	}
+	state->outbuf_offset = 0;
+    }
+}
+
+static void
+storeAufsDirWriteCleanDone(SwapDir * sd)
+{
+    int fd;
+    struct _clean_state *state = sd->log.clean.state;
+    if (NULL == state)
+	return;
+    if (state->fd < 0)
+	return;
+    state->walker->Done(state->walker);
+    if (write(state->fd, state->outbuf, state->outbuf_offset) < 0) {
+	debug(50, 0) ("storeDirWriteCleanLogs: %s: write: %s\n",
+	    state->new, xstrerror());
+	debug(20, 0) ("storeDirWriteCleanLogs: Current swap logfile "
+	    "not replaced.\n");
+	file_close(state->fd);
+	state->fd = -1;
+	unlink(state->new);
+    }
+    safe_free(state->outbuf);
+    /*
+     * You can't rename open files on Microsoft "operating systems"
+     * so we have to close before renaming.
+     */
+    storeAufsDirCloseSwapLog(sd);
+    /* save the fd value for a later test */
+    fd = state->fd;
+    /* rename */
+    if (state->fd >= 0) {
+#if defined(_SQUID_OS2_) || defined (_SQUID_CYGWIN_) || defined(_SQUID_MSWIN_)
+	file_close(state->fd);
+	state->fd = -1;
+	if (unlink(state->cur) < 0)
+	    debug(50, 0) ("storeDirWriteCleanLogs: unlinkd failed: %s, %s\n",
+		xstrerror(), state->cur);
+#endif
+	xrename(state->new, state->cur);
+    }
+    /* touch a timestamp file if we're not still validating */
+    if (store_dirs_rebuilding)
+	(void) 0;
+    else if (fd < 0)
+	(void) 0;
+    else
+	file_close(file_open(state->cln, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY));
+    /* close */
+    safe_free(state->cur);
+    safe_free(state->new);
+    safe_free(state->cln);
+    if (state->fd >= 0)
+	file_close(state->fd);
+    state->fd = -1;
+    safe_free(state);
+    sd->log.clean.state = NULL;
+    sd->log.clean.write = NULL;
+}
+
+static void
+storeSwapLogDataFree(void *s)
+{
+    memFree(s, MEM_SWAP_LOG_DATA);
+}
+
+static void
+storeAufsDirSwapLog(const SwapDir * sd, const StoreEntry * e, int op)
+{
+    squidaioinfo_t *aioinfo = (squidaioinfo_t *) sd->fsdata;
+    storeSwapLogData *s = memAllocate(MEM_SWAP_LOG_DATA);
+    s->op = (char) op;
+    s->swap_filen = e->swap_filen;
+    s->timestamp = e->timestamp;
+    s->lastref = e->lastref;
+    s->expires = e->expires;
+    s->lastmod = e->lastmod;
+    s->swap_file_sz = e->swap_file_sz;
+    s->refcount = e->refcount;
+    s->flags = e->flags;
+    xmemcpy(s->key, e->hash.key, MD5_DIGEST_CHARS);
+    file_write(aioinfo->swaplog_fd,
+	-1,
+	s,
+	sizeof(storeSwapLogData),
+	NULL,
+	NULL,
+	(FREE *) storeSwapLogDataFree);
+}
+
+static void
+storeAufsDirNewfs(SwapDir * sd)
+{
+    debug(47, 3) ("Creating swap space in %s\n", sd->path);
+    storeAufsDirCreateDirectory(sd->path, 0);
+    storeAufsDirCreateSwapSubDirs(sd);
+}
+
+static int
+rev_int_sort(const void *A, const void *B)
+{
+    const int *i1 = A;
+    const int *i2 = B;
+    return *i2 - *i1;
+}
+
+static int
+storeAufsDirClean(int swap_index)
+{
+    DIR *dp = NULL;
+    struct dirent *de = NULL;
+    LOCAL_ARRAY(char, p1, MAXPATHLEN + 1);
+    LOCAL_ARRAY(char, p2, MAXPATHLEN + 1);
+#if USE_TRUNCATE
+    struct stat sb;
+#endif
+    int files[20];
+    int swapfileno;
+    int fn;			/* same as swapfileno, but with dirn bits set */
+    int n = 0;
+    int k = 0;
+    int N0, N1, N2;
+    int D0, D1, D2;
+    SwapDir *SD;
+    squidaioinfo_t *aioinfo;
+    N0 = n_asyncufs_dirs;
+    D0 = asyncufs_dir_index[swap_index % N0];
+    SD = &Config.cacheSwap.swapDirs[D0];
+    aioinfo = (squidaioinfo_t *) SD->fsdata;
+    N1 = aioinfo->l1;
+    D1 = (swap_index / N0) % N1;
+    N2 = aioinfo->l2;
+    D2 = ((swap_index / N0) / N1) % N2;
+    snprintf(p1, SQUID_MAXPATHLEN, "%s/%02X/%02X",
+	Config.cacheSwap.swapDirs[D0].path, D1, D2);
+    debug(36, 3) ("storeDirClean: Cleaning directory %s\n", p1);
+    dp = opendir(p1);
+    if (dp == NULL) {
+	if (errno == ENOENT) {
+	    debug(36, 0) ("storeDirClean: WARNING: Creating %s\n", p1);
+	    if (mkdir(p1, 0777) == 0)
+		return 0;
+	}
+	debug(50, 0) ("storeDirClean: %s: %s\n", p1, xstrerror());
+	safeunlink(p1, 1);
+	return 0;
+    }
+    while ((de = readdir(dp)) != NULL && k < 20) {
+	if (sscanf(de->d_name, "%X", &swapfileno) != 1)
+	    continue;
+	fn = swapfileno;	/* XXX should remove this cruft ! */
+	if (storeAufsDirValidFileno(SD, fn, 1))
+	    if (storeAufsDirMapBitTest(SD, fn))
+		if (storeAufsFilenoBelongsHere(fn, D0, D1, D2))
+		    continue;
+#if USE_TRUNCATE
+	if (!stat(de->d_name, &sb))
+	    if (sb.st_size == 0)
+		continue;
+#endif
+	files[k++] = swapfileno;
+    }
+    closedir(dp);
+    if (k == 0)
+	return 0;
+    qsort(files, k, sizeof(int), rev_int_sort);
+    if (k > 10)
+	k = 10;
+    for (n = 0; n < k; n++) {
+	debug(36, 3) ("storeDirClean: Cleaning file %08X\n", files[n]);
+	snprintf(p2, MAXPATHLEN + 1, "%s/%08X", p1, files[n]);
+#if USE_TRUNCATE
+	truncate(p2, 0);
+#else
+	safeunlink(p2, 0);
+#endif
+	statCounter.swap.files_cleaned++;
+    }
+    debug(36, 3) ("Cleaned %d unused files from %s\n", k, p1);
+    return k;
+}
+
+static void
+storeAufsDirCleanEvent(void *unused)
+{
+    static int swap_index = 0;
+    int i;
+    int j = 0;
+    int n = 0;
+    /*
+     * Assert that there are AUFS cache_dirs configured, otherwise
+     * we should never be called.
+     */
+    assert(n_asyncufs_dirs);
+    if (NULL == asyncufs_dir_index) {
+	SwapDir *sd;
+	squidaioinfo_t *aioinfo;
+	/*
+	 * Initialize the little array that translates AUFS cache_dir
+	 * number into the Config.cacheSwap.swapDirs array index.
+	 */
+	asyncufs_dir_index = xcalloc(n_asyncufs_dirs, sizeof(*asyncufs_dir_index));
+	for (i = 0, n = 0; i < Config.cacheSwap.n_configured; i++) {
+	    sd = &Config.cacheSwap.swapDirs[i];
+	    if (!storeAufsDirIs(sd))
+		continue;
+	    asyncufs_dir_index[n++] = i;
+	    aioinfo = (squidaioinfo_t *) sd->fsdata;
+	    j += (aioinfo->l1 * aioinfo->l2);
+	}
+	assert(n == n_asyncufs_dirs);
+	/*
+	 * Start the storeAufsDirClean() swap_index with a random
+	 * value.  j equals the total number of AUFS level 2
+	 * swap directories
+	 */
+	swap_index = (int) (squid_random() % j);
+    }
+    if (0 == store_dirs_rebuilding) {
+	n = storeAufsDirClean(swap_index);
+	swap_index++;
+    }
+    eventAdd("storeDirClean", storeAufsDirCleanEvent, NULL,
+	15.0 * exp(-0.25 * n), 1);
+}
+
+static int
+storeAufsDirIs(SwapDir * sd)
+{
+    if (strncmp(sd->type, "awin32", 3) == 0)
+	return 1;
+    return 0;
+}
+
+/*
+ * Does swapfile number 'fn' belong in cachedir #F0,
+ * level1 dir #F1, level2 dir #F2?
+ */
+static int
+storeAufsFilenoBelongsHere(int fn, int F0, int F1, int F2)
+{
+    int D1, D2;
+    int L1, L2;
+    int filn = fn;
+    squidaioinfo_t *aioinfo;
+    assert(F0 < Config.cacheSwap.n_configured);
+    aioinfo = (squidaioinfo_t *) Config.cacheSwap.swapDirs[F0].fsdata;
+    L1 = aioinfo->l1;
+    L2 = aioinfo->l2;
+    D1 = ((filn / L2) / L2) % L1;
+    if (F1 != D1)
+	return 0;
+    D2 = (filn / L2) % L2;
+    if (F2 != D2)
+	return 0;
+    return 1;
+}
+
+int
+storeAufsDirValidFileno(SwapDir * SD, sfileno filn, int flag)
+{
+    squidaioinfo_t *aioinfo = (squidaioinfo_t *) SD->fsdata;
+    if (filn < 0)
+	return 0;
+    /*
+     * If flag is set it means out-of-range file number should
+     * be considered invalid.
+     */
+    if (flag)
+	if (filn > aioinfo->map->max_n_files)
+	    return 0;
+    return 1;
+}
+
+void
+storeAufsDirMaintain(SwapDir * SD)
+{
+    StoreEntry *e = NULL;
+    int removed = 0;
+    int max_scan;
+    int max_remove;
+    double f;
+    RemovalPurgeWalker *walker;
+    /* We can't delete objects while rebuilding swap */
+    if (store_dirs_rebuilding) {
+	return;
+    } else {
+	f = (double) (SD->cur_size - SD->low_size) / (SD->max_size - SD->low_size);
+	f = f < 0.0 ? 0.0 : f > 1.0 ? 1.0 : f;
+	max_scan = (int) (f * 400.0 + 100.0);
+	max_remove = (int) (f * 70.0 + 10.0);
+	/*
+	 * This is kinda cheap, but so we need this priority hack?
+	 */
+    }
+    debug(20, 3) ("storeMaintainSwapSpace: f=%f, max_scan=%d, max_remove=%d\n",
+	f, max_scan, max_remove);
+    walker = SD->repl->PurgeInit(SD->repl, max_scan);
+    while (1) {
+	if (SD->cur_size < SD->low_size)
+	    break;
+	if (removed >= max_remove)
+	    break;
+	e = walker->Next(walker);
+	if (!e)
+	    break;		/* no more objects */
+	removed++;
+	storeRelease(e);
+    }
+    walker->Done(walker);
+    debug(20, (removed ? 2 : 3)) ("storeUfsDirMaintain: %s removed %d/%d f=%.03f max_scan=%d\n",
+	SD->path, removed, max_remove, f, max_scan);
+}
+
+/*
+ * storeAufsDirCheckObj
+ *
+ * This routine is called by storeDirSelectSwapDir to see if the given
+ * object is able to be stored on this filesystem. AUFS filesystems will
+ * happily store anything as long as the LRU time isn't too small.
+ */
+int
+storeAufsDirCheckObj(SwapDir * SD, const StoreEntry * e)
+{
+    int loadav;
+    int ql;
+
+#if OLD_UNUSED_CODE
+    if (storeAufsDirExpiredReferenceAge(SD) < 300) {
+	debug(20, 3) ("storeAufsDirCheckObj: NO: LRU Age = %d\n",
+	    storeAufsDirExpiredReferenceAge(SD));
+	/* store_check_cachable_hist.no.lru_age_too_low++; */
+	return -1;
+    }
+#endif
+    ql = aioQueueSize();
+    if (ql == 0)
+	loadav = 0;
+    loadav = ql * 1000 / MAGIC1;
+    debug(41, 9) ("storeAufsDirCheckObj: load=%d\n", loadav);
+    return loadav;
+}
+
+/*
+ * storeAufsDirRefObj
+ *
+ * This routine is called whenever an object is referenced, so we can
+ * maintain replacement information within the storage fs.
+ */
+void
+storeAufsDirRefObj(SwapDir * SD, StoreEntry * e)
+{
+    debug(1, 3) ("storeAufsDirRefObj: referencing %p %d/%d\n", e, e->swap_dirn,
+	e->swap_filen);
+    if (SD->repl->Referenced)
+	SD->repl->Referenced(SD->repl, e, &e->repl);
+}
+
+/*
+ * storeAufsDirUnrefObj
+ * This routine is called whenever the last reference to an object is
+ * removed, to maintain replacement information within the storage fs.
+ */
+void
+storeAufsDirUnrefObj(SwapDir * SD, StoreEntry * e)
+{
+    debug(1, 3) ("storeAufsDirUnrefObj: referencing %p %d/%d\n", e, e->swap_dirn,
+	e->swap_filen);
+    if (SD->repl->Dereferenced)
+	SD->repl->Dereferenced(SD->repl, e, &e->repl);
+}
+
+/*
+ * storeAufsDirUnlinkFile
+ *
+ * This routine unlinks a file and pulls it out of the bitmap.
+ * It used to be in storeAufsUnlink(), however an interface change
+ * forced this bit of code here. Eeek.
+ */
+void
+storeAufsDirUnlinkFile(SwapDir * SD, sfileno f)
+{
+    debug(79, 3) ("storeAufsDirUnlinkFile: unlinking fileno %08X\n", f);
+    /* storeAufsDirMapBitReset(SD, f); */
+#if USE_TRUNCATE_NOT_UNLINK
+    aioTruncate(storeAufsDirFullPath(SD, f, NULL), NULL, NULL);
+#else
+    aioUnlink(storeAufsDirFullPath(SD, f, NULL), NULL, NULL);
+#endif
+}
+
+/*
+ * Add and remove the given StoreEntry from the replacement policy in
+ * use.
+ */
+
+void
+storeAufsDirReplAdd(SwapDir * SD, StoreEntry * e)
+{
+    debug(20, 4) ("storeAufsDirReplAdd: added node %p to dir %d\n", e,
+	SD->index);
+    SD->repl->Add(SD->repl, e, &e->repl);
+}
+
+
+void
+storeAufsDirReplRemove(StoreEntry * e)
+{
+    SwapDir *SD = INDEXSD(e->swap_dirn);
+    debug(20, 4) ("storeAufsDirReplRemove: remove node %p from dir %d\n", e,
+	SD->index);
+    SD->repl->Remove(SD->repl, e, &e->repl);
+}
+
+
+
+/* ========== LOCAL FUNCTIONS ABOVE, GLOBAL FUNCTIONS BELOW ========== */
+
+void
+storeAufsDirStats(SwapDir * SD, StoreEntry * sentry)
+{
+    squidaioinfo_t *aioinfo = SD->fsdata;
+    int totl_kb = 0;
+    int free_kb = 0;
+    int totl_in = 0;
+    int free_in = 0;
+    int x;
+    storeAppendPrintf(sentry, "First level subdirectories: %d\n", aioinfo->l1);
+    storeAppendPrintf(sentry, "Second level subdirectories: %d\n", aioinfo->l2);
+    storeAppendPrintf(sentry, "Maximum Size: %d KB\n", SD->max_size);
+    storeAppendPrintf(sentry, "Current Size: %d KB\n", SD->cur_size);
+    storeAppendPrintf(sentry, "Percent Used: %0.2f%%\n",
+	100.0 * SD->cur_size / SD->max_size);
+    storeAppendPrintf(sentry, "Filemap bits in use: %d of %d (%d%%)\n",
+	aioinfo->map->n_files_in_map, aioinfo->map->max_n_files,
+	percent(aioinfo->map->n_files_in_map, aioinfo->map->max_n_files));
+    x = storeDirGetUFSStats(SD->path, &totl_kb, &free_kb, &totl_in, &free_in);
+    if (0 == x) {
+	storeAppendPrintf(sentry, "Filesystem Space in use: %d/%d KB (%d%%)\n",
+	    totl_kb - free_kb,
+	    totl_kb,
+	    percent(totl_kb - free_kb, totl_kb));
+	storeAppendPrintf(sentry, "Filesystem Inodes in use: %d/%d (%d%%)\n",
+	    totl_in - free_in,
+	    totl_in,
+	    percent(totl_in - free_in, totl_in));
+    }
+    storeAppendPrintf(sentry, "Flags:");
+    if (SD->flags.selected)
+	storeAppendPrintf(sentry, " SELECTED");
+    if (SD->flags.read_only)
+	storeAppendPrintf(sentry, " READ-ONLY");
+    storeAppendPrintf(sentry, "\n");
+}
+
+static struct cache_dir_option options[] =
+{
+#if NOT_YET_DONE
+    {"L1", storeAufsDirParseL1},
+    {"L2", storeAufsDirParseL2},
+#endif
+    {NULL, NULL}
+};
+
+/*
+ * storeAufsDirReconfigure
+ *
+ * This routine is called when the given swapdir needs reconfiguring 
+ */
+static void
+storeAufsDirReconfigure(SwapDir * sd, int index, char *path)
+{
+    int i;
+    int size;
+    int l1;
+    int l2;
+
+    i = GetInteger();
+    size = i << 10;		/* Mbytes to kbytes */
+    if (size <= 0)
+	fatal("storeAufsDirReconfigure: invalid size value");
+    i = GetInteger();
+    l1 = i;
+    if (l1 <= 0)
+	fatal("storeAufsDirReconfigure: invalid level 1 directories value");
+    i = GetInteger();
+    l2 = i;
+    if (l2 <= 0)
+	fatal("storeAufsDirReconfigure: invalid level 2 directories value");
+
+    /* just reconfigure it */
+    if (size == sd->max_size)
+	debug(3, 1) ("Cache dir '%s' size remains unchanged at %d KB\n",
+	    path, size);
+    else
+	debug(3, 1) ("Cache dir '%s' size changed to %d KB\n",
+	    path, size);
+    sd->max_size = size;
+
+    parse_cachedir_options(sd, options, 0);
+
+    return;
+}
+
+void
+storeAufsDirDump(StoreEntry * entry, SwapDir * s)
+{
+    squidaioinfo_t *aioinfo = (squidaioinfo_t *) s->fsdata;
+    storeAppendPrintf(entry, " %d %d %d",
+	s->max_size >> 10,
+	aioinfo->l1,
+	aioinfo->l2);
+    dump_cachedir_options(entry, options, s);
+}
+
+/*
+ * Only "free" the filesystem specific stuff here
+ */
+static void
+storeAufsDirFree(SwapDir * s)
+{
+    squidaioinfo_t *aioinfo = (squidaioinfo_t *) s->fsdata;
+    if (aioinfo->swaplog_fd > -1) {
+	file_close(aioinfo->swaplog_fd);
+	aioinfo->swaplog_fd = -1;
+    }
+    filemapFreeMemory(aioinfo->map);
+    xfree(aioinfo);
+    s->fsdata = NULL;		/* Will aid debugging... */
+}
+
+char *
+storeAufsDirFullPath(SwapDir * SD, sfileno filn, char *fullpath)
+{
+    LOCAL_ARRAY(char, fullfilename, SQUID_MAXPATHLEN);
+    squidaioinfo_t *aioinfo = (squidaioinfo_t *) SD->fsdata;
+    int L1 = aioinfo->l1;
+    int L2 = aioinfo->l2;
+    if (!fullpath)
+	fullpath = fullfilename;
+    fullpath[0] = '\0';
+    snprintf(fullpath, SQUID_MAXPATHLEN, "%s/%02X/%02X/%08X",
+	SD->path,
+	((filn / L2) / L2) % L1,
+	(filn / L2) % L2,
+	filn);
+    return fullpath;
+}
+
+/*
+ * storeAufsCleanupDoubleCheck
+ *
+ * This is called by storeCleanup() if -S was given on the command line.
+ */
+static int
+storeAufsCleanupDoubleCheck(SwapDir * sd, StoreEntry * e)
+{
+#ifndef _SQUID_MSWIN_
+    struct stat sb;
+    if (stat(storeAufsDirFullPath(sd, e->swap_filen, NULL), &sb) < 0) {
+	debug(20, 0) ("storeAufsCleanupDoubleCheck: MISSING SWAP FILE\n");
+	debug(20, 0) ("storeAufsCleanupDoubleCheck: FILENO %08X\n", e->swap_filen);
+	debug(20, 0) ("storeAufsCleanupDoubleCheck: PATH %s\n",
+	    storeAufsDirFullPath(sd, e->swap_filen, NULL));
+	storeEntryDump(e, 0);
+	return -1;
+    }
+    if (e->swap_file_sz != sb.st_size) {
+	debug(20, 0) ("storeAufsCleanupDoubleCheck: SIZE MISMATCH\n");
+	debug(20, 0) ("storeAufsCleanupDoubleCheck: FILENO %08X\n", e->swap_filen);
+	debug(20, 0) ("storeAufsCleanupDoubleCheck: PATH %s\n",
+	    storeAufsDirFullPath(sd, e->swap_filen, NULL));
+	debug(20, 0) ("storeAufsCleanupDoubleCheck: ENTRY SIZE: %ld, FILE SIZE: %ld\n",
+	    (long int) e->swap_file_sz, (long int) sb.st_size);
+	storeEntryDump(e, 0);
+	return -1;
+    }
+#endif
+    return 0;
+}
+
+/*
+ * storeAufsDirParse *
+ * Called when a *new* fs is being setup.
+ */
+static void
+storeAufsDirParse(SwapDir * sd, int index, char *path)
+{
+    int i;
+    int size;
+    int l1;
+    int l2;
+    squidaioinfo_t *aioinfo;
+
+    i = GetInteger();
+    size = i << 10;		/* Mbytes to kbytes */
+    if (size <= 0)
+	fatal("storeAufsDirParse: invalid size value");
+    i = GetInteger();
+    l1 = i;
+    if (l1 <= 0)
+	fatal("storeAufsDirParse: invalid level 1 directories value");
+    i = GetInteger();
+    l2 = i;
+    if (l2 <= 0)
+	fatal("storeAufsDirParse: invalid level 2 directories value");
+
+    aioinfo = xmalloc(sizeof(squidaioinfo_t));
+    if (aioinfo == NULL)
+	fatal("storeAufsDirParse: couldn't xmalloc() squidaioinfo_t!\n");
+
+    sd->index = index;
+    sd->path = xstrdup(path);
+    sd->max_size = size;
+    sd->fsdata = aioinfo;
+    aioinfo->l1 = l1;
+    aioinfo->l2 = l2;
+    aioinfo->swaplog_fd = -1;
+    aioinfo->map = NULL;	/* Debugging purposes */
+    aioinfo->suggest = 0;
+    sd->init = storeAufsDirInit;
+    sd->newfs = storeAufsDirNewfs;
+    sd->dump = storeAufsDirDump;
+    sd->freefs = storeAufsDirFree;
+    sd->dblcheck = storeAufsCleanupDoubleCheck;
+    sd->statfs = storeAufsDirStats;
+    sd->maintainfs = storeAufsDirMaintain;
+    sd->checkobj = storeAufsDirCheckObj;
+    sd->refobj = storeAufsDirRefObj;
+    sd->unrefobj = storeAufsDirUnrefObj;
+    sd->callback = aioCheckCallbacks;
+    sd->sync = aioSync;
+    sd->obj.create = storeAufsCreate;
+    sd->obj.open = storeAufsOpen;
+    sd->obj.close = storeAufsClose;
+    sd->obj.read = storeAufsRead;
+    sd->obj.write = storeAufsWrite;
+    sd->obj.unlink = storeAufsUnlink;
+    sd->log.open = storeAufsDirOpenSwapLog;
+    sd->log.close = storeAufsDirCloseSwapLog;
+    sd->log.write = storeAufsDirSwapLog;
+    sd->log.clean.start = storeAufsDirWriteCleanStart;
+    sd->log.clean.nextentry = storeAufsDirCleanLogNextEntry;
+    sd->log.clean.done = storeAufsDirWriteCleanDone;
+
+    parse_cachedir_options(sd, options, 0);
+
+    /* Initialise replacement policy stuff */
+    sd->repl = createRemovalPolicy(Config.replPolicy);
+}
+
+/*
+ * Initial setup / end destruction
+ */
+static void
+storeAufsDirDone(void)
+{
+    aioDone();
+    memPoolDestroy(squidaio_state_pool);
+    memPoolDestroy(aufs_qread_pool);
+    memPoolDestroy(aufs_qwrite_pool);
+    asyncufs_initialised = 0;
+}
+
+void
+storeFsSetup_awin32(storefs_entry_t * storefs)
+{
+    assert(!asyncufs_initialised);
+    storefs->parsefunc = storeAufsDirParse;
+    storefs->reconfigurefunc = storeAufsDirReconfigure;
+    storefs->donefunc = storeAufsDirDone;
+    squidaio_state_pool = memPoolCreate("AUFS IO State data", sizeof(squidaiostate_t));
+    aufs_qread_pool = memPoolCreate("AUFS Queued read data",
+	sizeof(queued_read));
+    aufs_qwrite_pool = memPoolCreate("AUFS Queued write data",
+	sizeof(queued_write));
+
+    asyncufs_initialised = 1;
+    aioInit();
+}
--- /dev/null	Wed Feb 14 00:55:47 2007
+++ squid/src/fs/awin32/store_io_aufs.c	Wed Feb 14 00:57:27 2007
@@ -0,0 +1,446 @@
+
+/*
+ * DEBUG 78
+ */
+
+#include "squid.h"
+#include "store_asyncufs.h"
+
+#if ASYNC_READ
+static AIOCB storeAufsReadDone;
+#else
+static DRCB storeAufsReadDone;
+#endif
+#if ASYNC_WRITE
+static AIOCB storeAufsWriteDone;
+#else
+static DWCB storeAufsWriteDone;
+#endif
+static void storeAufsIOCallback(storeIOState * sio, int errflag);
+static AIOCB storeAufsOpenDone;
+static int storeAufsSomethingPending(storeIOState *);
+static int storeAufsKickWriteQueue(storeIOState * sio);
+static CBDUNL storeAufsIOFreeEntry;
+
+CBDATA_TYPE(storeIOState);
+
+/* === PUBLIC =========================================================== */
+
+/* open for reading */
+storeIOState *
+storeAufsOpen(SwapDir * SD, StoreEntry * e, STFNCB * file_callback,
+    STIOCB * callback, void *callback_data)
+{
+    sfileno f = e->swap_filen;
+    char *path = storeAufsDirFullPath(SD, f, NULL);
+    storeIOState *sio;
+#if !ASYNC_OPEN
+    int fd;
+#endif
+    debug(78, 3) ("storeAufsOpen: fileno %08X\n", f);
+    /*
+     * we should detect some 'too many files open' condition and return
+     * NULL here.
+     */
+#ifdef MAGIC2
+    if (aioQueueSize() > MAGIC2)
+	return NULL;
+#endif
+#if !ASYNC_OPEN
+    fd = file_open(path, O_RDONLY | O_BINARY);
+    if (fd < 0) {
+	debug(78, 3) ("storeAufsOpen: got failude (%d)\n", errno);
+	return NULL;
+    }
+#endif
+    CBDATA_INIT_TYPE_FREECB(storeIOState, storeAufsIOFreeEntry);
+    sio = cbdataAlloc(storeIOState);
+    sio->fsstate = memPoolAlloc(squidaio_state_pool);
+    ((squidaiostate_t *) (sio->fsstate))->fd = -1;
+    ((squidaiostate_t *) (sio->fsstate))->flags.opening = 1;
+    sio->swap_filen = f;
+    sio->swap_dirn = SD->index;
+    sio->mode = O_RDONLY;
+    sio->callback = callback;
+    sio->callback_data = callback_data;
+    sio->e = e;
+    cbdataLock(callback_data);
+    Opening_FD++;
+#if ASYNC_OPEN
+    aioOpen(path, O_RDONLY | O_BINARY, 0644, storeAufsOpenDone, sio);
+#else
+    storeAufsOpenDone(fd, sio, fd, 0);
+#endif
+    return sio;
+}
+
+/* open for creating */
+storeIOState *
+storeAufsCreate(SwapDir * SD, StoreEntry * e, STFNCB * file_callback, STIOCB * callback, void *callback_data)
+{
+    char *path;
+    storeIOState *sio;
+    sfileno filn;
+    sdirno dirn;
+#if !ASYNC_CREATE
+    int fd;
+#endif
+
+    /* Allocate a number */
+    dirn = SD->index;
+    filn = storeAufsDirMapBitAllocate(SD);
+    path = storeAufsDirFullPath(SD, filn, NULL);
+
+    debug(78, 3) ("storeAufsCreate: fileno %08X\n", filn);
+    /*
+     * we should detect some 'too many files open' condition and return
+     * NULL here.
+     */
+#ifdef MAGIC2
+    if (aioQueueSize() > MAGIC2)
+	return NULL;
+#endif
+#if !ASYNC_CREATE
+    fd = file_open(path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY);
+    if (fd < 0) {
+	debug(78, 3) ("storeAufsCreate: got failude (%d)\n", errno);
+	return NULL;
+    }
+#endif
+    CBDATA_INIT_TYPE_FREECB(storeIOState, storeAufsIOFreeEntry);
+    sio = cbdataAlloc(storeIOState);
+    sio->fsstate = memPoolAlloc(squidaio_state_pool);
+    ((squidaiostate_t *) (sio->fsstate))->fd = -1;
+    ((squidaiostate_t *) (sio->fsstate))->flags.opening = 1;
+    sio->swap_filen = filn;
+    sio->swap_dirn = dirn;
+    sio->mode = O_WRONLY | O_BINARY;
+    sio->callback = callback;
+    sio->callback_data = callback_data;
+    sio->e = (StoreEntry *) e;
+    cbdataLock(callback_data);
+    Opening_FD++;
+#if ASYNC_CREATE
+    aioOpen(path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644, storeAufsOpenDone, sio);
+#else
+    storeAufsOpenDone(fd, sio, fd, 0);
+#endif
+
+    /* now insert into the replacement policy */
+    storeAufsDirReplAdd(SD, e);
+    return sio;
+
+}
+
+
+
+/* Close */
+void
+storeAufsClose(SwapDir * SD, storeIOState * sio)
+{
+    squidaiostate_t *aiostate = (squidaiostate_t *) sio->fsstate;
+    debug(78, 3) ("storeAufsClose: dirno %d, fileno %08X, FD %d\n",
+	sio->swap_dirn, sio->swap_filen, aiostate->fd);
+    if (storeAufsSomethingPending(sio)) {
+	aiostate->flags.close_request = 1;
+	return;
+    }
+    storeAufsIOCallback(sio, DISK_OK);
+}
+
+
+/* Read */
+void
+storeAufsRead(SwapDir * SD, storeIOState * sio, char *buf, size_t size, off_t offset, STRCB * callback, void *callback_data)
+{
+    squidaiostate_t *aiostate = (squidaiostate_t *) sio->fsstate;
+    assert(sio->read.callback == NULL);
+    assert(sio->read.callback_data == NULL);
+    assert(!aiostate->flags.reading);
+    if (aiostate->fd < 0) {
+	struct _queued_read *q;
+	debug(78, 3) ("storeAufsRead: queueing read because FD < 0\n");
+	assert(aiostate->flags.opening);
+	assert(aiostate->pending_reads == NULL);
+	q = memPoolAlloc(aufs_qread_pool);
+	q->buf = buf;
+	q->size = size;
+	q->offset = offset;
+	q->callback = callback;
+	q->callback_data = callback_data;
+	linklistPush(&(aiostate->pending_reads), q);
+	return;
+    }
+    sio->read.callback = callback;
+    sio->read.callback_data = callback_data;
+    aiostate->read_buf = buf;
+    cbdataLock(callback_data);
+    debug(78, 3) ("storeAufsRead: dirno %d, fileno %08X, FD %d\n",
+	sio->swap_dirn, sio->swap_filen, aiostate->fd);
+    sio->offset = offset;
+    aiostate->flags.reading = 1;
+#if ASYNC_READ
+    aioRead(aiostate->fd, offset, buf, size, storeAufsReadDone, sio);
+#else
+    file_read(aiostate->fd, offset, buf, size, storeAufsReadDone, sio);
+#endif
+}
+
+
+/* Write */
+void
+storeAufsWrite(SwapDir * SD, storeIOState * sio, char *buf, size_t size, off_t offset, FREE * free_func)
+{
+    squidaiostate_t *aiostate = (squidaiostate_t *) sio->fsstate;
+    debug(78, 3) ("storeAufsWrite: dirno %d, fileno %08X, FD %d\n",
+	sio->swap_dirn, sio->swap_filen, aiostate->fd);
+    if (aiostate->fd < 0) {
+	/* disk file not opened yet */
+	struct _queued_write *q;
+	assert(aiostate->flags.opening);
+	q = memPoolAlloc(aufs_qwrite_pool);
+	q->buf = buf;
+	q->size = size;
+	q->offset = offset;
+	q->free_func = free_func;
+	linklistPush(&(aiostate->pending_writes), q);
+	return;
+    }
+#if ASYNC_WRITE
+    if (aiostate->flags.writing) {
+	struct _queued_write *q;
+	debug(78, 3) ("storeAufsWrite: queuing write\n");
+	q = memPoolAlloc(aufs_qwrite_pool);
+	q->buf = buf;
+	q->size = size;
+	q->offset = offset;
+	q->free_func = free_func;
+	linklistPush(&(aiostate->pending_writes), q);
+	return;
+    }
+    aiostate->flags.writing = 1;
+    aioWrite(aiostate->fd, offset, buf, size, storeAufsWriteDone, sio,
+	free_func);
+#else
+    file_write(aiostate->fd, offset, buf, size, storeAufsWriteDone, sio,
+	free_func);
+#endif
+}
+
+/* Unlink */
+void
+storeAufsUnlink(SwapDir * SD, StoreEntry * e)
+{
+    debug(78, 3) ("storeAufsUnlink: dirno %d, fileno %08X\n", SD->index, e->swap_filen);
+    storeAufsDirReplRemove(e);
+    storeAufsDirMapBitReset(SD, e->swap_filen);
+    storeAufsDirUnlinkFile(SD, e->swap_filen);
+}
+
+/*  === STATIC =========================================================== */
+
+static int
+storeAufsKickWriteQueue(storeIOState * sio)
+{
+    squidaiostate_t *aiostate = (squidaiostate_t *) sio->fsstate;
+    struct _queued_write *q = linklistShift(&aiostate->pending_writes);
+    if (NULL == q)
+	return 0;
+    debug(78, 3) ("storeAufsKickWriteQueue: writing queued chunk of %ld bytes\n",
+	(long int) q->size);
+    storeAufsWrite(INDEXSD(sio->swap_dirn), sio, q->buf, q->size, q->offset, q->free_func);
+    memPoolFree(aufs_qwrite_pool, q);
+    return 1;
+}
+
+static int
+storeAufsKickReadQueue(storeIOState * sio)
+{
+    squidaiostate_t *aiostate = (squidaiostate_t *) sio->fsstate;
+    struct _queued_read *q = linklistShift(&(aiostate->pending_reads));
+    if (NULL == q)
+	return 0;
+    debug(78, 3) ("storeAufsKickReadQueue: reading queued request of %ld bytes\n",
+	(long int) q->size);
+    storeAufsRead(INDEXSD(sio->swap_dirn), sio, q->buf, q->size, q->offset, q->callback, q->callback_data);
+    memPoolFree(aufs_qread_pool, q);
+    return 1;
+}
+
+static void
+storeAufsOpenDone(int unused, void *my_data, int fd, int errflag)
+{
+    storeIOState *sio = my_data;
+    squidaiostate_t *aiostate = (squidaiostate_t *) sio->fsstate;
+    debug(78, 3) ("storeAufsOpenDone: FD %d, errflag %d\n", fd, errflag);
+    Opening_FD--;
+    aiostate->flags.opening = 0;
+    if (errflag || fd < 0) {
+	errno = errflag;
+	debug(78, 0) ("storeAufsOpenDone: %s\n", xstrerror());
+	debug(78, 1) ("\t%s\n", storeAufsDirFullPath(INDEXSD(sio->swap_dirn), sio->swap_filen, NULL));
+	storeAufsIOCallback(sio, DISK_ERROR);
+	return;
+    }
+    store_open_disk_fd++;
+    aiostate->fd = fd;
+    commSetCloseOnExec(fd);
+    fd_open(fd, FD_FILE, storeAufsDirFullPath(INDEXSD(sio->swap_dirn), sio->swap_filen, NULL));
+    if (sio->mode == O_WRONLY)
+	storeAufsKickWriteQueue(sio);
+    else if (sio->mode == O_RDONLY)
+	storeAufsKickReadQueue(sio);
+    if (aiostate->flags.close_request)
+	storeAufsIOCallback(sio, errflag);
+    debug(78, 3) ("storeAufsOpenDone: exiting\n");
+}
+
+#if ASYNC_READ
+static void
+storeAufsReadDone(int fd, void *my_data, int len, int errflag)
+#else
+static void
+storeAufsReadDone(int fd, int errflag, size_t len, void *my_data)
+#endif
+{
+    storeIOState *sio = my_data;
+    squidaiostate_t *aiostate = (squidaiostate_t *) sio->fsstate;
+    STRCB *callback = sio->read.callback;
+    void *their_data = sio->read.callback_data;
+    ssize_t rlen;
+    debug(78, 3) ("storeAufsReadDone: dirno %d, fileno %08X, FD %d, len %d\n",
+	sio->swap_dirn, sio->swap_filen, fd, len);
+    aiostate->flags.inreaddone = 1;
+    aiostate->flags.reading = 0;
+    if (errflag) {
+	debug(78, 3) ("storeAufsReadDone: got failure (%d)\n", errflag);
+	rlen = -1;
+    } else {
+	rlen = (ssize_t) len;
+	sio->offset += len;
+    }
+#if ASYNC_READ
+    /* translate errflag from errno to Squid disk error */
+    errno = errflag;
+    if (errflag)
+	errflag = DISK_ERROR;
+    else
+	errflag = DISK_OK;
+#else
+    if (errflag == DISK_EOF)
+	errflag = DISK_OK;	/* EOF is signalled by len == 0, not errors... */
+#endif
+    assert(callback);
+    assert(their_data);
+    sio->read.callback = NULL;
+    sio->read.callback_data = NULL;
+    if (cbdataValid(their_data))
+	callback(their_data, aiostate->read_buf, rlen);
+    cbdataUnlock(their_data);
+    aiostate->flags.inreaddone = 0;
+    if (aiostate->flags.close_request)
+	storeAufsIOCallback(sio, errflag);
+}
+
+#if ASYNC_WRITE
+static void
+storeAufsWriteDone(int fd, void *my_data, int len, int errflag)
+#else
+static void
+storeAufsWriteDone(int fd, int errflag, size_t len, void *my_data)
+#endif
+{
+    static int loop_detect = 0;
+    storeIOState *sio = my_data;
+    squidaiostate_t *aiostate = (squidaiostate_t *) sio->fsstate;
+    debug(78, 3) ("storeAufsWriteDone: dirno %d, fileno %08X, FD %d, len %ld, err=%d\n",
+	sio->swap_dirn, sio->swap_filen, fd, (long int) len, errflag);
+#if ASYNC_WRITE
+    /* Translate from errno to Squid disk error */
+    errno = errflag;
+    if (errflag)
+	errflag = errno == ENOSP ? DISK_NO_SPACE_LEFT : DISK_ERROR;
+    else
+	errflag = DISK_OK;
+#endif
+    assert(++loop_detect < 10);
+    aiostate->flags.writing = 0;
+    if (errflag) {
+	debug(78, 0) ("storeAufsWriteDone: got failure (%d)\n", errflag);
+	storeAufsIOCallback(sio, errflag);
+	loop_detect--;
+	return;
+    }
+    sio->offset += len;
+#if ASYNC_WRITE
+    if (!storeAufsKickWriteQueue(sio))
+	0;
+    else if (aiostate->flags.close_request)
+	storeAufsIOCallback(sio, errflag);
+#else
+    if (!aiostate->flags.write_kicking) {
+	aiostate->flags.write_kicking = 1;
+	while (storeAufsKickWriteQueue(sio))
+	    (void) 0;
+	aiostate->flags.write_kicking = 0;
+	if (aiostate->flags.close_request)
+	    storeAufsIOCallback(sio, errflag);
+    }
+#endif
+    loop_detect--;
+}
+
+static void
+storeAufsIOCallback(storeIOState * sio, int errflag)
+{
+    STIOCB *callback = sio->callback;
+    void *their_data = sio->callback_data;
+    squidaiostate_t *aiostate = (squidaiostate_t *) sio->fsstate;
+    int fd = aiostate->fd;
+    debug(78, 3) ("storeAufsIOCallback: errflag=%d\n", errflag);
+    sio->callback = NULL;
+    sio->callback_data = NULL;
+    debug(78, 3) ("%s:%d\n", __FILE__, __LINE__);
+    if (callback)
+	if (NULL == their_data || cbdataValid(their_data))
+	    callback(their_data, errflag, sio);
+    debug(78, 3) ("%s:%d\n", __FILE__, __LINE__);
+    cbdataUnlock(their_data);
+    aiostate->fd = -1;
+    cbdataFree(sio);
+    if (fd < 0)
+	return;
+    debug(78, 3) ("%s:%d\n", __FILE__, __LINE__);
+    aioClose(fd);
+    fd_close(fd);
+    store_open_disk_fd--;
+    debug(78, 3) ("%s:%d\n", __FILE__, __LINE__);
+}
+
+
+static int
+storeAufsSomethingPending(storeIOState * sio)
+{
+    squidaiostate_t *aiostate = (squidaiostate_t *) sio->fsstate;
+    if (aiostate->flags.reading)
+	return 1;
+    if (aiostate->flags.writing)
+	return 1;
+    if (aiostate->flags.opening)
+	return 1;
+    if (aiostate->flags.inreaddone)
+	return 1;
+    return 0;
+}
+
+
+/*      
+ * Clean up references from the SIO before it gets released.
+ * The actuall SIO is managed by cbdata so we do not need
+ * to bother with that.
+ */
+static void
+storeAufsIOFreeEntry(void *sio)
+{
+    memPoolFree(squidaio_state_pool, ((storeIOState *) sio)->fsstate);
+}
Index: squid/src/fs/ufs/store_dir_ufs.c
===================================================================
RCS file: /cvsroot/squid-sf//squid/src/fs/ufs/store_dir_ufs.c,v
retrieving revision 1.21.6.1
retrieving revision 1.21.6.1.4.1
diff -u -r1.21.6.1 -r1.21.6.1.4.1
--- squid/src/fs/ufs/store_dir_ufs.c	20 Feb 2002 02:49:04 -0000	1.21.6.1
+++ squid/src/fs/ufs/store_dir_ufs.c	14 Mar 2002 20:23:47 -0000	1.21.6.1.4.1
@@ -416,7 +416,12 @@
 		rb->sd->path, rb->counts.scancount);
 	debug(20, 9) ("file_in: fd=%d %08X\n", fd, filn);
 	statCounter.syscalls.disk.reads++;
-	if (read(fd, hdr_buf, SM_PAGE_SIZE) < 0) {
+#ifdef _SQUID_MSWIN_
+	if (fread(hdr_buf, 1, SM_PAGE_SIZE, fd_table[fd].fbuf) == 0 
+	    && ferror(fd_table[fd].fbuf)) {
+#else
+	if (read(fd, hdr_buf, SM_PAGE_SIZE) < 0) {
+#endif
 	    debug(20, 1) ("storeUfsDirRebuildFromDirectory: read(FD %d): %s\n",
 		fd, xstrerror());
 	    file_close(fd);
@@ -860,7 +865,7 @@
     char *new_path = xstrdup(storeUfsDirSwapLogFile(sd, ".new"));
     int fd;
     file_close(ufsinfo->swaplog_fd);
-#if defined (_SQUID_OS2_) || defined (_SQUID_CYGWIN_)
+#if defined (_SQUID_OS2_) || defined (_SQUID_CYGWIN_) || defined (_SQUID_MSWIN_)
     if (unlink(swaplog_path) < 0) {
 	debug(50, 0) ("%s: %s\n", swaplog_path, xstrerror());
 	fatal("storeUfsDirCloseTmpSwapLog: unlink failed");
@@ -915,7 +920,7 @@
 	debug(50, 0) ("%s: %s\n", swaplog_path, xstrerror());
 	fatal("Failed to open swap log for reading");
     }
-#if defined(_SQUID_CYGWIN_)
+#if defined(_SQUID_MSWIN_) || defined(_SQUID_CYGWIN_)
     setmode(fileno(fp), O_BINARY);
 #endif
     memset(&clean_sb, '\0', sizeof(struct stat));
@@ -967,6 +972,9 @@
     state->outbuf = xcalloc(CLEAN_BUF_SZ, 1);
     state->outbuf_offset = 0;
     state->walker = sd->repl->WalkInit(sd->repl);
+#if !(defined(_SQUID_OS2_) || defined (_SQUID_CYGWIN_)) || defined (_SQUID_MSWIN_)
+    unlink(state->new);
+#endif
     unlink(state->cln);
     debug(20, 3) ("storeDirWriteCleanLogs: opened %s, FD %d\n",
 	state->new, state->fd);
@@ -1016,7 +1024,11 @@
     state->outbuf_offset += ss;
     /* buffered write */
     if (state->outbuf_offset + ss > CLEAN_BUF_SZ) {
+#ifdef _SQUID_MSWIN_
+	if (fwrite(state->outbuf, 1, state->outbuf_offset, fd_table[state->fd].fbuf) < state->outbuf_offset) {
+#else
 	if (write(state->fd, state->outbuf, state->outbuf_offset) < 0) {
+#endif
 	    debug(50, 0) ("storeDirWriteCleanLogs: %s: write: %s\n",
 		state->new, xstrerror());
 	    debug(20, 0) ("storeDirWriteCleanLogs: Current swap logfile not replaced.\n");
@@ -1026,14 +1038,17 @@
 	    safe_free(state);
 	    sd->log.clean.state = NULL;
 	    sd->log.clean.write = NULL;
-	}
-	state->outbuf_offset = 0;
+	} else
+	    state->outbuf_offset = 0;
     }
 }
 
 static void
 storeUfsDirWriteCleanDone(SwapDir * sd)
 {
+#ifdef _SQUID_MSWIN_
+    FILE *fbuf;
+#endif
     int fd;
     struct _clean_state *state = sd->log.clean.state;
     if (NULL == state)
@@ -1041,7 +1056,13 @@
     if (state->fd < 0)
 	return;
     state->walker->Done(state->walker);
+#ifdef _SQUID_MSWIN_
+    fbuf=fd_table[state->fd].fbuf;
+    fwrite(state->outbuf, 1, state->outbuf_offset, fbuf);
+    if (ferror(fbuf) || feof(fbuf)){
+#else
     if (write(state->fd, state->outbuf, state->outbuf_offset) < 0) {
+#endif
 	debug(50, 0) ("storeDirWriteCleanLogs: %s: write: %s\n",
 	    state->new, xstrerror());
 	debug(20, 0) ("storeDirWriteCleanLogs: Current swap logfile "
@@ -1060,7 +1081,7 @@
     fd = state->fd;
     /* rename */
     if (state->fd >= 0) {
-#if defined(_SQUID_OS2_) || defined (_SQUID_CYGWIN_)
+#if defined(_SQUID_OS2_) || defined (_SQUID_CYGWIN_) || defined (_SQUID_MSWIN_)
 	file_close(state->fd);
 	state->fd = -1;
 	if (unlink(state->cur) < 0)
@@ -1570,6 +1591,7 @@
 static int
 storeUfsCleanupDoubleCheck(SwapDir * sd, StoreEntry * e)
 {
+#ifndef _SQUID_MSWIN_
     struct stat sb;
     if (stat(storeUfsDirFullPath(sd, e->swap_filen, NULL), &sb) < 0) {
 	debug(20, 0) ("storeUfsCleanupDoubleCheck: MISSING SWAP FILE\n");
@@ -1589,6 +1611,7 @@
 	storeEntryDump(e, 0);
 	return -1;
     }
+#endif
     return 0;
 }
 
