---------------------
PatchSet 5353 
Date: 2002/10/12 12:21:42
Author: rbcollins
Branch: rbcollins_cxxtest
Tag: (none) 
Log:
store modules to .cc

Members: 
	src/Makefile.am:1.29.2.27->1.29.2.28 
	src/ufscommon.c:1.1.4.1->1.1.4.2(DEAD) 
	src/ufscommon.cc:1.1->1.1.2.1 
	src/fs/Makefile.am:1.3.18.2->1.3.18.3 
	src/fs/aufs/aiops.c:1.12.14.1->1.12.14.2(DEAD) 
	src/fs/aufs/aiops.cc:1.1->1.1.2.1 
	src/fs/aufs/async_io.c:1.11.10.2->1.11.10.3(DEAD) 
	src/fs/aufs/async_io.cc:1.1->1.1.2.1 
	src/fs/aufs/store_dir_aufs.c:1.32.10.2->1.32.10.3(DEAD) 
	src/fs/aufs/store_dir_aufs.cc:1.1->1.1.2.1 
	src/fs/aufs/store_io_aufs.c:1.19.10.2->1.19.10.3(DEAD) 
	src/fs/aufs/store_io_aufs.cc:1.1->1.1.2.1 
	src/fs/coss/async_io.c:1.6->1.6.14.1(DEAD) 
	src/fs/coss/async_io.cc:1.1->1.1.2.1 
	src/fs/coss/store_dir_coss.c:1.24.10.1->1.24.10.2(DEAD) 
	src/fs/coss/store_dir_coss.cc:1.1->1.1.2.1 
	src/fs/coss/store_io_coss.c:1.14.10.1->1.14.10.2(DEAD) 
	src/fs/coss/store_io_coss.cc:1.1->1.1.2.1 
	src/fs/diskd/Makefile.am:1.2->1.2.72.1 
	src/fs/diskd/diskd.c:1.6->1.6.92.1(DEAD) 
	src/fs/diskd/diskd.cc:1.1->1.1.2.1 
	src/fs/diskd/store_dir_diskd.c:1.42.10.2->1.42.10.3(DEAD) 
	src/fs/diskd/store_dir_diskd.cc:1.1->1.1.2.1 
	src/fs/diskd/store_io_diskd.c:1.12.10.2->1.12.10.3(DEAD) 
	src/fs/diskd/store_io_diskd.cc:1.1->1.1.2.1 
	src/fs/null/store_null.c:1.3->1.3.16.1(DEAD) 
	src/fs/null/store_null.cc:1.1->1.1.2.1 
	src/fs/ufs/store_dir_ufs.c:1.31.6.2->1.31.6.3(DEAD) 
	src/fs/ufs/store_dir_ufs.cc:1.1->1.1.2.1 
	src/fs/ufs/store_io_ufs.c:1.9.10.2->1.9.10.3(DEAD) 
	src/fs/ufs/store_io_ufs.cc:1.1->1.1.2.1 

Index: squid/src/Makefile.am
===================================================================
RCS file: /cvsroot/squid-sf//squid/src/Makefile.am,v
retrieving revision 1.29.2.27
retrieving revision 1.29.2.28
diff -u -r1.29.2.27 -r1.29.2.28
--- squid/src/Makefile.am	12 Oct 2002 10:35:54 -0000	1.29.2.27
+++ squid/src/Makefile.am	12 Oct 2002 12:21:42 -0000	1.29.2.28
@@ -219,7 +219,7 @@
 	structs.h \
 	tools.cc \
 	typedefs.h \
-	ufscommon.c \
+	ufscommon.cc \
 	ufscommon.h \
 	$(UNLINKDSOURCE) \
 	url.cc \
@@ -233,7 +233,7 @@
 nodist_squid_SOURCES = \
 	repl_modules.cc \
 	auth_modules.cc \
-	store_modules.c \
+	store_modules.cc \
 	cf_parser.h \
 	globals.c \
 	string_arrays.c
@@ -275,7 +275,7 @@
 	string_arrays.c \
 	repl_modules.cc \
 	auth_modules.cc \
-	store_modules.c
+	store_modules.cc
 
 sysconf_DATA = \
 	squid.conf.default \
@@ -367,8 +367,8 @@
 	s%@DEFAULT_HOSTS@%$(DEFAULT_HOSTS)%g;"\
 	< $(srcdir)/cf.data.pre >$@
 
-store_modules.c: store_modules.sh Makefile
-	$(SHELL) $(srcdir)/store_modules.sh $(STORE_MODULES) >store_modules.c
+store_modules.cc: store_modules.sh Makefile
+	$(SHELL) $(srcdir)/store_modules.sh $(STORE_MODULES) >store_modules.cc
 
 repl_modules.cc: repl_modules.sh Makefile
 	$(SHELL) $(srcdir)/repl_modules.sh $(REPL_POLICIES) > repl_modules.cc
@@ -402,7 +402,7 @@
 	fi
 
 DISTCLEANFILES = cf_gen_defines.h cf.data cf_parser.h squid.conf.default \
-	globals.c string_arrays.c repl_modules.cc auth_modules.cc store_modules.c
+	globals.c string_arrays.c repl_modules.cc auth_modules.cc store_modules.cc
 
 ##install-pinger:
 ##	@f=$(PINGER_EXE); \
--- squid/src/ufscommon.c	Wed Feb 14 01:07:42 2007
+++ /dev/null	Wed Feb 14 01:07:22 2007
@@ -1,1734 +0,0 @@
-/*
- * $Id: ufscommon.c,v 1.1.4.1 2002/10/12 10:35:54 rbcollins 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 "ufscommon.h"
-#include "Store.h"
-
-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_dirs = 0;
-static int *dir_index = NULL;
-#if 0
-MemPool *squidaio_state_pool = NULL;
-MemPool *aufs_qread_pool = NULL;
-MemPool *aufs_qwrite_pool = NULL;
-static int asyncufs_initialised = 0;
-#endif
-static int commonUfsFilenoBelongsHere(int fn, int F0, int F1, int F2);
-static char *commonUfsDirSwapSubDir(SwapDir *, int subdirn);
-static int commonUfsDirCreateDirectory(const char *path, int);
-static int commonUfsDirVerifyCacheDirs(SwapDir * sd);
-static int commonUfsDirVerifyDirectory(const char *path);
-static void commonUfsDirCreateSwapSubDirs(SwapDir *);
-static int commonUfsDirMapBitTest(SwapDir * SD, sfileno filn);
-static char *commonUfsDirSwapLogFile(SwapDir *, const char *);
-static EVH commonUfsDirRebuildFromDirectory;
-static EVH commonUfsDirRebuildFromSwapLog;
-static int commonUfsDirGetNextFile(RebuildState *, sfileno *, int *size);
-static StoreEntry *commonUfsDirAddDiskRestore(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_int32_t refcount,
-    u_int16_t flags,
-    int clean);
-static void commonUfsDirRebuild(SwapDir * sd);
-static void commonUfsDirCloseTmpSwapLog(SwapDir * sd);
-static FILE *commonUfsDirOpenTmpSwapLog(SwapDir *, int *, int *);
-#if 0
-static STLOGOPEN commonUfsDirOpenSwapLog;
-static STINIT commonUfsDirInit;
-static STFREE commonUfsDirFree;
-static STLOGCLEANSTART commonUfsDirWriteCleanStart;
-static STLOGCLEANNEXTENTRY commonUfsDirCleanLogNextEntry;
-#endif
-static STLOGCLEANWRITE commonUfsDirWriteCleanEntry;
-#if 0
-static STLOGCLEANDONE commonUfsDirWriteCleanDone;
-static STLOGCLOSE commonUfsDirCloseSwapLog;
-static STLOGWRITE commonUfsDirSwapLog;
-static STNEWFS commonUfsDirNewfs;
-static STCHECKOBJ commonUfsDirCheckObj;
-#endif
-static QS rev_int_sort;
-static void commonUfsDirMapBitSet(SwapDir * SD, sfileno filn);
-static EVH commonUfsDirCleanEvent;
-static int commonUfsDirClean(int swap_index);
-static int commonUfsDirIs(SwapDir * sd);
-#if 0
-static int commonUfsCleanupDoubleCheck(SwapDir *, StoreEntry *);
-#endif
-static void commonUfsDirInitBitmap(SwapDir *);
-static int commonUfsDirValidFileno(SwapDir * SD, sfileno filn, int flag);
-static int commonUfsDirMapBitTest(SwapDir * SD, sfileno filn);
-void commonUfsDirMapBitReset(SwapDir * SD, sfileno filn);
-
-#if 0
-
-/* The MAIN externally visible function */
-STSETUP storeFsSetup_aufs;
-
-/*
- * 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!
- */
-
-#endif
-int
-commonUfsDirMapBitTest(SwapDir * SD, sfileno filn)
-{
-    squidufsinfo_t *ioinfo;
-    ioinfo = (squidufsinfo_t *) SD->fsdata;
-    return file_map_bit_test(ioinfo->map, filn);
-}
-
-void
-commonUfsDirMapBitSet(SwapDir * SD, sfileno filn)
-{
-    squidufsinfo_t *ioinfo;
-    ioinfo = (squidufsinfo_t *) SD->fsdata;
-    file_map_bit_set(ioinfo->map, filn);
-}
-
-void
-commonUfsDirMapBitReset(SwapDir * SD, sfileno filn)
-{
-    squidufsinfo_t *ioinfo;
-    ioinfo = (squidufsinfo_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(ioinfo->map, filn))
-	file_map_bit_reset(ioinfo->map, filn);
-}
-
-int
-commonUfsDirMapBitAllocate(SwapDir * SD)
-{
-    squidufsinfo_t *ioinfo = (squidufsinfo_t *) SD->fsdata;
-    int fn;
-    fn = file_map_allocate(ioinfo->map, ioinfo->suggest);
-    file_map_bit_set(ioinfo->map, fn);
-    ioinfo->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.
- */
-void
-commonUfsDirInitBitmap(SwapDir * sd)
-{
-    squidufsinfo_t *ioinfo = (squidufsinfo_t *) sd->fsdata;
-
-    if (ioinfo->map == NULL) {
-	/* First time */
-	ioinfo->map = file_map_create();
-    } else if (ioinfo->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 */
-}
-
-char *
-commonUfsDirSwapSubDir(SwapDir * sd, int subdirn)
-{
-    squidufsinfo_t *ioinfo = (squidufsinfo_t *) sd->fsdata;
-
-    LOCAL_ARRAY(char, fullfilename, SQUID_MAXPATHLEN);
-    assert(0 <= subdirn && subdirn < ioinfo->l1);
-    snprintf(fullfilename, SQUID_MAXPATHLEN, "%s/%02X", sd->path, subdirn);
-    return fullfilename;
-}
-
-int
-commonUfsDirCreateDirectory(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(47, should_exist ? 3 : 1) ("%s exists\n", path);
-	} else {
-	    fatalf("Swap directory %s is not a directory.", path);
-	}
-#ifdef _SQUID_MSWIN_
-    } else if (0 == mkdir(path)) {
-#else
-    } else if (0 == mkdir(path, 0755)) {
-#endif
-	debug(47, should_exist ? 1 : 3) ("%s created\n", path);
-	created = 1;
-    } else {
-	fatalf("Failed to make swap directory %s: %s",
-	    path, xstrerror());
-    }
-    return created;
-}
-
-int
-commonUfsDirVerifyDirectory(const char *path)
-{
-    struct stat sb;
-    if (stat(path, &sb) < 0) {
-	debug(47, 0) ("%s: %s\n", path, xstrerror());
-	return -1;
-    }
-    if (S_ISDIR(sb.st_mode) == 0) {
-	debug(47, 0) ("%s is not a directory\n", path);
-	return -1;
-    }
-    return 0;
-}
-
-/*
- * This function is called by commonUfsDirInit().  If this returns < 0,
- * then Squid exits, complains about swap directories not
- * existing, and instructs the admin to run 'squid -z'
- */
-int
-commonUfsDirVerifyCacheDirs(SwapDir * sd)
-{
-    squidufsinfo_t *ioinfo = (squidufsinfo_t *) sd->fsdata;
-    int j;
-    const char *path = sd->path;
-
-    if (commonUfsDirVerifyDirectory(path) < 0)
-	return -1;
-    for (j = 0; j < ioinfo->l1; j++) {
-	path = commonUfsDirSwapSubDir(sd, j);
-	if (commonUfsDirVerifyDirectory(path) < 0)
-	    return -1;
-    }
-    return 0;
-}
-
-void
-commonUfsDirCreateSwapSubDirs(SwapDir * sd)
-{
-    squidufsinfo_t *ioinfo = (squidufsinfo_t *) sd->fsdata;
-    int i, k;
-    int should_exist;
-    LOCAL_ARRAY(char, name, MAXPATHLEN);
-    for (i = 0; i < ioinfo->l1; i++) {
-	snprintf(name, MAXPATHLEN, "%s/%02X", sd->path, i);
-	if (commonUfsDirCreateDirectory(name, 0))
-	    should_exist = 0;
-	else
-	    should_exist = 1;
-	debug(47, 1) ("Making directories in %s\n", name);
-	for (k = 0; k < ioinfo->l2; k++) {
-	    snprintf(name, MAXPATHLEN, "%s/%02X/%02X", sd->path, i, k);
-	    commonUfsDirCreateDirectory(name, should_exist);
-	}
-    }
-}
-
-char *
-commonUfsDirSwapLogFile(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;
-}
-
-void
-commonUfsDirOpenSwapLog(SwapDir * sd)
-{
-    squidufsinfo_t *ioinfo = (squidufsinfo_t *) sd->fsdata;
-    char *path;
-    int fd;
-    path = commonUfsDirSwapLogFile(sd, NULL);
-    fd = file_open(path, O_WRONLY | O_CREAT | O_BINARY);
-    if (fd < 0) {
-	debug(50, 1) ("%s: %s\n", path, xstrerror());
-	fatal("commonUfsDirOpenSwapLog: Failed to open swap log.");
-    }
-    debug(50, 3) ("Cache Dir #%d log opened on FD %d\n", sd->index, fd);
-    ioinfo->swaplog_fd = fd;
-    if (0 == n_dirs)
-	assert(NULL == dir_index);
-    ++n_dirs;
-    assert(n_dirs <= Config.cacheSwap.n_configured);
-}
-
-void
-commonUfsDirCloseSwapLog(SwapDir * sd)
-{
-    squidufsinfo_t *ioinfo = (squidufsinfo_t *) sd->fsdata;
-    if (ioinfo->swaplog_fd < 0)	/* not open */
-	return;
-    file_close(ioinfo->swaplog_fd);
-    debug(47, 3) ("Cache Dir #%d log closed on FD %d\n",
-	sd->index, ioinfo->swaplog_fd);
-    ioinfo->swaplog_fd = -1;
-    n_dirs--;
-    assert(n_dirs >= 0);
-    if (0 == n_dirs)
-	safe_free(dir_index);
-}
-
-void
-commonUfsDirInit(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.";
-    commonUfsDirInitBitmap(sd);
-    if (commonUfsDirVerifyCacheDirs(sd) < 0)
-	fatal(errmsg);
-    commonUfsDirOpenSwapLog(sd);
-    commonUfsDirRebuild(sd);
-    if (!started_clean_event) {
-	eventAdd("storeDirClean", commonUfsDirCleanEvent, NULL, 15.0, 1);
-	started_clean_event = 1;
-    }
-    (void) storeDirGetBlkSize(sd->path, &sd->fs.blksize);
-}
-
-void
-commonUfsDirRebuildFromDirectory(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(47, 3) ("commonUfsDirRebuildFromDirectory: DIR #%d\n", rb->sd->index);
-    for (count = 0; count < rb->speed; count++) {
-	assert(fd == -1);
-	fd = commonUfsDirGetNextFile(rb, &filn, &size);
-	if (fd == -2) {
-	    debug(47, 1) ("Done scanning %s swaplog (%d entries)\n",
-		rb->sd->path, rb->n_read);
-	    store_dirs_rebuilding--;
-	    commonUfsDirCloseTmpSwapLog(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(47, 1) ("commonUfsDirRebuildFromDirectory: fstat(FD %d): %s\n",
-		fd, xstrerror());
-	    file_close(fd);
-	    store_open_disk_fd--;
-	    fd = -1;
-	    continue;
-	}
-	if ((++rb->counts.scancount & 0xFFFF) == 0)
-	    debug(47, 3) ("  %s %7d files opened so far.\n",
-		rb->sd->path, rb->counts.scancount);
-	debug(47, 9) ("file_in: fd=%d %08X\n", fd, filn);
-	statCounter.syscalls.disk.reads++;
-	if (FD_READ_METHOD(fd, hdr_buf, SM_PAGE_SIZE) < 0) {
-	    debug(47, 1) ("commonUfsDirRebuildFromDirectory: 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(47, 1) ("commonUfsDirRebuildFromDirectory: failed to get meta data\n");
-	    /* XXX shouldn't this be a call to commonUfsUnlink ? */
-	    commonUfsDirUnlinkFile(SD, filn);
-	    continue;
-	}
-	debug(47, 3) ("commonUfsDirRebuildFromDirectory: 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(47, 1) ("commonUfsDirRebuildFromDirectory: NULL key\n");
-	    commonUfsDirUnlinkFile(SD, filn);
-	    continue;
-	}
-	tmpe.hash.key = key;
-	/* check sizes */
-	if (tmpe.swap_file_sz == 0) {
-	    tmpe.swap_file_sz = (size_t) sb.st_size;
-	} else if (tmpe.swap_file_sz == sb.st_size - swap_hdr_len) {
-	    tmpe.swap_file_sz = (size_t) sb.st_size;
-	} else if (tmpe.swap_file_sz != sb.st_size) {
-	    debug(47, 1) ("commonUfsDirRebuildFromDirectory: SIZE MISMATCH %ld!=%ld\n",
-		(long int) tmpe.swap_file_sz, (long int) sb.st_size);
-	    commonUfsDirUnlinkFile(SD, filn);
-	    continue;
-	}
-	if (EBIT_TEST(tmpe.flags, KEY_PRIVATE)) {
-	    commonUfsDirUnlinkFile(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 = commonUfsDirAddDiskRestore(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", commonUfsDirRebuildFromDirectory, rb, 0.0, 1);
-}
-
-void
-commonUfsDirRebuildFromSwapLog(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(47, 1) ("Done reading %s swaplog (%d entries)\n",
-		rb->sd->path, rb->n_read);
-	    fclose(rb->log);
-	    rb->log = NULL;
-	    store_dirs_rebuilding--;
-	    commonUfsDirCloseTmpSwapLog(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(47, 3) ("commonUfsDirRebuildFromSwapLog: %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) {
-		    commonUfsDirReplRemove(e);
-		    commonUfsDirMapBitReset(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(47, 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 (!commonUfsDirValidFileno(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 = commonUfsDirMapBitTest(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;
-		commonUfsDirUnrefObj(SD, e);
-	    } else {
-		debug_trap("commonUfsDirRebuildFromSwapLog: bad condition");
-		debug(47, 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(47, 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) {
-		commonUfsDirReplRemove(e);
-		/* Make sure we don't actually unlink the file */
-		commonUfsDirMapBitReset(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 = commonUfsDirAddDiskRestore(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", commonUfsDirRebuildFromSwapLog, rb, 0.0, 1);
-}
-
-int
-commonUfsDirGetNextFile(RebuildState * rb, sfileno * filn_p, int *size)
-{
-    SwapDir *SD = rb->sd;
-    squidufsinfo_t *ioinfo = (squidufsinfo_t *) SD->fsdata;
-    int fd = -1;
-    int used = 0;
-    int dirs_opened = 0;
-    debug(47, 3) ("commonUfsDirGetNextFile: 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 (dirs_opened)
-		return -1;
-	    rb->td = opendir(rb->fullpath);
-	    dirs_opened++;
-	    if (rb->td == NULL) {
-		debug(47, 1) ("commonUfsDirGetNextFile: 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(47, 1) ("commonUfsDirGetNextFile: directory does not exist!.\n");
-		debug(47, 3) ("commonUfsDirGetNextFile: 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(47, 3) ("commonUfsDirGetNextFile: invalid %s\n",
-		    rb->entry->d_name);
-		continue;
-	    }
-	    if (!commonUfsFilenoBelongsHere(rb->fn, rb->sd->index, rb->curlvl1, rb->curlvl2)) {
-		debug(47, 3) ("commonUfsDirGetNextFile: %08X does not belong in %d/%d/%d\n",
-		    rb->fn, rb->sd->index, rb->curlvl1, rb->curlvl2);
-		continue;
-	    }
-	    used = commonUfsDirMapBitTest(SD, rb->fn);
-	    if (used) {
-		debug(47, 3) ("commonUfsDirGetNextFile: Locked, continuing with next.\n");
-		continue;
-	    }
-	    snprintf(rb->fullfilename, SQUID_MAXPATHLEN, "%s/%s",
-		rb->fullpath, rb->entry->d_name);
-	    debug(47, 3) ("commonUfsDirGetNextFile: Opening %s\n", rb->fullfilename);
-	    fd = file_open(rb->fullfilename, O_RDONLY | O_BINARY);
-	    if (fd < 0)
-		debug(47, 1) ("commonUfsDirGetNextFile: %s: %s\n", rb->fullfilename, xstrerror());
-	    else
-		store_open_disk_fd++;
-	    continue;
-	}
-	if (rb->td != NULL)
-	    closedir(rb->td);
-	rb->td = NULL;
-	rb->in_dir = 0;
-	if (++rb->curlvl2 < ioinfo->l2)
-	    continue;
-	rb->curlvl2 = 0;
-	if (++rb->curlvl1 < ioinfo->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. */
-StoreEntry *
-commonUfsDirAddDiskRestore(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_int32_t refcount,
-    u_int16_t flags,
-    int clean)
-{
-    StoreEntry *e = NULL;
-    debug(47, 5) ("commonUfsAddDiskRestore: %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);
-    commonUfsDirMapBitSet(SD, e->swap_filen);
-    storeHashInsert(e, key);	/* do it after we clear KEY_PRIVATE */
-    commonUfsDirReplAdd(SD, e);
-    return e;
-}
-
-CBDATA_TYPE(RebuildState);
-
-void
-commonUfsDirRebuild(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 commonUfsDirRebuildFromSwapLog(), otherwise we'll
-     * use commonUfsDirRebuildFromDirectory() to open up each file
-     * and suck in the meta data.
-     */
-    fp = commonUfsDirOpenTmpSwapLog(sd, &clean, &zero);
-    if (fp == NULL || zero) {
-	if (fp != NULL)
-	    fclose(fp);
-	func = commonUfsDirRebuildFromDirectory;
-    } else {
-	func = commonUfsDirRebuildFromSwapLog;
-	rb->log = fp;
-	rb->flags.clean = (unsigned int) clean;
-    }
-    if (!clean)
-	rb->flags.need_to_validate = 1;
-    debug(47, 1) ("Rebuilding storage in %s (%s)\n",
-	sd->path, clean ? "CLEAN" : "DIRTY");
-    store_dirs_rebuilding++;
-    eventAdd("storeRebuild", func, rb, 0.0, 1);
-}
-
-void
-commonUfsDirCloseTmpSwapLog(SwapDir * sd)
-{
-    squidufsinfo_t *ioinfo = (squidufsinfo_t *) sd->fsdata;
-    char *swaplog_path = xstrdup(commonUfsDirSwapLogFile(sd, NULL));
-    char *new_path = xstrdup(commonUfsDirSwapLogFile(sd, ".new"));
-    int fd;
-    file_close(ioinfo->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("commonUfsDirCloseTmpSwapLog: unlink failed");
-    }
-#endif
-    if (xrename(new_path, swaplog_path) < 0) {
-	fatal("commonUfsDirCloseTmpSwapLog: 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("commonUfsDirCloseTmpSwapLog: Failed to open swap log.");
-    }
-    safe_free(swaplog_path);
-    safe_free(new_path);
-    ioinfo->swaplog_fd = fd;
-    debug(47, 3) ("Cache Dir #%d log opened on FD %d\n", sd->index, fd);
-}
-
-FILE *
-commonUfsDirOpenTmpSwapLog(SwapDir * sd, int *clean_flag, int *zero_flag)
-{
-    squidufsinfo_t *ioinfo = (squidufsinfo_t *) sd->fsdata;
-    char *swaplog_path = xstrdup(commonUfsDirSwapLogFile(sd, NULL));
-    char *clean_path = xstrdup(commonUfsDirSwapLogFile(sd, ".last-clean"));
-    char *new_path = xstrdup(commonUfsDirSwapLogFile(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 (ioinfo->swaplog_fd >= 0)
-	file_close(ioinfo->swaplog_fd);
-    /* open a write-only FD for the new log */
-    fd = file_open(new_path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY);
-    if (fd < 0) {
-	debug(50, 1) ("%s: %s\n", new_path, xstrerror());
-	fatal("storeDirOpenTmpSwapLog: Failed to open swap log.");
-    }
-    ioinfo->swaplog_fd = fd;
-    /* open a read-only stream of the old log */
-    fp = fopen(swaplog_path, "rb");
-    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.
- */
-int
-commonUfsDirWriteCleanStart(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(commonUfsDirSwapLogFile(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(commonUfsDirSwapLogFile(sd, NULL));
-    state->cln = xstrdup(commonUfsDirSwapLogFile(sd, ".last-clean"));
-    state->outbuf = xcalloc(CLEAN_BUF_SZ, 1);
-    state->outbuf_offset = 0;
-    state->walker = sd->repl->WalkInit(sd->repl);
-    unlink(state->cln);
-    debug(47, 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 = commonUfsDirWriteCleanEntry;
-    sd->log.clean.state = state;
-    return 0;
-}
-
-/*
- * Get the next entry that is a candidate for clean log writing
- */
-const StoreEntry *
-commonUfsDirCleanLogNextEntry(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.
- */
-void
-commonUfsDirWriteCleanEntry(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 (FD_WRITE_METHOD(state->fd, state->outbuf, state->outbuf_offset) < 0) {
-	    debug(50, 0) ("storeDirWriteCleanLogs: %s: write: %s\n",
-		state->new, xstrerror());
-	    debug(50, 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;
-	    return;
-	}
-	state->outbuf_offset = 0;
-    }
-}
-
-void
-commonUfsDirWriteCleanDone(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 (FD_WRITE_METHOD(state->fd, state->outbuf, state->outbuf_offset) < 0) {
-	debug(50, 0) ("storeDirWriteCleanLogs: %s: write: %s\n",
-	    state->new, xstrerror());
-	debug(50, 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.
-     */
-    commonUfsDirCloseSwapLog(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;
-}
-
-void
-storeSwapLogDataFree(void *s)
-{
-    memFree(s, MEM_SWAP_LOG_DATA);
-}
-
-void
-commonUfsDirSwapLog(const SwapDir * sd, const StoreEntry * e, int op)
-{
-    squidufsinfo_t *ioinfo = (squidufsinfo_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(ioinfo->swaplog_fd,
-	-1,
-	s,
-	sizeof(storeSwapLogData),
-	NULL,
-	NULL,
-	(FREE *) storeSwapLogDataFree);
-}
-
-void
-commonUfsDirNewfs(SwapDir * sd)
-{
-    debug(47, 3) ("Creating swap space in %s\n", sd->path);
-    commonUfsDirCreateDirectory(sd->path, 0);
-    commonUfsDirCreateSwapSubDirs(sd);
-}
-
-static int
-rev_int_sort(const void *A, const void *B)
-{
-    const int *i1 = A;
-    const int *i2 = B;
-    return *i2 - *i1;
-}
-
-int
-commonUfsDirClean(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;
-    squidufsinfo_t *ioinfo;
-    N0 = n_dirs;
-    D0 = dir_index[swap_index % N0];
-    SD = &Config.cacheSwap.swapDirs[D0];
-    ioinfo = (squidufsinfo_t *) SD->fsdata;
-    N1 = ioinfo->l1;
-    D1 = (swap_index / N0) % N1;
-    N2 = ioinfo->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);
-#ifdef _SQUID_MSWIN_
-	    if (mkdir(p1) == 0)
-#else
-	    if (mkdir(p1, 0777) == 0)
-#endif
-		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 (commonUfsDirValidFileno(SD, fn, 1))
-	    if (commonUfsDirMapBitTest(SD, fn))
-		if (commonUfsFilenoBelongsHere(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;
-}
-
-void
-commonUfsDirCleanEvent(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_dirs);
-    if (NULL == dir_index) {
-	SwapDir *sd;
-	squidufsinfo_t *ioinfo;
-	/*
-	 * Initialize the little array that translates AUFS cache_dir
-	 * number into the Config.cacheSwap.swapDirs array index.
-	 */
-	dir_index = xcalloc(n_dirs, sizeof(*dir_index));
-	for (i = 0, n = 0; i < Config.cacheSwap.n_configured; i++) {
-	    sd = &Config.cacheSwap.swapDirs[i];
-	    if (!commonUfsDirIs(sd))
-		continue;
-	    dir_index[n++] = i;
-	    ioinfo = (squidufsinfo_t *) sd->fsdata;
-	    j += (ioinfo->l1 * ioinfo->l2);
-	}
-	assert(n == n_dirs);
-	/*
-	 * Start the commonUfsDirClean() 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 = commonUfsDirClean(swap_index);
-	swap_index++;
-    }
-    eventAdd("storeDirClean", commonUfsDirCleanEvent, NULL,
-	15.0 * exp(-0.25 * n), 1);
-}
-
-int
-commonUfsDirIs(SwapDir * sd)
-{
-    if (strncmp(sd->type, "aufs", 4) == 0)
-	return 1;
-    if (strncmp(sd->type, "diskd", 5) == 0)
-	return 1;
-    if (strncmp(sd->type, "ufs", 3) == 0)
-	return 1;
-    return 0;
-}
-
-/*
- * Does swapfile number 'fn' belong in cachedir #F0,
- * level1 dir #F1, level2 dir #F2?
- */
-int
-commonUfsFilenoBelongsHere(int fn, int F0, int F1, int F2)
-{
-    int D1, D2;
-    int L1, L2;
-    int filn = fn;
-    squidufsinfo_t *ioinfo;
-    assert(F0 < Config.cacheSwap.n_configured);
-    ioinfo = (squidufsinfo_t *) Config.cacheSwap.swapDirs[F0].fsdata;
-    L1 = ioinfo->l1;
-    L2 = ioinfo->l2;
-    D1 = ((filn / L2) / L2) % L1;
-    if (F1 != D1)
-	return 0;
-    D2 = (filn / L2) % L2;
-    if (F2 != D2)
-	return 0;
-    return 1;
-}
-
-int
-commonUfsDirValidFileno(SwapDir * SD, sfileno filn, int flag)
-{
-    squidufsinfo_t *ioinfo = (squidufsinfo_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 > ioinfo->map->max_n_files)
-	    return 0;
-    return 1;
-}
-
-void
-commonUfsDirMaintain(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(47, 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(47, (removed ? 2 : 3)) ("commonUfsDirMaintain: %s removed %d/%d f=%.03f max_scan=%d\n",
-	SD->path, removed, max_remove, f, max_scan);
-}
-
-#if 0
-/*
- * commonUfsDirCheckObj
- *
- * 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
-commonUfsDirCheckObj(SwapDir * SD, const StoreEntry * e)
-{
-    int loadav;
-    int ql;
-
-#if OLD_UNUSED_CODE
-    if (commonUfsDirExpiredReferenceAge(SD) < 300) {
-	debug(47, 3) ("commonUfsDirCheckObj: NO: LRU Age = %d\n",
-	    commonUfsDirExpiredReferenceAge(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(47, 9) ("commonUfsDirCheckObj: load=%d\n", loadav);
-    return loadav;
-}
-#endif
-/*
- * commonUfsDirRefObj
- *
- * This routine is called whenever an object is referenced, so we can
- * maintain replacement information within the storage fs.
- */
-void
-commonUfsDirRefObj(SwapDir * SD, StoreEntry * e)
-{
-    debug(47, 3) ("commonUfsDirRefObj: referencing %p %d/%d\n", e, e->swap_dirn,
-	e->swap_filen);
-    if (SD->repl->Referenced)
-	SD->repl->Referenced(SD->repl, e, &e->repl);
-}
-
-/*
- * commonUfsDirUnrefObj
- * This routine is called whenever the last reference to an object is
- * removed, to maintain replacement information within the storage fs.
- */
-void
-commonUfsDirUnrefObj(SwapDir * SD, StoreEntry * e)
-{
-    debug(47, 3) ("commonUfsDirUnrefObj: referencing %p %d/%d\n", e, e->swap_dirn,
-	e->swap_filen);
-    if (SD->repl->Dereferenced)
-	SD->repl->Dereferenced(SD->repl, e, &e->repl);
-}
-
-/*
- * commonUfsDirUnlinkFile
- *
- * This routine unlinks a file and pulls it out of the bitmap.
- * It used to be in commonUfsUnlink(), however an interface change
- * forced this bit of code here. Eeek.
- */
-void
-commonUfsDirUnlinkFile(SwapDir * SD, sfileno f)
-{
-    squidufsinfo_t *ioinfo = SD->fsdata;
-    debug(79, 3) ("commonUfsDirUnlinkFile: unlinking fileno %08X\n", f);
-    /* commonUfsDirMapBitReset(SD, f); */
-    assert(ioinfo->io.storeDirUnlinkFile);
-    ioinfo->io.storeDirUnlinkFile(commonUfsDirFullPath(SD, f, NULL));
-}
-
-
-/*
- * Add and remove the given StoreEntry from the replacement policy in
- * use.
- */
-
-void
-commonUfsDirReplAdd(SwapDir * SD, StoreEntry * e)
-{
-    debug(47, 4) ("commonUfsDirReplAdd: added node %p to dir %d\n", e,
-	SD->index);
-    SD->repl->Add(SD->repl, e, &e->repl);
-}
-
-
-void
-commonUfsDirReplRemove(StoreEntry * e)
-{
-    SwapDir *SD;
-    if (e->swap_dirn < 0)
-	return;
-    SD = INDEXSD(e->swap_dirn);
-    debug(47, 4) ("commonUfsDirReplRemove: 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
-commonUfsDirStats(SwapDir * SD, StoreEntry * sentry)
-{
-    squidufsinfo_t *ioinfo = 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", ioinfo->l1);
-    storeAppendPrintf(sentry, "Second level subdirectories: %d\n", ioinfo->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",
-	ioinfo->map->n_files_in_map, ioinfo->map->max_n_files,
-	percent(ioinfo->map->n_files_in_map, ioinfo->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");
-}
-
-#if 0
-static struct cache_dir_option options[] =
-{
-#if NOT_YET_DONE
-    {"L1", commonUfsDirParseL1, commonUfsDirDumpL1},
-    {"L2", commonUfsDirParseL2, commonUfsDirDumpL2},
-#endif
-    {NULL, NULL}
-};
-
-/*
- * commonUfsDirReconfigure
- *
- * This routine is called when the given swapdir needs reconfiguring 
- */
-static void
-commonUfsDirReconfigure(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("commonUfsDirReconfigure: invalid size value");
-    i = GetInteger();
-    l1 = i;
-    if (l1 <= 0)
-	fatal("commonUfsDirReconfigure: invalid level 1 directories value");
-    i = GetInteger();
-    l2 = i;
-    if (l2 <= 0)
-	fatal("commonUfsDirReconfigure: 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;
-}
-
-#endif
-
-void
-commonUfsDirDump(StoreEntry * entry, SwapDir * s)
-{
-    squidufsinfo_t *ioinfo = (squidufsinfo_t *) s->fsdata;
-    storeAppendPrintf(entry, " %d %d %d",
-	s->max_size >> 10,
-	ioinfo->l1,
-	ioinfo->l2);
-}
-
-/*
- * Only "free" the filesystem specific stuff here
- */
-void
-commonUfsDirFree(SwapDir * s)
-{
-    squidufsinfo_t *ioinfo = (squidufsinfo_t *) s->fsdata;
-    if (ioinfo->swaplog_fd > -1) {
-	file_close(ioinfo->swaplog_fd);
-	ioinfo->swaplog_fd = -1;
-    }
-    filemapFreeMemory(ioinfo->map);
-    xfree(ioinfo);
-    s->fsdata = NULL;		/* Will aid debugging... */
-}
-
-
-char *
-commonUfsDirFullPath(SwapDir * SD, sfileno filn, char *fullpath)
-{
-    LOCAL_ARRAY(char, fullfilename, SQUID_MAXPATHLEN);
-    squidufsinfo_t *ioinfo = (squidufsinfo_t *) SD->fsdata;
-    int L1 = ioinfo->l1;
-    int L2 = ioinfo->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;
-}
-
-/*
- * commonUfsCleanupDoubleCheck
- *
- * This is called by storeCleanup() if -S was given on the command line.
- */
-int
-commonUfsCleanupDoubleCheck(SwapDir * sd, StoreEntry * e)
-{
-    struct stat sb;
-    if (stat(commonUfsDirFullPath(sd, e->swap_filen, NULL), &sb) < 0) {
-	debug(47, 0) ("commonUfsCleanupDoubleCheck: MISSING SWAP FILE\n");
-	debug(47, 0) ("commonUfsCleanupDoubleCheck: FILENO %08X\n", e->swap_filen);
-	debug(47, 0) ("commonUfsCleanupDoubleCheck: PATH %s\n",
-	    commonUfsDirFullPath(sd, e->swap_filen, NULL));
-	storeEntryDump(e, 0);
-	return -1;
-    }
-    if (e->swap_file_sz != sb.st_size) {
-	debug(47, 0) ("commonUfsCleanupDoubleCheck: SIZE MISMATCH\n");
-	debug(47, 0) ("commonUfsCleanupDoubleCheck: FILENO %08X\n", e->swap_filen);
-	debug(47, 0) ("commonUfsCleanupDoubleCheck: PATH %s\n",
-	    commonUfsDirFullPath(sd, e->swap_filen, NULL));
-	debug(47, 0) ("commonUfsCleanupDoubleCheck: ENTRY SIZE: %ld, FILE SIZE: %ld\n",
-	    (long int) e->swap_file_sz, (long int) sb.st_size);
-	storeEntryDump(e, 0);
-	return -1;
-    }
-    return 0;
-}
-
-#if 0
-/*
- * commonUfsDirParse *
- * Called when a *new* fs is being setup.
- */
-static void
-commonUfsDirParse(SwapDir * sd, int index, char *path)
-{
-    int i;
-    int size;
-    int l1;
-    int l2;
-    squidufsinfo_t *ioinfo;
-
-    i = GetInteger();
-    size = i << 10;		/* Mbytes to kbytes */
-    if (size <= 0)
-	fatal("commonUfsDirParse: invalid size value");
-    i = GetInteger();
-    l1 = i;
-    if (l1 <= 0)
-	fatal("commonUfsDirParse: invalid level 1 directories value");
-    i = GetInteger();
-    l2 = i;
-    if (l2 <= 0)
-	fatal("commonUfsDirParse: invalid level 2 directories value");
-
-    ioinfo = xmalloc(sizeof(squidufsinfo_t));
-    if (ioinfo == NULL)
-	fatal("commonUfsDirParse: couldn't xmalloc() squidufsinfo_t!\n");
-
-    sd->index = index;
-    sd->path = xstrdup(path);
-    sd->max_size = size;
-    sd->fsdata = ioinfo;
-    ioinfo->l1 = l1;
-    ioinfo->l2 = l2;
-    ioinfo->swaplog_fd = -1;
-    ioinfo->map = NULL;		/* Debugging purposes */
-    ioinfo->suggest = 0;
-    sd->init = commonUfsDirInit;
-    sd->newfs = commonUfsDirNewfs;
-    sd->dump = commonUfsDirDump;
-    sd->freefs = commonUfsDirFree;
-    sd->dblcheck = commonUfsCleanupDoubleCheck;
-    sd->statfs = commonUfsDirStats;
-    sd->maintainfs = commonUfsDirMaintain;
-    sd->checkobj = commonUfsDirCheckObj;
-    sd->refobj = commonUfsDirRefObj;
-    sd->unrefobj = commonUfsDirUnrefObj;
-    sd->callback = aioCheckCallbacks;
-    sd->sync = aioSync;
-    sd->obj.create = commonUfsCreate;
-    sd->obj.open = commonUfsOpen;
-    sd->obj.close = commonUfsClose;
-    sd->obj.read = commonUfsRead;
-    sd->obj.write = commonUfsWrite;
-    sd->obj.unlink = commonUfsUnlink;
-    sd->log.open = commonUfsDirOpenSwapLog;
-    sd->log.close = commonUfsDirCloseSwapLog;
-    sd->log.write = commonUfsDirSwapLog;
-    sd->log.clean.start = commonUfsDirWriteCleanStart;
-    sd->log.clean.nextentry = commonUfsDirCleanLogNextEntry;
-    sd->log.clean.done = commonUfsDirWriteCleanDone;
-
-    parse_cachedir_options(sd, options, 0);
-
-    /* Initialise replacement policy stuff */
-    sd->repl = createRemovalPolicy(Config.replPolicy);
-}
-
-/*
- * Initial setup / end destruction
- */
-static void
-commonUfsDirDone(void)
-{
-    aioDone();
-    memPoolDestroy(&squidaio_state_pool);
-    memPoolDestroy(&aufs_qread_pool);
-    memPoolDestroy(&aufs_qwrite_pool);
-    asyncufs_initialised = 0;
-}
-
-void
-storeFsSetup_aufs(storefs_entry_t * storefs)
-{
-    assert(!asyncufs_initialised);
-    storefs->parsefunc = commonUfsDirParse;
-    storefs->reconfigurefunc = commonUfsDirReconfigure;
-    storefs->donefunc = commonUfsDirDone;
-    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();
-}
-#endif
--- /dev/null	Wed Feb 14 01:07:22 2007
+++ squid/src/ufscommon.cc	Wed Feb 14 01:07:42 2007
@@ -0,0 +1,1632 @@
+/*
+ * $Id: ufscommon.cc,v 1.1.2.1 2002/10/12 12:21:42 rbcollins 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 "ufscommon.h"
+#include "Store.h"
+
+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_dirs = 0;
+static int *dir_index = NULL;
+#if 0
+MemPool *squidaio_state_pool = NULL;
+MemPool *aufs_qread_pool = NULL;
+MemPool *aufs_qwrite_pool = NULL;
+static int asyncufs_initialised = 0;
+#endif
+static int commonUfsFilenoBelongsHere(int fn, int F0, int F1, int F2);
+static char *commonUfsDirSwapSubDir(SwapDir *, int subdirn);
+static int commonUfsDirCreateDirectory(const char *path, int);
+static int commonUfsDirVerifyCacheDirs(SwapDir * sd);
+static int commonUfsDirVerifyDirectory(const char *path);
+static void commonUfsDirCreateSwapSubDirs(SwapDir *);
+static int commonUfsDirMapBitTest(SwapDir * SD, sfileno filn);
+static char *commonUfsDirSwapLogFile(SwapDir *, const char *);
+static EVH commonUfsDirRebuildFromDirectory;
+static EVH commonUfsDirRebuildFromSwapLog;
+static int commonUfsDirGetNextFile(RebuildState *, sfileno *, int *size);
+static StoreEntry *commonUfsDirAddDiskRestore(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_int32_t refcount,
+    u_int16_t flags,
+    int clean);
+static void commonUfsDirRebuild(SwapDir * sd);
+static void commonUfsDirCloseTmpSwapLog(SwapDir * sd);
+static FILE *commonUfsDirOpenTmpSwapLog(SwapDir *, int *, int *);
+#if 0
+static STLOGOPEN commonUfsDirOpenSwapLog;
+static STINIT commonUfsDirInit;
+static STFREE commonUfsDirFree;
+static STLOGCLEANSTART commonUfsDirWriteCleanStart;
+static STLOGCLEANNEXTENTRY commonUfsDirCleanLogNextEntry;
+#endif
+static STLOGCLEANWRITE commonUfsDirWriteCleanEntry;
+#if 0
+static STLOGCLEANDONE commonUfsDirWriteCleanDone;
+static STLOGCLOSE commonUfsDirCloseSwapLog;
+static STLOGWRITE commonUfsDirSwapLog;
+static STNEWFS commonUfsDirNewfs;
+static STCHECKOBJ commonUfsDirCheckObj;
+#endif
+static QS rev_int_sort;
+static void commonUfsDirMapBitSet(SwapDir * SD, sfileno filn);
+static EVH commonUfsDirCleanEvent;
+static int commonUfsDirClean(int swap_index);
+static int commonUfsDirIs(SwapDir * sd);
+#if 0
+static int commonUfsCleanupDoubleCheck(SwapDir *, StoreEntry *);
+#endif
+static void commonUfsDirInitBitmap(SwapDir *);
+static int commonUfsDirValidFileno(SwapDir * SD, sfileno filn, int flag);
+static int commonUfsDirMapBitTest(SwapDir * SD, sfileno filn);
+void commonUfsDirMapBitReset(SwapDir * SD, sfileno filn);
+
+#if 0
+
+/* The MAIN externally visible function */
+STSETUP storeFsSetup_aufs;
+
+/*
+ * 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!
+ */
+
+#endif
+int
+commonUfsDirMapBitTest(SwapDir * SD, sfileno filn)
+{
+    squidufsinfo_t *ioinfo;
+    ioinfo = (squidufsinfo_t *) SD->fsdata;
+    return file_map_bit_test(ioinfo->map, filn);
+}
+
+void
+commonUfsDirMapBitSet(SwapDir * SD, sfileno filn)
+{
+    squidufsinfo_t *ioinfo;
+    ioinfo = (squidufsinfo_t *) SD->fsdata;
+    file_map_bit_set(ioinfo->map, filn);
+}
+
+void
+commonUfsDirMapBitReset(SwapDir * SD, sfileno filn)
+{
+    squidufsinfo_t *ioinfo;
+    ioinfo = (squidufsinfo_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(ioinfo->map, filn))
+	file_map_bit_reset(ioinfo->map, filn);
+}
+
+int
+commonUfsDirMapBitAllocate(SwapDir * SD)
+{
+    squidufsinfo_t *ioinfo = (squidufsinfo_t *) SD->fsdata;
+    int fn;
+    fn = file_map_allocate(ioinfo->map, ioinfo->suggest);
+    file_map_bit_set(ioinfo->map, fn);
+    ioinfo->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.
+ */
+void
+commonUfsDirInitBitmap(SwapDir * sd)
+{
+    squidufsinfo_t *ioinfo = (squidufsinfo_t *) sd->fsdata;
+
+    if (ioinfo->map == NULL) {
+	/* First time */
+	ioinfo->map = file_map_create();
+    } else if (ioinfo->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 */
+}
+
+char *
+commonUfsDirSwapSubDir(SwapDir * sd, int subdirn)
+{
+    squidufsinfo_t *ioinfo = (squidufsinfo_t *) sd->fsdata;
+
+    LOCAL_ARRAY(char, fullfilename, SQUID_MAXPATHLEN);
+    assert(0 <= subdirn && subdirn < ioinfo->l1);
+    snprintf(fullfilename, SQUID_MAXPATHLEN, "%s/%02X", sd->path, subdirn);
+    return fullfilename;
+}
+
+int
+commonUfsDirCreateDirectory(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(47, should_exist ? 3 : 1) ("%s exists\n", path);
+	} else {
+	    fatalf("Swap directory %s is not a directory.", path);
+	}
+#ifdef _SQUID_MSWIN_
+    } else if (0 == mkdir(path)) {
+#else
+    } else if (0 == mkdir(path, 0755)) {
+#endif
+	debug(47, should_exist ? 1 : 3) ("%s created\n", path);
+	created = 1;
+    } else {
+	fatalf("Failed to make swap directory %s: %s",
+	    path, xstrerror());
+    }
+    return created;
+}
+
+int
+commonUfsDirVerifyDirectory(const char *path)
+{
+    struct stat sb;
+    if (stat(path, &sb) < 0) {
+	debug(47, 0) ("%s: %s\n", path, xstrerror());
+	return -1;
+    }
+    if (S_ISDIR(sb.st_mode) == 0) {
+	debug(47, 0) ("%s is not a directory\n", path);
+	return -1;
+    }
+    return 0;
+}
+
+/*
+ * This function is called by commonUfsDirInit().  If this returns < 0,
+ * then Squid exits, complains about swap directories not
+ * existing, and instructs the admin to run 'squid -z'
+ */
+int
+commonUfsDirVerifyCacheDirs(SwapDir * sd)
+{
+    squidufsinfo_t *ioinfo = (squidufsinfo_t *) sd->fsdata;
+    int j;
+    const char *path = sd->path;
+
+    if (commonUfsDirVerifyDirectory(path) < 0)
+	return -1;
+    for (j = 0; j < ioinfo->l1; j++) {
+	path = commonUfsDirSwapSubDir(sd, j);
+	if (commonUfsDirVerifyDirectory(path) < 0)
+	    return -1;
+    }
+    return 0;
+}
+
+void
+commonUfsDirCreateSwapSubDirs(SwapDir * sd)
+{
+    squidufsinfo_t *ioinfo = (squidufsinfo_t *) sd->fsdata;
+    int i, k;
+    int should_exist;
+    LOCAL_ARRAY(char, name, MAXPATHLEN);
+    for (i = 0; i < ioinfo->l1; i++) {
+	snprintf(name, MAXPATHLEN, "%s/%02X", sd->path, i);
+	if (commonUfsDirCreateDirectory(name, 0))
+	    should_exist = 0;
+	else
+	    should_exist = 1;
+	debug(47, 1) ("Making directories in %s\n", name);
+	for (k = 0; k < ioinfo->l2; k++) {
+	    snprintf(name, MAXPATHLEN, "%s/%02X/%02X", sd->path, i, k);
+	    commonUfsDirCreateDirectory(name, should_exist);
+	}
+    }
+}
+
+char *
+commonUfsDirSwapLogFile(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;
+}
+
+void
+commonUfsDirOpenSwapLog(SwapDir * sd)
+{
+    squidufsinfo_t *ioinfo = (squidufsinfo_t *) sd->fsdata;
+    char *path;
+    int fd;
+    path = commonUfsDirSwapLogFile(sd, NULL);
+    fd = file_open(path, O_WRONLY | O_CREAT | O_BINARY);
+    if (fd < 0) {
+	debug(50, 1) ("%s: %s\n", path, xstrerror());
+	fatal("commonUfsDirOpenSwapLog: Failed to open swap log.");
+    }
+    debug(50, 3) ("Cache Dir #%d log opened on FD %d\n", sd->index, fd);
+    ioinfo->swaplog_fd = fd;
+    if (0 == n_dirs)
+	assert(NULL == dir_index);
+    ++n_dirs;
+    assert(n_dirs <= Config.cacheSwap.n_configured);
+}
+
+void
+commonUfsDirCloseSwapLog(SwapDir * sd)
+{
+    squidufsinfo_t *ioinfo = (squidufsinfo_t *) sd->fsdata;
+    if (ioinfo->swaplog_fd < 0)	/* not open */
+	return;
+    file_close(ioinfo->swaplog_fd);
+    debug(47, 3) ("Cache Dir #%d log closed on FD %d\n",
+	sd->index, ioinfo->swaplog_fd);
+    ioinfo->swaplog_fd = -1;
+    n_dirs--;
+    assert(n_dirs >= 0);
+    if (0 == n_dirs)
+	safe_free(dir_index);
+}
+
+void
+commonUfsDirInit(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.";
+    commonUfsDirInitBitmap(sd);
+    if (commonUfsDirVerifyCacheDirs(sd) < 0)
+	fatal(errmsg);
+    commonUfsDirOpenSwapLog(sd);
+    commonUfsDirRebuild(sd);
+    if (!started_clean_event) {
+	eventAdd("storeDirClean", commonUfsDirCleanEvent, NULL, 15.0, 1);
+	started_clean_event = 1;
+    }
+    (void) storeDirGetBlkSize(sd->path, &sd->fs.blksize);
+}
+
+void
+commonUfsDirRebuildFromDirectory(void *data)
+{
+    RebuildState *rb = (RebuildState *)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(47, 3) ("commonUfsDirRebuildFromDirectory: DIR #%d\n", rb->sd->index);
+    for (count = 0; count < rb->speed; count++) {
+	assert(fd == -1);
+	fd = commonUfsDirGetNextFile(rb, &filn, &size);
+	if (fd == -2) {
+	    debug(47, 1) ("Done scanning %s swaplog (%d entries)\n",
+		rb->sd->path, rb->n_read);
+	    store_dirs_rebuilding--;
+	    commonUfsDirCloseTmpSwapLog(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(47, 1) ("commonUfsDirRebuildFromDirectory: fstat(FD %d): %s\n",
+		fd, xstrerror());
+	    file_close(fd);
+	    store_open_disk_fd--;
+	    fd = -1;
+	    continue;
+	}
+	if ((++rb->counts.scancount & 0xFFFF) == 0)
+	    debug(47, 3) ("  %s %7d files opened so far.\n",
+		rb->sd->path, rb->counts.scancount);
+	debug(47, 9) ("file_in: fd=%d %08X\n", fd, filn);
+	statCounter.syscalls.disk.reads++;
+	if (FD_READ_METHOD(fd, hdr_buf, SM_PAGE_SIZE) < 0) {
+	    debug(47, 1) ("commonUfsDirRebuildFromDirectory: 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(47, 1) ("commonUfsDirRebuildFromDirectory: failed to get meta data\n");
+	    /* XXX shouldn't this be a call to commonUfsUnlink ? */
+	    commonUfsDirUnlinkFile(SD, filn);
+	    continue;
+	}
+	debug(47, 3) ("commonUfsDirRebuildFromDirectory: 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(47, 1) ("commonUfsDirRebuildFromDirectory: NULL key\n");
+	    commonUfsDirUnlinkFile(SD, filn);
+	    continue;
+	}
+	tmpe.hash.key = key;
+	/* check sizes */
+	if (tmpe.swap_file_sz == 0) {
+	    tmpe.swap_file_sz = (size_t) sb.st_size;
+	} else if (tmpe.swap_file_sz == (size_t)(sb.st_size - swap_hdr_len)) {
+	    tmpe.swap_file_sz = (size_t) sb.st_size;
+	} else if (tmpe.swap_file_sz != (size_t)sb.st_size) {
+	    debug(47, 1) ("commonUfsDirRebuildFromDirectory: SIZE MISMATCH %ld!=%ld\n",
+		(long int) tmpe.swap_file_sz, (long int) sb.st_size);
+	    commonUfsDirUnlinkFile(SD, filn);
+	    continue;
+	}
+	if (EBIT_TEST(tmpe.flags, KEY_PRIVATE)) {
+	    commonUfsDirUnlinkFile(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 = commonUfsDirAddDiskRestore(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", commonUfsDirRebuildFromDirectory, rb, 0.0, 1);
+}
+
+void
+commonUfsDirRebuildFromSwapLog(void *data)
+{
+    RebuildState *rb = (RebuildState *)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(47, 1) ("Done reading %s swaplog (%d entries)\n",
+		rb->sd->path, rb->n_read);
+	    fclose(rb->log);
+	    rb->log = NULL;
+	    store_dirs_rebuilding--;
+	    commonUfsDirCloseTmpSwapLog(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(47, 3) ("commonUfsDirRebuildFromSwapLog: %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) {
+		    commonUfsDirReplRemove(e);
+		    commonUfsDirMapBitReset(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(47, 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 (!commonUfsDirValidFileno(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 = commonUfsDirMapBitTest(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;
+		commonUfsDirUnrefObj(SD, e);
+	    } else {
+		debug_trap("commonUfsDirRebuildFromSwapLog: bad condition");
+		debug(47, 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(47, 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) {
+		commonUfsDirReplRemove(e);
+		/* Make sure we don't actually unlink the file */
+		commonUfsDirMapBitReset(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 = commonUfsDirAddDiskRestore(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", commonUfsDirRebuildFromSwapLog, rb, 0.0, 1);
+}
+
+int
+commonUfsDirGetNextFile(RebuildState * rb, sfileno * filn_p, int *size)
+{
+    SwapDir *SD = rb->sd;
+    squidufsinfo_t *ioinfo = (squidufsinfo_t *) SD->fsdata;
+    int fd = -1;
+    int used = 0;
+    int dirs_opened = 0;
+    debug(47, 3) ("commonUfsDirGetNextFile: 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 (dirs_opened)
+		return -1;
+	    rb->td = opendir(rb->fullpath);
+	    dirs_opened++;
+	    if (rb->td == NULL) {
+		debug(47, 1) ("commonUfsDirGetNextFile: 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(47, 1) ("commonUfsDirGetNextFile: directory does not exist!.\n");
+		debug(47, 3) ("commonUfsDirGetNextFile: 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(47, 3) ("commonUfsDirGetNextFile: invalid %s\n",
+		    rb->entry->d_name);
+		continue;
+	    }
+	    if (!commonUfsFilenoBelongsHere(rb->fn, rb->sd->index, rb->curlvl1, rb->curlvl2)) {
+		debug(47, 3) ("commonUfsDirGetNextFile: %08X does not belong in %d/%d/%d\n",
+		    rb->fn, rb->sd->index, rb->curlvl1, rb->curlvl2);
+		continue;
+	    }
+	    used = commonUfsDirMapBitTest(SD, rb->fn);
+	    if (used) {
+		debug(47, 3) ("commonUfsDirGetNextFile: Locked, continuing with next.\n");
+		continue;
+	    }
+	    snprintf(rb->fullfilename, SQUID_MAXPATHLEN, "%s/%s",
+		rb->fullpath, rb->entry->d_name);
+	    debug(47, 3) ("commonUfsDirGetNextFile: Opening %s\n", rb->fullfilename);
+	    fd = file_open(rb->fullfilename, O_RDONLY | O_BINARY);
+	    if (fd < 0)
+		debug(47, 1) ("commonUfsDirGetNextFile: %s: %s\n", rb->fullfilename, xstrerror());
+	    else
+		store_open_disk_fd++;
+	    continue;
+	}
+	if (rb->td != NULL)
+	    closedir(rb->td);
+	rb->td = NULL;
+	rb->in_dir = 0;
+	if (++rb->curlvl2 < ioinfo->l2)
+	    continue;
+	rb->curlvl2 = 0;
+	if (++rb->curlvl1 < ioinfo->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. */
+StoreEntry *
+commonUfsDirAddDiskRestore(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_int32_t refcount,
+    u_int16_t flags,
+    int clean)
+{
+    StoreEntry *e = NULL;
+    debug(47, 5) ("commonUfsAddDiskRestore: %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);
+    commonUfsDirMapBitSet(SD, e->swap_filen);
+    storeHashInsert(e, key);	/* do it after we clear KEY_PRIVATE */
+    commonUfsDirReplAdd(SD, e);
+    return e;
+}
+
+CBDATA_TYPE(RebuildState);
+
+void
+commonUfsDirRebuild(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 commonUfsDirRebuildFromSwapLog(), otherwise we'll
+     * use commonUfsDirRebuildFromDirectory() to open up each file
+     * and suck in the meta data.
+     */
+    fp = commonUfsDirOpenTmpSwapLog(sd, &clean, &zero);
+    if (fp == NULL || zero) {
+	if (fp != NULL)
+	    fclose(fp);
+	func = commonUfsDirRebuildFromDirectory;
+    } else {
+	func = commonUfsDirRebuildFromSwapLog;
+	rb->log = fp;
+	rb->flags.clean = (unsigned int) clean;
+    }
+    if (!clean)
+	rb->flags.need_to_validate = 1;
+    debug(47, 1) ("Rebuilding storage in %s (%s)\n",
+	sd->path, clean ? "CLEAN" : "DIRTY");
+    store_dirs_rebuilding++;
+    eventAdd("storeRebuild", func, rb, 0.0, 1);
+}
+
+void
+commonUfsDirCloseTmpSwapLog(SwapDir * sd)
+{
+    squidufsinfo_t *ioinfo = (squidufsinfo_t *) sd->fsdata;
+    char *swaplog_path = xstrdup(commonUfsDirSwapLogFile(sd, NULL));
+    char *new_path = xstrdup(commonUfsDirSwapLogFile(sd, ".new"));
+    int fd;
+    file_close(ioinfo->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("commonUfsDirCloseTmpSwapLog: unlink failed");
+    }
+#endif
+    if (xrename(new_path, swaplog_path) < 0) {
+	fatal("commonUfsDirCloseTmpSwapLog: 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("commonUfsDirCloseTmpSwapLog: Failed to open swap log.");
+    }
+    safe_free(swaplog_path);
+    safe_free(new_path);
+    ioinfo->swaplog_fd = fd;
+    debug(47, 3) ("Cache Dir #%d log opened on FD %d\n", sd->index, fd);
+}
+
+FILE *
+commonUfsDirOpenTmpSwapLog(SwapDir * sd, int *clean_flag, int *zero_flag)
+{
+    squidufsinfo_t *ioinfo = (squidufsinfo_t *) sd->fsdata;
+    char *swaplog_path = xstrdup(commonUfsDirSwapLogFile(sd, NULL));
+    char *clean_path = xstrdup(commonUfsDirSwapLogFile(sd, ".last-clean"));
+    char *new_path = xstrdup(commonUfsDirSwapLogFile(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 (ioinfo->swaplog_fd >= 0)
+	file_close(ioinfo->swaplog_fd);
+    /* open a write-only FD for the new log */
+    fd = file_open(new_path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY);
+    if (fd < 0) {
+	debug(50, 1) ("%s: %s\n", new_path, xstrerror());
+	fatal("storeDirOpenTmpSwapLog: Failed to open swap log.");
+    }
+    ioinfo->swaplog_fd = fd;
+    /* open a read-only stream of the old log */
+    fp = fopen(swaplog_path, "rb");
+    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 *newLog;
+    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.
+ */
+int
+commonUfsDirWriteCleanStart(SwapDir * sd)
+{
+    struct _clean_state *state = (struct _clean_state *)xcalloc(1, sizeof(*state));
+#if HAVE_FCHMOD
+    struct stat sb;
+#endif
+    sd->log.clean.write = NULL;
+    sd->log.clean.state = NULL;
+    state->newLog = xstrdup(commonUfsDirSwapLogFile(sd, ".clean"));
+    state->fd = file_open(state->newLog, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY);
+    if (state->fd < 0) {
+	xfree(state->newLog);
+	xfree(state);
+	return -1;
+    }
+    state->cur = xstrdup(commonUfsDirSwapLogFile(sd, NULL));
+    state->cln = xstrdup(commonUfsDirSwapLogFile(sd, ".last-clean"));
+    state->outbuf = (char *)xcalloc(CLEAN_BUF_SZ, 1);
+    state->outbuf_offset = 0;
+    state->walker = sd->repl->WalkInit(sd->repl);
+    unlink(state->cln);
+    debug(47, 3) ("storeDirWriteCleanLogs: opened %s, FD %d\n",
+	state->newLog, state->fd);
+#if HAVE_FCHMOD
+    if (stat(state->cur, &sb) == 0)
+	fchmod(state->fd, sb.st_mode);
+#endif
+    sd->log.clean.write = commonUfsDirWriteCleanEntry;
+    sd->log.clean.state = state;
+    return 0;
+}
+
+/*
+ * Get the next entry that is a candidate for clean log writing
+ */
+const StoreEntry *
+commonUfsDirCleanLogNextEntry(SwapDir * sd)
+{
+    const StoreEntry *entry = NULL;
+    struct _clean_state *state = (struct _clean_state *)sd->log.clean.state;
+    if (state->walker)
+	entry = state->walker->Next(state->walker);
+    return entry;
+}
+
+/*
+ * "write" an entry to the clean log file.
+ */
+void
+commonUfsDirWriteCleanEntry(SwapDir * sd, const StoreEntry * e)
+{
+    storeSwapLogData s;
+    static size_t ss = sizeof(storeSwapLogData);
+    struct _clean_state *state = (struct _clean_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 (FD_WRITE_METHOD(state->fd, state->outbuf, state->outbuf_offset) < 0) {
+	    debug(50, 0) ("storeDirWriteCleanLogs: %s: write: %s\n",
+		state->newLog, xstrerror());
+	    debug(50, 0) ("storeDirWriteCleanLogs: Current swap logfile not replaced.\n");
+	    file_close(state->fd);
+	    state->fd = -1;
+	    unlink(state->newLog);
+	    safe_free(state);
+	    sd->log.clean.state = NULL;
+	    sd->log.clean.write = NULL;
+	    return;
+	}
+	state->outbuf_offset = 0;
+    }
+}
+
+void
+commonUfsDirWriteCleanDone(SwapDir * sd)
+{
+    int fd;
+    struct _clean_state *state = (struct _clean_state *)sd->log.clean.state;
+    if (NULL == state)
+	return;
+    if (state->fd < 0)
+	return;
+    state->walker->Done(state->walker);
+    if (FD_WRITE_METHOD(state->fd, state->outbuf, state->outbuf_offset) < 0) {
+	debug(50, 0) ("storeDirWriteCleanLogs: %s: write: %s\n",
+	    state->newLog, xstrerror());
+	debug(50, 0) ("storeDirWriteCleanLogs: Current swap logfile "
+	    "not replaced.\n");
+	file_close(state->fd);
+	state->fd = -1;
+	unlink(state->newLog);
+    }
+    safe_free(state->outbuf);
+    /*
+     * You can't rename open files on Microsoft "operating systems"
+     * so we have to close before renaming.
+     */
+    commonUfsDirCloseSwapLog(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->newLog, 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->newLog);
+    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;
+}
+
+void
+storeSwapLogDataFree(void *s)
+{
+    memFree(s, MEM_SWAP_LOG_DATA);
+}
+
+void
+commonUfsDirSwapLog(const SwapDir * sd, const StoreEntry * e, int op)
+{
+    squidufsinfo_t *ioinfo = (squidufsinfo_t *) sd->fsdata;
+    storeSwapLogData *s = (storeSwapLogData *)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(ioinfo->swaplog_fd,
+	-1,
+	s,
+	sizeof(storeSwapLogData),
+	NULL,
+	NULL,
+	(FREE *) storeSwapLogDataFree);
+}
+
+void
+commonUfsDirNewfs(SwapDir * sd)
+{
+    debug(47, 3) ("Creating swap space in %s\n", sd->path);
+    commonUfsDirCreateDirectory(sd->path, 0);
+    commonUfsDirCreateSwapSubDirs(sd);
+}
+
+static int
+rev_int_sort(const void *A, const void *B)
+{
+    const int *i1 = (const int *)A;
+    const int *i2 = (const int *)B;
+    return *i2 - *i1;
+}
+
+int
+commonUfsDirClean(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;
+    squidufsinfo_t *ioinfo;
+    N0 = n_dirs;
+    D0 = dir_index[swap_index % N0];
+    SD = &Config.cacheSwap.swapDirs[D0];
+    ioinfo = (squidufsinfo_t *) SD->fsdata;
+    N1 = ioinfo->l1;
+    D1 = (swap_index / N0) % N1;
+    N2 = ioinfo->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);
+#ifdef _SQUID_MSWIN_
+	    if (mkdir(p1) == 0)
+#else
+	    if (mkdir(p1, 0777) == 0)
+#endif
+		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 (commonUfsDirValidFileno(SD, fn, 1))
+	    if (commonUfsDirMapBitTest(SD, fn))
+		if (commonUfsFilenoBelongsHere(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;
+}
+
+void
+commonUfsDirCleanEvent(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_dirs);
+    if (NULL == dir_index) {
+	SwapDir *sd;
+	squidufsinfo_t *ioinfo;
+	/*
+	 * Initialize the little array that translates AUFS cache_dir
+	 * number into the Config.cacheSwap.swapDirs array index.
+	 */
+	dir_index = (int *)xcalloc(n_dirs, sizeof(*dir_index));
+	for (i = 0, n = 0; i < Config.cacheSwap.n_configured; i++) {
+	    sd = &Config.cacheSwap.swapDirs[i];
+	    if (!commonUfsDirIs(sd))
+		continue;
+	    dir_index[n++] = i;
+	    ioinfo = (squidufsinfo_t *) sd->fsdata;
+	    j += (ioinfo->l1 * ioinfo->l2);
+	}
+	assert(n == n_dirs);
+	/*
+	 * Start the commonUfsDirClean() 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 = commonUfsDirClean(swap_index);
+	swap_index++;
+    }
+    eventAdd("storeDirClean", commonUfsDirCleanEvent, NULL,
+	15.0 * exp(-0.25 * n), 1);
+}
+
+int
+commonUfsDirIs(SwapDir * sd)
+{
+    if (strncmp(sd->type, "aufs", 4) == 0)
+	return 1;
+    if (strncmp(sd->type, "diskd", 5) == 0)
+	return 1;
+    if (strncmp(sd->type, "ufs", 3) == 0)
+	return 1;
+    return 0;
+}
+
+/*
+ * Does swapfile number 'fn' belong in cachedir #F0,
+ * level1 dir #F1, level2 dir #F2?
+ */
+int
+commonUfsFilenoBelongsHere(int fn, int F0, int F1, int F2)
+{
+    int D1, D2;
+    int L1, L2;
+    int filn = fn;
+    squidufsinfo_t *ioinfo;
+    assert(F0 < Config.cacheSwap.n_configured);
+    ioinfo = (squidufsinfo_t *) Config.cacheSwap.swapDirs[F0].fsdata;
+    L1 = ioinfo->l1;
+    L2 = ioinfo->l2;
+    D1 = ((filn / L2) / L2) % L1;
+    if (F1 != D1)
+	return 0;
+    D2 = (filn / L2) % L2;
+    if (F2 != D2)
+	return 0;
+    return 1;
+}
+
+int
+commonUfsDirValidFileno(SwapDir * SD, sfileno filn, int flag)
+{
+    squidufsinfo_t *ioinfo = (squidufsinfo_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 > ioinfo->map->max_n_files)
+	    return 0;
+    return 1;
+}
+
+void
+commonUfsDirMaintain(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(47, 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(47, (removed ? 2 : 3)) ("commonUfsDirMaintain: %s removed %d/%d f=%.03f max_scan=%d\n",
+	SD->path, removed, max_remove, f, max_scan);
+}
+
+#if 0
+/*
+ * commonUfsDirCheckObj
+ *
+ * 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
+commonUfsDirCheckObj(SwapDir * SD, const StoreEntry * e)
+{
+    int loadav;
+    int ql;
+
+#if OLD_UNUSED_CODE
+    if (commonUfsDirExpiredReferenceAge(SD) < 300) {
+	debug(47, 3) ("commonUfsDirCheckObj: NO: LRU Age = %d\n",
+	    commonUfsDirExpiredReferenceAge(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(47, 9) ("commonUfsDirCheckObj: load=%d\n", loadav);
+    return loadav;
+}
+#endif
+/*
+ * commonUfsDirRefObj
+ *
+ * This routine is called whenever an object is referenced, so we can
+ * maintain replacement information within the storage fs.
+ */
+void
+commonUfsDirRefObj(SwapDir * SD, StoreEntry * e)
+{
+    debug(47, 3) ("commonUfsDirRefObj: referencing %p %d/%d\n", e, e->swap_dirn,
+	e->swap_filen);
+    if (SD->repl->Referenced)
+	SD->repl->Referenced(SD->repl, e, &e->repl);
+}
+
+/*
+ * commonUfsDirUnrefObj
+ * This routine is called whenever the last reference to an object is
+ * removed, to maintain replacement information within the storage fs.
+ */
+void
+commonUfsDirUnrefObj(SwapDir * SD, StoreEntry * e)
+{
+    debug(47, 3) ("commonUfsDirUnrefObj: referencing %p %d/%d\n", e, e->swap_dirn,
+	e->swap_filen);
+    if (SD->repl->Dereferenced)
+	SD->repl->Dereferenced(SD->repl, e, &e->repl);
+}
+
+/*
+ * commonUfsDirUnlinkFile
+ *
+ * This routine unlinks a file and pulls it out of the bitmap.
+ * It used to be in commonUfsUnlink(), however an interface change
+ * forced this bit of code here. Eeek.
+ */
+void
+commonUfsDirUnlinkFile(SwapDir * SD, sfileno f)
+{
+    squidufsinfo_t *ioinfo = (squidufsinfo_t *)SD->fsdata;
+    debug(79, 3) ("commonUfsDirUnlinkFile: unlinking fileno %08X\n", f);
+    /* commonUfsDirMapBitReset(SD, f); */
+    assert(ioinfo->io.storeDirUnlinkFile);
+    ioinfo->io.storeDirUnlinkFile(commonUfsDirFullPath(SD, f, NULL));
+}
+
+
+/*
+ * Add and remove the given StoreEntry from the replacement policy in
+ * use.
+ */
+
+void
+commonUfsDirReplAdd(SwapDir * SD, StoreEntry * e)
+{
+    debug(47, 4) ("commonUfsDirReplAdd: added node %p to dir %d\n", e,
+	SD->index);
+    SD->repl->Add(SD->repl, e, &e->repl);
+}
+
+
+void
+commonUfsDirReplRemove(StoreEntry * e)
+{
+    SwapDir *SD;
+    if (e->swap_dirn < 0)
+	return;
+    SD = INDEXSD(e->swap_dirn);
+    debug(47, 4) ("commonUfsDirReplRemove: 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
+commonUfsDirStats(SwapDir * SD, StoreEntry * sentry)
+{
+    squidufsinfo_t *ioinfo = (squidufsinfo_t *)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", ioinfo->l1);
+    storeAppendPrintf(sentry, "Second level subdirectories: %d\n", ioinfo->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",
+	ioinfo->map->n_files_in_map, ioinfo->map->max_n_files,
+	percent(ioinfo->map->n_files_in_map, ioinfo->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");
+}
+
+#if 0
+static struct cache_dir_option options[] =
+{
+#if NOT_YET_DONE
+    {"L1", commonUfsDirParseL1, commonUfsDirDumpL1},
+    {"L2", commonUfsDirParseL2, commonUfsDirDumpL2},
+#endif
+    {NULL, NULL}
+};
+
+/*
+ * commonUfsDirReconfigure
+ *
+ * This routine is called when the given swapdir needs reconfiguring 
+ */
+static void
+commonUfsDirReconfigure(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("commonUfsDirReconfigure: invalid size value");
+    i = GetInteger();
+    l1 = i;
+    if (l1 <= 0)
+	fatal("commonUfsDirReconfigure: invalid level 1 directories value");
+    i = GetInteger();
+    l2 = i;
+    if (l2 <= 0)
+	fatal("commonUfsDirReconfigure: 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;
+}
+
+#endif
+
+void
+commonUfsDirDump(StoreEntry * entry, SwapDir * s)
+{
+    squidufsinfo_t *ioinfo = (squidufsinfo_t *) s->fsdata;
+    storeAppendPrintf(entry, " %d %d %d",
+	s->max_size >> 10,
+	ioinfo->l1,
+	ioinfo->l2);
+}
+
+/*
+ * Only "free" the filesystem specific stuff here
+ */
+void
+commonUfsDirFree(SwapDir * s)
+{
+    squidufsinfo_t *ioinfo = (squidufsinfo_t *) s->fsdata;
+    if (ioinfo->swaplog_fd > -1) {
+	file_close(ioinfo->swaplog_fd);
+	ioinfo->swaplog_fd = -1;
+    }
+    filemapFreeMemory(ioinfo->map);
+    xfree(ioinfo);
+    s->fsdata = NULL;		/* Will aid debugging... */
+}
+
+
+char *
+commonUfsDirFullPath(SwapDir * SD, sfileno filn, char *fullpath)
+{
+    LOCAL_ARRAY(char, fullfilename, SQUID_MAXPATHLEN);
+    squidufsinfo_t *ioinfo = (squidufsinfo_t *) SD->fsdata;
+    int L1 = ioinfo->l1;
+    int L2 = ioinfo->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;
+}
+
+/*
+ * commonUfsCleanupDoubleCheck
+ *
+ * This is called by storeCleanup() if -S was given on the command line.
+ */
+int
+commonUfsCleanupDoubleCheck(SwapDir * sd, StoreEntry * e)
+{
+    struct stat sb;
+    if (stat(commonUfsDirFullPath(sd, e->swap_filen, NULL), &sb) < 0) {
+	debug(47, 0) ("commonUfsCleanupDoubleCheck: MISSING SWAP FILE\n");
+	debug(47, 0) ("commonUfsCleanupDoubleCheck: FILENO %08X\n", e->swap_filen);
+	debug(47, 0) ("commonUfsCleanupDoubleCheck: PATH %s\n",
+	    commonUfsDirFullPath(sd, e->swap_filen, NULL));
+	storeEntryDump(e, 0);
+	return -1;
+    }
+    if ((off_t)e->swap_file_sz != sb.st_size) {
+	debug(47, 0) ("commonUfsCleanupDoubleCheck: SIZE MISMATCH\n");
+	debug(47, 0) ("commonUfsCleanupDoubleCheck: FILENO %08X\n", e->swap_filen);
+	debug(47, 0) ("commonUfsCleanupDoubleCheck: PATH %s\n",
+	    commonUfsDirFullPath(sd, e->swap_filen, NULL));
+	debug(47, 0) ("commonUfsCleanupDoubleCheck: ENTRY SIZE: %ld, FILE SIZE: %ld\n",
+	    (long int) e->swap_file_sz, (long int) sb.st_size);
+	storeEntryDump(e, 0);
+	return -1;
+    }
+    return 0;
+}
Index: squid/src/fs/Makefile.am
===================================================================
RCS file: /cvsroot/squid-sf//squid/src/fs/Makefile.am,v
retrieving revision 1.3.18.2
retrieving revision 1.3.18.3
diff -u -r1.3.18.2 -r1.3.18.3
--- squid/src/fs/Makefile.am	4 Oct 2002 21:22:10 -0000	1.3.18.2
+++ squid/src/fs/Makefile.am	12 Oct 2002 12:21:42 -0000	1.3.18.3
@@ -13,14 +13,14 @@
 EXTRA_LIBRARIES = libaufs.a libcoss.a libdiskd.a libnull.a libufs.a
 noinst_LIBRARIES = @STORE_LIBS@
 
-libaufs_a_SOURCES = aufs/aiops.c aufs/async_io.c aufs/store_asyncufs.h \
-	aufs/store_dir_aufs.c aufs/store_io_aufs.c
-libcoss_a_SOURCES = coss/store_coss.h   coss/store_io_coss.c coss/store_dir_coss.c \
-	coss/async_io.c coss/async_io.h
-libdiskd_a_SOURCES = diskd/diskd.c diskd/store_dir_diskd.c diskd/store_diskd.h \
-	diskd/store_io_diskd.c
-libnull_a_SOURCES = null/store_null.c
-libufs_a_SOURCES = ufs/store_dir_ufs.c ufs/store_io_ufs.c ufs/store_ufs.h
+libaufs_a_SOURCES = aufs/aiops.cc aufs/async_io.cc aufs/store_asyncufs.h \
+	aufs/store_dir_aufs.cc aufs/store_io_aufs.cc
+libcoss_a_SOURCES = coss/store_coss.h   coss/store_io_coss.cc coss/store_dir_coss.cc \
+	coss/async_io.cc coss/async_io.h
+libdiskd_a_SOURCES = diskd/diskd.cc diskd/store_dir_diskd.cc diskd/store_diskd.h \
+	diskd/store_io_diskd.cc
+libnull_a_SOURCES = null/store_null.cc
+libufs_a_SOURCES = ufs/store_dir_ufs.cc ufs/store_io_ufs.cc ufs/store_ufs.h
 
 EXTRA_DIST = \
 	coss/coss-notes.txt
--- squid/src/fs/aufs/aiops.c	Wed Feb 14 01:07:42 2007
+++ /dev/null	Wed Feb 14 01:07:22 2007
@@ -1,900 +0,0 @@
-/*
- * $Id$
- *
- * 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 "store_asyncufs.h"
-
-#include	<stdio.h>
-#include	<sys/types.h>
-#include	<sys/stat.h>
-#include	<fcntl.h>
-#include	<pthread.h>
-#include	<errno.h>
-#include	<dirent.h>
-#include	<signal.h>
-#if HAVE_SCHED_H
-#include	<sched.h>
-#endif
-
-#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;
-
-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 {
-    pthread_mutex_t mutex;
-    pthread_cond_t cond;
-    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;
-    pthread_t thread;
-    squidaio_thread_status status;
-    struct squidaio_request_t *current_req;
-    unsigned long requests;
-};
-
-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 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;
-#if HAVE_SCHED_H
-static struct sched_param globsched;
-#endif
-static pthread_t 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;
-
-    pthread_attr_init(&globattr);
-#if HAVE_PTHREAD_ATTR_SETSCOPE
-    pthread_attr_setscope(&globattr, PTHREAD_SCOPE_SYSTEM);
-#endif
-#if HAVE_SCHED_H
-    globsched.sched_priority = 1;
-#endif
-    main_thread = pthread_self();
-#if HAVE_SCHED_H && HAVE_PTHREAD_SETSCHEDPARAM
-    pthread_setschedparam(main_thread, SCHED_OTHER, &globsched);
-#endif
-#if HAVE_SCHED_H
-    globsched.sched_priority = 2;
-#endif
-#if HAVE_SCHED_H && HAVE_PTHREAD_ATTR_SETSCHEDPARAM
-    pthread_attr_setschedparam(&globattr, &globsched);
-#endif
-
-    /* Initialize request queue */
-    if (pthread_mutex_init(&(request_queue.mutex), NULL))
-	fatal("Failed to create mutex");
-    if (pthread_cond_init(&(request_queue.cond), NULL))
-	fatal("Failed to create condition variable");
-    request_queue.head = NULL;
-    request_queue.tailp = &request_queue.head;
-    request_queue.requests = 0;
-    request_queue.blocked = 0;
-
-    /* Initialize done queue */
-    if (pthread_mutex_init(&(done_queue.mutex), NULL))
-	fatal("Failed to create mutex");
-    if (pthread_cond_init(&(done_queue.cond), NULL))
-	fatal("Failed to create condition variable");
-    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));
-    assert (NUMTHREADS);
-    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 (pthread_create(&threadp->thread, &globattr, squidaio_thread_loop, threadp)) {
-	    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 void *
-squidaio_thread_loop(void *ptr)
-{
-    squidaio_thread_t *threadp = ptr;
-    squidaio_request_t *request;
-    sigset_t new;
-
-    /*
-     * 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);
-
-    while (1) {
-	threadp->current_req = request = NULL;
-	request = NULL;
-	/* Get a request to process */
-	threadp->status = _THREAD_WAITING;
-	pthread_mutex_lock(&request_queue.mutex);
-	while (!request_queue.head) {
-	    pthread_cond_wait(&request_queue.cond, &request_queue.mutex);
-	}
-	request = request_queue.head;
-	if (request)
-	    request_queue.head = request->next;
-	if (!request_queue.head)
-	    request_queue.tailp = &request_queue.head;
-	pthread_mutex_unlock(&request_queue.mutex);
-	/* 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 */
-	pthread_mutex_lock(&done_queue.mutex);
-	*done_queue.tailp = request;
-	done_queue.tailp = &request->next;
-	pthread_mutex_unlock(&done_queue.mutex);
-	threadp->requests++;
-    }				/* while forever */
-    return NULL;
-}				/* squidaio_thread_loop */
-
-static void
-squidaio_queue_request(squidaio_request_t * request)
-{
-    static int high_start = 0;
-    debug(43, 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 (pthread_mutex_trylock(&request_queue.mutex) == 0) {
-	    /* Normal path */
-	    *request_queue.tailp = request;
-	    request_queue.tailp = &request->next;
-	    pthread_cond_signal(&request_queue.cond);
-	    pthread_mutex_unlock(&request_queue.mutex);
-	} 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 (pthread_mutex_trylock(&request_queue.mutex) == 0) {
-	    /* Ok, the queue is no longer blocked */
-	    *request_queue.tailp = request_queue2.head;
-	    request_queue.tailp = &request->next;
-	    pthread_cond_signal(&request_queue.cond);
-	    pthread_mutex_unlock(&request_queue.mutex);
-	    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(43, 9) ("squidaio_cancel: %p type=%d result=%p\n",
-	    request, request->request_type, request->resultp);
-	request->cancelled = 1;
-	request->resultp = NULL;
-	resultp->_data = NULL;
-	resultp->result_type = _AIO_OP_NONE;
-	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;
-    resultp->result_type = _AIO_OP_OPEN;
-
-    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;
-    resultp->result_type = _AIO_OP_READ;
-
-    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;
-    resultp->result_type = _AIO_OP_WRITE;
-
-    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;
-    resultp->result_type = _AIO_OP_CLOSE;
-
-    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;
-    resultp->result_type = _AIO_OP_STAT;
-
-    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;
-    resultp->result_type = _AIO_OP_UNLINK;
-
-    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;
-    resultp->result_type = _AIO_OP_TRUNCATE;
-
-    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);
-    resultp->result_type = _AIO_OP_OPENDIR;
-    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 &&
-	pthread_mutex_trylock(&request_queue.mutex) == 0) {
-	*request_queue.tailp = request_queue2.head;
-	request_queue.tailp = request_queue2.tailp;
-	pthread_cond_signal(&request_queue.cond);
-	pthread_mutex_unlock(&request_queue.mutex);
-	request_queue2.head = NULL;
-	request_queue2.tailp = &request_queue2.head;
-    }
-    /* poll done queue */
-    if (done_queue.head && pthread_mutex_trylock(&done_queue.mutex) == 0) {
-	struct squidaio_request_t *requests = done_queue.head;
-	done_queue.head = NULL;
-	done_queue.tailp = &done_queue.head;
-	pthread_mutex_unlock(&done_queue.mutex);
-	*done_requests.tailp = requests;
-	request_queue_len -= 1;
-	while (requests->next) {
-	    requests = requests->next;
-	    request_queue_len -= 1;
-	}
-	done_requests.tailp = &requests->next;
-    }
-#if HAVE_SCHED_H
-    /* Give up the CPU to allow the threads to do their work */
-    /*
-     * For Andres thoughts about yield(), see
-     * http://www.squid-cache.org/mail-archive/squid-dev/200012/0001.html
-     */
-    if (done_queue.head || request_queue.head)
-#ifndef _SQUID_SOLARIS_
-	sched_yield();
-#else
-	yield();
-#endif
-#endif
-}
-
-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(43, 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 01:07:22 2007
+++ squid/src/fs/aufs/aiops.cc	Wed Feb 14 01:07:42 2007
@@ -0,0 +1,900 @@
+/*
+ * $Id: aiops.cc,v 1.1.2.1 2002/10/12 12:21:42 rbcollins 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 "store_asyncufs.h"
+
+#include	<stdio.h>
+#include	<sys/types.h>
+#include	<sys/stat.h>
+#include	<fcntl.h>
+#include	<pthread.h>
+#include	<errno.h>
+#include	<dirent.h>
+#include	<signal.h>
+#if HAVE_SCHED_H
+#include	<sched.h>
+#endif
+
+#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;
+
+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 {
+    pthread_mutex_t mutex;
+    pthread_cond_t cond;
+    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;
+    pthread_t thread;
+    squidaio_thread_status status;
+    struct squidaio_request_t *current_req;
+    unsigned long requests;
+};
+
+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 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;
+#if HAVE_SCHED_H
+static struct sched_param globsched;
+#endif
+static pthread_t 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 = (char *)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;
+
+    pthread_attr_init(&globattr);
+#if HAVE_PTHREAD_ATTR_SETSCOPE
+    pthread_attr_setscope(&globattr, PTHREAD_SCOPE_SYSTEM);
+#endif
+#if HAVE_SCHED_H
+    globsched.sched_priority = 1;
+#endif
+    main_thread = pthread_self();
+#if HAVE_SCHED_H && HAVE_PTHREAD_SETSCHEDPARAM
+    pthread_setschedparam(main_thread, SCHED_OTHER, &globsched);
+#endif
+#if HAVE_SCHED_H
+    globsched.sched_priority = 2;
+#endif
+#if HAVE_SCHED_H && HAVE_PTHREAD_ATTR_SETSCHEDPARAM
+    pthread_attr_setschedparam(&globattr, &globsched);
+#endif
+
+    /* Initialize request queue */
+    if (pthread_mutex_init(&(request_queue.mutex), NULL))
+	fatal("Failed to create mutex");
+    if (pthread_cond_init(&(request_queue.cond), NULL))
+	fatal("Failed to create condition variable");
+    request_queue.head = NULL;
+    request_queue.tailp = &request_queue.head;
+    request_queue.requests = 0;
+    request_queue.blocked = 0;
+
+    /* Initialize done queue */
+    if (pthread_mutex_init(&(done_queue.mutex), NULL))
+	fatal("Failed to create mutex");
+    if (pthread_cond_init(&(done_queue.cond), NULL))
+	fatal("Failed to create condition variable");
+    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));
+    assert (NUMTHREADS);
+    for (i = 0; i < NUMTHREADS; i++) {
+	threadp = (squidaio_thread_t *)memPoolAlloc(squidaio_thread_pool);
+	threadp->status = _THREAD_STARTING;
+	threadp->current_req = NULL;
+	threadp->requests = 0;
+	threadp->next = threads;
+	threads = threadp;
+	if (pthread_create(&threadp->thread, &globattr, squidaio_thread_loop, threadp)) {
+	    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 void *
+squidaio_thread_loop(void *ptr)
+{
+    squidaio_thread_t *threadp = (squidaio_thread_t *)ptr;
+    squidaio_request_t *request;
+    sigset_t newSig;
+
+    /*
+     * 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(&newSig);
+    sigaddset(&newSig, SIGPIPE);
+    sigaddset(&newSig, SIGCHLD);
+#ifdef _SQUID_LINUX_THREADS_
+    sigaddset(&newSig, SIGQUIT);
+    sigaddset(&newSig, SIGTRAP);
+#else
+    sigaddset(&newSig, SIGUSR1);
+    sigaddset(&newSig, SIGUSR2);
+#endif
+    sigaddset(&newSig, SIGHUP);
+    sigaddset(&newSig, SIGTERM);
+    sigaddset(&newSig, SIGINT);
+    sigaddset(&newSig, SIGALRM);
+    pthread_sigmask(SIG_BLOCK, &newSig, NULL);
+
+    while (1) {
+	threadp->current_req = request = NULL;
+	request = NULL;
+	/* Get a request to process */
+	threadp->status = _THREAD_WAITING;
+	pthread_mutex_lock(&request_queue.mutex);
+	while (!request_queue.head) {
+	    pthread_cond_wait(&request_queue.cond, &request_queue.mutex);
+	}
+	request = request_queue.head;
+	if (request)
+	    request_queue.head = request->next;
+	if (!request_queue.head)
+	    request_queue.tailp = &request_queue.head;
+	pthread_mutex_unlock(&request_queue.mutex);
+	/* 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 */
+	pthread_mutex_lock(&done_queue.mutex);
+	*done_queue.tailp = request;
+	done_queue.tailp = &request->next;
+	pthread_mutex_unlock(&done_queue.mutex);
+	threadp->requests++;
+    }				/* while forever */
+    return NULL;
+}				/* squidaio_thread_loop */
+
+static void
+squidaio_queue_request(squidaio_request_t * request)
+{
+    static int high_start = 0;
+    debug(43, 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 (pthread_mutex_trylock(&request_queue.mutex) == 0) {
+	    /* Normal path */
+	    *request_queue.tailp = request;
+	    request_queue.tailp = &request->next;
+	    pthread_cond_signal(&request_queue.cond);
+	    pthread_mutex_unlock(&request_queue.mutex);
+	} 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 (pthread_mutex_trylock(&request_queue.mutex) == 0) {
+	    /* Ok, the queue is no longer blocked */
+	    *request_queue.tailp = request_queue2.head;
+	    request_queue.tailp = &request->next;
+	    pthread_cond_signal(&request_queue.cond);
+	    pthread_mutex_unlock(&request_queue.mutex);
+	    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 = (squidaio_request_t *)resultp->_data;
+
+    if (request && request->resultp == resultp) {
+	debug(43, 9) ("squidaio_cancel: %p type=%d result=%p\n",
+	    request, request->request_type, request->resultp);
+	request->cancelled = 1;
+	request->resultp = NULL;
+	resultp->_data = NULL;
+	resultp->result_type = _AIO_OP_NONE;
+	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 = (squidaio_request_t *)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;
+    resultp->result_type = _AIO_OP_OPEN;
+
+    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 = (squidaio_request_t *)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;
+    resultp->result_type = _AIO_OP_READ;
+
+    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 = (squidaio_request_t *)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;
+    resultp->result_type = _AIO_OP_WRITE;
+
+    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 = (squidaio_request_t *)memPoolAlloc(squidaio_request_pool);
+    requestp->fd = fd;
+    requestp->resultp = resultp;
+    requestp->request_type = _AIO_OP_CLOSE;
+    requestp->cancelled = 0;
+    resultp->result_type = _AIO_OP_CLOSE;
+
+    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 = (squidaio_request_t *)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;
+    resultp->result_type = _AIO_OP_STAT;
+
+    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 = (squidaio_request_t *)memPoolAlloc(squidaio_request_pool);
+    requestp->path = squidaio_xstrdup(path);
+    requestp->resultp = resultp;
+    requestp->request_type = _AIO_OP_UNLINK;
+    requestp->cancelled = 0;
+    resultp->result_type = _AIO_OP_UNLINK;
+
+    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 = (squidaio_request_t *)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;
+    resultp->result_type = _AIO_OP_TRUNCATE;
+
+    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);
+    resultp->result_type = _AIO_OP_OPENDIR;
+    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 &&
+	pthread_mutex_trylock(&request_queue.mutex) == 0) {
+	*request_queue.tailp = request_queue2.head;
+	request_queue.tailp = request_queue2.tailp;
+	pthread_cond_signal(&request_queue.cond);
+	pthread_mutex_unlock(&request_queue.mutex);
+	request_queue2.head = NULL;
+	request_queue2.tailp = &request_queue2.head;
+    }
+    /* poll done queue */
+    if (done_queue.head && pthread_mutex_trylock(&done_queue.mutex) == 0) {
+	struct squidaio_request_t *requests = done_queue.head;
+	done_queue.head = NULL;
+	done_queue.tailp = &done_queue.head;
+	pthread_mutex_unlock(&done_queue.mutex);
+	*done_requests.tailp = requests;
+	request_queue_len -= 1;
+	while (requests->next) {
+	    requests = requests->next;
+	    request_queue_len -= 1;
+	}
+	done_requests.tailp = &requests->next;
+    }
+#if HAVE_SCHED_H
+    /* Give up the CPU to allow the threads to do their work */
+    /*
+     * For Andres thoughts about yield(), see
+     * http://www.squid-cache.org/mail-archive/squid-dev/200012/0001.html
+     */
+    if (done_queue.head || request_queue.head)
+#ifndef _SQUID_SOLARIS_
+	sched_yield();
+#else
+	yield();
+#endif
+#endif
+}
+
+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(43, 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;
+    }
+}
--- squid/src/fs/aufs/async_io.c	Wed Feb 14 01:07:42 2007
+++ /dev/null	Wed Feb 14 01:07:22 2007
@@ -1,380 +0,0 @@
-
-/*
- * $Id$
- *
- * 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"
-#include "Store.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_start;
-     int open_finish;
-     int close_start;
-     int close_finish;
-     int cancel;
-     int write_start;
-     int write_finish;
-     int read_start;
-     int read_finish;
-     int stat_start;
-     int stat_finish;
-     int unlink_start;
-     int unlink_finish;
-     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_start++;
-    ctrlp = memPoolAlloc(squidaio_ctrl_pool);
-    ctrlp->fd = -2;
-    ctrlp->done_handler = callback;
-    ctrlp->done_handler_data = cbdataReference(callback_data);
-    ctrlp->operation = _AIO_OPEN;
-    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_start++;
-    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;
-    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 (curr->done_handler) {
-	    AIOCB *callback = curr->done_handler;
-	    void *cbdata;
-	    curr->done_handler = NULL;
-	    debug(32, 2) ("this be aioCancel\n");
-	    if (cbdataReferenceValidDone(curr->done_handler_data, &cbdata))
-		callback(fd, cbdata, -2, -2);
-	}
-	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_start++;
-    ctrlp = memPoolAlloc(squidaio_ctrl_pool);
-    ctrlp->fd = fd;
-    ctrlp->done_handler = callback;
-    ctrlp->done_handler_data = cbdataReference(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;
-    }
-    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_start++;
-    ctrlp = memPoolAlloc(squidaio_ctrl_pool);
-    ctrlp->fd = fd;
-    ctrlp->done_handler = callback;
-    ctrlp->done_handler_data = cbdataReference(callback_data);
-    ctrlp->operation = _AIO_READ;
-    if (offset >= 0)
-	seekmode = SEEK_SET;
-    else {
-	seekmode = SEEK_CUR;
-	offset = 0;
-    }
-    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_start++;
-    ctrlp = memPoolAlloc(squidaio_ctrl_pool);
-    ctrlp->fd = -2;
-    ctrlp->done_handler = callback;
-    ctrlp->done_handler_data = cbdataReference(callback_data);
-    ctrlp->operation = _AIO_STAT;
-    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_start++;
-    ctrlp = memPoolAlloc(squidaio_ctrl_pool);
-    ctrlp->fd = -2;
-    ctrlp->done_handler = callback;
-    ctrlp->done_handler_data = cbdataReference(callback_data);
-    ctrlp->operation = _AIO_UNLINK;
-    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_start++;
-    ctrlp = memPoolAlloc(squidaio_ctrl_pool);
-    ctrlp->fd = -2;
-    ctrlp->done_handler = callback;
-    ctrlp->done_handler_data = cbdataReference(callback_data);
-    ctrlp->operation = _AIO_TRUNCATE;
-    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;
-    int retval = 0;
-
-    assert(initialised);
-    squidaio_counts.check_callback++;
-    for (;;) {
-	if ((resultp = squidaio_poll_done()) == NULL)
-	    break;
-	ctrlp = (squidaio_ctrl_t *) resultp->data;
-	switch (resultp->result_type){
-	    case _AIO_OP_NONE:
-	    case _AIO_OP_TRUNCATE:
-	    case _AIO_OP_OPENDIR:
-		 break;
-	    case _AIO_OP_OPEN:
-		 ++squidaio_counts.open_finish;
-		 break;
-	    case _AIO_OP_READ:
-		 ++squidaio_counts.read_finish;
-		 break;
-	    case _AIO_OP_WRITE:
-		 ++squidaio_counts.write_finish;
-		 break;
-	    case _AIO_OP_CLOSE:
-		 ++squidaio_counts.close_finish;
-		 break;
-	    case _AIO_OP_UNLINK:
-		 ++squidaio_counts.unlink_finish;
-		 break;
-	    case _AIO_OP_STAT:
-		 ++squidaio_counts.stat_finish;
-		 break;
-	}
-	if (ctrlp == NULL)
-	    continue;		/* XXX Should not happen */
-	dlinkDelete(&ctrlp->node, &used_list);
-	if (ctrlp->done_handler) {
-	    AIOCB *callback = ctrlp->done_handler;
-	    void *cbdata;
-	    ctrlp->done_handler = NULL;
-	    if (cbdataReferenceValidDone(ctrlp->done_handler_data, &cbdata)) {
-		retval = 1;	/* Return that we've actually done some work */
-		callback(ctrlp->fd, cbdata,
-		    ctrlp->result.aio_return, ctrlp->result.aio_errno);
-	    }
-	}
-	/* 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, "Operation\t# Requests\tNumber serviced\n");
-    storeAppendPrintf(sentry, "open\t%d\t%d\n", squidaio_counts.open_start,squidaio_counts.open_finish);
-    storeAppendPrintf(sentry, "close\t%d\t%d\n", squidaio_counts.close_start,squidaio_counts.close_finish);
-    storeAppendPrintf(sentry, "cancel\t%d\t-\n", squidaio_counts.cancel);
-    storeAppendPrintf(sentry, "write\t%d\t%d\n", squidaio_counts.write_start,squidaio_counts.write_finish);
-    storeAppendPrintf(sentry, "read\t%d\t%d\n", squidaio_counts.read_start,squidaio_counts.read_finish);
-    storeAppendPrintf(sentry, "stat\t%d\t%d\n", squidaio_counts.stat_start,squidaio_counts.stat_finish);
-    storeAppendPrintf(sentry, "unlink\t%d\t%d\n", squidaio_counts.unlink_start,squidaio_counts.unlink_finish);
-    storeAppendPrintf(sentry, "check_callback\t%d\t-\n", squidaio_counts.check_callback);
-    storeAppendPrintf(sentry, "queue\t%d\t-\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 01:07:22 2007
+++ squid/src/fs/aufs/async_io.cc	Wed Feb 14 01:07:42 2007
@@ -0,0 +1,380 @@
+
+/*
+ * $Id: async_io.cc,v 1.1.2.1 2002/10/12 12:21:42 rbcollins 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"
+#include "Store.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_start;
+     int open_finish;
+     int close_start;
+     int close_finish;
+     int cancel;
+     int write_start;
+     int write_finish;
+     int read_start;
+     int read_finish;
+     int stat_start;
+     int stat_finish;
+     int unlink_start;
+     int unlink_finish;
+     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_start++;
+    ctrlp = (squidaio_ctrl_t *)memPoolAlloc(squidaio_ctrl_pool);
+    ctrlp->fd = -2;
+    ctrlp->done_handler = callback;
+    ctrlp->done_handler_data = cbdataReference(callback_data);
+    ctrlp->operation = _AIO_OPEN;
+    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_start++;
+    aioCancel(fd);
+    ctrlp = (squidaio_ctrl_t *)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;
+    dlink_node *m, *next;
+
+    assert(initialised);
+    squidaio_counts.cancel++;
+    for (m = used_list.head; m; m = next) {
+	next = m->next;
+	curr = (squidaio_ctrl_t *)m->data;
+	if (curr->fd != fd)
+	    continue;
+
+	squidaio_cancel(&curr->result);
+
+	if (curr->done_handler) {
+	    AIOCB *callback = curr->done_handler;
+	    void *cbdata;
+	    curr->done_handler = NULL;
+	    debug(32, 2) ("this be aioCancel\n");
+	    if (cbdataReferenceValidDone(curr->done_handler_data, &cbdata))
+		callback(fd, cbdata, -2, -2);
+	}
+	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_start++;
+    ctrlp = (squidaio_ctrl_t *)memPoolAlloc(squidaio_ctrl_pool);
+    ctrlp->fd = fd;
+    ctrlp->done_handler = callback;
+    ctrlp->done_handler_data = cbdataReference(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;
+    }
+    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_start++;
+    ctrlp = (squidaio_ctrl_t *)memPoolAlloc(squidaio_ctrl_pool);
+    ctrlp->fd = fd;
+    ctrlp->done_handler = callback;
+    ctrlp->done_handler_data = cbdataReference(callback_data);
+    ctrlp->operation = _AIO_READ;
+    if (offset >= 0)
+	seekmode = SEEK_SET;
+    else {
+	seekmode = SEEK_CUR;
+	offset = 0;
+    }
+    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_start++;
+    ctrlp = (squidaio_ctrl_t *)memPoolAlloc(squidaio_ctrl_pool);
+    ctrlp->fd = -2;
+    ctrlp->done_handler = callback;
+    ctrlp->done_handler_data = cbdataReference(callback_data);
+    ctrlp->operation = _AIO_STAT;
+    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_start++;
+    ctrlp = (squidaio_ctrl_t *)memPoolAlloc(squidaio_ctrl_pool);
+    ctrlp->fd = -2;
+    ctrlp->done_handler = callback;
+    ctrlp->done_handler_data = cbdataReference(callback_data);
+    ctrlp->operation = _AIO_UNLINK;
+    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_start++;
+    ctrlp = (squidaio_ctrl_t *)memPoolAlloc(squidaio_ctrl_pool);
+    ctrlp->fd = -2;
+    ctrlp->done_handler = callback;
+    ctrlp->done_handler_data = cbdataReference(callback_data);
+    ctrlp->operation = _AIO_TRUNCATE;
+    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;
+    int retval = 0;
+
+    assert(initialised);
+    squidaio_counts.check_callback++;
+    for (;;) {
+	if ((resultp = squidaio_poll_done()) == NULL)
+	    break;
+	ctrlp = (squidaio_ctrl_t *) resultp->data;
+	switch (resultp->result_type){
+	    case _AIO_OP_NONE:
+	    case _AIO_OP_TRUNCATE:
+	    case _AIO_OP_OPENDIR:
+		 break;
+	    case _AIO_OP_OPEN:
+		 ++squidaio_counts.open_finish;
+		 break;
+	    case _AIO_OP_READ:
+		 ++squidaio_counts.read_finish;
+		 break;
+	    case _AIO_OP_WRITE:
+		 ++squidaio_counts.write_finish;
+		 break;
+	    case _AIO_OP_CLOSE:
+		 ++squidaio_counts.close_finish;
+		 break;
+	    case _AIO_OP_UNLINK:
+		 ++squidaio_counts.unlink_finish;
+		 break;
+	    case _AIO_OP_STAT:
+		 ++squidaio_counts.stat_finish;
+		 break;
+	}
+	if (ctrlp == NULL)
+	    continue;		/* XXX Should not happen */
+	dlinkDelete(&ctrlp->node, &used_list);
+	if (ctrlp->done_handler) {
+	    AIOCB *callback = ctrlp->done_handler;
+	    void *cbdata;
+	    ctrlp->done_handler = NULL;
+	    if (cbdataReferenceValidDone(ctrlp->done_handler_data, &cbdata)) {
+		retval = 1;	/* Return that we've actually done some work */
+		callback(ctrlp->fd, cbdata,
+		    ctrlp->result.aio_return, ctrlp->result.aio_errno);
+	    }
+	}
+	/* 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, "Operation\t# Requests\tNumber serviced\n");
+    storeAppendPrintf(sentry, "open\t%d\t%d\n", squidaio_counts.open_start,squidaio_counts.open_finish);
+    storeAppendPrintf(sentry, "close\t%d\t%d\n", squidaio_counts.close_start,squidaio_counts.close_finish);
+    storeAppendPrintf(sentry, "cancel\t%d\t-\n", squidaio_counts.cancel);
+    storeAppendPrintf(sentry, "write\t%d\t%d\n", squidaio_counts.write_start,squidaio_counts.write_finish);
+    storeAppendPrintf(sentry, "read\t%d\t%d\n", squidaio_counts.read_start,squidaio_counts.read_finish);
+    storeAppendPrintf(sentry, "stat\t%d\t%d\n", squidaio_counts.stat_start,squidaio_counts.stat_finish);
+    storeAppendPrintf(sentry, "unlink\t%d\t%d\n", squidaio_counts.unlink_start,squidaio_counts.unlink_finish);
+    storeAppendPrintf(sentry, "check_callback\t%d\t-\n", squidaio_counts.check_callback);
+    storeAppendPrintf(sentry, "queue\t%d\t-\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);
+}
--- squid/src/fs/aufs/store_dir_aufs.c	Wed Feb 14 01:07:42 2007
+++ /dev/null	Wed Feb 14 01:07:22 2007
@@ -1,251 +0,0 @@
-
-/*
- * $Id$
- *
- * 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.h"
-
-#include "store_asyncufs.h"
-#include "ufscommon.h"
-
-MemPool *squidaio_state_pool = NULL;
-MemPool *aufs_qread_pool = NULL;
-MemPool *aufs_qwrite_pool = NULL;
-static int asyncufs_initialised = 0;
-
-static STDUMP storeAufsDirDump;
-static STCHECKOBJ storeAufsDirCheckObj;
-static void storeAufsDirIOUnlinkFile(char *path);
-
-
-/* The MAIN externally visible function */
-STSETUP storeFsSetup_aufs;
-
-/*
- * 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(47, 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(47, 9) ("storeAufsDirCheckObj: load=%d\n", loadav);
-    return loadav;
-}
-
-void
-storeAufsDirIOUnlinkFile(char *path)
-{
-#if USE_TRUNCATE_NOT_UNLINK
-    aioTruncate(path, NULL, NULL);
-#else
-    aioUnlink(path, NULL, NULL);
-#endif
-}
-
-/* ========== LOCAL FUNCTIONS ABOVE, GLOBAL FUNCTIONS BELOW ========== */
-
-static struct cache_dir_option options[] =
-{
-#if NOT_YET_DONE
-    {"L1", storeAufsDirParseL1, storeAufsDirDumpL1},
-    {"L2", storeAufsDirParseL2, storeAufsDirDumpL2},
-#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)
-{
-    commonUfsDirDump (entry, s);
-    dump_cachedir_options(entry, options, s);
-}
-
-/*
- * 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;
-    squidufsinfo_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(squidufsinfo_t));
-    if (aioinfo == NULL)
-	fatal("storeAufsDirParse: couldn't xmalloc() squidufsinfo_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;
-    aioinfo->io.storeDirUnlinkFile = storeAufsDirIOUnlinkFile;
-    sd->init = commonUfsDirInit;
-    sd->newfs = commonUfsDirNewfs;
-    sd->dump = storeAufsDirDump;
-    sd->freefs = commonUfsDirFree;
-    sd->dblcheck = commonUfsCleanupDoubleCheck;
-    sd->statfs = commonUfsDirStats;
-    sd->maintainfs = commonUfsDirMaintain;
-    sd->checkobj = storeAufsDirCheckObj;
-    sd->refobj = commonUfsDirRefObj;
-    sd->unrefobj = commonUfsDirUnrefObj;
-    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 = commonUfsDirOpenSwapLog;
-    sd->log.close = commonUfsDirCloseSwapLog;
-    sd->log.write = commonUfsDirSwapLog;
-    sd->log.clean.start = commonUfsDirWriteCleanStart;
-    sd->log.clean.nextentry = commonUfsDirCleanLogNextEntry;
-    sd->log.clean.done = commonUfsDirWriteCleanDone;
-
-    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_aufs(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 01:07:22 2007
+++ squid/src/fs/aufs/store_dir_aufs.cc	Wed Feb 14 01:07:42 2007
@@ -0,0 +1,251 @@
+
+/*
+ * $Id: store_dir_aufs.cc,v 1.1.2.1 2002/10/12 12:21:43 rbcollins 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.h"
+
+#include "store_asyncufs.h"
+#include "ufscommon.h"
+
+MemPool *squidaio_state_pool = NULL;
+MemPool *aufs_qread_pool = NULL;
+MemPool *aufs_qwrite_pool = NULL;
+static int asyncufs_initialised = 0;
+
+static STDUMP storeAufsDirDump;
+static STCHECKOBJ storeAufsDirCheckObj;
+static void storeAufsDirIOUnlinkFile(char *path);
+
+
+/* The MAIN externally visible function */
+STSETUP storeFsSetup_aufs;
+
+/*
+ * 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(47, 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(47, 9) ("storeAufsDirCheckObj: load=%d\n", loadav);
+    return loadav;
+}
+
+void
+storeAufsDirIOUnlinkFile(char *path)
+{
+#if USE_TRUNCATE_NOT_UNLINK
+    aioTruncate(path, NULL, NULL);
+#else
+    aioUnlink(path, NULL, NULL);
+#endif
+}
+
+/* ========== LOCAL FUNCTIONS ABOVE, GLOBAL FUNCTIONS BELOW ========== */
+
+static struct cache_dir_option options[] =
+{
+#if NOT_YET_DONE
+    {"L1", storeAufsDirParseL1, storeAufsDirDumpL1},
+    {"L2", storeAufsDirParseL2, storeAufsDirDumpL2},
+#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)
+{
+    commonUfsDirDump (entry, s);
+    dump_cachedir_options(entry, options, s);
+}
+
+/*
+ * 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;
+    squidufsinfo_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 = (squidufsinfo_t *)xmalloc(sizeof(squidufsinfo_t));
+    if (aioinfo == NULL)
+	fatal("storeAufsDirParse: couldn't xmalloc() squidufsinfo_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;
+    aioinfo->io.storeDirUnlinkFile = storeAufsDirIOUnlinkFile;
+    sd->init = commonUfsDirInit;
+    sd->newfs = commonUfsDirNewfs;
+    sd->dump = storeAufsDirDump;
+    sd->freefs = commonUfsDirFree;
+    sd->dblcheck = commonUfsCleanupDoubleCheck;
+    sd->statfs = commonUfsDirStats;
+    sd->maintainfs = commonUfsDirMaintain;
+    sd->checkobj = storeAufsDirCheckObj;
+    sd->refobj = commonUfsDirRefObj;
+    sd->unrefobj = commonUfsDirUnrefObj;
+    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 = commonUfsDirOpenSwapLog;
+    sd->log.close = commonUfsDirCloseSwapLog;
+    sd->log.write = commonUfsDirSwapLog;
+    sd->log.clean.start = commonUfsDirWriteCleanStart;
+    sd->log.clean.nextentry = commonUfsDirCleanLogNextEntry;
+    sd->log.clean.done = commonUfsDirWriteCleanDone;
+
+    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_aufs(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();
+}
--- squid/src/fs/aufs/store_io_aufs.c	Wed Feb 14 01:07:42 2007
+++ /dev/null	Wed Feb 14 01:07:22 2007
@@ -1,444 +0,0 @@
-
-/*
- * DEBUG 79
- */
-
-#include "squid.h"
-#include "store_asyncufs.h"
-#include "Store.h"
-#include "ufscommon.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 = commonUfsDirFullPath(SD, f, NULL);
-    storeIOState *sio;
-#if !ASYNC_OPEN
-    int fd;
-#endif
-    debug(79, 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(79, 3) ("storeAufsOpen: got failure (%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 | O_BINARY;
-    sio->callback = callback;
-    sio->callback_data = cbdataReference(callback_data);
-    sio->e = e;
-    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 = commonUfsDirMapBitAllocate(SD);
-    path = commonUfsDirFullPath(SD, filn, NULL);
-
-    debug(79, 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(79, 3) ("storeAufsCreate: got failure (%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 = cbdataReference(callback_data);
-    sio->e = (StoreEntry *) e;
-    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 */
-    commonUfsDirReplAdd(SD, e);
-    return sio;
-
-}
-
-
-
-/* Close */
-void
-storeAufsClose(SwapDir * SD, storeIOState * sio)
-{
-    squidaiostate_t *aiostate = (squidaiostate_t *) sio->fsstate;
-    debug(79, 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(79, 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 = cbdataReference(callback_data);
-    aiostate->read_buf = buf;
-    debug(79, 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, buf, size, offset, 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(79, 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(79, 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(79, 3) ("storeAufsUnlink: dirno %d, fileno %08X\n", SD->index, e->swap_filen);
-    commonUfsDirReplRemove(e);
-    commonUfsDirMapBitReset(SD, e->swap_filen);
-    commonUfsDirUnlinkFile(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(79, 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(79, 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(79, 3) ("storeAufsOpenDone: FD %d, errflag %d\n", fd, errflag);
-    Opening_FD--;
-    aiostate->flags.opening = 0;
-    if (errflag || fd < 0) {
-	errno = errflag;
-	debug(79, 0) ("storeAufsOpenDone: %s\n", xstrerror());
-	debug(79, 1) ("\t%s\n", commonUfsDirFullPath(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, commonUfsDirFullPath(INDEXSD(sio->swap_dirn), sio->swap_filen, NULL));
-    if (FILE_MODE(sio->mode) == O_WRONLY) {
-	if (storeAufsKickWriteQueue(sio))
-	    return;
-    } else if (FILE_MODE(sio->mode) == O_RDONLY) {
-	if (storeAufsKickReadQueue(sio))
-	    return;
-    }
-    if (aiostate->flags.close_request)
-	storeAufsIOCallback(sio, errflag);
-    debug(79, 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, const char *buf, int len, int errflag, void *my_data)
-#endif
-{
-    storeIOState *sio = my_data;
-    squidaiostate_t *aiostate = (squidaiostate_t *) sio->fsstate;
-    STRCB *callback = sio->read.callback;
-    void *cbdata;
-    ssize_t rlen;
-    debug(79, 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(79, 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);
-    sio->read.callback = NULL;
-    if (cbdataReferenceValidDone(sio->read.callback_data, &cbdata))
-	callback(cbdata, aiostate->read_buf, rlen);
-    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(79, 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 == ENOSPC ? DISK_NO_SPACE_LEFT : DISK_ERROR;
-    else
-	errflag = DISK_OK;
-#endif
-    assert(++loop_detect < 10);
-    aiostate->flags.writing = 0;
-    if (errflag) {
-	debug(79, 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;
-    squidaiostate_t *aiostate = (squidaiostate_t *) sio->fsstate;
-    int fd = aiostate->fd;
-    debug(79, 3) ("storeAufsIOCallback: errflag=%d\n", errflag);
-    debug(79, 3) ("%s:%d\n", __FILE__, __LINE__);
-    if (callback) {
-	void *cbdata;
-	sio->callback = NULL;
-	if (cbdataReferenceValidDone(sio->callback_data, &cbdata))
-	    callback(cbdata, errflag, sio);
-    }
-    debug(79, 3) ("%s:%d\n", __FILE__, __LINE__);
-    aiostate->fd = -1;
-    cbdataFree(sio);
-    if (fd < 0)
-	return;
-    debug(79, 3) ("%s:%d\n", __FILE__, __LINE__);
-    aioClose(fd);
-    fd_close(fd);
-    store_open_disk_fd--;
-    debug(79, 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);
-}
--- /dev/null	Wed Feb 14 01:07:22 2007
+++ squid/src/fs/aufs/store_io_aufs.cc	Wed Feb 14 01:07:42 2007
@@ -0,0 +1,444 @@
+
+/*
+ * DEBUG 79
+ */
+
+#include "squid.h"
+#include "store_asyncufs.h"
+#include "Store.h"
+#include "ufscommon.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 = commonUfsDirFullPath(SD, f, NULL);
+    storeIOState *sio;
+#if !ASYNC_OPEN
+    int fd;
+#endif
+    debug(79, 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(79, 3) ("storeAufsOpen: got failure (%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 | O_BINARY;
+    sio->callback = callback;
+    sio->callback_data = cbdataReference(callback_data);
+    sio->e = e;
+    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 = commonUfsDirMapBitAllocate(SD);
+    path = commonUfsDirFullPath(SD, filn, NULL);
+
+    debug(79, 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(79, 3) ("storeAufsCreate: got failure (%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 = cbdataReference(callback_data);
+    sio->e = (StoreEntry *) e;
+    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 */
+    commonUfsDirReplAdd(SD, e);
+    return sio;
+
+}
+
+
+
+/* Close */
+void
+storeAufsClose(SwapDir * SD, storeIOState * sio)
+{
+    squidaiostate_t *aiostate = (squidaiostate_t *) sio->fsstate;
+    debug(79, 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(79, 3) ("storeAufsRead: queueing read because FD < 0\n");
+	assert(aiostate->flags.opening);
+	assert(aiostate->pending_reads == NULL);
+	q = (struct _queued_read *)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 = cbdataReference(callback_data);
+    aiostate->read_buf = buf;
+    debug(79, 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, buf, size, offset, 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(79, 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 = (struct _queued_write *)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(79, 3) ("storeAufsWrite: queuing write\n");
+	q = (struct _queued_write *)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(79, 3) ("storeAufsUnlink: dirno %d, fileno %08X\n", SD->index, e->swap_filen);
+    commonUfsDirReplRemove(e);
+    commonUfsDirMapBitReset(SD, e->swap_filen);
+    commonUfsDirUnlinkFile(SD, e->swap_filen);
+}
+
+/*  === STATIC =========================================================== */
+
+static int
+storeAufsKickWriteQueue(storeIOState * sio)
+{
+    squidaiostate_t *aiostate = (squidaiostate_t *) sio->fsstate;
+    struct _queued_write *q = (struct _queued_write *)linklistShift(&aiostate->pending_writes);
+    if (NULL == q)
+	return 0;
+    debug(79, 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 = (struct _queued_read *)linklistShift(&(aiostate->pending_reads));
+    if (NULL == q)
+	return 0;
+    debug(79, 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 = (storeIOState *)my_data;
+    squidaiostate_t *aiostate = (squidaiostate_t *) sio->fsstate;
+    debug(79, 3) ("storeAufsOpenDone: FD %d, errflag %d\n", fd, errflag);
+    Opening_FD--;
+    aiostate->flags.opening = 0;
+    if (errflag || fd < 0) {
+	errno = errflag;
+	debug(79, 0) ("storeAufsOpenDone: %s\n", xstrerror());
+	debug(79, 1) ("\t%s\n", commonUfsDirFullPath(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, commonUfsDirFullPath(INDEXSD(sio->swap_dirn), sio->swap_filen, NULL));
+    if (FILE_MODE(sio->mode) == O_WRONLY) {
+	if (storeAufsKickWriteQueue(sio))
+	    return;
+    } else if (FILE_MODE(sio->mode) == O_RDONLY) {
+	if (storeAufsKickReadQueue(sio))
+	    return;
+    }
+    if (aiostate->flags.close_request)
+	storeAufsIOCallback(sio, errflag);
+    debug(79, 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, const char *buf, int len, int errflag, void *my_data)
+#endif
+{
+    storeIOState *sio = (storeIOState *)my_data;
+    squidaiostate_t *aiostate = (squidaiostate_t *) sio->fsstate;
+    STRCB *callback = sio->read.callback;
+    void *cbdata;
+    ssize_t rlen;
+    debug(79, 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(79, 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);
+    sio->read.callback = NULL;
+    if (cbdataReferenceValidDone(sio->read.callback_data, &cbdata))
+	callback(cbdata, aiostate->read_buf, rlen);
+    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 = (storeIOState *)my_data;
+    squidaiostate_t *aiostate = (squidaiostate_t *) sio->fsstate;
+    debug(79, 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 == ENOSPC ? DISK_NO_SPACE_LEFT : DISK_ERROR;
+    else
+	errflag = DISK_OK;
+#endif
+    assert(++loop_detect < 10);
+    aiostate->flags.writing = 0;
+    if (errflag) {
+	debug(79, 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;
+    squidaiostate_t *aiostate = (squidaiostate_t *) sio->fsstate;
+    int fd = aiostate->fd;
+    debug(79, 3) ("storeAufsIOCallback: errflag=%d\n", errflag);
+    debug(79, 3) ("%s:%d\n", __FILE__, __LINE__);
+    if (callback) {
+	void *cbdata;
+	sio->callback = NULL;
+	if (cbdataReferenceValidDone(sio->callback_data, &cbdata))
+	    callback(cbdata, errflag, sio);
+    }
+    debug(79, 3) ("%s:%d\n", __FILE__, __LINE__);
+    aiostate->fd = -1;
+    cbdataFree(sio);
+    if (fd < 0)
+	return;
+    debug(79, 3) ("%s:%d\n", __FILE__, __LINE__);
+    aioClose(fd);
+    fd_close(fd);
+    store_open_disk_fd--;
+    debug(79, 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);
+}
--- squid/src/fs/coss/async_io.c	Wed Feb 14 01:07:42 2007
+++ /dev/null	Wed Feb 14 01:07:22 2007
@@ -1,247 +0,0 @@
-/*
- * async_io.c - some quick async IO routines for COSS
- *
- * Adrian Chadd <adrian@squid-cache.org>
- *
- * These routines are simple plugin replacements for the file_* routines
- * in disk.c . They back-end into the POSIX AIO routines to provide
- * a nice and simple async IO framework for COSS.
- *
- * AIO is suitable for COSS - the only sync operations that the standard
- * supports are read/write, and since COSS works on a single file
- * per storedir it should work just fine.
- *
- * $Id$
- */
-
-#include "squid.h"
-#include <time.h>
-#include <aio.h>
-
-#include "async_io.h"
-
-/*
- * For the time being, we kinda don't need to have our own
- * open/close. Just read/write (with the queueing), and callback
- * with the dequeueing)
- */
-
-
-/* Internal routines */
-
-/*
- * find a free aio slot.
- * Return the index, or -1 if we can't find one.
- */
-static int
-a_file_findslot(async_queue_t * q)
-{
-    int i;
-
-    /* Later we should use something a little more .. efficient :) */
-    for (i = 0; i < MAX_ASYNCOP; i++) {
-	if (q->aq_queue[i].aq_e_state == AQ_ENTRY_FREE)
-	    /* Found! */
-	    return i;
-    }
-    /* found nothing */
-    return -1;
-}
-
-
-
-
-/* Exported routines */
-
-void
-a_file_read(async_queue_t * q, int fd, void *buf, int req_len, off_t offset,
-    DRCB * callback, void *data)
-{
-    int slot;
-    async_queue_entry_t *qe;
-
-    assert(q->aq_state == AQ_STATE_SETUP);
-
-#if 0
-    file_read(fd, buf, req_len, offset, callback, data);
-#endif
-    /* Find a free slot */
-    slot = a_file_findslot(q);
-    if (slot < 0) {
-	/* No free slot? Callback error, and return */
-	fatal("Aiee! out of aiocb slots!\n");
-    }
-    /* Mark slot as ours */
-    qe = &q->aq_queue[slot];
-    qe->aq_e_state = AQ_ENTRY_USED;
-    qe->aq_e_callback.read = callback;
-    qe->aq_e_callback_data = cbdataReference(data);
-    qe->aq_e_type = AQ_ENTRY_READ;
-    qe->aq_e_free = NULL;
-    qe->aq_e_buf = buf;
-    qe->aq_e_fd = fd;
-
-    qe->aq_e_aiocb.aio_fildes = fd;
-    qe->aq_e_aiocb.aio_nbytes = req_len;
-    qe->aq_e_aiocb.aio_offset = offset;
-    qe->aq_e_aiocb.aio_buf = buf;
-
-    /* Account */
-    q->aq_numpending++;
-
-    /* Initiate aio */
-    if (aio_read(&qe->aq_e_aiocb) < 0) {
-	fatalf("Aiee! aio_read() returned error (%d)!\n", errno);
-    }
-}
-
-
-void
-a_file_write(async_queue_t * q, int fd, off_t offset, void *buf, int len,
-    DWCB * callback, void *data, FREE * freefunc)
-{
-    int slot;
-    async_queue_entry_t *qe;
-
-    assert(q->aq_state == AQ_STATE_SETUP);
-
-#if 0
-    file_write(fd, offset, buf, len, callback, data, freefunc);
-#endif
-    /* Find a free slot */
-    slot = a_file_findslot(q);
-    if (slot < 0) {
-	/* No free slot? Callback error, and return */
-	fatal("Aiee! out of aiocb slots!\n");
-    }
-    /* Mark slot as ours */
-    qe = &q->aq_queue[slot];
-    qe->aq_e_state = AQ_ENTRY_USED;
-    qe->aq_e_callback.write = callback;
-    qe->aq_e_callback_data = cbdataReference(data);
-    qe->aq_e_type = AQ_ENTRY_WRITE;
-    qe->aq_e_free = freefunc;
-    qe->aq_e_buf = buf;
-    qe->aq_e_fd = fd;
-
-    qe->aq_e_aiocb.aio_fildes = fd;
-    qe->aq_e_aiocb.aio_nbytes = len;
-    qe->aq_e_aiocb.aio_offset = offset;
-    qe->aq_e_aiocb.aio_buf = buf;
-
-    /* Account */
-    q->aq_numpending++;
-
-    /* Initiate aio */
-    if (aio_write(&qe->aq_e_aiocb) < 0) {
-	fatalf("Aiee! aio_read() returned error (%d)!\n", errno);
-	assert(1 == 0);
-    }
-}
-
-
-/*
- * Note: we grab the state and free the state before calling the callback
- * because this allows us to cut down the amount of time it'll take
- * to find a free slot (since if we call the callback first, we're going
- * to probably be allocated the slot _after_ this one..)
- *
- * I'll make it much more optimal later.
- */
-int
-a_file_callback(async_queue_t * q)
-{
-    int i;
-    int completed = 0;
-    int retval, reterr;
-    DRCB *rc;
-    DWCB *wc;
-    FREE *freefunc;
-    void *cbdata;
-    int callback_valid;
-    void *buf;
-    int fd;
-    async_queue_entry_t *aqe;
-    async_queue_entry_type_t type;
-
-    assert(q->aq_state == AQ_STATE_SETUP);
-
-    /* Loop through all slots */
-    for (i = 0; i < MAX_ASYNCOP; i++) {
-	if (q->aq_queue[i].aq_e_state == AQ_ENTRY_USED) {
-	    aqe = &q->aq_queue[i];
-	    /* Active, get status */
-	    reterr = aio_error(&aqe->aq_e_aiocb);
-	    if (reterr < 0) {
-		fatal("aio_error returned an error!\n");
-	    }
-	    if (reterr != EINPROGRESS) {
-		/* Get the return code */
-		retval = aio_return(&aqe->aq_e_aiocb);
-
-		/* Get the callback parameters */
-		freefunc = aqe->aq_e_free;
-		rc = aqe->aq_e_callback.read;
-		wc = aqe->aq_e_callback.write;
-		buf = aqe->aq_e_buf;
-		fd = aqe->aq_e_fd;
-		type = aqe->aq_e_type;
-		callback_valid = cbdataReferenceValidDone(aqe->aq_e_callback_data, &cbdata);
-
-		/* Free slot */
-		bzero(aqe, sizeof(async_queue_entry_t));
-		aqe->aq_e_state = AQ_ENTRY_FREE;
-		q->aq_numpending--;
-
-		/* Callback */
-		if (callback_valid) {
-		    if (type == AQ_ENTRY_READ)
-			rc(fd, buf, retval, reterr, cbdata);
-		    if (type == AQ_ENTRY_WRITE)
-			wc(fd, reterr, retval, cbdata);
-		}
-		if (type == AQ_ENTRY_WRITE && freefunc)
-		    freefunc(buf);
-	    }
-	}
-    }
-    return completed;
-}
-
-
-void
-a_file_setupqueue(async_queue_t * q)
-{
-    /* Make sure the queue isn't setup */
-    assert(q->aq_state == AQ_STATE_NONE);
-
-    /* Loop through, blanking the queue entries */
-
-    /* Done */
-    q->aq_state = AQ_STATE_SETUP;
-}
-
-
-void
-a_file_syncqueue(async_queue_t * q)
-{
-    assert(q->aq_state == AQ_STATE_SETUP);
-
-    /*
-     * Keep calling callback to complete ops until the queue is empty
-     * We can't quit when callback returns 0 - some calls may not
-     * return any completed pending events, but they're still pending!
-     */
-    while (q->aq_numpending)
-	a_file_callback(q);
-}
-
-
-void
-a_file_closequeue(async_queue_t * q)
-{
-    assert(q->aq_state == AQ_STATE_SETUP);
-
-    a_file_syncqueue(q);
-    q->aq_state = AQ_STATE_NONE;
-}
--- /dev/null	Wed Feb 14 01:07:22 2007
+++ squid/src/fs/coss/async_io.cc	Wed Feb 14 01:07:42 2007
@@ -0,0 +1,247 @@
+/*
+ * async_io.c - some quick async IO routines for COSS
+ *
+ * Adrian Chadd <adrian@squid-cache.org>
+ *
+ * These routines are simple plugin replacements for the file_* routines
+ * in disk.c . They back-end into the POSIX AIO routines to provide
+ * a nice and simple async IO framework for COSS.
+ *
+ * AIO is suitable for COSS - the only sync operations that the standard
+ * supports are read/write, and since COSS works on a single file
+ * per storedir it should work just fine.
+ *
+ * $Id: async_io.cc,v 1.1.2.1 2002/10/12 12:21:43 rbcollins Exp $
+ */
+
+#include "squid.h"
+#include <time.h>
+#include <aio.h>
+
+#include "async_io.h"
+
+/*
+ * For the time being, we kinda don't need to have our own
+ * open/close. Just read/write (with the queueing), and callback
+ * with the dequeueing)
+ */
+
+
+/* Internal routines */
+
+/*
+ * find a free aio slot.
+ * Return the index, or -1 if we can't find one.
+ */
+static int
+a_file_findslot(async_queue_t * q)
+{
+    int i;
+
+    /* Later we should use something a little more .. efficient :) */
+    for (i = 0; i < MAX_ASYNCOP; i++) {
+	if (q->aq_queue[i].aq_e_state == AQ_ENTRY_FREE)
+	    /* Found! */
+	    return i;
+    }
+    /* found nothing */
+    return -1;
+}
+
+
+
+
+/* Exported routines */
+
+void
+a_file_read(async_queue_t * q, int fd, void *buf, int req_len, off_t offset,
+    DRCB * callback, void *data)
+{
+    int slot;
+    async_queue_entry_t *qe;
+
+    assert(q->aq_state == AQ_STATE_SETUP);
+
+#if 0
+    file_read(fd, buf, req_len, offset, callback, data);
+#endif
+    /* Find a free slot */
+    slot = a_file_findslot(q);
+    if (slot < 0) {
+	/* No free slot? Callback error, and return */
+	fatal("Aiee! out of aiocb slots!\n");
+    }
+    /* Mark slot as ours */
+    qe = &q->aq_queue[slot];
+    qe->aq_e_state = AQ_ENTRY_USED;
+    qe->aq_e_callback.read = callback;
+    qe->aq_e_callback_data = cbdataReference(data);
+    qe->aq_e_type = AQ_ENTRY_READ;
+    qe->aq_e_free = NULL;
+    qe->aq_e_buf = buf;
+    qe->aq_e_fd = fd;
+
+    qe->aq_e_aiocb.aio_fildes = fd;
+    qe->aq_e_aiocb.aio_nbytes = req_len;
+    qe->aq_e_aiocb.aio_offset = offset;
+    qe->aq_e_aiocb.aio_buf = buf;
+
+    /* Account */
+    q->aq_numpending++;
+
+    /* Initiate aio */
+    if (aio_read(&qe->aq_e_aiocb) < 0) {
+	fatalf("Aiee! aio_read() returned error (%d)!\n", errno);
+    }
+}
+
+
+void
+a_file_write(async_queue_t * q, int fd, off_t offset, void *buf, int len,
+    DWCB * callback, void *data, FREE * freefunc)
+{
+    int slot;
+    async_queue_entry_t *qe;
+
+    assert(q->aq_state == AQ_STATE_SETUP);
+
+#if 0
+    file_write(fd, offset, buf, len, callback, data, freefunc);
+#endif
+    /* Find a free slot */
+    slot = a_file_findslot(q);
+    if (slot < 0) {
+	/* No free slot? Callback error, and return */
+	fatal("Aiee! out of aiocb slots!\n");
+    }
+    /* Mark slot as ours */
+    qe = &q->aq_queue[slot];
+    qe->aq_e_state = AQ_ENTRY_USED;
+    qe->aq_e_callback.write = callback;
+    qe->aq_e_callback_data = cbdataReference(data);
+    qe->aq_e_type = AQ_ENTRY_WRITE;
+    qe->aq_e_free = freefunc;
+    qe->aq_e_buf = buf;
+    qe->aq_e_fd = fd;
+
+    qe->aq_e_aiocb.aio_fildes = fd;
+    qe->aq_e_aiocb.aio_nbytes = len;
+    qe->aq_e_aiocb.aio_offset = offset;
+    qe->aq_e_aiocb.aio_buf = buf;
+
+    /* Account */
+    q->aq_numpending++;
+
+    /* Initiate aio */
+    if (aio_write(&qe->aq_e_aiocb) < 0) {
+	fatalf("Aiee! aio_read() returned error (%d)!\n", errno);
+	assert(1 == 0);
+    }
+}
+
+
+/*
+ * Note: we grab the state and free the state before calling the callback
+ * because this allows us to cut down the amount of time it'll take
+ * to find a free slot (since if we call the callback first, we're going
+ * to probably be allocated the slot _after_ this one..)
+ *
+ * I'll make it much more optimal later.
+ */
+int
+a_file_callback(async_queue_t * q)
+{
+    int i;
+    int completed = 0;
+    int retval, reterr;
+    DRCB *rc;
+    DWCB *wc;
+    FREE *freefunc;
+    void *cbdata;
+    int callback_valid;
+    void *buf;
+    int fd;
+    async_queue_entry_t *aqe;
+    async_queue_entry_type_t type;
+
+    assert(q->aq_state == AQ_STATE_SETUP);
+
+    /* Loop through all slots */
+    for (i = 0; i < MAX_ASYNCOP; i++) {
+	if (q->aq_queue[i].aq_e_state == AQ_ENTRY_USED) {
+	    aqe = &q->aq_queue[i];
+	    /* Active, get status */
+	    reterr = aio_error(&aqe->aq_e_aiocb);
+	    if (reterr < 0) {
+		fatal("aio_error returned an error!\n");
+	    }
+	    if (reterr != EINPROGRESS) {
+		/* Get the return code */
+		retval = aio_return(&aqe->aq_e_aiocb);
+
+		/* Get the callback parameters */
+		freefunc = aqe->aq_e_free;
+		rc = aqe->aq_e_callback.read;
+		wc = aqe->aq_e_callback.write;
+		buf = aqe->aq_e_buf;
+		fd = aqe->aq_e_fd;
+		type = aqe->aq_e_type;
+		callback_valid = cbdataReferenceValidDone(aqe->aq_e_callback_data, &cbdata);
+
+		/* Free slot */
+		bzero(aqe, sizeof(async_queue_entry_t));
+		aqe->aq_e_state = AQ_ENTRY_FREE;
+		q->aq_numpending--;
+
+		/* Callback */
+		if (callback_valid) {
+		    if (type == AQ_ENTRY_READ)
+			rc(fd, (char *)buf, retval, reterr, cbdata);
+		    if (type == AQ_ENTRY_WRITE)
+			wc(fd, reterr, retval, cbdata);
+		}
+		if (type == AQ_ENTRY_WRITE && freefunc)
+		    freefunc(buf);
+	    }
+	}
+    }
+    return completed;
+}
+
+
+void
+a_file_setupqueue(async_queue_t * q)
+{
+    /* Make sure the queue isn't setup */
+    assert(q->aq_state == AQ_STATE_NONE);
+
+    /* Loop through, blanking the queue entries */
+
+    /* Done */
+    q->aq_state = AQ_STATE_SETUP;
+}
+
+
+void
+a_file_syncqueue(async_queue_t * q)
+{
+    assert(q->aq_state == AQ_STATE_SETUP);
+
+    /*
+     * Keep calling callback to complete ops until the queue is empty
+     * We can't quit when callback returns 0 - some calls may not
+     * return any completed pending events, but they're still pending!
+     */
+    while (q->aq_numpending)
+	a_file_callback(q);
+}
+
+
+void
+a_file_closequeue(async_queue_t * q)
+{
+    assert(q->aq_state == AQ_STATE_SETUP);
+
+    a_file_syncqueue(q);
+    q->aq_state = AQ_STATE_NONE;
+}
--- squid/src/fs/coss/store_dir_coss.c	Wed Feb 14 01:07:42 2007
+++ /dev/null	Wed Feb 14 01:07:22 2007
@@ -1,904 +0,0 @@
-
-/*
- * $Id$
- *
- * DEBUG: section 47    Store COSS Directory Routines
- * AUTHOR: Eric Stern
- *
- * 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.h"
-#include <aio.h>
-
-#include "async_io.h"
-#include "store_coss.h"
-
-#define STORE_META_BUFSZ 4096
-
-int n_coss_dirs = 0;
-/* static int last_coss_pick_index = -1; */
-int coss_initialised = 0;
-MemPool *coss_state_pool = NULL;
-MemPool *coss_index_pool = NULL;
-
-typedef struct _RebuildState RebuildState;
-struct _RebuildState {
-    SwapDir *sd;
-    int n_read;
-    FILE *log;
-    int speed;
-    struct {
-	unsigned int clean:1;
-    } flags;
-    struct _store_rebuild_data counts;
-};
-
-static char *storeCossDirSwapLogFile(SwapDir *, const char *);
-static EVH storeCossRebuildFromSwapLog;
-static StoreEntry *storeCossAddDiskRestore(SwapDir * SD, const cache_key * key,
-    int file_number,
-    size_t swap_file_sz,
-    time_t expires,
-    time_t timestamp,
-    time_t lastref,
-    time_t lastmod,
-    u_int32_t refcount,
-    u_int16_t flags,
-    int clean);
-static void storeCossDirRebuild(SwapDir * sd);
-static void storeCossDirCloseTmpSwapLog(SwapDir * sd);
-static FILE *storeCossDirOpenTmpSwapLog(SwapDir *, int *, int *);
-static STLOGOPEN storeCossDirOpenSwapLog;
-static STINIT storeCossDirInit;
-static STLOGCLEANSTART storeCossDirWriteCleanStart;
-static STLOGCLEANNEXTENTRY storeCossDirCleanLogNextEntry;
-static STLOGCLEANWRITE storeCossDirWriteCleanEntry;
-static STLOGCLEANDONE storeCossDirWriteCleanDone;
-static STLOGCLOSE storeCossDirCloseSwapLog;
-static STLOGWRITE storeCossDirSwapLog;
-static STNEWFS storeCossDirNewfs;
-static STCHECKOBJ storeCossDirCheckObj;
-static STFREE storeCossDirShutdown;
-static STFSPARSE storeCossDirParse;
-static STFSRECONFIGURE storeCossDirReconfigure;
-static STDUMP storeCossDirDump;
-static STCALLBACK storeCossDirCallback;
-
-/* The "only" externally visible function */
-STSETUP storeFsSetup_coss;
-
-static char *
-storeCossDirSwapLogFile(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
-storeCossDirOpenSwapLog(SwapDir * sd)
-{
-    CossInfo *cs = (CossInfo *) sd->fsdata;
-    char *path;
-    int fd;
-    path = storeCossDirSwapLogFile(sd, NULL);
-    fd = file_open(path, O_WRONLY | O_CREAT | O_BINARY);
-    if (fd < 0) {
-	debug(47, 1) ("%s: %s\n", path, xstrerror());
-	fatal("storeCossDirOpenSwapLog: Failed to open swap log.");
-    }
-    debug(47, 3) ("Cache COSS Dir #%d log opened on FD %d\n", sd->index, fd);
-    cs->swaplog_fd = fd;
-}
-
-static void
-storeCossDirCloseSwapLog(SwapDir * sd)
-{
-    CossInfo *cs = (CossInfo *) sd->fsdata;
-    if (cs->swaplog_fd < 0)	/* not open */
-	return;
-    file_close(cs->swaplog_fd);
-    debug(47, 3) ("Cache COSS Dir #%d log closed on FD %d\n",
-	sd->index, cs->swaplog_fd);
-    cs->swaplog_fd = -1;
-}
-
-static void
-storeCossDirInit(SwapDir * sd)
-{
-    CossInfo *cs = (CossInfo *) sd->fsdata;
-    a_file_setupqueue(&cs->aq);
-    storeCossDirOpenSwapLog(sd);
-    storeCossDirRebuild(sd);
-    cs->fd = file_open(sd->path, O_RDWR | O_CREAT);
-    if (cs->fd < 0) {
-	debug(47, 1) ("%s: %s\n", sd->path, xstrerror());
-	fatal("storeCossDirInit: Failed to open a COSS directory.");
-    }
-    n_coss_dirs++;
-    (void) storeDirGetBlkSize(sd->path, &sd->fs.blksize);
-}
-
-void
-storeCossRemove(SwapDir * sd, StoreEntry * e)
-{
-    CossInfo *cs = (CossInfo *) sd->fsdata;
-    CossIndexNode *coss_node = e->repl.data;
-    e->repl.data = NULL;
-    dlinkDelete(&coss_node->node, &cs->index);
-    memPoolFree(coss_index_pool, coss_node);
-    cs->count -= 1;
-}
-
-void
-storeCossAdd(SwapDir * sd, StoreEntry * e)
-{
-    CossInfo *cs = (CossInfo *) sd->fsdata;
-    CossIndexNode *coss_node = memPoolAlloc(coss_index_pool);
-    assert(!e->repl.data);
-    e->repl.data = coss_node;
-    dlinkAdd(e, &coss_node->node, &cs->index);
-    cs->count += 1;
-}
-
-static void
-storeCossRebuildComplete(void *data)
-{
-    RebuildState *rb = data;
-    SwapDir *sd = rb->sd;
-    storeCossStartMembuf(sd);
-    store_dirs_rebuilding--;
-    storeCossDirCloseTmpSwapLog(rb->sd);
-    storeRebuildComplete(&rb->counts);
-    cbdataFree(rb);
-}
-
-static void
-storeCossRebuildFromSwapLog(void *data)
-{
-    RebuildState *rb = data;
-    StoreEntry *e = NULL;
-    storeSwapLogData s;
-    size_t ss = sizeof(storeSwapLogData);
-    int count;
-    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(47, 1) ("Done reading %s swaplog (%d entries)\n",
-		rb->sd->path, rb->n_read);
-	    fclose(rb->log);
-	    rb->log = NULL;
-	    storeCossRebuildComplete(rb);
-	    return;
-	}
-	rb->n_read++;
-	if (s.op <= SWAP_LOG_NOP)
-	    continue;
-	if (s.op >= SWAP_LOG_MAX)
-	    continue;
-	debug(47, 3) ("storeCossRebuildFromSwapLog: %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) {
-		    e->swap_filen = -1;
-		}
-		storeRelease(e);
-		/* Fake an unlink here, this is a bad hack :( */
-		storeCossRemove(rb->sd, 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(47, 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(rb->sd->index,
-		    (int) sb.st_size / ss, rb->n_read);
-	}
-	if (EBIT_TEST(s.flags, KEY_PRIVATE)) {
-	    rb->counts.badflags++;
-	    continue;
-	}
-	e = storeGet(s.key);
-	if (e) {
-	    /* key already exists, current entry is newer */
-	    /* keep old, ignore new */
-	    rb->counts.dupcount++;
-	    continue;
-	}
-	/* update store_swap_size */
-	rb->counts.objcount++;
-	e = storeCossAddDiskRestore(rb->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("storeCossRebuild", storeCossRebuildFromSwapLog, rb, 0.0, 1);
-}
-
-/* Add a new object to the cache with empty memory copy and pointer to disk
- * use to rebuild store from disk. */
-static StoreEntry *
-storeCossAddDiskRestore(SwapDir * SD, const cache_key * key,
-    int file_number,
-    size_t swap_file_sz,
-    time_t expires,
-    time_t timestamp,
-    time_t lastref,
-    time_t lastmod,
-    u_int32_t refcount,
-    u_int16_t flags,
-    int clean)
-{
-    StoreEntry *e = NULL;
-    debug(47, 5) ("storeCossAddDiskRestore: %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;
-    e->swap_dirn = SD->index;
-    storeSetMemStatus(e, NOT_IN_MEMORY);
-    e->swap_status = SWAPOUT_DONE;
-    e->swap_filen = file_number;
-    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);
-    storeHashInsert(e, key);	/* do it after we clear KEY_PRIVATE */
-    storeCossAdd(SD, e);
-    e->swap_filen = storeCossAllocate(SD, e, COSS_ALLOC_NOTIFY);
-    return e;
-}
-
-CBDATA_TYPE(RebuildState);
-static void
-storeCossDirRebuild(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;
-    func = storeCossRebuildFromSwapLog;
-    rb->flags.clean = (unsigned int) clean;
-    /*
-     * If the swap.state file exists in the cache_dir, then
-     * we'll use storeCossRebuildFromSwapLog().
-     */
-    fp = storeCossDirOpenTmpSwapLog(sd, &clean, &zero);
-    debug(47, 1) ("Rebuilding COSS storage in %s (%s)\n",
-	sd->path, clean ? "CLEAN" : "DIRTY");
-    rb->log = fp;
-    store_dirs_rebuilding++;
-    if (!clean || fp == NULL) {
-	/* COSS cannot yet rebuild from a dirty state. If the log
-	 * is dirty then the COSS contents is thrown away.
-	 * Why? I guess it is because some contents will be lost,
-	 * and COSS cannot verify this..
-	 */
-	if (fp != NULL)
-	    fclose(fp);
-	/*
-	 * XXX Make sure we don't trigger an assertion if this is the first
-	 * storedir, since if we are, this call will cause storeRebuildComplete
-	 * to prematurely complete the rebuild process, and then some other
-	 * storedir will try to rebuild and eventually die.
-	 */
-	eventAdd("storeCossRebuildComplete", storeCossRebuildComplete, rb, 0.0, 0);
-	return;
-    }
-    eventAdd("storeCossRebuild", func, rb, 0.0, 1);
-}
-
-static void
-storeCossDirCloseTmpSwapLog(SwapDir * sd)
-{
-    CossInfo *cs = (CossInfo *) sd->fsdata;
-    char *swaplog_path = xstrdup(storeCossDirSwapLogFile(sd, NULL));
-    char *new_path = xstrdup(storeCossDirSwapLogFile(sd, ".new"));
-    int fd;
-    file_close(cs->swaplog_fd);
-#if defined (_SQUID_OS2_) || defined (_SQUID_CYGWIN_)
-    if (unlink(swaplog_path) < 0) {
-	debug(50, 0) ("%s: %s\n", swaplog_path, xstrerror());
-	fatal("storeCossDirCloseTmpSwapLog: unlink failed");
-    }
-#endif
-    if (xrename(new_path, swaplog_path) < 0) {
-	fatal("storeCossDirCloseTmpSwapLog: 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("storeCossDirCloseTmpSwapLog: Failed to open swap log.");
-    }
-    safe_free(swaplog_path);
-    safe_free(new_path);
-    cs->swaplog_fd = fd;
-    debug(47, 3) ("Cache COSS Dir #%d log opened on FD %d\n", sd->index, fd);
-}
-
-static FILE *
-storeCossDirOpenTmpSwapLog(SwapDir * sd, int *clean_flag, int *zero_flag)
-{
-    CossInfo *cs = (CossInfo *) sd->fsdata;
-    char *swaplog_path = xstrdup(storeCossDirSwapLogFile(sd, NULL));
-    char *clean_path = xstrdup(storeCossDirSwapLogFile(sd, ".last-clean"));
-    char *new_path = xstrdup(storeCossDirSwapLogFile(sd, ".new"));
-    struct stat log_sb;
-    struct stat clean_sb;
-    FILE *fp;
-    int fd;
-    if (stat(swaplog_path, &log_sb) < 0) {
-	debug(50, 1) ("Cache COSS 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 (cs->swaplog_fd >= 0)
-	file_close(cs->swaplog_fd);
-    /* open a write-only FD for the new log */
-    fd = file_open(new_path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY);
-    if (fd < 0) {
-	debug(50, 1) ("%s: %s\n", new_path, xstrerror());
-	fatal("storeDirOpenTmpSwapLog: Failed to open swap log.");
-    }
-    cs->swaplog_fd = fd;
-    /* open a read-only stream of the old log */
-    fp = fopen(swaplog_path, "rb");
-    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;
-    dlink_node *current;
-};
-
-#define CLEAN_BUF_SZ 16384
-/*
- * Begin the process to write clean cache state.  For COSS 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
-storeCossDirWriteCleanStart(SwapDir * sd)
-{
-    CossInfo *cs = (CossInfo *) sd->fsdata;
-    struct _clean_state *state = xcalloc(1, sizeof(*state));
-#if HAVE_FCHMOD
-    struct stat sb;
-#endif
-    state->new = xstrdup(storeCossDirSwapLogFile(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;
-    }
-    sd->log.clean.write = NULL;
-    sd->log.clean.state = NULL;
-    state->cur = xstrdup(storeCossDirSwapLogFile(sd, NULL));
-    state->cln = xstrdup(storeCossDirSwapLogFile(sd, ".last-clean"));
-    state->outbuf = xcalloc(CLEAN_BUF_SZ, 1);
-    state->outbuf_offset = 0;
-    unlink(state->cln);
-    state->current = cs->index.tail;
-    debug(50, 3) ("storeCOssDirWriteCleanLogs: 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 = storeCossDirWriteCleanEntry;
-    sd->log.clean.state = state;
-
-    return 0;
-}
-
-static const StoreEntry *
-storeCossDirCleanLogNextEntry(SwapDir * sd)
-{
-    struct _clean_state *state = sd->log.clean.state;
-    const StoreEntry *entry;
-    if (!state)
-	return NULL;
-    if (!state->current)
-	return NULL;
-    entry = (const StoreEntry *) state->current->data;
-    state->current = state->current->prev;
-    return entry;
-}
-
-/*
- * "write" an entry to the clean log file.
- */
-static void
-storeCossDirWriteCleanEntry(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 (FD_WRITE_METHOD(state->fd, state->outbuf, state->outbuf_offset) < 0) {
-	    debug(50, 0) ("storeCossDirWriteCleanLogs: %s: write: %s\n",
-		state->new, xstrerror());
-	    debug(50, 0) ("storeCossDirWriteCleanLogs: 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;
-	    return;
-	}
-	state->outbuf_offset = 0;
-    }
-}
-
-static void
-storeCossDirWriteCleanDone(SwapDir * sd)
-{
-    int fd;
-    struct _clean_state *state = sd->log.clean.state;
-    if (NULL == state)
-	return;
-    if (state->fd < 0)
-	return;
-    if (FD_WRITE_METHOD(state->fd, state->outbuf, state->outbuf_offset) < 0) {
-	debug(50, 0) ("storeCossDirWriteCleanLogs: %s: write: %s\n",
-	    state->new, xstrerror());
-	debug(50, 0) ("storeCossDirWriteCleanLogs: 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.
-     */
-    storeCossDirCloseSwapLog(sd);
-    /* save the fd value for a later test */
-    fd = state->fd;
-    /* rename */
-    if (state->fd >= 0) {
-#if defined(_SQUID_OS2_) || defined (_SQUID_CYGWIN_)
-	file_close(state->fd);
-	state->fd = -1;
-	if (unlink(state->cur) < 0)
-	    debug(50, 0) ("storeCossDirWriteCleanLogs: 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
-storeCossDirSwapLog(const SwapDir * sd, const StoreEntry * e, int op)
-{
-    CossInfo *cs = (CossInfo *) 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(cs->swaplog_fd,
-	-1,
-	s,
-	sizeof(storeSwapLogData),
-	NULL,
-	NULL,
-	(FREE *) storeSwapLogDataFree);
-}
-
-static void
-storeCossDirNewfs(SwapDir * sd)
-{
-    debug(47, 3) ("Creating swap space in %s\n", sd->path);
-}
-
-/* we are shutting down, flush all membufs to disk */
-static void
-storeCossDirShutdown(SwapDir * SD)
-{
-    CossInfo *cs = (CossInfo *) SD->fsdata;
-
-    storeCossSync(SD);		/* This'll call a_file_syncqueue() */
-    a_file_closequeue(&cs->aq);
-    file_close(cs->fd);
-    cs->fd = -1;
-
-    if (cs->swaplog_fd > -1) {
-	file_close(cs->swaplog_fd);
-	cs->swaplog_fd = -1;
-    }
-    n_coss_dirs--;
-}
-
-/*
- * storeCossDirCheckObj
- *
- * This routine is called by storeDirSelectSwapDir to see if the given
- * object is able to be stored on this filesystem. COSS filesystems will
- * not store everything. We don't check for maxobjsize here since its
- * done by the upper layers.
- */
-int
-storeCossDirCheckObj(SwapDir * SD, const StoreEntry * e)
-{
-    CossInfo *cs = (CossInfo *) SD->fsdata;
-    int loadav;
-
-    /* Check if the object is a special object, we can't cache these */
-    if (EBIT_TEST(e->flags, ENTRY_SPECIAL))
-	return -1;
-
-    /* Otherwise, we're ok */
-    /* Return load, cs->aq.aq_numpending out of MAX_ASYNCOP */
-    loadav = cs->aq.aq_numpending * 1000 / MAX_ASYNCOP;
-    return loadav;
-}
-
-
-/*
- * storeCossDirCallback - do the IO completions
- */
-static int
-storeCossDirCallback(SwapDir * SD)
-{
-    CossInfo *cs = (CossInfo *) SD->fsdata;
-
-    return a_file_callback(&cs->aq);
-}
-
-/* ========== LOCAL FUNCTIONS ABOVE, GLOBAL FUNCTIONS BELOW ========== */
-
-static void
-storeCossDirStats(SwapDir * SD, StoreEntry * sentry)
-{
-    CossInfo *cs = (CossInfo *) SD->fsdata;
-
-    storeAppendPrintf(sentry, "\n");
-    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, "Number of object collisions: %d\n", (int) cs->numcollisions);
-#if 0
-    /* is this applicable? I Hope not .. */
-    storeAppendPrintf(sentry, "Filemap bits in use: %d of %d (%d%%)\n",
-	SD->map->n_files_in_map, SD->map->max_n_files,
-	percent(SD->map->n_files_in_map, SD->map->max_n_files));
-#endif
-    storeAppendPrintf(sentry, "Pending operations: %d out of %d\n", cs->aq.aq_numpending, MAX_ASYNCOP);
-    storeAppendPrintf(sentry, "Flags:");
-    if (SD->flags.selected)
-	storeAppendPrintf(sentry, " SELECTED");
-    if (SD->flags.read_only)
-	storeAppendPrintf(sentry, " READ-ONLY");
-    storeAppendPrintf(sentry, "\n");
-}
-
-static void
-storeCossDirParse(SwapDir * sd, int index, char *path)
-{
-    unsigned int i;
-    unsigned int size;
-    CossInfo *cs;
-
-    i = GetInteger();
-    size = i << 10;		/* Mbytes to Kbytes */
-    if (size <= 0)
-	fatal("storeCossDirParse: invalid size value");
-
-    cs = xmalloc(sizeof(CossInfo));
-    if (cs == NULL)
-	fatal("storeCossDirParse: couldn't xmalloc() CossInfo!\n");
-
-    sd->index = index;
-    sd->path = xstrdup(path);
-    sd->max_size = size;
-    sd->fsdata = cs;
-
-    cs->fd = -1;
-    cs->swaplog_fd = -1;
-
-    sd->init = storeCossDirInit;
-    sd->newfs = storeCossDirNewfs;
-    sd->dump = storeCossDirDump;
-    sd->freefs = storeCossDirShutdown;
-    sd->dblcheck = NULL;
-    sd->statfs = storeCossDirStats;
-    sd->maintainfs = NULL;
-    sd->checkobj = storeCossDirCheckObj;
-    sd->refobj = NULL;		/* LRU is done in storeCossRead */
-    sd->unrefobj = NULL;
-    sd->callback = storeCossDirCallback;
-    sd->sync = storeCossSync;
-
-    sd->obj.create = storeCossCreate;
-    sd->obj.open = storeCossOpen;
-    sd->obj.close = storeCossClose;
-    sd->obj.read = storeCossRead;
-    sd->obj.write = storeCossWrite;
-    sd->obj.unlink = storeCossUnlink;
-
-    sd->log.open = storeCossDirOpenSwapLog;
-    sd->log.close = storeCossDirCloseSwapLog;
-    sd->log.write = storeCossDirSwapLog;
-    sd->log.clean.start = storeCossDirWriteCleanStart;
-    sd->log.clean.write = storeCossDirWriteCleanEntry;
-    sd->log.clean.nextentry = storeCossDirCleanLogNextEntry;
-    sd->log.clean.done = storeCossDirWriteCleanDone;
-
-    cs->current_offset = 0;
-    cs->fd = -1;
-    cs->swaplog_fd = -1;
-    cs->numcollisions = 0;
-    cs->membufs.head = cs->membufs.tail = NULL;		/* set when the rebuild completes */
-    cs->current_membuf = NULL;
-    cs->index.head = NULL;
-    cs->index.tail = NULL;
-
-    parse_cachedir_options(sd, NULL, 0);
-    /* Enforce maxobjsize being set to something */
-    if (sd->max_objsize == -1)
-	fatal("COSS requires max-size to be set to something other than -1!\n");
-}
-
-
-static void
-storeCossDirReconfigure(SwapDir * sd, int index, char *path)
-{
-    unsigned int i;
-    unsigned int size;
-
-    i = GetInteger();
-    size = i << 10;		/* Mbytes to Kbytes */
-    if (size <= 0)
-	fatal("storeCossDirParse: invalid size value");
-
-    if (size == sd->max_size)
-	debug(3, 1) ("Cache COSS dir '%s' size remains unchanged at %d KB\n", path, size);
-    else {
-	debug(3, 1) ("Cache COSS dir '%s' size changed to %d KB\n", path, size);
-	sd->max_size = size;
-    }
-    parse_cachedir_options(sd, NULL, 1);
-    /* Enforce maxobjsize being set to something */
-    if (sd->max_objsize == -1)
-	fatal("COSS requires max-size to be set to something other than -1!\n");
-}
-
-void
-storeCossDirDump(StoreEntry * entry, SwapDir * s)
-{
-    storeAppendPrintf(entry, " %d",
-	s->max_size >> 20);
-    dump_cachedir_options(entry, NULL, s);
-}
-
-#if OLD_UNUSED_CODE
-SwapDir *
-storeCossDirPick(void)
-{
-    int i, choosenext = 0;
-    SwapDir *SD;
-
-    if (n_coss_dirs == 0)
-	return NULL;
-    for (i = 0; i < Config.cacheSwap.n_configured; i++) {
-	SD = &Config.cacheSwap.swapDirs[i];
-	if (SD->type == SWAPDIR_COSS) {
-	    if ((last_coss_pick_index == -1) || (n_coss_dirs == 1)) {
-		last_coss_pick_index = i;
-		return SD;
-	    } else if (choosenext) {
-		last_coss_pick_index = i;
-		return SD;
-	    } else if (last_coss_pick_index == i) {
-		choosenext = 1;
-	    }
-	}
-    }
-    for (i = 0; i < Config.cacheSwap.n_configured; i++) {
-	SD = &Config.cacheSwap.swapDirs[i];
-	if (SD->type == SWAPDIR_COSS) {
-	    if ((last_coss_pick_index == -1) || (n_coss_dirs == 1)) {
-		last_coss_pick_index = i;
-		return SD;
-	    } else if (choosenext) {
-		last_coss_pick_index = i;
-		return SD;
-	    } else if (last_coss_pick_index == i) {
-		choosenext = 1;
-	    }
-	}
-    }
-    return NULL;
-}
-#endif
-
-/*
- * initial setup/done code
- */
-static void
-storeCossDirDone(void)
-{
-    memPoolDestroy(&coss_state_pool);
-/*  memPoolDestroy(&coss_index_pool);  XXX Should be here? */
-    coss_initialised = 0;
-}
-
-void
-storeFsSetup_coss(storefs_entry_t * storefs)
-{
-    assert(!coss_initialised);
-
-    storefs->parsefunc = storeCossDirParse;
-    storefs->reconfigurefunc = storeCossDirReconfigure;
-    storefs->donefunc = storeCossDirDone;
-    coss_state_pool = memPoolCreate("COSS IO State data", sizeof(CossState));
-    coss_index_pool = memPoolCreate("COSS index data", sizeof(CossIndexNode));
-    coss_initialised = 1;
-}
--- /dev/null	Wed Feb 14 01:07:22 2007
+++ squid/src/fs/coss/store_dir_coss.cc	Wed Feb 14 01:07:42 2007
@@ -0,0 +1,904 @@
+
+/*
+ * $Id: store_dir_coss.cc,v 1.1.2.1 2002/10/12 12:21:43 rbcollins Exp $
+ *
+ * DEBUG: section 47    Store COSS Directory Routines
+ * AUTHOR: Eric Stern
+ *
+ * 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.h"
+#include <aio.h>
+
+#include "async_io.h"
+#include "store_coss.h"
+
+#define STORE_META_BUFSZ 4096
+
+int n_coss_dirs = 0;
+/* static int last_coss_pick_index = -1; */
+int coss_initialised = 0;
+MemPool *coss_state_pool = NULL;
+MemPool *coss_index_pool = NULL;
+
+typedef struct _RebuildState RebuildState;
+struct _RebuildState {
+    SwapDir *sd;
+    int n_read;
+    FILE *log;
+    int speed;
+    struct {
+	unsigned int clean:1;
+    } flags;
+    struct _store_rebuild_data counts;
+};
+
+static char *storeCossDirSwapLogFile(SwapDir *, const char *);
+static EVH storeCossRebuildFromSwapLog;
+static StoreEntry *storeCossAddDiskRestore(SwapDir * SD, const cache_key * key,
+    int file_number,
+    size_t swap_file_sz,
+    time_t expires,
+    time_t timestamp,
+    time_t lastref,
+    time_t lastmod,
+    u_int32_t refcount,
+    u_int16_t flags,
+    int clean);
+static void storeCossDirRebuild(SwapDir * sd);
+static void storeCossDirCloseTmpSwapLog(SwapDir * sd);
+static FILE *storeCossDirOpenTmpSwapLog(SwapDir *, int *, int *);
+static STLOGOPEN storeCossDirOpenSwapLog;
+static STINIT storeCossDirInit;
+static STLOGCLEANSTART storeCossDirWriteCleanStart;
+static STLOGCLEANNEXTENTRY storeCossDirCleanLogNextEntry;
+static STLOGCLEANWRITE storeCossDirWriteCleanEntry;
+static STLOGCLEANDONE storeCossDirWriteCleanDone;
+static STLOGCLOSE storeCossDirCloseSwapLog;
+static STLOGWRITE storeCossDirSwapLog;
+static STNEWFS storeCossDirNewfs;
+static STCHECKOBJ storeCossDirCheckObj;
+static STFREE storeCossDirShutdown;
+static STFSPARSE storeCossDirParse;
+static STFSRECONFIGURE storeCossDirReconfigure;
+static STDUMP storeCossDirDump;
+static STCALLBACK storeCossDirCallback;
+
+/* The "only" externally visible function */
+STSETUP storeFsSetup_coss;
+
+static char *
+storeCossDirSwapLogFile(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
+storeCossDirOpenSwapLog(SwapDir * sd)
+{
+    CossInfo *cs = (CossInfo *) sd->fsdata;
+    char *path;
+    int fd;
+    path = storeCossDirSwapLogFile(sd, NULL);
+    fd = file_open(path, O_WRONLY | O_CREAT | O_BINARY);
+    if (fd < 0) {
+	debug(47, 1) ("%s: %s\n", path, xstrerror());
+	fatal("storeCossDirOpenSwapLog: Failed to open swap log.");
+    }
+    debug(47, 3) ("Cache COSS Dir #%d log opened on FD %d\n", sd->index, fd);
+    cs->swaplog_fd = fd;
+}
+
+static void
+storeCossDirCloseSwapLog(SwapDir * sd)
+{
+    CossInfo *cs = (CossInfo *) sd->fsdata;
+    if (cs->swaplog_fd < 0)	/* not open */
+	return;
+    file_close(cs->swaplog_fd);
+    debug(47, 3) ("Cache COSS Dir #%d log closed on FD %d\n",
+	sd->index, cs->swaplog_fd);
+    cs->swaplog_fd = -1;
+}
+
+static void
+storeCossDirInit(SwapDir * sd)
+{
+    CossInfo *cs = (CossInfo *) sd->fsdata;
+    a_file_setupqueue(&cs->aq);
+    storeCossDirOpenSwapLog(sd);
+    storeCossDirRebuild(sd);
+    cs->fd = file_open(sd->path, O_RDWR | O_CREAT);
+    if (cs->fd < 0) {
+	debug(47, 1) ("%s: %s\n", sd->path, xstrerror());
+	fatal("storeCossDirInit: Failed to open a COSS directory.");
+    }
+    n_coss_dirs++;
+    (void) storeDirGetBlkSize(sd->path, &sd->fs.blksize);
+}
+
+void
+storeCossRemove(SwapDir * sd, StoreEntry * e)
+{
+    CossInfo *cs = (CossInfo *) sd->fsdata;
+    CossIndexNode *coss_node = (CossIndexNode *)e->repl.data;
+    e->repl.data = NULL;
+    dlinkDelete(&coss_node->node, &cs->index);
+    memPoolFree(coss_index_pool, coss_node);
+    cs->count -= 1;
+}
+
+void
+storeCossAdd(SwapDir * sd, StoreEntry * e)
+{
+    CossInfo *cs = (CossInfo *) sd->fsdata;
+    CossIndexNode *coss_node = (CossIndexNode *)memPoolAlloc(coss_index_pool);
+    assert(!e->repl.data);
+    e->repl.data = coss_node;
+    dlinkAdd(e, &coss_node->node, &cs->index);
+    cs->count += 1;
+}
+
+static void
+storeCossRebuildComplete(void *data)
+{
+    RebuildState *rb = (RebuildState *)data;
+    SwapDir *sd = rb->sd;
+    storeCossStartMembuf(sd);
+    store_dirs_rebuilding--;
+    storeCossDirCloseTmpSwapLog(rb->sd);
+    storeRebuildComplete(&rb->counts);
+    cbdataFree(rb);
+}
+
+static void
+storeCossRebuildFromSwapLog(void *data)
+{
+    RebuildState *rb = (RebuildState *)data;
+    StoreEntry *e = NULL;
+    storeSwapLogData s;
+    size_t ss = sizeof(storeSwapLogData);
+    int count;
+    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(47, 1) ("Done reading %s swaplog (%d entries)\n",
+		rb->sd->path, rb->n_read);
+	    fclose(rb->log);
+	    rb->log = NULL;
+	    storeCossRebuildComplete(rb);
+	    return;
+	}
+	rb->n_read++;
+	if (s.op <= SWAP_LOG_NOP)
+	    continue;
+	if (s.op >= SWAP_LOG_MAX)
+	    continue;
+	debug(47, 3) ("storeCossRebuildFromSwapLog: %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) {
+		    e->swap_filen = -1;
+		}
+		storeRelease(e);
+		/* Fake an unlink here, this is a bad hack :( */
+		storeCossRemove(rb->sd, 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(47, 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(rb->sd->index,
+		    (int) sb.st_size / ss, rb->n_read);
+	}
+	if (EBIT_TEST(s.flags, KEY_PRIVATE)) {
+	    rb->counts.badflags++;
+	    continue;
+	}
+	e = storeGet(s.key);
+	if (e) {
+	    /* key already exists, current entry is newer */
+	    /* keep old, ignore new */
+	    rb->counts.dupcount++;
+	    continue;
+	}
+	/* update store_swap_size */
+	rb->counts.objcount++;
+	e = storeCossAddDiskRestore(rb->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("storeCossRebuild", storeCossRebuildFromSwapLog, rb, 0.0, 1);
+}
+
+/* Add a new object to the cache with empty memory copy and pointer to disk
+ * use to rebuild store from disk. */
+static StoreEntry *
+storeCossAddDiskRestore(SwapDir * SD, const cache_key * key,
+    int file_number,
+    size_t swap_file_sz,
+    time_t expires,
+    time_t timestamp,
+    time_t lastref,
+    time_t lastmod,
+    u_int32_t refcount,
+    u_int16_t flags,
+    int clean)
+{
+    StoreEntry *e = NULL;
+    debug(47, 5) ("storeCossAddDiskRestore: %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;
+    e->swap_dirn = SD->index;
+    storeSetMemStatus(e, NOT_IN_MEMORY);
+    e->swap_status = SWAPOUT_DONE;
+    e->swap_filen = file_number;
+    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);
+    storeHashInsert(e, key);	/* do it after we clear KEY_PRIVATE */
+    storeCossAdd(SD, e);
+    e->swap_filen = storeCossAllocate(SD, e, COSS_ALLOC_NOTIFY);
+    return e;
+}
+
+CBDATA_TYPE(RebuildState);
+static void
+storeCossDirRebuild(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;
+    func = storeCossRebuildFromSwapLog;
+    rb->flags.clean = (unsigned int) clean;
+    /*
+     * If the swap.state file exists in the cache_dir, then
+     * we'll use storeCossRebuildFromSwapLog().
+     */
+    fp = storeCossDirOpenTmpSwapLog(sd, &clean, &zero);
+    debug(47, 1) ("Rebuilding COSS storage in %s (%s)\n",
+	sd->path, clean ? "CLEAN" : "DIRTY");
+    rb->log = fp;
+    store_dirs_rebuilding++;
+    if (!clean || fp == NULL) {
+	/* COSS cannot yet rebuild from a dirty state. If the log
+	 * is dirty then the COSS contents is thrown away.
+	 * Why? I guess it is because some contents will be lost,
+	 * and COSS cannot verify this..
+	 */
+	if (fp != NULL)
+	    fclose(fp);
+	/*
+	 * XXX Make sure we don't trigger an assertion if this is the first
+	 * storedir, since if we are, this call will cause storeRebuildComplete
+	 * to prematurely complete the rebuild process, and then some other
+	 * storedir will try to rebuild and eventually die.
+	 */
+	eventAdd("storeCossRebuildComplete", storeCossRebuildComplete, rb, 0.0, 0);
+	return;
+    }
+    eventAdd("storeCossRebuild", func, rb, 0.0, 1);
+}
+
+static void
+storeCossDirCloseTmpSwapLog(SwapDir * sd)
+{
+    CossInfo *cs = (CossInfo *) sd->fsdata;
+    char *swaplog_path = xstrdup(storeCossDirSwapLogFile(sd, NULL));
+    char *new_path = xstrdup(storeCossDirSwapLogFile(sd, ".new"));
+    int fd;
+    file_close(cs->swaplog_fd);
+#if defined (_SQUID_OS2_) || defined (_SQUID_CYGWIN_)
+    if (unlink(swaplog_path) < 0) {
+	debug(50, 0) ("%s: %s\n", swaplog_path, xstrerror());
+	fatal("storeCossDirCloseTmpSwapLog: unlink failed");
+    }
+#endif
+    if (xrename(new_path, swaplog_path) < 0) {
+	fatal("storeCossDirCloseTmpSwapLog: 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("storeCossDirCloseTmpSwapLog: Failed to open swap log.");
+    }
+    safe_free(swaplog_path);
+    safe_free(new_path);
+    cs->swaplog_fd = fd;
+    debug(47, 3) ("Cache COSS Dir #%d log opened on FD %d\n", sd->index, fd);
+}
+
+static FILE *
+storeCossDirOpenTmpSwapLog(SwapDir * sd, int *clean_flag, int *zero_flag)
+{
+    CossInfo *cs = (CossInfo *) sd->fsdata;
+    char *swaplog_path = xstrdup(storeCossDirSwapLogFile(sd, NULL));
+    char *clean_path = xstrdup(storeCossDirSwapLogFile(sd, ".last-clean"));
+    char *new_path = xstrdup(storeCossDirSwapLogFile(sd, ".new"));
+    struct stat log_sb;
+    struct stat clean_sb;
+    FILE *fp;
+    int fd;
+    if (stat(swaplog_path, &log_sb) < 0) {
+	debug(50, 1) ("Cache COSS 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 (cs->swaplog_fd >= 0)
+	file_close(cs->swaplog_fd);
+    /* open a write-only FD for the new log */
+    fd = file_open(new_path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY);
+    if (fd < 0) {
+	debug(50, 1) ("%s: %s\n", new_path, xstrerror());
+	fatal("storeDirOpenTmpSwapLog: Failed to open swap log.");
+    }
+    cs->swaplog_fd = fd;
+    /* open a read-only stream of the old log */
+    fp = fopen(swaplog_path, "rb");
+    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 *newLog;
+    char *cln;
+    char *outbuf;
+    off_t outbuf_offset;
+    int fd;
+    dlink_node *current;
+};
+
+#define CLEAN_BUF_SZ 16384
+/*
+ * Begin the process to write clean cache state.  For COSS 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
+storeCossDirWriteCleanStart(SwapDir * sd)
+{
+    CossInfo *cs = (CossInfo *) sd->fsdata;
+    struct _clean_state *state = (struct _clean_state *)xcalloc(1, sizeof(*state));
+#if HAVE_FCHMOD
+    struct stat sb;
+#endif
+    state->newLog = xstrdup(storeCossDirSwapLogFile(sd, ".clean"));
+    state->fd = file_open(state->newLog, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY);
+    if (state->fd < 0) {
+	xfree(state->newLog);
+	xfree(state);
+	return -1;
+    }
+    sd->log.clean.write = NULL;
+    sd->log.clean.state = NULL;
+    state->cur = xstrdup(storeCossDirSwapLogFile(sd, NULL));
+    state->cln = xstrdup(storeCossDirSwapLogFile(sd, ".last-clean"));
+    state->outbuf = (char *)xcalloc(CLEAN_BUF_SZ, 1);
+    state->outbuf_offset = 0;
+    unlink(state->cln);
+    state->current = cs->index.tail;
+    debug(50, 3) ("storeCOssDirWriteCleanLogs: opened %s, FD %d\n",
+	state->newLog, state->fd);
+#if HAVE_FCHMOD
+    if (stat(state->cur, &sb) == 0)
+	fchmod(state->fd, sb.st_mode);
+#endif
+    sd->log.clean.write = storeCossDirWriteCleanEntry;
+    sd->log.clean.state = state;
+
+    return 0;
+}
+
+static const StoreEntry *
+storeCossDirCleanLogNextEntry(SwapDir * sd)
+{
+    struct _clean_state *state = (struct _clean_state *)sd->log.clean.state;
+    const StoreEntry *entry;
+    if (!state)
+	return NULL;
+    if (!state->current)
+	return NULL;
+    entry = (const StoreEntry *) state->current->data;
+    state->current = state->current->prev;
+    return entry;
+}
+
+/*
+ * "write" an entry to the clean log file.
+ */
+static void
+storeCossDirWriteCleanEntry(SwapDir * sd, const StoreEntry * e)
+{
+    storeSwapLogData s;
+    static size_t ss = sizeof(storeSwapLogData);
+    struct _clean_state *state = (struct _clean_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 (FD_WRITE_METHOD(state->fd, state->outbuf, state->outbuf_offset) < 0) {
+	    debug(50, 0) ("storeCossDirWriteCleanLogs: %s: write: %s\n",
+		state->newLog, xstrerror());
+	    debug(50, 0) ("storeCossDirWriteCleanLogs: Current swap logfile not replaced.\n");
+	    file_close(state->fd);
+	    state->fd = -1;
+	    unlink(state->newLog);
+	    safe_free(state);
+	    sd->log.clean.state = NULL;
+	    sd->log.clean.write = NULL;
+	    return;
+	}
+	state->outbuf_offset = 0;
+    }
+}
+
+static void
+storeCossDirWriteCleanDone(SwapDir * sd)
+{
+    int fd;
+    struct _clean_state *state = (struct _clean_state *)sd->log.clean.state;
+    if (NULL == state)
+	return;
+    if (state->fd < 0)
+	return;
+    if (FD_WRITE_METHOD(state->fd, state->outbuf, state->outbuf_offset) < 0) {
+	debug(50, 0) ("storeCossDirWriteCleanLogs: %s: write: %s\n",
+	    state->newLog, xstrerror());
+	debug(50, 0) ("storeCossDirWriteCleanLogs: Current swap logfile "
+	    "not replaced.\n");
+	file_close(state->fd);
+	state->fd = -1;
+	unlink(state->newLog);
+    }
+    safe_free(state->outbuf);
+    /*
+     * You can't rename open files on Microsoft "operating systems"
+     * so we have to close before renaming.
+     */
+    storeCossDirCloseSwapLog(sd);
+    /* save the fd value for a later test */
+    fd = state->fd;
+    /* rename */
+    if (state->fd >= 0) {
+#if defined(_SQUID_OS2_) || defined (_SQUID_CYGWIN_)
+	file_close(state->fd);
+	state->fd = -1;
+	if (unlink(state->cur) < 0)
+	    debug(50, 0) ("storeCossDirWriteCleanLogs: unlinkd failed: %s, %s\n",
+		xstrerror(), state->cur);
+#endif
+	xrename(state->newLog, 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->newLog);
+    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
+storeCossDirSwapLog(const SwapDir * sd, const StoreEntry * e, int op)
+{
+    CossInfo *cs = (CossInfo *) sd->fsdata;
+    storeSwapLogData *s = (storeSwapLogData *)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(cs->swaplog_fd,
+	-1,
+	s,
+	sizeof(storeSwapLogData),
+	NULL,
+	NULL,
+	(FREE *) storeSwapLogDataFree);
+}
+
+static void
+storeCossDirNewfs(SwapDir * sd)
+{
+    debug(47, 3) ("Creating swap space in %s\n", sd->path);
+}
+
+/* we are shutting down, flush all membufs to disk */
+static void
+storeCossDirShutdown(SwapDir * SD)
+{
+    CossInfo *cs = (CossInfo *) SD->fsdata;
+
+    storeCossSync(SD);		/* This'll call a_file_syncqueue() */
+    a_file_closequeue(&cs->aq);
+    file_close(cs->fd);
+    cs->fd = -1;
+
+    if (cs->swaplog_fd > -1) {
+	file_close(cs->swaplog_fd);
+	cs->swaplog_fd = -1;
+    }
+    n_coss_dirs--;
+}
+
+/*
+ * storeCossDirCheckObj
+ *
+ * This routine is called by storeDirSelectSwapDir to see if the given
+ * object is able to be stored on this filesystem. COSS filesystems will
+ * not store everything. We don't check for maxobjsize here since its
+ * done by the upper layers.
+ */
+int
+storeCossDirCheckObj(SwapDir * SD, const StoreEntry * e)
+{
+    CossInfo *cs = (CossInfo *) SD->fsdata;
+    int loadav;
+
+    /* Check if the object is a special object, we can't cache these */
+    if (EBIT_TEST(e->flags, ENTRY_SPECIAL))
+	return -1;
+
+    /* Otherwise, we're ok */
+    /* Return load, cs->aq.aq_numpending out of MAX_ASYNCOP */
+    loadav = cs->aq.aq_numpending * 1000 / MAX_ASYNCOP;
+    return loadav;
+}
+
+
+/*
+ * storeCossDirCallback - do the IO completions
+ */
+static int
+storeCossDirCallback(SwapDir * SD)
+{
+    CossInfo *cs = (CossInfo *) SD->fsdata;
+
+    return a_file_callback(&cs->aq);
+}
+
+/* ========== LOCAL FUNCTIONS ABOVE, GLOBAL FUNCTIONS BELOW ========== */
+
+static void
+storeCossDirStats(SwapDir * SD, StoreEntry * sentry)
+{
+    CossInfo *cs = (CossInfo *) SD->fsdata;
+
+    storeAppendPrintf(sentry, "\n");
+    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, "Number of object collisions: %d\n", (int) cs->numcollisions);
+#if 0
+    /* is this applicable? I Hope not .. */
+    storeAppendPrintf(sentry, "Filemap bits in use: %d of %d (%d%%)\n",
+	SD->map->n_files_in_map, SD->map->max_n_files,
+	percent(SD->map->n_files_in_map, SD->map->max_n_files));
+#endif
+    storeAppendPrintf(sentry, "Pending operations: %d out of %d\n", cs->aq.aq_numpending, MAX_ASYNCOP);
+    storeAppendPrintf(sentry, "Flags:");
+    if (SD->flags.selected)
+	storeAppendPrintf(sentry, " SELECTED");
+    if (SD->flags.read_only)
+	storeAppendPrintf(sentry, " READ-ONLY");
+    storeAppendPrintf(sentry, "\n");
+}
+
+static void
+storeCossDirParse(SwapDir * sd, int index, char *path)
+{
+    unsigned int i;
+    unsigned int size;
+    CossInfo *cs;
+
+    i = GetInteger();
+    size = i << 10;		/* Mbytes to Kbytes */
+    if (size <= 0)
+	fatal("storeCossDirParse: invalid size value");
+
+    cs = (CossInfo *)xmalloc(sizeof(CossInfo));
+    if (cs == NULL)
+	fatal("storeCossDirParse: couldn't xmalloc() CossInfo!\n");
+
+    sd->index = index;
+    sd->path = xstrdup(path);
+    sd->max_size = size;
+    sd->fsdata = cs;
+
+    cs->fd = -1;
+    cs->swaplog_fd = -1;
+
+    sd->init = storeCossDirInit;
+    sd->newfs = storeCossDirNewfs;
+    sd->dump = storeCossDirDump;
+    sd->freefs = storeCossDirShutdown;
+    sd->dblcheck = NULL;
+    sd->statfs = storeCossDirStats;
+    sd->maintainfs = NULL;
+    sd->checkobj = storeCossDirCheckObj;
+    sd->refobj = NULL;		/* LRU is done in storeCossRead */
+    sd->unrefobj = NULL;
+    sd->callback = storeCossDirCallback;
+    sd->sync = storeCossSync;
+
+    sd->obj.create = storeCossCreate;
+    sd->obj.open = storeCossOpen;
+    sd->obj.close = storeCossClose;
+    sd->obj.read = storeCossRead;
+    sd->obj.write = storeCossWrite;
+    sd->obj.unlink = storeCossUnlink;
+
+    sd->log.open = storeCossDirOpenSwapLog;
+    sd->log.close = storeCossDirCloseSwapLog;
+    sd->log.write = storeCossDirSwapLog;
+    sd->log.clean.start = storeCossDirWriteCleanStart;
+    sd->log.clean.write = storeCossDirWriteCleanEntry;
+    sd->log.clean.nextentry = storeCossDirCleanLogNextEntry;
+    sd->log.clean.done = storeCossDirWriteCleanDone;
+
+    cs->current_offset = 0;
+    cs->fd = -1;
+    cs->swaplog_fd = -1;
+    cs->numcollisions = 0;
+    cs->membufs.head = cs->membufs.tail = NULL;		/* set when the rebuild completes */
+    cs->current_membuf = NULL;
+    cs->index.head = NULL;
+    cs->index.tail = NULL;
+
+    parse_cachedir_options(sd, NULL, 0);
+    /* Enforce maxobjsize being set to something */
+    if (sd->max_objsize == -1)
+	fatal("COSS requires max-size to be set to something other than -1!\n");
+}
+
+
+static void
+storeCossDirReconfigure(SwapDir * sd, int index, char *path)
+{
+    unsigned int i;
+    unsigned int size;
+
+    i = GetInteger();
+    size = i << 10;		/* Mbytes to Kbytes */
+    if (size <= 0)
+	fatal("storeCossDirParse: invalid size value");
+
+    if (size == (size_t)sd->max_size)
+	debug(3, 1) ("Cache COSS dir '%s' size remains unchanged at %d KB\n", path, size);
+    else {
+	debug(3, 1) ("Cache COSS dir '%s' size changed to %d KB\n", path, size);
+	sd->max_size = size;
+    }
+    parse_cachedir_options(sd, NULL, 1);
+    /* Enforce maxobjsize being set to something */
+    if (sd->max_objsize == -1)
+	fatal("COSS requires max-size to be set to something other than -1!\n");
+}
+
+void
+storeCossDirDump(StoreEntry * entry, SwapDir * s)
+{
+    storeAppendPrintf(entry, " %d",
+	s->max_size >> 20);
+    dump_cachedir_options(entry, NULL, s);
+}
+
+#if OLD_UNUSED_CODE
+SwapDir *
+storeCossDirPick(void)
+{
+    int i, choosenext = 0;
+    SwapDir *SD;
+
+    if (n_coss_dirs == 0)
+	return NULL;
+    for (i = 0; i < Config.cacheSwap.n_configured; i++) {
+	SD = &Config.cacheSwap.swapDirs[i];
+	if (SD->type == SWAPDIR_COSS) {
+	    if ((last_coss_pick_index == -1) || (n_coss_dirs == 1)) {
+		last_coss_pick_index = i;
+		return SD;
+	    } else if (choosenext) {
+		last_coss_pick_index = i;
+		return SD;
+	    } else if (last_coss_pick_index == i) {
+		choosenext = 1;
+	    }
+	}
+    }
+    for (i = 0; i < Config.cacheSwap.n_configured; i++) {
+	SD = &Config.cacheSwap.swapDirs[i];
+	if (SD->type == SWAPDIR_COSS) {
+	    if ((last_coss_pick_index == -1) || (n_coss_dirs == 1)) {
+		last_coss_pick_index = i;
+		return SD;
+	    } else if (choosenext) {
+		last_coss_pick_index = i;
+		return SD;
+	    } else if (last_coss_pick_index == i) {
+		choosenext = 1;
+	    }
+	}
+    }
+    return NULL;
+}
+#endif
+
+/*
+ * initial setup/done code
+ */
+static void
+storeCossDirDone(void)
+{
+    memPoolDestroy(&coss_state_pool);
+/*  memPoolDestroy(&coss_index_pool);  XXX Should be here? */
+    coss_initialised = 0;
+}
+
+void
+storeFsSetup_coss(storefs_entry_t * storefs)
+{
+    assert(!coss_initialised);
+
+    storefs->parsefunc = storeCossDirParse;
+    storefs->reconfigurefunc = storeCossDirReconfigure;
+    storefs->donefunc = storeCossDirDone;
+    coss_state_pool = memPoolCreate("COSS IO State data", sizeof(CossState));
+    coss_index_pool = memPoolCreate("COSS index data", sizeof(CossIndexNode));
+    coss_initialised = 1;
+}
--- squid/src/fs/coss/store_io_coss.c	Wed Feb 14 01:07:42 2007
+++ /dev/null	Wed Feb 14 01:07:22 2007
@@ -1,568 +0,0 @@
-
-/*
- * $Id$
- *
- * DEBUG: section 79    Storage Manager COSS Interface
- * AUTHOR: Eric Stern
- *
- * 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.h"
-#include <aio.h>
-#include "async_io.h"
-#include "store_coss.h"
-
-static DWCB storeCossWriteMemBufDone;
-static DRCB storeCossReadDone;
-static void storeCossIOCallback(storeIOState * sio, int errflag);
-static char *storeCossMemPointerFromDiskOffset(SwapDir * SD, size_t offset, CossMemBuf ** mb);
-static void storeCossMemBufLock(SwapDir * SD, storeIOState * e);
-static void storeCossMemBufUnlock(SwapDir * SD, storeIOState * e);
-static void storeCossWriteMemBuf(SwapDir * SD, CossMemBuf * t);
-static void storeCossWriteMemBufDone(int fd, int errflag, size_t len, void *my_data);
-static CossMemBuf *storeCossCreateMemBuf(SwapDir * SD, size_t start,
-    sfileno curfn, int *collision);
-static CBDUNL storeCossIOFreeEntry;
-
-CBDATA_TYPE(storeIOState);
-CBDATA_TYPE(CossMemBuf);
-
-/* === PUBLIC =========================================================== */
-
-/*
- * This routine sucks. I want to rewrite it when possible, and I also think
- * that we should check after creatmembuf() to see if the object has a
- * RELEASE_REQUEST set on it (thanks Eric!) rather than this way which seems
- * to work..
- * -- Adrian
- */
-off_t
-storeCossAllocate(SwapDir * SD, const StoreEntry * e, int which)
-{
-    CossInfo *cs = (CossInfo *) SD->fsdata;
-    CossMemBuf *newmb;
-    off_t retofs;
-    size_t allocsize;
-    int coll = 0;
-    sfileno checkf;
-
-    /* Make sure we chcek collisions if reallocating */
-    if (which == COSS_ALLOC_REALLOC)
-	checkf = e->swap_filen;
-    else
-	checkf = -1;
-
-    retofs = e->swap_filen;	/* Just for defaults, or while rebuilding */
-
-    if (e->swap_file_sz > 0)
-	allocsize = e->swap_file_sz;
-    else
-	allocsize = objectLen(e) + e->mem_obj->swap_hdr_sz;
-
-    /* Since we're not supporting NOTIFY anymore, lets fail */
-    assert(which != COSS_ALLOC_NOTIFY);
-
-    /* Check if we have overflowed the disk .. */
-    if ((cs->current_offset + allocsize) > (SD->max_size << 10)) {
-	/*
-	 * tried to allocate past the end of the disk, so wrap
-	 * back to the beginning
-	 */
-	cs->current_membuf->flags.full = 1;
-	cs->current_membuf->diskend = cs->current_offset - 1;
-	cs->current_offset = 0;	/* wrap back to beginning */
-	debug(79, 2) ("storeCossAllocate: wrap to 0\n");
-
-	newmb = storeCossCreateMemBuf(SD, 0, checkf, &coll);
-	cs->current_membuf = newmb;
-
-	/* Check if we have overflowed the MemBuf */
-    } else if ((cs->current_offset + allocsize) > cs->current_membuf->diskend) {
-	/*
-	 * Skip the blank space at the end of the stripe. start over.
-	 */
-	cs->current_membuf->flags.full = 1;
-	cs->current_offset = cs->current_membuf->diskend + 1;
-	debug(79, 2) ("storeCossAllocate: New offset - %ld\n",
-	    (long int) cs->current_offset);
-	newmb = storeCossCreateMemBuf(SD, cs->current_offset, checkf, &coll);
-	cs->current_membuf = newmb;
-    }
-    /* If we didn't get a collision, then update the current offset and return it */
-    if (coll == 0) {
-	retofs = cs->current_offset;
-	cs->current_offset = retofs + allocsize;
-	return retofs;
-    } else {
-	debug(79, 3) ("storeCossAllocate: Collision\n");
-	return -1;
-    }
-}
-
-void
-storeCossUnlink(SwapDir * SD, StoreEntry * e)
-{
-    debug(79, 3) ("storeCossUnlink: offset %d\n", e->swap_filen);
-    storeCossRemove(SD, e);
-}
-
-
-storeIOState *
-storeCossCreate(SwapDir * SD, StoreEntry * e, STFNCB * file_callback, STIOCB * callback, void *callback_data)
-{
-    CossState *cstate;
-    storeIOState *sio;
-
-    CBDATA_INIT_TYPE_FREECB(storeIOState, storeCossIOFreeEntry);
-    sio = cbdataAlloc(storeIOState);
-    cstate = memPoolAlloc(coss_state_pool);
-    sio->fsstate = cstate;
-    sio->offset = 0;
-    sio->mode = O_WRONLY | O_BINARY;
-
-    /*
-     * If we get handed an object with a size of -1,
-     * the squid code is broken
-     */
-    assert(e->mem_obj->object_sz != -1);
-
-    /*
-     * this one is kinda strange - Eric called storeCossAllocate(), then
-     * storeCossOpen(O_RDONLY) .. weird. Anyway, I'm allocating this now.
-     */
-    sio->st_size = objectLen(e) + e->mem_obj->swap_hdr_sz;
-    sio->swap_dirn = SD->index;
-    sio->swap_filen = storeCossAllocate(SD, e, COSS_ALLOC_ALLOCATE);
-    debug(79, 3) ("storeCossCreate: offset %d, size %ld, end %ld\n", sio->swap_filen, (long int) sio->st_size, (long int) (sio->swap_filen + sio->st_size));
-
-    sio->callback = callback;
-    sio->file_callback = file_callback;
-    sio->callback_data = cbdataReference(callback_data);
-    sio->e = (StoreEntry *) e;
-
-    cstate->flags.writing = 0;
-    cstate->flags.reading = 0;
-    cstate->readbuffer = NULL;
-    cstate->reqdiskoffset = -1;
-
-    /* Now add it into the index list */
-    storeCossAdd(SD, e);
-
-    storeCossMemBufLock(SD, sio);
-    return sio;
-}
-
-storeIOState *
-storeCossOpen(SwapDir * SD, StoreEntry * e, STFNCB * file_callback,
-    STIOCB * callback, void *callback_data)
-{
-    storeIOState *sio;
-    char *p;
-    CossState *cstate;
-    sfileno f = e->swap_filen;
-    CossInfo *cs = (CossInfo *) SD->fsdata;
-
-    debug(79, 3) ("storeCossOpen: offset %d\n", f);
-
-    CBDATA_INIT_TYPE_FREECB(storeIOState, storeCossIOFreeEntry);
-    sio = cbdataAlloc(storeIOState);
-    cstate = memPoolAlloc(coss_state_pool);
-
-    sio->fsstate = cstate;
-    sio->swap_filen = f;
-    sio->swap_dirn = SD->index;
-    sio->offset = 0;
-    sio->mode = O_RDONLY | O_BINARY;
-    sio->callback = callback;
-    sio->file_callback = file_callback;
-    sio->callback_data = cbdataReference(callback_data);
-    sio->st_size = e->swap_file_sz;
-    sio->e = e;
-
-    cstate->flags.writing = 0;
-    cstate->flags.reading = 0;
-    cstate->readbuffer = NULL;
-    cstate->reqdiskoffset = -1;
-    p = storeCossMemPointerFromDiskOffset(SD, f, NULL);
-    /* make local copy so we don't have to lock membuf */
-    if (p) {
-	cstate->readbuffer = xmalloc(sio->st_size);
-	xmemcpy(cstate->readbuffer, p, sio->st_size);
-    } else {
-	/* Do the allocation */
-	/* this is the first time we've been called on a new sio
-	 * read the whole object into memory, then return the 
-	 * requested amount
-	 */
-	/*
-	 * This bit of code actually does the LRU disk thing - we realloc
-	 * a place for the object here, and the file_read() reads the object
-	 * into the cossmembuf for later writing ..
-	 */
-	cstate->reqdiskoffset = sio->swap_filen;
-	sio->swap_filen = -1;
-	sio->swap_filen = storeCossAllocate(SD, e, COSS_ALLOC_REALLOC);
-	if (sio->swap_filen == -1) {
-	    /* We have to clean up neatly .. */
-	    cbdataFree(sio);
-	    cs->numcollisions++;
-	    debug(79, 2) ("storeCossOpen: Reallocation of %d/%d failed\n", e->swap_dirn, e->swap_filen);
-	    /* XXX XXX XXX Will squid call storeUnlink for this object? */
-	    return NULL;
-	}
-	/* Notify the upper levels that we've changed file number */
-	sio->file_callback(sio->callback_data, 0, sio);
-
-	/*
-	 * lock the buffer so it doesn't get swapped out on us
-	 * this will get unlocked in storeCossReadDone
-	 */
-	storeCossMemBufLock(SD, sio);
-
-	/*
-	 * Do the index magic to keep the disk and memory LRUs identical
-	 */
-	storeCossRemove(SD, e);
-	storeCossAdd(SD, e);
-
-	/*
-	 * Since we've reallocated a spot for this object, we need to
-	 * write it to the cossmembuf *and* return it in the read ..
-	 */
-	cstate->readbuffer = NULL;
-    }
-    return sio;
-}
-
-void
-storeCossClose(SwapDir * SD, storeIOState * sio)
-{
-    debug(79, 3) ("storeCossClose: offset %d\n", sio->swap_filen);
-    if (FILE_MODE(sio->mode) == O_WRONLY)
-	storeCossMemBufUnlock(SD, sio);
-    storeCossIOCallback(sio, 0);
-}
-
-void
-storeCossRead(SwapDir * SD, storeIOState * sio, char *buf, size_t size, off_t offset, STRCB * callback, void *callback_data)
-{
-    char *p;
-    CossState *cstate = (CossState *) sio->fsstate;
-    CossInfo *cs = (CossInfo *) SD->fsdata;
-
-    assert(sio->read.callback == NULL);
-    assert(sio->read.callback_data == NULL);
-    sio->read.callback = callback;
-    sio->read.callback_data = cbdataReference(callback_data);
-    debug(79, 3) ("storeCossRead: offset %ld\n", (long int) offset);
-    sio->offset = offset;
-    cstate->flags.reading = 1;
-    if ((offset + size) > sio->st_size)
-	size = sio->st_size - offset;
-    cstate->requestlen = size;
-    cstate->requestbuf = buf;
-    cstate->requestoffset = offset;
-    if (cstate->readbuffer == NULL) {
-	p = storeCossMemPointerFromDiskOffset(SD, sio->swap_filen, NULL);
-	/* Remember we need to translate the block offset to a disk offset! */
-	a_file_read(&cs->aq, cs->fd,
-	    p,
-	    sio->st_size,
-	    cstate->reqdiskoffset,
-	    storeCossReadDone,
-	    sio);
-	cstate->reqdiskoffset = 0;	/* XXX */
-    } else {
-	storeCossReadDone(cs->fd,
-	    cstate->readbuffer,
-	    sio->st_size,
-	    0,
-	    sio);
-    }
-}
-
-void
-storeCossWrite(SwapDir * SD, storeIOState * sio, char *buf, size_t size, off_t offset, FREE * free_func)
-{
-    char *dest;
-    CossMemBuf *membuf;
-    off_t diskoffset;
-
-    /*
-     * If we get handed an object with a size of -1,
-     * the squid code is broken
-     */
-    assert(sio->e->mem_obj->object_sz != -1);
-
-    debug(79, 3) ("storeCossWrite: offset %ld, len %lu\n", (long int) sio->offset, (unsigned long int) size);
-    diskoffset = sio->swap_filen + sio->offset;
-    dest = storeCossMemPointerFromDiskOffset(SD, diskoffset, &membuf);
-    assert(dest != NULL);
-    xmemcpy(dest, buf, size);
-    sio->offset += size;
-    if (free_func)
-	(free_func) (buf);
-}
-
-
-/*  === STATIC =========================================================== */
-
-static void
-storeCossReadDone(int fd, const char *buf, int len, int errflag, void *my_data)
-{
-    storeIOState *sio = my_data;
-    char *p;
-    STRCB *callback = sio->read.callback;
-    void *cbdata;
-    SwapDir *SD = INDEXSD(sio->swap_dirn);
-    CossState *cstate = (CossState *) sio->fsstate;
-    size_t rlen;
-
-    debug(79, 3) ("storeCossReadDone: fileno %d, FD %d, len %d\n",
-	sio->swap_filen, fd, len);
-    cstate->flags.reading = 0;
-    if (errflag) {
-	debug(79, 3) ("storeCossReadDone: got failure (%d)\n", errflag);
-	rlen = -1;
-    } else {
-	if (cstate->readbuffer == NULL) {
-	    cstate->readbuffer = xmalloc(sio->st_size);
-	    p = storeCossMemPointerFromDiskOffset(SD, sio->swap_filen, NULL);
-	    xmemcpy(cstate->readbuffer, p, sio->st_size);
-	    storeCossMemBufUnlock(SD, sio);
-	}
-	sio->offset += len;
-	xmemcpy(cstate->requestbuf, &cstate->readbuffer[cstate->requestoffset],
-	    cstate->requestlen);
-	rlen = (size_t) cstate->requestlen;
-    }
-    assert(callback);
-    sio->read.callback = NULL;
-    if (cbdataReferenceValidDone(sio->read.callback_data, &cbdata))
-	callback(cbdata, cstate->requestbuf, rlen);
-}
-
-static void
-storeCossIOCallback(storeIOState * sio, int errflag)
-{
-    CossState *cstate = (CossState *) sio->fsstate;
-    STIOCB *callback = sio->callback;
-    void *cbdata;
-    debug(79, 3) ("storeCossIOCallback: errflag=%d\n", errflag);
-    xfree(cstate->readbuffer);
-    sio->callback = NULL;
-    if (cbdataReferenceValidDone(sio->callback_data, &cbdata))
-	callback(cbdata, errflag, sio);
-    cbdataFree(sio);
-}
-
-static char *
-storeCossMemPointerFromDiskOffset(SwapDir * SD, size_t offset, CossMemBuf ** mb)
-{
-    CossMemBuf *t;
-    dlink_node *m;
-    CossInfo *cs = (CossInfo *) SD->fsdata;
-
-    for (m = cs->membufs.head; m; m = m->next) {
-	t = m->data;
-	if ((offset >= t->diskstart) && (offset <= t->diskend)) {
-	    if (mb)
-		*mb = t;
-	    return &t->buffer[offset - t->diskstart];
-	}
-    }
-
-    if (mb)
-	*mb = NULL;
-    return NULL;
-}
-
-static void
-storeCossMemBufLock(SwapDir * SD, storeIOState * e)
-{
-    CossMemBuf *t;
-    dlink_node *m;
-    CossInfo *cs = (CossInfo *) SD->fsdata;
-
-    for (m = cs->membufs.head; m; m = m->next) {
-	t = m->data;
-	if ((e->swap_filen >= t->diskstart) && (e->swap_filen <= t->diskend)) {
-	    debug(79, 3) ("storeCossMemBufLock: locking %p, lockcount %d\n", t, t->lockcount);
-	    t->lockcount++;
-	    return;
-	}
-    }
-    debug(79, 3) ("storeCossMemBufLock: FAILED to lock %p\n", e);
-}
-
-static void
-storeCossMemBufUnlock(SwapDir * SD, storeIOState * e)
-{
-    CossMemBuf *t;
-    dlink_node *m, *n;
-    CossInfo *cs = (CossInfo *) SD->fsdata;
-
-    for (m = cs->membufs.head; m; m = n) {
-	/*
-	 * Note that storeCossWriteMemBuf() might call storeCossWriteMemBufDone
-	 * immediately (if the write finishes immediately, of course!) which
-	 * will make m = m->next kinda unworkable. So, get the next pointer.
-	 */
-	n = m->next;
-	t = m->data;
-	if ((e->swap_filen >= t->diskstart) && (e->swap_filen <= t->diskend)) {
-	    t->lockcount--;
-	    debug(79, 3) ("storeCossMemBufUnlock: unlocking %p, lockcount %d\n", t, t->lockcount);
-	}
-	if (t->flags.full && !t->flags.writing && !t->lockcount)
-	    storeCossWriteMemBuf(SD, t);
-    }
-}
-
-void
-storeCossSync(SwapDir * SD)
-{
-    CossInfo *cs = (CossInfo *) SD->fsdata;
-    CossMemBuf *t;
-    dlink_node *m;
-    int end;
-
-    /* First, flush pending IO ops */
-    a_file_syncqueue(&cs->aq);
-
-    /* Then, flush any in-memory partial membufs */
-    if (!cs->membufs.head)
-	return;
-    for (m = cs->membufs.head; m; m = m->next) {
-	t = m->data;
-	if (t->flags.writing)
-	    sleep(5);		/* XXX EEEWWW! */
-	lseek(cs->fd, t->diskstart, SEEK_SET);
-	end = (t == cs->current_membuf) ? cs->current_offset : t->diskend;
-	FD_WRITE_METHOD(cs->fd, t->buffer, end - t->diskstart);
-    }
-}
-
-static void
-storeCossWriteMemBuf(SwapDir * SD, CossMemBuf * t)
-{
-    CossInfo *cs = (CossInfo *) SD->fsdata;
-    debug(79, 3) ("storeCossWriteMemBuf: offset %ld, len %ld\n",
-	(long int) t->diskstart, (long int) (t->diskend - t->diskstart));
-    t->flags.writing = 1;
-    /* Remember that diskstart/diskend are block offsets! */
-    a_file_write(&cs->aq, cs->fd, t->diskstart, &t->buffer,
-	t->diskend - t->diskstart, storeCossWriteMemBufDone, t, NULL);
-}
-
-
-static void
-storeCossWriteMemBufDone(int fd, int errflag, size_t len, void *my_data)
-{
-    CossMemBuf *t = my_data;
-    CossInfo *cs = (CossInfo *) t->SD->fsdata;
-
-    debug(79, 3) ("storeCossWriteMemBufDone: buf %p, len %ld\n", t, (long int) len);
-    if (errflag)
-	debug(79, 0) ("storeCossMemBufWriteDone: got failure (%d)\n", errflag);
-
-    dlinkDelete(&t->node, &cs->membufs);
-    cbdataFree(t);
-}
-
-static CossMemBuf *
-storeCossCreateMemBuf(SwapDir * SD, size_t start,
-    sfileno curfn, int *collision)
-{
-    CossMemBuf *newmb, *t;
-    StoreEntry *e;
-    dlink_node *m, *prev;
-    int numreleased = 0;
-    CossInfo *cs = (CossInfo *) SD->fsdata;
-
-    CBDATA_INIT_TYPE_FREECB(CossMemBuf, NULL);
-    newmb = cbdataAlloc(CossMemBuf);
-    newmb->diskstart = start;
-    debug(79, 3) ("storeCossCreateMemBuf: creating new membuf at %ld\n", (long int) newmb->diskstart);
-    debug(79, 3) ("storeCossCreateMemBuf: at %p\n", newmb);
-    newmb->diskend = newmb->diskstart + COSS_MEMBUF_SZ - 1;
-    newmb->flags.full = 0;
-    newmb->flags.writing = 0;
-    newmb->lockcount = 0;
-    newmb->SD = SD;
-    /* XXX This should be reversed, with the new buffer last in the chain */
-    dlinkAdd(newmb, &newmb->node, &cs->membufs);
-
-    /* Print out the list of membufs */
-    for (m = cs->membufs.head; m; m = m->next) {
-	t = m->data;
-	debug(79, 3) ("storeCossCreateMemBuf: membuflist %ld lockcount %d\n", (long int) t->diskstart, t->lockcount);
-    }
-
-    /*
-     * Kill objects from the tail to make space for a new chunk
-     */
-    for (m = cs->index.tail; m; m = prev) {
-	prev = m->prev;
-	e = m->data;
-	if (curfn == e->swap_filen)
-	    *collision = 1;	/* Mark an object alloc collision */
-	if ((e->swap_filen >= newmb->diskstart) &&
-	    (e->swap_filen <= newmb->diskend)) {
-	    storeRelease(e);
-	    numreleased++;
-	} else
-	    break;
-    }
-    if (numreleased > 0)
-	debug(79, 3) ("storeCossCreateMemBuf: this allocation released %d storeEntries\n", numreleased);
-    return newmb;
-}
-
-/*
- * Creates the initial membuf after rebuild
- */
-void
-storeCossStartMembuf(SwapDir * sd)
-{
-    CossInfo *cs = (CossInfo *) sd->fsdata;
-    CossMemBuf *newmb = storeCossCreateMemBuf(sd, cs->current_offset, -1, NULL);
-    assert(!cs->current_membuf);
-    cs->current_membuf = newmb;
-}
-
-/*
- * Clean up any references from the SIO before it get's released.
- */
-static void
-storeCossIOFreeEntry(void *sio)
-{
-    memPoolFree(coss_state_pool, ((storeIOState *) sio)->fsstate);
-}
--- /dev/null	Wed Feb 14 01:07:22 2007
+++ squid/src/fs/coss/store_io_coss.cc	Wed Feb 14 01:07:42 2007
@@ -0,0 +1,568 @@
+
+/*
+ * $Id: store_io_coss.cc,v 1.1.2.1 2002/10/12 12:21:43 rbcollins Exp $
+ *
+ * DEBUG: section 79    Storage Manager COSS Interface
+ * AUTHOR: Eric Stern
+ *
+ * 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.h"
+#include <aio.h>
+#include "async_io.h"
+#include "store_coss.h"
+
+static DWCB storeCossWriteMemBufDone;
+static DRCB storeCossReadDone;
+static void storeCossIOCallback(storeIOState * sio, int errflag);
+static char *storeCossMemPointerFromDiskOffset(SwapDir * SD, size_t offset, CossMemBuf ** mb);
+static void storeCossMemBufLock(SwapDir * SD, storeIOState * e);
+static void storeCossMemBufUnlock(SwapDir * SD, storeIOState * e);
+static void storeCossWriteMemBuf(SwapDir * SD, CossMemBuf * t);
+static void storeCossWriteMemBufDone(int fd, int errflag, size_t len, void *my_data);
+static CossMemBuf *storeCossCreateMemBuf(SwapDir * SD, size_t start,
+    sfileno curfn, int *collision);
+static CBDUNL storeCossIOFreeEntry;
+
+CBDATA_TYPE(storeIOState);
+CBDATA_TYPE(CossMemBuf);
+
+/* === PUBLIC =========================================================== */
+
+/*
+ * This routine sucks. I want to rewrite it when possible, and I also think
+ * that we should check after creatmembuf() to see if the object has a
+ * RELEASE_REQUEST set on it (thanks Eric!) rather than this way which seems
+ * to work..
+ * -- Adrian
+ */
+off_t
+storeCossAllocate(SwapDir * SD, const StoreEntry * e, int which)
+{
+    CossInfo *cs = (CossInfo *) SD->fsdata;
+    CossMemBuf *newmb;
+    off_t retofs;
+    size_t allocsize;
+    int coll = 0;
+    sfileno checkf;
+
+    /* Make sure we chcek collisions if reallocating */
+    if (which == COSS_ALLOC_REALLOC)
+	checkf = e->swap_filen;
+    else
+	checkf = -1;
+
+    retofs = e->swap_filen;	/* Just for defaults, or while rebuilding */
+
+    if (e->swap_file_sz > 0)
+	allocsize = e->swap_file_sz;
+    else
+	allocsize = objectLen(e) + e->mem_obj->swap_hdr_sz;
+
+    /* Since we're not supporting NOTIFY anymore, lets fail */
+    assert(which != COSS_ALLOC_NOTIFY);
+
+    /* Check if we have overflowed the disk .. */
+    if ((cs->current_offset + allocsize) > (size_t)(SD->max_size << 10)) {
+	/*
+	 * tried to allocate past the end of the disk, so wrap
+	 * back to the beginning
+	 */
+	cs->current_membuf->flags.full = 1;
+	cs->current_membuf->diskend = cs->current_offset - 1;
+	cs->current_offset = 0;	/* wrap back to beginning */
+	debug(79, 2) ("storeCossAllocate: wrap to 0\n");
+
+	newmb = storeCossCreateMemBuf(SD, 0, checkf, &coll);
+	cs->current_membuf = newmb;
+
+	/* Check if we have overflowed the MemBuf */
+    } else if ((cs->current_offset + allocsize) > cs->current_membuf->diskend) {
+	/*
+	 * Skip the blank space at the end of the stripe. start over.
+	 */
+	cs->current_membuf->flags.full = 1;
+	cs->current_offset = cs->current_membuf->diskend + 1;
+	debug(79, 2) ("storeCossAllocate: New offset - %ld\n",
+	    (long int) cs->current_offset);
+	newmb = storeCossCreateMemBuf(SD, cs->current_offset, checkf, &coll);
+	cs->current_membuf = newmb;
+    }
+    /* If we didn't get a collision, then update the current offset and return it */
+    if (coll == 0) {
+	retofs = cs->current_offset;
+	cs->current_offset = retofs + allocsize;
+	return retofs;
+    } else {
+	debug(79, 3) ("storeCossAllocate: Collision\n");
+	return -1;
+    }
+}
+
+void
+storeCossUnlink(SwapDir * SD, StoreEntry * e)
+{
+    debug(79, 3) ("storeCossUnlink: offset %d\n", e->swap_filen);
+    storeCossRemove(SD, e);
+}
+
+
+storeIOState *
+storeCossCreate(SwapDir * SD, StoreEntry * e, STFNCB * file_callback, STIOCB * callback, void *callback_data)
+{
+    CossState *cstate;
+    storeIOState *sio;
+
+    CBDATA_INIT_TYPE_FREECB(storeIOState, storeCossIOFreeEntry);
+    sio = cbdataAlloc(storeIOState);
+    cstate = (CossState *)memPoolAlloc(coss_state_pool);
+    sio->fsstate = cstate;
+    sio->offset = 0;
+    sio->mode = O_WRONLY | O_BINARY;
+
+    /*
+     * If we get handed an object with a size of -1,
+     * the squid code is broken
+     */
+    assert(e->mem_obj->object_sz != -1);
+
+    /*
+     * this one is kinda strange - Eric called storeCossAllocate(), then
+     * storeCossOpen(O_RDONLY) .. weird. Anyway, I'm allocating this now.
+     */
+    sio->st_size = objectLen(e) + e->mem_obj->swap_hdr_sz;
+    sio->swap_dirn = SD->index;
+    sio->swap_filen = storeCossAllocate(SD, e, COSS_ALLOC_ALLOCATE);
+    debug(79, 3) ("storeCossCreate: offset %d, size %ld, end %ld\n", sio->swap_filen, (long int) sio->st_size, (long int) (sio->swap_filen + sio->st_size));
+
+    sio->callback = callback;
+    sio->file_callback = file_callback;
+    sio->callback_data = cbdataReference(callback_data);
+    sio->e = (StoreEntry *) e;
+
+    cstate->flags.writing = 0;
+    cstate->flags.reading = 0;
+    cstate->readbuffer = NULL;
+    cstate->reqdiskoffset = -1;
+
+    /* Now add it into the index list */
+    storeCossAdd(SD, e);
+
+    storeCossMemBufLock(SD, sio);
+    return sio;
+}
+
+storeIOState *
+storeCossOpen(SwapDir * SD, StoreEntry * e, STFNCB * file_callback,
+    STIOCB * callback, void *callback_data)
+{
+    storeIOState *sio;
+    char *p;
+    CossState *cstate;
+    sfileno f = e->swap_filen;
+    CossInfo *cs = (CossInfo *) SD->fsdata;
+
+    debug(79, 3) ("storeCossOpen: offset %d\n", f);
+
+    CBDATA_INIT_TYPE_FREECB(storeIOState, storeCossIOFreeEntry);
+    sio = cbdataAlloc(storeIOState);
+    cstate = (CossState *)memPoolAlloc(coss_state_pool);
+
+    sio->fsstate = cstate;
+    sio->swap_filen = f;
+    sio->swap_dirn = SD->index;
+    sio->offset = 0;
+    sio->mode = O_RDONLY | O_BINARY;
+    sio->callback = callback;
+    sio->file_callback = file_callback;
+    sio->callback_data = cbdataReference(callback_data);
+    sio->st_size = e->swap_file_sz;
+    sio->e = e;
+
+    cstate->flags.writing = 0;
+    cstate->flags.reading = 0;
+    cstate->readbuffer = NULL;
+    cstate->reqdiskoffset = -1;
+    p = storeCossMemPointerFromDiskOffset(SD, f, NULL);
+    /* make local copy so we don't have to lock membuf */
+    if (p) {
+	cstate->readbuffer = (char *)xmalloc(sio->st_size);
+	xmemcpy(cstate->readbuffer, p, sio->st_size);
+    } else {
+	/* Do the allocation */
+	/* this is the first time we've been called on a new sio
+	 * read the whole object into memory, then return the 
+	 * requested amount
+	 */
+	/*
+	 * This bit of code actually does the LRU disk thing - we realloc
+	 * a place for the object here, and the file_read() reads the object
+	 * into the cossmembuf for later writing ..
+	 */
+	cstate->reqdiskoffset = sio->swap_filen;
+	sio->swap_filen = -1;
+	sio->swap_filen = storeCossAllocate(SD, e, COSS_ALLOC_REALLOC);
+	if (sio->swap_filen == -1) {
+	    /* We have to clean up neatly .. */
+	    cbdataFree(sio);
+	    cs->numcollisions++;
+	    debug(79, 2) ("storeCossOpen: Reallocation of %d/%d failed\n", e->swap_dirn, e->swap_filen);
+	    /* XXX XXX XXX Will squid call storeUnlink for this object? */
+	    return NULL;
+	}
+	/* Notify the upper levels that we've changed file number */
+	sio->file_callback(sio->callback_data, 0, sio);
+
+	/*
+	 * lock the buffer so it doesn't get swapped out on us
+	 * this will get unlocked in storeCossReadDone
+	 */
+	storeCossMemBufLock(SD, sio);
+
+	/*
+	 * Do the index magic to keep the disk and memory LRUs identical
+	 */
+	storeCossRemove(SD, e);
+	storeCossAdd(SD, e);
+
+	/*
+	 * Since we've reallocated a spot for this object, we need to
+	 * write it to the cossmembuf *and* return it in the read ..
+	 */
+	cstate->readbuffer = NULL;
+    }
+    return sio;
+}
+
+void
+storeCossClose(SwapDir * SD, storeIOState * sio)
+{
+    debug(79, 3) ("storeCossClose: offset %d\n", sio->swap_filen);
+    if (FILE_MODE(sio->mode) == O_WRONLY)
+	storeCossMemBufUnlock(SD, sio);
+    storeCossIOCallback(sio, 0);
+}
+
+void
+storeCossRead(SwapDir * SD, storeIOState * sio, char *buf, size_t size, off_t offset, STRCB * callback, void *callback_data)
+{
+    char *p;
+    CossState *cstate = (CossState *) sio->fsstate;
+    CossInfo *cs = (CossInfo *) SD->fsdata;
+
+    assert(sio->read.callback == NULL);
+    assert(sio->read.callback_data == NULL);
+    sio->read.callback = callback;
+    sio->read.callback_data = cbdataReference(callback_data);
+    debug(79, 3) ("storeCossRead: offset %ld\n", (long int) offset);
+    sio->offset = offset;
+    cstate->flags.reading = 1;
+    if ((offset + size) > sio->st_size)
+	size = sio->st_size - offset;
+    cstate->requestlen = size;
+    cstate->requestbuf = buf;
+    cstate->requestoffset = offset;
+    if (cstate->readbuffer == NULL) {
+	p = storeCossMemPointerFromDiskOffset(SD, sio->swap_filen, NULL);
+	/* Remember we need to translate the block offset to a disk offset! */
+	a_file_read(&cs->aq, cs->fd,
+	    p,
+	    sio->st_size,
+	    cstate->reqdiskoffset,
+	    storeCossReadDone,
+	    sio);
+	cstate->reqdiskoffset = 0;	/* XXX */
+    } else {
+	storeCossReadDone(cs->fd,
+	    cstate->readbuffer,
+	    sio->st_size,
+	    0,
+	    sio);
+    }
+}
+
+void
+storeCossWrite(SwapDir * SD, storeIOState * sio, char *buf, size_t size, off_t offset, FREE * free_func)
+{
+    char *dest;
+    CossMemBuf *membuf;
+    off_t diskoffset;
+
+    /*
+     * If we get handed an object with a size of -1,
+     * the squid code is broken
+     */
+    assert(sio->e->mem_obj->object_sz != -1);
+
+    debug(79, 3) ("storeCossWrite: offset %ld, len %lu\n", (long int) sio->offset, (unsigned long int) size);
+    diskoffset = sio->swap_filen + sio->offset;
+    dest = storeCossMemPointerFromDiskOffset(SD, diskoffset, &membuf);
+    assert(dest != NULL);
+    xmemcpy(dest, buf, size);
+    sio->offset += size;
+    if (free_func)
+	(free_func) (buf);
+}
+
+
+/*  === STATIC =========================================================== */
+
+static void
+storeCossReadDone(int fd, const char *buf, int len, int errflag, void *my_data)
+{
+    storeIOState *sio = (storeIOState *)my_data;
+    char *p;
+    STRCB *callback = sio->read.callback;
+    void *cbdata;
+    SwapDir *SD = INDEXSD(sio->swap_dirn);
+    CossState *cstate = (CossState *) sio->fsstate;
+    ssize_t rlen;
+
+    debug(79, 3) ("storeCossReadDone: fileno %d, FD %d, len %d\n",
+	sio->swap_filen, fd, len);
+    cstate->flags.reading = 0;
+    if (errflag) {
+	debug(79, 3) ("storeCossReadDone: got failure (%d)\n", errflag);
+	rlen = -1;
+    } else {
+	if (cstate->readbuffer == NULL) {
+	    cstate->readbuffer = (char *)xmalloc(sio->st_size);
+	    p = storeCossMemPointerFromDiskOffset(SD, sio->swap_filen, NULL);
+	    xmemcpy(cstate->readbuffer, p, sio->st_size);
+	    storeCossMemBufUnlock(SD, sio);
+	}
+	sio->offset += len;
+	xmemcpy(cstate->requestbuf, &cstate->readbuffer[cstate->requestoffset],
+	    cstate->requestlen);
+	rlen = (size_t) cstate->requestlen;
+    }
+    assert(callback);
+    sio->read.callback = NULL;
+    if (cbdataReferenceValidDone(sio->read.callback_data, &cbdata))
+	callback(cbdata, cstate->requestbuf, rlen);
+}
+
+static void
+storeCossIOCallback(storeIOState * sio, int errflag)
+{
+    CossState *cstate = (CossState *) sio->fsstate;
+    STIOCB *callback = sio->callback;
+    void *cbdata;
+    debug(79, 3) ("storeCossIOCallback: errflag=%d\n", errflag);
+    xfree(cstate->readbuffer);
+    sio->callback = NULL;
+    if (cbdataReferenceValidDone(sio->callback_data, &cbdata))
+	callback(cbdata, errflag, sio);
+    cbdataFree(sio);
+}
+
+static char *
+storeCossMemPointerFromDiskOffset(SwapDir * SD, size_t offset, CossMemBuf ** mb)
+{
+    CossMemBuf *t;
+    dlink_node *m;
+    CossInfo *cs = (CossInfo *) SD->fsdata;
+
+    for (m = cs->membufs.head; m; m = m->next) {
+	t = (CossMemBuf *)m->data;
+	if ((offset >= t->diskstart) && (offset <= t->diskend)) {
+	    if (mb)
+		*mb = t;
+	    return &t->buffer[offset - t->diskstart];
+	}
+    }
+
+    if (mb)
+	*mb = NULL;
+    return NULL;
+}
+
+static void
+storeCossMemBufLock(SwapDir * SD, storeIOState * e)
+{
+    CossMemBuf *t;
+    dlink_node *m;
+    CossInfo *cs = (CossInfo *) SD->fsdata;
+
+    for (m = cs->membufs.head; m; m = m->next) {
+	t = (CossMemBuf *)m->data;
+	if (((size_t)e->swap_filen >= t->diskstart) && ((size_t)e->swap_filen <= t->diskend)) {
+	    debug(79, 3) ("storeCossMemBufLock: locking %p, lockcount %d\n", t, t->lockcount);
+	    t->lockcount++;
+	    return;
+	}
+    }
+    debug(79, 3) ("storeCossMemBufLock: FAILED to lock %p\n", e);
+}
+
+static void
+storeCossMemBufUnlock(SwapDir * SD, storeIOState * e)
+{
+    CossMemBuf *t;
+    dlink_node *m, *n;
+    CossInfo *cs = (CossInfo *) SD->fsdata;
+
+    for (m = cs->membufs.head; m; m = n) {
+	/*
+	 * Note that storeCossWriteMemBuf() might call storeCossWriteMemBufDone
+	 * immediately (if the write finishes immediately, of course!) which
+	 * will make m = m->next kinda unworkable. So, get the next pointer.
+	 */
+	n = m->next;
+	t = (CossMemBuf *)m->data;
+	if (((size_t)e->swap_filen >= t->diskstart) && ((size_t)e->swap_filen <= t->diskend)) {
+	    t->lockcount--;
+	    debug(79, 3) ("storeCossMemBufUnlock: unlocking %p, lockcount %d\n", t, t->lockcount);
+	}
+	if (t->flags.full && !t->flags.writing && !t->lockcount)
+	    storeCossWriteMemBuf(SD, t);
+    }
+}
+
+void
+storeCossSync(SwapDir * SD)
+{
+    CossInfo *cs = (CossInfo *) SD->fsdata;
+    CossMemBuf *t;
+    dlink_node *m;
+    int end;
+
+    /* First, flush pending IO ops */
+    a_file_syncqueue(&cs->aq);
+
+    /* Then, flush any in-memory partial membufs */
+    if (!cs->membufs.head)
+	return;
+    for (m = cs->membufs.head; m; m = m->next) {
+	t = (CossMemBuf *)m->data;
+	if (t->flags.writing)
+	    sleep(5);		/* XXX EEEWWW! */
+	lseek(cs->fd, t->diskstart, SEEK_SET);
+	end = (t == cs->current_membuf) ? cs->current_offset : t->diskend;
+	FD_WRITE_METHOD(cs->fd, t->buffer, end - t->diskstart);
+    }
+}
+
+static void
+storeCossWriteMemBuf(SwapDir * SD, CossMemBuf * t)
+{
+    CossInfo *cs = (CossInfo *) SD->fsdata;
+    debug(79, 3) ("storeCossWriteMemBuf: offset %ld, len %ld\n",
+	(long int) t->diskstart, (long int) (t->diskend - t->diskstart));
+    t->flags.writing = 1;
+    /* Remember that diskstart/diskend are block offsets! */
+    a_file_write(&cs->aq, cs->fd, t->diskstart, &t->buffer,
+	t->diskend - t->diskstart, storeCossWriteMemBufDone, t, NULL);
+}
+
+
+static void
+storeCossWriteMemBufDone(int fd, int errflag, size_t len, void *my_data)
+{
+    CossMemBuf *t = (CossMemBuf *)my_data;
+    CossInfo *cs = (CossInfo *) t->SD->fsdata;
+
+    debug(79, 3) ("storeCossWriteMemBufDone: buf %p, len %ld\n", t, (long int) len);
+    if (errflag)
+	debug(79, 0) ("storeCossMemBufWriteDone: got failure (%d)\n", errflag);
+
+    dlinkDelete(&t->node, &cs->membufs);
+    cbdataFree(t);
+}
+
+static CossMemBuf *
+storeCossCreateMemBuf(SwapDir * SD, size_t start,
+    sfileno curfn, int *collision)
+{
+    CossMemBuf *newmb, *t;
+    StoreEntry *e;
+    dlink_node *m, *prev;
+    int numreleased = 0;
+    CossInfo *cs = (CossInfo *) SD->fsdata;
+
+    CBDATA_INIT_TYPE_FREECB(CossMemBuf, NULL);
+    newmb = cbdataAlloc(CossMemBuf);
+    newmb->diskstart = start;
+    debug(79, 3) ("storeCossCreateMemBuf: creating new membuf at %ld\n", (long int) newmb->diskstart);
+    debug(79, 3) ("storeCossCreateMemBuf: at %p\n", newmb);
+    newmb->diskend = newmb->diskstart + COSS_MEMBUF_SZ - 1;
+    newmb->flags.full = 0;
+    newmb->flags.writing = 0;
+    newmb->lockcount = 0;
+    newmb->SD = SD;
+    /* XXX This should be reversed, with the new buffer last in the chain */
+    dlinkAdd(newmb, &newmb->node, &cs->membufs);
+
+    /* Print out the list of membufs */
+    for (m = cs->membufs.head; m; m = m->next) {
+	t = (CossMemBuf *)m->data;
+	debug(79, 3) ("storeCossCreateMemBuf: membuflist %ld lockcount %d\n", (long int) t->diskstart, t->lockcount);
+    }
+
+    /*
+     * Kill objects from the tail to make space for a new chunk
+     */
+    for (m = cs->index.tail; m; m = prev) {
+	prev = m->prev;
+	e = (StoreEntry *)m->data;
+	if (curfn == e->swap_filen)
+	    *collision = 1;	/* Mark an object alloc collision */
+	if (((size_t)e->swap_filen >= newmb->diskstart) &&
+	    ((size_t)e->swap_filen <= newmb->diskend)) {
+	    storeRelease(e);
+	    numreleased++;
+	} else
+	    break;
+    }
+    if (numreleased > 0)
+	debug(79, 3) ("storeCossCreateMemBuf: this allocation released %d storeEntries\n", numreleased);
+    return newmb;
+}
+
+/*
+ * Creates the initial membuf after rebuild
+ */
+void
+storeCossStartMembuf(SwapDir * sd)
+{
+    CossInfo *cs = (CossInfo *) sd->fsdata;
+    CossMemBuf *newmb = storeCossCreateMemBuf(sd, cs->current_offset, -1, NULL);
+    assert(!cs->current_membuf);
+    cs->current_membuf = newmb;
+}
+
+/*
+ * Clean up any references from the SIO before it get's released.
+ */
+static void
+storeCossIOFreeEntry(void *sio)
+{
+    memPoolFree(coss_state_pool, ((storeIOState *) sio)->fsstate);
+}
Index: squid/src/fs/diskd/Makefile.am
===================================================================
RCS file: /cvsroot/squid-sf//squid/src/fs/diskd/Attic/Makefile.am,v
retrieving revision 1.2
retrieving revision 1.2.72.1
diff -u -r1.2 -r1.2.72.1
--- squid/src/fs/diskd/Makefile.am	1 Sep 2001 11:46:51 -0000	1.2
+++ squid/src/fs/diskd/Makefile.am	12 Oct 2002 12:21:43 -0000	1.2.72.1
@@ -5,6 +5,7 @@
 #
 
 libexec_PROGRAMS = diskd
+diskd_SOURCES = diskd.cc
 LDADD = $(top_builddir)/lib/libmiscutil.a @XTRA_LIBS@
 
 INCLUDES      = -I. -I$(top_builddir)/include -I$(top_srcdir)/include \
--- squid/src/fs/diskd/diskd.c	Wed Feb 14 01:07:42 2007
+++ /dev/null	Wed Feb 14 01:07:22 2007
@@ -1,358 +0,0 @@
-/*
- * $Id$
- *
- * DEBUG: section --    External DISKD process implementation.
- * AUTHOR: Harvest Derived
- *
- * 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 "config.h"
-#include "squid.h"
-
-#include <sys/ipc.h>
-#include <sys/msg.h>
-#include <sys/shm.h>
-
-#include "store_diskd.h"
-
-#undef assert
-#include <assert.h>
-
-
-#define DEBUG(LEVEL) if (LEVEL <= DebugLevel)
-
-typedef struct _file_state file_state;
-
-struct _file_state {
-    void *key;
-    file_state *next;
-    int id;
-    int fd;
-    off_t offset;
-};
-
-static hash_table *hash = NULL;
-static pid_t mypid;
-static char *shmbuf;
-static int DebugLevel = 0;
-
-static int
-do_open(diomsg * r, int len, const char *buf)
-{
-    int fd;
-    file_state *fs;
-    /*
-     * note r->offset holds open() flags
-     */
-    fd = open(buf, r->offset, 0600);
-    if (fd < 0) {
-	DEBUG(1) {
-	    fprintf(stderr, "%d %s: ", (int) mypid, buf);
-	    perror("open");
-	}
-	return -errno;
-    }
-    fs = xcalloc(1, sizeof(*fs));
-    fs->id = r->id;
-    fs->key = &fs->id;		/* gack */
-    fs->fd = fd;
-    hash_join(hash, (hash_link *) fs);
-    DEBUG(2)
-	fprintf(stderr, "%d OPEN  id %d, FD %d, fs %p\n",
-	(int) mypid,
-	fs->id,
-	fs->fd,
-	fs);
-    return fd;
-}
-
-static int
-do_close(diomsg * r, int len)
-{
-    int fd;
-    file_state *fs;
-    fs = (file_state *) hash_lookup(hash, &r->id);
-    if (NULL == fs) {
-	errno = EBADF;
-	DEBUG(1) {
-	    fprintf(stderr, "%d CLOSE id %d: ", (int) mypid, r->id);
-	    perror("do_close");
-	}
-	return -errno;
-    }
-    fd = fs->fd;
-    hash_remove_link(hash, (hash_link *) fs);
-    DEBUG(2)
-	fprintf(stderr, "%d CLOSE id %d, FD %d, fs %p\n",
-	(int) mypid,
-	r->id,
-	fs->fd,
-	fs);
-    xfree(fs);
-    return close(fd);
-}
-
-static int
-do_read(diomsg * r, int len, char *buf)
-{
-    int x;
-    int readlen = r->size;
-    file_state *fs;
-    fs = (file_state *) hash_lookup(hash, &r->id);
-    if (NULL == fs) {
-	errno = EBADF;
-	DEBUG(1) {
-	    fprintf(stderr, "%d READ  id %d: ", (int) mypid, r->id);
-	    perror("do_read");
-	}
-	return -errno;
-    }
-    if (r->offset > -1 && r->offset != fs->offset) {
-	DEBUG(2)
-	    fprintf(stderr, "seeking to %d\n", r->offset);
-	if (lseek(fs->fd, r->offset, SEEK_SET) < 0) {
-	    DEBUG(1) {
-		fprintf(stderr, "%d FD %d, offset %d: ", (int) mypid, fs->fd, r->offset);
-		perror("lseek");
-	    }
-	}
-    }
-    x = read(fs->fd, buf, readlen);
-    DEBUG(2)
-	fprintf(stderr, "%d READ %d,%d,%d ret %d\n", (int) mypid,
-	fs->fd, readlen, r->offset, x);
-    if (x < 0) {
-	DEBUG(1) {
-	    fprintf(stderr, "%d FD %d: ", (int) mypid, fs->fd);
-	    perror("read");
-	}
-	return -errno;
-    }
-    fs->offset = r->offset + x;
-    return x;
-}
-
-static int
-do_write(diomsg * r, int len, const char *buf)
-{
-    int wrtlen = r->size;
-    int x;
-    file_state *fs;
-    fs = (file_state *) hash_lookup(hash, &r->id);
-    if (NULL == fs) {
-	errno = EBADF;
-	DEBUG(1) {
-	    fprintf(stderr, "%d WRITE id %d: ", (int) mypid, r->id);
-	    perror("do_write");
-	}
-	return -errno;
-    }
-    if (r->offset > -1 && r->offset != fs->offset) {
-	if (lseek(fs->fd, r->offset, SEEK_SET) < 0) {
-	    DEBUG(1) {
-		fprintf(stderr, "%d FD %d, offset %d: ", (int) mypid, fs->fd, r->offset);
-		perror("lseek");
-	    }
-	}
-    }
-    DEBUG(2)
-	fprintf(stderr, "%d WRITE %d,%d,%d\n", (int) mypid,
-	fs->fd, wrtlen, r->offset);
-    x = write(fs->fd, buf, wrtlen);
-    if (x < 0) {
-	DEBUG(1) {
-	    fprintf(stderr, "%d FD %d: ", (int) mypid, fs->fd);
-	    perror("write");
-	}
-	return -errno;
-    }
-    fs->offset = r->offset + x;
-    return x;
-}
-
-static int
-do_unlink(diomsg * r, int len, const char *buf)
-{
-#if USE_TRUNCATE
-    if (truncate(buf, 0) < 0)
-#else
-    if (unlink(buf) < 0)
-#endif
-    {
-	DEBUG(1) {
-	    fprintf(stderr, "%d UNLNK id %d %s: ", (int) mypid, r->id, buf);
-	    perror("truncate");
-	}
-	return -errno;
-    }
-    DEBUG(2)
-	fprintf(stderr, "%d UNLNK %s\n", (int) mypid, buf);
-    return 0;
-}
-
-static void
-msg_handle(diomsg * r, int rl, diomsg * s)
-{
-    char *buf = NULL;
-    s->mtype = r->mtype;
-    s->callback_data = r->callback_data;
-    s->shm_offset = r->shm_offset;
-    s->id = r->id;
-    if (s->shm_offset > -1)
-	buf = shmbuf + s->shm_offset;
-    switch (r->mtype) {
-    case _MQD_OPEN:
-	s->status = do_open(r, rl, buf);
-	break;
-    case _MQD_CLOSE:
-	s->status = do_close(r, rl);
-	break;
-    case _MQD_READ:
-	s->status = do_read(r, rl, buf);
-	break;
-    case _MQD_WRITE:
-	s->status = do_write(r, rl, buf);
-	break;
-    case _MQD_UNLINK:
-	s->status = do_unlink(r, rl, buf);
-	break;
-    default:
-	assert(0);
-	break;
-    }
-}
-
-static int
-fsCmp(const void *a, const void *b)
-{
-    const int *A = a;
-    const int *B = b;
-    return *A != *B;
-}
-
-static unsigned int
-fsHash(const void *key, unsigned int n)
-{
-    /* note, n must be a power of 2! */
-    const int *k = key;
-    return (*k & (--n));
-}
-
-static void
-alarm_handler(int sig)
-{
-    (void) 0;
-}
-
-int
-main(int argc, char *argv[])
-{
-    int key;
-    int rmsgid;
-    int smsgid;
-    int shmid;
-    diomsg rmsg;
-    diomsg smsg;
-    int rlen;
-    char rbuf[512];
-    struct sigaction sa;
-    setbuf(stdout, NULL);
-    setbuf(stderr, NULL);
-    mypid = getpid();
-    assert(4 == argc);
-    key = atoi(argv[1]);
-    rmsgid = msgget(key, 0600);
-    if (rmsgid < 0) {
-	perror("msgget");
-	return 1;
-    }
-    key = atoi(argv[2]);
-    smsgid = msgget(key, 0600);
-    if (smsgid < 0) {
-	perror("msgget");
-	return 1;
-    }
-    key = atoi(argv[3]);
-    shmid = shmget(key, 0, 0600);
-    if (shmid < 0) {
-	perror("shmget");
-	return 1;
-    }
-    shmbuf = shmat(shmid, NULL, 0);
-    if (shmbuf == (void *) -1) {
-	perror("shmat");
-	return 1;
-    }
-    hash = hash_create(fsCmp, 1 << 4, fsHash);
-    assert(hash);
-    fcntl(0, F_SETFL, SQUID_NONBLOCK);
-    memset(&sa, '\0', sizeof(sa));
-    sa.sa_handler = alarm_handler;
-    sa.sa_flags = SA_RESTART;
-    sigaction(SIGALRM, &sa, NULL);
-    for (;;) {
-	alarm(1);
-	memset(&rmsg, '\0', sizeof(rmsg));
-	rlen = msgrcv(rmsgid, &rmsg, msg_snd_rcv_sz, 0, 0);
-	if (rlen < 0) {
-	    if (EINTR == errno) {
-		if (read(0, rbuf, 512) <= 0) {
-		    if (EWOULDBLOCK == errno)
-			(void) 0;
-		    else if (EAGAIN == errno)
-			(void) 0;
-		    else
-			break;
-		}
-	    }
-	    if (EAGAIN == errno) {
-		continue;
-	    }
-	    perror("msgrcv");
-	    break;
-	}
-	alarm(0);
-	msg_handle(&rmsg, rlen, &smsg);
-	if (msgsnd(smsgid, &smsg, msg_snd_rcv_sz, 0) < 0) {
-	    perror("msgsnd");
-	    break;
-	}
-    }
-    DEBUG(2)
-	fprintf(stderr, "%d diskd exiting\n", (int) mypid);
-    if (msgctl(rmsgid, IPC_RMID, 0) < 0)
-	perror("msgctl IPC_RMID");
-    if (msgctl(smsgid, IPC_RMID, 0) < 0)
-	perror("msgctl IPC_RMID");
-    if (shmdt(shmbuf) < 0)
-	perror("shmdt");
-    if (shmctl(shmid, IPC_RMID, 0) < 0)
-	perror("shmctl IPC_RMID");
-    return 0;
-}
--- /dev/null	Wed Feb 14 01:07:22 2007
+++ squid/src/fs/diskd/diskd.cc	Wed Feb 14 01:07:42 2007
@@ -0,0 +1,358 @@
+/*
+ * $Id: diskd.cc,v 1.1.2.1 2002/10/12 12:21:43 rbcollins Exp $
+ *
+ * DEBUG: section --    External DISKD process implementation.
+ * AUTHOR: Harvest Derived
+ *
+ * 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 "config.h"
+#include "squid.h"
+
+#include <sys/ipc.h>
+#include <sys/msg.h>
+#include <sys/shm.h>
+
+#include "store_diskd.h"
+
+#undef assert
+#include <assert.h>
+
+
+#define DEBUG(LEVEL) if (LEVEL <= DebugLevel)
+
+typedef struct _file_state file_state;
+
+struct _file_state {
+    void *key;
+    file_state *next;
+    int id;
+    int fd;
+    off_t offset;
+};
+
+static hash_table *hash = NULL;
+static pid_t mypid;
+static char *shmbuf;
+static int DebugLevel = 0;
+
+static int
+do_open(diomsg * r, int len, const char *buf)
+{
+    int fd;
+    file_state *fs;
+    /*
+     * note r->offset holds open() flags
+     */
+    fd = open(buf, r->offset, 0600);
+    if (fd < 0) {
+	DEBUG(1) {
+	    fprintf(stderr, "%d %s: ", (int) mypid, buf);
+	    perror("open");
+	}
+	return -errno;
+    }
+    fs = (file_state *)xcalloc(1, sizeof(*fs));
+    fs->id = r->id;
+    fs->key = &fs->id;		/* gack */
+    fs->fd = fd;
+    hash_join(hash, (hash_link *) fs);
+    DEBUG(2)
+	fprintf(stderr, "%d OPEN  id %d, FD %d, fs %p\n",
+	(int) mypid,
+	fs->id,
+	fs->fd,
+	fs);
+    return fd;
+}
+
+static int
+do_close(diomsg * r, int len)
+{
+    int fd;
+    file_state *fs;
+    fs = (file_state *) hash_lookup(hash, &r->id);
+    if (NULL == fs) {
+	errno = EBADF;
+	DEBUG(1) {
+	    fprintf(stderr, "%d CLOSE id %d: ", (int) mypid, r->id);
+	    perror("do_close");
+	}
+	return -errno;
+    }
+    fd = fs->fd;
+    hash_remove_link(hash, (hash_link *) fs);
+    DEBUG(2)
+	fprintf(stderr, "%d CLOSE id %d, FD %d, fs %p\n",
+	(int) mypid,
+	r->id,
+	fs->fd,
+	fs);
+    xfree(fs);
+    return close(fd);
+}
+
+static int
+do_read(diomsg * r, int len, char *buf)
+{
+    int x;
+    int readlen = r->size;
+    file_state *fs;
+    fs = (file_state *) hash_lookup(hash, &r->id);
+    if (NULL == fs) {
+	errno = EBADF;
+	DEBUG(1) {
+	    fprintf(stderr, "%d READ  id %d: ", (int) mypid, r->id);
+	    perror("do_read");
+	}
+	return -errno;
+    }
+    if (r->offset > -1 && r->offset != fs->offset) {
+	DEBUG(2)
+	    fprintf(stderr, "seeking to %d\n", r->offset);
+	if (lseek(fs->fd, r->offset, SEEK_SET) < 0) {
+	    DEBUG(1) {
+		fprintf(stderr, "%d FD %d, offset %d: ", (int) mypid, fs->fd, r->offset);
+		perror("lseek");
+	    }
+	}
+    }
+    x = read(fs->fd, buf, readlen);
+    DEBUG(2)
+	fprintf(stderr, "%d READ %d,%d,%d ret %d\n", (int) mypid,
+	fs->fd, readlen, r->offset, x);
+    if (x < 0) {
+	DEBUG(1) {
+	    fprintf(stderr, "%d FD %d: ", (int) mypid, fs->fd);
+	    perror("read");
+	}
+	return -errno;
+    }
+    fs->offset = r->offset + x;
+    return x;
+}
+
+static int
+do_write(diomsg * r, int len, const char *buf)
+{
+    int wrtlen = r->size;
+    int x;
+    file_state *fs;
+    fs = (file_state *) hash_lookup(hash, &r->id);
+    if (NULL == fs) {
+	errno = EBADF;
+	DEBUG(1) {
+	    fprintf(stderr, "%d WRITE id %d: ", (int) mypid, r->id);
+	    perror("do_write");
+	}
+	return -errno;
+    }
+    if (r->offset > -1 && r->offset != fs->offset) {
+	if (lseek(fs->fd, r->offset, SEEK_SET) < 0) {
+	    DEBUG(1) {
+		fprintf(stderr, "%d FD %d, offset %d: ", (int) mypid, fs->fd, r->offset);
+		perror("lseek");
+	    }
+	}
+    }
+    DEBUG(2)
+	fprintf(stderr, "%d WRITE %d,%d,%d\n", (int) mypid,
+	fs->fd, wrtlen, r->offset);
+    x = write(fs->fd, buf, wrtlen);
+    if (x < 0) {
+	DEBUG(1) {
+	    fprintf(stderr, "%d FD %d: ", (int) mypid, fs->fd);
+	    perror("write");
+	}
+	return -errno;
+    }
+    fs->offset = r->offset + x;
+    return x;
+}
+
+static int
+do_unlink(diomsg * r, int len, const char *buf)
+{
+#if USE_TRUNCATE
+    if (truncate(buf, 0) < 0)
+#else
+    if (unlink(buf) < 0)
+#endif
+    {
+	DEBUG(1) {
+	    fprintf(stderr, "%d UNLNK id %d %s: ", (int) mypid, r->id, buf);
+	    perror("truncate");
+	}
+	return -errno;
+    }
+    DEBUG(2)
+	fprintf(stderr, "%d UNLNK %s\n", (int) mypid, buf);
+    return 0;
+}
+
+static void
+msg_handle(diomsg * r, int rl, diomsg * s)
+{
+    char *buf = NULL;
+    s->mtype = r->mtype;
+    s->callback_data = r->callback_data;
+    s->shm_offset = r->shm_offset;
+    s->id = r->id;
+    if (s->shm_offset > -1)
+	buf = shmbuf + s->shm_offset;
+    switch (r->mtype) {
+    case _MQD_OPEN:
+	s->status = do_open(r, rl, buf);
+	break;
+    case _MQD_CLOSE:
+	s->status = do_close(r, rl);
+	break;
+    case _MQD_READ:
+	s->status = do_read(r, rl, buf);
+	break;
+    case _MQD_WRITE:
+	s->status = do_write(r, rl, buf);
+	break;
+    case _MQD_UNLINK:
+	s->status = do_unlink(r, rl, buf);
+	break;
+    default:
+	assert(0);
+	break;
+    }
+}
+
+static int
+fsCmp(const void *a, const void *b)
+{
+    const int *A = (const int *)a;
+    const int *B = (const int *)b;
+    return *A != *B;
+}
+
+static unsigned int
+fsHash(const void *key, unsigned int n)
+{
+    /* note, n must be a power of 2! */
+    const int *k = (const int *)key;
+    return (*k & (--n));
+}
+
+static void
+alarm_handler(int sig)
+{
+    (void) 0;
+}
+
+int
+main(int argc, char *argv[])
+{
+    int key;
+    int rmsgid;
+    int smsgid;
+    int shmid;
+    diomsg rmsg;
+    diomsg smsg;
+    int rlen;
+    char rbuf[512];
+    struct sigaction sa;
+    setbuf(stdout, NULL);
+    setbuf(stderr, NULL);
+    mypid = getpid();
+    assert(4 == argc);
+    key = atoi(argv[1]);
+    rmsgid = msgget(key, 0600);
+    if (rmsgid < 0) {
+	perror("msgget");
+	return 1;
+    }
+    key = atoi(argv[2]);
+    smsgid = msgget(key, 0600);
+    if (smsgid < 0) {
+	perror("msgget");
+	return 1;
+    }
+    key = atoi(argv[3]);
+    shmid = shmget(key, 0, 0600);
+    if (shmid < 0) {
+	perror("shmget");
+	return 1;
+    }
+    shmbuf = (char *)shmat(shmid, NULL, 0);
+    if (shmbuf == (void *) -1) {
+	perror("shmat");
+	return 1;
+    }
+    hash = hash_create(fsCmp, 1 << 4, fsHash);
+    assert(hash);
+    fcntl(0, F_SETFL, SQUID_NONBLOCK);
+    memset(&sa, '\0', sizeof(sa));
+    sa.sa_handler = alarm_handler;
+    sa.sa_flags = SA_RESTART;
+    sigaction(SIGALRM, &sa, NULL);
+    for (;;) {
+	alarm(1);
+	memset(&rmsg, '\0', sizeof(rmsg));
+	rlen = msgrcv(rmsgid, &rmsg, msg_snd_rcv_sz, 0, 0);
+	if (rlen < 0) {
+	    if (EINTR == errno) {
+		if (read(0, rbuf, 512) <= 0) {
+		    if (EWOULDBLOCK == errno)
+			(void) 0;
+		    else if (EAGAIN == errno)
+			(void) 0;
+		    else
+			break;
+		}
+	    }
+	    if (EAGAIN == errno) {
+		continue;
+	    }
+	    perror("msgrcv");
+	    break;
+	}
+	alarm(0);
+	msg_handle(&rmsg, rlen, &smsg);
+	if (msgsnd(smsgid, &smsg, msg_snd_rcv_sz, 0) < 0) {
+	    perror("msgsnd");
+	    break;
+	}
+    }
+    DEBUG(2)
+	fprintf(stderr, "%d diskd exiting\n", (int) mypid);
+    if (msgctl(rmsgid, IPC_RMID, 0) < 0)
+	perror("msgctl IPC_RMID");
+    if (msgctl(smsgid, IPC_RMID, 0) < 0)
+	perror("msgctl IPC_RMID");
+    if (shmdt(shmbuf) < 0)
+	perror("shmdt");
+    if (shmctl(shmid, IPC_RMID, 0) < 0)
+	perror("shmctl IPC_RMID");
+    return 0;
+}
--- squid/src/fs/diskd/store_dir_diskd.c	Wed Feb 14 01:07:42 2007
+++ /dev/null	Wed Feb 14 01:07:22 2007
@@ -1,516 +0,0 @@
-
-/*
- * $Id$
- *
- * 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.h"
-
-#include <sys/ipc.h>
-#include <sys/msg.h>
-#include <sys/shm.h>
-
-#include "store_diskd.h"
-
-#include "ufscommon.h"
-
-diskd_stats_t diskd_stats;
-
-MemPool *diskd_state_pool = NULL;
-static int diskd_initialised = 0;
-
-static STINIT storeDiskdDirInit;
-static STDUMP storeDiskdDirDump;
-static STCHECKOBJ storeDiskdDirCheckObj;
-static void storeDiskdDirStats(SwapDir *, StoreEntry *);
-static void storeDiskdStats(StoreEntry * sentry);
-static void storeDiskdDirSync(SwapDir *);
-static void storeDiskdDirIOUnlinkFile(char *path);
-
-/* The only externally visible interface */
-STSETUP storeFsSetup_diskd;
-
-static void
-storeDiskdDirInit(SwapDir * sd)
-{
-    int x;
-    int i;
-    int rfd;
-    int ikey;
-    const char *args[5];
-    char skey1[32];
-    char skey2[32];
-    char skey3[32];
-    diskdinfo_t *diskdinfo = sd->fsdata;
-
-    ikey = (getpid() << 10) + (sd->index << 2);
-    ikey &= 0x7fffffff;
-    diskdinfo->smsgid = msgget((key_t) ikey, 0700 | IPC_CREAT);
-    if (diskdinfo->smsgid < 0) {
-	debug(50, 0) ("storeDiskdInit: msgget: %s\n", xstrerror());
-	fatal("msgget failed");
-    }
-    diskdinfo->rmsgid = msgget((key_t) (ikey + 1), 0700 | IPC_CREAT);
-    if (diskdinfo->rmsgid < 0) {
-	debug(50, 0) ("storeDiskdInit: msgget: %s\n", xstrerror());
-	fatal("msgget failed");
-    }
-    diskdinfo->shm.nbufs = diskdinfo->magic2 * 1.3;
-    diskdinfo->shm.id = shmget((key_t) (ikey + 2),
-	diskdinfo->shm.nbufs * SHMBUF_BLKSZ, 0600 | IPC_CREAT);
-    if (diskdinfo->shm.id < 0) {
-	debug(50, 0) ("storeDiskdInit: shmget: %s\n", xstrerror());
-	fatal("shmget failed");
-    }
-    diskdinfo->shm.buf = shmat(diskdinfo->shm.id, NULL, 0);
-    if (diskdinfo->shm.buf == (void *) -1) {
-	debug(50, 0) ("storeDiskdInit: shmat: %s\n", xstrerror());
-	fatal("shmat failed");
-    }
-    diskdinfo->shm.inuse_map = xcalloc((diskdinfo->shm.nbufs + 7) / 8, 1);
-    diskd_stats.shmbuf_count += diskdinfo->shm.nbufs;
-    for (i = 0; i < diskdinfo->shm.nbufs; i++) {
-	CBIT_SET(diskdinfo->shm.inuse_map, i);
-	storeDiskdShmPut(sd, i * SHMBUF_BLKSZ);
-    }
-    snprintf(skey1, 32, "%d", ikey);
-    snprintf(skey2, 32, "%d", ikey + 1);
-    snprintf(skey3, 32, "%d", ikey + 2);
-    args[0] = "diskd";
-    args[1] = skey1;
-    args[2] = skey2;
-    args[3] = skey3;
-    args[4] = NULL;
-    x = ipcCreate(IPC_STREAM,
-	Config.Program.diskd,
-	args,
-	"diskd",
-	&rfd,
-	&diskdinfo->wfd);
-    if (x < 0)
-	fatalf("execl: %s", Config.Program.diskd);
-    if (rfd != diskdinfo->wfd)
-	comm_close(rfd);
-    fd_note(diskdinfo->wfd, "squid -> diskd");
-    commSetTimeout(diskdinfo->wfd, -1, NULL, NULL);
-    commSetNonBlocking(diskdinfo->wfd);
-    
-    commonUfsDirInit (sd);
-    
-    comm_quick_poll_required();
-}
-
-
-static void
-storeDiskdStats(StoreEntry * sentry)
-{
-    storeAppendPrintf(sentry, "sent_count: %d\n", diskd_stats.sent_count);
-    storeAppendPrintf(sentry, "recv_count: %d\n", diskd_stats.recv_count);
-    storeAppendPrintf(sentry, "max_away: %d\n", diskd_stats.max_away);
-    storeAppendPrintf(sentry, "max_shmuse: %d\n", diskd_stats.max_shmuse);
-    storeAppendPrintf(sentry, "open_fail_queue_len: %d\n", diskd_stats.open_fail_queue_len);
-    storeAppendPrintf(sentry, "block_queue_len: %d\n", diskd_stats.block_queue_len);
-    diskd_stats.max_away = diskd_stats.max_shmuse = 0;
-    storeAppendPrintf(sentry, "\n             OPS SUCCESS    FAIL\n");
-    storeAppendPrintf(sentry, "%7s %7d %7d %7d\n",
-	"open", diskd_stats.open.ops, diskd_stats.open.success, diskd_stats.open.fail);
-    storeAppendPrintf(sentry, "%7s %7d %7d %7d\n",
-	"create", diskd_stats.create.ops, diskd_stats.create.success, diskd_stats.create.fail);
-    storeAppendPrintf(sentry, "%7s %7d %7d %7d\n",
-	"close", diskd_stats.close.ops, diskd_stats.close.success, diskd_stats.close.fail);
-    storeAppendPrintf(sentry, "%7s %7d %7d %7d\n",
-	"unlink", diskd_stats.unlink.ops, diskd_stats.unlink.success, diskd_stats.unlink.fail);
-    storeAppendPrintf(sentry, "%7s %7d %7d %7d\n",
-	"read", diskd_stats.read.ops, diskd_stats.read.success, diskd_stats.read.fail);
-    storeAppendPrintf(sentry, "%7s %7d %7d %7d\n",
-	"write", diskd_stats.write.ops, diskd_stats.write.success, diskd_stats.write.fail);
-}
-
-/*
- * storeDiskdDirSync
- *
- * Sync any pending data. We just sit around and read the queue
- * until the data has finished writing.
- */
-static void
-storeDiskdDirSync(SwapDir * SD)
-{
-    static time_t lastmsg = 0;
-    diskdinfo_t *diskdinfo = SD->fsdata;
-    while (diskdinfo->away > 0) {
-	if (squid_curtime > lastmsg) {
-	    debug(47, 1) ("storeDiskdDirSync: %d messages away\n",
-		diskdinfo->away);
-	    lastmsg = squid_curtime;
-	}
-	storeDiskdDirCallback(SD);
-    }
-}
-
-
-/*
- * storeDiskdDirCallback
- *
- * Handle callbacks. If we have more than magic2 requests away, we block
- * until the queue is below magic2. Otherwise, we simply return when we
- * don't get a message.
- */
-int
-storeDiskdDirCallback(SwapDir * SD)
-{
-    diomsg M;
-    int x;
-    diskdinfo_t *diskdinfo = SD->fsdata;
-    int retval = 0;
-
-    if (diskdinfo->away >= diskdinfo->magic2) {
-	diskd_stats.block_queue_len++;
-	retval = 1;		/* We might not have anything to do, but our queue
-				 * is full.. */
-    }
-    if (diskd_stats.sent_count - diskd_stats.recv_count >
-	diskd_stats.max_away) {
-	diskd_stats.max_away = diskd_stats.sent_count - diskd_stats.recv_count;
-    }
-    while (1) {
-	memset(&M, '\0', sizeof(M));
-	x = msgrcv(diskdinfo->rmsgid, &M, msg_snd_rcv_sz, 0, IPC_NOWAIT);
-	if (x < 0)
-	    break;
-	else if (x != msg_snd_rcv_sz) {
-	    debug(47, 1) ("storeDiskdDirCallback: msgget returns %d\n",
-		x);
-	    break;
-	}
-	diskd_stats.recv_count++;
-	diskdinfo->away--;
-	storeDiskdHandle(&M);
-	retval = 1;		/* Return that we've actually done some work */
-	if (M.shm_offset > -1)
-	    storeDiskdShmPut(SD, (off_t) M.shm_offset);
-    }
-    return retval;
-}
-
-/*
- * storeDiskdDirCheckObj
- *
- * This routine is called by storeDirSelectSwapDir to see if the given
- * object is able to be stored on this filesystem. DISKD filesystems will
- * happily store anything as long as the LRU time isn't too small.
- */
-int
-storeDiskdDirCheckObj(SwapDir * SD, const StoreEntry * e)
-{
-    diskdinfo_t *diskdinfo = SD->fsdata;
-    /* Check the queue length */
-    if (diskdinfo->away >= diskdinfo->magic1)
-	return -1;
-    /* Calculate the storedir load relative to magic2 on a scale of 0 .. 1000 */
-    /* the parse function guarantees magic2 is positivie */
-    return diskdinfo->away * 1000 / diskdinfo->magic2;
-}
-
-void
-storeDiskdDirIOUnlinkFile(char *path)
-{
-#if USE_UNLINKD
-    unlinkdUnlink(path);
-#elif USE_TRUNCATE
-    truncate(path, 0);
-#else
-    unlink(path);
-#endif
-		
-}
-
-/*
- * SHM manipulation routines
- */
-
-void *
-storeDiskdShmGet(SwapDir * sd, off_t * shm_offset)
-{
-    char *buf = NULL;
-    diskdinfo_t *diskdinfo = sd->fsdata;
-    int i;
-    for (i = 0; i < diskdinfo->shm.nbufs; i++) {
-	if (CBIT_TEST(diskdinfo->shm.inuse_map, i))
-	    continue;
-	CBIT_SET(diskdinfo->shm.inuse_map, i);
-	*shm_offset = i * SHMBUF_BLKSZ;
-	buf = diskdinfo->shm.buf + (*shm_offset);
-	break;
-    }
-    assert(buf);
-    assert(buf >= diskdinfo->shm.buf);
-    assert(buf < diskdinfo->shm.buf + (diskdinfo->shm.nbufs * SHMBUF_BLKSZ));
-    diskd_stats.shmbuf_count++;
-    if (diskd_stats.max_shmuse < diskd_stats.shmbuf_count)
-	diskd_stats.max_shmuse = diskd_stats.shmbuf_count;
-    return buf;
-}
-
-void
-storeDiskdShmPut(SwapDir * sd, off_t offset)
-{
-    int i;
-    diskdinfo_t *diskdinfo = sd->fsdata;
-    assert(offset >= 0);
-    assert(offset < diskdinfo->shm.nbufs * SHMBUF_BLKSZ);
-    i = offset / SHMBUF_BLKSZ;
-    assert(i < diskdinfo->shm.nbufs);
-    assert(CBIT_TEST(diskdinfo->shm.inuse_map, i));
-    CBIT_CLR(diskdinfo->shm.inuse_map, i);
-    diskd_stats.shmbuf_count--;
-}
-
-
-
-
-/* ========== LOCAL FUNCTIONS ABOVE, GLOBAL FUNCTIONS BELOW ========== */
-
-void
-storeDiskdDirStats(SwapDir * SD, StoreEntry * sentry)
-{
-    diskdinfo_t *diskdinfo = SD->fsdata;
-    commonUfsDirStats (SD, sentry);
-    storeAppendPrintf(sentry, "Pending operations: %d\n", diskdinfo->away);
-}
-
-static void
-storeDiskdDirParseQ1(SwapDir * sd, const char *name, const char *value, int reconfiguring)
-{
-    diskdinfo_t *diskdinfo = sd->fsdata;
-    int old_magic1 = diskdinfo->magic1;
-    diskdinfo->magic1 = atoi(value);
-    if (!reconfiguring)
-	return;
-    if (old_magic1 < diskdinfo->magic1) {
-       /*
-	* This is because shm.nbufs is computed at startup, when
-	* we call shmget().  We can't increase the Q1/Q2 parameters
-	* beyond their initial values because then we might have
-	* more "Q2 messages" than shared memory chunks, and this
-	* will cause an assertion in storeDiskdShmGet().
-	*/
-       debug(3, 1) ("WARNING: cannot increase cache_dir '%s' Q1 value while Squid is running.\n", sd->path);
-       diskdinfo->magic1 = old_magic1;
-       return;
-    }
-    if (old_magic1 != diskdinfo->magic1)
-	debug(3, 1) ("cache_dir '%s' new Q1 value '%d'\n",
-	    sd->path, diskdinfo->magic1);
-}
-
-static void
-storeDiskdDirDumpQ1(StoreEntry * e, const char *option, SwapDir * sd)
-{
-    diskdinfo_t *diskdinfo = sd->fsdata;
-    storeAppendPrintf(e, " Q1=%d", diskdinfo->magic1);
-}
-
-static void
-storeDiskdDirParseQ2(SwapDir * sd, const char *name, const char *value, int reconfiguring)
-{
-    diskdinfo_t *diskdinfo = sd->fsdata;
-    int old_magic2 = diskdinfo->magic2;
-    diskdinfo->magic2 = atoi(value);
-    if (!reconfiguring)
-       return;
-    if (old_magic2 < diskdinfo->magic2) {
-       /* See comments in Q1 function above */
-       debug(3, 1) ("WARNING: cannot increase cache_dir '%s' Q2 value while Squid is running.\n", sd->path);
-       diskdinfo->magic2 = old_magic2;
-       return;
-    }
-    if (old_magic2 != diskdinfo->magic2)
-	debug(3, 1) ("cache_dir '%s' new Q2 value '%d'\n",
-	    sd->path, diskdinfo->magic2);
-}
-
-static void
-storeDiskdDirDumpQ2(StoreEntry * e, const char *option, SwapDir * sd)
-{
-    diskdinfo_t *diskdinfo = sd->fsdata;
-    storeAppendPrintf(e, " Q2=%d", diskdinfo->magic2);
-}
-
-struct cache_dir_option options[] =
-{
-#if NOT_YET
-    {"L1", storeDiskdDirParseL1, storeDiskdDirDumpL1},
-    {"L2", storeDiskdDirParseL2, storeDiskdDirDumpL2},
-#endif
-    {"Q1", storeDiskdDirParseQ1, storeDiskdDirDumpQ1},
-    {"Q2", storeDiskdDirParseQ2, storeDiskdDirDumpQ2},
-    {NULL, NULL}
-};
-
-/*
- * storeDiskdDirReconfigure
- *
- * This routine is called when the given swapdir needs reconfiguring 
- */
-static void
-storeDiskdDirReconfigure(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("storeDiskdDirReconfigure: invalid size value");
-    i = GetInteger();
-    l1 = i;
-    if (l1 <= 0)
-	fatal("storeDiskdDirReconfigure: invalid level 1 directories value");
-    i = GetInteger();
-    l2 = i;
-    if (l2 <= 0)
-	fatal("storeDiskdDirReconfigure: 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, 1);
-}
-
-void
-storeDiskdDirDump(StoreEntry * entry, SwapDir * s)
-{
-    commonUfsDirDump (entry, s);
-    dump_cachedir_options(entry, options, s);
-}
-
-/*
- * storeDiskdDirParse
- *
- * Called when a *new* fs is being setup.
- */
-static void
-storeDiskdDirParse(SwapDir * sd, int index, char *path)
-{
-    int i;
-    int size;
-    int l1;
-    int l2;
-    diskdinfo_t *diskdinfo;
-
-    i = GetInteger();
-    size = i << 10;		/* Mbytes to kbytes */
-    if (size <= 0)
-	fatal("storeDiskdDirParse: invalid size value");
-    i = GetInteger();
-    l1 = i;
-    if (l1 <= 0)
-	fatal("storeDiskdDirParse: invalid level 1 directories value");
-    i = GetInteger();
-    l2 = i;
-    if (l2 <= 0)
-	fatal("storeDiskdDirParse: invalid level 2 directories value");
-
-    sd->fsdata = diskdinfo = xcalloc(1, sizeof(*diskdinfo));
-    sd->index = index;
-    sd->path = xstrdup(path);
-    sd->max_size = size;
-    diskdinfo->commondata.l1 = l1;
-    diskdinfo->commondata.l2 = l2;
-    diskdinfo->commondata.swaplog_fd = -1;
-    diskdinfo->commondata.map = NULL;	/* Debugging purposes */
-    diskdinfo->commondata.suggest = 0;
-    diskdinfo->commondata.io.storeDirUnlinkFile = storeDiskdDirIOUnlinkFile;
-    diskdinfo->magic1 = 64;
-    diskdinfo->magic2 = 72;
-    sd->init = storeDiskdDirInit;
-    sd->newfs = commonUfsDirNewfs;
-    sd->dump = storeDiskdDirDump;
-    sd->freefs = commonUfsDirFree;
-    sd->dblcheck = commonUfsCleanupDoubleCheck;
-    sd->statfs = storeDiskdDirStats;
-    sd->maintainfs = commonUfsDirMaintain;
-    sd->checkobj = storeDiskdDirCheckObj;
-    sd->refobj = commonUfsDirRefObj;
-    sd->unrefobj = commonUfsDirUnrefObj;
-    sd->callback = storeDiskdDirCallback;
-    sd->sync = storeDiskdDirSync;
-    sd->obj.create = storeDiskdCreate;
-    sd->obj.open = storeDiskdOpen;
-    sd->obj.close = storeDiskdClose;
-    sd->obj.read = storeDiskdRead;
-    sd->obj.write = storeDiskdWrite;
-    sd->obj.unlink = storeDiskdUnlink;
-    sd->log.open = commonUfsDirOpenSwapLog;
-    sd->log.close = commonUfsDirCloseSwapLog;
-    sd->log.write = commonUfsDirSwapLog;
-    sd->log.clean.start = commonUfsDirWriteCleanStart;
-    sd->log.clean.nextentry = commonUfsDirCleanLogNextEntry;
-    sd->log.clean.done = commonUfsDirWriteCleanDone;
-
-    parse_cachedir_options(sd, options, 0);
-
-    /* Initialise replacement policy stuff */
-    sd->repl = createRemovalPolicy(Config.replPolicy);
-}
-
-/*
- * Initial setup / end destruction
- */
-static void
-storeDiskdDirDone(void)
-{
-    memPoolDestroy(&diskd_state_pool);
-    diskd_initialised = 0;
-}
-
-void
-storeFsSetup_diskd(storefs_entry_t * storefs)
-{
-    assert(!diskd_initialised);
-    storefs->parsefunc = storeDiskdDirParse;
-    storefs->reconfigurefunc = storeDiskdDirReconfigure;
-    storefs->donefunc = storeDiskdDirDone;
-    diskd_state_pool = memPoolCreate("DISKD IO State data", sizeof(diskdstate_t));
-    memset(&diskd_stats, '\0', sizeof(diskd_stats));
-    cachemgrRegister("diskd", "DISKD Stats", storeDiskdStats, 0, 1);
-    debug(47, 1) ("diskd started\n");
-    diskd_initialised = 1;
-}
--- /dev/null	Wed Feb 14 01:07:22 2007
+++ squid/src/fs/diskd/store_dir_diskd.cc	Wed Feb 14 01:07:42 2007
@@ -0,0 +1,516 @@
+
+/*
+ * $Id: store_dir_diskd.cc,v 1.1.2.1 2002/10/12 12:21:43 rbcollins 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.h"
+
+#include <sys/ipc.h>
+#include <sys/msg.h>
+#include <sys/shm.h>
+
+#include "store_diskd.h"
+
+#include "ufscommon.h"
+
+diskd_stats_t diskd_stats;
+
+MemPool *diskd_state_pool = NULL;
+static int diskd_initialised = 0;
+
+static STINIT storeDiskdDirInit;
+static STDUMP storeDiskdDirDump;
+static STCHECKOBJ storeDiskdDirCheckObj;
+static void storeDiskdDirStats(SwapDir *, StoreEntry *);
+static void storeDiskdStats(StoreEntry * sentry);
+static void storeDiskdDirSync(SwapDir *);
+static void storeDiskdDirIOUnlinkFile(char *path);
+
+/* The only externally visible interface */
+STSETUP storeFsSetup_diskd;
+
+static void
+storeDiskdDirInit(SwapDir * sd)
+{
+    int x;
+    int i;
+    int rfd;
+    int ikey;
+    const char *args[5];
+    char skey1[32];
+    char skey2[32];
+    char skey3[32];
+    diskdinfo_t *diskdinfo = (diskdinfo_t *)sd->fsdata;
+
+    ikey = (getpid() << 10) + (sd->index << 2);
+    ikey &= 0x7fffffff;
+    diskdinfo->smsgid = msgget((key_t) ikey, 0700 | IPC_CREAT);
+    if (diskdinfo->smsgid < 0) {
+	debug(50, 0) ("storeDiskdInit: msgget: %s\n", xstrerror());
+	fatal("msgget failed");
+    }
+    diskdinfo->rmsgid = msgget((key_t) (ikey + 1), 0700 | IPC_CREAT);
+    if (diskdinfo->rmsgid < 0) {
+	debug(50, 0) ("storeDiskdInit: msgget: %s\n", xstrerror());
+	fatal("msgget failed");
+    }
+    diskdinfo->shm.nbufs = (int)(diskdinfo->magic2 * 1.3);
+    diskdinfo->shm.id = shmget((key_t) (ikey + 2),
+	diskdinfo->shm.nbufs * SHMBUF_BLKSZ, 0600 | IPC_CREAT);
+    if (diskdinfo->shm.id < 0) {
+	debug(50, 0) ("storeDiskdInit: shmget: %s\n", xstrerror());
+	fatal("shmget failed");
+    }
+    diskdinfo->shm.buf = (char *)shmat(diskdinfo->shm.id, NULL, 0);
+    if (diskdinfo->shm.buf == (void *) -1) {
+	debug(50, 0) ("storeDiskdInit: shmat: %s\n", xstrerror());
+	fatal("shmat failed");
+    }
+    diskdinfo->shm.inuse_map = (char *)xcalloc((diskdinfo->shm.nbufs + 7) / 8, 1);
+    diskd_stats.shmbuf_count += diskdinfo->shm.nbufs;
+    for (i = 0; i < diskdinfo->shm.nbufs; i++) {
+	CBIT_SET(diskdinfo->shm.inuse_map, i);
+	storeDiskdShmPut(sd, i * SHMBUF_BLKSZ);
+    }
+    snprintf(skey1, 32, "%d", ikey);
+    snprintf(skey2, 32, "%d", ikey + 1);
+    snprintf(skey3, 32, "%d", ikey + 2);
+    args[0] = "diskd";
+    args[1] = skey1;
+    args[2] = skey2;
+    args[3] = skey3;
+    args[4] = NULL;
+    x = ipcCreate(IPC_STREAM,
+	Config.Program.diskd,
+	args,
+	"diskd",
+	&rfd,
+	&diskdinfo->wfd);
+    if (x < 0)
+	fatalf("execl: %s", Config.Program.diskd);
+    if (rfd != diskdinfo->wfd)
+	comm_close(rfd);
+    fd_note(diskdinfo->wfd, "squid -> diskd");
+    commSetTimeout(diskdinfo->wfd, -1, NULL, NULL);
+    commSetNonBlocking(diskdinfo->wfd);
+    
+    commonUfsDirInit (sd);
+    
+    comm_quick_poll_required();
+}
+
+
+static void
+storeDiskdStats(StoreEntry * sentry)
+{
+    storeAppendPrintf(sentry, "sent_count: %d\n", diskd_stats.sent_count);
+    storeAppendPrintf(sentry, "recv_count: %d\n", diskd_stats.recv_count);
+    storeAppendPrintf(sentry, "max_away: %d\n", diskd_stats.max_away);
+    storeAppendPrintf(sentry, "max_shmuse: %d\n", diskd_stats.max_shmuse);
+    storeAppendPrintf(sentry, "open_fail_queue_len: %d\n", diskd_stats.open_fail_queue_len);
+    storeAppendPrintf(sentry, "block_queue_len: %d\n", diskd_stats.block_queue_len);
+    diskd_stats.max_away = diskd_stats.max_shmuse = 0;
+    storeAppendPrintf(sentry, "\n             OPS SUCCESS    FAIL\n");
+    storeAppendPrintf(sentry, "%7s %7d %7d %7d\n",
+	"open", diskd_stats.open.ops, diskd_stats.open.success, diskd_stats.open.fail);
+    storeAppendPrintf(sentry, "%7s %7d %7d %7d\n",
+	"create", diskd_stats.create.ops, diskd_stats.create.success, diskd_stats.create.fail);
+    storeAppendPrintf(sentry, "%7s %7d %7d %7d\n",
+	"close", diskd_stats.close.ops, diskd_stats.close.success, diskd_stats.close.fail);
+    storeAppendPrintf(sentry, "%7s %7d %7d %7d\n",
+	"unlink", diskd_stats.unlink.ops, diskd_stats.unlink.success, diskd_stats.unlink.fail);
+    storeAppendPrintf(sentry, "%7s %7d %7d %7d\n",
+	"read", diskd_stats.read.ops, diskd_stats.read.success, diskd_stats.read.fail);
+    storeAppendPrintf(sentry, "%7s %7d %7d %7d\n",
+	"write", diskd_stats.write.ops, diskd_stats.write.success, diskd_stats.write.fail);
+}
+
+/*
+ * storeDiskdDirSync
+ *
+ * Sync any pending data. We just sit around and read the queue
+ * until the data has finished writing.
+ */
+static void
+storeDiskdDirSync(SwapDir * SD)
+{
+    static time_t lastmsg = 0;
+    diskdinfo_t *diskdinfo = (diskdinfo_t *)SD->fsdata;
+    while (diskdinfo->away > 0) {
+	if (squid_curtime > lastmsg) {
+	    debug(47, 1) ("storeDiskdDirSync: %d messages away\n",
+		diskdinfo->away);
+	    lastmsg = squid_curtime;
+	}
+	storeDiskdDirCallback(SD);
+    }
+}
+
+
+/*
+ * storeDiskdDirCallback
+ *
+ * Handle callbacks. If we have more than magic2 requests away, we block
+ * until the queue is below magic2. Otherwise, we simply return when we
+ * don't get a message.
+ */
+int
+storeDiskdDirCallback(SwapDir * SD)
+{
+    diomsg M;
+    int x;
+    diskdinfo_t *diskdinfo = (diskdinfo_t *)SD->fsdata;
+    int retval = 0;
+
+    if (diskdinfo->away >= diskdinfo->magic2) {
+	diskd_stats.block_queue_len++;
+	retval = 1;		/* We might not have anything to do, but our queue
+				 * is full.. */
+    }
+    if (diskd_stats.sent_count - diskd_stats.recv_count >
+	diskd_stats.max_away) {
+	diskd_stats.max_away = diskd_stats.sent_count - diskd_stats.recv_count;
+    }
+    while (1) {
+	memset(&M, '\0', sizeof(M));
+	x = msgrcv(diskdinfo->rmsgid, &M, msg_snd_rcv_sz, 0, IPC_NOWAIT);
+	if (x < 0)
+	    break;
+	else if (x != msg_snd_rcv_sz) {
+	    debug(47, 1) ("storeDiskdDirCallback: msgget returns %d\n",
+		x);
+	    break;
+	}
+	diskd_stats.recv_count++;
+	diskdinfo->away--;
+	storeDiskdHandle(&M);
+	retval = 1;		/* Return that we've actually done some work */
+	if (M.shm_offset > -1)
+	    storeDiskdShmPut(SD, (off_t) M.shm_offset);
+    }
+    return retval;
+}
+
+/*
+ * storeDiskdDirCheckObj
+ *
+ * This routine is called by storeDirSelectSwapDir to see if the given
+ * object is able to be stored on this filesystem. DISKD filesystems will
+ * happily store anything as long as the LRU time isn't too small.
+ */
+int
+storeDiskdDirCheckObj(SwapDir * SD, const StoreEntry * e)
+{
+    diskdinfo_t *diskdinfo = (diskdinfo_t *)SD->fsdata;
+    /* Check the queue length */
+    if (diskdinfo->away >= diskdinfo->magic1)
+	return -1;
+    /* Calculate the storedir load relative to magic2 on a scale of 0 .. 1000 */
+    /* the parse function guarantees magic2 is positivie */
+    return diskdinfo->away * 1000 / diskdinfo->magic2;
+}
+
+void
+storeDiskdDirIOUnlinkFile(char *path)
+{
+#if USE_UNLINKD
+    unlinkdUnlink(path);
+#elif USE_TRUNCATE
+    truncate(path, 0);
+#else
+    unlink(path);
+#endif
+		
+}
+
+/*
+ * SHM manipulation routines
+ */
+
+void *
+storeDiskdShmGet(SwapDir * sd, off_t * shm_offset)
+{
+    char *buf = NULL;
+    diskdinfo_t *diskdinfo = (diskdinfo_t *)sd->fsdata;
+    int i;
+    for (i = 0; i < diskdinfo->shm.nbufs; i++) {
+	if (CBIT_TEST(diskdinfo->shm.inuse_map, i))
+	    continue;
+	CBIT_SET(diskdinfo->shm.inuse_map, i);
+	*shm_offset = i * SHMBUF_BLKSZ;
+	buf = diskdinfo->shm.buf + (*shm_offset);
+	break;
+    }
+    assert(buf);
+    assert(buf >= diskdinfo->shm.buf);
+    assert(buf < diskdinfo->shm.buf + (diskdinfo->shm.nbufs * SHMBUF_BLKSZ));
+    diskd_stats.shmbuf_count++;
+    if (diskd_stats.max_shmuse < diskd_stats.shmbuf_count)
+	diskd_stats.max_shmuse = diskd_stats.shmbuf_count;
+    return buf;
+}
+
+void
+storeDiskdShmPut(SwapDir * sd, off_t offset)
+{
+    int i;
+    diskdinfo_t *diskdinfo = (diskdinfo_t *)sd->fsdata;
+    assert(offset >= 0);
+    assert(offset < diskdinfo->shm.nbufs * SHMBUF_BLKSZ);
+    i = offset / SHMBUF_BLKSZ;
+    assert(i < diskdinfo->shm.nbufs);
+    assert(CBIT_TEST(diskdinfo->shm.inuse_map, i));
+    CBIT_CLR(diskdinfo->shm.inuse_map, i);
+    diskd_stats.shmbuf_count--;
+}
+
+
+
+
+/* ========== LOCAL FUNCTIONS ABOVE, GLOBAL FUNCTIONS BELOW ========== */
+
+void
+storeDiskdDirStats(SwapDir * SD, StoreEntry * sentry)
+{
+    diskdinfo_t *diskdinfo = (diskdinfo_t *)SD->fsdata;
+    commonUfsDirStats (SD, sentry);
+    storeAppendPrintf(sentry, "Pending operations: %d\n", diskdinfo->away);
+}
+
+static void
+storeDiskdDirParseQ1(SwapDir * sd, const char *name, const char *value, int reconfiguring)
+{
+    diskdinfo_t *diskdinfo = (diskdinfo_t *)sd->fsdata;
+    int old_magic1 = diskdinfo->magic1;
+    diskdinfo->magic1 = atoi(value);
+    if (!reconfiguring)
+	return;
+    if (old_magic1 < diskdinfo->magic1) {
+       /*
+	* This is because shm.nbufs is computed at startup, when
+	* we call shmget().  We can't increase the Q1/Q2 parameters
+	* beyond their initial values because then we might have
+	* more "Q2 messages" than shared memory chunks, and this
+	* will cause an assertion in storeDiskdShmGet().
+	*/
+       debug(3, 1) ("WARNING: cannot increase cache_dir '%s' Q1 value while Squid is running.\n", sd->path);
+       diskdinfo->magic1 = old_magic1;
+       return;
+    }
+    if (old_magic1 != diskdinfo->magic1)
+	debug(3, 1) ("cache_dir '%s' new Q1 value '%d'\n",
+	    sd->path, diskdinfo->magic1);
+}
+
+static void
+storeDiskdDirDumpQ1(StoreEntry * e, const char *option, SwapDir * sd)
+{
+    diskdinfo_t *diskdinfo = (diskdinfo_t *)sd->fsdata;
+    storeAppendPrintf(e, " Q1=%d", diskdinfo->magic1);
+}
+
+static void
+storeDiskdDirParseQ2(SwapDir * sd, const char *name, const char *value, int reconfiguring)
+{
+    diskdinfo_t *diskdinfo = (diskdinfo_t *)sd->fsdata;
+    int old_magic2 = diskdinfo->magic2;
+    diskdinfo->magic2 = atoi(value);
+    if (!reconfiguring)
+       return;
+    if (old_magic2 < diskdinfo->magic2) {
+       /* See comments in Q1 function above */
+       debug(3, 1) ("WARNING: cannot increase cache_dir '%s' Q2 value while Squid is running.\n", sd->path);
+       diskdinfo->magic2 = old_magic2;
+       return;
+    }
+    if (old_magic2 != diskdinfo->magic2)
+	debug(3, 1) ("cache_dir '%s' new Q2 value '%d'\n",
+	    sd->path, diskdinfo->magic2);
+}
+
+static void
+storeDiskdDirDumpQ2(StoreEntry * e, const char *option, SwapDir * sd)
+{
+    diskdinfo_t *diskdinfo = (diskdinfo_t *)sd->fsdata;
+    storeAppendPrintf(e, " Q2=%d", diskdinfo->magic2);
+}
+
+struct cache_dir_option options[] =
+{
+#if NOT_YET
+    {"L1", storeDiskdDirParseL1, storeDiskdDirDumpL1},
+    {"L2", storeDiskdDirParseL2, storeDiskdDirDumpL2},
+#endif
+    {"Q1", storeDiskdDirParseQ1, storeDiskdDirDumpQ1},
+    {"Q2", storeDiskdDirParseQ2, storeDiskdDirDumpQ2},
+    {NULL, NULL}
+};
+
+/*
+ * storeDiskdDirReconfigure
+ *
+ * This routine is called when the given swapdir needs reconfiguring 
+ */
+static void
+storeDiskdDirReconfigure(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("storeDiskdDirReconfigure: invalid size value");
+    i = GetInteger();
+    l1 = i;
+    if (l1 <= 0)
+	fatal("storeDiskdDirReconfigure: invalid level 1 directories value");
+    i = GetInteger();
+    l2 = i;
+    if (l2 <= 0)
+	fatal("storeDiskdDirReconfigure: 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, 1);
+}
+
+void
+storeDiskdDirDump(StoreEntry * entry, SwapDir * s)
+{
+    commonUfsDirDump (entry, s);
+    dump_cachedir_options(entry, options, s);
+}
+
+/*
+ * storeDiskdDirParse
+ *
+ * Called when a *new* fs is being setup.
+ */
+static void
+storeDiskdDirParse(SwapDir * sd, int index, char *path)
+{
+    int i;
+    int size;
+    int l1;
+    int l2;
+    diskdinfo_t *diskdinfo;
+
+    i = GetInteger();
+    size = i << 10;		/* Mbytes to kbytes */
+    if (size <= 0)
+	fatal("storeDiskdDirParse: invalid size value");
+    i = GetInteger();
+    l1 = i;
+    if (l1 <= 0)
+	fatal("storeDiskdDirParse: invalid level 1 directories value");
+    i = GetInteger();
+    l2 = i;
+    if (l2 <= 0)
+	fatal("storeDiskdDirParse: invalid level 2 directories value");
+
+    sd->fsdata = diskdinfo = (diskdinfo_t *)xcalloc(1, sizeof(*diskdinfo));
+    sd->index = index;
+    sd->path = xstrdup(path);
+    sd->max_size = size;
+    diskdinfo->commondata.l1 = l1;
+    diskdinfo->commondata.l2 = l2;
+    diskdinfo->commondata.swaplog_fd = -1;
+    diskdinfo->commondata.map = NULL;	/* Debugging purposes */
+    diskdinfo->commondata.suggest = 0;
+    diskdinfo->commondata.io.storeDirUnlinkFile = storeDiskdDirIOUnlinkFile;
+    diskdinfo->magic1 = 64;
+    diskdinfo->magic2 = 72;
+    sd->init = storeDiskdDirInit;
+    sd->newfs = commonUfsDirNewfs;
+    sd->dump = storeDiskdDirDump;
+    sd->freefs = commonUfsDirFree;
+    sd->dblcheck = commonUfsCleanupDoubleCheck;
+    sd->statfs = storeDiskdDirStats;
+    sd->maintainfs = commonUfsDirMaintain;
+    sd->checkobj = storeDiskdDirCheckObj;
+    sd->refobj = commonUfsDirRefObj;
+    sd->unrefobj = commonUfsDirUnrefObj;
+    sd->callback = storeDiskdDirCallback;
+    sd->sync = storeDiskdDirSync;
+    sd->obj.create = storeDiskdCreate;
+    sd->obj.open = storeDiskdOpen;
+    sd->obj.close = storeDiskdClose;
+    sd->obj.read = storeDiskdRead;
+    sd->obj.write = storeDiskdWrite;
+    sd->obj.unlink = storeDiskdUnlink;
+    sd->log.open = commonUfsDirOpenSwapLog;
+    sd->log.close = commonUfsDirCloseSwapLog;
+    sd->log.write = commonUfsDirSwapLog;
+    sd->log.clean.start = commonUfsDirWriteCleanStart;
+    sd->log.clean.nextentry = commonUfsDirCleanLogNextEntry;
+    sd->log.clean.done = commonUfsDirWriteCleanDone;
+
+    parse_cachedir_options(sd, options, 0);
+
+    /* Initialise replacement policy stuff */
+    sd->repl = createRemovalPolicy(Config.replPolicy);
+}
+
+/*
+ * Initial setup / end destruction
+ */
+static void
+storeDiskdDirDone(void)
+{
+    memPoolDestroy(&diskd_state_pool);
+    diskd_initialised = 0;
+}
+
+void
+storeFsSetup_diskd(storefs_entry_t * storefs)
+{
+    assert(!diskd_initialised);
+    storefs->parsefunc = storeDiskdDirParse;
+    storefs->reconfigurefunc = storeDiskdDirReconfigure;
+    storefs->donefunc = storeDiskdDirDone;
+    diskd_state_pool = memPoolCreate("DISKD IO State data", sizeof(diskdstate_t));
+    memset(&diskd_stats, '\0', sizeof(diskd_stats));
+    cachemgrRegister("diskd", "DISKD Stats", storeDiskdStats, 0, 1);
+    debug(47, 1) ("diskd started\n");
+    diskd_initialised = 1;
+}
--- squid/src/fs/diskd/store_io_diskd.c	Wed Feb 14 01:07:42 2007
+++ /dev/null	Wed Feb 14 01:07:22 2007
@@ -1,520 +0,0 @@
-
-/*
- * $Id$
- *
- * DEBUG: section 79    Squid-side DISKD I/O functions.
- * 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 "config.h"
-#include "squid.h"
-#include "Store.h"
-
-#include <sys/ipc.h>
-#include <sys/msg.h>
-#include <sys/shm.h>
-
-#include "store_diskd.h"
-
-static int storeDiskdSend(int, SwapDir *, int, storeIOState *, int, int, off_t);
-static void storeDiskdIOCallback(storeIOState * sio, int errflag);
-static CBDUNL storeDiskdIOFreeEntry;
-
-CBDATA_TYPE(storeIOState);
-
-/* === PUBLIC =========================================================== */
-
-storeIOState *
-storeDiskdOpen(SwapDir * SD, StoreEntry * e, STFNCB * file_callback,
-    STIOCB * callback, void *callback_data)
-{
-    sfileno f = e->swap_filen;
-    int x;
-    storeIOState *sio;
-    char *buf;
-    diskdstate_t *diskdstate;
-    off_t shm_offset;
-    diskdinfo_t *diskdinfo = SD->fsdata;
-    debug(79, 3) ("storeDiskdOpen: fileno %08X\n", f);
-    /*
-     * Fail on open() if there are too many requests queued.
-     */
-    if (diskdinfo->away > diskdinfo->magic1) {
-	debug(79, 3) ("storeDiskdOpen: FAILING, too many requests away\n");
-	diskd_stats.open_fail_queue_len++;
-	return NULL;
-    }
-    CBDATA_INIT_TYPE_FREECB(storeIOState, storeDiskdIOFreeEntry);
-    sio = cbdataAlloc(storeIOState);
-    sio->fsstate = diskdstate = memPoolAlloc(diskd_state_pool);
-
-    sio->swap_filen = f;
-    sio->swap_dirn = SD->index;
-    sio->mode = O_RDONLY | O_BINARY;
-    sio->callback = callback;
-    sio->callback_data = cbdataReference(callback_data);
-    sio->e = e;
-
-    diskdstate->flags.writing = 0;
-    diskdstate->flags.reading = 0;
-    diskdstate->flags.close_request = 0;
-    diskdstate->id = diskd_stats.sio_id++;
-
-    buf = storeDiskdShmGet(SD, &shm_offset);
-    xstrncpy(buf, commonUfsDirFullPath(SD, f, NULL), SHMBUF_BLKSZ);
-    x = storeDiskdSend(_MQD_OPEN,
-	SD,
-	diskdstate->id,
-	sio,
-	strlen(buf) + 1,
-	O_RDONLY,
-	shm_offset);
-    if (x < 0) {
-	debug(79, 1) ("storeDiskdSend OPEN: %s\n", xstrerror());
-	storeDiskdShmPut(SD, shm_offset);
-	cbdataReferenceDone(sio->callback_data);
-	cbdataFree(sio);
-	return NULL;
-    }
-    diskd_stats.open.ops++;
-    return sio;
-}
-
-storeIOState *
-storeDiskdCreate(SwapDir * SD, StoreEntry * e, STFNCB * file_callback,
-    STIOCB * callback, void *callback_data)
-{
-    sfileno f;
-    int x;
-    storeIOState *sio;
-    char *buf;
-    off_t shm_offset;
-    diskdinfo_t *diskdinfo = SD->fsdata;
-    diskdstate_t *diskdstate;
-    /*
-     * Fail on open() if there are too many requests queued.
-     */
-    if (diskdinfo->away > diskdinfo->magic1) {
-	diskd_stats.open_fail_queue_len++;
-	return NULL;
-    }
-    /* Allocate a number */
-    f = commonUfsDirMapBitAllocate(SD);
-    debug(79, 3) ("storeDiskdCreate: fileno %08X\n", f);
-
-    CBDATA_INIT_TYPE_FREECB(storeIOState, storeDiskdIOFreeEntry);
-    sio = cbdataAlloc(storeIOState);
-    sio->fsstate = diskdstate = memPoolAlloc(diskd_state_pool);
-
-    sio->swap_filen = f;
-    sio->swap_dirn = SD->index;
-    sio->mode = O_WRONLY | O_CREAT | O_TRUNC;
-    sio->callback = callback;
-    sio->callback_data = cbdataReference(callback_data);
-    sio->e = e;
-
-    diskdstate->flags.writing = 0;
-    diskdstate->flags.reading = 0;
-    diskdstate->flags.close_request = 0;
-    diskdstate->id = diskd_stats.sio_id++;
-
-    buf = storeDiskdShmGet(SD, &shm_offset);
-    xstrncpy(buf, commonUfsDirFullPath(SD, f, NULL), SHMBUF_BLKSZ);
-    x = storeDiskdSend(_MQD_OPEN,
-	SD,
-	diskdstate->id,
-	sio,
-	strlen(buf) + 1,
-	sio->mode,
-	shm_offset);
-    if (x < 0) {
-	debug(79, 1) ("storeDiskdSend OPEN: %s\n", xstrerror());
-	storeDiskdShmPut(SD, shm_offset);
-	cbdataReferenceDone(sio->callback_data);
-	cbdataFree(sio);
-	return NULL;
-    }
-    commonUfsDirReplAdd(SD, e);
-    diskd_stats.create.ops++;
-    return sio;
-}
-
-
-void
-storeDiskdClose(SwapDir * SD, storeIOState * sio)
-{
-    int x;
-    diskdstate_t *diskdstate = sio->fsstate;
-    debug(79, 3) ("storeDiskdClose: dirno %d, fileno %08X\n", SD->index,
-	sio->swap_filen);
-    x = storeDiskdSend(_MQD_CLOSE,
-	SD,
-	diskdstate->id,
-	sio,
-	0,
-	0,
-	-1);
-    if (x < 0) {
-	debug(79, 1) ("storeDiskdSend CLOSE: %s\n", xstrerror());
-	storeDiskdIOCallback(sio, DISK_ERROR);
-    }
-    diskdstate->flags.close_request = 1;
-    diskd_stats.close.ops++;
-}
-
-void
-storeDiskdRead(SwapDir * SD, storeIOState * sio, char *buf, size_t size, off_t offset, STRCB * callback, void *callback_data)
-{
-    int x;
-    off_t shm_offset;
-    char *rbuf;
-    diskdstate_t *diskdstate = sio->fsstate;
-    debug(79, 3) ("storeDiskdRead: dirno %d, fileno %08X\n", sio->swap_dirn, sio->swap_filen);
-    assert(!diskdstate->flags.close_request);
-    if (!cbdataReferenceValid(sio))
-	return;
-    if (diskdstate->flags.reading) {
-	debug(79, 1) ("storeDiskdRead: already reading!\n");
-	return;
-    }
-    assert(sio->read.callback == NULL);
-    assert(sio->read.callback_data == NULL);
-    sio->read.callback = callback;
-    sio->read.callback_data = cbdataReference(callback_data);
-    diskdstate->read_buf = buf;	/* the one passed from above */
-    sio->offset = offset;
-    diskdstate->flags.reading = 1;
-    rbuf = storeDiskdShmGet(SD, &shm_offset);
-    assert(rbuf);
-    x = storeDiskdSend(_MQD_READ,
-	SD,
-	diskdstate->id,
-	sio,
-	(int) size,
-	(int) offset,
-	shm_offset);
-    if (x < 0) {
-	debug(79, 1) ("storeDiskdSend READ: %s\n", xstrerror());
-	storeDiskdShmPut(SD, shm_offset);
-	storeDiskdIOCallback(sio, DISK_ERROR);
-    }
-    diskd_stats.read.ops++;
-}
-
-void
-storeDiskdWrite(SwapDir * SD, storeIOState * sio, char *buf, size_t size, off_t offset, FREE * free_func)
-{
-    int x;
-    char *sbuf;
-    off_t shm_offset;
-    diskdstate_t *diskdstate = sio->fsstate;
-    debug(79, 3) ("storeDiskdWrite: dirno %d, fileno %08X\n", SD->index, sio->swap_filen);
-    assert(!diskdstate->flags.close_request);
-    if (!cbdataReferenceValid(sio)) {
-	free_func(buf);
-	return;
-    }
-    diskdstate->flags.writing = 1;
-    sbuf = storeDiskdShmGet(SD, &shm_offset);
-    xmemcpy(sbuf, buf, size);
-    if (free_func)
-	free_func(buf);
-    x = storeDiskdSend(_MQD_WRITE,
-	SD,
-	diskdstate->id,
-	sio,
-	(int) size,
-	(int) offset,
-	shm_offset);
-    if (x < 0) {
-	debug(79, 1) ("storeDiskdSend WRITE: %s\n", xstrerror());
-	storeDiskdShmPut(SD, shm_offset);
-	storeDiskdIOCallback(sio, DISK_ERROR);
-    }
-    diskd_stats.write.ops++;
-}
-
-void
-storeDiskdUnlink(SwapDir * SD, StoreEntry * e)
-{
-    int x;
-    off_t shm_offset;
-    char *buf;
-    diskdinfo_t *diskdinfo = SD->fsdata;
-
-    debug(79, 3) ("storeDiskdUnlink: dirno %d, fileno %08X\n", SD->index,
-	e->swap_filen);
-    commonUfsDirReplRemove(e);
-    commonUfsDirMapBitReset(SD, e->swap_filen);
-    if (diskdinfo->away >= diskdinfo->magic1) {
-	/* Damn, we need to issue a sync unlink here :( */
-	debug(79, 2) ("storeDiskUnlink: Out of queue space, sync unlink\n");
-	commonUfsDirUnlinkFile(SD, e->swap_filen);
-	return;
-    }
-    /* We can attempt a diskd unlink */
-    buf = storeDiskdShmGet(SD, &shm_offset);
-    xstrncpy(buf, commonUfsDirFullPath(SD, e->swap_filen, NULL), SHMBUF_BLKSZ);
-    x = storeDiskdSend(_MQD_UNLINK,
-	SD,
-	e->swap_filen,
-	NULL,
-	0,
-	0,
-	shm_offset);
-    if (x < 0) {
-	debug(79, 1) ("storeDiskdSend UNLINK: %s\n", xstrerror());
-	unlink(buf);		/* XXX EWW! */
-	storeDiskdShmPut(SD, shm_offset);
-    }
-    diskd_stats.unlink.ops++;
-}
-
-
-/*  === STATIC =========================================================== */
-
-static void
-storeDiskdOpenDone(diomsg * M)
-{
-    storeIOState *sio = M->callback_data;
-    statCounter.syscalls.disk.opens++;
-    debug(79, 3) ("storeDiskdOpenDone: dirno %d, fileno %08x status %d\n",
-	sio->swap_dirn, sio->swap_filen, M->status);
-    if (M->status < 0) {
-	FILE_MODE(sio->mode) == O_RDONLY ? diskd_stats.open.fail++ : diskd_stats.create.fail++;
-	storeDiskdIOCallback(sio, DISK_ERROR);
-    } else {
-	FILE_MODE(sio->mode) == O_RDONLY ? diskd_stats.open.success++ : diskd_stats.create.success++;
-    }
-}
-
-static void
-storeDiskdCloseDone(diomsg * M)
-{
-    storeIOState *sio = M->callback_data;
-    statCounter.syscalls.disk.closes++;
-    debug(79, 3) ("storeDiskdCloseDone: dirno %d, fileno %08x status %d\n",
-	sio->swap_dirn, sio->swap_filen, M->status);
-    if (M->status < 0) {
-	diskd_stats.close.fail++;
-	storeDiskdIOCallback(sio, DISK_ERROR);
-	return;
-    }
-    diskd_stats.close.success++;
-    storeDiskdIOCallback(sio, DISK_OK);
-}
-
-static void
-storeDiskdReadDone(diomsg * M)
-{
-    storeIOState *sio = M->callback_data;
-    STRCB *callback = sio->read.callback;
-    void *cbdata;
-    SwapDir *sd = INDEXSD(sio->swap_dirn);
-    diskdstate_t *diskdstate = sio->fsstate;
-    diskdinfo_t *diskdinfo = sd->fsdata;
-    char *their_buf = diskdstate->read_buf;
-    char *sbuf;
-    size_t len;
-    statCounter.syscalls.disk.reads++;
-    diskdstate->flags.reading = 0;
-    debug(79, 3) ("storeDiskdReadDone: dirno %d, fileno %08x status %d\n",
-	sio->swap_dirn, sio->swap_filen, M->status);
-    if (M->status < 0) {
-	diskd_stats.read.fail++;
-	storeDiskdIOCallback(sio, DISK_ERROR);
-	return;
-    }
-    diskd_stats.read.success++;
-    sbuf = diskdinfo->shm.buf + M->shm_offset;
-    len = M->status;
-    sio->offset += len;
-    assert(callback);
-    sio->read.callback = NULL;
-    if (cbdataReferenceValidDone(sio->read.callback_data, &cbdata)) {
-	assert(!diskdstate->flags.close_request);
-	/*
-	 * Only copy the data if the callback is still valid,
-	 * if it isn't valid then the request should have been
-	 * aborted.
-	 *   -- adrian
-	 */
-	xmemcpy(their_buf, sbuf, len);	/* yucky copy */
-	callback(cbdata, their_buf, len);
-    }
-}
-
-static void
-storeDiskdWriteDone(diomsg * M)
-{
-    storeIOState *sio = M->callback_data;
-    diskdstate_t *diskdstate = sio->fsstate;
-    statCounter.syscalls.disk.writes++;
-    diskdstate->flags.writing = 0;
-    debug(79, 3) ("storeDiskdWriteDone: dirno %d, fileno %08x status %d\n",
-	sio->swap_dirn, sio->swap_filen, M->status);
-    if (M->status < 0) {
-	diskd_stats.write.fail++;
-	storeDiskdIOCallback(sio, DISK_ERROR);
-	return;
-    }
-    diskd_stats.write.success++;
-    sio->offset += M->status;
-}
-
-static void
-storeDiskdUnlinkDone(diomsg * M)
-{
-    debug(79, 3) ("storeDiskdUnlinkDone: fileno %08x status %d\n",
-	M->id, M->status);
-    statCounter.syscalls.disk.unlinks++;
-    if (M->status < 0)
-	diskd_stats.unlink.fail++;
-    else
-	diskd_stats.unlink.success++;
-}
-
-void
-storeDiskdHandle(diomsg * M)
-{
-    if (cbdataReferenceValid(M->callback_data)) {
-	switch (M->mtype) {
-	case _MQD_OPEN:
-	    storeDiskdOpenDone(M);
-	    break;
-	case _MQD_CLOSE:
-	    storeDiskdCloseDone(M);
-	    break;
-	case _MQD_READ:
-	    storeDiskdReadDone(M);
-	    break;
-	case _MQD_WRITE:
-	    storeDiskdWriteDone(M);
-	    break;
-	case _MQD_UNLINK:
-	    storeDiskdUnlinkDone(M);
-	    break;
-	default:
-	    assert(0);
-	    break;
-	}
-    } else {
-	debug(79, 3) ("storeDiskdHandle: Invalid callback_data %p\n",
-	    M->callback_data);
-	/*
-	 * The read operation has its own callback.  If we don't
-	 * call storeDiskdReadDone(), then we must make sure the
-	 * callback_data gets unlocked!
-	 */
-	if (_MQD_READ == M->mtype) {
-	    /* XXX This cannot be the correct approach. This
-	     * is most likely the wrong place for this. It should
-	     * be done before the sio becomes invalid, not here.
-	     */
-	    storeIOState *sio = M->callback_data;
-	    cbdataReferenceDone(sio->read.callback_data);
-	}
-    }
-    cbdataReferenceDone(M->callback_data);
-}
-
-static void
-storeDiskdIOCallback(storeIOState * sio, int errflag)
-{
-    void *cbdata;
-    STIOCB *callback = sio->callback;
-    debug(79, 3) ("storeUfsIOCallback: errflag=%d\n", errflag);
-    sio->callback = NULL;
-    if (cbdataReferenceValidDone(sio->callback_data, &cbdata))
-	callback(cbdata, errflag, sio);
-    cbdataFree(sio);
-}
-
-static int
-storeDiskdSend(int mtype, SwapDir * sd, int id, storeIOState * sio, int size, int offset, off_t shm_offset)
-{
-    int x;
-    diomsg M;
-    static int send_errors = 0;
-    static int last_seq_no = 0;
-    static int seq_no = 0;
-    diskdinfo_t *diskdinfo = sd->fsdata;
-    M.mtype = mtype;
-    M.callback_data = cbdataReference(sio);
-    M.size = size;
-    M.offset = offset;
-    M.status = -1;
-    M.shm_offset = (int) shm_offset;
-    M.id = id;
-    M.seq_no = ++seq_no;
-    if (M.seq_no < last_seq_no)
-	debug(79, 1) ("WARNING: sequencing out of order\n");
-    x = msgsnd(diskdinfo->smsgid, &M, msg_snd_rcv_sz, IPC_NOWAIT);
-    last_seq_no = M.seq_no;
-    if (0 == x) {
-	diskd_stats.sent_count++;
-	diskdinfo->away++;
-    } else {
-	debug(79, 1) ("storeDiskdSend: msgsnd: %s\n", xstrerror());
-	cbdataReferenceDone(M.callback_data);
-	assert(++send_errors < 100);
-    }
-    /*
-     * We have to drain the queue here if necessary.  If we don't,
-     * then we can have a lot of messages in the queue (probably
-     * up to 2*magic1) and we can run out of shared memory buffers.
-     */
-    /*
-     * Note that we call storeDirCallback (for all SDs), rather
-     * than storeDiskdDirCallback for just this SD, so that while
-     * we're "blocking" on this SD we can also handle callbacks
-     * from other SDs that might be ready.
-     */
-    while (diskdinfo->away > diskdinfo->magic2) {
-	struct timeval delay =
-	{0, 1};
-	select(0, NULL, NULL, NULL, &delay);
-	storeDirCallback();
-	if (delay.tv_usec < 1000000)
-	    delay.tv_usec <<= 1;
-    }
-    return x;
-}
-
-
-/*
- * We can't pass memFree() as a free function here, because we need to free
- * the fsstate variable ..
- */
-static void
-storeDiskdIOFreeEntry(void *sio)
-{
-    memPoolFree(diskd_state_pool, ((storeIOState *) sio)->fsstate);
-}
--- /dev/null	Wed Feb 14 01:07:22 2007
+++ squid/src/fs/diskd/store_io_diskd.cc	Wed Feb 14 01:07:42 2007
@@ -0,0 +1,520 @@
+
+/*
+ * $Id: store_io_diskd.cc,v 1.1.2.1 2002/10/12 12:21:43 rbcollins Exp $
+ *
+ * DEBUG: section 79    Squid-side DISKD I/O functions.
+ * 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 "config.h"
+#include "squid.h"
+#include "Store.h"
+
+#include <sys/ipc.h>
+#include <sys/msg.h>
+#include <sys/shm.h>
+
+#include "store_diskd.h"
+
+static int storeDiskdSend(int, SwapDir *, int, storeIOState *, int, int, off_t);
+static void storeDiskdIOCallback(storeIOState * sio, int errflag);
+static CBDUNL storeDiskdIOFreeEntry;
+
+CBDATA_TYPE(storeIOState);
+
+/* === PUBLIC =========================================================== */
+
+storeIOState *
+storeDiskdOpen(SwapDir * SD, StoreEntry * e, STFNCB * file_callback,
+    STIOCB * callback, void *callback_data)
+{
+    sfileno f = e->swap_filen;
+    int x;
+    storeIOState *sio;
+    char *buf;
+    diskdstate_t *diskdstate;
+    off_t shm_offset;
+    diskdinfo_t *diskdinfo = (diskdinfo_t *)SD->fsdata;
+    debug(79, 3) ("storeDiskdOpen: fileno %08X\n", f);
+    /*
+     * Fail on open() if there are too many requests queued.
+     */
+    if (diskdinfo->away > diskdinfo->magic1) {
+	debug(79, 3) ("storeDiskdOpen: FAILING, too many requests away\n");
+	diskd_stats.open_fail_queue_len++;
+	return NULL;
+    }
+    CBDATA_INIT_TYPE_FREECB(storeIOState, storeDiskdIOFreeEntry);
+    sio = cbdataAlloc(storeIOState);
+    sio->fsstate = diskdstate = (diskdstate_t *)memPoolAlloc(diskd_state_pool);
+
+    sio->swap_filen = f;
+    sio->swap_dirn = SD->index;
+    sio->mode = O_RDONLY | O_BINARY;
+    sio->callback = callback;
+    sio->callback_data = cbdataReference(callback_data);
+    sio->e = e;
+
+    diskdstate->flags.writing = 0;
+    diskdstate->flags.reading = 0;
+    diskdstate->flags.close_request = 0;
+    diskdstate->id = diskd_stats.sio_id++;
+
+    buf = (char *)storeDiskdShmGet(SD, &shm_offset);
+    xstrncpy(buf, commonUfsDirFullPath(SD, f, NULL), SHMBUF_BLKSZ);
+    x = storeDiskdSend(_MQD_OPEN,
+	SD,
+	diskdstate->id,
+	sio,
+	strlen(buf) + 1,
+	O_RDONLY,
+	shm_offset);
+    if (x < 0) {
+	debug(79, 1) ("storeDiskdSend OPEN: %s\n", xstrerror());
+	storeDiskdShmPut(SD, shm_offset);
+	cbdataReferenceDone(sio->callback_data);
+	cbdataFree(sio);
+	return NULL;
+    }
+    diskd_stats.open.ops++;
+    return sio;
+}
+
+storeIOState *
+storeDiskdCreate(SwapDir * SD, StoreEntry * e, STFNCB * file_callback,
+    STIOCB * callback, void *callback_data)
+{
+    sfileno f;
+    int x;
+    storeIOState *sio;
+    char *buf;
+    off_t shm_offset;
+    diskdinfo_t *diskdinfo = (diskdinfo_t *)SD->fsdata;
+    diskdstate_t *diskdstate;
+    /*
+     * Fail on open() if there are too many requests queued.
+     */
+    if (diskdinfo->away > diskdinfo->magic1) {
+	diskd_stats.open_fail_queue_len++;
+	return NULL;
+    }
+    /* Allocate a number */
+    f = commonUfsDirMapBitAllocate(SD);
+    debug(79, 3) ("storeDiskdCreate: fileno %08X\n", f);
+
+    CBDATA_INIT_TYPE_FREECB(storeIOState, storeDiskdIOFreeEntry);
+    sio = cbdataAlloc(storeIOState);
+    sio->fsstate = diskdstate = (diskdstate_t *)memPoolAlloc(diskd_state_pool);
+
+    sio->swap_filen = f;
+    sio->swap_dirn = SD->index;
+    sio->mode = O_WRONLY | O_CREAT | O_TRUNC;
+    sio->callback = callback;
+    sio->callback_data = cbdataReference(callback_data);
+    sio->e = e;
+
+    diskdstate->flags.writing = 0;
+    diskdstate->flags.reading = 0;
+    diskdstate->flags.close_request = 0;
+    diskdstate->id = diskd_stats.sio_id++;
+
+    buf = (char *)storeDiskdShmGet(SD, &shm_offset);
+    xstrncpy(buf, commonUfsDirFullPath(SD, f, NULL), SHMBUF_BLKSZ);
+    x = storeDiskdSend(_MQD_OPEN,
+	SD,
+	diskdstate->id,
+	sio,
+	strlen(buf) + 1,
+	sio->mode,
+	shm_offset);
+    if (x < 0) {
+	debug(79, 1) ("storeDiskdSend OPEN: %s\n", xstrerror());
+	storeDiskdShmPut(SD, shm_offset);
+	cbdataReferenceDone(sio->callback_data);
+	cbdataFree(sio);
+	return NULL;
+    }
+    commonUfsDirReplAdd(SD, e);
+    diskd_stats.create.ops++;
+    return sio;
+}
+
+
+void
+storeDiskdClose(SwapDir * SD, storeIOState * sio)
+{
+    int x;
+    diskdstate_t *diskdstate = (diskdstate_t *)sio->fsstate;
+    debug(79, 3) ("storeDiskdClose: dirno %d, fileno %08X\n", SD->index,
+	sio->swap_filen);
+    x = storeDiskdSend(_MQD_CLOSE,
+	SD,
+	diskdstate->id,
+	sio,
+	0,
+	0,
+	-1);
+    if (x < 0) {
+	debug(79, 1) ("storeDiskdSend CLOSE: %s\n", xstrerror());
+	storeDiskdIOCallback(sio, DISK_ERROR);
+    }
+    diskdstate->flags.close_request = 1;
+    diskd_stats.close.ops++;
+}
+
+void
+storeDiskdRead(SwapDir * SD, storeIOState * sio, char *buf, size_t size, off_t offset, STRCB * callback, void *callback_data)
+{
+    int x;
+    off_t shm_offset;
+    char *rbuf;
+    diskdstate_t *diskdstate = (diskdstate_t *)sio->fsstate;
+    debug(79, 3) ("storeDiskdRead: dirno %d, fileno %08X\n", sio->swap_dirn, sio->swap_filen);
+    assert(!diskdstate->flags.close_request);
+    if (!cbdataReferenceValid(sio))
+	return;
+    if (diskdstate->flags.reading) {
+	debug(79, 1) ("storeDiskdRead: already reading!\n");
+	return;
+    }
+    assert(sio->read.callback == NULL);
+    assert(sio->read.callback_data == NULL);
+    sio->read.callback = callback;
+    sio->read.callback_data = cbdataReference(callback_data);
+    diskdstate->read_buf = buf;	/* the one passed from above */
+    sio->offset = offset;
+    diskdstate->flags.reading = 1;
+    rbuf = (char *)storeDiskdShmGet(SD, &shm_offset);
+    assert(rbuf);
+    x = storeDiskdSend(_MQD_READ,
+	SD,
+	diskdstate->id,
+	sio,
+	(int) size,
+	(int) offset,
+	shm_offset);
+    if (x < 0) {
+	debug(79, 1) ("storeDiskdSend READ: %s\n", xstrerror());
+	storeDiskdShmPut(SD, shm_offset);
+	storeDiskdIOCallback(sio, DISK_ERROR);
+    }
+    diskd_stats.read.ops++;
+}
+
+void
+storeDiskdWrite(SwapDir * SD, storeIOState * sio, char *buf, size_t size, off_t offset, FREE * free_func)
+{
+    int x;
+    char *sbuf;
+    off_t shm_offset;
+    diskdstate_t *diskdstate = (diskdstate_t *)sio->fsstate;
+    debug(79, 3) ("storeDiskdWrite: dirno %d, fileno %08X\n", SD->index, sio->swap_filen);
+    assert(!diskdstate->flags.close_request);
+    if (!cbdataReferenceValid(sio)) {
+	free_func(buf);
+	return;
+    }
+    diskdstate->flags.writing = 1;
+    sbuf = (char *)storeDiskdShmGet(SD, &shm_offset);
+    xmemcpy(sbuf, buf, size);
+    if (free_func)
+	free_func(buf);
+    x = storeDiskdSend(_MQD_WRITE,
+	SD,
+	diskdstate->id,
+	sio,
+	(int) size,
+	(int) offset,
+	shm_offset);
+    if (x < 0) {
+	debug(79, 1) ("storeDiskdSend WRITE: %s\n", xstrerror());
+	storeDiskdShmPut(SD, shm_offset);
+	storeDiskdIOCallback(sio, DISK_ERROR);
+    }
+    diskd_stats.write.ops++;
+}
+
+void
+storeDiskdUnlink(SwapDir * SD, StoreEntry * e)
+{
+    int x;
+    off_t shm_offset;
+    char *buf;
+    diskdinfo_t *diskdinfo = (diskdinfo_t *)SD->fsdata;
+
+    debug(79, 3) ("storeDiskdUnlink: dirno %d, fileno %08X\n", SD->index,
+	e->swap_filen);
+    commonUfsDirReplRemove(e);
+    commonUfsDirMapBitReset(SD, e->swap_filen);
+    if (diskdinfo->away >= diskdinfo->magic1) {
+	/* Damn, we need to issue a sync unlink here :( */
+	debug(79, 2) ("storeDiskUnlink: Out of queue space, sync unlink\n");
+	commonUfsDirUnlinkFile(SD, e->swap_filen);
+	return;
+    }
+    /* We can attempt a diskd unlink */
+    buf = (char *)storeDiskdShmGet(SD, &shm_offset);
+    xstrncpy(buf, commonUfsDirFullPath(SD, e->swap_filen, NULL), SHMBUF_BLKSZ);
+    x = storeDiskdSend(_MQD_UNLINK,
+	SD,
+	e->swap_filen,
+	NULL,
+	0,
+	0,
+	shm_offset);
+    if (x < 0) {
+	debug(79, 1) ("storeDiskdSend UNLINK: %s\n", xstrerror());
+	unlink(buf);		/* XXX EWW! */
+	storeDiskdShmPut(SD, shm_offset);
+    }
+    diskd_stats.unlink.ops++;
+}
+
+
+/*  === STATIC =========================================================== */
+
+static void
+storeDiskdOpenDone(diomsg * M)
+{
+    storeIOState *sio = (storeIOState *)M->callback_data;
+    statCounter.syscalls.disk.opens++;
+    debug(79, 3) ("storeDiskdOpenDone: dirno %d, fileno %08x status %d\n",
+	sio->swap_dirn, sio->swap_filen, M->status);
+    if (M->status < 0) {
+	FILE_MODE(sio->mode) == O_RDONLY ? diskd_stats.open.fail++ : diskd_stats.create.fail++;
+	storeDiskdIOCallback(sio, DISK_ERROR);
+    } else {
+	FILE_MODE(sio->mode) == O_RDONLY ? diskd_stats.open.success++ : diskd_stats.create.success++;
+    }
+}
+
+static void
+storeDiskdCloseDone(diomsg * M)
+{
+    storeIOState *sio = (storeIOState *)M->callback_data;
+    statCounter.syscalls.disk.closes++;
+    debug(79, 3) ("storeDiskdCloseDone: dirno %d, fileno %08x status %d\n",
+	sio->swap_dirn, sio->swap_filen, M->status);
+    if (M->status < 0) {
+	diskd_stats.close.fail++;
+	storeDiskdIOCallback(sio, DISK_ERROR);
+	return;
+    }
+    diskd_stats.close.success++;
+    storeDiskdIOCallback(sio, DISK_OK);
+}
+
+static void
+storeDiskdReadDone(diomsg * M)
+{
+    storeIOState *sio = (storeIOState *)M->callback_data;
+    STRCB *callback = sio->read.callback;
+    void *cbdata;
+    SwapDir *sd = INDEXSD(sio->swap_dirn);
+    diskdstate_t *diskdstate = (diskdstate_t *)sio->fsstate;
+    diskdinfo_t *diskdinfo = (diskdinfo_t *)sd->fsdata;
+    char *their_buf = diskdstate->read_buf;
+    char *sbuf;
+    size_t len;
+    statCounter.syscalls.disk.reads++;
+    diskdstate->flags.reading = 0;
+    debug(79, 3) ("storeDiskdReadDone: dirno %d, fileno %08x status %d\n",
+	sio->swap_dirn, sio->swap_filen, M->status);
+    if (M->status < 0) {
+	diskd_stats.read.fail++;
+	storeDiskdIOCallback(sio, DISK_ERROR);
+	return;
+    }
+    diskd_stats.read.success++;
+    sbuf = diskdinfo->shm.buf + M->shm_offset;
+    len = M->status;
+    sio->offset += len;
+    assert(callback);
+    sio->read.callback = NULL;
+    if (cbdataReferenceValidDone(sio->read.callback_data, &cbdata)) {
+	assert(!diskdstate->flags.close_request);
+	/*
+	 * Only copy the data if the callback is still valid,
+	 * if it isn't valid then the request should have been
+	 * aborted.
+	 *   -- adrian
+	 */
+	xmemcpy(their_buf, sbuf, len);	/* yucky copy */
+	callback(cbdata, their_buf, len);
+    }
+}
+
+static void
+storeDiskdWriteDone(diomsg * M)
+{
+    storeIOState *sio = (storeIOState *)M->callback_data;
+    diskdstate_t *diskdstate = (diskdstate_t *)sio->fsstate;
+    statCounter.syscalls.disk.writes++;
+    diskdstate->flags.writing = 0;
+    debug(79, 3) ("storeDiskdWriteDone: dirno %d, fileno %08x status %d\n",
+	sio->swap_dirn, sio->swap_filen, M->status);
+    if (M->status < 0) {
+	diskd_stats.write.fail++;
+	storeDiskdIOCallback(sio, DISK_ERROR);
+	return;
+    }
+    diskd_stats.write.success++;
+    sio->offset += M->status;
+}
+
+static void
+storeDiskdUnlinkDone(diomsg * M)
+{
+    debug(79, 3) ("storeDiskdUnlinkDone: fileno %08x status %d\n",
+	M->id, M->status);
+    statCounter.syscalls.disk.unlinks++;
+    if (M->status < 0)
+	diskd_stats.unlink.fail++;
+    else
+	diskd_stats.unlink.success++;
+}
+
+void
+storeDiskdHandle(diomsg * M)
+{
+    if (cbdataReferenceValid(M->callback_data)) {
+	switch (M->mtype) {
+	case _MQD_OPEN:
+	    storeDiskdOpenDone(M);
+	    break;
+	case _MQD_CLOSE:
+	    storeDiskdCloseDone(M);
+	    break;
+	case _MQD_READ:
+	    storeDiskdReadDone(M);
+	    break;
+	case _MQD_WRITE:
+	    storeDiskdWriteDone(M);
+	    break;
+	case _MQD_UNLINK:
+	    storeDiskdUnlinkDone(M);
+	    break;
+	default:
+	    assert(0);
+	    break;
+	}
+    } else {
+	debug(79, 3) ("storeDiskdHandle: Invalid callback_data %p\n",
+	    M->callback_data);
+	/*
+	 * The read operation has its own callback.  If we don't
+	 * call storeDiskdReadDone(), then we must make sure the
+	 * callback_data gets unlocked!
+	 */
+	if (_MQD_READ == M->mtype) {
+	    /* XXX This cannot be the correct approach. This
+	     * is most likely the wrong place for this. It should
+	     * be done before the sio becomes invalid, not here.
+	     */
+	    storeIOState *sio = (storeIOState *)M->callback_data;
+	    cbdataReferenceDone(sio->read.callback_data);
+	}
+    }
+    cbdataReferenceDone(M->callback_data);
+}
+
+static void
+storeDiskdIOCallback(storeIOState * sio, int errflag)
+{
+    void *cbdata;
+    STIOCB *callback = sio->callback;
+    debug(79, 3) ("storeUfsIOCallback: errflag=%d\n", errflag);
+    sio->callback = NULL;
+    if (cbdataReferenceValidDone(sio->callback_data, &cbdata))
+	callback(cbdata, errflag, sio);
+    cbdataFree(sio);
+}
+
+static int
+storeDiskdSend(int mtype, SwapDir * sd, int id, storeIOState * sio, int size, int offset, off_t shm_offset)
+{
+    int x;
+    diomsg M;
+    static int send_errors = 0;
+    static int last_seq_no = 0;
+    static int seq_no = 0;
+    diskdinfo_t *diskdinfo = (diskdinfo_t *)sd->fsdata;
+    M.mtype = mtype;
+    M.callback_data = cbdataReference(sio);
+    M.size = size;
+    M.offset = offset;
+    M.status = -1;
+    M.shm_offset = (int) shm_offset;
+    M.id = id;
+    M.seq_no = ++seq_no;
+    if (M.seq_no < last_seq_no)
+	debug(79, 1) ("WARNING: sequencing out of order\n");
+    x = msgsnd(diskdinfo->smsgid, &M, msg_snd_rcv_sz, IPC_NOWAIT);
+    last_seq_no = M.seq_no;
+    if (0 == x) {
+	diskd_stats.sent_count++;
+	diskdinfo->away++;
+    } else {
+	debug(79, 1) ("storeDiskdSend: msgsnd: %s\n", xstrerror());
+	cbdataReferenceDone(M.callback_data);
+	assert(++send_errors < 100);
+    }
+    /*
+     * We have to drain the queue here if necessary.  If we don't,
+     * then we can have a lot of messages in the queue (probably
+     * up to 2*magic1) and we can run out of shared memory buffers.
+     */
+    /*
+     * Note that we call storeDirCallback (for all SDs), rather
+     * than storeDiskdDirCallback for just this SD, so that while
+     * we're "blocking" on this SD we can also handle callbacks
+     * from other SDs that might be ready.
+     */
+    while (diskdinfo->away > diskdinfo->magic2) {
+	struct timeval delay =
+	{0, 1};
+	select(0, NULL, NULL, NULL, &delay);
+	storeDirCallback();
+	if (delay.tv_usec < 1000000)
+	    delay.tv_usec <<= 1;
+    }
+    return x;
+}
+
+
+/*
+ * We can't pass memFree() as a free function here, because we need to free
+ * the fsstate variable ..
+ */
+static void
+storeDiskdIOFreeEntry(void *sio)
+{
+    memPoolFree(diskd_state_pool, ((storeIOState *) sio)->fsstate);
+}
--- squid/src/fs/null/store_null.c	Wed Feb 14 01:07:42 2007
+++ /dev/null	Wed Feb 14 01:07:22 2007
@@ -1,131 +0,0 @@
-
-/*
- * $Id$
- *
- * 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"
-#if HAVE_STATVFS
-#if HAVE_SYS_STATVFS_H
-#include <sys/statvfs.h>
-#endif
-#endif
-
-static int null_initialised = 0;
-static void storeNullDirInit(SwapDir * sd);
-static void storeNullDirStats(SwapDir * SD, StoreEntry * sentry);
-static STCHECKOBJ storeNullDirCheckObj;
-static STFSRECONFIGURE storeNullDirReconfigure;
-static STLOGCLEANSTART storeNullDirWriteCleanStart;
-static STLOGCLEANDONE storeNullDirWriteCleanDone;
-static EVH storeNullDirRebuildComplete;
-
-/* The only externally visible interface */
-STSETUP storeFsSetup_null;
-
-static void
-storeNullDirReconfigure(SwapDir * sd, int index, char *path)
-{
-    (void) 0;
-}
-
-static void
-storeNullDirDone(void)
-{
-    null_initialised = 0;
-}
-
-static void
-storeNullDirStats(SwapDir * SD, StoreEntry * sentry)
-{
-    (void) 0;
-}
-
-static void
-storeNullDirInit(SwapDir * sd)
-{
-    store_dirs_rebuilding++;
-    eventAdd("storeNullDirRebuildComplete", storeNullDirRebuildComplete,
-	NULL, 0.0, 1);
-}
-
-static void
-storeNullDirRebuildComplete(void *unused)
-{
-    struct _store_rebuild_data counts;
-    memset(&counts, '\0', sizeof(counts));
-    store_dirs_rebuilding--;
-    storeRebuildComplete(&counts);
-}
-
-static int
-storeNullDirCheckObj(SwapDir * SD, const StoreEntry * e)
-{
-    return -1;
-}
-
-static int
-storeNullDirWriteCleanStart(SwapDir * unused)
-{
-    return 0;
-}
-
-static void
-storeNullDirWriteCleanDone(SwapDir * unused)
-{
-    (void) 0;
-}
-
-static void
-storeNullDirParse(SwapDir * sd, int index, char *path)
-{
-    sd->index = index;
-    sd->path = xstrdup(path);
-    sd->statfs = storeNullDirStats;
-    sd->init = storeNullDirInit;
-    sd->checkobj = storeNullDirCheckObj;
-    sd->log.clean.start = storeNullDirWriteCleanStart;
-    sd->log.clean.done = storeNullDirWriteCleanDone;
-    parse_cachedir_options(sd, NULL, 0);
-}
-
-/* Setup and register the store module */
-
-void
-storeFsSetup_null(storefs_entry_t * storefs)
-{
-    assert(!null_initialised);
-    storefs->parsefunc = storeNullDirParse;
-    storefs->reconfigurefunc = storeNullDirReconfigure;
-    storefs->donefunc = storeNullDirDone;
-    null_initialised = 1;
-}
--- /dev/null	Wed Feb 14 01:07:22 2007
+++ squid/src/fs/null/store_null.cc	Wed Feb 14 01:07:42 2007
@@ -0,0 +1,131 @@
+
+/*
+ * $Id: store_null.cc,v 1.1.2.1 2002/10/12 12:21:44 rbcollins 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"
+#if HAVE_STATVFS
+#if HAVE_SYS_STATVFS_H
+#include <sys/statvfs.h>
+#endif
+#endif
+
+static int null_initialised = 0;
+static void storeNullDirInit(SwapDir * sd);
+static void storeNullDirStats(SwapDir * SD, StoreEntry * sentry);
+static STCHECKOBJ storeNullDirCheckObj;
+static STFSRECONFIGURE storeNullDirReconfigure;
+static STLOGCLEANSTART storeNullDirWriteCleanStart;
+static STLOGCLEANDONE storeNullDirWriteCleanDone;
+static EVH storeNullDirRebuildComplete;
+
+/* The only externally visible interface */
+STSETUP storeFsSetup_null;
+
+static void
+storeNullDirReconfigure(SwapDir * sd, int index, char *path)
+{
+    (void) 0;
+}
+
+static void
+storeNullDirDone(void)
+{
+    null_initialised = 0;
+}
+
+static void
+storeNullDirStats(SwapDir * SD, StoreEntry * sentry)
+{
+    (void) 0;
+}
+
+static void
+storeNullDirInit(SwapDir * sd)
+{
+    store_dirs_rebuilding++;
+    eventAdd("storeNullDirRebuildComplete", storeNullDirRebuildComplete,
+	NULL, 0.0, 1);
+}
+
+static void
+storeNullDirRebuildComplete(void *unused)
+{
+    struct _store_rebuild_data counts;
+    memset(&counts, '\0', sizeof(counts));
+    store_dirs_rebuilding--;
+    storeRebuildComplete(&counts);
+}
+
+static int
+storeNullDirCheckObj(SwapDir * SD, const StoreEntry * e)
+{
+    return -1;
+}
+
+static int
+storeNullDirWriteCleanStart(SwapDir * unused)
+{
+    return 0;
+}
+
+static void
+storeNullDirWriteCleanDone(SwapDir * unused)
+{
+    (void) 0;
+}
+
+static void
+storeNullDirParse(SwapDir * sd, int index, char *path)
+{
+    sd->index = index;
+    sd->path = xstrdup(path);
+    sd->statfs = storeNullDirStats;
+    sd->init = storeNullDirInit;
+    sd->checkobj = storeNullDirCheckObj;
+    sd->log.clean.start = storeNullDirWriteCleanStart;
+    sd->log.clean.done = storeNullDirWriteCleanDone;
+    parse_cachedir_options(sd, NULL, 0);
+}
+
+/* Setup and register the store module */
+
+void
+storeFsSetup_null(storefs_entry_t * storefs)
+{
+    assert(!null_initialised);
+    storefs->parsefunc = storeNullDirParse;
+    storefs->reconfigurefunc = storeNullDirReconfigure;
+    storefs->donefunc = storeNullDirDone;
+    null_initialised = 1;
+}
--- squid/src/fs/ufs/store_dir_ufs.c	Wed Feb 14 01:07:42 2007
+++ /dev/null	Wed Feb 14 01:07:22 2007
@@ -1,224 +0,0 @@
-
-/*
- * $Id$
- *
- * 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.h"
-
-#include "store_ufs.h"
-#include "ufscommon.h"
-
-MemPool *ufs_state_pool = NULL;
-static int ufs_initialised = 0;
-
-static STDUMP storeUfsDirDump;
-static STCHECKOBJ storeUfsDirCheckObj;
-static void storeUfsDirIOUnlinkFile(char *path);
-
-STSETUP storeFsSetup_ufs;
-
-/*
- * storeUfsDirCheckObj
- *
- * This routine is called by storeDirSelectSwapDir to see if the given
- * object is able to be stored on this filesystem. UFS filesystems will
- * happily store anything as long as the LRU time isn't too small.
- */
-int
-storeUfsDirCheckObj(SwapDir * SD, const StoreEntry * e)
-{
-    /* Return 999 (99.9%) constant load */
-    return 999;
-}
-
-void
-storeUfsDirIOUnlinkFile(char *path)
-{
-#if USE_UNLINKD
-    unlinkdUnlink(path);
-#elif USE_TRUNCATE
-    truncate(path, 0);
-#else
-    unlink(path);
-#endif
-}
-
-/* ========== LOCAL FUNCTIONS ABOVE, GLOBAL FUNCTIONS BELOW ========== */
-
-static struct cache_dir_option options[] =
-{
-#if NOT_YET_DONE
-    {"L1", storeUfsDirParseL1, storeUfsDirDumpL1},
-    {"L2", storeUfsDirParseL2, storeUfsDirDumpL2},
-#endif
-    {NULL, NULL}
-};
-
-/*
- * storeUfsDirReconfigure
- *
- * This routine is called when the given swapdir needs reconfiguring 
- */
-static void
-storeUfsDirReconfigure(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("storeUfsDirReconfigure: invalid size value");
-    i = GetInteger();
-    l1 = i;
-    if (l1 <= 0)
-	fatal("storeUfsDirReconfigure: invalid level 1 directories value");
-    i = GetInteger();
-    l2 = i;
-    if (l2 <= 0)
-	fatal("storeUfsDirReconfigure: 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, 1);
-}
-
-void
-storeUfsDirDump(StoreEntry * entry, SwapDir * s)
-{
-    commonUfsDirDump (entry, s);
-    dump_cachedir_options(entry, options, s);
-}
-
-/*
- * storeUfsDirParse
- *
- * Called when a *new* fs is being setup.
- */
-static void
-storeUfsDirParse(SwapDir * sd, int index, char *path)
-{
-    int i;
-    int size;
-    int l1;
-    int l2;
-    squidufsinfo_t *ufsinfo;
-
-    i = GetInteger();
-    size = i << 10;		/* Mbytes to kbytes */
-    if (size <= 0)
-	fatal("storeUfsDirParse: invalid size value");
-    i = GetInteger();
-    l1 = i;
-    if (l1 <= 0)
-	fatal("storeUfsDirParse: invalid level 1 directories value");
-    i = GetInteger();
-    l2 = i;
-    if (l2 <= 0)
-	fatal("storeUfsDirParse: invalid level 2 directories value");
-
-    ufsinfo = xmalloc(sizeof(squidufsinfo_t));
-    if (ufsinfo == NULL)
-	fatal("storeUfsDirParse: couldn't xmalloc() squidufsinfo_t!\n");
-
-    sd->index = index;
-    sd->path = xstrdup(path);
-    sd->max_size = size;
-    sd->fsdata = ufsinfo;
-    ufsinfo->l1 = l1;
-    ufsinfo->l2 = l2;
-    ufsinfo->swaplog_fd = -1;
-    ufsinfo->map = NULL;	/* Debugging purposes */
-    ufsinfo->suggest = 0;
-    ufsinfo->io.storeDirUnlinkFile = storeUfsDirIOUnlinkFile;
-    sd->init = commonUfsDirInit;
-    sd->newfs = commonUfsDirNewfs;
-    sd->dump = storeUfsDirDump;
-    sd->freefs = commonUfsDirFree;
-    sd->dblcheck = commonUfsCleanupDoubleCheck;
-    sd->statfs = commonUfsDirStats;
-    sd->maintainfs = commonUfsDirMaintain;
-    sd->checkobj = storeUfsDirCheckObj;
-    sd->refobj = commonUfsDirRefObj;
-    sd->unrefobj = commonUfsDirUnrefObj;
-    sd->callback = NULL;
-    sd->sync = NULL;
-    sd->obj.create = storeUfsCreate;
-    sd->obj.open = storeUfsOpen;
-    sd->obj.close = storeUfsClose;
-    sd->obj.read = storeUfsRead;
-    sd->obj.write = storeUfsWrite;
-    sd->obj.unlink = storeUfsUnlink;
-    sd->log.open = commonUfsDirOpenSwapLog;
-    sd->log.close = commonUfsDirCloseSwapLog;
-    sd->log.write = commonUfsDirSwapLog;
-    sd->log.clean.start = commonUfsDirWriteCleanStart;
-    sd->log.clean.nextentry = commonUfsDirCleanLogNextEntry;
-    sd->log.clean.done = commonUfsDirWriteCleanDone;
-
-    parse_cachedir_options(sd, options, 1);
-
-    /* Initialise replacement policy stuff */
-    sd->repl = createRemovalPolicy(Config.replPolicy);
-}
-
-/*
- * Initial setup / end destruction
- */
-static void
-storeUfsDirDone(void)
-{
-    memPoolDestroy(&ufs_state_pool);
-    ufs_initialised = 0;
-}
-
-void
-storeFsSetup_ufs(storefs_entry_t * storefs)
-{
-    assert(!ufs_initialised);
-    storefs->parsefunc = storeUfsDirParse;
-    storefs->reconfigurefunc = storeUfsDirReconfigure;
-    storefs->donefunc = storeUfsDirDone;
-    ufs_state_pool = memPoolCreate("UFS IO State data", sizeof(ufsstate_t));
-    ufs_initialised = 1;
-}
--- /dev/null	Wed Feb 14 01:07:22 2007
+++ squid/src/fs/ufs/store_dir_ufs.cc	Wed Feb 14 01:07:42 2007
@@ -0,0 +1,224 @@
+
+/*
+ * $Id: store_dir_ufs.cc,v 1.1.2.1 2002/10/12 12:21:45 rbcollins 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.h"
+
+#include "store_ufs.h"
+#include "ufscommon.h"
+
+MemPool *ufs_state_pool = NULL;
+static int ufs_initialised = 0;
+
+static STDUMP storeUfsDirDump;
+static STCHECKOBJ storeUfsDirCheckObj;
+static void storeUfsDirIOUnlinkFile(char *path);
+
+STSETUP storeFsSetup_ufs;
+
+/*
+ * storeUfsDirCheckObj
+ *
+ * This routine is called by storeDirSelectSwapDir to see if the given
+ * object is able to be stored on this filesystem. UFS filesystems will
+ * happily store anything as long as the LRU time isn't too small.
+ */
+int
+storeUfsDirCheckObj(SwapDir * SD, const StoreEntry * e)
+{
+    /* Return 999 (99.9%) constant load */
+    return 999;
+}
+
+void
+storeUfsDirIOUnlinkFile(char *path)
+{
+#if USE_UNLINKD
+    unlinkdUnlink(path);
+#elif USE_TRUNCATE
+    truncate(path, 0);
+#else
+    unlink(path);
+#endif
+}
+
+/* ========== LOCAL FUNCTIONS ABOVE, GLOBAL FUNCTIONS BELOW ========== */
+
+static struct cache_dir_option options[] =
+{
+#if NOT_YET_DONE
+    {"L1", storeUfsDirParseL1, storeUfsDirDumpL1},
+    {"L2", storeUfsDirParseL2, storeUfsDirDumpL2},
+#endif
+    {NULL, NULL}
+};
+
+/*
+ * storeUfsDirReconfigure
+ *
+ * This routine is called when the given swapdir needs reconfiguring 
+ */
+static void
+storeUfsDirReconfigure(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("storeUfsDirReconfigure: invalid size value");
+    i = GetInteger();
+    l1 = i;
+    if (l1 <= 0)
+	fatal("storeUfsDirReconfigure: invalid level 1 directories value");
+    i = GetInteger();
+    l2 = i;
+    if (l2 <= 0)
+	fatal("storeUfsDirReconfigure: 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, 1);
+}
+
+void
+storeUfsDirDump(StoreEntry * entry, SwapDir * s)
+{
+    commonUfsDirDump (entry, s);
+    dump_cachedir_options(entry, options, s);
+}
+
+/*
+ * storeUfsDirParse
+ *
+ * Called when a *new* fs is being setup.
+ */
+static void
+storeUfsDirParse(SwapDir * sd, int index, char *path)
+{
+    int i;
+    int size;
+    int l1;
+    int l2;
+    squidufsinfo_t *ufsinfo;
+
+    i = GetInteger();
+    size = i << 10;		/* Mbytes to kbytes */
+    if (size <= 0)
+	fatal("storeUfsDirParse: invalid size value");
+    i = GetInteger();
+    l1 = i;
+    if (l1 <= 0)
+	fatal("storeUfsDirParse: invalid level 1 directories value");
+    i = GetInteger();
+    l2 = i;
+    if (l2 <= 0)
+	fatal("storeUfsDirParse: invalid level 2 directories value");
+
+    ufsinfo = (squidufsinfo_t *)xmalloc(sizeof(squidufsinfo_t));
+    if (ufsinfo == NULL)
+	fatal("storeUfsDirParse: couldn't xmalloc() squidufsinfo_t!\n");
+
+    sd->index = index;
+    sd->path = xstrdup(path);
+    sd->max_size = size;
+    sd->fsdata = ufsinfo;
+    ufsinfo->l1 = l1;
+    ufsinfo->l2 = l2;
+    ufsinfo->swaplog_fd = -1;
+    ufsinfo->map = NULL;	/* Debugging purposes */
+    ufsinfo->suggest = 0;
+    ufsinfo->io.storeDirUnlinkFile = storeUfsDirIOUnlinkFile;
+    sd->init = commonUfsDirInit;
+    sd->newfs = commonUfsDirNewfs;
+    sd->dump = storeUfsDirDump;
+    sd->freefs = commonUfsDirFree;
+    sd->dblcheck = commonUfsCleanupDoubleCheck;
+    sd->statfs = commonUfsDirStats;
+    sd->maintainfs = commonUfsDirMaintain;
+    sd->checkobj = storeUfsDirCheckObj;
+    sd->refobj = commonUfsDirRefObj;
+    sd->unrefobj = commonUfsDirUnrefObj;
+    sd->callback = NULL;
+    sd->sync = NULL;
+    sd->obj.create = storeUfsCreate;
+    sd->obj.open = storeUfsOpen;
+    sd->obj.close = storeUfsClose;
+    sd->obj.read = storeUfsRead;
+    sd->obj.write = storeUfsWrite;
+    sd->obj.unlink = storeUfsUnlink;
+    sd->log.open = commonUfsDirOpenSwapLog;
+    sd->log.close = commonUfsDirCloseSwapLog;
+    sd->log.write = commonUfsDirSwapLog;
+    sd->log.clean.start = commonUfsDirWriteCleanStart;
+    sd->log.clean.nextentry = commonUfsDirCleanLogNextEntry;
+    sd->log.clean.done = commonUfsDirWriteCleanDone;
+
+    parse_cachedir_options(sd, options, 1);
+
+    /* Initialise replacement policy stuff */
+    sd->repl = createRemovalPolicy(Config.replPolicy);
+}
+
+/*
+ * Initial setup / end destruction
+ */
+static void
+storeUfsDirDone(void)
+{
+    memPoolDestroy(&ufs_state_pool);
+    ufs_initialised = 0;
+}
+
+void
+storeFsSetup_ufs(storefs_entry_t * storefs)
+{
+    assert(!ufs_initialised);
+    storefs->parsefunc = storeUfsDirParse;
+    storefs->reconfigurefunc = storeUfsDirReconfigure;
+    storefs->donefunc = storeUfsDirDone;
+    ufs_state_pool = memPoolCreate("UFS IO State data", sizeof(ufsstate_t));
+    ufs_initialised = 1;
+}
--- squid/src/fs/ufs/store_io_ufs.c	Wed Feb 14 01:07:42 2007
+++ /dev/null	Wed Feb 14 01:07:22 2007
@@ -1,265 +0,0 @@
-
-/*
- * $Id$
- *
- * DEBUG: section 79    Storage Manager UFS Interface
- * 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_ufs.h"
-#include "Store.h"
-#include "ufscommon.h"
-
-
-static DRCB storeUfsReadDone;
-static DWCB storeUfsWriteDone;
-static void storeUfsIOCallback(storeIOState * sio, int errflag);
-static CBDUNL storeUfsIOFreeEntry;
-
-CBDATA_TYPE(storeIOState);
-
-/* === PUBLIC =========================================================== */
-
-storeIOState *
-storeUfsOpen(SwapDir * SD, StoreEntry * e, STFNCB * file_callback,
-    STIOCB * callback, void *callback_data)
-{
-    sfileno f = e->swap_filen;
-    char *path = commonUfsDirFullPath(SD, f, NULL);
-    storeIOState *sio;
-    struct stat sb;
-    int fd;
-    debug(79, 3) ("storeUfsOpen: fileno %08X\n", f);
-    fd = file_open(path, O_RDONLY | O_BINARY);
-    if (fd < 0) {
-	debug(79, 3) ("storeUfsOpen: got failure (%d)\n", errno);
-	return NULL;
-    }
-    debug(79, 3) ("storeUfsOpen: opened FD %d\n", fd);
-    CBDATA_INIT_TYPE_FREECB(storeIOState, storeUfsIOFreeEntry);
-    sio = cbdataAlloc(storeIOState);
-    sio->fsstate = memPoolAlloc(ufs_state_pool);
-
-    sio->swap_filen = f;
-    sio->swap_dirn = SD->index;
-    sio->mode = O_RDONLY | O_BINARY;
-    sio->callback = callback;
-    sio->callback_data = cbdataReference(callback_data);
-    sio->e = e;
-    ((ufsstate_t *) (sio->fsstate))->fd = fd;
-    ((ufsstate_t *) (sio->fsstate))->flags.writing = 0;
-    ((ufsstate_t *) (sio->fsstate))->flags.reading = 0;
-    ((ufsstate_t *) (sio->fsstate))->flags.close_request = 0;
-    if (fstat(fd, &sb) == 0)
-	sio->st_size = sb.st_size;
-    store_open_disk_fd++;
-
-    /* We should update the heap/dlink position here ! */
-    return sio;
-}
-
-storeIOState *
-storeUfsCreate(SwapDir * SD, StoreEntry * e, STFNCB * file_callback, STIOCB * callback, void *callback_data)
-{
-    storeIOState *sio;
-    int fd;
-    int mode = (O_WRONLY | O_CREAT | O_TRUNC | O_BINARY);
-    char *path;
-    squidufsinfo_t *ufsinfo = (squidufsinfo_t *) SD->fsdata;
-    sfileno filn;
-    sdirno dirn;
-
-    /* Allocate a number */
-    dirn = SD->index;
-    filn = commonUfsDirMapBitAllocate(SD);
-    ufsinfo->suggest = filn + 1;
-    /* Shouldn't we handle a 'bitmap full' error here? */
-    path = commonUfsDirFullPath(SD, filn, NULL);
-
-    debug(79, 3) ("storeUfsCreate: fileno %08X\n", filn);
-    fd = file_open(path, mode);
-    if (fd < 0) {
-	debug(79, 3) ("storeUfsCreate: got failure (%d)\n", errno);
-	return NULL;
-    }
-    debug(79, 3) ("storeUfsCreate: opened FD %d\n", fd);
-    CBDATA_INIT_TYPE_FREECB(storeIOState, storeUfsIOFreeEntry);
-    sio = cbdataAlloc(storeIOState);
-    sio->fsstate = memPoolAlloc(ufs_state_pool);
-
-    sio->swap_filen = filn;
-    sio->swap_dirn = dirn;
-    sio->mode = mode;
-    sio->callback = callback;
-    sio->callback_data = cbdataReference(callback_data);
-    sio->e = (StoreEntry *) e;
-    ((ufsstate_t *) (sio->fsstate))->fd = fd;
-    ((ufsstate_t *) (sio->fsstate))->flags.writing = 0;
-    ((ufsstate_t *) (sio->fsstate))->flags.reading = 0;
-    ((ufsstate_t *) (sio->fsstate))->flags.close_request = 0;
-    store_open_disk_fd++;
-
-    /* now insert into the replacement policy */
-    commonUfsDirReplAdd(SD, e);
-    return sio;
-}
-
-void
-storeUfsClose(SwapDir * SD, storeIOState * sio)
-{
-    ufsstate_t *ufsstate = (ufsstate_t *) sio->fsstate;
-
-    debug(79, 3) ("storeUfsClose: dirno %d, fileno %08X, FD %d\n",
-	sio->swap_dirn, sio->swap_filen, ufsstate->fd);
-    if (ufsstate->flags.reading || ufsstate->flags.writing) {
-	ufsstate->flags.close_request = 1;
-	return;
-    }
-    storeUfsIOCallback(sio, 0);
-}
-
-void
-storeUfsRead(SwapDir * SD, storeIOState * sio, char *buf, size_t size, off_t offset, STRCB * callback, void *callback_data)
-{
-    ufsstate_t *ufsstate = (ufsstate_t *) sio->fsstate;
-
-    assert(sio->read.callback == NULL);
-    assert(sio->read.callback_data == NULL);
-    sio->read.callback = callback;
-    sio->read.callback_data = cbdataReference(callback_data);
-    debug(79, 3) ("storeUfsRead: dirno %d, fileno %08X, FD %d\n",
-	sio->swap_dirn, sio->swap_filen, ufsstate->fd);
-    sio->offset = offset;
-    ufsstate->flags.reading = 1;
-    file_read(ufsstate->fd,
-	buf,
-	size,
-	offset,
-	storeUfsReadDone,
-	sio);
-}
-
-void
-storeUfsWrite(SwapDir * SD, storeIOState * sio, char *buf, size_t size, off_t offset, FREE * free_func)
-{
-    ufsstate_t *ufsstate = (ufsstate_t *) sio->fsstate;
-    debug(79, 3) ("storeUfsWrite: dirn %d, fileno %08X, FD %d\n", sio->swap_dirn, sio->swap_filen, ufsstate->fd);
-    ufsstate->flags.writing = 1;
-    file_write(ufsstate->fd,
-	offset,
-	buf,
-	size,
-	storeUfsWriteDone,
-	sio,
-	free_func);
-}
-
-void
-storeUfsUnlink(SwapDir * SD, StoreEntry * e)
-{
-    debug(79, 3) ("storeUfsUnlink: fileno %08X\n", e->swap_filen);
-    commonUfsDirReplRemove(e);
-    commonUfsDirMapBitReset(SD, e->swap_filen);
-    commonUfsDirUnlinkFile(SD, e->swap_filen);
-}
-
-/*  === STATIC =========================================================== */
-
-static void
-storeUfsReadDone(int fd, const char *buf, int len, int errflag, void *my_data)
-{
-    storeIOState *sio = my_data;
-    ufsstate_t *ufsstate = (ufsstate_t *) sio->fsstate;
-    STRCB *callback;
-    void *cbdata;
-    ssize_t rlen;
-
-    debug(79, 3) ("storeUfsReadDone: dirno %d, fileno %08X, FD %d, len %d\n",
-	sio->swap_dirn, sio->swap_filen, fd, len);
-    ufsstate->flags.reading = 0;
-    if (errflag) {
-	debug(79, 3) ("storeUfsReadDone: got failure (%d)\n", errflag);
-	rlen = -1;
-    } else {
-	rlen = (ssize_t) len;
-	sio->offset += len;
-    }
-    assert(sio->read.callback);
-    assert(sio->read.callback_data);
-    callback = sio->read.callback;
-    sio->read.callback = NULL;
-    if (cbdataReferenceValidDone(sio->read.callback_data, &cbdata))
-	callback(cbdata, buf, (size_t) rlen);
-}
-
-static void
-storeUfsWriteDone(int fd, int errflag, size_t len, void *my_data)
-{
-    storeIOState *sio = my_data;
-    ufsstate_t *ufsstate = (ufsstate_t *) sio->fsstate;
-    debug(79, 3) ("storeUfsWriteDone: dirno %d, fileno %08X, FD %d, len %ld\n",
-	sio->swap_dirn, sio->swap_filen, fd, (long int) len);
-    ufsstate->flags.writing = 0;
-    if (errflag) {
-	debug(79, 0) ("storeUfsWriteDone: got failure (%d)\n", errflag);
-	storeUfsIOCallback(sio, errflag);
-	return;
-    }
-    sio->offset += len;
-    if (ufsstate->flags.close_request)
-	storeUfsIOCallback(sio, errflag);
-}
-
-static void
-storeUfsIOCallback(storeIOState * sio, int errflag)
-{
-    ufsstate_t *ufsstate = (ufsstate_t *) sio->fsstate;
-    void *cbdata;
-    debug(79, 3) ("storeUfsIOCallback: errflag=%d\n", errflag);
-    if (ufsstate->fd > -1) {
-	file_close(ufsstate->fd);
-	store_open_disk_fd--;
-    }
-    if (cbdataReferenceValidDone(sio->callback_data, &cbdata))
-	sio->callback(cbdata, errflag, sio);
-    sio->callback = NULL;
-    cbdataFree(sio);
-}
-
-
-/*
- * Clean up any references from the SIO before it get's released.
- */
-static void
-storeUfsIOFreeEntry(void *sio)
-{
-    memPoolFree(ufs_state_pool, ((storeIOState *) sio)->fsstate);
-}
--- /dev/null	Wed Feb 14 01:07:22 2007
+++ squid/src/fs/ufs/store_io_ufs.cc	Wed Feb 14 01:07:42 2007
@@ -0,0 +1,265 @@
+
+/*
+ * $Id: store_io_ufs.cc,v 1.1.2.1 2002/10/12 12:21:45 rbcollins Exp $
+ *
+ * DEBUG: section 79    Storage Manager UFS Interface
+ * 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_ufs.h"
+#include "Store.h"
+#include "ufscommon.h"
+
+
+static DRCB storeUfsReadDone;
+static DWCB storeUfsWriteDone;
+static void storeUfsIOCallback(storeIOState * sio, int errflag);
+static CBDUNL storeUfsIOFreeEntry;
+
+CBDATA_TYPE(storeIOState);
+
+/* === PUBLIC =========================================================== */
+
+storeIOState *
+storeUfsOpen(SwapDir * SD, StoreEntry * e, STFNCB * file_callback,
+    STIOCB * callback, void *callback_data)
+{
+    sfileno f = e->swap_filen;
+    char *path = commonUfsDirFullPath(SD, f, NULL);
+    storeIOState *sio;
+    struct stat sb;
+    int fd;
+    debug(79, 3) ("storeUfsOpen: fileno %08X\n", f);
+    fd = file_open(path, O_RDONLY | O_BINARY);
+    if (fd < 0) {
+	debug(79, 3) ("storeUfsOpen: got failure (%d)\n", errno);
+	return NULL;
+    }
+    debug(79, 3) ("storeUfsOpen: opened FD %d\n", fd);
+    CBDATA_INIT_TYPE_FREECB(storeIOState, storeUfsIOFreeEntry);
+    sio = cbdataAlloc(storeIOState);
+    sio->fsstate = memPoolAlloc(ufs_state_pool);
+
+    sio->swap_filen = f;
+    sio->swap_dirn = SD->index;
+    sio->mode = O_RDONLY | O_BINARY;
+    sio->callback = callback;
+    sio->callback_data = cbdataReference(callback_data);
+    sio->e = e;
+    ((ufsstate_t *) (sio->fsstate))->fd = fd;
+    ((ufsstate_t *) (sio->fsstate))->flags.writing = 0;
+    ((ufsstate_t *) (sio->fsstate))->flags.reading = 0;
+    ((ufsstate_t *) (sio->fsstate))->flags.close_request = 0;
+    if (fstat(fd, &sb) == 0)
+	sio->st_size = sb.st_size;
+    store_open_disk_fd++;
+
+    /* We should update the heap/dlink position here ! */
+    return sio;
+}
+
+storeIOState *
+storeUfsCreate(SwapDir * SD, StoreEntry * e, STFNCB * file_callback, STIOCB * callback, void *callback_data)
+{
+    storeIOState *sio;
+    int fd;
+    int mode = (O_WRONLY | O_CREAT | O_TRUNC | O_BINARY);
+    char *path;
+    squidufsinfo_t *ufsinfo = (squidufsinfo_t *) SD->fsdata;
+    sfileno filn;
+    sdirno dirn;
+
+    /* Allocate a number */
+    dirn = SD->index;
+    filn = commonUfsDirMapBitAllocate(SD);
+    ufsinfo->suggest = filn + 1;
+    /* Shouldn't we handle a 'bitmap full' error here? */
+    path = commonUfsDirFullPath(SD, filn, NULL);
+
+    debug(79, 3) ("storeUfsCreate: fileno %08X\n", filn);
+    fd = file_open(path, mode);
+    if (fd < 0) {
+	debug(79, 3) ("storeUfsCreate: got failure (%d)\n", errno);
+	return NULL;
+    }
+    debug(79, 3) ("storeUfsCreate: opened FD %d\n", fd);
+    CBDATA_INIT_TYPE_FREECB(storeIOState, storeUfsIOFreeEntry);
+    sio = cbdataAlloc(storeIOState);
+    sio->fsstate = memPoolAlloc(ufs_state_pool);
+
+    sio->swap_filen = filn;
+    sio->swap_dirn = dirn;
+    sio->mode = mode;
+    sio->callback = callback;
+    sio->callback_data = cbdataReference(callback_data);
+    sio->e = (StoreEntry *) e;
+    ((ufsstate_t *) (sio->fsstate))->fd = fd;
+    ((ufsstate_t *) (sio->fsstate))->flags.writing = 0;
+    ((ufsstate_t *) (sio->fsstate))->flags.reading = 0;
+    ((ufsstate_t *) (sio->fsstate))->flags.close_request = 0;
+    store_open_disk_fd++;
+
+    /* now insert into the replacement policy */
+    commonUfsDirReplAdd(SD, e);
+    return sio;
+}
+
+void
+storeUfsClose(SwapDir * SD, storeIOState * sio)
+{
+    ufsstate_t *ufsstate = (ufsstate_t *) sio->fsstate;
+
+    debug(79, 3) ("storeUfsClose: dirno %d, fileno %08X, FD %d\n",
+	sio->swap_dirn, sio->swap_filen, ufsstate->fd);
+    if (ufsstate->flags.reading || ufsstate->flags.writing) {
+	ufsstate->flags.close_request = 1;
+	return;
+    }
+    storeUfsIOCallback(sio, 0);
+}
+
+void
+storeUfsRead(SwapDir * SD, storeIOState * sio, char *buf, size_t size, off_t offset, STRCB * callback, void *callback_data)
+{
+    ufsstate_t *ufsstate = (ufsstate_t *) sio->fsstate;
+
+    assert(sio->read.callback == NULL);
+    assert(sio->read.callback_data == NULL);
+    sio->read.callback = callback;
+    sio->read.callback_data = cbdataReference(callback_data);
+    debug(79, 3) ("storeUfsRead: dirno %d, fileno %08X, FD %d\n",
+	sio->swap_dirn, sio->swap_filen, ufsstate->fd);
+    sio->offset = offset;
+    ufsstate->flags.reading = 1;
+    file_read(ufsstate->fd,
+	buf,
+	size,
+	offset,
+	storeUfsReadDone,
+	sio);
+}
+
+void
+storeUfsWrite(SwapDir * SD, storeIOState * sio, char *buf, size_t size, off_t offset, FREE * free_func)
+{
+    ufsstate_t *ufsstate = (ufsstate_t *) sio->fsstate;
+    debug(79, 3) ("storeUfsWrite: dirn %d, fileno %08X, FD %d\n", sio->swap_dirn, sio->swap_filen, ufsstate->fd);
+    ufsstate->flags.writing = 1;
+    file_write(ufsstate->fd,
+	offset,
+	buf,
+	size,
+	storeUfsWriteDone,
+	sio,
+	free_func);
+}
+
+void
+storeUfsUnlink(SwapDir * SD, StoreEntry * e)
+{
+    debug(79, 3) ("storeUfsUnlink: fileno %08X\n", e->swap_filen);
+    commonUfsDirReplRemove(e);
+    commonUfsDirMapBitReset(SD, e->swap_filen);
+    commonUfsDirUnlinkFile(SD, e->swap_filen);
+}
+
+/*  === STATIC =========================================================== */
+
+static void
+storeUfsReadDone(int fd, const char *buf, int len, int errflag, void *my_data)
+{
+    storeIOState *sio = (storeIOState *)my_data;
+    ufsstate_t *ufsstate = (ufsstate_t *) sio->fsstate;
+    STRCB *callback;
+    void *cbdata;
+    ssize_t rlen;
+
+    debug(79, 3) ("storeUfsReadDone: dirno %d, fileno %08X, FD %d, len %d\n",
+	sio->swap_dirn, sio->swap_filen, fd, len);
+    ufsstate->flags.reading = 0;
+    if (errflag) {
+	debug(79, 3) ("storeUfsReadDone: got failure (%d)\n", errflag);
+	rlen = -1;
+    } else {
+	rlen = (ssize_t) len;
+	sio->offset += len;
+    }
+    assert(sio->read.callback);
+    assert(sio->read.callback_data);
+    callback = sio->read.callback;
+    sio->read.callback = NULL;
+    if (cbdataReferenceValidDone(sio->read.callback_data, &cbdata))
+	callback(cbdata, buf, (size_t) rlen);
+}
+
+static void
+storeUfsWriteDone(int fd, int errflag, size_t len, void *my_data)
+{
+    storeIOState *sio = (storeIOState *)my_data;
+    ufsstate_t *ufsstate = (ufsstate_t *) sio->fsstate;
+    debug(79, 3) ("storeUfsWriteDone: dirno %d, fileno %08X, FD %d, len %ld\n",
+	sio->swap_dirn, sio->swap_filen, fd, (long int) len);
+    ufsstate->flags.writing = 0;
+    if (errflag) {
+	debug(79, 0) ("storeUfsWriteDone: got failure (%d)\n", errflag);
+	storeUfsIOCallback(sio, errflag);
+	return;
+    }
+    sio->offset += len;
+    if (ufsstate->flags.close_request)
+	storeUfsIOCallback(sio, errflag);
+}
+
+static void
+storeUfsIOCallback(storeIOState * sio, int errflag)
+{
+    ufsstate_t *ufsstate = (ufsstate_t *) sio->fsstate;
+    void *cbdata;
+    debug(79, 3) ("storeUfsIOCallback: errflag=%d\n", errflag);
+    if (ufsstate->fd > -1) {
+	file_close(ufsstate->fd);
+	store_open_disk_fd--;
+    }
+    if (cbdataReferenceValidDone(sio->callback_data, &cbdata))
+	sio->callback(cbdata, errflag, sio);
+    sio->callback = NULL;
+    cbdataFree(sio);
+}
+
+
+/*
+ * Clean up any references from the SIO before it get's released.
+ */
+static void
+storeUfsIOFreeEntry(void *sio)
+{
+    memPoolFree(ufs_state_pool, ((storeIOState *) sio)->fsstate);
+}
