]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
extract duplicate ufs code to ufscommon.c/h
authorrobertc <>
Sat, 12 Oct 2002 15:45:55 +0000 (15:45 +0000)
committerrobertc <>
Sat, 12 Oct 2002 15:45:55 +0000 (15:45 +0000)
14 files changed:
src/Makefile.am
src/Makefile.in
src/fs/aufs/store_asyncufs.h
src/fs/aufs/store_dir_aufs.cc
src/fs/aufs/store_io_aufs.cc
src/fs/diskd/store_dir_diskd.cc
src/fs/diskd/store_diskd.h
src/fs/diskd/store_io_diskd.cc
src/fs/ufs/store_dir_ufs.cc
src/fs/ufs/store_io_ufs.cc
src/fs/ufs/store_ufs.h
src/leakfinder.cc
src/ufscommon.cc [new file with mode: 0644]
src/ufscommon.h [new file with mode: 0644]

index 71ef90610f26623e61a0925ffbdba4cf75f2586d..71299c117e904ece1d8af311ceb030cf08b0cb9e 100644 (file)
@@ -1,7 +1,7 @@
 #
 #  Makefile for the Squid Object Cache server
 #
-#  $Id: Makefile.am,v 1.34 2002/10/02 11:06:31 robertc Exp $
+#  $Id: Makefile.am,v 1.35 2002/10/12 09:45:55 robertc Exp $
 #
 #  Uncomment and customize the following to suit your needs:
 #
@@ -217,6 +217,8 @@ squid_SOURCES = \
        structs.h \
        tools.c \
        typedefs.h \
+       ufscommon.c \
+       ufscommon.h \
        $(UNLINKDSOURCE) \
        url.c \
        urn.c \
index b961a52b5fb5f59015b9b1120ffe22dc415ec4bc..6ca7a5c2c441bea7d0241cfb28c56be11c610ef9 100644 (file)
@@ -16,7 +16,7 @@
 #
 #  Makefile for the Squid Object Cache server
 #
-#  $Id: Makefile.in,v 1.248 2002/10/02 11:06:31 robertc Exp $
+#  $Id: Makefile.in,v 1.249 2002/10/12 09:45:55 robertc Exp $
 #
 #  Uncomment and customize the following to suit your needs:
 #
@@ -308,6 +308,8 @@ squid_SOURCES = \
        structs.h \
        tools.c \
        typedefs.h \
+       ufscommon.c \
+       ufscommon.h \
        $(UNLINKDSOURCE) \
        url.c \
        urn.c \
@@ -471,23 +473,23 @@ pinger_OBJECTS = $(am_pinger_OBJECTS) $(nodist_pinger_OBJECTS)
 pinger_LDADD = $(LDADD)
 pinger_DEPENDENCIES =
 pinger_LDFLAGS =
-@USE_DELAY_POOLS_FALSE@am__objects_4 =
-@USE_DELAY_POOLS_TRUE@am__objects_4 = delay_pools.$(OBJEXT)
-@USE_DNSSERVER_FALSE@am__objects_5 = dns_internal.$(OBJEXT)
-@USE_DNSSERVER_TRUE@am__objects_5 = dns.$(OBJEXT)
-@ENABLE_HTCP_TRUE@am__objects_6 = htcp.$(OBJEXT)
-@MAKE_LEAKFINDER_TRUE@am__objects_7 = leakfinder.$(OBJEXT)
-@MAKE_LEAKFINDER_FALSE@am__objects_7 =
-@ENABLE_XPROF_STATS_TRUE@am__objects_3 = ProfStats.$(OBJEXT)
-@ENABLE_XPROF_STATS_FALSE@am__objects_3 =
-@USE_SNMP_TRUE@am__objects_8 = snmp_core.$(OBJEXT) snmp_agent.$(OBJEXT)
-@USE_SNMP_FALSE@am__objects_8 =
-@ENABLE_SSL_FALSE@am__objects_9 =
-@ENABLE_SSL_TRUE@am__objects_9 = ssl_support.$(OBJEXT)
-@ENABLE_UNLINKD_TRUE@am__objects_10 = unlinkd.$(OBJEXT)
-@ENABLE_UNLINKD_FALSE@am__objects_10 =
-@ENABLE_WIN32SPECIFIC_FALSE@am__objects_11 =
-@ENABLE_WIN32SPECIFIC_TRUE@am__objects_11 = win32.$(OBJEXT)
+@USE_DELAY_POOLS_FALSE@am__objects_1 =
+@USE_DELAY_POOLS_TRUE@am__objects_1 = delay_pools.$(OBJEXT)
+@USE_DNSSERVER_FALSE@am__objects_2 = dns_internal.$(OBJEXT)
+@USE_DNSSERVER_TRUE@am__objects_2 = dns.$(OBJEXT)
+@ENABLE_HTCP_TRUE@am__objects_3 = htcp.$(OBJEXT)
+@MAKE_LEAKFINDER_TRUE@am__objects_4 = leakfinder.$(OBJEXT)
+@MAKE_LEAKFINDER_FALSE@am__objects_4 =
+@ENABLE_XPROF_STATS_TRUE@am__objects_5 = ProfStats.$(OBJEXT)
+@ENABLE_XPROF_STATS_FALSE@am__objects_5 =
+@USE_SNMP_TRUE@am__objects_6 = snmp_core.$(OBJEXT) snmp_agent.$(OBJEXT)
+@USE_SNMP_FALSE@am__objects_6 =
+@ENABLE_SSL_FALSE@am__objects_7 =
+@ENABLE_SSL_TRUE@am__objects_7 = ssl_support.$(OBJEXT)
+@ENABLE_UNLINKD_TRUE@am__objects_8 = unlinkd.$(OBJEXT)
+@ENABLE_UNLINKD_FALSE@am__objects_8 =
+@ENABLE_WIN32SPECIFIC_FALSE@am__objects_9 =
+@ENABLE_WIN32SPECIFIC_TRUE@am__objects_9 = win32.$(OBJEXT)
 am_squid_OBJECTS = access_log.$(OBJEXT) acl.$(OBJEXT) asn.$(OBJEXT) \
        authenticate.$(OBJEXT) cache_cf.$(OBJEXT) CacheDigest.$(OBJEXT) \
        cache_manager.$(OBJEXT) carp.$(OBJEXT) cbdata.$(OBJEXT) \
@@ -495,11 +497,11 @@ am_squid_OBJECTS = access_log.$(OBJEXT) acl.$(OBJEXT) asn.$(OBJEXT) \
        client_side_reply.$(OBJEXT) client_side_request.$(OBJEXT) \
        clientStream.$(OBJEXT) comm.$(OBJEXT) comm_select.$(OBJEXT) \
        comm_poll.$(OBJEXT) comm_kqueue.$(OBJEXT) debug.$(OBJEXT) \
-       $(am__objects_4) disk.$(OBJEXT) $(am__objects_5) \
+       $(am__objects_1) disk.$(OBJEXT) $(am__objects_2) \
        errorpage.$(OBJEXT) ETag.$(OBJEXT) event.$(OBJEXT) \
        external_acl.$(OBJEXT) fd.$(OBJEXT) filemap.$(OBJEXT) \
        forward.$(OBJEXT) fqdncache.$(OBJEXT) ftp.$(OBJEXT) \
-       gopher.$(OBJEXT) helper.$(OBJEXT) $(am__objects_6) \
+       gopher.$(OBJEXT) helper.$(OBJEXT) $(am__objects_3) \
        http.$(OBJEXT) HttpStatusLine.$(OBJEXT) HttpHdrCc.$(OBJEXT) \
        HttpHdrRange.$(OBJEXT) HttpHdrContRange.$(OBJEXT) \
        HttpHeader.$(OBJEXT) HttpHeaderTools.$(OBJEXT) \
@@ -507,22 +509,22 @@ am_squid_OBJECTS = access_log.$(OBJEXT) acl.$(OBJEXT) asn.$(OBJEXT) \
        HttpRequest.$(OBJEXT) icmp.$(OBJEXT) icp_v2.$(OBJEXT) \
        icp_v3.$(OBJEXT) ident.$(OBJEXT) internal.$(OBJEXT) \
        ipc.$(OBJEXT) ipcache.$(OBJEXT) IPInterception.$(OBJEXT) \
-       $(am__objects_7) logfile.$(OBJEXT) main.$(OBJEXT) mem.$(OBJEXT) \
+       $(am__objects_4) logfile.$(OBJEXT) main.$(OBJEXT) mem.$(OBJEXT) \
        MemBuf.$(OBJEXT) mime.$(OBJEXT) multicast.$(OBJEXT) \
        neighbors.$(OBJEXT) net_db.$(OBJEXT) Packer.$(OBJEXT) \
-       $(am__objects_3) pconn.$(OBJEXT) peer_digest.$(OBJEXT) \
+       $(am__objects_5) pconn.$(OBJEXT) peer_digest.$(OBJEXT) \
        peer_select.$(OBJEXT) redirect.$(OBJEXT) referer.$(OBJEXT) \
-       refresh.$(OBJEXT) send-announce.$(OBJEXT) $(am__objects_8) \
-       ssl.$(OBJEXT) $(am__objects_9) stat.$(OBJEXT) \
+       refresh.$(OBJEXT) send-announce.$(OBJEXT) $(am__objects_6) \
+       ssl.$(OBJEXT) $(am__objects_7) stat.$(OBJEXT) \
        StatHist.$(OBJEXT) String.$(OBJEXT) stmem.$(OBJEXT) \
        store.$(OBJEXT) store_io.$(OBJEXT) store_client.$(OBJEXT) \
        store_digest.$(OBJEXT) store_dir.$(OBJEXT) \
        store_key_md5.$(OBJEXT) store_log.$(OBJEXT) \
        store_rebuild.$(OBJEXT) store_swapin.$(OBJEXT) \
        store_swapmeta.$(OBJEXT) store_swapout.$(OBJEXT) \
-       tools.$(OBJEXT) $(am__objects_10) url.$(OBJEXT) urn.$(OBJEXT) \
-       useragent.$(OBJEXT) wais.$(OBJEXT) wccp.$(OBJEXT) \
-       whois.$(OBJEXT) $(am__objects_11)
+       tools.$(OBJEXT) ufscommon.$(OBJEXT) $(am__objects_8) \
+       url.$(OBJEXT) urn.$(OBJEXT) useragent.$(OBJEXT) wais.$(OBJEXT) \
+       wccp.$(OBJEXT) whois.$(OBJEXT) $(am__objects_9)
 nodist_squid_OBJECTS = repl_modules.$(OBJEXT) auth_modules.$(OBJEXT) \
        store_modules.$(OBJEXT) globals.$(OBJEXT) \
        string_arrays.$(OBJEXT)
@@ -598,10 +600,11 @@ depcomp = $(SHELL) $(top_srcdir)/cfgaux/depcomp
 @AMDEP_TRUE@   $(DEPDIR)/store_swapmeta.Po \
 @AMDEP_TRUE@   $(DEPDIR)/store_swapout.Po \
 @AMDEP_TRUE@   $(DEPDIR)/string_arrays.Po $(DEPDIR)/tools.Po \
-@AMDEP_TRUE@   $(DEPDIR)/unlinkd.Po $(DEPDIR)/url.Po \
-@AMDEP_TRUE@   $(DEPDIR)/urn.Po $(DEPDIR)/useragent.Po \
-@AMDEP_TRUE@   $(DEPDIR)/wais.Po $(DEPDIR)/wccp.Po \
-@AMDEP_TRUE@   $(DEPDIR)/whois.Po $(DEPDIR)/win32.Po
+@AMDEP_TRUE@   $(DEPDIR)/ufscommon.Po $(DEPDIR)/unlinkd.Po \
+@AMDEP_TRUE@   $(DEPDIR)/url.Po $(DEPDIR)/urn.Po \
+@AMDEP_TRUE@   $(DEPDIR)/useragent.Po $(DEPDIR)/wais.Po \
+@AMDEP_TRUE@   $(DEPDIR)/wccp.Po $(DEPDIR)/whois.Po \
+@AMDEP_TRUE@   $(DEPDIR)/win32.Po
 COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
        $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
 CCLD = $(CC)
@@ -836,6 +839,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/store_swapout.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/string_arrays.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/tools.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/ufscommon.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/unlinkd.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/url.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/urn.Po@am__quote@
index 83250e3f70be1cc06f9a2b27ed578fae3c73e9cb..664240a10be1831bfac8ac40a44b88b46fe704fb 100644 (file)
@@ -78,14 +78,6 @@ int aioCheckCallbacks(SwapDir *);
 void aioSync(SwapDir *);
 int aioQueueSize(void);
 
-struct _squidaioinfo_t {
-    int swaplog_fd;
-    int l1;
-    int l2;
-    fileMap *map;
-    int suggest;
-};
-
 struct _squidaiostate_t {
     int fd;
     struct {
@@ -117,7 +109,6 @@ struct _queued_read {
     void *callback_data;
 };
 
-typedef struct _squidaioinfo_t squidaioinfo_t;
 typedef struct _squidaiostate_t squidaiostate_t;
 
 /* The squidaio_state memory pools */
@@ -125,14 +116,6 @@ extern MemPool *squidaio_state_pool;
 extern MemPool *aufs_qread_pool;
 extern MemPool *aufs_qwrite_pool;
 
-extern void storeAufsDirMapBitReset(SwapDir *, sfileno);
-extern int storeAufsDirMapBitAllocate(SwapDir *);
-
-extern char *storeAufsDirFullPath(SwapDir * SD, sfileno filn, char *fullpath);
-extern void storeAufsDirUnlinkFile(SwapDir *, sfileno);
-extern void storeAufsDirReplAdd(SwapDir * SD, StoreEntry *);
-extern void storeAufsDirReplRemove(StoreEntry *);
-
 /*
  * Store IO stuff
  */
index 399e927ec4c63ad31b66450cf5bfb899ed2389b4..5e8736d0838290a0d5027cc4cbb0915ae6838df6 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * $Id: store_dir_aufs.cc,v 1.49 2002/08/23 22:31:09 hno Exp $
+ * $Id: store_dir_aufs.cc,v 1.50 2002/10/12 09:45:56 robertc Exp $
  *
  * DEBUG: section 47    Store Directory Routines
  * AUTHOR: Duane Wessels
 #include "squid.h"
 
 #include "store_asyncufs.h"
+#include "ufscommon.h"
 
-#define DefaultLevelOneDirs     16
-#define DefaultLevelTwoDirs     256
-#define STORE_META_BUFSZ 4096
-
-typedef struct _RebuildState RebuildState;
-struct _RebuildState {
-    SwapDir *sd;
-    int n_read;
-    FILE *log;
-    int speed;
-    int curlvl1;
-    int curlvl2;
-    struct {
-       unsigned int need_to_validate:1;
-       unsigned int clean:1;
-       unsigned int init:1;
-    } flags;
-    int done;
-    int in_dir;
-    int fn;
-    struct dirent *entry;
-    DIR *td;
-    char fullpath[SQUID_MAXPATHLEN];
-    char fullfilename[SQUID_MAXPATHLEN];
-    struct _store_rebuild_data counts;
-};
-
-static int n_asyncufs_dirs = 0;
-static int *asyncufs_dir_index = NULL;
 MemPool *squidaio_state_pool = NULL;
 MemPool *aufs_qread_pool = NULL;
 MemPool *aufs_qwrite_pool = NULL;
 static int asyncufs_initialised = 0;
 
-static char *storeAufsDirSwapSubDir(SwapDir *, int subdirn);
-static int storeAufsDirCreateDirectory(const char *path, int);
-static int storeAufsDirVerifyCacheDirs(SwapDir *);
-static int storeAufsDirVerifyDirectory(const char *path);
-static void storeAufsDirCreateSwapSubDirs(SwapDir *);
-static char *storeAufsDirSwapLogFile(SwapDir *, const char *);
-static EVH storeAufsDirRebuildFromDirectory;
-static EVH storeAufsDirRebuildFromSwapLog;
-static int storeAufsDirGetNextFile(RebuildState *, sfileno *, int *size);
-static StoreEntry *storeAufsDirAddDiskRestore(SwapDir * SD, const cache_key * key,
-    sfileno file_number,
-    size_t swap_file_sz,
-    time_t expires,
-    time_t timestamp,
-    time_t lastref,
-    time_t lastmod,
-    u_int32_t refcount,
-    u_int16_t flags,
-    int clean);
-static void storeAufsDirRebuild(SwapDir * sd);
-static void storeAufsDirCloseTmpSwapLog(SwapDir * sd);
-static FILE *storeAufsDirOpenTmpSwapLog(SwapDir *, int *, int *);
-static STLOGOPEN storeAufsDirOpenSwapLog;
-static STINIT storeAufsDirInit;
-static STFREE storeAufsDirFree;
-static STLOGCLEANSTART storeAufsDirWriteCleanStart;
-static STLOGCLEANNEXTENTRY storeAufsDirCleanLogNextEntry;
-static STLOGCLEANWRITE storeAufsDirWriteCleanEntry;
-static STLOGCLEANDONE storeAufsDirWriteCleanDone;
-static STLOGCLOSE storeAufsDirCloseSwapLog;
-static STLOGWRITE storeAufsDirSwapLog;
-static STNEWFS storeAufsDirNewfs;
 static STDUMP storeAufsDirDump;
-static STMAINTAINFS storeAufsDirMaintain;
 static STCHECKOBJ storeAufsDirCheckObj;
-static STREFOBJ storeAufsDirRefObj;
-static STUNREFOBJ storeAufsDirUnrefObj;
-static QS rev_int_sort;
-static int storeAufsDirClean(int swap_index);
-static EVH storeAufsDirCleanEvent;
-static int storeAufsDirIs(SwapDir * sd);
-static int storeAufsFilenoBelongsHere(int fn, int F0, int F1, int F2);
-static int storeAufsCleanupDoubleCheck(SwapDir *, StoreEntry *);
-static void storeAufsDirStats(SwapDir *, StoreEntry *);
-static void storeAufsDirInitBitmap(SwapDir *);
-static int storeAufsDirValidFileno(SwapDir *, sfileno, int);
+static void storeAufsDirIOUnlinkFile(char *path);
+
 
 /* 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!
- */
-
-static int
-storeAufsDirMapBitTest(SwapDir * SD, sfileno filn)
-{
-    squidaioinfo_t *aioinfo;
-    aioinfo = (squidaioinfo_t *) SD->fsdata;
-    return file_map_bit_test(aioinfo->map, filn);
-}
-
-static void
-storeAufsDirMapBitSet(SwapDir * SD, sfileno filn)
-{
-    squidaioinfo_t *aioinfo;
-    aioinfo = (squidaioinfo_t *) SD->fsdata;
-    file_map_bit_set(aioinfo->map, filn);
-}
-
-void
-storeAufsDirMapBitReset(SwapDir * SD, sfileno filn)
-{
-    squidaioinfo_t *aioinfo;
-    aioinfo = (squidaioinfo_t *) SD->fsdata;
-    /*
-     * We have to test the bit before calling file_map_bit_reset.
-     * file_map_bit_reset doesn't do bounds checking.  It assumes
-     * filn is a valid file number, but it might not be because
-     * the map is dynamic in size.  Also clearing an already clear
-     * bit puts the map counter of-of-whack.
-     */
-    if (file_map_bit_test(aioinfo->map, filn))
-       file_map_bit_reset(aioinfo->map, filn);
-}
-
-int
-storeAufsDirMapBitAllocate(SwapDir * SD)
-{
-    squidaioinfo_t *aioinfo = (squidaioinfo_t *) SD->fsdata;
-    int fn;
-    fn = file_map_allocate(aioinfo->map, aioinfo->suggest);
-    file_map_bit_set(aioinfo->map, fn);
-    aioinfo->suggest = fn + 1;
-    return fn;
-}
-
-/*
- * Initialise the asyncufs bitmap
- *
- * If there already is a bitmap, and the numobjects is larger than currently
- * configured, we allocate a new bitmap and 'grow' the old one into it.
- */
-static void
-storeAufsDirInitBitmap(SwapDir * sd)
-{
-    squidaioinfo_t *aioinfo = (squidaioinfo_t *) sd->fsdata;
-
-    if (aioinfo->map == NULL) {
-       /* First time */
-       aioinfo->map = file_map_create();
-    } else if (aioinfo->map->max_n_files) {
-       /* it grew, need to expand */
-       /* XXX We don't need it anymore .. */
-    }
-    /* else it shrunk, and we leave the old one in place */
-}
-
-static char *
-storeAufsDirSwapSubDir(SwapDir * sd, int subdirn)
-{
-    squidaioinfo_t *aioinfo = (squidaioinfo_t *) sd->fsdata;
-
-    LOCAL_ARRAY(char, fullfilename, SQUID_MAXPATHLEN);
-    assert(0 <= subdirn && subdirn < aioinfo->l1);
-    snprintf(fullfilename, SQUID_MAXPATHLEN, "%s/%02X", sd->path, subdirn);
-    return fullfilename;
-}
-
-static int
-storeAufsDirCreateDirectory(const char *path, int should_exist)
-{
-    int created = 0;
-    struct stat st;
-    getCurrentTime();
-    if (0 == stat(path, &st)) {
-       if (S_ISDIR(st.st_mode)) {
-           debug(47, should_exist ? 3 : 1) ("%s exists\n", path);
-       } else {
-           fatalf("Swap directory %s is not a directory.", path);
-       }
-    } else if (0 == mkdir(path, 0755)) {
-       debug(47, should_exist ? 1 : 3) ("%s created\n", path);
-       created = 1;
-    } else {
-       fatalf("Failed to make swap directory %s: %s",
-           path, xstrerror());
-    }
-    return created;
-}
-
-static int
-storeAufsDirVerifyDirectory(const char *path)
-{
-    struct stat sb;
-    if (stat(path, &sb) < 0) {
-       debug(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 storeAufsDirInit().  If this returns < 0,
- * then Squid exits, complains about swap directories not
- * existing, and instructs the admin to run 'squid -z'
- */
-static int
-storeAufsDirVerifyCacheDirs(SwapDir * sd)
-{
-    squidaioinfo_t *aioinfo = (squidaioinfo_t *) sd->fsdata;
-    int j;
-    const char *path = sd->path;
-
-    if (storeAufsDirVerifyDirectory(path) < 0)
-       return -1;
-    for (j = 0; j < aioinfo->l1; j++) {
-       path = storeAufsDirSwapSubDir(sd, j);
-       if (storeAufsDirVerifyDirectory(path) < 0)
-           return -1;
-    }
-    return 0;
-}
-
-static void
-storeAufsDirCreateSwapSubDirs(SwapDir * sd)
-{
-    squidaioinfo_t *aioinfo = (squidaioinfo_t *) sd->fsdata;
-    int i, k;
-    int should_exist;
-    LOCAL_ARRAY(char, name, MAXPATHLEN);
-    for (i = 0; i < aioinfo->l1; i++) {
-       snprintf(name, MAXPATHLEN, "%s/%02X", sd->path, i);
-       if (storeAufsDirCreateDirectory(name, 0))
-           should_exist = 0;
-       else
-           should_exist = 1;
-       debug(47, 1) ("Making directories in %s\n", name);
-       for (k = 0; k < aioinfo->l2; k++) {
-           snprintf(name, MAXPATHLEN, "%s/%02X/%02X", sd->path, i, k);
-           storeAufsDirCreateDirectory(name, should_exist);
-       }
-    }
-}
-
-static char *
-storeAufsDirSwapLogFile(SwapDir * sd, const char *ext)
-{
-    LOCAL_ARRAY(char, path, SQUID_MAXPATHLEN);
-    LOCAL_ARRAY(char, pathtmp, SQUID_MAXPATHLEN);
-    LOCAL_ARRAY(char, digit, 32);
-    char *pathtmp2;
-    if (Config.Log.swap) {
-       xstrncpy(pathtmp, sd->path, SQUID_MAXPATHLEN - 64);
-       pathtmp2 = pathtmp;
-       while ((pathtmp2 = strchr(pathtmp2, '/')) != NULL)
-           *pathtmp2 = '.';
-       while (strlen(pathtmp) && pathtmp[strlen(pathtmp) - 1] == '.')
-           pathtmp[strlen(pathtmp) - 1] = '\0';
-       for (pathtmp2 = pathtmp; *pathtmp2 == '.'; pathtmp2++);
-       snprintf(path, SQUID_MAXPATHLEN - 64, Config.Log.swap, pathtmp2);
-       if (strncmp(path, Config.Log.swap, SQUID_MAXPATHLEN - 64) == 0) {
-           strcat(path, ".");
-           snprintf(digit, 32, "%02d", sd->index);
-           strncat(path, digit, 3);
-       }
-    } else {
-       xstrncpy(path, sd->path, SQUID_MAXPATHLEN - 64);
-       strcat(path, "/swap.state");
-    }
-    if (ext)
-       strncat(path, ext, 16);
-    return path;
-}
-
-static void
-storeAufsDirOpenSwapLog(SwapDir * sd)
-{
-    squidaioinfo_t *aioinfo = (squidaioinfo_t *) sd->fsdata;
-    char *path;
-    int fd;
-    path = storeAufsDirSwapLogFile(sd, NULL);
-    fd = file_open(path, O_WRONLY | O_CREAT | O_BINARY);
-    if (fd < 0) {
-       debug(50, 1) ("%s: %s\n", path, xstrerror());
-       fatal("storeAufsDirOpenSwapLog: Failed to open swap log.");
-    }
-    debug(50, 3) ("Cache Dir #%d log opened on FD %d\n", sd->index, fd);
-    aioinfo->swaplog_fd = fd;
-    if (0 == n_asyncufs_dirs)
-       assert(NULL == asyncufs_dir_index);
-    n_asyncufs_dirs++;
-    assert(n_asyncufs_dirs <= Config.cacheSwap.n_configured);
-}
-
-static void
-storeAufsDirCloseSwapLog(SwapDir * sd)
-{
-    squidaioinfo_t *aioinfo = (squidaioinfo_t *) sd->fsdata;
-    if (aioinfo->swaplog_fd < 0)       /* not open */
-       return;
-    file_close(aioinfo->swaplog_fd);
-    debug(47, 3) ("Cache Dir #%d log closed on FD %d\n",
-       sd->index, aioinfo->swaplog_fd);
-    aioinfo->swaplog_fd = -1;
-    n_asyncufs_dirs--;
-    assert(n_asyncufs_dirs >= 0);
-    if (0 == n_asyncufs_dirs)
-       safe_free(asyncufs_dir_index);
-}
-
-static void
-storeAufsDirInit(SwapDir * sd)
-{
-    static int started_clean_event = 0;
-    static const char *errmsg =
-    "\tFailed to verify one of the swap directories, Check cache.log\n"
-    "\tfor details.  Run 'squid -z' to create swap directories\n"
-    "\tif needed, or if running Squid for the first time.";
-    storeAufsDirInitBitmap(sd);
-    if (storeAufsDirVerifyCacheDirs(sd) < 0)
-       fatal(errmsg);
-    storeAufsDirOpenSwapLog(sd);
-    storeAufsDirRebuild(sd);
-    if (!started_clean_event) {
-       eventAdd("storeDirClean", storeAufsDirCleanEvent, NULL, 15.0, 1);
-       started_clean_event = 1;
-    }
-    (void) storeDirGetBlkSize(sd->path, &sd->fs.blksize);
-}
-
-static void
-storeAufsDirRebuildFromDirectory(void *data)
-{
-    RebuildState *rb = data;
-    SwapDir *SD = rb->sd;
-    LOCAL_ARRAY(char, hdr_buf, SM_PAGE_SIZE);
-    StoreEntry *e = NULL;
-    StoreEntry tmpe;
-    cache_key key[MD5_DIGEST_CHARS];
-    sfileno filn = 0;
-    int count;
-    int size;
-    struct stat sb;
-    int swap_hdr_len;
-    int fd = -1;
-    tlv *tlv_list;
-    tlv *t;
-    assert(rb != NULL);
-    debug(47, 3) ("storeAufsDirRebuildFromDirectory: DIR #%d\n", rb->sd->index);
-    for (count = 0; count < rb->speed; count++) {
-       assert(fd == -1);
-       fd = storeAufsDirGetNextFile(rb, &filn, &size);
-       if (fd == -2) {
-           debug(47, 1) ("Done scanning %s swaplog (%d entries)\n",
-               rb->sd->path, rb->n_read);
-           store_dirs_rebuilding--;
-           storeAufsDirCloseTmpSwapLog(rb->sd);
-           storeRebuildComplete(&rb->counts);
-           cbdataFree(rb);
-           return;
-       } else if (fd < 0) {
-           continue;
-       }
-       assert(fd > -1);
-       /* lets get file stats here */
-       if (fstat(fd, &sb) < 0) {
-           debug(47, 1) ("storeAufsDirRebuildFromDirectory: fstat(FD %d): %s\n",
-               fd, xstrerror());
-           file_close(fd);
-           store_open_disk_fd--;
-           fd = -1;
-           continue;
-       }
-       if ((++rb->counts.scancount & 0xFFFF) == 0)
-           debug(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) ("storeAufsDirRebuildFromDirectory: read(FD %d): %s\n",
-               fd, xstrerror());
-           file_close(fd);
-           store_open_disk_fd--;
-           fd = -1;
-           continue;
-       }
-       file_close(fd);
-       store_open_disk_fd--;
-       fd = -1;
-       swap_hdr_len = 0;
-#if USE_TRUNCATE
-       if (sb.st_size == 0)
-           continue;
-#endif
-       tlv_list = storeSwapMetaUnpack(hdr_buf, &swap_hdr_len);
-       if (tlv_list == NULL) {
-           debug(47, 1) ("storeAufsDirRebuildFromDirectory: failed to get meta data\n");
-           /* XXX shouldn't this be a call to storeAufsUnlink ? */
-           storeAufsDirUnlinkFile(SD, filn);
-           continue;
-       }
-       debug(47, 3) ("storeAufsDirRebuildFromDirectory: successful swap meta unpacking\n");
-       memset(key, '\0', MD5_DIGEST_CHARS);
-       memset(&tmpe, '\0', sizeof(StoreEntry));
-       for (t = tlv_list; t; t = t->next) {
-           switch (t->type) {
-           case STORE_META_KEY:
-               assert(t->length == MD5_DIGEST_CHARS);
-               xmemcpy(key, t->value, MD5_DIGEST_CHARS);
-               break;
-           case STORE_META_STD:
-               assert(t->length == STORE_HDR_METASIZE);
-               xmemcpy(&tmpe.timestamp, t->value, STORE_HDR_METASIZE);
-               break;
-           default:
-               break;
-           }
-       }
-       storeSwapTLVFree(tlv_list);
-       tlv_list = NULL;
-       if (storeKeyNull(key)) {
-           debug(47, 1) ("storeAufsDirRebuildFromDirectory: NULL key\n");
-           storeAufsDirUnlinkFile(SD, filn);
-           continue;
-       }
-       tmpe.hash.key = key;
-       /* check sizes */
-       if (tmpe.swap_file_sz == 0) {
-           tmpe.swap_file_sz = sb.st_size;
-       } else if (tmpe.swap_file_sz == sb.st_size - swap_hdr_len) {
-           tmpe.swap_file_sz = sb.st_size;
-       } else if (tmpe.swap_file_sz != sb.st_size) {
-           debug(47, 1) ("storeAufsDirRebuildFromDirectory: SIZE MISMATCH %ld!=%ld\n",
-               (long int) tmpe.swap_file_sz, (long int) sb.st_size);
-           storeAufsDirUnlinkFile(SD, filn);
-           continue;
-       }
-       if (EBIT_TEST(tmpe.flags, KEY_PRIVATE)) {
-           storeAufsDirUnlinkFile(SD, filn);
-           rb->counts.badflags++;
-           continue;
-       }
-       e = storeGet(key);
-       if (e && e->lastref >= tmpe.lastref) {
-           /* key already exists, current entry is newer */
-           /* keep old, ignore new */
-           rb->counts.dupcount++;
-           continue;
-       } else if (NULL != e) {
-           /* URL already exists, this swapfile not being used */
-           /* junk old, load new */
-           storeRelease(e);    /* release old entry */
-           rb->counts.dupcount++;
-       }
-       rb->counts.objcount++;
-       storeEntryDump(&tmpe, 5);
-       e = storeAufsDirAddDiskRestore(SD, key,
-           filn,
-           tmpe.swap_file_sz,
-           tmpe.expires,
-           tmpe.timestamp,
-           tmpe.lastref,
-           tmpe.lastmod,
-           tmpe.refcount,      /* refcount */
-           tmpe.flags,         /* flags */
-           (int) rb->flags.clean);
-       storeDirSwapLog(e, SWAP_LOG_ADD);
-    }
-    eventAdd("storeRebuild", storeAufsDirRebuildFromDirectory, rb, 0.0, 1);
-}
-
-static void
-storeAufsDirRebuildFromSwapLog(void *data)
-{
-    RebuildState *rb = data;
-    SwapDir *SD = rb->sd;
-    StoreEntry *e = NULL;
-    storeSwapLogData s;
-    size_t ss = sizeof(storeSwapLogData);
-    int count;
-    int used;                  /* is swapfile already in use? */
-    int disk_entry_newer;      /* is the log entry newer than current entry? */
-    double x;
-    assert(rb != NULL);
-    /* load a number of objects per invocation */
-    for (count = 0; count < rb->speed; count++) {
-       if (fread(&s, ss, 1, rb->log) != 1) {
-           debug(47, 1) ("Done reading %s swaplog (%d entries)\n",
-               rb->sd->path, rb->n_read);
-           fclose(rb->log);
-           rb->log = NULL;
-           store_dirs_rebuilding--;
-           storeAufsDirCloseTmpSwapLog(rb->sd);
-           storeRebuildComplete(&rb->counts);
-           cbdataFree(rb);
-           return;
-       }
-       rb->n_read++;
-       if (s.op <= SWAP_LOG_NOP)
-           continue;
-       if (s.op >= SWAP_LOG_MAX)
-           continue;
-       /*
-        * BC: during 2.4 development, we changed the way swap file
-        * numbers are assigned and stored.  The high 16 bits used
-        * to encode the SD index number.  There used to be a call
-        * to storeDirProperFileno here that re-assigned the index 
-        * bits.  Now, for backwards compatibility, we just need
-        * to mask it off.
-        */
-       s.swap_filen &= 0x00FFFFFF;
-       debug(47, 3) ("storeAufsDirRebuildFromSwapLog: %s %s %08X\n",
-           swap_log_op_str[(int) s.op],
-           storeKeyText(s.key),
-           s.swap_filen);
-       if (s.op == SWAP_LOG_ADD) {
-           (void) 0;
-       } else if (s.op == SWAP_LOG_DEL) {
-           if ((e = storeGet(s.key)) != NULL) {
-               /*
-                * Make sure we don't unlink the file, it might be
-                * in use by a subsequent entry.  Also note that
-                * we don't have to subtract from store_swap_size
-                * because adding to store_swap_size happens in
-                * the cleanup procedure.
-                */
-               storeExpireNow(e);
-               storeReleaseRequest(e);
-               if (e->swap_filen > -1) {
-                   storeAufsDirReplRemove(e);
-                   storeAufsDirMapBitReset(SD, e->swap_filen);
-                   e->swap_filen = -1;
-                   e->swap_dirn = -1;
-               }
-               storeRelease(e);
-               rb->counts.objcount--;
-               rb->counts.cancelcount++;
-           }
-           continue;
-       } else {
-           x = log(++rb->counts.bad_log_op) / log(10.0);
-           if (0.0 == x - (double) (int) x)
-               debug(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 (!storeAufsDirValidFileno(SD, s.swap_filen, 0)) {
-           rb->counts.invalid++;
-           continue;
-       }
-       if (EBIT_TEST(s.flags, KEY_PRIVATE)) {
-           rb->counts.badflags++;
-           continue;
-       }
-       e = storeGet(s.key);
-       used = storeAufsDirMapBitTest(SD, s.swap_filen);
-       /* If this URL already exists in the cache, does the swap log
-        * appear to have a newer entry?  Compare 'lastref' from the
-        * swap log to e->lastref. */
-       disk_entry_newer = e ? (s.lastref > e->lastref ? 1 : 0) : 0;
-       if (used && !disk_entry_newer) {
-           /* log entry is old, ignore it */
-           rb->counts.clashcount++;
-           continue;
-       } else if (used && e && e->swap_filen == s.swap_filen && e->swap_dirn == SD->index) {
-           /* swapfile taken, same URL, newer, update meta */
-           if (e->store_status == STORE_OK) {
-               e->lastref = s.timestamp;
-               e->timestamp = s.timestamp;
-               e->expires = s.expires;
-               e->lastmod = s.lastmod;
-               e->flags = s.flags;
-               e->refcount += s.refcount;
-               storeAufsDirUnrefObj(SD, e);
-           } else {
-               debug_trap("storeAufsDirRebuildFromSwapLog: bad condition");
-               debug(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) {
-               storeAufsDirReplRemove(e);
-               /* Make sure we don't actually unlink the file */
-               storeAufsDirMapBitReset(SD, e->swap_filen);
-               e->swap_filen = -1;
-               e->swap_dirn = -1;
-           }
-           storeRelease(e);
-           rb->counts.dupcount++;
-       } else {
-           /* URL doesnt exist, swapfile not in use */
-           /* load new */
-           (void) 0;
-       }
-       /* update store_swap_size */
-       rb->counts.objcount++;
-       e = storeAufsDirAddDiskRestore(SD, s.key,
-           s.swap_filen,
-           s.swap_file_sz,
-           s.expires,
-           s.timestamp,
-           s.lastref,
-           s.lastmod,
-           s.refcount,
-           s.flags,
-           (int) rb->flags.clean);
-       storeDirSwapLog(e, SWAP_LOG_ADD);
-    }
-    eventAdd("storeRebuild", storeAufsDirRebuildFromSwapLog, rb, 0.0, 1);
-}
-
-static int
-storeAufsDirGetNextFile(RebuildState * rb, sfileno * filn_p, int *size)
-{
-    SwapDir *SD = rb->sd;
-    squidaioinfo_t *aioinfo = (squidaioinfo_t *) SD->fsdata;
-    int fd = -1;
-    int used = 0;
-    int dirs_opened = 0;
-    debug(47, 3) ("storeAufsDirGetNextFile: flag=%d, %d: /%02X/%02X\n",
-       rb->flags.init,
-       rb->sd->index,
-       rb->curlvl1,
-       rb->curlvl2);
-    if (rb->done)
-       return -2;
-    while (fd < 0 && rb->done == 0) {
-       fd = -1;
-       if (0 == rb->flags.init) {      /* initialize, open first file */
-           rb->done = 0;
-           rb->curlvl1 = 0;
-           rb->curlvl2 = 0;
-           rb->in_dir = 0;
-           rb->flags.init = 1;
-           assert(Config.cacheSwap.n_configured > 0);
-       }
-       if (0 == rb->in_dir) {  /* we need to read in a new directory */
-           snprintf(rb->fullpath, SQUID_MAXPATHLEN, "%s/%02X/%02X",
-               rb->sd->path,
-               rb->curlvl1, rb->curlvl2);
-           if (dirs_opened)
-               return -1;
-           rb->td = opendir(rb->fullpath);
-           dirs_opened++;
-           if (rb->td == NULL) {
-               debug(47, 1) ("storeAufsDirGetNextFile: opendir: %s: %s\n",
-                   rb->fullpath, xstrerror());
-           } else {
-               rb->entry = readdir(rb->td);    /* skip . and .. */
-               rb->entry = readdir(rb->td);
-               if (rb->entry == NULL && errno == ENOENT)
-                   debug(47, 1) ("storeAufsDirGetNextFile: directory does not exist!.\n");
-               debug(47, 3) ("storeAufsDirGetNextFile: Directory %s\n", rb->fullpath);
-           }
-       }
-       if (rb->td != NULL && (rb->entry = readdir(rb->td)) != NULL) {
-           rb->in_dir++;
-           if (sscanf(rb->entry->d_name, "%x", &rb->fn) != 1) {
-               debug(47, 3) ("storeAufsDirGetNextFile: invalid %s\n",
-                   rb->entry->d_name);
-               continue;
-           }
-           if (!storeAufsFilenoBelongsHere(rb->fn, rb->sd->index, rb->curlvl1, rb->curlvl2)) {
-               debug(47, 3) ("storeAufsDirGetNextFile: %08X does not belong in %d/%d/%d\n",
-                   rb->fn, rb->sd->index, rb->curlvl1, rb->curlvl2);
-               continue;
-           }
-           used = storeAufsDirMapBitTest(SD, rb->fn);
-           if (used) {
-               debug(47, 3) ("storeAufsDirGetNextFile: Locked, continuing with next.\n");
-               continue;
-           }
-           snprintf(rb->fullfilename, SQUID_MAXPATHLEN, "%s/%s",
-               rb->fullpath, rb->entry->d_name);
-           debug(47, 3) ("storeAufsDirGetNextFile: Opening %s\n", rb->fullfilename);
-           fd = file_open(rb->fullfilename, O_RDONLY | O_BINARY);
-           if (fd < 0)
-               debug(47, 1) ("storeAufsDirGetNextFile: %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 < aioinfo->l2)
-           continue;
-       rb->curlvl2 = 0;
-       if (++rb->curlvl1 < aioinfo->l1)
-           continue;
-       rb->curlvl1 = 0;
-       rb->done = 1;
-    }
-    *filn_p = rb->fn;
-    return fd;
-}
-
-/* Add a new object to the cache with empty memory copy and pointer to disk
- * use to rebuild store from disk. */
-static StoreEntry *
-storeAufsDirAddDiskRestore(SwapDir * SD, const cache_key * key,
-    sfileno file_number,
-    size_t swap_file_sz,
-    time_t expires,
-    time_t timestamp,
-    time_t lastref,
-    time_t lastmod,
-    u_int32_t refcount,
-    u_int16_t flags,
-    int clean)
-{
-    StoreEntry *e = NULL;
-    debug(47, 5) ("storeAufsAddDiskRestore: %s, fileno=%08X\n", storeKeyText(key), file_number);
-    /* if you call this you'd better be sure file_number is not 
-     * already in use! */
-    e = new_StoreEntry(STORE_ENTRY_WITHOUT_MEMOBJ, NULL, NULL);
-    e->store_status = STORE_OK;
-    storeSetMemStatus(e, NOT_IN_MEMORY);
-    e->swap_status = SWAPOUT_DONE;
-    e->swap_filen = file_number;
-    e->swap_dirn = SD->index;
-    e->swap_file_sz = swap_file_sz;
-    e->lock_count = 0;
-    e->lastref = lastref;
-    e->timestamp = timestamp;
-    e->expires = expires;
-    e->lastmod = lastmod;
-    e->refcount = refcount;
-    e->flags = flags;
-    EBIT_SET(e->flags, ENTRY_CACHABLE);
-    EBIT_CLR(e->flags, RELEASE_REQUEST);
-    EBIT_CLR(e->flags, KEY_PRIVATE);
-    e->ping_status = PING_NONE;
-    EBIT_CLR(e->flags, ENTRY_VALIDATED);
-    storeAufsDirMapBitSet(SD, e->swap_filen);
-    storeHashInsert(e, key);   /* do it after we clear KEY_PRIVATE */
-    storeAufsDirReplAdd(SD, e);
-    return e;
-}
-
-CBDATA_TYPE(RebuildState);
-
-static void
-storeAufsDirRebuild(SwapDir * sd)
-{
-    RebuildState *rb;
-    int clean = 0;
-    int zero = 0;
-    FILE *fp;
-    EVH *func = NULL;
-    CBDATA_INIT_TYPE(RebuildState);
-    rb = cbdataAlloc(RebuildState);
-    rb->sd = sd;
-    rb->speed = opt_foreground_rebuild ? 1 << 30 : 50;
-    /*
-     * If the swap.state file exists in the cache_dir, then
-     * we'll use storeAufsDirRebuildFromSwapLog(), otherwise we'll
-     * use storeAufsDirRebuildFromDirectory() to open up each file
-     * and suck in the meta data.
-     */
-    fp = storeAufsDirOpenTmpSwapLog(sd, &clean, &zero);
-    if (fp == NULL || zero) {
-       if (fp != NULL)
-           fclose(fp);
-       func = storeAufsDirRebuildFromDirectory;
-    } else {
-       func = storeAufsDirRebuildFromSwapLog;
-       rb->log = fp;
-       rb->flags.clean = (unsigned int) clean;
-    }
-    if (!clean)
-       rb->flags.need_to_validate = 1;
-    debug(47, 1) ("Rebuilding storage in %s (%s)\n",
-       sd->path, clean ? "CLEAN" : "DIRTY");
-    store_dirs_rebuilding++;
-    eventAdd("storeRebuild", func, rb, 0.0, 1);
-}
-
-static void
-storeAufsDirCloseTmpSwapLog(SwapDir * sd)
-{
-    squidaioinfo_t *aioinfo = (squidaioinfo_t *) sd->fsdata;
-    char *swaplog_path = xstrdup(storeAufsDirSwapLogFile(sd, NULL));
-    char *new_path = xstrdup(storeAufsDirSwapLogFile(sd, ".new"));
-    int fd;
-    file_close(aioinfo->swaplog_fd);
-#if defined (_SQUID_OS2_) || defined (_SQUID_CYGWIN_)
-    if (unlink(swaplog_path) < 0) {
-       debug(50, 0) ("%s: %s\n", swaplog_path, xstrerror());
-       fatal("storeAufsDirCloseTmpSwapLog: unlink failed");
-    }
-#endif
-    if (xrename(new_path, swaplog_path) < 0) {
-       fatal("storeAufsDirCloseTmpSwapLog: rename failed");
-    }
-    fd = file_open(swaplog_path, O_WRONLY | O_CREAT | O_BINARY);
-    if (fd < 0) {
-       debug(50, 1) ("%s: %s\n", swaplog_path, xstrerror());
-       fatal("storeAufsDirCloseTmpSwapLog: Failed to open swap log.");
-    }
-    safe_free(swaplog_path);
-    safe_free(new_path);
-    aioinfo->swaplog_fd = fd;
-    debug(47, 3) ("Cache Dir #%d log opened on FD %d\n", sd->index, fd);
-}
-
-static FILE *
-storeAufsDirOpenTmpSwapLog(SwapDir * sd, int *clean_flag, int *zero_flag)
-{
-    squidaioinfo_t *aioinfo = (squidaioinfo_t *) sd->fsdata;
-    char *swaplog_path = xstrdup(storeAufsDirSwapLogFile(sd, NULL));
-    char *clean_path = xstrdup(storeAufsDirSwapLogFile(sd, ".last-clean"));
-    char *new_path = xstrdup(storeAufsDirSwapLogFile(sd, ".new"));
-    struct stat log_sb;
-    struct stat clean_sb;
-    FILE *fp;
-    int fd;
-    if (stat(swaplog_path, &log_sb) < 0) {
-       debug(47, 1) ("Cache Dir #%d: No log file\n", sd->index);
-       safe_free(swaplog_path);
-       safe_free(clean_path);
-       safe_free(new_path);
-       return NULL;
-    }
-    *zero_flag = log_sb.st_size == 0 ? 1 : 0;
-    /* close the existing write-only FD */
-    if (aioinfo->swaplog_fd >= 0)
-       file_close(aioinfo->swaplog_fd);
-    /* open a write-only FD for the new log */
-    fd = file_open(new_path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY);
-    if (fd < 0) {
-       debug(50, 1) ("%s: %s\n", new_path, xstrerror());
-       fatal("storeDirOpenTmpSwapLog: Failed to open swap log.");
-    }
-    aioinfo->swaplog_fd = fd;
-    /* open a read-only stream of the old log */
-    fp = fopen(swaplog_path, "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.
- */
-static int
-storeAufsDirWriteCleanStart(SwapDir * sd)
-{
-    struct _clean_state *state = xcalloc(1, sizeof(*state));
-#if HAVE_FCHMOD
-    struct stat sb;
-#endif
-    sd->log.clean.write = NULL;
-    sd->log.clean.state = NULL;
-    state->new = xstrdup(storeAufsDirSwapLogFile(sd, ".clean"));
-    state->fd = file_open(state->new, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY);
-    if (state->fd < 0) {
-       xfree(state->new);
-       xfree(state);
-       return -1;
-    }
-    state->cur = xstrdup(storeAufsDirSwapLogFile(sd, NULL));
-    state->cln = xstrdup(storeAufsDirSwapLogFile(sd, ".last-clean"));
-    state->outbuf = xcalloc(CLEAN_BUF_SZ, 1);
-    state->outbuf_offset = 0;
-    state->walker = sd->repl->WalkInit(sd->repl);
-    unlink(state->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 = storeAufsDirWriteCleanEntry;
-    sd->log.clean.state = state;
-    return 0;
-}
-
-/*
- * Get the next entry that is a candidate for clean log writing
- */
-const StoreEntry *
-storeAufsDirCleanLogNextEntry(SwapDir * sd)
-{
-    const StoreEntry *entry = NULL;
-    struct _clean_state *state = sd->log.clean.state;
-    if (state->walker)
-       entry = state->walker->Next(state->walker);
-    return entry;
-}
-
-/*
- * "write" an entry to the clean log file.
- */
-static void
-storeAufsDirWriteCleanEntry(SwapDir * sd, const StoreEntry * e)
-{
-    storeSwapLogData s;
-    static size_t ss = sizeof(storeSwapLogData);
-    struct _clean_state *state = sd->log.clean.state;
-    memset(&s, '\0', ss);
-    s.op = (char) SWAP_LOG_ADD;
-    s.swap_filen = e->swap_filen;
-    s.timestamp = e->timestamp;
-    s.lastref = e->lastref;
-    s.expires = e->expires;
-    s.lastmod = e->lastmod;
-    s.swap_file_sz = e->swap_file_sz;
-    s.refcount = e->refcount;
-    s.flags = e->flags;
-    xmemcpy(&s.key, e->hash.key, MD5_DIGEST_CHARS);
-    xmemcpy(state->outbuf + state->outbuf_offset, &s, ss);
-    state->outbuf_offset += ss;
-    /* buffered write */
-    if (state->outbuf_offset + ss > CLEAN_BUF_SZ) {
-       if (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;
-    }
-}
-
-static void
-storeAufsDirWriteCleanDone(SwapDir * sd)
-{
-    int fd;
-    struct _clean_state *state = sd->log.clean.state;
-    if (NULL == state)
-       return;
-    if (state->fd < 0)
-       return;
-    state->walker->Done(state->walker);
-    if (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.
-     */
-    storeAufsDirCloseSwapLog(sd);
-    /* save the fd value for a later test */
-    fd = state->fd;
-    /* rename */
-    if (state->fd >= 0) {
-#if defined(_SQUID_OS2_) || defined (_SQUID_CYGWIN_)
-       file_close(state->fd);
-       state->fd = -1;
-       if (unlink(state->cur) < 0)
-           debug(50, 0) ("storeDirWriteCleanLogs: unlinkd failed: %s, %s\n",
-               xstrerror(), state->cur);
-#endif
-       xrename(state->new, state->cur);
-    }
-    /* touch a timestamp file if we're not still validating */
-    if (store_dirs_rebuilding)
-       (void) 0;
-    else if (fd < 0)
-       (void) 0;
-    else
-       file_close(file_open(state->cln, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY));
-    /* close */
-    safe_free(state->cur);
-    safe_free(state->new);
-    safe_free(state->cln);
-    if (state->fd >= 0)
-       file_close(state->fd);
-    state->fd = -1;
-    safe_free(state);
-    sd->log.clean.state = NULL;
-    sd->log.clean.write = NULL;
-}
-
-static void
-storeSwapLogDataFree(void *s)
-{
-    memFree(s, MEM_SWAP_LOG_DATA);
-}
-
-static void
-storeAufsDirSwapLog(const SwapDir * sd, const StoreEntry * e, int op)
-{
-    squidaioinfo_t *aioinfo = (squidaioinfo_t *) sd->fsdata;
-    storeSwapLogData *s = memAllocate(MEM_SWAP_LOG_DATA);
-    s->op = (char) op;
-    s->swap_filen = e->swap_filen;
-    s->timestamp = e->timestamp;
-    s->lastref = e->lastref;
-    s->expires = e->expires;
-    s->lastmod = e->lastmod;
-    s->swap_file_sz = e->swap_file_sz;
-    s->refcount = e->refcount;
-    s->flags = e->flags;
-    xmemcpy(s->key, e->hash.key, MD5_DIGEST_CHARS);
-    file_write(aioinfo->swaplog_fd,
-       -1,
-       s,
-       sizeof(storeSwapLogData),
-       NULL,
-       NULL,
-       (FREE *) storeSwapLogDataFree);
-}
-
-static void
-storeAufsDirNewfs(SwapDir * sd)
-{
-    debug(47, 3) ("Creating swap space in %s\n", sd->path);
-    storeAufsDirCreateDirectory(sd->path, 0);
-    storeAufsDirCreateSwapSubDirs(sd);
-}
-
-static int
-rev_int_sort(const void *A, const void *B)
-{
-    const int *i1 = A;
-    const int *i2 = B;
-    return *i2 - *i1;
-}
-
-static int
-storeAufsDirClean(int swap_index)
-{
-    DIR *dp = NULL;
-    struct dirent *de = NULL;
-    LOCAL_ARRAY(char, p1, MAXPATHLEN + 1);
-    LOCAL_ARRAY(char, p2, MAXPATHLEN + 1);
-#if USE_TRUNCATE
-    struct stat sb;
-#endif
-    int files[20];
-    int swapfileno;
-    int fn;                    /* same as swapfileno, but with dirn bits set */
-    int n = 0;
-    int k = 0;
-    int N0, N1, N2;
-    int D0, D1, D2;
-    SwapDir *SD;
-    squidaioinfo_t *aioinfo;
-    N0 = n_asyncufs_dirs;
-    D0 = asyncufs_dir_index[swap_index % N0];
-    SD = &Config.cacheSwap.swapDirs[D0];
-    aioinfo = (squidaioinfo_t *) SD->fsdata;
-    N1 = aioinfo->l1;
-    D1 = (swap_index / N0) % N1;
-    N2 = aioinfo->l2;
-    D2 = ((swap_index / N0) / N1) % N2;
-    snprintf(p1, SQUID_MAXPATHLEN, "%s/%02X/%02X",
-       Config.cacheSwap.swapDirs[D0].path, D1, D2);
-    debug(36, 3) ("storeDirClean: Cleaning directory %s\n", p1);
-    dp = opendir(p1);
-    if (dp == NULL) {
-       if (errno == ENOENT) {
-           debug(36, 0) ("storeDirClean: WARNING: Creating %s\n", p1);
-           if (mkdir(p1, 0777) == 0)
-               return 0;
-       }
-       debug(50, 0) ("storeDirClean: %s: %s\n", p1, xstrerror());
-       safeunlink(p1, 1);
-       return 0;
-    }
-    while ((de = readdir(dp)) != NULL && k < 20) {
-       if (sscanf(de->d_name, "%X", &swapfileno) != 1)
-           continue;
-       fn = swapfileno;        /* XXX should remove this cruft ! */
-       if (storeAufsDirValidFileno(SD, fn, 1))
-           if (storeAufsDirMapBitTest(SD, fn))
-               if (storeAufsFilenoBelongsHere(fn, D0, D1, D2))
-                   continue;
-#if USE_TRUNCATE
-       if (!stat(de->d_name, &sb))
-           if (sb.st_size == 0)
-               continue;
-#endif
-       files[k++] = swapfileno;
-    }
-    closedir(dp);
-    if (k == 0)
-       return 0;
-    qsort(files, k, sizeof(int), rev_int_sort);
-    if (k > 10)
-       k = 10;
-    for (n = 0; n < k; n++) {
-       debug(36, 3) ("storeDirClean: Cleaning file %08X\n", files[n]);
-       snprintf(p2, MAXPATHLEN + 1, "%s/%08X", p1, files[n]);
-#if USE_TRUNCATE
-       truncate(p2, 0);
-#else
-       safeunlink(p2, 0);
-#endif
-       statCounter.swap.files_cleaned++;
-    }
-    debug(36, 3) ("Cleaned %d unused files from %s\n", k, p1);
-    return k;
-}
-
-static void
-storeAufsDirCleanEvent(void *unused)
-{
-    static int swap_index = 0;
-    int i;
-    int j = 0;
-    int n = 0;
-    /*
-     * Assert that there are AUFS cache_dirs configured, otherwise
-     * we should never be called.
-     */
-    assert(n_asyncufs_dirs);
-    if (NULL == asyncufs_dir_index) {
-       SwapDir *sd;
-       squidaioinfo_t *aioinfo;
-       /*
-        * Initialize the little array that translates AUFS cache_dir
-        * number into the Config.cacheSwap.swapDirs array index.
-        */
-       asyncufs_dir_index = xcalloc(n_asyncufs_dirs, sizeof(*asyncufs_dir_index));
-       for (i = 0, n = 0; i < Config.cacheSwap.n_configured; i++) {
-           sd = &Config.cacheSwap.swapDirs[i];
-           if (!storeAufsDirIs(sd))
-               continue;
-           asyncufs_dir_index[n++] = i;
-           aioinfo = (squidaioinfo_t *) sd->fsdata;
-           j += (aioinfo->l1 * aioinfo->l2);
-       }
-       assert(n == n_asyncufs_dirs);
-       /*
-        * Start the storeAufsDirClean() swap_index with a random
-        * value.  j equals the total number of AUFS level 2
-        * swap directories
-        */
-       swap_index = (int) (squid_random() % j);
-    }
-    if (0 == store_dirs_rebuilding) {
-       n = storeAufsDirClean(swap_index);
-       swap_index++;
-    }
-    eventAdd("storeDirClean", storeAufsDirCleanEvent, NULL,
-       15.0 * exp(-0.25 * n), 1);
-}
-
-static int
-storeAufsDirIs(SwapDir * sd)
-{
-    if (strncmp(sd->type, "aufs", 4) == 0)
-       return 1;
-    return 0;
-}
-
-/*
- * Does swapfile number 'fn' belong in cachedir #F0,
- * level1 dir #F1, level2 dir #F2?
- */
-static int
-storeAufsFilenoBelongsHere(int fn, int F0, int F1, int F2)
-{
-    int D1, D2;
-    int L1, L2;
-    int filn = fn;
-    squidaioinfo_t *aioinfo;
-    assert(F0 < Config.cacheSwap.n_configured);
-    aioinfo = (squidaioinfo_t *) Config.cacheSwap.swapDirs[F0].fsdata;
-    L1 = aioinfo->l1;
-    L2 = aioinfo->l2;
-    D1 = ((filn / L2) / L2) % L1;
-    if (F1 != D1)
-       return 0;
-    D2 = (filn / L2) % L2;
-    if (F2 != D2)
-       return 0;
-    return 1;
-}
-
-int
-storeAufsDirValidFileno(SwapDir * SD, sfileno filn, int flag)
-{
-    squidaioinfo_t *aioinfo = (squidaioinfo_t *) SD->fsdata;
-    if (filn < 0)
-       return 0;
-    /*
-     * If flag is set it means out-of-range file number should
-     * be considered invalid.
-     */
-    if (flag)
-       if (filn > aioinfo->map->max_n_files)
-           return 0;
-    return 1;
-}
-
-void
-storeAufsDirMaintain(SwapDir * SD)
-{
-    StoreEntry *e = NULL;
-    int removed = 0;
-    int max_scan;
-    int max_remove;
-    double f;
-    RemovalPurgeWalker *walker;
-    /* We can't delete objects while rebuilding swap */
-    if (store_dirs_rebuilding) {
-       return;
-    } else {
-       f = (double) (SD->cur_size - SD->low_size) / (SD->max_size - SD->low_size);
-       f = f < 0.0 ? 0.0 : f > 1.0 ? 1.0 : f;
-       max_scan = (int) (f * 400.0 + 100.0);
-       max_remove = (int) (f * 70.0 + 10.0);
-       /*
-        * This is kinda cheap, but so we need this priority hack?
-        */
-    }
-    debug(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)) ("storeAufsDirMaintain: %s removed %d/%d f=%.03f max_scan=%d\n",
-       SD->path, removed, max_remove, f, max_scan);
-}
-
 /*
  * storeAufsDirCheckObj
  *
@@ -1373,118 +80,18 @@ storeAufsDirCheckObj(SwapDir * SD, const StoreEntry * e)
     return loadav;
 }
 
-/*
- * storeAufsDirRefObj
- *
- * This routine is called whenever an object is referenced, so we can
- * maintain replacement information within the storage fs.
- */
-void
-storeAufsDirRefObj(SwapDir * SD, StoreEntry * e)
-{
-    debug(47, 3) ("storeAufsDirRefObj: referencing %p %d/%d\n", e, e->swap_dirn,
-       e->swap_filen);
-    if (SD->repl->Referenced)
-       SD->repl->Referenced(SD->repl, e, &e->repl);
-}
-
-/*
- * storeAufsDirUnrefObj
- * This routine is called whenever the last reference to an object is
- * removed, to maintain replacement information within the storage fs.
- */
-void
-storeAufsDirUnrefObj(SwapDir * SD, StoreEntry * e)
-{
-    debug(47, 3) ("storeAufsDirUnrefObj: referencing %p %d/%d\n", e, e->swap_dirn,
-       e->swap_filen);
-    if (SD->repl->Dereferenced)
-       SD->repl->Dereferenced(SD->repl, e, &e->repl);
-}
-
-/*
- * storeAufsDirUnlinkFile
- *
- * This routine unlinks a file and pulls it out of the bitmap.
- * It used to be in storeAufsUnlink(), however an interface change
- * forced this bit of code here. Eeek.
- */
 void
-storeAufsDirUnlinkFile(SwapDir * SD, sfileno f)
+storeAufsDirIOUnlinkFile(char *path)
 {
-    debug(79, 3) ("storeAufsDirUnlinkFile: unlinking fileno %08X\n", f);
-    /* storeAufsDirMapBitReset(SD, f); */
 #if USE_TRUNCATE_NOT_UNLINK
-    aioTruncate(storeAufsDirFullPath(SD, f, NULL), NULL, NULL);
+    aioTruncate(path, NULL, NULL);
 #else
-    aioUnlink(storeAufsDirFullPath(SD, f, NULL), NULL, NULL);
+    aioUnlink(path, NULL, NULL);
 #endif
 }
 
-/*
- * Add and remove the given StoreEntry from the replacement policy in
- * use.
- */
-
-void
-storeAufsDirReplAdd(SwapDir * SD, StoreEntry * e)
-{
-    debug(47, 4) ("storeAufsDirReplAdd: added node %p to dir %d\n", e,
-       SD->index);
-    SD->repl->Add(SD->repl, e, &e->repl);
-}
-
-
-void
-storeAufsDirReplRemove(StoreEntry * e)
-{
-    SwapDir *SD = INDEXSD(e->swap_dirn);
-    debug(47, 4) ("storeAufsDirReplRemove: remove node %p from dir %d\n", e,
-       SD->index);
-    SD->repl->Remove(SD->repl, e, &e->repl);
-}
-
-
-
 /* ========== LOCAL FUNCTIONS ABOVE, GLOBAL FUNCTIONS BELOW ========== */
 
-void
-storeAufsDirStats(SwapDir * SD, StoreEntry * sentry)
-{
-    squidaioinfo_t *aioinfo = SD->fsdata;
-    int totl_kb = 0;
-    int free_kb = 0;
-    int totl_in = 0;
-    int free_in = 0;
-    int x;
-    storeAppendPrintf(sentry, "First level subdirectories: %d\n", aioinfo->l1);
-    storeAppendPrintf(sentry, "Second level subdirectories: %d\n", aioinfo->l2);
-    storeAppendPrintf(sentry, "Maximum Size: %d KB\n", SD->max_size);
-    storeAppendPrintf(sentry, "Current Size: %d KB\n", SD->cur_size);
-    storeAppendPrintf(sentry, "Percent Used: %0.2f%%\n",
-       100.0 * SD->cur_size / SD->max_size);
-    storeAppendPrintf(sentry, "Filemap bits in use: %d of %d (%d%%)\n",
-       aioinfo->map->n_files_in_map, aioinfo->map->max_n_files,
-       percent(aioinfo->map->n_files_in_map, aioinfo->map->max_n_files));
-    x = storeDirGetUFSStats(SD->path, &totl_kb, &free_kb, &totl_in, &free_in);
-    if (0 == x) {
-       storeAppendPrintf(sentry, "Filesystem Space in use: %d/%d KB (%d%%)\n",
-           totl_kb - free_kb,
-           totl_kb,
-           percent(totl_kb - free_kb, totl_kb));
-       storeAppendPrintf(sentry, "Filesystem Inodes in use: %d/%d (%d%%)\n",
-           totl_in - free_in,
-           totl_in,
-           percent(totl_in - free_in, totl_in));
-    }
-    storeAppendPrintf(sentry, "Flags:");
-    if (SD->flags.selected)
-       storeAppendPrintf(sentry, " SELECTED");
-    if (SD->flags.read_only)
-       storeAppendPrintf(sentry, " READ-ONLY");
-    storeAppendPrintf(sentry, "\n");
-}
-
 static struct cache_dir_option options[] =
 {
 #if NOT_YET_DONE
@@ -1537,78 +144,10 @@ storeAufsDirReconfigure(SwapDir * sd, int index, char *path)
 void
 storeAufsDirDump(StoreEntry * entry, SwapDir * s)
 {
-    squidaioinfo_t *aioinfo = (squidaioinfo_t *) s->fsdata;
-    storeAppendPrintf(entry, " %d %d %d",
-       s->max_size >> 10,
-       aioinfo->l1,
-       aioinfo->l2);
+    commonUfsDirDump (entry, s);
     dump_cachedir_options(entry, options, s);
 }
 
-/*
- * Only "free" the filesystem specific stuff here
- */
-static void
-storeAufsDirFree(SwapDir * s)
-{
-    squidaioinfo_t *aioinfo = (squidaioinfo_t *) s->fsdata;
-    if (aioinfo->swaplog_fd > -1) {
-       file_close(aioinfo->swaplog_fd);
-       aioinfo->swaplog_fd = -1;
-    }
-    filemapFreeMemory(aioinfo->map);
-    xfree(aioinfo);
-    s->fsdata = NULL;          /* Will aid debugging... */
-}
-
-char *
-storeAufsDirFullPath(SwapDir * SD, sfileno filn, char *fullpath)
-{
-    LOCAL_ARRAY(char, fullfilename, SQUID_MAXPATHLEN);
-    squidaioinfo_t *aioinfo = (squidaioinfo_t *) SD->fsdata;
-    int L1 = aioinfo->l1;
-    int L2 = aioinfo->l2;
-    if (!fullpath)
-       fullpath = fullfilename;
-    fullpath[0] = '\0';
-    snprintf(fullpath, SQUID_MAXPATHLEN, "%s/%02X/%02X/%08X",
-       SD->path,
-       ((filn / L2) / L2) % L1,
-       (filn / L2) % L2,
-       filn);
-    return fullpath;
-}
-
-/*
- * storeAufsCleanupDoubleCheck
- *
- * This is called by storeCleanup() if -S was given on the command line.
- */
-static int
-storeAufsCleanupDoubleCheck(SwapDir * sd, StoreEntry * e)
-{
-    struct stat sb;
-    if (stat(storeAufsDirFullPath(sd, e->swap_filen, NULL), &sb) < 0) {
-       debug(47, 0) ("storeAufsCleanupDoubleCheck: MISSING SWAP FILE\n");
-       debug(47, 0) ("storeAufsCleanupDoubleCheck: FILENO %08X\n", e->swap_filen);
-       debug(47, 0) ("storeAufsCleanupDoubleCheck: PATH %s\n",
-           storeAufsDirFullPath(sd, e->swap_filen, NULL));
-       storeEntryDump(e, 0);
-       return -1;
-    }
-    if (e->swap_file_sz != sb.st_size) {
-       debug(47, 0) ("storeAufsCleanupDoubleCheck: SIZE MISMATCH\n");
-       debug(47, 0) ("storeAufsCleanupDoubleCheck: FILENO %08X\n", e->swap_filen);
-       debug(47, 0) ("storeAufsCleanupDoubleCheck: PATH %s\n",
-           storeAufsDirFullPath(sd, e->swap_filen, NULL));
-       debug(47, 0) ("storeAufsCleanupDoubleCheck: ENTRY SIZE: %ld, FILE SIZE: %ld\n",
-           (long int) e->swap_file_sz, (long int) sb.st_size);
-       storeEntryDump(e, 0);
-       return -1;
-    }
-    return 0;
-}
-
 /*
  * storeAufsDirParse *
  * Called when a *new* fs is being setup.
@@ -1620,7 +159,7 @@ storeAufsDirParse(SwapDir * sd, int index, char *path)
     int size;
     int l1;
     int l2;
-    squidaioinfo_t *aioinfo;
+    squidufsinfo_t *aioinfo;
 
     i = GetInteger();
     size = i << 10;            /* Mbytes to kbytes */
@@ -1635,9 +174,9 @@ storeAufsDirParse(SwapDir * sd, int index, char *path)
     if (l2 <= 0)
        fatal("storeAufsDirParse: invalid level 2 directories value");
 
-    aioinfo = xmalloc(sizeof(squidaioinfo_t));
+    aioinfo = xmalloc(sizeof(squidufsinfo_t));
     if (aioinfo == NULL)
-       fatal("storeAufsDirParse: couldn't xmalloc() squidaioinfo_t!\n");
+       fatal("storeAufsDirParse: couldn't xmalloc() squidufsinfo_t!\n");
 
     sd->index = index;
     sd->path = xstrdup(path);
@@ -1648,16 +187,17 @@ storeAufsDirParse(SwapDir * sd, int index, char *path)
     aioinfo->swaplog_fd = -1;
     aioinfo->map = NULL;       /* Debugging purposes */
     aioinfo->suggest = 0;
-    sd->init = storeAufsDirInit;
-    sd->newfs = storeAufsDirNewfs;
+    aioinfo->io.storeDirUnlinkFile = storeAufsDirIOUnlinkFile;
+    sd->init = commonUfsDirInit;
+    sd->newfs = commonUfsDirNewfs;
     sd->dump = storeAufsDirDump;
-    sd->freefs = storeAufsDirFree;
-    sd->dblcheck = storeAufsCleanupDoubleCheck;
-    sd->statfs = storeAufsDirStats;
-    sd->maintainfs = storeAufsDirMaintain;
+    sd->freefs = commonUfsDirFree;
+    sd->dblcheck = commonUfsCleanupDoubleCheck;
+    sd->statfs = commonUfsDirStats;
+    sd->maintainfs = commonUfsDirMaintain;
     sd->checkobj = storeAufsDirCheckObj;
-    sd->refobj = storeAufsDirRefObj;
-    sd->unrefobj = storeAufsDirUnrefObj;
+    sd->refobj = commonUfsDirRefObj;
+    sd->unrefobj = commonUfsDirUnrefObj;
     sd->callback = aioCheckCallbacks;
     sd->sync = aioSync;
     sd->obj.create = storeAufsCreate;
@@ -1666,12 +206,12 @@ storeAufsDirParse(SwapDir * sd, int index, char *path)
     sd->obj.read = storeAufsRead;
     sd->obj.write = storeAufsWrite;
     sd->obj.unlink = storeAufsUnlink;
-    sd->log.open = storeAufsDirOpenSwapLog;
-    sd->log.close = storeAufsDirCloseSwapLog;
-    sd->log.write = storeAufsDirSwapLog;
-    sd->log.clean.start = storeAufsDirWriteCleanStart;
-    sd->log.clean.nextentry = storeAufsDirCleanLogNextEntry;
-    sd->log.clean.done = storeAufsDirWriteCleanDone;
+    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);
 
index 18e4924643448a622eb1a9da0302cecac0cb8e14..1d138fc552a7643fce89c3d8d510facadb29468a 100644 (file)
@@ -5,6 +5,7 @@
 
 #include "squid.h"
 #include "store_asyncufs.h"
+#include "ufscommon.h"
 
 #if ASYNC_READ
 static AIOCB storeAufsReadDone;
@@ -32,7 +33,7 @@ storeAufsOpen(SwapDir * SD, StoreEntry * e, STFNCB * file_callback,
     STIOCB * callback, void *callback_data)
 {
     sfileno f = e->swap_filen;
-    char *path = storeAufsDirFullPath(SD, f, NULL);
+    char *path = commonUfsDirFullPath(SD, f, NULL);
     storeIOState *sio;
 #if !ASYNC_OPEN
     int fd;
@@ -87,8 +88,8 @@ storeAufsCreate(SwapDir * SD, StoreEntry * e, STFNCB * file_callback, STIOCB * c
 
     /* Allocate a number */
     dirn = SD->index;
-    filn = storeAufsDirMapBitAllocate(SD);
-    path = storeAufsDirFullPath(SD, filn, NULL);
+    filn = commonUfsDirMapBitAllocate(SD);
+    path = commonUfsDirFullPath(SD, filn, NULL);
 
     debug(79, 3) ("storeAufsCreate: fileno %08X\n", filn);
     /*
@@ -125,7 +126,7 @@ storeAufsCreate(SwapDir * SD, StoreEntry * e, STFNCB * file_callback, STIOCB * c
 #endif
 
     /* now insert into the replacement policy */
-    storeAufsDirReplAdd(SD, e);
+    commonUfsDirReplAdd(SD, e);
     return sio;
 
 }
@@ -229,9 +230,9 @@ void
 storeAufsUnlink(SwapDir * SD, StoreEntry * e)
 {
     debug(79, 3) ("storeAufsUnlink: dirno %d, fileno %08X\n", SD->index, e->swap_filen);
-    storeAufsDirReplRemove(e);
-    storeAufsDirMapBitReset(SD, e->swap_filen);
-    storeAufsDirUnlinkFile(SD, e->swap_filen);
+    commonUfsDirReplRemove(e);
+    commonUfsDirMapBitReset(SD, e->swap_filen);
+    commonUfsDirUnlinkFile(SD, e->swap_filen);
 }
 
 /*  === STATIC =========================================================== */
@@ -275,14 +276,14 @@ storeAufsOpenDone(int unused, void *my_data, int fd, int errflag)
     if (errflag || fd < 0) {
        errno = errflag;
        debug(79, 0) ("storeAufsOpenDone: %s\n", xstrerror());
-       debug(79, 1) ("\t%s\n", storeAufsDirFullPath(INDEXSD(sio->swap_dirn), sio->swap_filen, NULL));
+       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, storeAufsDirFullPath(INDEXSD(sio->swap_dirn), sio->swap_filen, NULL));
+    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;
index edc64f811c23a2012a239a9a8ddd19adb3e96507..c52dd857d3a04231beddfb06852af751cae2c9d6 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * $Id: store_dir_diskd.cc,v 1.70 2002/08/09 10:57:46 robertc Exp $
+ * $Id: store_dir_diskd.cc,v 1.71 2002/10/12 09:45:57 robertc Exp $
  *
  * DEBUG: section 47    Store Directory Routines
  * AUTHOR: Duane Wessels
 
 #include "store_diskd.h"
 
-#define DefaultLevelOneDirs     16
-#define DefaultLevelTwoDirs     256
-#define STORE_META_BDISKDZ 4096
+#include "ufscommon.h"
 
 diskd_stats_t diskd_stats;
 
-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_diskd_dirs = 0;
-static int *diskd_dir_index = NULL;
 MemPool *diskd_state_pool = NULL;
 static int diskd_initialised = 0;
 
-static char *storeDiskdDirSwapSubDir(SwapDir *, int subdirn);
-static int storeDiskdDirCreateDirectory(const char *path, int);
-static int storeDiskdDirVerifyCacheDirs(SwapDir *);
-static int storeDiskdDirVerifyDirectory(const char *path);
-static void storeDiskdDirCreateSwapSubDirs(SwapDir *);
-static char *storeDiskdDirSwapLogFile(SwapDir *, const char *);
-static EVH storeDiskdDirRebuildFromDirectory;
-static EVH storeDiskdDirRebuildFromSwapLog;
-static int storeDiskdDirGetNextFile(RebuildState *, sfileno *, int *size);
-static StoreEntry *storeDiskdDirAddDiskRestore(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 storeDiskdDirRebuild(SwapDir * sd);
-static void storeDiskdDirCloseTmpSwapLog(SwapDir * sd);
-static FILE *storeDiskdDirOpenTmpSwapLog(SwapDir *, int *, int *);
-static STLOGOPEN storeDiskdDirOpenSwapLog;
 static STINIT storeDiskdDirInit;
-static STFREE storeDiskdDirFree;
-static STLOGCLEANSTART storeDiskdDirWriteCleanStart;
-static STLOGCLEANNEXTENTRY storeDiskdDirCleanLogNextEntry;
-static STLOGCLEANWRITE storeDiskdDirWriteCleanEntry;
-static STLOGCLEANDONE storeDiskdDirWriteCleanDone;
-static STLOGCLOSE storeDiskdDirCloseSwapLog;
-static STLOGWRITE storeDiskdDirSwapLog;
-static STNEWFS storeDiskdDirNewfs;
 static STDUMP storeDiskdDirDump;
-static STMAINTAINFS storeDiskdDirMaintain;
 static STCHECKOBJ storeDiskdDirCheckObj;
-static STREFOBJ storeDiskdDirRefObj;
-static STUNREFOBJ storeDiskdDirUnrefObj;
-static QS rev_int_sort;
-static int storeDiskdDirClean(int swap_index);
-static EVH storeDiskdDirCleanEvent;
-static int storeDiskdDirIs(SwapDir * sd);
-static int storeDiskdFilenoBelongsHere(int fn, int F0, int F1, int F2);
-static int storeDiskdCleanupDoubleCheck(SwapDir *, StoreEntry *);
 static void storeDiskdDirStats(SwapDir *, StoreEntry *);
-static void storeDiskdDirInitBitmap(SwapDir *);
-static int storeDiskdDirValidFileno(SwapDir *, sfileno, int);
 static void storeDiskdStats(StoreEntry * sentry);
 static void storeDiskdDirSync(SwapDir *);
+static void storeDiskdDirIOUnlinkFile(char *path);
 
 /* The only externally visible interface */
 STSETUP storeFsSetup_diskd;
 
-/*
- * These functions were ripped straight out of the heart of store_dir.c.
- * They assume that the given filenum is on a diskd partiton, which may or
- * may not be true.. 
- * XXX this evilness should be tidied up at a later date!
- */
-
-static int
-storeDiskdDirMapBitTest(SwapDir * SD, sfileno filn)
-{
-    diskdinfo_t *diskdinfo;
-    diskdinfo = SD->fsdata;
-    return file_map_bit_test(diskdinfo->map, filn);
-}
-
-static void
-storeDiskdDirMapBitSet(SwapDir * SD, sfileno filn)
-{
-    diskdinfo_t *diskdinfo;
-    diskdinfo = SD->fsdata;
-    file_map_bit_set(diskdinfo->map, filn);
-}
-
-void
-storeDiskdDirMapBitReset(SwapDir * SD, sfileno filn)
-{
-    diskdinfo_t *diskdinfo;
-    diskdinfo = 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(diskdinfo->map, filn))
-       file_map_bit_reset(diskdinfo->map, filn);
-}
-
-int
-storeDiskdDirMapBitAllocate(SwapDir * SD)
-{
-    diskdinfo_t *diskdinfo = SD->fsdata;
-    int fn;
-    fn = file_map_allocate(diskdinfo->map, diskdinfo->suggest);
-    file_map_bit_set(diskdinfo->map, fn);
-    diskdinfo->suggest = fn + 1;
-    return fn;
-}
-
-/*
- * Initialise the diskd bitmap
- *
- * If there already is a bitmap, and the numobjects is larger than currently
- * configured, we allocate a new bitmap and 'grow' the old one into it.
- */
-static void
-storeDiskdDirInitBitmap(SwapDir * sd)
-{
-    diskdinfo_t *diskdinfo = sd->fsdata;
-
-    if (diskdinfo->map == NULL) {
-       /* First time */
-       diskdinfo->map = file_map_create();
-    } else if (diskdinfo->map->max_n_files) {
-       /* it grew, need to expand */
-       /* XXX We don't need it anymore .. */
-    }
-    /* else it shrunk, and we leave the old one in place */
-}
-
-static char *
-storeDiskdDirSwapSubDir(SwapDir * sd, int subdirn)
-{
-    diskdinfo_t *diskdinfo = sd->fsdata;
-
-    LOCAL_ARRAY(char, fullfilename, SQUID_MAXPATHLEN);
-    assert(0 <= subdirn && subdirn < diskdinfo->l1);
-    snprintf(fullfilename, SQUID_MAXPATHLEN, "%s/%02X", sd->path, subdirn);
-    return fullfilename;
-}
-
-static int
-storeDiskdDirCreateDirectory(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);
-       }
-    } else if (0 == mkdir(path, 0755)) {
-       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;
-}
-
-static int
-storeDiskdDirVerifyDirectory(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 storeDiskdDirInit().  If this returns < 0,
- * then Squid exits, complains about swap directories not
- * existing, and instructs the admin to run 'squid -z'
- */
-static int
-storeDiskdDirVerifyCacheDirs(SwapDir * sd)
-{
-    diskdinfo_t *diskdinfo = sd->fsdata;
-    int j;
-    const char *path = sd->path;
-
-    if (storeDiskdDirVerifyDirectory(path) < 0)
-       return -1;
-    for (j = 0; j < diskdinfo->l1; j++) {
-       path = storeDiskdDirSwapSubDir(sd, j);
-       if (storeDiskdDirVerifyDirectory(path) < 0)
-           return -1;
-    }
-    return 0;
-}
-
-static void
-storeDiskdDirCreateSwapSubDirs(SwapDir * sd)
-{
-    diskdinfo_t *diskdinfo = sd->fsdata;
-    int i, k;
-    int should_exist;
-    LOCAL_ARRAY(char, name, MAXPATHLEN);
-    for (i = 0; i < diskdinfo->l1; i++) {
-       snprintf(name, MAXPATHLEN, "%s/%02X", sd->path, i);
-       if (storeDiskdDirCreateDirectory(name, 0))
-           should_exist = 0;
-       else
-           should_exist = 1;
-       debug(47, 1) ("Making directories in %s\n", name);
-       for (k = 0; k < diskdinfo->l2; k++) {
-           snprintf(name, MAXPATHLEN, "%s/%02X/%02X", sd->path, i, k);
-           storeDiskdDirCreateDirectory(name, should_exist);
-       }
-    }
-}
-
-static char *
-storeDiskdDirSwapLogFile(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
-storeDiskdDirOpenSwapLog(SwapDir * sd)
-{
-    diskdinfo_t *diskdinfo = sd->fsdata;
-    char *path;
-    int fd;
-    path = storeDiskdDirSwapLogFile(sd, NULL);
-    fd = file_open(path, O_WRONLY | O_CREAT);
-    if (fd < 0) {
-       debug(50, 1) ("%s: %s\n", path, xstrerror());
-       fatal("storeDiskdDirOpenSwapLog: Failed to open swap log.");
-    }
-    debug(47, 3) ("Cache Dir #%d log opened on FD %d\n", sd->index, fd);
-    diskdinfo->swaplog_fd = fd;
-    if (0 == n_diskd_dirs)
-       assert(NULL == diskd_dir_index);
-    n_diskd_dirs++;
-    assert(n_diskd_dirs <= Config.cacheSwap.n_configured);
-}
-
-static void
-storeDiskdDirCloseSwapLog(SwapDir * sd)
-{
-    diskdinfo_t *diskdinfo = sd->fsdata;
-    if (diskdinfo->swaplog_fd < 0)     /* not open */
-       return;
-    file_close(diskdinfo->swaplog_fd);
-    debug(47, 3) ("Cache Dir #%d log closed on FD %d\n",
-       sd->index, diskdinfo->swaplog_fd);
-    diskdinfo->swaplog_fd = -1;
-    n_diskd_dirs--;
-    assert(n_diskd_dirs >= 0);
-    if (0 == n_diskd_dirs)
-       safe_free(diskd_dir_index);
-}
-
 static void
 storeDiskdDirInit(SwapDir * sd)
 {
-    static int started_clean_event = 0;
     int x;
     int i;
     int rfd;
@@ -368,10 +71,6 @@ storeDiskdDirInit(SwapDir * sd)
     char skey2[32];
     char skey3[32];
     diskdinfo_t *diskdinfo = sd->fsdata;
-    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.";
 
     ikey = (getpid() << 10) + (sd->index << 2);
     ikey &= 0x7fffffff;
@@ -424,16 +123,9 @@ storeDiskdDirInit(SwapDir * sd)
     fd_note(diskdinfo->wfd, "squid -> diskd");
     commSetTimeout(diskdinfo->wfd, -1, NULL, NULL);
     commSetNonBlocking(diskdinfo->wfd);
-    storeDiskdDirInitBitmap(sd);
-    if (storeDiskdDirVerifyCacheDirs(sd) < 0)
-       fatal(errmsg);
-    storeDiskdDirOpenSwapLog(sd);
-    storeDiskdDirRebuild(sd);
-    if (!started_clean_event) {
-       eventAdd("storeDirClean", storeDiskdDirCleanEvent, NULL, 15.0, 1);
-       started_clean_event = 1;
-    }
-    (void) storeDirGetBlkSize(sd->path, &sd->fs.blksize);
+    
+    commonUfsDirInit (sd);
+    
     comm_quick_poll_required();
 }
 
@@ -529,1006 +221,6 @@ storeDiskdDirCallback(SwapDir * SD)
     return retval;
 }
 
-
-
-static void
-storeDiskdDirRebuildFromDirectory(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) ("storeDiskdDirRebuildFromDirectory: DIR #%d\n", rb->sd->index);
-    for (count = 0; count < rb->speed; count++) {
-       assert(fd == -1);
-       fd = storeDiskdDirGetNextFile(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--;
-           storeDiskdDirCloseTmpSwapLog(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) ("storeDiskdDirRebuildFromDirectory: 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) ("storeDiskdDirRebuildFromDirectory: 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) ("storeDiskdDirRebuildFromDirectory: failed to get meta data\n");
-           /* XXX shouldn't this be a call to storeDiskdUnlink ? */
-           storeDiskdDirUnlinkFile(SD, filn);
-           continue;
-       }
-       debug(47, 3) ("storeDiskdDirRebuildFromDirectory: 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) ("storeDiskdDirRebuildFromDirectory: NULL key\n");
-           storeDiskdDirUnlinkFile(SD, filn);
-           continue;
-       }
-       tmpe.hash.key = key;
-       /* check sizes */
-       if (tmpe.swap_file_sz == 0) {
-           tmpe.swap_file_sz = sb.st_size;
-       } else if (tmpe.swap_file_sz == sb.st_size - swap_hdr_len) {
-           tmpe.swap_file_sz = sb.st_size;
-       } else if (tmpe.swap_file_sz != sb.st_size) {
-           debug(47, 1) ("storeDiskdDirRebuildFromDirectory: SIZE MISMATCH %ld!=%ld\n",
-               (long int) tmpe.swap_file_sz, (long int) sb.st_size);
-           storeDiskdDirUnlinkFile(SD, filn);
-           continue;
-       }
-       if (EBIT_TEST(tmpe.flags, KEY_PRIVATE)) {
-           storeDiskdDirUnlinkFile(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 = storeDiskdDirAddDiskRestore(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", storeDiskdDirRebuildFromDirectory, rb, 0.0, 1);
-}
-
-static void
-storeDiskdDirRebuildFromSwapLog(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--;
-           storeDiskdDirCloseTmpSwapLog(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) ("storeDiskdDirRebuildFromSwapLog: %s %s %08X\n",
-           swap_log_op_str[(int) s.op],
-           storeKeyText(s.key),
-           s.swap_filen);
-       if (s.op == SWAP_LOG_ADD) {
-           /*
-            * Here we have some special checks for large files.
-            * I've been seeing a system crash followed by a reboot
-            * that seems to corrupt the swap log.  Squid believes
-            * that the disk holds some really large files.  It
-            * complains about using being over the high water mark
-            * and proceeds to delete files as fast as it can.  To
-            * prevent that, we call stat() on sufficiently large
-            * files (>128KB) and reject those that are missing or
-            * have the wrong size.
-            */
-           struct stat sb;
-           char *p = storeDiskdDirFullPath(SD, s.swap_filen, NULL);
-           if (s.swap_file_sz < (1 << 17)) {
-               (void) 0;
-           } else if (stat(p, &sb) < 0) {
-               debug(47, 2) ("its missing!: %s\n", p);
-               continue;
-           } else if (sb.st_size != s.swap_file_sz) {
-               debug(47, 2) ("size mismatch!: stat=%d, log=%d\n",
-                   (int) sb.st_size, (int) s.swap_file_sz);
-               continue;
-           } else {
-               debug(47, 2) ("big file (%d bytes) checks out\n",
-                   (int) s.swap_file_sz);
-           }
-       } 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) {
-                   storeDiskdDirReplRemove(e);
-                   storeDiskdDirMapBitReset(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 (!storeDiskdDirValidFileno(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 = storeDiskdDirMapBitTest(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;
-               storeDiskdDirUnrefObj(SD, e);
-           } else {
-               debug_trap("storeDiskdDirRebuildFromSwapLog: 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) {
-               storeDiskdDirReplRemove(e);
-               /* Make sure we don't actually unlink the file */
-               storeDiskdDirMapBitReset(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 = storeDiskdDirAddDiskRestore(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", storeDiskdDirRebuildFromSwapLog, rb, 0.0, 1);
-}
-
-static int
-storeDiskdDirGetNextFile(RebuildState * rb, sfileno * filn_p, int *size)
-{
-    SwapDir *SD = rb->sd;
-    diskdinfo_t *diskdinfo = SD->fsdata;
-    int fd = -1;
-    int used = 0;
-    int dirs_opened = 0;
-    debug(47, 3) ("storeDiskdDirGetNextFile: 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(50, 1) ("storeDiskdDirGetNextFile: 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) ("storeDiskdDirGetNextFile: directory does not exist!.\n");
-               debug(47, 3) ("storeDiskdDirGetNextFile: 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) ("storeDiskdDirGetNextFile: invalid %s\n",
-                   rb->entry->d_name);
-               continue;
-           }
-           if (!storeDiskdFilenoBelongsHere(rb->fn, rb->sd->index, rb->curlvl1, rb->curlvl2)) {
-               debug(47, 3) ("storeDiskdDirGetNextFile: %08X does not belong in %d/%d/%d\n",
-                   rb->fn, rb->sd->index, rb->curlvl1, rb->curlvl2);
-               continue;
-           }
-           used = storeDiskdDirMapBitTest(SD, rb->fn);
-           if (used) {
-               debug(47, 3) ("storeDiskdDirGetNextFile: Locked, continuing with next.\n");
-               continue;
-           }
-           snprintf(rb->fullfilename, SQUID_MAXPATHLEN, "%s/%s",
-               rb->fullpath, rb->entry->d_name);
-           debug(47, 3) ("storeDiskdDirGetNextFile: Opening %s\n", rb->fullfilename);
-           fd = file_open(rb->fullfilename, O_RDONLY);
-           if (fd < 0)
-               debug(50, 1) ("storeDiskdDirGetNextFile: %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 < diskdinfo->l2)
-           continue;
-       rb->curlvl2 = 0;
-       if (++rb->curlvl1 < diskdinfo->l1)
-           continue;
-       rb->curlvl1 = 0;
-       rb->done = 1;
-    }
-    *filn_p = rb->fn;
-    return fd;
-}
-
-/* Add a new object to the cache with empty memory copy and pointer to disk
- * use to rebuild store from disk. */
-static StoreEntry *
-storeDiskdDirAddDiskRestore(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) ("storeDiskdAddDiskRestore: %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);
-    storeDiskdDirMapBitSet(SD, e->swap_filen);
-    storeHashInsert(e, key);   /* do it after we clear KEY_PRIVATE */
-    storeDiskdDirReplAdd(SD, e);
-    return e;
-}
-
-CBDATA_TYPE(RebuildState);
-
-static void
-storeDiskdDirRebuild(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 storeDiskdDirRebuildFromSwapLog(), otherwise we'll
-     * use storeDiskdDirRebuildFromDirectory() to open up each file
-     * and suck in the meta data.
-     */
-    fp = storeDiskdDirOpenTmpSwapLog(sd, &clean, &zero);
-    if (fp == NULL || zero) {
-       if (fp != NULL)
-           fclose(fp);
-       func = storeDiskdDirRebuildFromDirectory;
-    } else {
-       func = storeDiskdDirRebuildFromSwapLog;
-       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);
-}
-
-static void
-storeDiskdDirCloseTmpSwapLog(SwapDir * sd)
-{
-    diskdinfo_t *diskdinfo = sd->fsdata;
-    char *swaplog_path = xstrdup(storeDiskdDirSwapLogFile(sd, NULL));
-    char *new_path = xstrdup(storeDiskdDirSwapLogFile(sd, ".new"));
-    int fd;
-    file_close(diskdinfo->swaplog_fd);
-#ifdef _SQUID_OS2_
-    if (unlink(swaplog_path) < 0) {
-       debug(50, 0) ("%s: %s\n", swaplog_path, xstrerror());
-       fatal("storeDiskdDirCloseTmpSwapLog: unlink failed");
-    }
-#endif
-    if (xrename(new_path, swaplog_path) < 0) {
-       fatal("storeDiskdDirCloseTmpSwapLog: rename failed");
-    }
-    fd = file_open(swaplog_path, O_WRONLY | O_CREAT);
-    if (fd < 0) {
-       debug(50, 1) ("%s: %s\n", swaplog_path, xstrerror());
-       fatal("storeDiskdDirCloseTmpSwapLog: Failed to open swap log.");
-    }
-    safe_free(swaplog_path);
-    safe_free(new_path);
-    diskdinfo->swaplog_fd = fd;
-    debug(47, 3) ("Cache Dir #%d log opened on FD %d\n", sd->index, fd);
-}
-
-static FILE *
-storeDiskdDirOpenTmpSwapLog(SwapDir * sd, int *clean_flag, int *zero_flag)
-{
-    diskdinfo_t *diskdinfo = sd->fsdata;
-    char *swaplog_path = xstrdup(storeDiskdDirSwapLogFile(sd, NULL));
-    char *clean_path = xstrdup(storeDiskdDirSwapLogFile(sd, ".last-clean"));
-    char *new_path = xstrdup(storeDiskdDirSwapLogFile(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 (diskdinfo->swaplog_fd >= 0)
-       file_close(diskdinfo->swaplog_fd);
-    /* open a write-only FD for the new log */
-    fd = file_open(new_path, O_WRONLY | O_CREAT | O_TRUNC);
-    if (fd < 0) {
-       debug(50, 1) ("%s: %s\n", new_path, xstrerror());
-       fatal("storeDirOpenTmpSwapLog: Failed to open swap log.");
-    }
-    diskdinfo->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 DISKD 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
-storeDiskdDirWriteCleanStart(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(storeDiskdDirSwapLogFile(sd, ".clean"));
-    state->cur = xstrdup(storeDiskdDirSwapLogFile(sd, NULL));
-    state->cln = xstrdup(storeDiskdDirSwapLogFile(sd, ".last-clean"));
-    state->outbuf = xcalloc(CLEAN_BUF_SZ, 1);
-    state->outbuf_offset = 0;
-    state->walker = sd->repl->WalkInit(sd->repl);
-    unlink(state->cln);
-    state->fd = file_open(state->new, O_WRONLY | O_CREAT | O_TRUNC);
-    if (state->fd < 0) {
-       xfree(state->new);
-       xfree(state->cur);
-       xfree(state->cln);
-       xfree(state);
-       return -1;
-    }
-    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 = storeDiskdDirWriteCleanEntry;
-    sd->log.clean.state = state;
-    return 0;
-}
-
-/*
- * Get the next entry that is a candidate for clean log writing
- */
-const StoreEntry *
-storeDiskdDirCleanLogNextEntry(SwapDir * sd)
-{
-    const StoreEntry *entry = NULL;
-    struct _clean_state *state = sd->log.clean.state;
-    if (state->walker)
-       entry = state->walker->Next(state->walker);
-    return entry;
-}
-
-/*
- * "write" an entry to the clean log file.
- */
-static void
-storeDiskdDirWriteCleanEntry(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(47, 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;
-    }
-}
-
-static void
-storeDiskdDirWriteCleanDone(SwapDir * sd)
-{
-    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(47, 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.
-     */
-    storeDiskdDirCloseSwapLog(sd);
-    /* rename */
-    if (state->fd >= 0) {
-#ifdef _SQUID_OS2_
-       file_close(state->fd);
-       state->fd = -1;
-       if (unlink(cur) < 0)
-           debug(50, 0) ("storeDirWriteCleanLogs: unlinkd failed: %s, %s\n",
-               xstrerror(), 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 (state->fd < 0)
-       (void) 0;
-    else
-       file_close(file_open(state->cln, O_WRONLY | O_CREAT | O_TRUNC));
-    /* 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
-storeDiskdDirSwapLog(const SwapDir * sd, const StoreEntry * e, int op)
-{
-    diskdinfo_t *diskdinfo = 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(diskdinfo->swaplog_fd,
-       -1,
-       s,
-       sizeof(storeSwapLogData),
-       NULL,
-       NULL,
-       (FREE *) storeSwapLogDataFree);
-}
-
-static void
-storeDiskdDirNewfs(SwapDir * sd)
-{
-    debug(47, 3) ("Creating swap space in %s\n", sd->path);
-    storeDiskdDirCreateDirectory(sd->path, 0);
-    storeDiskdDirCreateSwapSubDirs(sd);
-}
-
-static int
-rev_int_sort(const void *A, const void *B)
-{
-    const int *i1 = A;
-    const int *i2 = B;
-    return *i2 - *i1;
-}
-
-static int
-storeDiskdDirClean(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;
-    diskdinfo_t *diskdinfo;
-    N0 = n_diskd_dirs;
-    D0 = diskd_dir_index[swap_index % N0];
-    SD = &Config.cacheSwap.swapDirs[D0];
-    diskdinfo = SD->fsdata;
-    N1 = diskdinfo->l1;
-    D1 = (swap_index / N0) % N1;
-    N2 = diskdinfo->l2;
-    D2 = ((swap_index / N0) / N1) % N2;
-    snprintf(p1, SQUID_MAXPATHLEN, "%s/%02X/%02X",
-       Config.cacheSwap.swapDirs[D0].path, D1, D2);
-    debug(36, 3) ("storeDirClean: Cleaning directory %s\n", p1);
-    dp = opendir(p1);
-    if (dp == NULL) {
-       if (errno == ENOENT) {
-           debug(36, 0) ("storeDirClean: WARNING: Creating %s\n", p1);
-           if (mkdir(p1, 0777) == 0)
-               return 0;
-       }
-       debug(50, 0) ("storeDirClean: %s: %s\n", p1, xstrerror());
-       safeunlink(p1, 1);
-       return 0;
-    }
-    while ((de = readdir(dp)) != NULL && k < 20) {
-       if (sscanf(de->d_name, "%X", &swapfileno) != 1)
-           continue;
-       fn = swapfileno;        /* XXX should remove this cruft ! */
-       if (storeDiskdDirValidFileno(SD, fn, 1))
-           if (storeDiskdDirMapBitTest(SD, fn))
-               if (storeDiskdFilenoBelongsHere(fn, D0, D1, D2))
-                   continue;
-#if USE_TRUNCATE
-       if (!stat(de->d_name, &sb))
-           if (sb.st_size == 0)
-               continue;
-#endif
-       files[k++] = swapfileno;
-    }
-    closedir(dp);
-    if (k == 0)
-       return 0;
-    qsort(files, k, sizeof(int), rev_int_sort);
-    if (k > 10)
-       k = 10;
-    for (n = 0; n < k; n++) {
-       debug(36, 3) ("storeDirClean: Cleaning file %08X\n", files[n]);
-       snprintf(p2, MAXPATHLEN + 1, "%s/%08X", p1, files[n]);
-#if USE_TRUNCATE
-       truncate(p2, 0);
-#else
-       safeunlink(p2, 0);
-#endif
-       statCounter.swap.files_cleaned++;
-    }
-    debug(36, 3) ("Cleaned %d unused files from %s\n", k, p1);
-    return k;
-}
-
-static void
-storeDiskdDirCleanEvent(void *unused)
-{
-    static int swap_index = 0;
-    int i;
-    int j = 0;
-    int n = 0;
-    /*
-     * Assert that there are DISKD cache_dirs configured, otherwise
-     * we should never be called.
-     */
-    assert(n_diskd_dirs);
-    if (NULL == diskd_dir_index) {
-       SwapDir *sd;
-       diskdinfo_t *diskdinfo;
-       /*
-        * Initialize the little array that translates DISKD cache_dir
-        * number into the Config.cacheSwap.swapDirs array index.
-        */
-       diskd_dir_index = xcalloc(n_diskd_dirs, sizeof(*diskd_dir_index));
-       for (i = 0, n = 0; i < Config.cacheSwap.n_configured; i++) {
-           sd = &Config.cacheSwap.swapDirs[i];
-           if (!storeDiskdDirIs(sd))
-               continue;
-           diskd_dir_index[n++] = i;
-           diskdinfo = sd->fsdata;
-           j += (diskdinfo->l1 * diskdinfo->l2);
-       }
-       assert(n == n_diskd_dirs);
-       /*
-        * Start the storeDiskdDirClean() swap_index with a random
-        * value.  j equals the total number of DISKD level 2
-        * swap directories
-        */
-       swap_index = (int) (squid_random() % j);
-    }
-    if (0 == store_dirs_rebuilding) {
-       n = storeDiskdDirClean(swap_index);
-       swap_index++;
-    }
-    eventAdd("storeDirClean", storeDiskdDirCleanEvent, NULL,
-       15.0 * exp(-0.25 * n), 1);
-}
-
-static int
-storeDiskdDirIs(SwapDir * sd)
-{
-    if (strncmp(sd->type, "diskd", 3) == 0)
-       return 1;
-    return 0;
-}
-
-/*
- * Does swapfile number 'fn' belong in cachedir #F0,
- * level1 dir #F1, level2 dir #F2?
- */
-static int
-storeDiskdFilenoBelongsHere(int fn, int F0, int F1, int F2)
-{
-    int D1, D2;
-    int L1, L2;
-    int filn = fn;
-    diskdinfo_t *diskdinfo;
-    assert(F0 < Config.cacheSwap.n_configured);
-    diskdinfo = Config.cacheSwap.swapDirs[F0].fsdata;
-    L1 = diskdinfo->l1;
-    L2 = diskdinfo->l2;
-    D1 = ((filn / L2) / L2) % L1;
-    if (F1 != D1)
-       return 0;
-    D2 = (filn / L2) % L2;
-    if (F2 != D2)
-       return 0;
-    return 1;
-}
-
-int
-storeDiskdDirValidFileno(SwapDir * SD, sfileno filn, int flag)
-{
-    diskdinfo_t *diskdinfo = 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 > diskdinfo->map->max_n_files)
-           return 0;
-    return 1;
-}
-
-void
-storeDiskdDirMaintain(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)) ("storeDiskdDirMaintain: %s removed %d/%d f=%.03f max_scan=%d\n",
-       SD->path, removed, max_remove, f, max_scan);
-}
-
 /*
  * storeDiskdDirCheckObj
  *
@@ -1548,84 +240,19 @@ storeDiskdDirCheckObj(SwapDir * SD, const StoreEntry * e)
     return diskdinfo->away * 1000 / diskdinfo->magic2;
 }
 
-/*
- * storeDiskdDirRefObj
- *
- * This routine is called whenever an object is referenced, so we can
- * maintain replacement information within the storage fs.
- */
-void
-storeDiskdDirRefObj(SwapDir * SD, StoreEntry * e)
-{
-    debug(47, 3) ("storeDiskdDirRefObj: referencing %p %d/%d\n", e, e->swap_dirn,
-       e->swap_filen);
-    if (SD->repl->Referenced)
-       SD->repl->Referenced(SD->repl, e, &e->repl);
-}
-
-/*
- * storeDiskdDirUnrefObj
- * This routine is called whenever the last reference to an object is
- * removed, to maintain replacement information within the storage fs.
- */
-void
-storeDiskdDirUnrefObj(SwapDir * SD, StoreEntry * e)
-{
-    debug(47, 3) ("storeDiskdDirUnrefObj: referencing %p %d/%d\n", e,
-       e->swap_dirn, e->swap_filen);
-    if (SD->repl->Dereferenced)
-       SD->repl->Dereferenced(SD->repl, e, &e->repl);
-}
-
-/*
- * storeDiskdDirUnlinkFile
- *
- * This is a *synchronous* unlink which is currently used in the rebuild
- * process. This is bad, but it'll have to stay until the dir rebuild
- * uses storeDiskdUnlink() ..
- */
 void
-storeDiskdDirUnlinkFile(SwapDir * SD, sfileno f)
+storeDiskdDirIOUnlinkFile(char *path)
 {
-    debug(47, 3) ("storeDiskdDirUnlinkFile: unlinking fileno %08X\n", f);
-    /* storeDiskdDirMapBitReset(SD, f); */
 #if USE_UNLINKD
-    unlinkdUnlink(storeDiskdDirFullPath(SD, f, NULL));
+    unlinkdUnlink(path);
 #elif USE_TRUNCATE
-    truncate(storeDiskdDirFullPath(SD, f, NULL), 0);
+    truncate(path, 0);
 #else
-    unlink(storeDiskdDirFullPath(SD, f, NULL));
+    unlink(path);
 #endif
+               
 }
 
-/*
- * Add and remove the given StoreEntry from the replacement policy in
- * use.
- */
-
-void
-storeDiskdDirReplAdd(SwapDir * SD, StoreEntry * e)
-{
-    debug(47, 4) ("storeDiskdDirReplAdd: added node %p to dir %d\n", e,
-       SD->index);
-    SD->repl->Add(SD->repl, e, &e->repl);
-}
-
-
-void
-storeDiskdDirReplRemove(StoreEntry * e)
-{
-    SwapDir *SD;
-    if (e->swap_dirn < 0)
-       return;
-    SD = INDEXSD(e->swap_dirn);
-    debug(47, 4) ("storeDiskdDirReplRemove: remove node %p from dir %d\n", e,
-       SD->index);
-    SD->repl->Remove(SD->repl, e, &e->repl);
-}
-
-
-
 /*
  * SHM manipulation routines
  */
@@ -1676,37 +303,7 @@ void
 storeDiskdDirStats(SwapDir * SD, StoreEntry * sentry)
 {
     diskdinfo_t *diskdinfo = 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", diskdinfo->l1);
-    storeAppendPrintf(sentry, "Second level subdirectories: %d\n", diskdinfo->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",
-       diskdinfo->map->n_files_in_map, diskdinfo->map->max_n_files,
-       percent(diskdinfo->map->n_files_in_map, diskdinfo->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");
+    commonUfsDirStats (SD, sentry);
     storeAppendPrintf(sentry, "Pending operations: %d\n", diskdinfo->away);
 }
 
@@ -1819,79 +416,10 @@ storeDiskdDirReconfigure(SwapDir * sd, int index, char *path)
 void
 storeDiskdDirDump(StoreEntry * entry, SwapDir * s)
 {
-    diskdinfo_t *diskdinfo = s->fsdata;
-    storeAppendPrintf(entry, " %d %d %d",
-       s->max_size >> 10,
-       diskdinfo->l1,
-       diskdinfo->l2);
+    commonUfsDirDump (entry, s);
     dump_cachedir_options(entry, options, s);
 }
 
-/*
- * Only "free" the filesystem specific stuff here
- */
-static void
-storeDiskdDirFree(SwapDir * s)
-{
-    diskdinfo_t *diskdinfo = s->fsdata;
-    if (diskdinfo->swaplog_fd > -1) {
-       file_close(diskdinfo->swaplog_fd);
-       diskdinfo->swaplog_fd = -1;
-    }
-    filemapFreeMemory(diskdinfo->map);
-    xfree(diskdinfo);
-    s->fsdata = NULL;          /* Will aid debugging... */
-
-}
-
-char *
-storeDiskdDirFullPath(SwapDir * SD, sfileno filn, char *fullpath)
-{
-    LOCAL_ARRAY(char, fullfilename, SQUID_MAXPATHLEN);
-    diskdinfo_t *diskdinfo = SD->fsdata;
-    int L1 = diskdinfo->l1;
-    int L2 = diskdinfo->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;
-}
-
-/*
- * storeDiskdCleanupDoubleCheck
- *
- * This is called by storeCleanup() if -S was given on the command line.
- */
-static int
-storeDiskdCleanupDoubleCheck(SwapDir * sd, StoreEntry * e)
-{
-    struct stat sb;
-    if (stat(storeDiskdDirFullPath(sd, e->swap_filen, NULL), &sb) < 0) {
-       debug(47, 0) ("storeDiskdCleanupDoubleCheck: MISSING SWAP FILE\n");
-       debug(47, 0) ("storeDiskdCleanupDoubleCheck: FILENO %08X\n", e->swap_filen);
-       debug(47, 0) ("storeDiskdCleanupDoubleCheck: PATH %s\n",
-           storeDiskdDirFullPath(sd, e->swap_filen, NULL));
-       storeEntryDump(e, 0);
-       return -1;
-    }
-    if (e->swap_file_sz != sb.st_size) {
-       debug(47, 0) ("storeDiskdCleanupDoubleCheck: SIZE MISMATCH\n");
-       debug(47, 0) ("storeDiskdCleanupDoubleCheck: FILENO %08X\n", e->swap_filen);
-       debug(47, 0) ("storeDiskdCleanupDoubleCheck: PATH %s\n",
-           storeDiskdDirFullPath(sd, e->swap_filen, NULL));
-       debug(47, 0) ("storeDiskdCleanupDoubleCheck: 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;
-}
-
 /*
  * storeDiskdDirParse
  *
@@ -1923,23 +451,24 @@ storeDiskdDirParse(SwapDir * sd, int index, char *path)
     sd->index = index;
     sd->path = xstrdup(path);
     sd->max_size = size;
-    diskdinfo->l1 = l1;
-    diskdinfo->l2 = l2;
-    diskdinfo->swaplog_fd = -1;
-    diskdinfo->map = NULL;     /* Debugging purposes */
-    diskdinfo->suggest = 0;
+    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 = storeDiskdDirNewfs;
+    sd->newfs = commonUfsDirNewfs;
     sd->dump = storeDiskdDirDump;
-    sd->freefs = storeDiskdDirFree;
-    sd->dblcheck = storeDiskdCleanupDoubleCheck;
+    sd->freefs = commonUfsDirFree;
+    sd->dblcheck = commonUfsCleanupDoubleCheck;
     sd->statfs = storeDiskdDirStats;
-    sd->maintainfs = storeDiskdDirMaintain;
+    sd->maintainfs = commonUfsDirMaintain;
     sd->checkobj = storeDiskdDirCheckObj;
-    sd->refobj = storeDiskdDirRefObj;
-    sd->unrefobj = storeDiskdDirUnrefObj;
+    sd->refobj = commonUfsDirRefObj;
+    sd->unrefobj = commonUfsDirUnrefObj;
     sd->callback = storeDiskdDirCallback;
     sd->sync = storeDiskdDirSync;
     sd->obj.create = storeDiskdCreate;
@@ -1948,12 +477,12 @@ storeDiskdDirParse(SwapDir * sd, int index, char *path)
     sd->obj.read = storeDiskdRead;
     sd->obj.write = storeDiskdWrite;
     sd->obj.unlink = storeDiskdUnlink;
-    sd->log.open = storeDiskdDirOpenSwapLog;
-    sd->log.close = storeDiskdDirCloseSwapLog;
-    sd->log.write = storeDiskdDirSwapLog;
-    sd->log.clean.start = storeDiskdDirWriteCleanStart;
-    sd->log.clean.nextentry = storeDiskdDirCleanLogNextEntry;
-    sd->log.clean.done = storeDiskdDirWriteCleanDone;
+    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);
 
index 1a955b76ef640077b971864944a6836a7d11119c..8833fabc053eec94a9b918d8de65c9775283c726 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef __STORE_DISKD_H__
 #define __STORE_DISKD_H__
 
+#include "ufscommon.h"
+
 /*
  * magic2 is the point at which we start blocking on msgsnd/msgrcv.
  * If a queue has magic2 (or more) messages away, then we read the
  */
 
 struct _diskdinfo_t {
-    int swaplog_fd;
-    int l1;
-    int l2;
-    fileMap *map;
-    int suggest;
+    /* MUST BE FIRST */
+    squidufsinfo_t commondata;
     int smsgid;
     int rmsgid;
     int wfd;
@@ -90,12 +89,6 @@ static const int msg_snd_rcv_sz = sizeof(diomsg) - sizeof(mtyp_t);
 /* The diskd_state memory pool */
 extern MemPool *diskd_state_pool;
 
-extern void storeDiskdDirMapBitReset(SwapDir *, sfileno);
-extern int storeDiskdDirMapBitAllocate(SwapDir *);
-extern char *storeDiskdDirFullPath(SwapDir * SD, sfileno filn, char *fullpath);
-extern void storeDiskdDirUnlinkFile(SwapDir *, sfileno);
-extern void storeDiskdDirReplAdd(SwapDir *, StoreEntry *);
-extern void storeDiskdDirReplRemove(StoreEntry *);
 extern void storeDiskdShmPut(SwapDir *, off_t);
 extern void *storeDiskdShmGet(SwapDir *, off_t *);
 extern void storeDiskdHandle(diomsg * M);
index fe1a48d47b25f3c70751633a8751b53326e00ae0..f5cdb4e0b6790c37f20e37a9cdeaef8d0a56a055 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * $Id: store_io_diskd.cc,v 1.27 2002/08/08 20:12:46 hno Exp $
+ * $Id: store_io_diskd.cc,v 1.28 2002/10/12 09:45:57 robertc Exp $
  *
  * DEBUG: section 79    Squid-side DISKD I/O functions.
  * AUTHOR: Duane Wessels
@@ -87,7 +87,7 @@ storeDiskdOpen(SwapDir * SD, StoreEntry * e, STFNCB * file_callback,
     diskdstate->id = diskd_stats.sio_id++;
 
     buf = storeDiskdShmGet(SD, &shm_offset);
-    xstrncpy(buf, storeDiskdDirFullPath(SD, f, NULL), SHMBUF_BLKSZ);
+    xstrncpy(buf, commonUfsDirFullPath(SD, f, NULL), SHMBUF_BLKSZ);
     x = storeDiskdSend(_MQD_OPEN,
        SD,
        diskdstate->id,
@@ -125,7 +125,7 @@ storeDiskdCreate(SwapDir * SD, StoreEntry * e, STFNCB * file_callback,
        return NULL;
     }
     /* Allocate a number */
-    f = storeDiskdDirMapBitAllocate(SD);
+    f = commonUfsDirMapBitAllocate(SD);
     debug(79, 3) ("storeDiskdCreate: fileno %08X\n", f);
 
     CBDATA_INIT_TYPE_FREECB(storeIOState, storeDiskdIOFreeEntry);
@@ -145,7 +145,7 @@ storeDiskdCreate(SwapDir * SD, StoreEntry * e, STFNCB * file_callback,
     diskdstate->id = diskd_stats.sio_id++;
 
     buf = storeDiskdShmGet(SD, &shm_offset);
-    xstrncpy(buf, storeDiskdDirFullPath(SD, f, NULL), SHMBUF_BLKSZ);
+    xstrncpy(buf, commonUfsDirFullPath(SD, f, NULL), SHMBUF_BLKSZ);
     x = storeDiskdSend(_MQD_OPEN,
        SD,
        diskdstate->id,
@@ -160,7 +160,7 @@ storeDiskdCreate(SwapDir * SD, StoreEntry * e, STFNCB * file_callback,
        cbdataFree(sio);
        return NULL;
     }
-    storeDiskdDirReplAdd(SD, e);
+    commonUfsDirReplAdd(SD, e);
     diskd_stats.create.ops++;
     return sio;
 }
@@ -270,17 +270,17 @@ storeDiskdUnlink(SwapDir * SD, StoreEntry * e)
 
     debug(79, 3) ("storeDiskdUnlink: dirno %d, fileno %08X\n", SD->index,
        e->swap_filen);
-    storeDiskdDirReplRemove(e);
-    storeDiskdDirMapBitReset(SD, 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");
-       storeDiskdDirUnlinkFile(SD, e->swap_filen);
+       commonUfsDirUnlinkFile(SD, e->swap_filen);
        return;
     }
     /* We can attempt a diskd unlink */
     buf = storeDiskdShmGet(SD, &shm_offset);
-    xstrncpy(buf, storeDiskdDirFullPath(SD, e->swap_filen, NULL), SHMBUF_BLKSZ);
+    xstrncpy(buf, commonUfsDirFullPath(SD, e->swap_filen, NULL), SHMBUF_BLKSZ);
     x = storeDiskdSend(_MQD_UNLINK,
        SD,
        e->swap_filen,
index b1ef74eb525ea13ed7650feceb47d039fd1c75eb..2b67ae437344644fc5996b5c61e6b5472f4020c6 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * $Id: store_dir_ufs.cc,v 1.49 2002/09/01 15:16:36 hno Exp $
+ * $Id: store_dir_ufs.cc,v 1.50 2002/10/12 09:45:58 robertc Exp $
  *
  * DEBUG: section 47    Store Directory Routines
  * AUTHOR: Duane Wessels
 #include "squid.h"
 
 #include "store_ufs.h"
+#include "ufscommon.h"
 
-#define DefaultLevelOneDirs     16
-#define DefaultLevelTwoDirs     256
-#define STORE_META_BUFSZ 4096
-
-typedef struct _RebuildState RebuildState;
-struct _RebuildState {
-    SwapDir *sd;
-    int n_read;
-    FILE *log;
-    int speed;
-    int curlvl1;
-    int curlvl2;
-    struct {
-       unsigned int need_to_validate:1;
-       unsigned int clean:1;
-       unsigned int init:1;
-    } flags;
-    int done;
-    int in_dir;
-    int fn;
-    struct dirent *entry;
-    DIR *td;
-    char fullpath[SQUID_MAXPATHLEN];
-    char fullfilename[SQUID_MAXPATHLEN];
-    struct _store_rebuild_data counts;
-};
-
-static int n_ufs_dirs = 0;
-static int *ufs_dir_index = NULL;
 MemPool *ufs_state_pool = NULL;
 static int ufs_initialised = 0;
 
-static char *storeUfsDirSwapSubDir(SwapDir *, int subdirn);
-static int storeUfsDirCreateDirectory(const char *path, int);
-static int storeUfsDirVerifyCacheDirs(SwapDir *);
-static int storeUfsDirVerifyDirectory(const char *path);
-static void storeUfsDirCreateSwapSubDirs(SwapDir *);
-static char *storeUfsDirSwapLogFile(SwapDir *, const char *);
-static EVH storeUfsDirRebuildFromDirectory;
-static EVH storeUfsDirRebuildFromSwapLog;
-static int storeUfsDirGetNextFile(RebuildState *, sfileno *, int *size);
-static StoreEntry *storeUfsDirAddDiskRestore(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 storeUfsDirRebuild(SwapDir * sd);
-static void storeUfsDirCloseTmpSwapLog(SwapDir * sd);
-static FILE *storeUfsDirOpenTmpSwapLog(SwapDir *, int *, int *);
-static STLOGOPEN storeUfsDirOpenSwapLog;
-static STINIT storeUfsDirInit;
-static STFREE storeUfsDirFree;
-static STLOGCLEANSTART storeUfsDirWriteCleanStart;
-static STLOGCLEANNEXTENTRY storeUfsDirCleanLogNextEntry;
-static STLOGCLEANWRITE storeUfsDirWriteCleanEntry;
-static STLOGCLEANDONE storeUfsDirWriteCleanDone;
-static STLOGCLOSE storeUfsDirCloseSwapLog;
-static STLOGWRITE storeUfsDirSwapLog;
-static STNEWFS storeUfsDirNewfs;
 static STDUMP storeUfsDirDump;
-static STMAINTAINFS storeUfsDirMaintain;
 static STCHECKOBJ storeUfsDirCheckObj;
-static STREFOBJ storeUfsDirRefObj;
-static STUNREFOBJ storeUfsDirUnrefObj;
-static QS rev_int_sort;
-static int storeUfsDirClean(int swap_index);
-static EVH storeUfsDirCleanEvent;
-static int storeUfsDirIs(SwapDir * sd);
-static int storeUfsFilenoBelongsHere(int fn, int F0, int F1, int F2);
-static int storeUfsCleanupDoubleCheck(SwapDir *, StoreEntry *);
-static void storeUfsDirStats(SwapDir *, StoreEntry *);
-static void storeUfsDirInitBitmap(SwapDir *);
-static int storeUfsDirValidFileno(SwapDir *, sfileno, int);
+static void storeUfsDirIOUnlinkFile(char *path);
 
 STSETUP storeFsSetup_ufs;
 
-/*
- * These functions were ripped straight out of the heart of store_dir.c.
- * They assume that the given filenum is on a ufs partiton, which may or
- * may not be true.. 
- * XXX this evilness should be tidied up at a later date!
- */
-
-static int
-storeUfsDirMapBitTest(SwapDir * SD, sfileno filn)
-{
-    ufsinfo_t *ufsinfo;
-    ufsinfo = (ufsinfo_t *) SD->fsdata;
-    return file_map_bit_test(ufsinfo->map, filn);
-}
-
-static void
-storeUfsDirMapBitSet(SwapDir * SD, int fn)
-{
-    sfileno filn = fn;
-    ufsinfo_t *ufsinfo;
-    ufsinfo = (ufsinfo_t *) SD->fsdata;
-    file_map_bit_set(ufsinfo->map, filn);
-}
-
-void
-storeUfsDirMapBitReset(SwapDir * SD, int fn)
-{
-    sfileno filn = fn;
-    ufsinfo_t *ufsinfo;
-    ufsinfo = (ufsinfo_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(ufsinfo->map, filn))
-       file_map_bit_reset(ufsinfo->map, filn);
-}
-
-int
-storeUfsDirMapBitAllocate(SwapDir * SD)
-{
-    ufsinfo_t *ufsinfo = (ufsinfo_t *) SD->fsdata;
-    int fn;
-    fn = file_map_allocate(ufsinfo->map, ufsinfo->suggest);
-    file_map_bit_set(ufsinfo->map, fn);
-    ufsinfo->suggest = fn + 1;
-    return fn;
-}
-
-/*
- * Initialise the ufs bitmap
- *
- * If there already is a bitmap, and the numobjects is larger than currently
- * configured, we allocate a new bitmap and 'grow' the old one into it.
- */
-static void
-storeUfsDirInitBitmap(SwapDir * sd)
-{
-    ufsinfo_t *ufsinfo = (ufsinfo_t *) sd->fsdata;
-
-    if (ufsinfo->map == NULL) {
-       /* First time */
-       ufsinfo->map = file_map_create();
-    } else if (ufsinfo->map->max_n_files) {
-       /* it grew, need to expand */
-       /* XXX We don't need it anymore .. */
-    }
-    /* else it shrunk, and we leave the old one in place */
-}
-
-static char *
-storeUfsDirSwapSubDir(SwapDir * sd, int subdirn)
-{
-    ufsinfo_t *ufsinfo = (ufsinfo_t *) sd->fsdata;
-
-    LOCAL_ARRAY(char, fullfilename, SQUID_MAXPATHLEN);
-    assert(0 <= subdirn && subdirn < ufsinfo->l1);
-    snprintf(fullfilename, SQUID_MAXPATHLEN, "%s/%02X", sd->path, subdirn);
-    return fullfilename;
-}
-
-static int
-storeUfsDirCreateDirectory(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);
-       }
-    } else if (0 == mkdir(path, 0755)) {
-       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;
-}
-
-static int
-storeUfsDirVerifyDirectory(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 storeUfsDirInit().  If this returns < 0,
- * then Squid exits, complains about swap directories not
- * existing, and instructs the admin to run 'squid -z'
- */
-static int
-storeUfsDirVerifyCacheDirs(SwapDir * sd)
-{
-    ufsinfo_t *ufsinfo = (ufsinfo_t *) sd->fsdata;
-    int j;
-    const char *path = sd->path;
-
-    if (storeUfsDirVerifyDirectory(path) < 0)
-       return -1;
-    for (j = 0; j < ufsinfo->l1; j++) {
-       path = storeUfsDirSwapSubDir(sd, j);
-       if (storeUfsDirVerifyDirectory(path) < 0)
-           return -1;
-    }
-    return 0;
-}
-
-static void
-storeUfsDirCreateSwapSubDirs(SwapDir * sd)
-{
-    ufsinfo_t *ufsinfo = (ufsinfo_t *) sd->fsdata;
-    int i, k;
-    int should_exist;
-    LOCAL_ARRAY(char, name, MAXPATHLEN);
-    for (i = 0; i < ufsinfo->l1; i++) {
-       snprintf(name, MAXPATHLEN, "%s/%02X", sd->path, i);
-       if (storeUfsDirCreateDirectory(name, 0))
-           should_exist = 0;
-       else
-           should_exist = 1;
-       debug(47, 1) ("Making directories in %s\n", name);
-       for (k = 0; k < ufsinfo->l2; k++) {
-           snprintf(name, MAXPATHLEN, "%s/%02X/%02X", sd->path, i, k);
-           storeUfsDirCreateDirectory(name, should_exist);
-       }
-    }
-}
-
-static char *
-storeUfsDirSwapLogFile(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
-storeUfsDirOpenSwapLog(SwapDir * sd)
-{
-    ufsinfo_t *ufsinfo = (ufsinfo_t *) sd->fsdata;
-    char *path;
-    int fd;
-    path = storeUfsDirSwapLogFile(sd, NULL);
-    fd = file_open(path, O_WRONLY | O_CREAT | O_BINARY);
-    if (fd < 0) {
-       debug(50, 1) ("%s: %s\n", path, xstrerror());
-       fatal("storeUfsDirOpenSwapLog: Failed to open swap log.");
-    }
-    debug(50, 3) ("Cache Dir #%d log opened on FD %d\n", sd->index, fd);
-    ufsinfo->swaplog_fd = fd;
-    if (0 == n_ufs_dirs)
-       assert(NULL == ufs_dir_index);
-    n_ufs_dirs++;
-    assert(n_ufs_dirs <= Config.cacheSwap.n_configured);
-}
-
-static void
-storeUfsDirCloseSwapLog(SwapDir * sd)
-{
-    ufsinfo_t *ufsinfo = (ufsinfo_t *) sd->fsdata;
-    if (ufsinfo->swaplog_fd < 0)       /* not open */
-       return;
-    file_close(ufsinfo->swaplog_fd);
-    debug(47, 3) ("Cache Dir #%d log closed on FD %d\n",
-       sd->index, ufsinfo->swaplog_fd);
-    ufsinfo->swaplog_fd = -1;
-    n_ufs_dirs--;
-    assert(n_ufs_dirs >= 0);
-    if (0 == n_ufs_dirs)
-       safe_free(ufs_dir_index);
-}
-
-static void
-storeUfsDirInit(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.";
-    storeUfsDirInitBitmap(sd);
-    if (storeUfsDirVerifyCacheDirs(sd) < 0)
-       fatal(errmsg);
-    storeUfsDirOpenSwapLog(sd);
-    storeUfsDirRebuild(sd);
-    if (!started_clean_event) {
-       eventAdd("storeDirClean", storeUfsDirCleanEvent, NULL, 15.0, 1);
-       started_clean_event = 1;
-    }
-    (void) storeDirGetBlkSize(sd->path, &sd->fs.blksize);
-}
-
-static void
-storeUfsDirRebuildFromDirectory(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) ("storeUfsDirRebuildFromDirectory: DIR #%d\n", rb->sd->index);
-    for (count = 0; count < rb->speed; count++) {
-       assert(fd == -1);
-       fd = storeUfsDirGetNextFile(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--;
-           storeUfsDirCloseTmpSwapLog(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) ("storeUfsDirRebuildFromDirectory: 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) ("storeUfsDirRebuildFromDirectory: 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) ("storeUfsDirRebuildFromDirectory: failed to get meta data\n");
-           /* XXX shouldn't this be a call to storeUfsUnlink ? */
-           storeUfsDirUnlinkFile(SD, filn);
-           continue;
-       }
-       debug(47, 3) ("storeUfsDirRebuildFromDirectory: 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) ("storeUfsDirRebuildFromDirectory: NULL key\n");
-           storeUfsDirUnlinkFile(SD, filn);
-           continue;
-       }
-       tmpe.hash.key = key;
-       /* check sizes */
-       if (tmpe.swap_file_sz == 0) {
-           tmpe.swap_file_sz = sb.st_size;
-       } else if (tmpe.swap_file_sz == sb.st_size - swap_hdr_len) {
-           tmpe.swap_file_sz = sb.st_size;
-       } else if (tmpe.swap_file_sz != sb.st_size) {
-           debug(47, 1) ("storeUfsDirRebuildFromDirectory: SIZE MISMATCH %ld!=%ld\n",
-               (long int) tmpe.swap_file_sz, (long int) sb.st_size);
-           storeUfsDirUnlinkFile(SD, filn);
-           continue;
-       }
-       if (EBIT_TEST(tmpe.flags, KEY_PRIVATE)) {
-           storeUfsDirUnlinkFile(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 = storeUfsDirAddDiskRestore(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", storeUfsDirRebuildFromDirectory, rb, 0.0, 1);
-}
-
-static void
-storeUfsDirRebuildFromSwapLog(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--;
-           storeUfsDirCloseTmpSwapLog(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) ("storeUfsDirRebuildFromSwapLog: %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) {
-                   storeUfsDirReplRemove(e);
-                   storeUfsDirMapBitReset(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 (!storeUfsDirValidFileno(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 = storeUfsDirMapBitTest(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;
-               storeUfsDirUnrefObj(SD, e);
-           } else {
-               debug_trap("storeUfsDirRebuildFromSwapLog: 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) {
-               storeUfsDirReplRemove(e);
-               /* Make sure we don't actually unlink the file */
-               storeUfsDirMapBitReset(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 = storeUfsDirAddDiskRestore(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", storeUfsDirRebuildFromSwapLog, rb, 0.0, 1);
-}
-
-static int
-storeUfsDirGetNextFile(RebuildState * rb, sfileno * filn_p, int *size)
-{
-    SwapDir *SD = rb->sd;
-    ufsinfo_t *ufsinfo = (ufsinfo_t *) SD->fsdata;
-    int fd = -1;
-    int used = 0;
-    int dirs_opened = 0;
-    debug(47, 3) ("storeUfsDirGetNextFile: 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) ("storeUfsDirGetNextFile: 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) ("storeUfsDirGetNextFile: directory does not exist!.\n");
-               debug(47, 3) ("storeUfsDirGetNextFile: 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) ("storeUfsDirGetNextFile: invalid %s\n",
-                   rb->entry->d_name);
-               continue;
-           }
-           if (!storeUfsFilenoBelongsHere(rb->fn, rb->sd->index, rb->curlvl1, rb->curlvl2)) {
-               debug(47, 3) ("storeUfsDirGetNextFile: %08X does not belong in %d/%d/%d\n",
-                   rb->fn, rb->sd->index, rb->curlvl1, rb->curlvl2);
-               continue;
-           }
-           used = storeUfsDirMapBitTest(SD, rb->fn);
-           if (used) {
-               debug(47, 3) ("storeUfsDirGetNextFile: Locked, continuing with next.\n");
-               continue;
-           }
-           snprintf(rb->fullfilename, SQUID_MAXPATHLEN, "%s/%s",
-               rb->fullpath, rb->entry->d_name);
-           debug(47, 3) ("storeUfsDirGetNextFile: Opening %s\n", rb->fullfilename);
-           fd = file_open(rb->fullfilename, O_RDONLY | O_BINARY);
-           if (fd < 0)
-               debug(47, 1) ("storeUfsDirGetNextFile: %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 < ufsinfo->l2)
-           continue;
-       rb->curlvl2 = 0;
-       if (++rb->curlvl1 < ufsinfo->l1)
-           continue;
-       rb->curlvl1 = 0;
-       rb->done = 1;
-    }
-    *filn_p = rb->fn;
-    return fd;
-}
-
-/* Add a new object to the cache with empty memory copy and pointer to disk
- * use to rebuild store from disk. */
-static StoreEntry *
-storeUfsDirAddDiskRestore(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) ("storeUfsAddDiskRestore: %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);
-    storeUfsDirMapBitSet(SD, e->swap_filen);
-    storeHashInsert(e, key);   /* do it after we clear KEY_PRIVATE */
-    storeUfsDirReplAdd(SD, e);
-    return e;
-}
-
-CBDATA_TYPE(RebuildState);
-static void
-storeUfsDirRebuild(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 storeUfsDirRebuildFromSwapLog(), otherwise we'll
-     * use storeUfsDirRebuildFromDirectory() to open up each file
-     * and suck in the meta data.
-     */
-    fp = storeUfsDirOpenTmpSwapLog(sd, &clean, &zero);
-    if (fp == NULL || zero) {
-       if (fp != NULL)
-           fclose(fp);
-       func = storeUfsDirRebuildFromDirectory;
-    } else {
-       func = storeUfsDirRebuildFromSwapLog;
-       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);
-}
-
-static void
-storeUfsDirCloseTmpSwapLog(SwapDir * sd)
-{
-    ufsinfo_t *ufsinfo = (ufsinfo_t *) sd->fsdata;
-    char *swaplog_path = xstrdup(storeUfsDirSwapLogFile(sd, NULL));
-    char *new_path = xstrdup(storeUfsDirSwapLogFile(sd, ".new"));
-    int fd;
-    file_close(ufsinfo->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("storeUfsDirCloseTmpSwapLog: unlink failed");
-    }
-#endif
-    if (xrename(new_path, swaplog_path) < 0) {
-       fatal("storeUfsDirCloseTmpSwapLog: 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("storeUfsDirCloseTmpSwapLog: Failed to open swap log.");
-    }
-    safe_free(swaplog_path);
-    safe_free(new_path);
-    ufsinfo->swaplog_fd = fd;
-    debug(47, 3) ("Cache Dir #%d log opened on FD %d\n", sd->index, fd);
-}
-
-static FILE *
-storeUfsDirOpenTmpSwapLog(SwapDir * sd, int *clean_flag, int *zero_flag)
-{
-    ufsinfo_t *ufsinfo = (ufsinfo_t *) sd->fsdata;
-    char *swaplog_path = xstrdup(storeUfsDirSwapLogFile(sd, NULL));
-    char *clean_path = xstrdup(storeUfsDirSwapLogFile(sd, ".last-clean"));
-    char *new_path = xstrdup(storeUfsDirSwapLogFile(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 (ufsinfo->swaplog_fd >= 0)
-       file_close(ufsinfo->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.");
-    }
-    ufsinfo->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 UFS 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
-storeUfsDirWriteCleanStart(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(storeUfsDirSwapLogFile(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(storeUfsDirSwapLogFile(sd, NULL));
-    state->cln = xstrdup(storeUfsDirSwapLogFile(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 = storeUfsDirWriteCleanEntry;
-    sd->log.clean.state = state;
-    return 0;
-}
-
-/*
- * Get the next entry that is a candidate for clean log writing
- */
-const StoreEntry *
-storeUfsDirCleanLogNextEntry(SwapDir * sd)
-{
-    const StoreEntry *entry = NULL;
-    struct _clean_state *state = sd->log.clean.state;
-    if (state->walker)
-       entry = state->walker->Next(state->walker);
-    return entry;
-}
-
-/*
- * "write" an entry to the clean log file.
- */
-static void
-storeUfsDirWriteCleanEntry(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;
-    }
-}
-
-static void
-storeUfsDirWriteCleanDone(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.
-     */
-    storeUfsDirCloseSwapLog(sd);
-    /* save the fd value for a later test */
-    fd = state->fd;
-    /* rename */
-    if (state->fd >= 0) {
-#if defined(_SQUID_OS2_) || defined(_SQUID_CYGWIN_) || defined(_SQUID_MSWIN_)
-       file_close(state->fd);
-       state->fd = -1;
-       if (unlink(state->cur) < 0)
-           debug(50, 0) ("storeDirWriteCleanLogs: unlinkd failed: %s, %s\n",
-               xstrerror(), state->cur);
-#endif
-       xrename(state->new, state->cur);
-    }
-    /* touch a timestamp file if we're not still validating */
-    if (store_dirs_rebuilding)
-       (void) 0;
-    else if (fd < 0)
-       (void) 0;
-    else
-       file_close(file_open(state->cln, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY));
-    /* close */
-    safe_free(state->cur);
-    safe_free(state->new);
-    safe_free(state->cln);
-    if (state->fd >= 0)
-       file_close(state->fd);
-    state->fd = -1;
-    safe_free(state);
-    sd->log.clean.state = NULL;
-    sd->log.clean.write = NULL;
-}
-
-static void
-storeSwapLogDataFree(void *s)
-{
-    memFree(s, MEM_SWAP_LOG_DATA);
-}
-
-static void
-storeUfsDirSwapLog(const SwapDir * sd, const StoreEntry * e, int op)
-{
-    ufsinfo_t *ufsinfo = (ufsinfo_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(ufsinfo->swaplog_fd,
-       -1,
-       s,
-       sizeof(storeSwapLogData),
-       NULL,
-       NULL,
-       (FREE *) storeSwapLogDataFree);
-}
-
-static void
-storeUfsDirNewfs(SwapDir * sd)
-{
-    debug(47, 3) ("Creating swap space in %s\n", sd->path);
-    storeUfsDirCreateDirectory(sd->path, 0);
-    storeUfsDirCreateSwapSubDirs(sd);
-}
-
-static int
-rev_int_sort(const void *A, const void *B)
-{
-    const int *i1 = A;
-    const int *i2 = B;
-    return *i2 - *i1;
-}
-
-static int
-storeUfsDirClean(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;
-    ufsinfo_t *ufsinfo;
-    N0 = n_ufs_dirs;
-    D0 = ufs_dir_index[swap_index % N0];
-    SD = &Config.cacheSwap.swapDirs[D0];
-    ufsinfo = (ufsinfo_t *) SD->fsdata;
-    N1 = ufsinfo->l1;
-    D1 = (swap_index / N0) % N1;
-    N2 = ufsinfo->l2;
-    D2 = ((swap_index / N0) / N1) % N2;
-    snprintf(p1, SQUID_MAXPATHLEN, "%s/%02X/%02X",
-       Config.cacheSwap.swapDirs[D0].path, D1, D2);
-    debug(36, 3) ("storeDirClean: Cleaning directory %s\n", p1);
-    dp = opendir(p1);
-    if (dp == NULL) {
-       if (errno == ENOENT) {
-           debug(36, 0) ("storeDirClean: WARNING: Creating %s\n", p1);
-           if (mkdir(p1, 0777) == 0)
-               return 0;
-       }
-       debug(50, 0) ("storeDirClean: %s: %s\n", p1, xstrerror());
-       safeunlink(p1, 1);
-       return 0;
-    }
-    while ((de = readdir(dp)) != NULL && k < 20) {
-       if (sscanf(de->d_name, "%X", &swapfileno) != 1)
-           continue;
-       fn = swapfileno;        /* XXX should remove this cruft ! */
-       if (storeUfsDirValidFileno(SD, fn, 1))
-           if (storeUfsDirMapBitTest(SD, fn))
-               if (storeUfsFilenoBelongsHere(fn, D0, D1, D2))
-                   continue;
-#if USE_TRUNCATE
-       if (!stat(de->d_name, &sb))
-           if (sb.st_size == 0)
-               continue;
-#endif
-       files[k++] = swapfileno;
-    }
-    closedir(dp);
-    if (k == 0)
-       return 0;
-    qsort(files, k, sizeof(int), rev_int_sort);
-    if (k > 10)
-       k = 10;
-    for (n = 0; n < k; n++) {
-       debug(36, 3) ("storeDirClean: Cleaning file %08X\n", files[n]);
-       snprintf(p2, MAXPATHLEN + 1, "%s/%08X", p1, files[n]);
-#if USE_TRUNCATE
-       truncate(p2, 0);
-#else
-       safeunlink(p2, 0);
-#endif
-       statCounter.swap.files_cleaned++;
-    }
-    debug(36, 3) ("Cleaned %d unused files from %s\n", k, p1);
-    return k;
-}
-
-static void
-storeUfsDirCleanEvent(void *unused)
-{
-    static int swap_index = 0;
-    int i;
-    int j = 0;
-    int n = 0;
-    /*
-     * Assert that there are UFS cache_dirs configured, otherwise
-     * we should never be called.
-     */
-    assert(n_ufs_dirs);
-    if (NULL == ufs_dir_index) {
-       SwapDir *sd;
-       ufsinfo_t *ufsinfo;
-       /*
-        * Initialize the little array that translates UFS cache_dir
-        * number into the Config.cacheSwap.swapDirs array index.
-        */
-       ufs_dir_index = xcalloc(n_ufs_dirs, sizeof(*ufs_dir_index));
-       for (i = 0, n = 0; i < Config.cacheSwap.n_configured; i++) {
-           sd = &Config.cacheSwap.swapDirs[i];
-           if (!storeUfsDirIs(sd))
-               continue;
-           ufs_dir_index[n++] = i;
-           ufsinfo = (ufsinfo_t *) sd->fsdata;
-           j += (ufsinfo->l1 * ufsinfo->l2);
-       }
-       assert(n == n_ufs_dirs);
-       /*
-        * Start the storeUfsDirClean() swap_index with a random
-        * value.  j equals the total number of UFS level 2
-        * swap directories
-        */
-       swap_index = (int) (squid_random() % j);
-    }
-    if (0 == store_dirs_rebuilding) {
-       n = storeUfsDirClean(swap_index);
-       swap_index++;
-    }
-    eventAdd("storeDirClean", storeUfsDirCleanEvent, NULL,
-       15.0 * exp(-0.25 * n), 1);
-}
-
-static int
-storeUfsDirIs(SwapDir * sd)
-{
-    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?
- */
-static int
-storeUfsFilenoBelongsHere(int fn, int F0, int F1, int F2)
-{
-    int D1, D2;
-    int L1, L2;
-    int filn = fn;
-    ufsinfo_t *ufsinfo;
-    assert(F0 < Config.cacheSwap.n_configured);
-    ufsinfo = (ufsinfo_t *) Config.cacheSwap.swapDirs[F0].fsdata;
-    L1 = ufsinfo->l1;
-    L2 = ufsinfo->l2;
-    D1 = ((filn / L2) / L2) % L1;
-    if (F1 != D1)
-       return 0;
-    D2 = (filn / L2) % L2;
-    if (F2 != D2)
-       return 0;
-    return 1;
-}
-
-int
-storeUfsDirValidFileno(SwapDir * SD, sfileno filn, int flag)
-{
-    ufsinfo_t *ufsinfo = (ufsinfo_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 > ufsinfo->map->max_n_files)
-           return 0;
-    return 1;
-}
-
-void
-storeUfsDirMaintain(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)) ("storeUfsDirMaintain: %s removed %d/%d f=%.03f max_scan=%d\n",
-       SD->path, removed, max_remove, f, max_scan);
-}
-
 /*
  * storeUfsDirCheckObj
  *
@@ -1355,120 +61,20 @@ storeUfsDirCheckObj(SwapDir * SD, const StoreEntry * e)
     return 999;
 }
 
-/*
- * storeUfsDirRefObj
- *
- * This routine is called whenever an object is referenced, so we can
- * maintain replacement information within the storage fs.
- */
-void
-storeUfsDirRefObj(SwapDir * SD, StoreEntry * e)
-{
-    debug(47, 3) ("storeUfsDirRefObj: referencing %p %d/%d\n", e, e->swap_dirn,
-       e->swap_filen);
-    if (SD->repl->Referenced)
-       SD->repl->Referenced(SD->repl, e, &e->repl);
-}
-
-/*
- * storeUfsDirUnrefObj
- * This routine is called whenever the last reference to an object is
- * removed, to maintain replacement information within the storage fs.
- */
 void
-storeUfsDirUnrefObj(SwapDir * SD, StoreEntry * e)
+storeUfsDirIOUnlinkFile(char *path)
 {
-    debug(47, 3) ("storeUfsDirUnrefObj: referencing %p %d/%d\n", e, e->swap_dirn,
-       e->swap_filen);
-    if (SD->repl->Dereferenced)
-       SD->repl->Dereferenced(SD->repl, e, &e->repl);
-}
-
-/*
- * storeUfsDirUnlinkFile
- *
- * This routine unlinks a file and pulls it out of the bitmap.
- * It used to be in storeUfsUnlink(), however an interface change
- * forced this bit of code here. Eeek.
- */
-void
-storeUfsDirUnlinkFile(SwapDir * SD, sfileno f)
-{
-    debug(79, 3) ("storeUfsDirUnlinkFile: unlinking fileno %08X\n", f);
-    /* storeUfsDirMapBitReset(SD, f); */
 #if USE_UNLINKD
-    unlinkdUnlink(storeUfsDirFullPath(SD, f, NULL));
+    unlinkdUnlink(path);
 #elif USE_TRUNCATE
-    truncate(storeUfsDirFullPath(SD, f, NULL), 0);
+    truncate(path, 0);
 #else
-    unlink(storeUfsDirFullPath(SD, f, NULL));
+    unlink(path);
 #endif
 }
 
-/*
- * Add and remove the given StoreEntry from the replacement policy in
- * use.
- */
-
-void
-storeUfsDirReplAdd(SwapDir * SD, StoreEntry * e)
-{
-    debug(47, 4) ("storeUfsDirReplAdd: added node %p to dir %d\n", e,
-       SD->index);
-    SD->repl->Add(SD->repl, e, &e->repl);
-}
-
-
-void
-storeUfsDirReplRemove(StoreEntry * e)
-{
-    SwapDir *SD = INDEXSD(e->swap_dirn);
-    debug(47, 4) ("storeUfsDirReplRemove: 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
-storeUfsDirStats(SwapDir * SD, StoreEntry * sentry)
-{
-    ufsinfo_t *ufsinfo = 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", ufsinfo->l1);
-    storeAppendPrintf(sentry, "Second level subdirectories: %d\n", ufsinfo->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",
-       ufsinfo->map->n_files_in_map, ufsinfo->map->max_n_files,
-       percent(ufsinfo->map->n_files_in_map, ufsinfo->map->max_n_files));
-    x = storeDirGetUFSStats(SD->path, &totl_kb, &free_kb, &totl_in, &free_in);
-    if (0 == x) {
-       storeAppendPrintf(sentry, "Filesystem Space in use: %d/%d KB (%d%%)\n",
-           totl_kb - free_kb,
-           totl_kb,
-           percent(totl_kb - free_kb, totl_kb));
-       storeAppendPrintf(sentry, "Filesystem Inodes in use: %d/%d (%d%%)\n",
-           totl_in - free_in,
-           totl_in,
-           percent(totl_in - free_in, totl_in));
-    }
-    storeAppendPrintf(sentry, "Flags:");
-    if (SD->flags.selected)
-       storeAppendPrintf(sentry, " SELECTED");
-    if (SD->flags.read_only)
-       storeAppendPrintf(sentry, " READ-ONLY");
-    storeAppendPrintf(sentry, "\n");
-}
-
 static struct cache_dir_option options[] =
 {
 #if NOT_YET_DONE
@@ -1519,79 +125,10 @@ storeUfsDirReconfigure(SwapDir * sd, int index, char *path)
 void
 storeUfsDirDump(StoreEntry * entry, SwapDir * s)
 {
-    ufsinfo_t *ufsinfo = (ufsinfo_t *) s->fsdata;
-    storeAppendPrintf(entry, " %d %d %d",
-       s->max_size >> 10,
-       ufsinfo->l1,
-       ufsinfo->l2);
+    commonUfsDirDump (entry, s);
     dump_cachedir_options(entry, options, s);
 }
 
-/*
- * Only "free" the filesystem specific stuff here
- */
-static void
-storeUfsDirFree(SwapDir * s)
-{
-    ufsinfo_t *ufsinfo = (ufsinfo_t *) s->fsdata;
-    if (ufsinfo->swaplog_fd > -1) {
-       file_close(ufsinfo->swaplog_fd);
-       ufsinfo->swaplog_fd = -1;
-    }
-    filemapFreeMemory(ufsinfo->map);
-    xfree(ufsinfo);
-    s->fsdata = NULL;          /* Will aid debugging... */
-
-}
-
-char *
-storeUfsDirFullPath(SwapDir * SD, sfileno filn, char *fullpath)
-{
-    LOCAL_ARRAY(char, fullfilename, SQUID_MAXPATHLEN);
-    ufsinfo_t *ufsinfo = (ufsinfo_t *) SD->fsdata;
-    int L1 = ufsinfo->l1;
-    int L2 = ufsinfo->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;
-}
-
-/*
- * storeUfsCleanupDoubleCheck
- *
- * This is called by storeCleanup() if -S was given on the command line.
- */
-static int
-storeUfsCleanupDoubleCheck(SwapDir * sd, StoreEntry * e)
-{
-    struct stat sb;
-    if (stat(storeUfsDirFullPath(sd, e->swap_filen, NULL), &sb) < 0) {
-       debug(47, 0) ("storeUfsCleanupDoubleCheck: MISSING SWAP FILE\n");
-       debug(47, 0) ("storeUfsCleanupDoubleCheck: FILENO %08X\n", e->swap_filen);
-       debug(47, 0) ("storeUfsCleanupDoubleCheck: PATH %s\n",
-           storeUfsDirFullPath(sd, e->swap_filen, NULL));
-       storeEntryDump(e, 0);
-       return -1;
-    }
-    if (e->swap_file_sz != sb.st_size) {
-       debug(47, 0) ("storeUfsCleanupDoubleCheck: SIZE MISMATCH\n");
-       debug(47, 0) ("storeUfsCleanupDoubleCheck: FILENO %08X\n", e->swap_filen);
-       debug(47, 0) ("storeUfsCleanupDoubleCheck: PATH %s\n",
-           storeUfsDirFullPath(sd, e->swap_filen, NULL));
-       debug(47, 0) ("storeUfsCleanupDoubleCheck: 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;
-}
-
 /*
  * storeUfsDirParse
  *
@@ -1604,7 +141,7 @@ storeUfsDirParse(SwapDir * sd, int index, char *path)
     int size;
     int l1;
     int l2;
-    ufsinfo_t *ufsinfo;
+    squidufsinfo_t *ufsinfo;
 
     i = GetInteger();
     size = i << 10;            /* Mbytes to kbytes */
@@ -1619,9 +156,9 @@ storeUfsDirParse(SwapDir * sd, int index, char *path)
     if (l2 <= 0)
        fatal("storeUfsDirParse: invalid level 2 directories value");
 
-    ufsinfo = xmalloc(sizeof(ufsinfo_t));
+    ufsinfo = xmalloc(sizeof(squidufsinfo_t));
     if (ufsinfo == NULL)
-       fatal("storeUfsDirParse: couldn't xmalloc() ufsinfo_t!\n");
+       fatal("storeUfsDirParse: couldn't xmalloc() squidufsinfo_t!\n");
 
     sd->index = index;
     sd->path = xstrdup(path);
@@ -1632,16 +169,17 @@ storeUfsDirParse(SwapDir * sd, int index, char *path)
     ufsinfo->swaplog_fd = -1;
     ufsinfo->map = NULL;       /* Debugging purposes */
     ufsinfo->suggest = 0;
-    sd->init = storeUfsDirInit;
-    sd->newfs = storeUfsDirNewfs;
+    ufsinfo->io.storeDirUnlinkFile = storeUfsDirIOUnlinkFile;
+    sd->init = commonUfsDirInit;
+    sd->newfs = commonUfsDirNewfs;
     sd->dump = storeUfsDirDump;
-    sd->freefs = storeUfsDirFree;
-    sd->dblcheck = storeUfsCleanupDoubleCheck;
-    sd->statfs = storeUfsDirStats;
-    sd->maintainfs = storeUfsDirMaintain;
+    sd->freefs = commonUfsDirFree;
+    sd->dblcheck = commonUfsCleanupDoubleCheck;
+    sd->statfs = commonUfsDirStats;
+    sd->maintainfs = commonUfsDirMaintain;
     sd->checkobj = storeUfsDirCheckObj;
-    sd->refobj = storeUfsDirRefObj;
-    sd->unrefobj = storeUfsDirUnrefObj;
+    sd->refobj = commonUfsDirRefObj;
+    sd->unrefobj = commonUfsDirUnrefObj;
     sd->callback = NULL;
     sd->sync = NULL;
     sd->obj.create = storeUfsCreate;
@@ -1650,12 +188,12 @@ storeUfsDirParse(SwapDir * sd, int index, char *path)
     sd->obj.read = storeUfsRead;
     sd->obj.write = storeUfsWrite;
     sd->obj.unlink = storeUfsUnlink;
-    sd->log.open = storeUfsDirOpenSwapLog;
-    sd->log.close = storeUfsDirCloseSwapLog;
-    sd->log.write = storeUfsDirSwapLog;
-    sd->log.clean.start = storeUfsDirWriteCleanStart;
-    sd->log.clean.nextentry = storeUfsDirCleanLogNextEntry;
-    sd->log.clean.done = storeUfsDirWriteCleanDone;
+    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);
 
index 5d152c6c261c49f3a88e0be922021252458c396c..cd8524cf929acb9594174715c3106835e650e525 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * $Id: store_io_ufs.cc,v 1.11 2002/08/08 20:12:46 hno Exp $
+ * $Id: store_io_ufs.cc,v 1.12 2002/10/12 09:45:58 robertc Exp $
  *
  * DEBUG: section 79    Storage Manager UFS Interface
  * AUTHOR: Duane Wessels
@@ -35,6 +35,7 @@
 
 #include "squid.h"
 #include "store_ufs.h"
+#include "ufscommon.h"
 
 
 static DRCB storeUfsReadDone;
@@ -51,7 +52,7 @@ storeUfsOpen(SwapDir * SD, StoreEntry * e, STFNCB * file_callback,
     STIOCB * callback, void *callback_data)
 {
     sfileno f = e->swap_filen;
-    char *path = storeUfsDirFullPath(SD, f, NULL);
+    char *path = commonUfsDirFullPath(SD, f, NULL);
     storeIOState *sio;
     struct stat sb;
     int fd;
@@ -91,16 +92,16 @@ storeUfsCreate(SwapDir * SD, StoreEntry * e, STFNCB * file_callback, STIOCB * ca
     int fd;
     int mode = (O_WRONLY | O_CREAT | O_TRUNC | O_BINARY);
     char *path;
-    ufsinfo_t *ufsinfo = (ufsinfo_t *) SD->fsdata;
+    squidufsinfo_t *ufsinfo = (squidufsinfo_t *) SD->fsdata;
     sfileno filn;
     sdirno dirn;
 
     /* Allocate a number */
     dirn = SD->index;
-    filn = storeUfsDirMapBitAllocate(SD);
+    filn = commonUfsDirMapBitAllocate(SD);
     ufsinfo->suggest = filn + 1;
     /* Shouldn't we handle a 'bitmap full' error here? */
-    path = storeUfsDirFullPath(SD, filn, NULL);
+    path = commonUfsDirFullPath(SD, filn, NULL);
 
     debug(79, 3) ("storeUfsCreate: fileno %08X\n", filn);
     fd = file_open(path, mode);
@@ -126,7 +127,7 @@ storeUfsCreate(SwapDir * SD, StoreEntry * e, STFNCB * file_callback, STIOCB * ca
     store_open_disk_fd++;
 
     /* now insert into the replacement policy */
-    storeUfsDirReplAdd(SD, e);
+    commonUfsDirReplAdd(SD, e);
     return sio;
 }
 
@@ -184,9 +185,9 @@ void
 storeUfsUnlink(SwapDir * SD, StoreEntry * e)
 {
     debug(79, 3) ("storeUfsUnlink: fileno %08X\n", e->swap_filen);
-    storeUfsDirReplRemove(e);
-    storeUfsDirMapBitReset(SD, e->swap_filen);
-    storeUfsDirUnlinkFile(SD, e->swap_filen);
+    commonUfsDirReplRemove(e);
+    commonUfsDirMapBitReset(SD, e->swap_filen);
+    commonUfsDirUnlinkFile(SD, e->swap_filen);
 }
 
 /*  === STATIC =========================================================== */
index 63c985835fc0df59d35bb9e7e7fd2ae05ecb3e05..8568c00218e7151c6f277b0046ebf89b9b3b669b 100644 (file)
@@ -7,14 +7,6 @@
 #ifndef __STORE_UFS_H__
 #define __STORE_UFS_H__
 
-struct _ufsinfo_t {
-    int swaplog_fd;
-    int l1;
-    int l2;
-    fileMap *map;
-    int suggest;
-};
-
 struct _ufsstate_t {
     int fd;
     struct {
@@ -24,19 +16,11 @@ struct _ufsstate_t {
     } flags;
 };
 
-typedef struct _ufsinfo_t ufsinfo_t;
 typedef struct _ufsstate_t ufsstate_t;
 
 /* The ufs_state memory pool */
 extern MemPool *ufs_state_pool;
 
-extern void storeUfsDirMapBitReset(SwapDir *, sfileno);
-extern int storeUfsDirMapBitAllocate(SwapDir *);
-extern char *storeUfsDirFullPath(SwapDir * SD, sfileno filn, char *fullpath);
-extern void storeUfsDirUnlinkFile(SwapDir *, sfileno);
-extern void storeUfsDirReplAdd(SwapDir * SD, StoreEntry *);
-extern void storeUfsDirReplRemove(StoreEntry *);
-
 /*
  * Store IO stuff
  */
index 1cb19363b647300f266442d8a21970ec76aeb2b2..21f30df89741bedade202c365310a78046cb74eb 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * $Id: leakfinder.cc,v 1.5 2001/01/12 00:37:19 wessels Exp $
+ * $Id: leakfinder.cc,v 1.6 2002/10/12 09:45:56 robertc Exp $
  *
  * DEBUG: section 45    Callback Data Registry
  * AUTHOR: Duane Wessels
@@ -135,6 +135,6 @@ ptrDump(StoreEntry * sentry)
     while ((hptr = hash_next(htable))) {
        c = (ptr *) hptr;
        storeAppendPrintf(sentry, "%20p last used %9d seconds ago by %s:%d\n",
-           c->key, squid_curtime - c->when, c->file, c->line);
+           c->key, (int) (squid_curtime - c->when), c->file, c->line);
     }
 }
diff --git a/src/ufscommon.cc b/src/ufscommon.cc
new file mode 100644 (file)
index 0000000..78e1419
--- /dev/null
@@ -0,0 +1,1735 @@
+/*
+ * $Id: ufscommon.cc,v 1.1 2002/10/12 09:45:56 robertc 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"
+#if 0
+
+#include "squid.h"
+
+#include "store_asyncufs.h"
+
+#define DefaultLevelOneDirs     16
+#define DefaultLevelTwoDirs     256
+#define STORE_META_BUFSZ 4096
+
+#endif
+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);
+       }
+    } else if (0 == mkdir(path, 0755)) {
+       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 = sb.st_size;
+       } else if (tmpe.swap_file_sz == sb.st_size - swap_hdr_len) {
+           tmpe.swap_file_sz = sb.st_size;
+       } else if (tmpe.swap_file_sz != sb.st_size) {
+           debug(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);
+           if (mkdir(p1, 0777) == 0)
+               return 0;
+       }
+       debug(50, 0) ("storeDirClean: %s: %s\n", p1, xstrerror());
+       safeunlink(p1, 1);
+       return 0;
+    }
+    while ((de = readdir(dp)) != NULL && k < 20) {
+       if (sscanf(de->d_name, "%X", &swapfileno) != 1)
+           continue;
+       fn = swapfileno;        /* XXX should remove this cruft ! */
+       if (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
diff --git a/src/ufscommon.h b/src/ufscommon.h
new file mode 100644 (file)
index 0000000..8380122
--- /dev/null
@@ -0,0 +1,81 @@
+
+/*
+ * $Id: ufscommon.h,v 1.1 2002/10/12 09:45:56 robertc Exp $
+ *
+ * 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.
+ *
+ */
+
+#ifndef SQUID_UFSCOMMON_H
+#define SQUID_UFSCOMMON_H
+
+#include "squid.h"
+
+#define DefaultLevelOneDirs     16
+#define DefaultLevelTwoDirs     256
+#define STORE_META_BUFSZ 4096
+
+typedef struct _iospecific_t iospecific_t;
+struct _iospecific_t {
+    void (*storeDirUnlinkFile) (char *);
+};
+
+typedef struct _squidufsinfo_t squidufsinfo_t;
+struct _squidufsinfo_t {
+    int swaplog_fd;
+    int l1;
+    int l2;
+    fileMap *map;
+    int suggest;
+    iospecific_t io;
+};
+
+/* Common UFS routines */
+void commonUfsDirSwapLog(const SwapDir * sd, const StoreEntry * e, int op);
+FREE storeSwapLogDataFree;
+void commonUfsDirWriteCleanDone(SwapDir * sd);
+const StoreEntry *commonUfsDirCleanLogNextEntry(SwapDir * sd);
+void commonUfsDirCloseSwapLog(SwapDir * sd);
+int commonUfsDirWriteCleanStart(SwapDir * sd);
+void commonUfsDirInit(SwapDir * sd);
+void commonUfsDirUnlinkFile(SwapDir * SD, sfileno f);
+void commonUfsDirOpenSwapLog(SwapDir * sd);
+void commonUfsDirNewfs(SwapDir * sd);
+void commonUfsDirMaintain(SwapDir * SD);
+void commonUfsDirRefObj(SwapDir * SD, StoreEntry * e);
+void commonUfsDirUnrefObj(SwapDir * SD, StoreEntry * e);
+void commonUfsDirReplAdd(SwapDir * SD, StoreEntry * e);
+void commonUfsDirReplRemove(StoreEntry * e);
+void commonUfsDirStats(SwapDir * SD, StoreEntry * sentry);
+void commonUfsDirDump(StoreEntry * entry, SwapDir * s);
+void commonUfsDirFree(SwapDir * s);
+char *commonUfsDirFullPath(SwapDir * SD, sfileno filn, char *fullpath);
+int commonUfsCleanupDoubleCheck(SwapDir * sd, StoreEntry * e);
+int commonUfsDirMapBitAllocate(SwapDir * SD);
+void commonUfsDirMapBitReset(SwapDir * SD, sfileno filn);
+
+#endif /* SQUID_UFSCOMMON_H */