]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
MODIO_1 commit. This change (including documentation) implements a more
authoradrian <>
Wed, 3 May 2000 23:15:38 +0000 (23:15 +0000)
committeradrian <>
Wed, 3 May 2000 23:15:38 +0000 (23:15 +0000)
modular storage directory system, which leaves object replacement and IO
up to the storage modules.

There is a lot of repeated code in the FS modules and some tidying up
is in the pipeline.

The documentation for this new API is in doc/Programming-Guide/prog-guide.sgml .

51 files changed:
configure
configure.in
doc/Programming-Guide/prog-guide.sgml
include/autoconf.h.in
src/Makefile.in
src/cache_cf.cc
src/cf.data.pre
src/comm_select.cc
src/defines.h
src/disk.cc
src/enums.h
src/fd.cc
src/fs/Makefile.in [new file with mode: 0644]
src/fs/aufs/Makefile.in [new file with mode: 0644]
src/fs/aufs/aiops.cc [new file with mode: 0644]
src/fs/aufs/async_io.cc [new file with mode: 0644]
src/fs/aufs/store_asyncufs.h [new file with mode: 0644]
src/fs/aufs/store_dir_aufs.cc [new file with mode: 0644]
src/fs/aufs/store_io_aufs.cc [new file with mode: 0644]
src/fs/coss/Makefile.in [new file with mode: 0644]
src/fs/coss/store_coss.h [new file with mode: 0644]
src/fs/coss/store_dir_coss.cc [new file with mode: 0644]
src/fs/coss/store_io_coss.cc [new file with mode: 0644]
src/fs/diskd/Makefile.in [new file with mode: 0644]
src/fs/diskd/diskd.cc [new file with mode: 0644]
src/fs/diskd/store_dir_diskd.cc [new file with mode: 0644]
src/fs/diskd/store_diskd.h [new file with mode: 0644]
src/fs/diskd/store_io_diskd.cc [new file with mode: 0644]
src/fs/ufs/Makefile.in [new file with mode: 0644]
src/fs/ufs/store_dir_ufs.cc [new file with mode: 0644]
src/fs/ufs/store_io_ufs.cc [new file with mode: 0644]
src/fs/ufs/store_ufs.h [new file with mode: 0644]
src/globals.h
src/main.cc
src/mem.cc
src/protos.h
src/snmp_agent.cc
src/squid.h
src/stat.cc
src/store.cc
src/store_client.cc
src/store_dir.cc
src/store_io.cc
src/store_log.cc
src/store_rebuild.cc
src/store_swapin.cc
src/store_swapout.cc
src/structs.h
src/typedefs.h
src/unlinkd.cc
src/url.cc

index 4229359908b7af56c0d05b219d38f418a2be2487..421290fa8678f116a3d4b077759f36365e3135a7 100755 (executable)
--- a/configure
+++ b/configure
@@ -28,10 +28,23 @@ ac_help="$ac_help
   --enable-carp           Enable CARP support"
 ac_help="$ac_help
   --enable-async-io[=N_THREADS]
-                          Do ASYNC disk I/O using threads.
-                          N_THREADS is the number of worker threads
-                          defaults to 16. See also src/squid.h for
-                          some additional platform tuning"
+                          Shorthand for
+                          --with-aio-threads=N_THREADS
+                          --with-pthreads
+                          --enable-storeio=ufs,aufs"
+ac_help="$ac_help
+  --with-aio-threads=N_THREADS
+                          Tune the number of worker threads for the aufs object
+                          store."
+ac_help="$ac_help
+  --with-pthreads         Use POSIX Threads"
+ac_help="$ac_help
+  --enable-storeio=\"list of modules\"
+                          Build support for the list of store I/O modules.
+                          The default is only to build the "ufs" module.
+                          See src/fs for a list of available modules, or
+                          Programmers Guide section <not yet written>
+                          for details on how to build your custom store module"
 ac_help="$ac_help
   --enable-icmp           Enable ICMP pinging"
 ac_help="$ac_help
@@ -64,6 +77,8 @@ ac_help="$ac_help
 ac_help="$ac_help
   --enable-err-language=lang
                           Select language for Error pages (see errors dir) "
+ac_help="$ac_help
+  --with-coss-membuf-size COSS membuf size (default 1048576 bytes) "
 ac_help="$ac_help
   --enable-poll           Enable poll() instead of select().  Normally poll
                           is preferred over select, but configure knows poll
@@ -110,17 +125,13 @@ ac_help="$ac_help
   --enable-heap-replacement
                           This option allows you to use various cache
                           replacement algorithms, instead of the standard
-                          LRU algorithm.
-                          "
+                          LRU algorithm."
 ac_help="$ac_help
   --enable-auth-modules=\"list of modules\"
                           This option selects wich proxy_auth helper modules
                           to build and install as part of the normal build
                           process. For a list of available modules see
                           the auth_modules directory."
-ac_help="$ac_help
-  --enable-diskd          Uses shared memory, message queues, and external
-                          processes for disk I/O."
 
 # Initialize some variables set by options.
 # The variables have the same names as the options, with
@@ -632,7 +643,7 @@ fi
 
 
 
-# From configure.in Revision
+# From configure.in Revision: 1.185 
 ac_aux_dir=
 for ac_dir in cfgaux $srcdir/cfgaux; do
   if test -f $ac_dir/install-sh; then
@@ -660,7 +671,7 @@ else { echo "configure: error: can not run $ac_config_sub" 1>&2; exit 1; }
 fi
 
 echo $ac_n "checking host system type""... $ac_c" 1>&6
-echo "configure:664: checking host system type" >&5
+echo "configure:675: checking host system type" >&5
 
 host_alias=$host
 case "$host_alias" in
@@ -728,7 +739,7 @@ PRESET_CFLAGS="$CFLAGS"
 # Extract the first word of "gcc", so it can be a program name with args.
 set dummy gcc; ac_word=$2
 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
-echo "configure:732: checking for $ac_word" >&5
+echo "configure:743: checking for $ac_word" >&5
 if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
@@ -758,7 +769,7 @@ if test -z "$CC"; then
   # Extract the first word of "cc", so it can be a program name with args.
 set dummy cc; ac_word=$2
 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
-echo "configure:762: checking for $ac_word" >&5
+echo "configure:773: checking for $ac_word" >&5
 if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
@@ -809,7 +820,7 @@ fi
       # Extract the first word of "cl", so it can be a program name with args.
 set dummy cl; ac_word=$2
 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
-echo "configure:813: checking for $ac_word" >&5
+echo "configure:824: checking for $ac_word" >&5
 if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
@@ -841,7 +852,7 @@ fi
 fi
 
 echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works""... $ac_c" 1>&6
-echo "configure:845: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5
+echo "configure:856: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5
 
 ac_ext=c
 # CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
@@ -852,12 +863,12 @@ cross_compiling=$ac_cv_prog_cc_cross
 
 cat > conftest.$ac_ext << EOF
 
-#line 856 "configure"
+#line 867 "configure"
 #include "confdefs.h"
 
 main(){return(0);}
 EOF
-if { (eval echo configure:861: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+if { (eval echo configure:872: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
   ac_cv_prog_cc_works=yes
   # If we can't run a trivial program, we are probably using a cross compiler.
   if (./conftest; exit) 2>/dev/null; then
@@ -883,12 +894,12 @@ if test $ac_cv_prog_cc_works = no; then
   { echo "configure: error: installation or configuration problem: C compiler cannot create executables." 1>&2; exit 1; }
 fi
 echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6
-echo "configure:887: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5
+echo "configure:898: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5
 echo "$ac_t""$ac_cv_prog_cc_cross" 1>&6
 cross_compiling=$ac_cv_prog_cc_cross
 
 echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6
-echo "configure:892: checking whether we are using GNU C" >&5
+echo "configure:903: checking whether we are using GNU C" >&5
 if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
@@ -897,7 +908,7 @@ else
   yes;
 #endif
 EOF
-if { ac_try='${CC-cc} -E conftest.c'; { (eval echo configure:901: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then
+if { ac_try='${CC-cc} -E conftest.c'; { (eval echo configure:912: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then
   ac_cv_prog_gcc=yes
 else
   ac_cv_prog_gcc=no
@@ -916,7 +927,7 @@ ac_test_CFLAGS="${CFLAGS+set}"
 ac_save_CFLAGS="$CFLAGS"
 CFLAGS=
 echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6
-echo "configure:920: checking whether ${CC-cc} accepts -g" >&5
+echo "configure:931: checking whether ${CC-cc} accepts -g" >&5
 if eval "test \"`echo '$''{'ac_cv_prog_cc_g'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
@@ -1148,49 +1159,93 @@ EOF
 fi
 
 
-# Check whether --enable-async_io or --disable-async_io was given.
+# Check whether --enable-async-io or --disable-async-io was given.
 if test "${enable_async_io+set}" = set; then
   enableval="$enable_async_io"
-   case "$enableval" in
+   case $enableval in
   yes)
-    async_io=yes
+       with_pthreads=yes
+       STORE_MODULES="ufs aufs"
     ;;
   no)
-    async_io=''
     ;;
   *)
-    async_io=yes
-    cat >> confdefs.h <<EOF
-#define NUMTHREADS $enableval
-EOF
-
+       async_io_threads=$enableval
+       with_pthreads=yes
+       STORE_MODULES="ufs aufs"
     ;;
   esac
 
 fi
 
 
-if test -n "$async_io" ; then
-    echo "Async I/O enabled"
-    async_io=yes
-    cat >> confdefs.h <<\EOF
-#define USE_ASYNC_IO 1
+# Check whether --with-aio-threads or --without-aio-threads was given.
+if test "${with_aio_threads+set}" = set; then
+  withval="$with_aio_threads"
+   async_io_threads=$withval 
+fi
+
+if test "$async_io_threads"; then
+    echo "With $async_io_threads AIO threads"
+    with_pthreads=yes
+    cat >> confdefs.h <<EOF
+#define ASYNC_IO_THREADS $async_io_threads
 EOF
 
-    ASYNC_OBJS='$(ASYNC_OBJS)'
+fi
+
+# Check whether --with-pthreads or --without-pthreads was given.
+if test "${with_pthreads+set}" = set; then
+  withval="$with_pthreads"
+   if test "$enableval" = "yes"; then
+    with_pthreads=yes
+  fi
+
+fi
+
+if test "$with_pthreads"; then
+    echo "With pthreads"
     SQUID_PTHREAD_LIB='$(PTHREADLIB)'
     CFLAGS="$CFLAGS -D_REENTRANT"
     case "$host" in
     i386-unknown-freebsd*)
-       if test "$GCC" = "yes" ; then
-           if test -z "$PRESET_LDFLAGS"; then
-               LDFLAGS="$LDFLAGS -pthread"
+        if test "$GCC" = "yes" ; then
+            if test -z "$PRESET_LDFLAGS"; then
+                LDFLAGS="$LDFLAGS -pthread"
+            fi
+        fi
+    ;;
+    esac
+fi
+
+
+# Check whether --enable-storeio or --disable-storeio was given.
+if test "${enable_storeio+set}" = set; then
+  enableval="$enable_storeio"
+   case $enableval in
+  yes)
+       for module in $srcdir/src/fs/*; do
+           if test -f $module/Makefile.in; then
+               STORE_MODULES="$STORE_MODULES `basename $module`"
            fi
-       fi
+       done
+       ;;
+  no)
+       ;;
+  *)   STORE_MODULES="`echo $enableval| sed -e 's/,/ /g;s/  */ /g'`"
        ;;
-    esac
+  esac
+
+else
+   if test -z "$STORE_MODULES"; then
+    STORE_MODULES="ufs"
+  fi
+
 fi
 
+echo "Store moules built: $STORE_MODULES"
+
+STORE_OBJS="fs/`echo $STORE_MODULES|sed -e's% %.a fs/%g'`.a"
 
 
 # Check whether --enable-icmp or --disable-icmp was given.
@@ -1429,6 +1484,20 @@ fi
 
 
 
+# Check whether --with-coss-membuf-size or --without-coss-membuf-size was given.
+if test "${with_coss_membuf_size+set}" = set; then
+  withval="$with_coss_membuf_size"
+    if test "$with_coss_membuf_size"; then
+      echo "Setting COSS membuf size to $with_coss_membuf_size bytes"
+      cat >> confdefs.h <<EOF
+#define COSS_MEMBUF_SZ $with_coss_membuf_size
+EOF
+
+   fi
+
+fi
+
+
 # Check whether --enable-poll or --disable-poll was given.
 if test "${enable_poll+set}" = set; then
   enableval="$enable_poll"
@@ -1603,24 +1672,6 @@ if test -n "$AUTH_MODULES"; then
 fi
 
 
-# Check whether --enable-diskd or --disable-diskd was given.
-if test "${enable_diskd+set}" = set; then
-  enableval="$enable_diskd"
-   if test "$enableval" = "yes" ; then
-    echo "DISKD enabled"
-    cat >> confdefs.h <<\EOF
-#define USE_DISKD 1
-EOF
-
-    OPT_DISKD_EXE='$(DISKD_EXE)'
-    
-    DISKD_OBJS='diskd.o'
-    
-  fi
-
-fi
-
-
 # Force some compilers to use ANSI features
 #
 case "$host" in
@@ -1641,7 +1692,7 @@ case "$host" in
 esac
 
 echo $ac_n "checking how to run the C preprocessor""... $ac_c" 1>&6
-echo "configure:1645: checking how to run the C preprocessor" >&5
+echo "configure:1696: checking how to run the C preprocessor" >&5
 # On Suns, sometimes $CPP names a directory.
 if test -n "$CPP" && test -d "$CPP"; then
   CPP=
@@ -1656,13 +1707,13 @@ else
   # On the NeXT, cc -E runs the code through the compiler's parser,
   # not just through cpp.
   cat > conftest.$ac_ext <<EOF
-#line 1660 "configure"
+#line 1711 "configure"
 #include "confdefs.h"
 #include <assert.h>
 Syntax Error
 EOF
 ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
-{ (eval echo configure:1666: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+{ (eval echo configure:1717: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
 ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
 if test -z "$ac_err"; then
   :
@@ -1673,13 +1724,13 @@ else
   rm -rf conftest*
   CPP="${CC-cc} -E -traditional-cpp"
   cat > conftest.$ac_ext <<EOF
-#line 1677 "configure"
+#line 1728 "configure"
 #include "confdefs.h"
 #include <assert.h>
 Syntax Error
 EOF
 ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
-{ (eval echo configure:1683: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+{ (eval echo configure:1734: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
 ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
 if test -z "$ac_err"; then
   :
@@ -1690,13 +1741,13 @@ else
   rm -rf conftest*
   CPP="${CC-cc} -nologo -E"
   cat > conftest.$ac_ext <<EOF
-#line 1694 "configure"
+#line 1745 "configure"
 #include "confdefs.h"
 #include <assert.h>
 Syntax Error
 EOF
 ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
-{ (eval echo configure:1700: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+{ (eval echo configure:1751: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
 ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
 if test -z "$ac_err"; then
   :
@@ -1732,7 +1783,7 @@ echo "$ac_t""$CPP" 1>&6
 # SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
 # ./install, which can be erroneously created by make from ./install.sh.
 echo $ac_n "checking for a BSD compatible install""... $ac_c" 1>&6
-echo "configure:1736: checking for a BSD compatible install" >&5
+echo "configure:1787: checking for a BSD compatible install" >&5
 if test -z "$INSTALL"; then
 if eval "test \"`echo '$''{'ac_cv_path_install'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
@@ -1787,7 +1838,7 @@ test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
 # Extract the first word of "ranlib", so it can be a program name with args.
 set dummy ranlib; ac_word=$2
 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
-echo "configure:1791: checking for $ac_word" >&5
+echo "configure:1842: checking for $ac_word" >&5
 if eval "test \"`echo '$''{'ac_cv_prog_RANLIB'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
@@ -1815,7 +1866,7 @@ else
 fi
 
 echo $ac_n "checking whether ln -s works""... $ac_c" 1>&6
-echo "configure:1819: checking whether ln -s works" >&5
+echo "configure:1870: checking whether ln -s works" >&5
 if eval "test \"`echo '$''{'ac_cv_prog_LN_S'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
@@ -1838,7 +1889,7 @@ fi
 # Extract the first word of "sh", so it can be a program name with args.
 set dummy sh; ac_word=$2
 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
-echo "configure:1842: checking for $ac_word" >&5
+echo "configure:1893: checking for $ac_word" >&5
 if eval "test \"`echo '$''{'ac_cv_path_SH'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
@@ -1874,7 +1925,7 @@ fi
 # Extract the first word of "false", so it can be a program name with args.
 set dummy false; ac_word=$2
 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
-echo "configure:1878: checking for $ac_word" >&5
+echo "configure:1929: checking for $ac_word" >&5
 if eval "test \"`echo '$''{'ac_cv_path_FALSE'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
@@ -1910,7 +1961,7 @@ fi
 # Extract the first word of "true", so it can be a program name with args.
 set dummy true; ac_word=$2
 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
-echo "configure:1914: checking for $ac_word" >&5
+echo "configure:1965: checking for $ac_word" >&5
 if eval "test \"`echo '$''{'ac_cv_path_TRUE'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
@@ -1946,7 +1997,7 @@ fi
 # Extract the first word of "rm", so it can be a program name with args.
 set dummy rm; ac_word=$2
 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
-echo "configure:1950: checking for $ac_word" >&5
+echo "configure:2001: checking for $ac_word" >&5
 if eval "test \"`echo '$''{'ac_cv_path_RM'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
@@ -1982,7 +2033,7 @@ fi
 # Extract the first word of "mv", so it can be a program name with args.
 set dummy mv; ac_word=$2
 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
-echo "configure:1986: checking for $ac_word" >&5
+echo "configure:2037: checking for $ac_word" >&5
 if eval "test \"`echo '$''{'ac_cv_path_MV'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
@@ -2018,7 +2069,7 @@ fi
 # Extract the first word of "mkdir", so it can be a program name with args.
 set dummy mkdir; ac_word=$2
 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
-echo "configure:2022: checking for $ac_word" >&5
+echo "configure:2073: checking for $ac_word" >&5
 if eval "test \"`echo '$''{'ac_cv_path_MKDIR'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
@@ -2054,7 +2105,7 @@ fi
 # Extract the first word of "ln", so it can be a program name with args.
 set dummy ln; ac_word=$2
 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
-echo "configure:2058: checking for $ac_word" >&5
+echo "configure:2109: checking for $ac_word" >&5
 if eval "test \"`echo '$''{'ac_cv_path_LN'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
@@ -2090,7 +2141,7 @@ fi
 # Extract the first word of "perl", so it can be a program name with args.
 set dummy perl; ac_word=$2
 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
-echo "configure:2094: checking for $ac_word" >&5
+echo "configure:2145: checking for $ac_word" >&5
 if eval "test \"`echo '$''{'ac_cv_path_PERL'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
@@ -2126,7 +2177,7 @@ fi
 # Extract the first word of "makedepend", so it can be a program name with args.
 set dummy makedepend; ac_word=$2
 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
-echo "configure:2130: checking for $ac_word" >&5
+echo "configure:2181: checking for $ac_word" >&5
 if eval "test \"`echo '$''{'ac_cv_path_MAKEDEPEND'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
@@ -2162,7 +2213,7 @@ fi
 # Extract the first word of "ar", so it can be a program name with args.
 set dummy ar; ac_word=$2
 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
-echo "configure:2166: checking for $ac_word" >&5
+echo "configure:2217: checking for $ac_word" >&5
 if eval "test \"`echo '$''{'ac_cv_path_AR'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
@@ -2216,12 +2267,12 @@ for ac_hdr in dirent.h sys/ndir.h sys/dir.h ndir.h
 do
 ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
 echo $ac_n "checking for $ac_hdr that defines DIR""... $ac_c" 1>&6
-echo "configure:2220: checking for $ac_hdr that defines DIR" >&5
+echo "configure:2271: checking for $ac_hdr that defines DIR" >&5
 if eval "test \"`echo '$''{'ac_cv_header_dirent_$ac_safe'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 2225 "configure"
+#line 2276 "configure"
 #include "confdefs.h"
 #include <sys/types.h>
 #include <$ac_hdr>
@@ -2229,7 +2280,7 @@ int main() {
 DIR *dirp = 0;
 ; return 0; }
 EOF
-if { (eval echo configure:2233: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+if { (eval echo configure:2284: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
   rm -rf conftest*
   eval "ac_cv_header_dirent_$ac_safe=yes"
 else
@@ -2254,7 +2305,7 @@ done
 # Two versions of opendir et al. are in -ldir and -lx on SCO Xenix.
 if test $ac_header_dirent = dirent.h; then
 echo $ac_n "checking for opendir in -ldir""... $ac_c" 1>&6
-echo "configure:2258: checking for opendir in -ldir" >&5
+echo "configure:2309: checking for opendir in -ldir" >&5
 ac_lib_var=`echo dir'_'opendir | sed 'y%./+-%__p_%'`
 if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
@@ -2262,7 +2313,7 @@ else
   ac_save_LIBS="$LIBS"
 LIBS="-ldir  $LIBS"
 cat > conftest.$ac_ext <<EOF
-#line 2266 "configure"
+#line 2317 "configure"
 #include "confdefs.h"
 /* Override any gcc2 internal prototype to avoid an error.  */
 /* We use char because int might match the return type of a gcc2
@@ -2273,7 +2324,7 @@ int main() {
 opendir()
 ; return 0; }
 EOF
-if { (eval echo configure:2277: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+if { (eval echo configure:2328: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
   rm -rf conftest*
   eval "ac_cv_lib_$ac_lib_var=yes"
 else
@@ -2295,7 +2346,7 @@ fi
 
 else
 echo $ac_n "checking for opendir in -lx""... $ac_c" 1>&6
-echo "configure:2299: checking for opendir in -lx" >&5
+echo "configure:2350: checking for opendir in -lx" >&5
 ac_lib_var=`echo x'_'opendir | sed 'y%./+-%__p_%'`
 if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
@@ -2303,7 +2354,7 @@ else
   ac_save_LIBS="$LIBS"
 LIBS="-lx  $LIBS"
 cat > conftest.$ac_ext <<EOF
-#line 2307 "configure"
+#line 2358 "configure"
 #include "confdefs.h"
 /* Override any gcc2 internal prototype to avoid an error.  */
 /* We use char because int might match the return type of a gcc2
@@ -2314,7 +2365,7 @@ int main() {
 opendir()
 ; return 0; }
 EOF
-if { (eval echo configure:2318: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+if { (eval echo configure:2369: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
   rm -rf conftest*
   eval "ac_cv_lib_$ac_lib_var=yes"
 else
@@ -2337,12 +2388,12 @@ fi
 fi
 
 echo $ac_n "checking for ANSI C header files""... $ac_c" 1>&6
-echo "configure:2341: checking for ANSI C header files" >&5
+echo "configure:2392: checking for ANSI C header files" >&5
 if eval "test \"`echo '$''{'ac_cv_header_stdc'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 2346 "configure"
+#line 2397 "configure"
 #include "confdefs.h"
 #include <stdlib.h>
 #include <stdarg.h>
@@ -2350,7 +2401,7 @@ else
 #include <float.h>
 EOF
 ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
-{ (eval echo configure:2354: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+{ (eval echo configure:2405: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
 ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
 if test -z "$ac_err"; then
   rm -rf conftest*
@@ -2367,7 +2418,7 @@ rm -f conftest*
 if test $ac_cv_header_stdc = yes; then
   # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
 cat > conftest.$ac_ext <<EOF
-#line 2371 "configure"
+#line 2422 "configure"
 #include "confdefs.h"
 #include <string.h>
 EOF
@@ -2385,7 +2436,7 @@ fi
 if test $ac_cv_header_stdc = yes; then
   # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
 cat > conftest.$ac_ext <<EOF
-#line 2389 "configure"
+#line 2440 "configure"
 #include "confdefs.h"
 #include <stdlib.h>
 EOF
@@ -2406,7 +2457,7 @@ if test "$cross_compiling" = yes; then
   :
 else
   cat > conftest.$ac_ext <<EOF
-#line 2410 "configure"
+#line 2461 "configure"
 #include "confdefs.h"
 #include <ctype.h>
 #define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
@@ -2417,7 +2468,7 @@ if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) exit(2);
 exit (0); }
 
 EOF
-if { (eval echo configure:2421: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+if { (eval echo configure:2472: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
 then
   :
 else
@@ -2508,17 +2559,17 @@ for ac_hdr in \
 do
 ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
 echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
-echo "configure:2512: checking for $ac_hdr" >&5
+echo "configure:2563: checking for $ac_hdr" >&5
 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 2517 "configure"
+#line 2568 "configure"
 #include "confdefs.h"
 #include <$ac_hdr>
 EOF
 ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
-{ (eval echo configure:2522: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+{ (eval echo configure:2573: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
 ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
 if test -z "$ac_err"; then
   rm -rf conftest*
@@ -2546,12 +2597,12 @@ done
 
 
 echo $ac_n "checking for working const""... $ac_c" 1>&6
-echo "configure:2550: checking for working const" >&5
+echo "configure:2601: checking for working const" >&5
 if eval "test \"`echo '$''{'ac_cv_c_const'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 2555 "configure"
+#line 2606 "configure"
 #include "confdefs.h"
 
 int main() {
@@ -2600,7 +2651,7 @@ ccp = (char const *const *) p;
 
 ; return 0; }
 EOF
-if { (eval echo configure:2604: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+if { (eval echo configure:2655: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
   rm -rf conftest*
   ac_cv_c_const=yes
 else
@@ -2621,14 +2672,14 @@ EOF
 fi
 
 echo $ac_n "checking whether byte ordering is bigendian""... $ac_c" 1>&6
-echo "configure:2625: checking whether byte ordering is bigendian" >&5
+echo "configure:2676: checking whether byte ordering is bigendian" >&5
 if eval "test \"`echo '$''{'ac_cv_c_bigendian'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   ac_cv_c_bigendian=unknown
 # See if sys/param.h defines the BYTE_ORDER macro.
 cat > conftest.$ac_ext <<EOF
-#line 2632 "configure"
+#line 2683 "configure"
 #include "confdefs.h"
 #include <sys/types.h>
 #include <sys/param.h>
@@ -2639,11 +2690,11 @@ int main() {
 #endif
 ; return 0; }
 EOF
-if { (eval echo configure:2643: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+if { (eval echo configure:2694: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
   rm -rf conftest*
   # It does; now see whether it defined to BIG_ENDIAN or not.
 cat > conftest.$ac_ext <<EOF
-#line 2647 "configure"
+#line 2698 "configure"
 #include "confdefs.h"
 #include <sys/types.h>
 #include <sys/param.h>
@@ -2654,7 +2705,7 @@ int main() {
 #endif
 ; return 0; }
 EOF
-if { (eval echo configure:2658: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+if { (eval echo configure:2709: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
   rm -rf conftest*
   ac_cv_c_bigendian=yes
 else
@@ -2674,7 +2725,7 @@ if test "$cross_compiling" = yes; then
     { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; }
 else
   cat > conftest.$ac_ext <<EOF
-#line 2678 "configure"
+#line 2729 "configure"
 #include "confdefs.h"
 main () {
   /* Are we little or big endian?  From Harbison&Steele.  */
@@ -2687,7 +2738,7 @@ main () {
   exit (u.c[sizeof (long) - 1] == 1);
 }
 EOF
-if { (eval echo configure:2691: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+if { (eval echo configure:2742: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
 then
   ac_cv_c_bigendian=no
 else
@@ -2712,20 +2763,20 @@ fi
 
 
 echo $ac_n "checking if ANSI prototypes work""... $ac_c" 1>&6
-echo "configure:2716: checking if ANSI prototypes work" >&5
+echo "configure:2767: checking if ANSI prototypes work" >&5
 if eval "test \"`echo '$''{'ac_cv_have_ansi_prototypes'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   
   cat > conftest.$ac_ext <<EOF
-#line 2722 "configure"
+#line 2773 "configure"
 #include "confdefs.h"
 int foo(char *); int foo (char *bar) {return 1;}
 int main() {
 foo("bar")
 ; return 0; }
 EOF
-if { (eval echo configure:2729: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+if { (eval echo configure:2780: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
   rm -rf conftest*
   ac_cv_have_ansi_prototypes="yes"
 else
@@ -2747,13 +2798,13 @@ EOF
 fi
 
 echo $ac_n "checking for tm->tm_gmtoff""... $ac_c" 1>&6
-echo "configure:2751: checking for tm->tm_gmtoff" >&5
+echo "configure:2802: checking for tm->tm_gmtoff" >&5
 if eval "test \"`echo '$''{'ac_cv_have_tm_gmoff'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   
   cat > conftest.$ac_ext <<EOF
-#line 2757 "configure"
+#line 2808 "configure"
 #include "confdefs.h"
 #include <time.h>
 #include <sys/time.h>
@@ -2762,7 +2813,7 @@ struct tm foo;
       foo.tm_gmtoff = 0;
 ; return 0; }
 EOF
-if { (eval echo configure:2766: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+if { (eval echo configure:2817: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
   rm -rf conftest*
   ac_cv_have_tm_gmoff="yes"
 else
@@ -2784,13 +2835,13 @@ EOF
 fi
 
 echo $ac_n "checking for struct mallinfo""... $ac_c" 1>&6
-echo "configure:2788: checking for struct mallinfo" >&5
+echo "configure:2839: checking for struct mallinfo" >&5
 if eval "test \"`echo '$''{'ac_cv_have_struct_mallinfo'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   
   cat > conftest.$ac_ext <<EOF
-#line 2794 "configure"
+#line 2845 "configure"
 #include "confdefs.h"
 #include <sys/types.h>
 #if HAVE_MALLOC_H
@@ -2808,7 +2859,7 @@ struct mallinfo foo;
     foo.keepcost = 0;
 ; return 0; }
 EOF
-if { (eval echo configure:2812: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+if { (eval echo configure:2863: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
   rm -rf conftest*
   ac_cv_have_struct_mallinfo="yes"
 else
@@ -2830,13 +2881,13 @@ EOF
 fi
 
 echo $ac_n "checking for extended mallinfo""... $ac_c" 1>&6
-echo "configure:2834: checking for extended mallinfo" >&5
+echo "configure:2885: checking for extended mallinfo" >&5
 if eval "test \"`echo '$''{'ac_cv_have_ext_mallinfo'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   
   cat > conftest.$ac_ext <<EOF
-#line 2840 "configure"
+#line 2891 "configure"
 #include "confdefs.h"
 #include <sys/types.h>
 #include <malloc.h>
@@ -2845,7 +2896,7 @@ struct mallinfo foo;
       foo.mxfast = 0;
 ; return 0; }
 EOF
-if { (eval echo configure:2849: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+if { (eval echo configure:2900: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
   rm -rf conftest*
   ac_cv_have_ext_mallinfo="yes"
 else
@@ -2867,13 +2918,13 @@ EOF
 fi
 
 echo $ac_n "checking for struct rusage""... $ac_c" 1>&6
-echo "configure:2871: checking for struct rusage" >&5
+echo "configure:2922: checking for struct rusage" >&5
 if eval "test \"`echo '$''{'ac_cv_have_struct_rusage'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   
   cat > conftest.$ac_ext <<EOF
-#line 2877 "configure"
+#line 2928 "configure"
 #include "confdefs.h"
 
 #if HAVE_SYS_TIME_H
@@ -2886,7 +2937,7 @@ int main() {
 struct rusage R;
 ; return 0; }
 EOF
-if { (eval echo configure:2890: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+if { (eval echo configure:2941: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
   rm -rf conftest*
   ac_cv_have_struct_rusage="yes"
 else
@@ -2908,13 +2959,13 @@ EOF
 fi
 
 echo $ac_n "checking for ip->ip_hl""... $ac_c" 1>&6
-echo "configure:2912: checking for ip->ip_hl" >&5
+echo "configure:2963: checking for ip->ip_hl" >&5
 if eval "test \"`echo '$''{'ac_cv_have_ip_hl'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   
   cat > conftest.$ac_ext <<EOF
-#line 2918 "configure"
+#line 2969 "configure"
 #include "confdefs.h"
 #include <sys/types.h>
 #include <netinet/in.h>
@@ -2931,7 +2982,7 @@ struct iphdr ip;
       ip.ip_hl= 0;
 ; return 0; }
 EOF
-if { (eval echo configure:2935: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+if { (eval echo configure:2986: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
   rm -rf conftest*
   ac_cv_have_ip_hl="yes"
 else
@@ -2953,7 +3004,7 @@ EOF
 fi
 
 echo $ac_n "checking size of int""... $ac_c" 1>&6
-echo "configure:2957: checking size of int" >&5
+echo "configure:3008: checking size of int" >&5
 if eval "test \"`echo '$''{'ac_cv_sizeof_int'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
@@ -2961,7 +3012,7 @@ else
     { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; }
 else
   cat > conftest.$ac_ext <<EOF
-#line 2965 "configure"
+#line 3016 "configure"
 #include "confdefs.h"
 #include <stdio.h>
 main()
@@ -2972,7 +3023,7 @@ main()
   exit(0);
 }
 EOF
-if { (eval echo configure:2976: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+if { (eval echo configure:3027: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
 then
   ac_cv_sizeof_int=`cat conftestval`
 else
@@ -2992,7 +3043,7 @@ EOF
 
 
 echo $ac_n "checking size of long""... $ac_c" 1>&6
-echo "configure:2996: checking size of long" >&5
+echo "configure:3047: checking size of long" >&5
 if eval "test \"`echo '$''{'ac_cv_sizeof_long'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
@@ -3000,7 +3051,7 @@ else
     { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; }
 else
   cat > conftest.$ac_ext <<EOF
-#line 3004 "configure"
+#line 3055 "configure"
 #include "confdefs.h"
 #include <stdio.h>
 main()
@@ -3011,7 +3062,7 @@ main()
   exit(0);
 }
 EOF
-if { (eval echo configure:3015: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+if { (eval echo configure:3066: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
 then
   ac_cv_sizeof_long=`cat conftestval`
 else
@@ -3031,7 +3082,7 @@ EOF
 
 
 echo $ac_n "checking size of void *""... $ac_c" 1>&6
-echo "configure:3035: checking size of void *" >&5
+echo "configure:3086: checking size of void *" >&5
 if eval "test \"`echo '$''{'ac_cv_sizeof_void_p'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
@@ -3039,7 +3090,7 @@ else
     { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; }
 else
   cat > conftest.$ac_ext <<EOF
-#line 3043 "configure"
+#line 3094 "configure"
 #include "confdefs.h"
 #include <stdio.h>
 main()
@@ -3050,7 +3101,7 @@ main()
   exit(0);
 }
 EOF
-if { (eval echo configure:3054: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+if { (eval echo configure:3105: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
 then
   ac_cv_sizeof_void_p=`cat conftestval`
 else
@@ -3073,19 +3124,19 @@ EOF
 # The Ultrix 4.2 mips builtin alloca declared by alloca.h only works
 # for constant arguments.  Useless!
 echo $ac_n "checking for working alloca.h""... $ac_c" 1>&6
-echo "configure:3077: checking for working alloca.h" >&5
+echo "configure:3128: checking for working alloca.h" >&5
 if eval "test \"`echo '$''{'ac_cv_header_alloca_h'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 3082 "configure"
+#line 3133 "configure"
 #include "confdefs.h"
 #include <alloca.h>
 int main() {
 char *p = alloca(2 * sizeof(int));
 ; return 0; }
 EOF
-if { (eval echo configure:3089: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+if { (eval echo configure:3140: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
   rm -rf conftest*
   ac_cv_header_alloca_h=yes
 else
@@ -3106,12 +3157,12 @@ EOF
 fi
 
 echo $ac_n "checking for alloca""... $ac_c" 1>&6
-echo "configure:3110: checking for alloca" >&5
+echo "configure:3161: checking for alloca" >&5
 if eval "test \"`echo '$''{'ac_cv_func_alloca_works'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 3115 "configure"
+#line 3166 "configure"
 #include "confdefs.h"
 
 #ifdef __GNUC__
@@ -3139,7 +3190,7 @@ int main() {
 char *p = (char *) alloca(1);
 ; return 0; }
 EOF
-if { (eval echo configure:3143: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+if { (eval echo configure:3194: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
   rm -rf conftest*
   ac_cv_func_alloca_works=yes
 else
@@ -3171,12 +3222,12 @@ EOF
 
 
 echo $ac_n "checking whether alloca needs Cray hooks""... $ac_c" 1>&6
-echo "configure:3175: checking whether alloca needs Cray hooks" >&5
+echo "configure:3226: checking whether alloca needs Cray hooks" >&5
 if eval "test \"`echo '$''{'ac_cv_os_cray'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 3180 "configure"
+#line 3231 "configure"
 #include "confdefs.h"
 #if defined(CRAY) && ! defined(CRAY2)
 webecray
@@ -3201,12 +3252,12 @@ echo "$ac_t""$ac_cv_os_cray" 1>&6
 if test $ac_cv_os_cray = yes; then
 for ac_func in _getb67 GETB67 getb67; do
   echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
-echo "configure:3205: checking for $ac_func" >&5
+echo "configure:3256: checking for $ac_func" >&5
 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 3210 "configure"
+#line 3261 "configure"
 #include "confdefs.h"
 /* System header to define __stub macros and hopefully few prototypes,
     which can conflict with char $ac_func(); below.  */
@@ -3229,7 +3280,7 @@ $ac_func();
 
 ; return 0; }
 EOF
-if { (eval echo configure:3233: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+if { (eval echo configure:3284: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
   rm -rf conftest*
   eval "ac_cv_func_$ac_func=yes"
 else
@@ -3256,7 +3307,7 @@ done
 fi
 
 echo $ac_n "checking stack direction for C alloca""... $ac_c" 1>&6
-echo "configure:3260: checking stack direction for C alloca" >&5
+echo "configure:3311: checking stack direction for C alloca" >&5
 if eval "test \"`echo '$''{'ac_cv_c_stack_direction'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
@@ -3264,7 +3315,7 @@ else
   ac_cv_c_stack_direction=0
 else
   cat > conftest.$ac_ext <<EOF
-#line 3268 "configure"
+#line 3319 "configure"
 #include "confdefs.h"
 find_stack_direction ()
 {
@@ -3283,7 +3334,7 @@ main ()
   exit (find_stack_direction() < 0);
 }
 EOF
-if { (eval echo configure:3287: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+if { (eval echo configure:3338: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
 then
   ac_cv_c_stack_direction=1
 else
@@ -3306,12 +3357,12 @@ fi
 
 
 echo $ac_n "checking for pid_t""... $ac_c" 1>&6
-echo "configure:3310: checking for pid_t" >&5
+echo "configure:3361: checking for pid_t" >&5
 if eval "test \"`echo '$''{'ac_cv_type_pid_t'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 3315 "configure"
+#line 3366 "configure"
 #include "confdefs.h"
 #include <sys/types.h>
 #if STDC_HEADERS
@@ -3339,12 +3390,12 @@ EOF
 fi
 
 echo $ac_n "checking for size_t""... $ac_c" 1>&6
-echo "configure:3343: checking for size_t" >&5
+echo "configure:3394: checking for size_t" >&5
 if eval "test \"`echo '$''{'ac_cv_type_size_t'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 3348 "configure"
+#line 3399 "configure"
 #include "confdefs.h"
 #include <sys/types.h>
 #if STDC_HEADERS
@@ -3372,12 +3423,12 @@ EOF
 fi
 
 echo $ac_n "checking for ssize_t""... $ac_c" 1>&6
-echo "configure:3376: checking for ssize_t" >&5
+echo "configure:3427: checking for ssize_t" >&5
 if eval "test \"`echo '$''{'ac_cv_type_ssize_t'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 3381 "configure"
+#line 3432 "configure"
 #include "confdefs.h"
 #include <sys/types.h>
 #if STDC_HEADERS
@@ -3405,12 +3456,12 @@ EOF
 fi
 
 echo $ac_n "checking for off_t""... $ac_c" 1>&6
-echo "configure:3409: checking for off_t" >&5
+echo "configure:3460: checking for off_t" >&5
 if eval "test \"`echo '$''{'ac_cv_type_off_t'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 3414 "configure"
+#line 3465 "configure"
 #include "confdefs.h"
 #include <sys/types.h>
 #if STDC_HEADERS
@@ -3438,12 +3489,12 @@ EOF
 fi
 
 echo $ac_n "checking for mode_t""... $ac_c" 1>&6
-echo "configure:3442: checking for mode_t" >&5
+echo "configure:3493: checking for mode_t" >&5
 if eval "test \"`echo '$''{'ac_cv_type_mode_t'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 3447 "configure"
+#line 3498 "configure"
 #include "confdefs.h"
 #include <sys/types.h>
 #if STDC_HEADERS
@@ -3471,12 +3522,12 @@ EOF
 fi
 
 echo $ac_n "checking for fd_mask""... $ac_c" 1>&6
-echo "configure:3475: checking for fd_mask" >&5
+echo "configure:3526: checking for fd_mask" >&5
 if eval "test \"`echo '$''{'ac_cv_type_fd_mask'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 3480 "configure"
+#line 3531 "configure"
 #include "confdefs.h"
 #include <sys/types.h>
 #if STDC_HEADERS
@@ -3504,12 +3555,12 @@ EOF
 fi
 
 echo $ac_n "checking for mtyp_t""... $ac_c" 1>&6
-echo "configure:3508: checking for mtyp_t" >&5
+echo "configure:3559: checking for mtyp_t" >&5
 if eval "test \"`echo '$''{'ac_cv_type_mtyp_t'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 3513 "configure"
+#line 3564 "configure"
 #include "confdefs.h"
 #include <sys/types.h>
 #if STDC_HEADERS
@@ -3538,13 +3589,13 @@ fi
 
 
 echo $ac_n "checking for socklen_t""... $ac_c" 1>&6
-echo "configure:3542: checking for socklen_t" >&5
+echo "configure:3593: checking for socklen_t" >&5
 if eval "test \"`echo '$''{'ac_cv_type_socklen_t'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   
   cat > conftest.$ac_ext <<EOF
-#line 3548 "configure"
+#line 3599 "configure"
 #include "confdefs.h"
 #include <sys/types.h>
 #include <sys/socket.h>
@@ -3575,7 +3626,7 @@ EOF
 fi
 
 echo $ac_n "checking for main in -lnsl""... $ac_c" 1>&6
-echo "configure:3579: checking for main in -lnsl" >&5
+echo "configure:3630: checking for main in -lnsl" >&5
 ac_lib_var=`echo nsl'_'main | sed 'y%./+-%__p_%'`
 if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
@@ -3583,14 +3634,14 @@ else
   ac_save_LIBS="$LIBS"
 LIBS="-lnsl  $LIBS"
 cat > conftest.$ac_ext <<EOF
-#line 3587 "configure"
+#line 3638 "configure"
 #include "confdefs.h"
 
 int main() {
 main()
 ; return 0; }
 EOF
-if { (eval echo configure:3594: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+if { (eval echo configure:3645: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
   rm -rf conftest*
   eval "ac_cv_lib_$ac_lib_var=yes"
 else
@@ -3618,7 +3669,7 @@ else
 fi
 
 echo $ac_n "checking for main in -lsocket""... $ac_c" 1>&6
-echo "configure:3622: checking for main in -lsocket" >&5
+echo "configure:3673: checking for main in -lsocket" >&5
 ac_lib_var=`echo socket'_'main | sed 'y%./+-%__p_%'`
 if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
@@ -3626,14 +3677,14 @@ else
   ac_save_LIBS="$LIBS"
 LIBS="-lsocket  $LIBS"
 cat > conftest.$ac_ext <<EOF
-#line 3630 "configure"
+#line 3681 "configure"
 #include "confdefs.h"
 
 int main() {
 main()
 ; return 0; }
 EOF
-if { (eval echo configure:3637: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+if { (eval echo configure:3688: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
   rm -rf conftest*
   eval "ac_cv_lib_$ac_lib_var=yes"
 else
@@ -3665,7 +3716,7 @@ if test "x$ac_cv_enabled_dlmalloc" = "xyes" ; then
   echo "skipping libmalloc check (--enable-dlmalloc specified)"
 else
   echo $ac_n "checking for main in -lgnumalloc""... $ac_c" 1>&6
-echo "configure:3669: checking for main in -lgnumalloc" >&5
+echo "configure:3720: checking for main in -lgnumalloc" >&5
 ac_lib_var=`echo gnumalloc'_'main | sed 'y%./+-%__p_%'`
 if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
@@ -3673,14 +3724,14 @@ else
   ac_save_LIBS="$LIBS"
 LIBS="-lgnumalloc  $LIBS"
 cat > conftest.$ac_ext <<EOF
-#line 3677 "configure"
+#line 3728 "configure"
 #include "confdefs.h"
 
 int main() {
 main()
 ; return 0; }
 EOF
-if { (eval echo configure:3684: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+if { (eval echo configure:3735: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
   rm -rf conftest*
   eval "ac_cv_lib_$ac_lib_var=yes"
 else
@@ -3723,7 +3774,7 @@ fi
                        *)
   
                                echo $ac_n "checking for main in -lmalloc""... $ac_c" 1>&6
-echo "configure:3727: checking for main in -lmalloc" >&5
+echo "configure:3778: checking for main in -lmalloc" >&5
 ac_lib_var=`echo malloc'_'main | sed 'y%./+-%__p_%'`
 if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
@@ -3731,14 +3782,14 @@ else
   ac_save_LIBS="$LIBS"
 LIBS="-lmalloc  $LIBS"
 cat > conftest.$ac_ext <<EOF
-#line 3735 "configure"
+#line 3786 "configure"
 #include "confdefs.h"
 
 int main() {
 main()
 ; return 0; }
 EOF
-if { (eval echo configure:3742: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+if { (eval echo configure:3793: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
   rm -rf conftest*
   eval "ac_cv_lib_$ac_lib_var=yes"
 else
@@ -3771,7 +3822,7 @@ fi
 fi
 
 echo $ac_n "checking for main in -lbsd""... $ac_c" 1>&6
-echo "configure:3775: checking for main in -lbsd" >&5
+echo "configure:3826: checking for main in -lbsd" >&5
 ac_lib_var=`echo bsd'_'main | sed 'y%./+-%__p_%'`
 if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
@@ -3779,14 +3830,14 @@ else
   ac_save_LIBS="$LIBS"
 LIBS="-lbsd  $LIBS"
 cat > conftest.$ac_ext <<EOF
-#line 3783 "configure"
+#line 3834 "configure"
 #include "confdefs.h"
 
 int main() {
 main()
 ; return 0; }
 EOF
-if { (eval echo configure:3790: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+if { (eval echo configure:3841: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
   rm -rf conftest*
   eval "ac_cv_lib_$ac_lib_var=yes"
 else
@@ -3814,7 +3865,7 @@ else
 fi
 
 echo $ac_n "checking for main in -lregex""... $ac_c" 1>&6
-echo "configure:3818: checking for main in -lregex" >&5
+echo "configure:3869: checking for main in -lregex" >&5
 ac_lib_var=`echo regex'_'main | sed 'y%./+-%__p_%'`
 if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
@@ -3822,14 +3873,14 @@ else
   ac_save_LIBS="$LIBS"
 LIBS="-lregex  $LIBS"
 cat > conftest.$ac_ext <<EOF
-#line 3826 "configure"
+#line 3877 "configure"
 #include "confdefs.h"
 
 int main() {
 main()
 ; return 0; }
 EOF
-if { (eval echo configure:3833: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+if { (eval echo configure:3884: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
   rm -rf conftest*
   eval "ac_cv_lib_$ac_lib_var=yes"
 else
@@ -3850,7 +3901,7 @@ else
 fi
 
 echo $ac_n "checking for gethostbyname in -lbind""... $ac_c" 1>&6
-echo "configure:3854: checking for gethostbyname in -lbind" >&5
+echo "configure:3905: checking for gethostbyname in -lbind" >&5
 ac_lib_var=`echo bind'_'gethostbyname | sed 'y%./+-%__p_%'`
 if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
@@ -3858,7 +3909,7 @@ else
   ac_save_LIBS="$LIBS"
 LIBS="-lbind  $LIBS"
 cat > conftest.$ac_ext <<EOF
-#line 3862 "configure"
+#line 3913 "configure"
 #include "confdefs.h"
 /* Override any gcc2 internal prototype to avoid an error.  */
 /* We use char because int might match the return type of a gcc2
@@ -3869,7 +3920,7 @@ int main() {
 gethostbyname()
 ; return 0; }
 EOF
-if { (eval echo configure:3873: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+if { (eval echo configure:3924: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
   rm -rf conftest*
   eval "ac_cv_lib_$ac_lib_var=yes"
 else
@@ -3903,7 +3954,7 @@ if test $ac_cv_lib_bind_gethostbyname = "no" ; then
                ;;
        *)
                echo $ac_n "checking for inet_aton in -lresolv""... $ac_c" 1>&6
-echo "configure:3907: checking for inet_aton in -lresolv" >&5
+echo "configure:3958: checking for inet_aton in -lresolv" >&5
 ac_lib_var=`echo resolv'_'inet_aton | sed 'y%./+-%__p_%'`
 if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
@@ -3911,7 +3962,7 @@ else
   ac_save_LIBS="$LIBS"
 LIBS="-lresolv  $LIBS"
 cat > conftest.$ac_ext <<EOF
-#line 3915 "configure"
+#line 3966 "configure"
 #include "confdefs.h"
 /* Override any gcc2 internal prototype to avoid an error.  */
 /* We use char because int might match the return type of a gcc2
@@ -3922,7 +3973,7 @@ int main() {
 inet_aton()
 ; return 0; }
 EOF
-if { (eval echo configure:3926: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+if { (eval echo configure:3977: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
   rm -rf conftest*
   eval "ac_cv_lib_$ac_lib_var=yes"
 else
@@ -3938,7 +3989,7 @@ fi
 if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
   echo "$ac_t""yes" 1>&6
   echo $ac_n "checking for inet_aton in -l44bsd""... $ac_c" 1>&6
-echo "configure:3942: checking for inet_aton in -l44bsd" >&5
+echo "configure:3993: checking for inet_aton in -l44bsd" >&5
 ac_lib_var=`echo 44bsd'_'inet_aton | sed 'y%./+-%__p_%'`
 if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
@@ -3946,7 +3997,7 @@ else
   ac_save_LIBS="$LIBS"
 LIBS="-l44bsd  $LIBS"
 cat > conftest.$ac_ext <<EOF
-#line 3950 "configure"
+#line 4001 "configure"
 #include "confdefs.h"
 /* Override any gcc2 internal prototype to avoid an error.  */
 /* We use char because int might match the return type of a gcc2
@@ -3957,7 +4008,7 @@ int main() {
 inet_aton()
 ; return 0; }
 EOF
-if { (eval echo configure:3961: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+if { (eval echo configure:4012: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
   rm -rf conftest*
   eval "ac_cv_lib_$ac_lib_var=yes"
 else
@@ -3989,7 +4040,7 @@ else
 fi
 
                echo $ac_n "checking for main in -lresolv""... $ac_c" 1>&6
-echo "configure:3993: checking for main in -lresolv" >&5
+echo "configure:4044: checking for main in -lresolv" >&5
 ac_lib_var=`echo resolv'_'main | sed 'y%./+-%__p_%'`
 if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
@@ -3997,14 +4048,14 @@ else
   ac_save_LIBS="$LIBS"
 LIBS="-lresolv  $LIBS"
 cat > conftest.$ac_ext <<EOF
-#line 4001 "configure"
+#line 4052 "configure"
 #include "confdefs.h"
 
 int main() {
 main()
 ; return 0; }
 EOF
-if { (eval echo configure:4008: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+if { (eval echo configure:4059: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
   rm -rf conftest*
   eval "ac_cv_lib_$ac_lib_var=yes"
 else
@@ -4035,7 +4086,7 @@ fi
     esac
 fi
 echo $ac_n "checking for main in -lm""... $ac_c" 1>&6
-echo "configure:4039: checking for main in -lm" >&5
+echo "configure:4090: checking for main in -lm" >&5
 ac_lib_var=`echo m'_'main | sed 'y%./+-%__p_%'`
 if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
@@ -4043,14 +4094,14 @@ else
   ac_save_LIBS="$LIBS"
 LIBS="-lm  $LIBS"
 cat > conftest.$ac_ext <<EOF
-#line 4047 "configure"
+#line 4098 "configure"
 #include "confdefs.h"
 
 int main() {
 main()
 ; return 0; }
 EOF
-if { (eval echo configure:4054: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+if { (eval echo configure:4105: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
   rm -rf conftest*
   eval "ac_cv_lib_$ac_lib_var=yes"
 else
@@ -4079,7 +4130,7 @@ fi
 
 
 echo $ac_n "checking for crypt in -lcrypt""... $ac_c" 1>&6
-echo "configure:4083: checking for crypt in -lcrypt" >&5
+echo "configure:4134: checking for crypt in -lcrypt" >&5
 ac_lib_var=`echo crypt'_'crypt | sed 'y%./+-%__p_%'`
 if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
@@ -4087,7 +4138,7 @@ else
   ac_save_LIBS="$LIBS"
 LIBS="-lcrypt  $LIBS"
 cat > conftest.$ac_ext <<EOF
-#line 4091 "configure"
+#line 4142 "configure"
 #include "confdefs.h"
 /* Override any gcc2 internal prototype to avoid an error.  */
 /* We use char because int might match the return type of a gcc2
@@ -4098,7 +4149,7 @@ int main() {
 crypt()
 ; return 0; }
 EOF
-if { (eval echo configure:4102: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+if { (eval echo configure:4153: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
   rm -rf conftest*
   eval "ac_cv_lib_$ac_lib_var=yes"
 else
@@ -4131,7 +4182,7 @@ fi
 
 
 echo $ac_n "checking for main in -lpthread""... $ac_c" 1>&6
-echo "configure:4135: checking for main in -lpthread" >&5
+echo "configure:4186: checking for main in -lpthread" >&5
 ac_lib_var=`echo pthread'_'main | sed 'y%./+-%__p_%'`
 if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
@@ -4139,14 +4190,14 @@ else
   ac_save_LIBS="$LIBS"
 LIBS="-lpthread  $LIBS"
 cat > conftest.$ac_ext <<EOF
-#line 4143 "configure"
+#line 4194 "configure"
 #include "confdefs.h"
 
 int main() {
 main()
 ; return 0; }
 EOF
-if { (eval echo configure:4150: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+if { (eval echo configure:4201: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
   rm -rf conftest*
   eval "ac_cv_lib_$ac_lib_var=yes"
 else
@@ -4181,7 +4232,7 @@ fi
 case "$host" in
        *-pc-sco3.2*)
                echo $ac_n "checking for strftime in -lintl""... $ac_c" 1>&6
-echo "configure:4185: checking for strftime in -lintl" >&5
+echo "configure:4236: checking for strftime in -lintl" >&5
 ac_lib_var=`echo intl'_'strftime | sed 'y%./+-%__p_%'`
 if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
@@ -4189,7 +4240,7 @@ else
   ac_save_LIBS="$LIBS"
 LIBS="-lintl  $LIBS"
 cat > conftest.$ac_ext <<EOF
-#line 4193 "configure"
+#line 4244 "configure"
 #include "confdefs.h"
 /* Override any gcc2 internal prototype to avoid an error.  */
 /* We use char because int might match the return type of a gcc2
@@ -4200,7 +4251,7 @@ int main() {
 strftime()
 ; return 0; }
 EOF
-if { (eval echo configure:4204: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+if { (eval echo configure:4255: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
   rm -rf conftest*
   eval "ac_cv_lib_$ac_lib_var=yes"
 else
@@ -4340,6 +4391,9 @@ for ac_func in \
        mktime \
        mstats \
        poll \
+       pthread_attr_setscope \
+       pthread_setschedparam \
+       pthread_attr_setschedparam \
        putenv \
        random \
        regcomp \
@@ -4365,12 +4419,12 @@ for ac_func in \
 
 do
 echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
-echo "configure:4369: checking for $ac_func" >&5
+echo "configure:4423: checking for $ac_func" >&5
 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 4374 "configure"
+#line 4428 "configure"
 #include "confdefs.h"
 /* System header to define __stub macros and hopefully few prototypes,
     which can conflict with char $ac_func(); below.  */
@@ -4393,7 +4447,7 @@ $ac_func();
 
 ; return 0; }
 EOF
-if { (eval echo configure:4397: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+if { (eval echo configure:4451: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
   rm -rf conftest*
   eval "ac_cv_func_$ac_func=yes"
 else
@@ -4418,70 +4472,8 @@ fi
 done
 
 
-if test "$async_io" = "yes" ; then
-       for ac_func in \
-               pthread_attr_setscope \
-               pthread_setschedparam \
-               pthread_attr_setschedparam \
-       
-do
-echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
-echo "configure:4430: checking for $ac_func" >&5
-if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
-  echo $ac_n "(cached) $ac_c" 1>&6
-else
-  cat > conftest.$ac_ext <<EOF
-#line 4435 "configure"
-#include "confdefs.h"
-/* System header to define __stub macros and hopefully few prototypes,
-    which can conflict with char $ac_func(); below.  */
-#include <assert.h>
-/* Override any gcc2 internal prototype to avoid an error.  */
-/* We use char because int might match the return type of a gcc2
-    builtin and then its argument prototype would still apply.  */
-char $ac_func();
-
-int main() {
-
-/* The GNU C library defines this for functions which it implements
-    to always fail with ENOSYS.  Some functions are actually named
-    something starting with __ and the normal name is an alias.  */
-#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
-choke me
-#else
-$ac_func();
-#endif
-
-; return 0; }
-EOF
-if { (eval echo configure:4458: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
-  rm -rf conftest*
-  eval "ac_cv_func_$ac_func=yes"
-else
-  echo "configure: failed program was:" >&5
-  cat conftest.$ac_ext >&5
-  rm -rf conftest*
-  eval "ac_cv_func_$ac_func=no"
-fi
-rm -f conftest*
-fi
-
-if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then
-  echo "$ac_t""yes" 1>&6
-    ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'`
-  cat >> confdefs.h <<EOF
-#define $ac_tr_func 1
-EOF
-else
-  echo "$ac_t""no" 1>&6
-fi
-done
-
-fi
-
 echo $ac_n "checking if setresuid is implemented""... $ac_c" 1>&6
-echo "configure:4485: checking if setresuid is implemented" >&5
+echo "configure:4477: checking if setresuid is implemented" >&5
 if eval "test \"`echo '$''{'ac_cv_func_setresuid'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
@@ -4489,7 +4481,7 @@ else
     { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; }
 else
   cat > conftest.$ac_ext <<EOF
-#line 4493 "configure"
+#line 4485 "configure"
 #include "confdefs.h"
 
 #include <stdlib.h>
@@ -4502,7 +4494,7 @@ else
   }
   
 EOF
-if { (eval echo configure:4506: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+if { (eval echo configure:4498: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
 then
   ac_cv_func_setresuid="yes"
 else
@@ -4527,7 +4519,7 @@ fi
 
 if test "$IPF_TRANSPARENT" ; then
     echo $ac_n "checking if IP-Filter header files are installed""... $ac_c" 1>&6
-echo "configure:4531: checking if IP-Filter header files are installed" >&5
+echo "configure:4523: checking if IP-Filter header files are installed" >&5
     if test "$ac_cv_header_ip_compat_h" = "yes" &&
        test "$ac_cv_header_ip_fil_h" = "yes" &&
        test "$ac_cv_header_ip_nat_h" = "yes" ; then
@@ -4570,13 +4562,13 @@ if test -z "$USE_GNUREGEX" ; then
     esac
 fi
 echo $ac_n "checking if GNUregex needs to be compiled""... $ac_c" 1>&6
-echo "configure:4574: checking if GNUregex needs to be compiled" >&5
+echo "configure:4566: checking if GNUregex needs to be compiled" >&5
 if test -z "$USE_GNUREGEX"; then
 if test "$ac_cv_func_regcomp" = "no" || test "$USE_GNUREGEX" = "yes" ; then
        USE_GNUREGEX="yes"
 else
        cat > conftest.$ac_ext <<EOF
-#line 4580 "configure"
+#line 4572 "configure"
 #include "confdefs.h"
 #include <sys/types.h>
 #include <regex.h>
@@ -4584,7 +4576,7 @@ int main() {
 regex_t t; regcomp(&t,"",0);
 ; return 0; }
 EOF
-if { (eval echo configure:4588: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+if { (eval echo configure:4580: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
   rm -rf conftest*
   USE_GNUREGEX="no"
 else
@@ -4615,12 +4607,12 @@ for ac_func in \
 
 do
 echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
-echo "configure:4619: checking for $ac_func" >&5
+echo "configure:4611: checking for $ac_func" >&5
 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 4624 "configure"
+#line 4616 "configure"
 #include "confdefs.h"
 /* System header to define __stub macros and hopefully few prototypes,
     which can conflict with char $ac_func(); below.  */
@@ -4643,7 +4635,7 @@ $ac_func();
 
 ; return 0; }
 EOF
-if { (eval echo configure:4647: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+if { (eval echo configure:4639: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
   rm -rf conftest*
   eval "ac_cv_func_$ac_func=yes"
 else
@@ -4671,12 +4663,12 @@ done
 
 
 echo $ac_n "checking Default FD_SETSIZE value""... $ac_c" 1>&6
-echo "configure:4675: checking Default FD_SETSIZE value" >&5
+echo "configure:4667: checking Default FD_SETSIZE value" >&5
 if test "$cross_compiling" = yes; then
   DEFAULT_FD_SETSIZE=256
 else
   cat > conftest.$ac_ext <<EOF
-#line 4680 "configure"
+#line 4672 "configure"
 #include "confdefs.h"
 
 #if HAVE_STDIO_H
@@ -4701,7 +4693,7 @@ main() {
 }
 
 EOF
-if { (eval echo configure:4705: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+if { (eval echo configure:4697: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
 then
   DEFAULT_FD_SETSIZE=`cat conftestval`
 else
@@ -4720,7 +4712,7 @@ EOF
 
 
 echo $ac_n "checking Maximum number of filedescriptors we can open""... $ac_c" 1>&6
-echo "configure:4724: checking Maximum number of filedescriptors we can open" >&5
+echo "configure:4716: checking Maximum number of filedescriptors we can open" >&5
 TLDFLAGS="$LDFLAGS"
 case $host in
 i386-unknown-freebsd*)
@@ -4732,7 +4724,7 @@ if test "$cross_compiling" = yes; then
   SQUID_MAXFD=256
 else
   cat > conftest.$ac_ext <<EOF
-#line 4736 "configure"
+#line 4728 "configure"
 #include "confdefs.h"
 
 #include <stdio.h>
@@ -4789,7 +4781,7 @@ main() {
 }
 
 EOF
-if { (eval echo configure:4793: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+if { (eval echo configure:4785: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
 then
   SQUID_MAXFD=`cat conftestval`
 else
@@ -4816,12 +4808,12 @@ fi
 LDFLAGS="$TLDFLAGS"
 
 echo $ac_n "checking Default UDP send buffer size""... $ac_c" 1>&6
-echo "configure:4820: checking Default UDP send buffer size" >&5
+echo "configure:4812: checking Default UDP send buffer size" >&5
 if test "$cross_compiling" = yes; then
   SQUID_UDP_SO_SNDBUF=16384
 else
   cat > conftest.$ac_ext <<EOF
-#line 4825 "configure"
+#line 4817 "configure"
 #include "confdefs.h"
 
 #include <stdlib.h>
@@ -4842,7 +4834,7 @@ main ()
 }
 
 EOF
-if { (eval echo configure:4846: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+if { (eval echo configure:4838: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
 then
   SQUID_UDP_SO_SNDBUF=`cat conftestval`
 else
@@ -4861,12 +4853,12 @@ EOF
 
 
 echo $ac_n "checking Default UDP receive buffer size""... $ac_c" 1>&6
-echo "configure:4865: checking Default UDP receive buffer size" >&5
+echo "configure:4857: checking Default UDP receive buffer size" >&5
 if test "$cross_compiling" = yes; then
   SQUID_UDP_SO_RCVBUF=16384
 else
   cat > conftest.$ac_ext <<EOF
-#line 4870 "configure"
+#line 4862 "configure"
 #include "confdefs.h"
 
 #include <stdlib.h>
@@ -4887,7 +4879,7 @@ main ()
 }
 
 EOF
-if { (eval echo configure:4891: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+if { (eval echo configure:4883: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
 then
   SQUID_UDP_SO_RCVBUF=`cat conftestval`
 else
@@ -4906,12 +4898,12 @@ EOF
 
 
 echo $ac_n "checking Default TCP send buffer size""... $ac_c" 1>&6
-echo "configure:4910: checking Default TCP send buffer size" >&5
+echo "configure:4902: checking Default TCP send buffer size" >&5
 if test "$cross_compiling" = yes; then
   SQUID_TCP_SO_SNDBUF=16384
 else
   cat > conftest.$ac_ext <<EOF
-#line 4915 "configure"
+#line 4907 "configure"
 #include "confdefs.h"
 
 #include <stdlib.h>
@@ -4932,7 +4924,7 @@ main ()
 }
 
 EOF
-if { (eval echo configure:4936: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+if { (eval echo configure:4928: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
 then
   SQUID_TCP_SO_SNDBUF=`cat conftestval`
 else
@@ -4951,12 +4943,12 @@ EOF
 
 
 echo $ac_n "checking Default TCP receive buffer size""... $ac_c" 1>&6
-echo "configure:4955: checking Default TCP receive buffer size" >&5
+echo "configure:4947: checking Default TCP receive buffer size" >&5
 if test "$cross_compiling" = yes; then
   SQUID_TCP_SO_RCVBUF=16384
 else
   cat > conftest.$ac_ext <<EOF
-#line 4960 "configure"
+#line 4952 "configure"
 #include "confdefs.h"
 
 #include <stdlib.h>
@@ -4977,7 +4969,7 @@ main ()
 }
 
 EOF
-if { (eval echo configure:4981: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+if { (eval echo configure:4973: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
 then
   SQUID_TCP_SO_RCVBUF=`cat conftestval`
 else
@@ -4996,19 +4988,19 @@ EOF
 
 
 echo $ac_n "checking if sys_errlist is already defined""... $ac_c" 1>&6
-echo "configure:5000: checking if sys_errlist is already defined" >&5
+echo "configure:4992: checking if sys_errlist is already defined" >&5
 if eval "test \"`echo '$''{'ac_cv_needs_sys_errlist'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 5005 "configure"
+#line 4997 "configure"
 #include "confdefs.h"
 #include <stdio.h>
 int main() {
 char *s = sys_errlist;
 ; return 0; }
 EOF
-if { (eval echo configure:5012: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+if { (eval echo configure:5004: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
   rm -rf conftest*
   ac_cv_needs_sys_errlist="no"
 else
@@ -5030,16 +5022,16 @@ EOF
 fi
 
 echo $ac_n "checking for libresolv _dns_ttl_ hack""... $ac_c" 1>&6
-echo "configure:5034: checking for libresolv _dns_ttl_ hack" >&5
+echo "configure:5026: checking for libresolv _dns_ttl_ hack" >&5
 cat > conftest.$ac_ext <<EOF
-#line 5036 "configure"
+#line 5028 "configure"
 #include "confdefs.h"
 extern int _dns_ttl_;
 int main() {
 return _dns_ttl_;
 ; return 0; }
 EOF
-if { (eval echo configure:5043: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+if { (eval echo configure:5035: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
   rm -rf conftest*
   echo "$ac_t""yes" 1>&6
 cat >> confdefs.h <<\EOF
@@ -5055,12 +5047,12 @@ fi
 rm -f conftest*
 
 echo $ac_n "checking if inet_ntoa() actually works""... $ac_c" 1>&6
-echo "configure:5059: checking if inet_ntoa() actually works" >&5
+echo "configure:5051: checking if inet_ntoa() actually works" >&5
 if test "$cross_compiling" = yes; then
   INET_NTOA_RESULT="broken"
 else
   cat > conftest.$ac_ext <<EOF
-#line 5064 "configure"
+#line 5056 "configure"
 #include "confdefs.h"
 
 #include <stdlib.h>
@@ -5079,7 +5071,7 @@ main ()
 }
 
 EOF
-if { (eval echo configure:5083: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+if { (eval echo configure:5075: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
 then
   INET_NTOA_RESULT=`cat conftestval`
 else
@@ -5105,9 +5097,9 @@ fi
 
 if test "$ac_cv_header_sys_statvfs_h" = "yes" ; then
 echo $ac_n "checking for working statvfs() interface""... $ac_c" 1>&6
-echo "configure:5109: checking for working statvfs() interface" >&5
+echo "configure:5101: checking for working statvfs() interface" >&5
 cat > conftest.$ac_ext <<EOF
-#line 5111 "configure"
+#line 5103 "configure"
 #include "confdefs.h"
 
 #include <stdlib.h>
@@ -5124,7 +5116,7 @@ statvfs("/tmp", &sfs);
 
 ; return 0; }
 EOF
-if { (eval echo configure:5128: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+if { (eval echo configure:5120: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
   rm -rf conftest*
   ac_cv_func_statvfs=yes
 else
@@ -5144,12 +5136,12 @@ fi
 fi
 
 echo $ac_n "checking for _res.nsaddr_list""... $ac_c" 1>&6
-echo "configure:5148: checking for _res.nsaddr_list" >&5
+echo "configure:5140: checking for _res.nsaddr_list" >&5
 if eval "test \"`echo '$''{'ac_cv_have_res_nsaddr_list'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 5153 "configure"
+#line 5145 "configure"
 #include "confdefs.h"
 
 #if HAVE_SYS_TYPES_H
@@ -5172,7 +5164,7 @@ int main() {
 _res.nsaddr_list[0];
 ; return 0; }
 EOF
-if { (eval echo configure:5176: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+if { (eval echo configure:5168: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
   rm -rf conftest*
   ac_cv_have_res_nsaddr_list="yes"
 else
@@ -5194,12 +5186,12 @@ fi
 
 if test $ac_cv_have_res_nsaddr_list = "no" ; then
 echo $ac_n "checking for _res.ns_list""... $ac_c" 1>&6
-echo "configure:5198: checking for _res.ns_list" >&5
+echo "configure:5190: checking for _res.ns_list" >&5
 if eval "test \"`echo '$''{'ac_cv_have_res_ns_list'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 5203 "configure"
+#line 5195 "configure"
 #include "confdefs.h"
 
 #if HAVE_SYS_TYPES_H
@@ -5222,7 +5214,7 @@ int main() {
 _res.ns_list[0].addr;
 ; return 0; }
 EOF
-if { (eval echo configure:5226: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+if { (eval echo configure:5218: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
   rm -rf conftest*
   ac_cv_have_res_ns_list="yes"
 else
@@ -5375,6 +5367,11 @@ trap 'rm -fr `echo "\
        ./scripts/RunCache \
        ./scripts/RunAccel \
        ./src/Makefile \
+       ./src/fs/Makefile \
+       ./src/fs/ufs/Makefile \
+       ./src/fs/aufs/Makefile \
+       ./src/fs/coss/Makefile \
+       ./src/fs/diskd/Makefile \
        ./contrib/Makefile \
        $SNMP_MAKEFILE \
        ./icons/Makefile \
@@ -5430,8 +5427,9 @@ s%@CACHE_ICP_PORT@%$CACHE_ICP_PORT%g
 s%@CC@%$CC%g
 s%@LIBDLMALLOC@%$LIBDLMALLOC%g
 s%@LIB_MALLOC@%$LIB_MALLOC%g
-s%@ASYNC_OBJS@%$ASYNC_OBJS%g
 s%@SQUID_PTHREAD_LIB@%$SQUID_PTHREAD_LIB%g
+s%@STORE_MODULES@%$STORE_MODULES%g
+s%@STORE_OBJS@%$STORE_OBJS%g
 s%@DELAY_OBJS@%$DELAY_OBJS%g
 s%@SNMPLIB@%$SNMPLIB%g
 s%@SNMP_OBJS@%$SNMP_OBJS%g
@@ -5440,8 +5438,6 @@ s%@HTCP_OBJS@%$HTCP_OBJS%g
 s%@ERR_LANGUAGE@%$ERR_LANGUAGE%g
 s%@LEAKFINDER_OBJS@%$LEAKFINDER_OBJS%g
 s%@AUTH_MODULES@%$AUTH_MODULES%g
-s%@OPT_DISKD_EXE@%$OPT_DISKD_EXE%g
-s%@DISKD_OBJS@%$DISKD_OBJS%g
 s%@CPP@%$CPP%g
 s%@INSTALL_PROGRAM@%$INSTALL_PROGRAM%g
 s%@INSTALL_SCRIPT@%$INSTALL_SCRIPT%g
@@ -5516,6 +5512,11 @@ CONFIG_FILES=\${CONFIG_FILES-"\
        ./scripts/RunCache \
        ./scripts/RunAccel \
        ./src/Makefile \
+       ./src/fs/Makefile \
+       ./src/fs/ufs/Makefile \
+       ./src/fs/aufs/Makefile \
+       ./src/fs/coss/Makefile \
+       ./src/fs/diskd/Makefile \
        ./contrib/Makefile \
        $SNMP_MAKEFILE \
        ./icons/Makefile \
index b67ccdabeb4a6c771d999339d80af20feb4ece74..9557ba3e1482a1097a090bc5af30c472247b8683 100644 (file)
@@ -3,13 +3,13 @@ dnl  Configuration input file for Squid
 dnl
 dnl  Duane Wessels, wessels@nlanr.net, February 1996 (autoconf v2.9)
 dnl
-dnl  $Id: configure.in,v 1.185 2000/05/02 19:22:00 hno Exp $
+dnl  $Id: configure.in,v 1.186 2000/05/03 17:15:39 adrian Exp $
 dnl
 dnl
 dnl
 AC_INIT(src/main.c)
 AC_CONFIG_HEADER(include/autoconf.h)
-AC_REVISION($Revision: 1.185 $)dnl
+AC_REVISION($Revision: 1.186 $)dnl
 AC_PREFIX_DEFAULT(/usr/local/squid)
 AC_CONFIG_AUX_DIR(cfgaux)
 
@@ -236,46 +236,90 @@ AC_ARG_ENABLE(carp,
   fi
 ])
 
-AC_ARG_ENABLE(async_io,
+AC_ARG_ENABLE(async-io,
 [  --enable-async-io[=N_THREADS]
-                          Do ASYNC disk I/O using threads.
-                          N_THREADS is the number of worker threads
-                          defaults to 16. See also src/squid.h for
-                          some additional platform tuning],
-[ case "$enableval" in
+                          Shorthand for
+                          --with-aio-threads=N_THREADS
+                          --with-pthreads
+                          --enable-storeio=ufs,aufs],
+[ case $enableval in
   yes)
-    async_io=yes
+       with_pthreads=yes
+       STORE_MODULES="ufs aufs"
     ;;
   no)
-    async_io=''
     ;;
   *)
-    async_io=yes
-    AC_DEFINE_UNQUOTED(NUMTHREADS, $enableval)
+       async_io_threads=$enableval
+       with_pthreads=yes
+       STORE_MODULES="ufs aufs"
     ;;
   esac
 ])
 
-if test -n "$async_io" ; then
-    echo "Async I/O enabled"
-    async_io=yes
-    AC_DEFINE(USE_ASYNC_IO)
-    ASYNC_OBJS='$(ASYNC_OBJS)'
+AC_ARG_WITH(aio-threads,
+[  --with-aio-threads=N_THREADS
+                          Tune the number of worker threads for the aufs object
+                          store.],
+[ async_io_threads=$withval ])
+if test "$async_io_threads"; then
+    echo "With $async_io_threads AIO threads"
+    with_pthreads=yes
+    AC_DEFINE_UNQUOTED(ASYNC_IO_THREADS,$async_io_threads)
+fi
+
+AC_ARG_WITH(pthreads,
+[  --with-pthreads         Use POSIX Threads],
+[ if test "$enableval" = "yes"; then
+    with_pthreads=yes
+  fi
+])
+if test "$with_pthreads"; then
+    echo "With pthreads"
     SQUID_PTHREAD_LIB='$(PTHREADLIB)'
     CFLAGS="$CFLAGS -D_REENTRANT"
     case "$host" in
     i386-unknown-freebsd*)
-       if test "$GCC" = "yes" ; then
-           if test -z "$PRESET_LDFLAGS"; then
-               LDFLAGS="$LDFLAGS -pthread"
-           fi
-       fi
-       ;;
+        if test "$GCC" = "yes" ; then
+            if test -z "$PRESET_LDFLAGS"; then
+                LDFLAGS="$LDFLAGS -pthread"
+            fi
+        fi
+    ;;
     esac
 fi
-AC_SUBST(ASYNC_OBJS)
 AC_SUBST(SQUID_PTHREAD_LIB)
 
+AC_ARG_ENABLE(storeio,
+[  --enable-storeio=\"list of modules\"
+                          Build support for the list of store I/O modules.
+                          The default is only to build the "ufs" module.
+                          See src/fs for a list of available modules, or
+                          Programmers Guide section <not yet written>
+                          for details on how to build your custom store module],
+[ case $enableval in
+  yes)
+       for module in $srcdir/src/fs/*; do
+           if test -f $module/Makefile.in; then
+               STORE_MODULES="$STORE_MODULES `basename $module`"
+           fi
+       done
+       ;;
+  no)
+       ;;
+  *)   STORE_MODULES="`echo $enableval| sed -e 's/,/ /g;s/  */ /g'`"
+       ;;
+  esac
+],
+[ if test -z "$STORE_MODULES"; then
+    STORE_MODULES="ufs"
+  fi
+])
+echo "Store moules built: $STORE_MODULES"
+AC_SUBST(STORE_MODULES)
+STORE_OBJS="fs/`echo $STORE_MODULES|sed -e's% %.a fs/%g'`.a"
+AC_SUBST(STORE_OBJS)
+
 AC_ARG_ENABLE(icmp,
 [  --enable-icmp           Enable ICMP pinging],
 [ if test "$enableval" = "yes" ; then
@@ -428,6 +472,15 @@ AC_ARG_ENABLE(err-language,
 ],[ERR_LANGUAGE="English"])
 AC_SUBST(ERR_LANGUAGE)
 
+dnl Size of COSS memory buffer
+AC_ARG_WITH(coss-membuf-size,
+[  --with-coss-membuf-size COSS membuf size (default 1048576 bytes) ],
+[  if test "$with_coss_membuf_size"; then
+      echo "Setting COSS membuf size to $with_coss_membuf_size bytes"
+      AC_DEFINE_UNQUOTED(COSS_MEMBUF_SZ, $with_coss_membuf_size)
+   fi
+])
+
 dnl Enable poll()
 AC_ARG_ENABLE(poll,
 [  --enable-poll           Enable poll() instead of select().  Normally poll
@@ -546,8 +599,7 @@ AC_ARG_ENABLE(heap-replacement,
 [  --enable-heap-replacement
                           This option allows you to use various cache
                           replacement algorithms, instead of the standard
-                          LRU algorithm.
-                          ],
+                          LRU algorithm.],
 [ if test "$enableval" = "yes" ; then
     echo "Enabling HEAP_REPLACEMENT"
     AC_DEFINE(HEAP_REPLACEMENT, 1)
@@ -581,20 +633,6 @@ if test -n "$AUTH_MODULES"; then
 fi
 AC_SUBST(AUTH_MODULES)
 
-dnl Enable "diskd" code
-AC_ARG_ENABLE(diskd,
-[  --enable-diskd          Uses shared memory, message queues, and external
-                          processes for disk I/O.],
-[ if test "$enableval" = "yes" ; then
-    echo "DISKD enabled"
-    AC_DEFINE(USE_DISKD, 1)
-    OPT_DISKD_EXE='$(DISKD_EXE)'
-    AC_SUBST(OPT_DISKD_EXE)
-    DISKD_OBJS='diskd.o'
-    AC_SUBST(DISKD_OBJS)
-  fi
-])
-
 # Force some compilers to use ANSI features
 #
 case "$host" in
@@ -1022,6 +1060,9 @@ AC_CHECK_FUNCS(\
        mktime \
        mstats \
        poll \
+       pthread_attr_setscope \
+       pthread_setschedparam \
+       pthread_attr_setschedparam \
        putenv \
        random \
        regcomp \
@@ -1046,14 +1087,6 @@ AC_CHECK_FUNCS(\
        vsnprintf \
 )
 
-if test "$async_io" = "yes" ; then
-       AC_CHECK_FUNCS(\
-               pthread_attr_setscope \
-               pthread_setschedparam \
-               pthread_attr_setschedparam \
-       )
-fi
-
 dnl Yay!  Another Linux brokenness.  Its not good enough
 dnl to know that setresuid() exists, because RedHat 5.0 declares
 dnl setresuid() but doesn't implement it.
@@ -1506,6 +1539,11 @@ AC_OUTPUT(\
        ./scripts/RunCache \
        ./scripts/RunAccel \
        ./src/Makefile \
+       ./src/fs/Makefile \
+       ./src/fs/ufs/Makefile \
+       ./src/fs/aufs/Makefile \
+       ./src/fs/coss/Makefile \
+       ./src/fs/diskd/Makefile \
        ./contrib/Makefile \
        $SNMP_MAKEFILE \
        ./icons/Makefile \
index 9757040ed4045fabc15e9135a1e2e8691bc90d92..fd90426900255c7b5c590a2d43336ddc152daca0 100644 (file)
@@ -2,7 +2,7 @@
 <article>
 <title>Squid Programmers Guide</title>
 <author>Duane Wessels, Squid Developers
-<date>$Id: prog-guide.sgml,v 1.26 2000/04/27 07:58:09 adrian Exp $</date>
+<date>$Id: prog-guide.sgml,v 1.27 2000/05/03 17:15:40 adrian Exp $</date>
 
 <abstract>
 Squid is a WWW Cache application developed by the National Laboratory
@@ -106,22 +106,22 @@ Squid consists of the following major components
        Squid can quickly locate cached objects because it keeps
        (in memory) a hash table of all <em/StoreEntry/'s.  The
        keys for the hash table are MD5 checksums of the objects
-       URI.  In addition there is also a doubly-linked list of
-       <em/StoreEntry/'s used for the LRU replacement algorithm.
-       When an entry is accessed, it is moved to the head of the
-       LRU list.  When Squid needs to replace cached objects, it
-       takes objects from the tail of the LRU list.
+       URI.  In addition there is also a storage policy such
+       as LRU that keeps track of the objects and determines
+       the removal order when space needs to be reclaimed.
+       For the LRU policy this is implemented as a doubly linked
+       list.
 
        <P>
-       Objects are saved to disk in a two-level directory structure.
-       For each object the <em/StoreEntry/ includes a 4-byte
-       <em/fileno/ field.  This file number is converted to a disk
-       pathname by a simple algorithm which evenly distributes
-       the files across all cache directories.  A cache swap file
-       consists of two parts: the cache metadata, and the object
-       data.  Note the object data includes the full HTTP
-       reply---headers and body.  The HTTP reply headers are not
-       the same as the cache metadata.
+       For each object the <em/StoreEntry/ maps to a cache_dir
+       and location via sdirn and sfilen. For the "ufs" store
+       this file number (sfilen) is converted to a disk pathname
+       by a simple modulo of L2 and L1, but other storage drivers may
+       map sfilen in other ways.  A cache swap file consists
+       of two parts: the cache metadata, and the object data. 
+       Note the object data includes the full HTTP reply---headers
+       and body.  The HTTP reply headers are not the same as the
+       cache metadata.
 
        <P>
        Client-side requests register themselves with a <em/StoreEntry/
@@ -652,11 +652,61 @@ Squid consists of the following major components
 <!-- %%%% Chapter : CLIENT REQUEST PROCESSING %%%% -->
 <sect>Processing Client Requests
 
+       <P>
+       To be written...
+
 <!-- %%%% Chapter : STORAGE MANAGER %%%% -->
 <sect>Storage Manager
 
-<!-- %%%% Chapter : FILESYSTEM INTERFACE %%%% -->
-<sect>Filesystem Interface
+<sect1>Introduction
+
+       <P>
+       The Storage Manager is the glue between client and server
+       sides.  Every object saved in the cache is allocated a
+       <em/StoreEntry/ structure.  While the object is being
+       accessed, it also has a <em/MemObject/ structure.
+
+       <P>
+       Squid can quickly locate cached objects because it keeps
+       (in memory) a hash table of all <em/StoreEntry/'s.  The
+       keys for the hash table are MD5 checksums of the objects
+       URI.  In addition there is also a storage policy such
+       as LRU that keeps track of the objects and determines
+       the removal order when space needs to be reclaimed.
+       For the LRU policy this is implemented as a doubly linked
+       list.
+
+       <P>
+       For each object the <em/StoreEntry/ maps to a cache_dir
+       and location via sdirn and sfilen. For the "ufs" store
+       this file number (sfilen) is converted to a disk pathname
+       by a simple modulo of L2 and L1, but other storage drivers may
+       map sfilen in other ways.  A cache swap file consists
+       of two parts: the cache metadata, and the object data. 
+       Note the object data includes the full HTTP reply---headers
+       and body.  The HTTP reply headers are not the same as the
+       cache metadata.
+
+       <P>
+       Client-side requests register themselves with a <em/StoreEntry/
+       to be notified when new data arrives.  Multiple clients
+       may receive data via a single <em/StoreEntry/.  For POST
+       and PUT request, this process works in reverse.  Server-side
+       functions are notified when additional data is read from
+       the client.
+       
+<sect1>Object storage
+
+       <P>
+       To be written...
+
+<sect1>Object retreival
+
+       <P>
+       To be written...
+
+<!-- %%%% Chapter : STORAGE INTERFACE %%%% -->
+<sect>Storage Interface
 
 <sect1>Introduction
 
@@ -673,134 +723,528 @@ Squid consists of the following major components
        We want to try out our own, customized filesystems with Squid.
        In order to do that, we need a well-defined interface
        for the bits of Squid that access the permanent storage
-       devices.
+       devices. We also require tigher control of the replacement
+       policy by each storage module, rather than a single global
+       replacement policy.
+
+<sect1>Build structure
+
+       <P>
+       The storage types live in squid/src/fs/ . Each subdirectory corresponds
+       to the name of the storage type. When a new storage type is implemented
+       configure.in must be updated to autogenerate a Makefile in
+       squid/src/fs/$type/ from a Makefile.in file.
 
-<sect1>The Interface
+       <P>
+       configure will take a list of storage types through the
+       <em/--enable-store-io/ parameter. This parameter takes a list of
+       space seperated storage types. For example,
+       --enable-store-io="ufs coss" .
 
-<sect2>Data Structures
+       <P>
+       Each storage type must create an archive file
+       <tt/in squid/src/fs/$type/.a . This file is automatically linked into
+       squid at compile time.
 
-<sect3><em/storeIOState/
+       <P>
+       Each storefs must export a function named <tt/storeFsSetup_$type()/.
+       This function is called at runtime to initialise each storage type.
+       The list of storage types is passed through <tt/store_modules.sh/
+       to generate the initialisation function <tt/storeFsSetup()/. This
+       function lives in <tt/store_modules.c/.
 
        <P>
-       Every cache object that is ``opened'' for reading or writing
-       will have a <em/storeIOState/ data structure associated with
-       it.  Currently, this structure looks like:
+       An example of the automatically generated file:
+
 <verb>
-        struct _storeIOState {
-            sfileno swap_file_number;
-            mode_t mode;
-            size_t st_size;             /* do stat(2) after read open */
-            off_t offset;               /* current offset pointer */
-            STIOCB *callback;
-            void *callback_data;
-            struct {
-                STRCB *callback;
-                void *callback_data;
-            } read;
-            struct {
-                unsigned int closing:1; /* debugging aid */
-            } flags;
-            union {
-                struct {
-                    int fd;
-                    struct {
-                        unsigned int close_request:1;
-                        unsigned int reading:1;
-                        unsigned int writing:1;
-                    } flags;
-                } ufs;
-            } type;
-        };
+       /* automatically generated by ./store_modules.sh ufs coss
+        * do not edit
+        */
+       #include "squid.h"
+
+       extern STSETUP storeFsSetup_ufs;
+       extern STSETUP storeFsSetup_coss;
+       void storeFsSetup(void)
+       {
+                       storeFsAdd("ufs", storeFsSetup_ufs);
+               storeFsAdd("coss", storeFsSetup_coss);
+       }
 </verb>
 
-       <em/swap_file_number/ is the 32-bit swap file number for the
-       object, taken from the <em/StoreEntry/.
 
-       <em/mode/ is either O_RDONLY or O_WRONLY.
+<sect1>Initialisation of a storage type
+
+       <P>
+       Each storage type initialises through the <tt/storeFsSetup_$type()/
+       function.  The <tt/storeFsSetup_$type()/ function takes a single
+       argument - a <tt/storefs_entry_t/ pointer. This pointer references
+       the storefs_entry to initialise. A typical setup function is as
+       follows:
+
+<verb>
+       void
+       storeFsSetup_ufs(storefs_entry_t *storefs)
+       {   
+               assert(!ufs_initialised);
+               storefs->parsefunc = storeUfsDirParse;
+               storefs->reconfigurefunc = storeUfsDirReconfigure;
+               storefs->donefunc = storeUfsDirDone;
+               ufs_state_pool = memPoolCreate("UFS IO State data", sizeof(ufsstate_t));
+               ufs_initialised = 1;
+       }
+</verb>
+
+       <P>
+       There are five function pointers in the storefs_entry which require
+       initialising. In this example, some protection is made against the
+       setup function being called twice, and a memory pool is initialised
+       for use inside the storage module.
 
-       <em/offset/ represents the file (byte) offset after the
-       last operation completed.  For example, after a read operation,
-       <em/offset/ must be incremented by the number of bytes read.
-       The same goes for write operations.  This means that the
-       filesystem layer needs explicit (callback) notification
-       for writes.  It is wrong to increment <em/offset/ before
-       an I/O operation has been known to succeed.
+       <P>
+       Each function will be covered below.
 
-       <em/st_size/ is filled in with the object's on-disk size
-       after an object is opened for reading.  This allows
-       the upper layers to double-check that the disk object
-       actually belongs to the StoreEntry.
 
-       Note that there are two callback functions.  The first,
-       <em/callback/, of type <em/STIOCB/ (store I/O callback),
-       is callback for the <em/storeIOState/ as a whole.  This
-       callback is used to indicate success or failure of accessing
-       the object, whether its for reading or writing.
-       There are no callbacks for open and write operations,
-       unless they fail.
+<sect2>done
 
-       The second, <em/read.callback/, of type <em/STRCB/ (store
-       read callback) is used for every read operation.
+       <P>
+<verb>
+       typedef void
+       STFSSHUTDOWN(void);
+</verb>
 
-       The ugly union is used to hold filesystem-specific state
-       information.
+       <P>
+       This function is called whenever the storage system is to be shut down.
+       It should take care of deallocating any resources currently allocated.
 
-       <em/storeIOState/ structures are allocated by calling
-       <tt/storeOpen()/, and will be deallocated by the
-       filesystem layer after
-       <tt/storeClose()/ is called.
 
-<sect2>External Functions
+<verb>
+       typedef void STFSPARSE(SwapDir *SD, int index, char *path);
+       typedef void STFSRECONFIGURE(SwapDir *SD, int index, char *path);
+</verb>
 
-<sect3>Object I/O
+       <P>
+       These functions handle configuring and reconfiguring a storage
+       directory. Additional arguments from the cache_dir configuration
+       line can be retrieved through calls to strtok() and GetInteger().
 
        <P>
-       These functions all relate to per-object I/O tasks: opening,
-       closing, reading, writing, and unlinking objects on disk.
-       These functions can all be found in <tt/store_io.c/.
+       <em/STFSPARSE/ has the task of initialising a new swapdir. It should
+       parse the remaining arguments on the cache_dir line, initialise the
+       relevant function pointers and data structures, and choose the
+       replacement policy. <em/STFSRECONFIGURE/ deals with reconfiguring an
+       active swapdir.  It should parse the remaining arguments on the
+       cache_dir line and change any active configuration parameters. The
+       actual storage initialisation is done through the <em/STINIT/ function
+       pointer in the SwapDir.
 
        <P>
-       Note that the underlying storage system functions are
-       accessed through function pointers, kept in the
-       <em/SwapDir/ structure:
 <verb>
-    struct _SwapDir {
-       ....
-        struct {
-            STOBJOPEN *open;
-            STOBJCLOSE *close;
-            STOBJREAD *read;
-            STOBJWRITE *write;
-            STOBJUNLINK *unlink;
-        } obj;
-       ....
-    };
+       struct _SwapDir {
+               char *type;                             /* Pointer to the store dir type string */
+               int cur_size;                           /* Current swapsize in kb */
+               int low_size;                           /* ?? */
+               int max_size;                           /* Maximum swapsize in kb */
+               char *path;                             /* Path to store */
+               int index;                              /* This entry's index into the swapDir array */
+               int suggest;                            /* Suggestion for UFS style stores (??) */
+               size_t max_objsize;                     /* Maximum object size for this store */
+               union {                                 /* Replacement policy-specific fields */
+               #ifdef HEAP_REPLACEMENT
+                       struct {
+                               heap *heap;
+                       } heap;
+               #endif
+                       struct {
+                               dlink_list list;
+                               dlink_node *walker;
+                       } lru;
+               } repl;
+               int removals;
+               int scanned;
+               struct {
+                       unsigned int selected:1;        /* Currently selected for write */
+                       unsigned int read_only:1;       /* This store is read only */
+               } flags;
+               STINIT *init;                           /* Initialise the fs */
+               STNEWFS *newfs;                         /* Create a new fs */
+               STDUMP *dump;                           /* Dump fs config snippet */
+               STFREE *freefs;                         /* Free the fs data */
+               STDBLCHECK *dblcheck;                   /* Double check the obj integrity */
+               STSTATFS *statfs;                       /* Dump fs statistics */
+               STMAINTAINFS *maintainfs;               /* Replacement maintainence */
+               STCHECKOBJ *checkob;                    /* Check if the fs will store an object, and get the FS load */
+               /* These two are notifications */
+               STREFOBJ *refobj;                       /* Reference this object */
+               STUNREFOBJ *unrefobj;                   /* Unreference this object */
+               STCALLBACK *callback;                   /* Handle pending callbacks */
+               STSYNC *sync;                           /* Sync the directory */
+               struct {
+                       STOBJCREATE *create;            /* Create a new object */
+                       STOBJOPEN *open;                /* Open an existing object */
+                       STOBJCLOSE *close;              /* Close an open object */
+                       STOBJREAD *read;                /* Read from an open object */
+                       STOBJWRITE *write;              /* Write to a created object */
+                       STOBJUNLINK *unlink;            /* Remove the given object */
+               } obj;
+               struct {
+                       STLOGOPEN *open;                /* Open the log */
+                       STLOGCLOSE *close;              /* Close the log */
+                       STLOGWRITE *write;              /* Write to the log */
+                       struct {
+                               STLOGCLEANOPEN *open;   /* Open a clean log */
+                               STLOGCLEANWRITE *write; /* Write to the log */
+                               void *state;            /* Current state */
+                       } clean;
+               } log;
+               void *fsdata;                           /* FS-specific data */
+       };
+</verb>
+
+
+
+<sect1>Operation of a storage module
+
+       <P>
+       Squid understands the concept of multiple diverse storage directories.
+       Each storage directory provides a caching object store, with object
+       storage, retrieval, indexing and replacement. 
+
+       <P>
+       Each open object has associated with it a <em/storeIOState/ object. The
+       <em/storeIOState/ object is used to record the state of the current
+       object. Each <em/storeIOState/ can have a storage module specific data
+       structure containing information private to the storage module.
+
+       <P>
+<verb>
+       struct _storeIOState {
+               sdirno swap_dirn;               /* SwapDir index */
+               sfileno swap_filen;             /* Unique file index number */
+               StoreEntry *e;                  /* Pointer to parent StoreEntry */
+               mode_t mode;                    /* Mode - O_RDONLY or O_WRONLY */
+               size_t st_size;                 /* Size of the object if known */
+               off_t offset;                   /* current _on-disk_ offset pointer */
+               STFNCB *file_callback;          /* called on delayed sfileno assignments */
+               STIOCB *callback;               /* IO Error handler callback */
+               void *callback_data;            /* IO Error handler callback data */
+               struct {
+                       STRCB *callback;        /* Read completion callback */
+                       void *callback_data;    /* Read complation callback data */
+               } read;
+               struct {
+                       unsigned int closing:1; /* debugging aid */
+               } flags;
+               void *fsstate;                  /* pointer to private fs state */
+       };
+</verb>
+
+       <P>
+       Each <em/SwapDir/ has the concept of a maximum object size. This is used
+       as a basic hint to the storage layer in first choosing a suitable
+       <em/SwapDir/. The checkobj function is then called for suitable
+       candidate <em/SwapDirs/ to find out whether it wants to store a
+       given <em/StoreEntry/. A <em/maxobjsize/ of -1 means 'any size'.
+
+       <P>
+       The specific filesystem operations listed in the SwapDir object are
+       covered below.
+
+<sect2>initfs
+
+       <P>
+<verb>
+       typedef void
+       STINIT(SwapDir *SD);
+</verb>
+
+       <P>
+       Initialise the given <em/SwapDir/. Operations such as verifying and
+       rebuilding the storage and creating any needed bitmaps are done
+       here.
+
+
+<sect2>newfs
+
+       <P>
+<verb>
+       typedef void
+       STNEWFS(SwapDir *SD);
+</verb>
+
+       <P>
+       Called for each configured <em/SwapDir/ to perform filesystem
+       initialisation. This happens when '-z' is given to squid on the
+       command line.
+
+
+<sect2>dumpfs
+
+       <P>
+<verb>
+       typedef void
+       STDUMP(StoreEntry *e, const char *path, SwapDir *SD);
+</verb>
+
+       <P>
+       Dump the configuration of the current <em/SwapDir/ to the given
+       <em/StoreEntry/.  Used to grab a configuration file dump from th
+       <em/cachemgr/ interface. 'const char *' refers to the path of the
+       given <em/Swapdir/, and is redundant.
+
+
+<sect2>freefs
+
+       <P>
+<verb>
+       typedef void
+       STFREE(SwapDir *SD);
+</verb>
+
+       <P>
+       Free the <em/SwapDir/ filesystem information. This routine should
+       deallocate <em/SD->fsdata/.
+
+
+<sect2>doublecheckfs
+
+       <P>
+<verb>
+       typedef int
+       STDBLCHECK(SwapDir *SD, StoreEntry *e);
+</verb>
+
+       <P>
+       Double-check the given object for validity. Called during rebuild if
+       the '-S' flag is given to squid on the command line. Returns 1 if the
+       object is indeed valid, and 0 if the object is found invalid.
+
+
+<sect2>statfs
+
+       <P>
+<verb>
+       typedef void
+       STSTATFS(SwapDir *SD, StoreEntry *e);
+</verb>
+
+       <P>
+       Called to retrieve filesystem statistics, such as usage, load and
+       errors. The information should be appended to the passed
+       <em/StoreEntry/ e.
+
+
+<sect2>maintainfs
+
+       <P>
+<verb>
+       typedef void
+       STMAINTAINFS(SwapDir *SD);
+</verb>
+
+       <P>
+       Called periodically to replace objects. The active replacement policy
+       should be used to timeout unused objects in order to make room for
+       new objects. 
+
+<sect2>callback
+
+       <P>
+<verb>
+       typedef void
+       STCALLBACK(SwapDir *SD);
+</verb>
+
+       <P>
+       This function is called inside the comm_select/comm_poll loop to handle
+       any callbacks pending.
+
+
+<sect2>sync
+
+       <P>
+<verb>
+       typedef void
+       STSYNC(SwapDir *SD);
+</verb>
+
+       <P>
+       This function is called whenever a sync to disk is required. This
+       function should not return until all pending data has been flushed to
+       disk.
+
+
+<sect2>parse/reconfigure
+
+       <P>
+
+<sect2>checkobj
+
+       <P>
+<verb>
+       typedef int
+       STCHECKOBJ(SwapDir *SD, const StoreEntry *e);
+</verb>
+
+       <P>
+       Called by <tt/storeDirSelectSwapDir()/ to determine whether the
+       <em/SwapDir/ will store the given <em/StoreEntry/ object. If the
+       <em/SwapDir/ is not willing to store the given <em/StoreEntry/
+       -1 should be returned. Otherwise, a value between 0 and 1000 should
+       be returned indicating the current IO load. A value of 1000 indicates
+       the <em/SwapDir/ has an IO load of 100%. This is used by
+       <tt/storeDirSelectSwapDir()/ to choose the <em/SwapDir/ with the
+       lowest IO load.
+
+
+<sect2>referenceobj
+
+       <P>
+<verb>
+       typedef void
+       STREFOBJ(SwapDir *SD, StoreEntry *e);
+</verb>
+
+       <P>
+       Called whenever an object is locked by <tt/storeLockObject()/.
+       It is typically used to update the objects position in the replacement
+       policy.
+
+
+<sect2>unreferenceobj
+
+       <P>
+<verb>
+       typedef void
+       STUNREFOBJ(SwapDir *SD, StoreEntry *e);
+</verb>
+
+       <P>
+       Called whenever the object is unlocked by <tt/storeUnlockObject()/
+       and the lock count reaches 0. It is also typically used to update the
+       objects position in the replacement policy.
+
+
+<sect2>createobj
+
+       <P>
+<verb>
+       typedef storeIOState *
+       STOBJCREATE(SwapDir *SD, StoreEntry *e, STFNCB *file_callback, STIOCB *io_callback, void *io_callback_data);
+</verb>
+
+       <P>
+       Create an object in the <em/SwapDir/ *SD. <em/file_callback/ is called
+       whenever the filesystem allocates or reallocates the <em/swap_filen/.
+       Note - <em/STFNCB/ is called with a generic cbdata pointer, which
+       points to the <em/StoreEntry/ e.  The <em/StoreEntry/ should not be
+       modified EXCEPT for the replacement policy fields.
+
+       <P>
+       The IO callback should be called when an error occurs and when the
+       object is closed. Once the IO callback is called, the <em/storeIOState/
+       becomes invalid.
+
+       <P>
+       <em/STOBJCREATE/ returns a <em/storeIOState/ suitable for writing on
+       sucess, or NULL if an error occurs.
+
+
+<sect2>openobj
+
+       <P>
+<verb>
+       typedef storeIOState *
+       STOBJOPEN(SwapDir *SD, StoreEntry *e, STFNCB *file_callback, STIOCB *io_callback, void *io_callback_data);
+</verb>
+
+       <P>
+       Open the <em/StoreEntry/ in <em/SwapDir/ *SD for reading. Much the
+       same is applicable from <em/STOBJCREATE/, the major difference being
+       that the data passed to <em/file_callback/ is the relevant
+       <em/store_client/ .
+
+
+<sect2>closeobj
+
+       <P>
+<verb>
+       typedef void
+       STOBJCLOSE(SwapDir *SD, storeIOState *sio);
+</verb>
+
+       <P>
+       Close an opened object. The <em/STIOCB/ callback should be called at
+       the end of this routine.
+
+
+<sect2>readobj
+
+       <P>
+<verb>
+       typedef void
+       STOBJREAD(SwapDir *SD, storeIOState *sio, char *buf, size_t size, off_t offset, STRCB *read_callback, void *read_callback_data);
 </verb>
 
        <P>
-       Thus, a storage system must do something like this
-       when initializing its <em/SwapDir/ structure:
+       Read part of the object of into <em/buf/. It is safe to request a read
+       when there are other pending reads or writes. <em/STRCB/ is called at
+       completion.
+
+       <P>
+       If a read operation fails, the filesystem layer notifies the
+       calling module by calling the <em/STIOCB/ callback with an
+       error status code.
+
+
+<sect2>writeobj
+
+       <P>
+<verb>
+       typedef void
+       STOBJWRITE(SwapDir *SD, storeIOState *sio, char *buf, size_t size, off_t offset, FREE *freefunc);
+</verb>
+
+       <P>
+       Write the given block of data to the given store object. <em/buf/ is
+       allocated by the caller. When the write is complete, the data is freed
+       through <em/free_func/.
+
+       <P>
+       If a write operation fails, the filesystem layer notifies the
+       calling module by calling the <em/STIOCB/ callback with an
+       error status code.
+
+
+<sect2>unlinkobj
+
+       <P>
 <verb>
-            SwapDir->obj.open = storeFooOpen;
-            SwapDir->obj.close = storeFooClose;
-            SwapDir->obj.read = storeFooRead;
-            SwapDir->obj.write = storeFooWrite;
-            SwapDir->obj.unlink = storeFooUnlink;
+       typedef void STOBJUNLINK(SwapDir *, StoreEntry *);
 </verb>
 
-<sect4><tt/storeOpen()/
+       <P>
+       Remove the <em/StoreEntry/ e from the <em/SwapDir/ SD and the
+       replacement policy.
+
+
+
+<sect1>Store IO calls
+
+       <P>
+       These routines are used inside the storage manager to create and
+       retrieve objects from a storage directory.
+
+<sect2>storeCreate()
 
        <P>
 <verb>
        storeIOState *
-       storeOpen(sfileno f, mode_t mode, STIOCB *callback, void *callback_data)
+       storeCreate(StoreEntry *e, STIOCB *file_callback, STIOCB *close_callback, void * callback_data)
 </verb>
 
        <P>
-       <tt/storeOpen()/
-       submits a request to open a cache object for reading or writing.
-       <tt/f/ is the 32-bit swap file number of the cached object.
-       <tt/mode/ should be either <tt/O_RDONLY/ or <tt/O_WRONLY/.
+       <tt/storeCreate/ is called to store the given <em/StoreEntry/ in
+       a storage directory. 
 
        <P>
        <tt/callback/ is a function that will be called either when
@@ -811,29 +1255,40 @@ Squid consists of the following major components
        or writing immediately.
 
        <P>
-       <tt/storeOpen()/ may return NULL if the requested object
-       can not be openeed.  In this case the <tt/callback/ function
+       <tt/storeCreate()/ may return NULL if the requested object
+       can not be created.  In this case the <tt/callback/ function
        will not be called.
 
-<sect4><tt/storeClose()/
+
+<sect2>storeOpen()
 
        <P>
 <verb>
-       void
-       storeClose(storeIOState *sio)
+       storeIOState *
+       storeOpen(StoreEntry *e, STFNCB * file_callback, STIOCB * callback, void *callback_data)
 </verb>
 
        <P>
-       <tt/storeClose()/
-       submits a request to close the cache object.  It is safe to request
-       a close even if there are read or write operations pending.
-       When the underlying filesystem actually closes the object,
-       the <em/STIOCB/ callback (registered with <tt/storeOpen()/) will
-       be called.
+       <tt/storeOpen/ is called to open the given <em/StoreEntry/ from
+       the storage directory it resides on.
 
-<sect4><tt/storeRead()/
+       <P>
+       <tt/callback/ is a function that will be called either when
+       an error is encountered, or when the object is closed (by
+       calling <tt/storeClose()/).  If the open request is
+       successful, there is no callback.  The calling module must
+       assume the open request will succeed, and may begin reading
+       or writing immediately.
 
        <P>
+       <tt/storeOpen()/ may return NULL if the requested object
+       can not be openeed.  In this case the <tt/callback/ function
+       will not be called.
+
+
+<sect2>storeRead()
+
+        <P>
 <verb>
        void
        storeRead(storeIOState *sio, char *buf, size_t size, off_t offset, STRCB *callback, void *callback_data)
@@ -842,7 +1297,7 @@ Squid consists of the following major components
        <P>
        <tt/storeRead()/ is more complicated than the other functions
        because it requires its own callback function to notify the
-       caller when the requested data has actually been read. 
+       caller when the requested data has actually been read.
        <em/buf/ must be a valid memory buffer of at least <em/size/
        bytes.  <em/offset/ specifies the byte offset where the
        read should begin.  Note that with the Swap Meta Headers
@@ -850,11 +1305,12 @@ Squid consists of the following major components
        the offset into the actual object data.
 
        <P>
-       The caller is responsible for allocating and freeing <em/buf/
+       The caller is responsible for allocating and freeing <em/buf/ .
 
-<sect4><tt/storeWrite()/
 
-       <P>
+<sect2>storeWrite()
+
+        <P>
 <verb>
        void
        storeWrite(storeIOState *sio, char *buf, size_t size, off_t offset, FREE *free_func)
@@ -862,7 +1318,7 @@ Squid consists of the following major components
 
        <P>
        <tt/storeWrite()/ submits a request to write a block
-       of data to the disk store.  
+       of data to the disk store.
        The caller is responsible for allocating <em/buf/, but since
        there is no per-write callback, this memory must be freed by
        the lower filesystem implementation.  Therefore, the caller
@@ -874,12 +1330,13 @@ Squid consists of the following major components
        calling module by calling the <em/STIOCB/ callback with an
        error status code.
 
-<sect4><tt/storeUnlink()/
 
-       <P>
+<sect2>storeUnlink()
+
+        <P>
 <verb>
-       void
-       storeUnlink(sfileno f)
+        void
+       storeUnlink(StoreEntry *e)
 </verb>
 
        <P>
@@ -888,21 +1345,28 @@ Squid consists of the following major components
        does not need to be opened first.  The filesystem
        layer will remove the object if it exists on the disk.
 
-<sect4><tt/storeOffset()/
 
-       <P>
+<sect2>storeOffset()
+
+        <P>
 <verb>
-       off_t
-       storeOffset(storeIOState *sio)
+        off_t storeOffset(storeIOState *sio)
 </verb>
 
+
+
        <P>
-       Returns the current byte-offset of the cache object on
-       disk.  For read-objects, this is the offset after the last
-       successful disk read operation.  For write-objects, it is
-       the offset of the last successful disk write operation.
+       <tt/storeOffset()/ returns the current _ondisk_ offset. This is used to
+       determine how much of an objects memory can be freed to make way for
+       other in-transit and cached objects. You must make sure that the
+       <em/storeIOState->offset/ refers to the ondisk offset, or undefined
+       results will occur. For reads, this returns the current offset of
+       sucessfully read data, not including queued reads.
+
 
-<sect4><em/STIOCB/ callback
+<sect1>Callbacks
+
+<sect2><em/STIOCB/ callback
 
        <P>
 <verb>
@@ -929,7 +1393,7 @@ Squid consists of the following major components
        Once the The <em/stiocb/ function has been called,
        the <em/sio/ structure should not be accessed further.
 
-<sect4><em/STRCB/ callback
+<sect2><em/STRCB/ callback
 
        <P>
 <verb>
@@ -946,43 +1410,11 @@ Squid consists of the following major components
        called if the read operation is successful.  If it fails,
        then the <em/STIOCB/ callback will be called instead.
 
-<sect3>Config file parsing
-
-       <P>
-       There are three functions relating to the Squid configuration
-       file: parsing, dumping, and freeing.
-
-       <P>
-       The parse function is called at startup, and during a reconfigure,
-       for a <em/cache_dir/ line.  The first keyword after the <em/cache_dir/
-       keyword will be a filesystem type (such as "ufs").  A switch
-       statement in <tt/parse_cachedir/ will call the appropriate
-       filesystem-dependent parsing function.  The parsing function
-       may use <tt/strtok()/ to continue reading keywords after the
-       filesystem type on the <em/cache_dir/ line.
-
-
-       <P>
-       The ``dump'' function is used to output a configuration
-       file from the in-memory configuration structure.  It is
-       called with a <em/SwapDir/ argument, and must append one
-       line to the <em/StoreEntry/ that is holding the configuration
-       file being generated.
-
-       <P>
-       The free function is called during a reconfigure (and at
-       exit) to free up (or un-initialize) any memory or structures
-       associated with the configuration line.  The <em/SwapDir/
-       structure includes common and private sections.  The
-       <tt/free_cachedir()/ function will handle freeing anything
-       in the common section, and relies on a filesystem-dependent
-       function to free, or un-initialize private members.
-
-<sect3>Filesystem Startup, Initialization, and State Logging
+<sect1>State Logging
 
        <P>
-       These functions deal with initializing, state
-       logging, and related tasks for a squid storage system.
+       These functions deal with state
+       logging and related tasks for a squid storage system.
        These functions are used (called) in <tt/store_dir.c/.
 
        <P>
@@ -1010,36 +1442,7 @@ Squid consists of the following major components
     };
 </verb>
 
-<sect4><tt/init()/
-
-       <P>
-<verb>
-        void
-        STINIT(SwapDir *);
-</verb>
-
-       <P>
-       The <tt/init/ function, of type <em/STINIT/ is called by
-       <tt/storeDirInit()/ when Squid first starts up.  The
-       <tt/init/ function should probably start the process of
-       reading saved state information from disk (aka the "rebuild"
-       procedure).
-
-<sect4><tt/newfs()/
-
-       <P>
-<verb>
-       void
-       STNEWFS(SwapDir *);
-</verb>
-
-       <P>
-       The <tt/newfs/ function, of type <em/STNEWFS/, is used to
-       prepare a cache_dir for use by squid.  It is called when
-       the user runs <em/squid -z/.  For the Unix file system,
-       the <tt/newfs/ function makes all the two-layer subdirectories.
-
-<sect4><tt/log.open()/
+<sect2><tt/log.open()/
 
        <P>
 <verb>
@@ -1060,7 +1463,7 @@ Squid consists of the following major components
        the state log and then re-opens them.  A <em/squid -k reconfigure/
        does the same.
 
-<sect4><tt/log.close()/
+<sect2><tt/log.close()/
 
        <P>
 <verb>
@@ -1074,7 +1477,7 @@ Squid consists of the following major components
        the open state-holding log files (if any) for the storage
        system.
 
-<sect4><tt/log.write()/
+<sect2><tt/log.write()/
 
        <P>
 <verb>
@@ -1089,7 +1492,7 @@ Squid consists of the following major components
        This feature may not be required by some storage systems
        and can be implemented as a null-function (no-op).
 
-<sect4><tt/log.clean.open()/
+<sect2><tt/log.clean.open()/
 
        <P>
 <verb>
@@ -1111,7 +1514,7 @@ Squid consists of the following major components
        keep state information for the clean-writing process, but
        should not be accessed by upper layers.
 
-<sect4><tt/log.clean.write()/
+<sect2><tt/log.clean.write()/
 
        <P>
 <verb>
@@ -1130,9 +1533,301 @@ Squid consists of the following major components
        to become the official state-holding log.
 
 
+<sect1>Replacement policy implementation
+
+<P>
+The replacement policy can be updated during STOBJREAD/STOBJWRITE/STOBJOPEN/
+STOBJCLOSE as well as STREFOBJ and STUNREFOBJ. Care should be taken to
+only modify the relevant replacement policy entries in the StoreEntry.
+The responsibility of replacement policy maintainence has been moved into
+each SwapDir so that the storage code can have tight control of the
+replacement policy. Cyclic filesystems such as COSS require this tight
+coupling between the storage layer and the replacement policy.
+
+
+<sect1>Future removal policy
+
+       <P>
+       (replaces the above Replace policy)
+
+       <P>
+       The removal policy is responsible for determining in which order
+       objects are deleted when Squid needs to reclaim space for new objects.
+       Such a policy is used by a object storage for maintaining the stored
+       objects and determining what to remove to reclaim space for new objects.
+       (together they implements a replacement policy)
+       
+<sect2>API
+       <P>
+       It is implemented as a modular API where a storage directory or
+       memory creates a policy of choice for maintaining it's objects,
+       and modules registerering to be used by this API.
+
+<sect3>createRemovalPolicy()
+
+<P>
+<verb>
+       RemovalPolicy policy = createRemovalPolicy(cons char *type, cons char *args)
+</verb>
+
+       <P>
+       Creates a removal policy instance where object priority can be
+       maintained
+
+       <P>
+       The returned RemovalPolicy instance is cbdata registered
+
+<sect3>policy.Free()
+       
+       <P>
+<verb>
+       policy-&gt;Free(RemovalPolicy *policy)
+</verb>
+
+<P>
+       Destroys the policy instance and frees all related memory.
+
+<sect3>policy.Add()
+
+<P>
+<verb>
+       policy-&gt;Add(RemovalPolicy *policy, StoreEntry *, RemovalPolicyNode *node)
+</verb>
+
+       <P>
+       Adds a StoreEntry to the policy instance.
+       
+       <P>
+       datap is a pointer to where policy specific data can be stored
+       for the store entry, currently the size of one (void *) pointer.
+
+<sect3>policy.Remove()
+
+<P>
+<verb>
+       policy-&gt;Remove(RemovalPolicy *policy, StoreEntry *, RemovalPolicyNode *node)
+</verb>
+
+       <P>
+       Removes a StoreEntry from the policy instance out of
+       policy order. For example when an object is replaced
+       by a newer one or is manually purged from the store.
+
+       <P>
+       datap is a pointer to where policy specific data is stored
+       for the store entry, currently the size of one (void *) pointer.
+
+<sect3>policy.Referenced()
+
+<P>
+<verb>
+       policy-&gt;Referenced(RemovalPolicy *policy, const StoreEntry *, RemovalPolicyNode *node)
+</verb>
+
+       <P>
+       Tells the policy that a StoreEntry has been referenced.
+
+       <P>
+       datap is a pointer to where policy specific data is stored
+       for the store entry, currently the size of one (void *) pointer.
+
+<sect3>policy.WalkInit()
+
+<P>
+<verb>
+       RemovalPolicyWalker walker = policy-&gt;WalkInit(RemovalPolicy *policy)
+</verb>
+
+       <P>
+       Initiates a walk of all objects in the policy instance.
+       The objects is returned in an order suitable for using
+       as reinsertion order when rebuilding the policy.
+
+       <P>
+       The returned RemovalPolicyWalker instance is cbdata registered
+
+       <P>
+       Note: The walk must be performed as an atomic operation
+       with no other policy actions interveaning, or the outcome
+       will be undefined.
+
+<sect3>walker.Next()
+
+       <P>
+<verb>
+       const StoreEntry *entry = walker-&gt;Next(RemovalPolicyWalker *walker)
+</verb>
+
+<P>
+       Gets the next object in the walk chain
+
+       <P>
+       Return NULL when there is no further objects
+
+<sect3>walker.Done()
+
+<P>
+<verb>
+       walker-&gt;Done(RemovalPolicyWalker *walker)
+</verb>
+
+       <P>
+       Finishes a walk of the maintaned objects, destroys
+       walker.
+
+<sect3>policy.PurgeInit()
+
+<P>
+<verb>
+       RemovalPurgeWalker purgewalker = policy-&gt;PurgeInit(RemovalPolicy *policy, int max_scan)
+</verb>
+
+       <P>
+       Initiates a search for removal candidates. Seach depth is indicated
+       by max_scan.
+
+       <P>
+       The returned RemovalPurgeWalker instance is cbdata registered
+
+       <P>
+       Note: The walk must be performed as an atomic operation
+       with no other policy actions interveaning, or the outcome
+       will be undefined.
+
+<sect3>purgewalker.Next()
+
+<P>
+<verb>
+       StoreEntry *entry = purgewalker-&gt;Next(RemovalPurgeWalker *purgewalker)
+</verb>
+
+       <P>
+       Gets the next object to purge. The purgewalker will remove each
+       returned object from the policy.
+       
+       <P>It is the policys responsibility to verify that the object
+       isn't locked or otherwise prevented from being removed. What this
+       means is that the policy must not return objects where
+       storeEntryLocked() is true.
+
+       <P>
+       Return NULL when there is no further purgeable objects in the policy.
+
+<sect3>purgewalker.Done()
+
+<P>
+<verb>
+       purgewalker-&gt;Done(RemovalPurgeWalker *purgewalker)
+</verb>
+
+       <P>
+       Finishes a walk of the maintaned objects, destroys
+       walker and restores the policy to it's normal state.
+
+<sect2>Future removal policy implementation
+
+<sect3>Source layout
+
+<P>
+       Policy implementations resides in src/repl/&lt;name&gt;/, and a make in
+       such a directory must result in a object archive src/repl/&lt;name&gt;.a
+       containing all the objects implementing the policy.
+
+<sect3>Internal structures
+
+<sect4>RemovalPolicy
+
+<P>
+<verb>
+       typedef struct _RemovalPolicy RemovalPolicy;
+       struct _RemovalPolicy {
+           char *_type;
+           void *_data;
+           void (*add)(RemovalPolicy *policy, StoreEntry *);
+           ... /* see the API definition above */
+       };
+</verb>
+
+<P>
+       The _type member is mainly for debugging and diagnostics purposes, and
+       should be a pointer to the name of the policy (same name as used for
+       creation)
+
+<P>
+       The _data member is for storing policy specific information.
+
+<sect4>RemovalPolicyWalker
+
+<P>
+<verb>
+       typedef struct _RemovalPolicyWalker RemovalPolicyWalker;
+       struct _RemovalPolicyWalker {
+           RemovalPolicy *_policy;
+           void *_data;
+           StoreEntry *(*next)(RemovalPolicyWalker *);
+           ... /* see the API definition above */
+       };
+</verb>
+
+<sect4>RemovalPolicyNode
+
+<P>
+<verb>
+       typedef struct _RemovalPolicyNode RemovalPolicyNode;
+       struct _RemovalPolicyNode {
+           void *data;
+       };
+</verb>
+
+       Stores policy specific information about a entry. Currently
+       there is only space for a single pointer, but plans are to
+       maybe later provide more space here to allow simple policies
+       to store all their data "inline" to preserve some memory.
+
+<sect3>Policy registration
+
+<P>
+       Policies are automatically registered in the Squid binary from the
+       policy selection made by the user building Squid. In the future this
+       might get extended to support loadable modules. All registered
+       policies are available to object stores which wishes to use them.
+
+<sect3>Policy instance creation
+
+<P>
+       Each policy must implement a "create/new" function "<tt/RemovalPolicy *
+       createRemovalPolicy_&lt;name&gt;(char *arguments)/". This function
+       creates the policy instance and populates it with at least the API
+       methods supported. Currently all API calls are mandatory, but the
+       policy implementation must make sure to NULL fill the structure prior
+       to populating it in order to assure future API compability.
+
+<P>
+       It should also populate the _data member with a pointer to policy
+       specific data.
+
+<P>
+       Prior to returning the created instance must be registered as
+       callback-data by calling cbdataAdd().
+
+<sect3>Walker
+
+<P>
+       When a walker is created the policy populates it with at least the API
+       methods supported. Currently all API calls are mandatory, but the
+       policy implementation must make sure to NULL fill the structure prior
+       to populating it in order to assure future API compability.
+
+<P>
+       Prior to returning the created instance must be registered as
+       callback-data by calling cbdataAdd().
+
 <!-- %%%% Chapter : FORWARDING SELECTION %%%% -->
 <sect>Forwarding Selection
 
+       <P>
+       To be written...
+
 <!-- %%%% Chapter : IP/FQDN CACHE %%%% -->
 <sect>IP Cache and FQDN Cache
 
@@ -1217,30 +1912,72 @@ Squid consists of the following major components
 <!-- %%%% Chapter : SERVER PROTOCOLS %%%% -->
 <sect>Server Protocols
 <sect1>HTTP
+
+       <P>
+       To be written...
+
 <sect1>FTP
+
+       <P>
+       To be written...
+
 <sect1>Gopher
+
+       <P>
+       To be written...
+
 <sect1>Wais
+
+       <P>
+       To be written...
+
 <sect1>SSL
+
+       <P>
+       To be written...
+
 <sect1>Passthrough
 
+       <P>
+       To be written...
+
 <!-- %%%% Chapter : TIMEOUTS %%%% -->
 <sect>Timeouts
 
+       <P>
+       To be written...
+
 <!-- %%%% Chapter : EVENTS %%%% -->
 <sect>Events
 
+       <P>
+       To be written...
+
 <!-- %%%% Chapter : ACCESS CONTROLS %%%% -->
 <sect>Access Controls
 
+       <P>
+       To be written...
+
 <!-- %%%% Chapter : ICP %%%% -->
 <sect>ICP
 
+       <P>
+       To be written...
+
 <!-- %%%% Chapter : NETDB %%%% -->
 <sect>Network Measurement Database
 
+       <P>
+       To be written...
+
 <!-- %%%% Chapter : Error Pages %%%% -->
 <sect>Error Pages
 
+       <P>
+       To be written...
+
+<!-- %%%% Chapter : Callback Data Base %%%% -->
 <sect>Callback Data Database
 
        <P>
@@ -1309,6 +2046,9 @@ Squid consists of the following major components
 <!-- %%%% Chapter : CACHE MANAGER %%%% -->
 <sect>Cache Manager
 
+       <P>
+       To be written...
+
 <!-- %%%% Chapter : HTTP Headers %%%% -->
 <sect>HTTP Headers
 
index 5b9df6b245254934632444c8d161fa8132f7b26b..ea7b51c539575a31fe9f57a07d554380dc75bb46 100644 (file)
 
 #undef FORW_VIA_DB
 
-/* Define to use async disk I/O operations */
-#undef USE_ASYNC_IO
-
 /* Defines how many threads to use for async I/O */
-#undef NUMTHREADS
+#undef ASYNC_IO_THREADS
 
 /*
  * If you want to use Squid's ICMP features (highly recommended!) then
  */
 #undef HEAP_REPLACEMENT
 
-/*
- * Use DISKD
- */
-#undef USE_DISKD
-
 /*
  * message type for message queues
  */
 #undef mtyp_t
 
+/* Define if you want to set the COSS membuf size */
+#undef COSS_MEMBUF_SZ
+
 /* The number of bytes in a int.  */
 #undef SIZEOF_INT
 
 /* Define if you have the srandom function.  */
 #undef HAVE_SRANDOM
 
+/* Define if you have the statfs function.  */
+#undef HAVE_STATFS
+
 /* Define if you have the strerror function.  */
 #undef HAVE_STRERROR
 
 /* Define if you have the <sys/ioctl.h> header file.  */
 #undef HAVE_SYS_IOCTL_H
 
-/* Define if you have the <sys/msg.h> header file.  */
-#undef HAVE_SYS_MSG_H
-
 /* Define if you have the <sys/mount.h> header file.  */
 #undef HAVE_SYS_MOUNT_H
 
+/* Define if you have the <sys/msg.h> header file.  */
+#undef HAVE_SYS_MSG_H
+
 /* Define if you have the <sys/ndir.h> header file.  */
 #undef HAVE_SYS_NDIR_H
 
index 885c2785f822069d6d05c54c9372dfbab19e0f8d..0f3c9b13e48e57f3eab49f730bfccc338c8ba0ec 100644 (file)
@@ -1,7 +1,7 @@
 #
 #  Makefile for the Squid Object Cache server
 #
-#  $Id: Makefile.in,v 1.184 2000/05/02 21:12:42 hno Exp $
+#  $Id: Makefile.in,v 1.185 2000/05/03 17:15:40 adrian Exp $
 #
 #  Uncomment and customize the following to suit your needs:
 #
@@ -18,6 +18,8 @@ localstatedir   = @localstatedir@
 srcdir         = @srcdir@
 VPATH          = @srcdir@
 
+SUBDIRS                = fs
+
 # Gotta love the DOS legacy
 #
 SQUID_EXE      = squid$(exec_suffix)
@@ -26,8 +28,6 @@ DNSSERVER_EXE = dnsserver$(exec_suffix)
 UNLINKD_EXE    = unlinkd$(exec_suffix)
 PINGER_EXE     = pinger$(exec_suffix)
 CACHEMGR_EXE   = cachemgr$(cgi_suffix)
-DISKD_EXE      = diskd$(exec_suffix)
-OPT_DISKD_EXE  = @OPT_DISKD_EXE@
 
 DEFAULT_PREFIX         = $(prefix)
 DEFAULT_CONFIG_FILE     = $(sysconfdir)/squid.conf
@@ -62,6 +62,8 @@ AC_CFLAGS     = @CFLAGS@
 LDFLAGS                = @LDFLAGS@
 XTRA_LIBS      = @XTRA_LIBS@
 XTRA_OBJS      = @XTRA_OBJS@
+STORE_OBJS     = @STORE_OBJS@
+STORE_MODULES  = @STORE_MODULES@
 MV             = @MV@
 RM             = @RM@
 SHELL          = /bin/sh
@@ -77,14 +79,13 @@ PINGER_LIBS = -L../lib -lmiscutil $(XTRA_LIBS)
 STD_APP_LIBS    = -L../lib -lmiscutil $(XTRA_LIBS)
 
 PROGS           = $(SQUID_EXE) $(CLIENT_EXE)
-UTILS           = $(DNSSERVER_EXE) $(UNLINKD_EXE) $(OPT_DISKD_EXE)
+UTILS           = $(DNSSERVER_EXE) $(UNLINKD_EXE) $(DISKD_EXE)
 SUID_UTILS     = $(PINGER_EXE)
 CGIPROGS       = $(CACHEMGR_EXE)
 OBJS           = \
                access_log.o \
                acl.o \
                asn.o \
-               @ASYNC_OBJS@ \
                authenticate.o \
                cache_cf.o \
                CacheDigest.o \
@@ -156,18 +157,17 @@ OBJS              = \
                stmem.o \
                store.o \
                store_io.o \
-               store_io_ufs.o \
-               @DISKD_OBJS@ \
                store_client.o \
                store_digest.o \
                store_dir.o \
-               store_dir_ufs.o \
                store_key_md5.o \
                store_log.o \
+               store_modules.o \
                store_rebuild.o \
                store_swapin.o \
                store_swapmeta.o \
                store_swapout.o \
+               store_heap_replacement.o \
                string_arrays.o \
                tools.o \
                unlinkd.o \
@@ -187,28 +187,23 @@ HTCP_OBJS = htcp.o
 
 DELAY_OBJS     = delay_pools.o
 
-ASYNC_OBJS     = \
-               aiops.o \
-               async_io.o \
-               store_io_asyncufs.o
-
 LEAKFINDER_OBJS        = \
                leakfinder.o
 
 DEFAULTS        = \
        -DDEFAULT_CONFIG_FILE=\"$(DEFAULT_CONFIG_FILE)\"
 
-all:    squid.conf $(PROGS) $(UTILS) $(SUID_UTILS) $(CGIPROGS)
+all:    squid.conf store_modules $(PROGS) $(UTILS) $(SUID_UTILS) $(CGIPROGS)
 
 $(OBJS): $(top_srcdir)/include/version.h
 
 $(SNMP_OBJS): ../snmplib/libsnmp.a $(top_srcdir)/include/cache_snmp.h
 
-$(SQUID_EXE): $(OBJS)
-       $(CC) -o $@ $(LDFLAGS) $(OBJS) $(SQUID_LIBS)
+$(SQUID_EXE): $(OBJS) $(STORE_OBJS)
+       $(CC) -o $@ $(LDFLAGS) $(OBJS) $(STORE_OBJS) $(SQUID_LIBS)
 
 globals.o: globals.c Makefile
-       $(CC) -c $< $(CFLAGS) -I$(srcdir) $(DEFAULTS)
+       $(CC) -c globals.c $(CFLAGS) -I$(srcdir) $(DEFAULTS)
 
 globals.c: globals.h mk-globals-c.pl
        $(PERL) $(srcdir)/mk-globals-c.pl < $(srcdir)/globals.h > $@
@@ -222,12 +217,6 @@ $(CLIENT_EXE): client.o
 $(DNSSERVER_EXE): dnsserver.o
        $(CC) -o $@ $(LDFLAGS) dnsserver.o $(DNSSERVER_LIBS)
 
-$(DISKD_EXE): diskd-daemon.o
-       $(CC) -o $@ $(LDFLAGS) diskd-daemon.o $(DISKD_LIBS)
-
-diskd-daemon.o: diskd.c
-       $(CC) -c $(CFLAGS) -DDISKD_DAEMON=1 $(srcdir)/diskd.c -o $@
-
 $(CACHEMGR_EXE): cachemgr.o
        $(CC) -o $@ $(LDFLAGS) cachemgr.o $(CLIENT_LIBS)
 
@@ -276,6 +265,17 @@ cf.data: cf.data.pre Makefile
        s%@DEFAULT_PREFIX@%$(DEFAULT_PREFIX)%g;"\
        < $(srcdir)/cf.data.pre >$@
 
+store_modules.c: store_modules.sh Makefile 
+       @sh $(srcdir)/store_modules.sh $(STORE_MODULES) >store_modules.c
+
+store_modules.o: store_modules.c
+       $(CC) -c store_modules.c $(CFLAGS) -I$(srcdir)
+
+$(STORE_OBJS): fs/stamp
+
+store_modules fs/stamp:
+       @sh -c "cd fs && $(MAKE) all"
+
 install-mkdirs:
        -@if test ! -d $(prefix); then \
                echo "mkdir $(prefix)"; \
@@ -309,6 +309,10 @@ install-mkdirs:
 # Michael Lupp <mike@nemesis.saar.de> wants to know about additions
 # to the install target.
 install: all install-mkdirs
+       @for dir in $(SUBDIRS); do \
+               echo "Making $@ in $$dir..."; \
+               (cd $$dir ; $(MAKE) $(MFLAGS) prefix="$(prefix)" $@) || exit 1; \
+       done
        @for f in $(PROGS); do \
                if test -f $(bindir)/$$f; then \
                        echo $(MV) $(bindir)/$$f $(bindir)/-$$f; \
@@ -378,13 +382,23 @@ install-pinger:
 clean: 
        -rm -rf *.o *pure_* core $(PROGS) $(UTILS) $(CGIPROGS) $(SUID_UTILS)
        -rm -f cf_gen cf_gen_defines.h cf_parser.c cf.data globals.c string_arrays.c
+       -rm -f store_modules.c
+       @for dir in $(SUBDIRS); do \
+               echo "Making $@ in $$dir..."; \
+               (cd $$dir ; $(MAKE) $(MFLAGS) prefix="$(prefix)" $@) || exit 1; \
+       done
 
 distclean:     clean
        -rm -f Makefile squid.conf squid.conf.pre
        -rm -f Makefile.bak
+       -rm -f tags
+       @for dir in $(SUBDIRS); do \
+               echo "Making $@ in $$dir..."; \
+               (cd $$dir ; $(MAKE) $(MFLAGS) prefix="$(prefix)" $@) || exit 1; \
+       done
 
 tags:
        ctags *.[ch] ../include/*.h ../lib/*.[ch]
 
-depend:
-       $(MAKEDEPEND) -I../include -I. -fMakefile *.c
+depend: cf_parser.c
+       $(MAKEDEPEND) $(INCLUDE) -fMakefile $(srcdir)/*.c
index d4c9e961c2381e0119298d4f6a05f4f69f1b5166..82a253e2944f7fb5c04c302f376d0ea1dd793947 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * $Id: cache_cf.cc,v 1.342 2000/05/02 20:58:30 hno Exp $
+ * $Id: cache_cf.cc,v 1.343 2000/05/03 17:15:41 adrian Exp $
  *
  * DEBUG: section 3     Configuration File Parsing
  * AUTHOR: Harvest Derived
@@ -58,6 +58,7 @@ static const char *const list_sep = ", \t\n\r";
 static int http_header_first;
 static int http_header_allowed = 0;
 
+static void update_maxobjsize(void);
 static void configDoConfigure(void);
 static void parse_refreshpattern(refresh_t **);
 static int parseTimeUnits(const char *unit);
@@ -192,6 +193,19 @@ GetInteger(void)
     return i;
 }
 
+static void
+update_maxobjsize(void)
+{
+    int i;
+    size_t ms = -1;
+
+    for (i = 0; i < Config.cacheSwap.n_configured; i++) {
+        if (Config.cacheSwap.swapDirs[i].max_objsize > ms)
+            ms = Config.cacheSwap.swapDirs[i].max_objsize; 
+    }
+    store_maxobjsize = ms;
+}
+
 int
 parseConfigFile(const char *file_name)
 {
@@ -346,9 +360,7 @@ configDoConfigure(void)
 #if USE_DNSSERVERS
     requirePathnameExists("cache_dns_program", Config.Program.dnsserver);
 #endif
-#if USE_UNLINKD
     requirePathnameExists("unlinkd_program", Config.Program.unlinkd);
-#endif
     if (Config.Program.redirect)
        requirePathnameExists("redirect_program", Config.Program.redirect->key);
     if (Config.Program.authenticate)
@@ -802,16 +814,7 @@ dump_cachedir(StoreEntry * entry, const char *name, cacheSwap swap)
     int i;
     for (i = 0; i < swap.n_configured; i++) {
        s = swap.swapDirs + i;
-       switch (s->type) {
-       case SWAPDIR_UFS:
-       case SWAPDIR_ASYNCUFS:
-           storeUfsDirDump(entry, name, s);
-           break;
-       default:
-           debug(0, 0) ("dump_cachedir doesn't know about type %d\n",
-               (int) s->type);
-           break;
-       }
+        s->dump(entry, name, s);
     }
 }
 
@@ -844,25 +847,84 @@ allocate_new_swapdir(cacheSwap * swap)
     }
 }
 
+static int
+find_fstype(char *type)
+{
+    int i;
+    for (i = 0; storefs_list[i].typestr != NULL; i++) {
+        if (strcasecmp(type, storefs_list[i].typestr) == 0) {
+           return i;
+       }
+    }
+    return (-1);
+}
+
 static void
 parse_cachedir(cacheSwap * swap)
 {
     char *type_str;
+    char *path_str;
+    SwapDir *sd;
+    int i;
+    int fs;
+    size_t maxobjsize;
+
     if ((type_str = strtok(NULL, w_space)) == NULL)
        self_destruct();
-    if (0 == strcasecmp(type_str, "ufs")) {
-       storeUfsDirParse(swap);
-#if USE_ASYNC_IO
-    } else if (0 == strcasecmp(type_str, "asyncufs")) {
-       storeAufsDirParse(swap);
-#endif
-#if USE_DISKD
-    } else if (0 == strcasecmp(type_str, "diskd")) {
-       storeDiskdDirParse(swap);
-#endif
-    } else {
-       fatalf("Unknown cache_dir type '%s'\n", type_str);
+
+    maxobjsize = (size_t)GetInteger();
+
+    if ((path_str = strtok(NULL, w_space)) == NULL)
+       self_destruct();
+
+    /*
+     * This bit of code is a little strange.
+     * See, if we find a path and type match for a given line, then
+     * as long as we're reconfiguring, we can just call its reconfigure
+     * function. No harm there.
+     *
+     * Trouble is, if we find a path match, but not a type match, we have
+     * a dilemma - we could gracefully shut down the fs, kill it, and
+     * create a new one of a new type in its place, BUT at this stage the
+     * fs is meant to be the *NEW* one, and so things go very strange. :-)
+     *
+     * So, we'll assume the person isn't going to change the fs type for now,
+     * and XXX later on we will make sure that its picked up.
+     *
+     * (moving around cache_dir lines will be looked at later in a little
+     * more sane detail..)
+     */
+
+    for (i = 0; i < swap->n_configured; i++) {
+        if (0 == strcasecmp(path_str, swap->swapDirs[i].path)) {
+           /* This is a little weird, you'll appreciate it later */
+           fs = find_fstype(type_str);
+           if (fs < 0) {
+                fatalf("Unknown cache_dir type '%s'\n", type_str);
+           }
+           sd = swap->swapDirs + i;
+           storefs_list[fs].reconfigurefunc(sd, i, path_str);
+            sd->max_objsize = maxobjsize;
+            update_maxobjsize();
+            return;
+       }
+    }
+
+    fs = find_fstype(type_str);
+    if (fs < 0) {
+        /* If we get here, we didn't find a matching cache_dir type */
+        fatalf("Unknown cache_dir type '%s'\n", type_str);
     }
+
+    allocate_new_swapdir(swap);
+    sd = swap->swapDirs + swap->n_configured;
+    storefs_list[fs].parsefunc(sd, swap->n_configured, path_str);
+    /* XXX should we dupe the string here, in case it gets trodden on? */
+    sd->type = storefs_list[fs].typestr;
+    sd->max_objsize = maxobjsize;
+    swap->n_configured++;
+    /* Update the max object size */
+    update_maxobjsize();
 }
 
 static void
@@ -875,18 +937,8 @@ free_cachedir(cacheSwap * swap)
        return;
     for (i = 0; i < swap->n_configured; i++) {
        s = swap->swapDirs + i;
-       switch (s->type) {
-       case SWAPDIR_UFS:
-       case SWAPDIR_ASYNCUFS:
-           storeUfsDirFree(s);
-           break;
-       default:
-           debug(0, 0) ("dump_cachedir doesn't know about type %d\n",
-               (int) s->type);
-           break;
-       }
+       s->freefs(s);
        xfree(s->path);
-       filemapFreeMemory(s->map);
     }
     safe_free(swap->swapDirs);
     swap->swapDirs = NULL;
index 9389574d1046bebaf56beb5092be9c9034e90abe..79d0f4bdd2d18dd70fcb0e1d7e31ee65d5b95e19 100644 (file)
@@ -1,6 +1,6 @@
 
 #
-# $Id: cf.data.pre,v 1.178 2000/05/02 21:38:12 hno Exp $
+# $Id: cf.data.pre,v 1.179 2000/05/03 17:15:41 adrian Exp $
 #
 #
 # SQUID Internet Object Cache  http://squid.nlanr.net/Squid/
@@ -588,12 +588,12 @@ COMMENT_END
 NAME: cache_dir
 TYPE: cachedir
 DEFAULT: none
-DEFAULT_IF_NONE: ufs @DEFAULT_SWAP_DIR@ 100 16 256
+DEFAULT_IF_NONE: ufs -1 @DEFAULT_SWAP_DIR@ 100 16 256
 LOC: Config.cacheSwap
 DOC_START
        Usage:
        
-       cache_dir Type Directory-Name Mbytes Level-1 Level2
+       cache_dir Type Maxobjsize Directory-Name Mbytes Level-1 Level2
 
        You can specify multiple cache_dir lines to spread the
        cache among different disk partitions.
@@ -604,6 +604,10 @@ DOC_START
        want to try "asyncufs" as the type.  Async IO support may be
        buggy, however, so beware.
 
+       Maxobjsize refers to the max object size this storedir supports.
+       It is used to initially choose the storedir to dump the object.
+       -1 means 'any size'.
+
        'Directory' is a top-level directory where cache swap
        files will be stored.  If you want to use an entire disk
        for caching, then this can be the mount-point directory.
@@ -1872,6 +1876,9 @@ DOC_START
        default is `0' which disables sending the announcement
        messages.
 
+       To enable announcing your cache, just uncomment the line
+       below.
+
 NOCOMMENT_START
 #To enable announcing your cache, just uncomment the line below.
 #announce_period 1 day
index dcaeb0c714e67360dd398908fc7fbb9e762f02a1..5ec60483fcbfd4cc2801e7f81f4f4080b855c2ac 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * $Id: comm_select.cc,v 1.41 2000/03/06 16:23:30 wessels Exp $
+ * $Id: comm_select.cc,v 1.42 2000/05/03 17:15:41 adrian Exp $
  *
  * DEBUG: section 5     Socket Functions
  *
 
 #include "squid.h"
 
-#if USE_ASYNC_IO
-#define MAX_POLL_TIME 10
-#elif USE_DISKD
-#define MAX_POLL_TIME 10
-#else
-#define MAX_POLL_TIME 1000
-#endif
+static int MAX_POLL_TIME = 1000; /* see also comm_quick_poll_required() */
 
 #ifndef        howmany
 #define howmany(x, y)   (((x)+((y)-1))/(y))
@@ -332,12 +326,8 @@ comm_poll(int msec)
        getCurrentTime();
        start = current_dtime;
 #endif
-#if USE_ASYNC_IO
-       aioCheckCallbacks();
-#endif
-#if USE_DISKD
-       storeDiskdReadQueue();
-#endif
+    /* Handle any fs callbacks that need doing */
+    storeDirCallback();
 #if DELAY_POOLS
        FD_ZERO(&slowfds);
 #endif
@@ -645,6 +635,7 @@ comm_select(int msec)
     fd_set slowfds;
 #endif
     PF *hdl = NULL;
+    SwapDir *SD;
     int fd;
     int maxfd;
     int num;
@@ -662,19 +653,16 @@ comm_select(int msec)
     struct timeval poll_time;
     double timeout = current_dtime + (msec / 1000.0);
     fde *F;
+    int i;
     do {
 #if !ALARM_UPDATES_TIME
        getCurrentTime();
 #endif
-#if USE_ASYNC_IO
-       aioCheckCallbacks();
-#endif
-#if USE_DISKD
-       storeDiskdReadQueue();
-#endif
 #if DELAY_POOLS
        FD_ZERO(&slowfds);
 #endif
+        /* Handle any fs callbacks that need doing */
+        storeDirCallback();
        if (commCheckICPIncoming)
            comm_select_icp_incoming();
        if (commCheckDNSIncoming)
@@ -1090,3 +1078,10 @@ commUpdateWriteBits(int fd, PF * handler)
        nwritefds--;
     }
 }
+
+/* Called by async-io or diskd to speed up the polling */
+void
+comm_quick_poll_required(void)
+{
+    MAX_POLL_TIME = 10;
+}
index 9a68bd97f9e9ab963b241dd747bed4f859e16b90..e1c1fcf11336bcfc996de6a8a9af731e06b7a5d3 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * $Id: defines.h,v 1.79 2000/05/02 20:58:30 hno Exp $
+ * $Id: defines.h,v 1.80 2000/05/03 17:15:41 adrian Exp $
  *
  *
  * SQUID Internet Object Cache  http://squid.nlanr.net/Squid/
 #define LOG_DISABLE 0
 
 #define SM_PAGE_SIZE 4096
-#define DISK_PAGE_SIZE  8192
 
 #define EBIT_SET(flag, bit)    ((void)((flag) |= ((1L<<(bit)))))
 #define EBIT_CLR(flag, bit)    ((void)((flag) &= ~((1L<<(bit)))))
 #define _PATH_DEVNULL "/dev/null"
 #endif
 
-#if USE_ASYNC_IO
-#ifndef NUMTHREADS
-#define NUMTHREADS 16
-#endif
-#endif
-
-#if USE_ASYNC_IO
-#undef USE_UNLINKD
-#else
-#define USE_UNLINKD 1
-#endif
-
index bdd225de2492aa3554ff63ce10c9599c1c657d68..9c1eaf28c9c4daa69c4ff2ec63e16b39cbede0c0 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * $Id: disk.cc,v 1.147 2000/03/06 16:23:30 wessels Exp $
+ * $Id: disk.cc,v 1.148 2000/05/03 17:15:41 adrian Exp $
  *
  * DEBUG: section 6     Disk I/O Routines
  * AUTHOR: Harvest Derived
@@ -187,6 +187,8 @@ diskHandleWrite(int fd, void *notused)
     debug(6, 3) ("diskHandleWrite: FD %d writing %d bytes\n",
        fd, (int) (fdd->write_q->len - fdd->write_q->buf_offset));
     errno = 0;
+    if (fdd->write_q->file_offset != -1)
+        lseek(fd, fdd->write_q->file_offset, SEEK_SET);
     len = write(fd,
        fdd->write_q->buf + fdd->write_q->buf_offset,
        fdd->write_q->len - fdd->write_q->buf_offset);
index 7c9038ba6c2e57a2b273567b5cc1737eff0aa077..301abb32e6820e884e0052ee627fe36101ca8092 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * $Id: enums.h,v 1.166 2000/05/02 21:38:12 hno Exp $
+ * $Id: enums.h,v 1.167 2000/05/03 17:15:41 adrian Exp $
  *
  *
  * SQUID Internet Object Cache  http://squid.nlanr.net/Squid/
@@ -507,7 +507,6 @@ typedef enum {
     MEM_ACL_NAME_LIST,
     MEM_ACL_PROXY_AUTH_USER,
     MEM_ACL_TIME_DATA,
-    MEM_AIO_RESULT_T,
     MEM_CACHEMGR_PASSWD,
 #if USE_CACHE_DIGESTS
     MEM_CACHE_DIGEST,
@@ -521,7 +520,6 @@ typedef enum {
 #if USE_CACHE_DIGESTS
     MEM_DIGEST_FETCH_STATE,
 #endif
-    MEM_DISK_BUF,
     MEM_DLINK_LIST,
     MEM_DLINK_NODE,
     MEM_DNSSERVER_T,
@@ -660,12 +658,3 @@ enum {
     NETDB_EX_RTT,
     NETDB_EX_HOPS
 };
-
-typedef enum {
-    SWAPDIR_UFS,
-    SWAPDIR_ASYNCUFS,
-#if USE_DISKD
-    SWAPDIR_DISKD,
-#endif
-    SWAPDIR_MAX
-} swapdir_t;
index 4995a40ff6445e655bda6fb58fba34e29e8dafe9..c2a2af85fa8259ffaf5d31ae02c2a16fc32c4515 100644 (file)
--- a/src/fd.cc
+++ b/src/fd.cc
@@ -1,6 +1,6 @@
 
 /*
- * $Id: fd.cc,v 1.38 2000/03/06 16:23:31 wessels Exp $
+ * $Id: fd.cc,v 1.39 2000/05/03 17:15:42 adrian Exp $
  *
  * DEBUG: section 51    Filedescriptor Functions
  * AUTHOR: Duane Wessels
@@ -95,12 +95,6 @@ fd_open(int fd, unsigned int type, const char *desc)
 {
     fde *F = &fd_table[fd];
     assert(fd >= 0);
-#if USE_ASYNC_IO
-    if (F->flags.closing) {
-       /* Reuse of a closed FD before we have noticed it is closed */
-       fd_close(fd);
-    }
-#endif
     if (F->flags.open) {
        debug(51, 1) ("WARNING: Closing open FD %4d\n", fd);
        fd_close(fd);
diff --git a/src/fs/Makefile.in b/src/fs/Makefile.in
new file mode 100644 (file)
index 0000000..31e7f86
--- /dev/null
@@ -0,0 +1,35 @@
+#  Makefile for storage modules in the Squid Object Cache server
+#
+#  $Id: Makefile.in,v 1.1 2000/05/03 17:15:46 adrian Exp $
+#
+
+SUBDIRS                = @STORE_MODULES@
+
+all:
+       @test -z "$(SUBDIRS)" || for dir in $(SUBDIRS); do \
+          sh -c "cd $$dir && $(MAKE) all" || exit 1; \
+       done; \
+       if [ ! -f stamp ]; then \
+          touch stamp; \
+       fi
+
+clean:
+       -rm -f *.a stamp
+       @for dir in *; do \
+           if [ -f $$dir/Makefile ]; then \
+              sh -c "cd $$dir && $(MAKE) $@" || exit 1;\
+           fi; \
+       done
+
+distclean:
+       -rm -f *.a Makefile
+       @for dir in *; do \
+           if [ -f $$dir/Makefile ]; then \
+               sh -c "cd $$dir && $(MAKE) distclean"; \
+           fi; \
+       done
+
+.DEFAULT:
+       @test -z "$(SUBDIRS)" || for dir in $(SUBDIRS); do \
+          sh -c "cd $$dir && $(MAKE) $@" || exit 1; \
+       done
diff --git a/src/fs/aufs/Makefile.in b/src/fs/aufs/Makefile.in
new file mode 100644 (file)
index 0000000..a577db7
--- /dev/null
@@ -0,0 +1,58 @@
+#
+#  Makefile for the AUFS storage driver for the Squid Object Cache server
+#
+#  $Id: Makefile.in,v 1.1 2000/05/03 17:15:46 adrian Exp $
+#
+
+FS             = aufs
+
+top_srcdir     = @top_srcdir@
+VPATH          = @srcdir@
+
+CC             = @CC@
+MAKEDEPEND     = @MAKEDEPEND@
+AR_R           = @AR_R@
+RANLIB         = @RANLIB@
+AC_CFLAGS      = @CFLAGS@
+SHELL          = /bin/sh
+
+INCLUDE                = -I../../../include -I$(top_srcdir)/include -I$(top_srcdir)/src/
+CFLAGS                 = $(AC_CFLAGS) $(INCLUDE) $(DEFINES)
+
+OUT            = ../$(FS).a
+
+OBJS           = \
+               aiops.o \
+               async_io.o \
+               store_dir_aufs.o \
+               store_io_aufs.o
+
+
+all:    $(OUT)
+
+$(OUT): $(OBJS)
+       @rm -f ../stamp
+       $(AR_R) $(OUT) $(OBJS)
+       $(RANLIB) $(OUT)
+
+$(OBJS): $(top_srcdir)/include/version.h ../../../include/autoconf.h
+
+.c.o:
+       @rm -f ../stamp
+       $(CC) $(CFLAGS) -c $<
+
+clean: 
+       -rm -rf *.o *pure_* core ../$(FS).a
+
+distclean:     clean
+       -rm -f Makefile
+       -rm -f Makefile.bak
+       -rm -f tags
+
+install:
+
+tags:
+       ctags *.[ch] $(top_srcdir)/src/*.[ch] $(top_srcdir)/include/*.h $(top_srcdir)/lib/*.[ch]
+
+depend:
+       $(MAKEDEPEND) $(INCLUDE) -fMakefile *.c
diff --git a/src/fs/aufs/aiops.cc b/src/fs/aufs/aiops.cc
new file mode 100644 (file)
index 0000000..e99af58
--- /dev/null
@@ -0,0 +1,907 @@
+/*
+ * $Id: aiops.cc,v 1.1 2000/05/03 17:15:46 adrian Exp $
+ *
+ * DEBUG: section 43    AIOPS
+ * AUTHOR: Stewart Forster <slf@connect.com.au>
+ *
+ * SQUID Internet Object Cache  http://squid.nlanr.net/Squid/
+ * ----------------------------------------------------------
+ *
+ *  Squid is the result of efforts by numerous individuals from the
+ *  Internet community.  Development is led by Duane Wessels of the
+ *  National Laboratory for Applied Network Research and funded by the
+ *  National Science Foundation.  Squid is Copyrighted (C) 1998 by
+ *  Duane Wessels and the University of California San Diego.  Please
+ *  see the COPYRIGHT file for full details.  Squid incorporates
+ *  software developed and/or copyrighted by other sources.  Please see
+ *  the CREDITS file for full details.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *  
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *  
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
+ *
+ */
+
+#include "squid.h"
+#include "store_asyncufs.h"
+
+#include       <stdio.h>
+#include       <sys/types.h>
+#include       <sys/stat.h>
+#include       <fcntl.h>
+#include       <pthread.h>
+#include       <errno.h>
+#include       <dirent.h>
+#include       <signal.h>
+#if HAVE_SCHED_H
+#include       <sched.h>
+#endif
+
+#define RIDICULOUS_LENGTH      4096
+
+#if defined(_SQUID_LINUX_)
+/* Linux requires proper use of mutexes or it will segfault deep in the
+ * thread libraries. Observed on Alpha SMP Linux 2.2.10-ac12.
+ */
+#define AIO_PROPER_MUTEX 1
+#endif
+
+enum _aio_thread_status {
+    _THREAD_STARTING = 0,
+    _THREAD_WAITING,
+    _THREAD_BUSY,
+    _THREAD_FAILED,
+    _THREAD_DONE
+};
+
+enum _aio_request_type {
+    _AIO_OP_NONE = 0,
+    _AIO_OP_OPEN,
+    _AIO_OP_READ,
+    _AIO_OP_WRITE,
+    _AIO_OP_CLOSE,
+    _AIO_OP_UNLINK,
+    _AIO_OP_OPENDIR,
+    _AIO_OP_STAT
+};
+
+typedef struct aio_request_t {
+    enum _aio_request_type request_type;
+    int cancelled;
+    char *path;
+    int oflag;
+    mode_t mode;
+    int fd;
+    char *bufferp;
+    char *tmpbufp;
+    int buflen;
+    off_t offset;
+    int whence;
+    int ret;
+    int err;
+    struct stat *tmpstatp;
+    struct stat *statp;
+    aio_result_t *resultp;
+    struct aio_request_t *next;
+} aio_request_t;
+
+
+typedef struct aio_thread_t {
+    pthread_t thread;
+    enum _aio_thread_status status;
+    pthread_mutex_t mutex;     /* Mutex for testing condition variable */
+    pthread_cond_t cond;       /* Condition variable */
+    struct aio_request_t *volatile req;                /* set by main, cleared by thread */
+    struct aio_request_t *processed_req;       /* reminder to main */
+    struct aio_thread_t *next;
+} aio_thread_t;
+
+
+int aio_cancel(aio_result_t *);
+int aio_open(const char *, int, mode_t, aio_result_t *);
+int aio_read(int, char *, int, off_t, int, aio_result_t *);
+int aio_write(int, char *, int, off_t, int, aio_result_t *);
+int aio_close(int, aio_result_t *);
+int aio_unlink(const char *, aio_result_t *);
+int aio_opendir(const char *, aio_result_t *);
+aio_result_t *aio_poll_done();
+int aio_sync(void);
+
+static void aio_init(void);
+static void aio_queue_request(aio_request_t *);
+static void aio_process_request_queue(void);
+static void aio_cleanup_request(aio_request_t *);
+static void *aio_thread_loop(void *);
+static void aio_do_open(aio_request_t *);
+static void aio_do_read(aio_request_t *);
+static void aio_do_write(aio_request_t *);
+static void aio_do_close(aio_request_t *);
+static void aio_do_stat(aio_request_t *);
+static void aio_do_unlink(aio_request_t *);
+#if AIO_OPENDIR
+static void *aio_do_opendir(aio_request_t *);
+#endif
+static void aio_debug(aio_request_t *);
+static void aio_poll_threads(void);
+
+static aio_thread_t *threads;
+static int aio_initialised = 0;
+
+static int request_queue_len = 0;
+static MemPool *aio_request_pool = NULL;
+static aio_request_t *request_queue_head = NULL;
+static aio_request_t *request_queue_tail = NULL;
+static aio_request_t *request_done_head = NULL;
+static aio_request_t *request_done_tail = NULL;
+static aio_thread_t *wait_threads = NULL;
+static aio_thread_t *busy_threads_head = NULL;
+static aio_thread_t *busy_threads_tail = NULL;
+static pthread_attr_t globattr;
+static struct sched_param globsched;
+static pthread_t main_thread;
+
+static void
+aio_init(void)
+{
+    int i;
+    aio_thread_t *threadp;
+
+    if (aio_initialised)
+       return;
+
+    pthread_attr_init(&globattr);
+#if HAVE_PTHREAD_ATTR_SETSCOPE
+    pthread_attr_setscope(&globattr, PTHREAD_SCOPE_SYSTEM);
+#endif
+    globsched.sched_priority = 1;
+    main_thread = pthread_self();
+#if HAVE_PTHREAD_SETSCHEDPARAM
+    pthread_setschedparam(main_thread, SCHED_OTHER, &globsched);
+#endif
+    globsched.sched_priority = 2;
+#if HAVE_PTHREAD_ATTR_SETSCHEDPARAM
+    pthread_attr_setschedparam(&globattr, &globsched);
+#endif
+
+    /* Create threads and get them to sit in their wait loop */
+    threads = xcalloc(NUMTHREADS, sizeof(aio_thread_t));
+
+    for (i = 0; i < NUMTHREADS; i++) {
+       threadp = &threads[i];
+       threadp->status = _THREAD_STARTING;
+       if (pthread_mutex_init(&(threadp->mutex), NULL)) {
+           threadp->status = _THREAD_FAILED;
+           continue;
+       }
+       if (pthread_cond_init(&(threadp->cond), NULL)) {
+           threadp->status = _THREAD_FAILED;
+           continue;
+       }
+       threadp->req = NULL;
+       threadp->processed_req = NULL;
+       if (pthread_create(&threadp->thread, &globattr, aio_thread_loop, threadp)) {
+           fprintf(stderr, "Thread creation failed\n");
+           threadp->status = _THREAD_FAILED;
+           continue;
+       }
+       threadp->next = wait_threads;
+       wait_threads = threadp;
+#if AIO_PROPER_MUTEX
+       pthread_mutex_lock(&threadp->mutex);
+#endif
+    }
+
+    /* Create request pool */
+    aio_request_pool = memPoolCreate("aio_request", sizeof(aio_request_t));
+
+    aio_initialised = 1;
+}
+
+
+static void *
+aio_thread_loop(void *ptr)
+{
+    aio_thread_t *threadp = ptr;
+    aio_request_t *request;
+    sigset_t new;
+#if !AIO_PROPER_MUTEX
+    struct timespec wait_time;
+#endif
+
+    /*
+     * Make sure to ignore signals which may possibly get sent to
+     * the parent squid thread.  Causes havoc with mutex's and
+     * condition waits otherwise
+     */
+
+    sigemptyset(&new);
+    sigaddset(&new, SIGPIPE);
+    sigaddset(&new, SIGCHLD);
+#ifdef _SQUID_LINUX_THREADS_
+    sigaddset(&new, SIGQUIT);
+    sigaddset(&new, SIGTRAP);
+#else
+    sigaddset(&new, SIGUSR1);
+    sigaddset(&new, SIGUSR2);
+#endif
+    sigaddset(&new, SIGHUP);
+    sigaddset(&new, SIGTERM);
+    sigaddset(&new, SIGINT);
+    sigaddset(&new, SIGALRM);
+    pthread_sigmask(SIG_BLOCK, &new, NULL);
+
+    pthread_mutex_lock(&threadp->mutex);
+    while (1) {
+#if AIO_PROPER_MUTEX
+       while (threadp->req == NULL) {
+           threadp->status = _THREAD_WAITING;
+           pthread_cond_wait(&threadp->cond, &threadp->mutex);
+       }
+#else
+       /* The timeout is used to unlock the race condition where
+        * ->req is set between the check and pthread_cond_wait.
+        * The thread steps it's own clock on each timeout, to avoid a CPU
+        * spin situation if the main thread is suspended (paging), and
+        * squid_curtime is not being updated timely.
+        */
+       wait_time.tv_sec = squid_curtime + 1;   /* little quicker first time */
+       wait_time.tv_nsec = 0;
+       while (threadp->req == NULL) {
+           threadp->status = _THREAD_WAITING;
+           pthread_cond_timedwait(&threadp->cond, &threadp->mutex, &wait_time);
+           wait_time.tv_sec += 3;      /* then wait 3 seconds between each check */
+       }
+#endif
+       request = threadp->req;
+       errno = 0;
+       if (!request->cancelled) {
+           switch (request->request_type) {
+           case _AIO_OP_OPEN:
+               aio_do_open(request);
+               break;
+           case _AIO_OP_READ:
+               aio_do_read(request);
+               break;
+           case _AIO_OP_WRITE:
+               aio_do_write(request);
+               break;
+           case _AIO_OP_CLOSE:
+               aio_do_close(request);
+               break;
+           case _AIO_OP_UNLINK:
+               aio_do_unlink(request);
+               break;
+#if AIO_OPENDIR                        /* Opendir not implemented yet */
+           case _AIO_OP_OPENDIR:
+               aio_do_opendir(request);
+               break;
+#endif
+           case _AIO_OP_STAT:
+               aio_do_stat(request);
+               break;
+           default:
+               request->ret = -1;
+               request->err = EINVAL;
+               break;
+           }
+       } else {                /* cancelled */
+           request->ret = -1;
+           request->err = EINTR;
+       }
+       threadp->req = NULL;    /* tells main thread that we are done */
+    }                          /* while */
+    return NULL;
+}                              /* aio_thread_loop */
+
+static void
+aio_do_request(aio_request_t * requestp)
+{
+    if (wait_threads == NULL && busy_threads_head == NULL) {
+       fprintf(stderr, "PANIC: No threads to service requests with!\n");
+       exit(-1);
+    }
+    aio_queue_request(requestp);
+}                              /* aio_do_request */
+
+
+static void
+aio_queue_request(aio_request_t * requestp)
+{
+    aio_request_t *rp;
+    static int last_warn = 0;
+    static int high_start = 0;
+    static int queue_high, queue_low;
+    int i;
+
+    /* Mark it as not executed (failing result, no error) */
+    requestp->ret = -1;
+    requestp->err = 0;
+    /* Queue it on the request queue */
+    if (request_queue_head == NULL) {
+       request_queue_head = requestp;
+       request_queue_tail = requestp;
+    } else {
+       request_queue_tail->next = requestp;
+       request_queue_tail = requestp;
+    }
+    requestp->next = NULL;
+    request_queue_len += 1;
+    /* Poll done threads if needed */
+    if (wait_threads == NULL)
+       aio_poll_threads();
+    /* Kick it rolling */
+    aio_process_request_queue();
+    /* Warn if out of threads */
+    if (request_queue_len > (NUMTHREADS >> 1)) {
+       if (high_start == 0) {
+           high_start = squid_curtime;
+           queue_high = request_queue_len;
+           queue_low = request_queue_len;
+       }
+       if (request_queue_len > queue_high)
+           queue_high = request_queue_len;
+       if (request_queue_len < queue_low)
+           queue_low = request_queue_len;
+       if (squid_curtime >= (last_warn + 15) &&
+           squid_curtime >= (high_start + 3)) {
+           debug(43, 1) ("aio_queue_request: WARNING - Running out of I/O threads\n");
+           debug(43, 2) ("aio_queue_request: Queue Length: current=%d, high=%d, low=%d, duration=%d\n",
+               request_queue_len, queue_high, queue_low, squid_curtime - high_start);
+           debug(43, 1) ("aio_queue_request: Perhaps you should increase NUMTHREADS\n");
+           debug(43, 1) ("aio_queue_request: Or install more disks to share the load\n");
+           debug(43, 3) ("aio_queue_request: First %d items on request queue\n", NUMTHREADS);
+           rp = request_queue_head;
+           for (i = 1; i <= NUMTHREADS; i++) {
+               switch (rp->request_type) {
+               case _AIO_OP_OPEN:
+                   debug(43, 3) ("aio_queue_request: %d : open -> %s\n", i, rp->path);
+                   break;
+               case _AIO_OP_READ:
+                   debug(43, 3) ("aio_queue_request: %d : read -> FD = %d\n", i, rp->fd);
+                   break;
+               case _AIO_OP_WRITE:
+                   debug(43, 3) ("aio_queue_request: %d : write -> FD = %d\n", i, rp->fd);
+                   break;
+               case _AIO_OP_CLOSE:
+                   debug(43, 3) ("aio_queue_request: %d : close -> FD = %d\n", i, rp->fd);
+                   break;
+               case _AIO_OP_UNLINK:
+                   debug(43, 3) ("aio_queue_request: %d : unlink -> %s\n", i, rp->path);
+                   break;
+               case _AIO_OP_STAT:
+                   debug(43, 3) ("aio_queue_request: %d : stat -> %s\n", i, rp->path);
+                   break;
+               default:
+                   debug(43, 1) ("aio_queue_request: %d : Unimplemented request type: %d\n", i, rp->request_type);
+                   break;
+               }
+               if ((rp = rp->next) == NULL)
+                   break;
+           }
+           last_warn = squid_curtime;
+       }
+    } else {
+       high_start = 0;
+    }
+    if (request_queue_len > RIDICULOUS_LENGTH) {
+       debug(43, 0) ("aio_queue_request: Async request queue growing uncontrollably!\n");
+       debug(43, 0) ("aio_queue_request: Syncing pending I/O operations.. (blocking)\n");
+       aio_sync();
+       debug(43, 0) ("aio_queue_request: Synced\n");
+    }
+}                              /* aio_queue_request */
+
+
+static void
+aio_process_request_queue(void)
+{
+    aio_thread_t *threadp;
+    aio_request_t *requestp;
+
+    for (;;) {
+       if (wait_threads == NULL || request_queue_head == NULL)
+           return;
+
+       requestp = request_queue_head;
+       if ((request_queue_head = requestp->next) == NULL)
+           request_queue_tail = NULL;
+       requestp->next = NULL;
+       request_queue_len--;
+
+       if (requestp->cancelled) {
+           aio_cleanup_request(requestp);
+           continue;
+       }
+       threadp = wait_threads;
+       wait_threads = threadp->next;
+       threadp->next = NULL;
+
+       if (busy_threads_head != NULL)
+           busy_threads_tail->next = threadp;
+       else
+           busy_threads_head = threadp;
+       busy_threads_tail = threadp;
+
+       threadp->status = _THREAD_BUSY;
+       threadp->req = threadp->processed_req = requestp;
+       pthread_cond_signal(&(threadp->cond));
+#if AIO_PROPER_MUTEX
+       pthread_mutex_unlock(&threadp->mutex);
+#endif
+    }
+}                              /* aio_process_request_queue */
+
+
+static void
+aio_cleanup_request(aio_request_t * requestp)
+{
+    aio_result_t *resultp = requestp->resultp;
+    int cancelled = requestp->cancelled;
+
+    /* Free allocated structures and copy data back to user space if the */
+    /* request hasn't been cancelled */
+    switch (requestp->request_type) {
+    case _AIO_OP_STAT:
+       if (!cancelled && requestp->ret == 0)
+           xmemcpy(requestp->statp, requestp->tmpstatp, sizeof(struct stat));
+       xfree(requestp->tmpstatp);
+    case _AIO_OP_OPEN:
+       if (cancelled && requestp->ret >= 0)
+           /* The open() was cancelled but completed */
+           close(requestp->ret);
+       xfree(requestp->path);
+       break;
+    case _AIO_OP_CLOSE:
+       if (cancelled && requestp->ret < 0)
+           /* The close() was cancelled and never got executed */
+           close(requestp->fd);
+       break;
+    case _AIO_OP_UNLINK:
+    case _AIO_OP_OPENDIR:
+       xfree(requestp->path);
+       break;
+    case _AIO_OP_READ:
+       if (!cancelled && requestp->ret > 0)
+           xmemcpy(requestp->bufferp, requestp->tmpbufp, requestp->ret);
+    case _AIO_OP_WRITE:
+       xfree(requestp->tmpbufp);
+       break;
+    default:
+       break;
+    }
+    if (resultp != NULL && !cancelled) {
+       resultp->aio_return = requestp->ret;
+       resultp->aio_errno = requestp->err;
+    }
+    memPoolFree(aio_request_pool, requestp);
+}                              /* aio_cleanup_request */
+
+
+int
+aio_cancel(aio_result_t * resultp)
+{
+    aio_thread_t *threadp;
+    aio_request_t *requestp;
+
+    for (threadp = busy_threads_head; threadp != NULL; threadp = threadp->next)
+       if (threadp->processed_req->resultp == resultp) {
+           threadp->processed_req->cancelled = 1;
+           threadp->processed_req->resultp = NULL;
+           return 0;
+       }
+    for (requestp = request_queue_head; requestp != NULL; requestp = requestp->next)
+       if (requestp->resultp == resultp) {
+           requestp->cancelled = 1;
+           requestp->resultp = NULL;
+           return 0;
+       }
+    for (requestp = request_done_head; requestp != NULL; requestp = requestp->next)
+       if (requestp->resultp == resultp) {
+           requestp->cancelled = 1;
+           requestp->resultp = NULL;
+           return 0;
+       }
+    return 1;
+}                              /* aio_cancel */
+
+
+int
+aio_open(const char *path, int oflag, mode_t mode, aio_result_t * resultp)
+{
+    aio_request_t *requestp;
+    int len;
+
+    if (!aio_initialised)
+       aio_init();
+    if ((requestp = memPoolAlloc(aio_request_pool)) == NULL) {
+       errno = ENOMEM;
+       return -1;
+    }
+    len = strlen(path) + 1;
+    if ((requestp->path = (char *) xmalloc(len)) == NULL) {
+       memPoolFree(aio_request_pool, requestp);
+       errno = ENOMEM;
+       return -1;
+    }
+    strncpy(requestp->path, path, len);
+    requestp->oflag = oflag;
+    requestp->mode = mode;
+    requestp->resultp = resultp;
+    requestp->request_type = _AIO_OP_OPEN;
+    requestp->cancelled = 0;
+
+    aio_do_request(requestp);
+    return 0;
+}
+
+
+static void
+aio_do_open(aio_request_t * requestp)
+{
+    requestp->ret = open(requestp->path, requestp->oflag, requestp->mode);
+    requestp->err = errno;
+}
+
+
+int
+aio_read(int fd, char *bufp, int bufs, off_t offset, int whence, aio_result_t * resultp)
+{
+    aio_request_t *requestp;
+
+    if (!aio_initialised)
+       aio_init();
+    if ((requestp = memPoolAlloc(aio_request_pool)) == NULL) {
+       errno = ENOMEM;
+       return -1;
+    }
+    requestp->fd = fd;
+    requestp->bufferp = bufp;
+    if ((requestp->tmpbufp = (char *) xmalloc(bufs)) == NULL) {
+       memPoolFree(aio_request_pool, requestp);
+       errno = ENOMEM;
+       return -1;
+    }
+    requestp->buflen = bufs;
+    requestp->offset = offset;
+    requestp->whence = whence;
+    requestp->resultp = resultp;
+    requestp->request_type = _AIO_OP_READ;
+    requestp->cancelled = 0;
+
+    aio_do_request(requestp);
+    return 0;
+}
+
+
+static void
+aio_do_read(aio_request_t * requestp)
+{
+    lseek(requestp->fd, requestp->offset, requestp->whence);
+    requestp->ret = read(requestp->fd, requestp->tmpbufp, requestp->buflen);
+    requestp->err = errno;
+}
+
+
+int
+aio_write(int fd, char *bufp, int bufs, off_t offset, int whence, aio_result_t * resultp)
+{
+    aio_request_t *requestp;
+
+    if (!aio_initialised)
+       aio_init();
+    if ((requestp = memPoolAlloc(aio_request_pool)) == NULL) {
+       errno = ENOMEM;
+       return -1;
+    }
+    requestp->fd = fd;
+    if ((requestp->tmpbufp = (char *) xmalloc(bufs)) == NULL) {
+       memPoolFree(aio_request_pool, requestp);
+       errno = ENOMEM;
+       return -1;
+    }
+    xmemcpy(requestp->tmpbufp, bufp, bufs);
+    requestp->buflen = bufs;
+    requestp->offset = offset;
+    requestp->whence = whence;
+    requestp->resultp = resultp;
+    requestp->request_type = _AIO_OP_WRITE;
+    requestp->cancelled = 0;
+
+    aio_do_request(requestp);
+    return 0;
+}
+
+
+static void
+aio_do_write(aio_request_t * requestp)
+{
+    requestp->ret = write(requestp->fd, requestp->tmpbufp, requestp->buflen);
+    requestp->err = errno;
+}
+
+
+int
+aio_close(int fd, aio_result_t * resultp)
+{
+    aio_request_t *requestp;
+
+    if (!aio_initialised)
+       aio_init();
+    if ((requestp = memPoolAlloc(aio_request_pool)) == NULL) {
+       errno = ENOMEM;
+       return -1;
+    }
+    requestp->fd = fd;
+    requestp->resultp = resultp;
+    requestp->request_type = _AIO_OP_CLOSE;
+    requestp->cancelled = 0;
+
+    aio_do_request(requestp);
+    return 0;
+}
+
+
+static void
+aio_do_close(aio_request_t * requestp)
+{
+    requestp->ret = close(requestp->fd);
+    requestp->err = errno;
+}
+
+
+int
+aio_stat(const char *path, struct stat *sb, aio_result_t * resultp)
+{
+    aio_request_t *requestp;
+    int len;
+
+    if (!aio_initialised)
+       aio_init();
+    if ((requestp = memPoolAlloc(aio_request_pool)) == NULL) {
+       errno = ENOMEM;
+       return -1;
+    }
+    len = strlen(path) + 1;
+    if ((requestp->path = (char *) xmalloc(len)) == NULL) {
+       memPoolFree(aio_request_pool, requestp);
+       errno = ENOMEM;
+       return -1;
+    }
+    strncpy(requestp->path, path, len);
+    requestp->statp = sb;
+    if ((requestp->tmpstatp = (struct stat *) xmalloc(sizeof(struct stat))) == NULL) {
+       xfree(requestp->path);
+       memPoolFree(aio_request_pool, requestp);
+       errno = ENOMEM;
+       return -1;
+    }
+    requestp->resultp = resultp;
+    requestp->request_type = _AIO_OP_STAT;
+    requestp->cancelled = 0;
+
+    aio_do_request(requestp);
+    return 0;
+}
+
+
+static void
+aio_do_stat(aio_request_t * requestp)
+{
+    requestp->ret = stat(requestp->path, requestp->tmpstatp);
+    requestp->err = errno;
+}
+
+
+int
+aio_unlink(const char *path, aio_result_t * resultp)
+{
+    aio_request_t *requestp;
+    int len;
+
+    if (!aio_initialised)
+       aio_init();
+    if ((requestp = memPoolAlloc(aio_request_pool)) == NULL) {
+       errno = ENOMEM;
+       return -1;
+    }
+    len = strlen(path) + 1;
+    if ((requestp->path = (char *) xmalloc(len)) == NULL) {
+       memPoolFree(aio_request_pool, requestp);
+       errno = ENOMEM;
+       return -1;
+    }
+    strncpy(requestp->path, path, len);
+    requestp->resultp = resultp;
+    requestp->request_type = _AIO_OP_UNLINK;
+    requestp->cancelled = 0;
+
+    aio_do_request(requestp);
+    return 0;
+}
+
+
+static void
+aio_do_unlink(aio_request_t * requestp)
+{
+    requestp->ret = unlink(requestp->path);
+    requestp->err = errno;
+}
+
+
+#if AIO_OPENDIR
+/* XXX aio_opendir NOT implemented? */
+
+int
+aio_opendir(const char *path, aio_result_t * resultp)
+{
+    aio_request_t *requestp;
+    int len;
+
+    if (!aio_initialised)
+       aio_init();
+    if ((requestp = memPoolAlloc(aio_request_pool)) == NULL) {
+       errno = ENOMEM;
+       return -1;
+    }
+    return -1;
+}
+
+static void
+aio_do_opendir(aio_request_t * requestp)
+{
+    /* NOT IMPLEMENTED */
+}
+
+#endif
+
+
+void
+aio_poll_threads(void)
+{
+    aio_thread_t *prev;
+    aio_thread_t *threadp;
+    aio_request_t *requestp;
+
+    do {                       /* while found completed thread */
+       prev = NULL;
+       threadp = busy_threads_head;
+       while (threadp) {
+           debug(43, 9) ("aio_poll_threads: %p: request type %d -> status %d\n",
+               threadp,
+               threadp->processed_req->request_type,
+               threadp->status);
+#if AIO_PROPER_MUTEX
+           if (threadp->req == NULL)
+               if (pthread_mutex_trylock(&threadp->mutex) == 0)
+                   break;
+#else
+           if (threadp->req == NULL)
+               break;
+#endif
+           prev = threadp;
+           threadp = threadp->next;
+       }
+       if (threadp == NULL)
+           break;
+
+       if (prev == NULL)
+           busy_threads_head = busy_threads_head->next;
+       else
+           prev->next = threadp->next;
+
+       if (busy_threads_tail == threadp)
+           busy_threads_tail = prev;
+
+       requestp = threadp->processed_req;
+       threadp->processed_req = NULL;
+
+       threadp->next = wait_threads;
+       wait_threads = threadp;
+
+       if (request_done_tail != NULL)
+           request_done_tail->next = requestp;
+       else
+           request_done_head = requestp;
+       request_done_tail = requestp;
+    } while (threadp);
+
+    aio_process_request_queue();
+}                              /* aio_poll_threads */
+
+aio_result_t *
+aio_poll_done(void)
+{
+    aio_request_t *requestp;
+    aio_result_t *resultp;
+    int cancelled;
+
+  AIO_REPOLL:
+    aio_poll_threads();
+    if (request_done_head == NULL) {
+       return NULL;
+    }
+    requestp = request_done_head;
+    request_done_head = requestp->next;
+    if (!request_done_head)
+       request_done_tail = NULL;
+
+    resultp = requestp->resultp;
+    cancelled = requestp->cancelled;
+    aio_debug(requestp);
+    debug(43, 5) ("DONE: %d -> %d\n", requestp->ret, requestp->err);
+    aio_cleanup_request(requestp);
+    if (cancelled)
+       goto AIO_REPOLL;
+    return resultp;
+}                              /* aio_poll_done */
+
+int
+aio_operations_pending(void)
+{
+    return request_queue_len + (request_done_head != NULL) + (busy_threads_head != NULL);
+}
+
+int
+aio_overloaded(void)
+{
+    static time_t last_warn = 0;
+    if (aio_operations_pending() > RIDICULOUS_LENGTH / 4) {
+       if (squid_curtime >= (last_warn + 15)) {
+           debug(43, 0) ("Warning: Async-IO overloaded\n");
+           last_warn = squid_curtime;
+       }
+       return 1;
+    }
+    return 0;
+}
+
+int
+aio_sync(void)
+{
+    int loop_count = 0;
+    do {
+       aio_poll_threads();
+       assert(++loop_count < 10);
+    } while (request_queue_len > 0);
+    return aio_operations_pending();
+}
+
+int
+aio_get_queue_len(void)
+{
+    return request_queue_len;
+}
+
+static void
+aio_debug(aio_request_t * requestp)
+{
+    switch (requestp->request_type) {
+    case _AIO_OP_OPEN:
+       debug(43, 5) ("OPEN of %s to FD %d\n", requestp->path, requestp->ret);
+       break;
+    case _AIO_OP_READ:
+       debug(43, 5) ("READ on fd: %d\n", requestp->fd);
+       break;
+    case _AIO_OP_WRITE:
+       debug(43, 5) ("WRITE on fd: %d\n", requestp->fd);
+       break;
+    case _AIO_OP_CLOSE:
+       debug(43, 5) ("CLOSE of fd: %d\n", requestp->fd);
+       break;
+    case _AIO_OP_UNLINK:
+       debug(43, 5) ("UNLINK of %s\n", requestp->path);
+       break;
+    default:
+       break;
+    }
+}
diff --git a/src/fs/aufs/async_io.cc b/src/fs/aufs/async_io.cc
new file mode 100644 (file)
index 0000000..084b4c6
--- /dev/null
@@ -0,0 +1,426 @@
+
+/*
+ * $Id: async_io.cc,v 1.1 2000/05/03 17:15:46 adrian Exp $
+ *
+ * DEBUG: section 32    Asynchronous Disk I/O
+ * AUTHOR: Pete Bentley <pete@demon.net>
+ * AUTHOR: Stewart Forster <slf@connect.com.au>
+ *
+ * SQUID Internet Object Cache  http://squid.nlanr.net/Squid/
+ * ----------------------------------------------------------
+ *
+ *  Squid is the result of efforts by numerous individuals from the
+ *  Internet community.  Development is led by Duane Wessels of the
+ *  National Laboratory for Applied Network Research and funded by the
+ *  National Science Foundation.  Squid is Copyrighted (C) 1998 by
+ *  Duane Wessels and the University of California San Diego.  Please
+ *  see the COPYRIGHT file for full details.  Squid incorporates
+ *  software developed and/or copyrighted by other sources.  Please see
+ *  the CREDITS file for full details.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *  
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *  
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
+ *
+ */
+
+#include "squid.h"
+#include "store_asyncufs.h"
+
+#define _AIO_OPEN      0
+#define _AIO_READ      1
+#define _AIO_WRITE     2
+#define _AIO_CLOSE     3
+#define _AIO_UNLINK    4
+#define _AIO_OPENDIR   5
+#define _AIO_STAT      6
+
+typedef struct aio_ctrl_t {
+    struct aio_ctrl_t *next;
+    int fd;
+    int operation;
+    AIOCB *done_handler;
+    void *done_handler_data;
+    aio_result_t result;
+} aio_ctrl_t;
+
+struct {
+    int open;
+    int close;
+    int cancel;
+    int write;
+    int read;
+    int stat;
+    int unlink;
+    int check_callback;
+} aio_counts;
+
+typedef struct aio_unlinkq_t {
+    char *path;
+    struct aio_unlinkq_t *next;
+} aio_unlinkq_t;
+
+static aio_ctrl_t *used_list = NULL;
+static int initialised = 0;
+static OBJH aioStats;
+static MemPool *aio_ctrl_pool;
+static void aioFDWasClosed(int fd);
+
+MemPool * aio_state_pool;
+
+static void
+aioFDWasClosed(int fd)
+{
+    if (fd_table[fd].flags.closing)
+       fd_close(fd);
+}
+
+void
+aioInit(void)
+{
+    if (initialised)
+       return;
+    aio_ctrl_pool = memPoolCreate("aio_ctrl", sizeof(aio_ctrl_t));
+    aio_state_pool = memPoolCreate("Async UFS IO State data", sizeof(aiostate_t));
+    cachemgrRegister("aio_counts", "Async IO Function Counters",
+       aioStats, 0, 1);
+    initialised = 1;
+    comm_quick_poll_required();
+}
+
+void
+aioDone(void)
+{
+    memPoolDestroy(aio_ctrl_pool);
+    memPoolDestroy(aio_state_pool);
+    initialised = 0;
+}
+
+void
+aioOpen(const char *path, int oflag, mode_t mode, AIOCB * callback, void *callback_data)
+{
+    aio_ctrl_t *ctrlp;
+    int ret;
+
+    assert(initialised);
+    aio_counts.open++;
+    ctrlp = memPoolAlloc(aio_ctrl_pool);
+    ctrlp->fd = -2;
+    ctrlp->done_handler = callback;
+    ctrlp->done_handler_data = callback_data;
+    ctrlp->operation = _AIO_OPEN;
+    cbdataLock(callback_data);
+    if (aio_open(path, oflag, mode, &ctrlp->result) < 0) {
+       ret = open(path, oflag, mode);
+       if (callback)
+           (callback) (ctrlp->fd, callback_data, ret, errno);
+       cbdataUnlock(callback_data);
+       memPoolFree(aio_ctrl_pool, ctrlp);
+       return;
+    }
+    ctrlp->next = used_list;
+    used_list = ctrlp;
+    return;
+}
+
+void
+aioClose(int fd)
+{
+    aio_ctrl_t *ctrlp;
+
+    assert(initialised);
+    aio_counts.close++;
+    aioCancel(fd);
+    ctrlp = memPoolAlloc(aio_ctrl_pool);
+    ctrlp->fd = fd;
+    ctrlp->done_handler = NULL;
+    ctrlp->done_handler_data = NULL;
+    ctrlp->operation = _AIO_CLOSE;
+    if (aio_close(fd, &ctrlp->result) < 0) {
+       close(fd);              /* Can't create thread - do a normal close */
+       memPoolFree(aio_ctrl_pool, ctrlp);
+       aioFDWasClosed(fd);
+       return;
+    }
+    ctrlp->next = used_list;
+    used_list = ctrlp;
+    return;
+}
+
+void
+aioCancel(int fd)
+{
+    aio_ctrl_t *curr;
+    aio_ctrl_t *prev;
+    aio_ctrl_t *next;
+    AIOCB *done_handler;
+    void *their_data;
+
+    assert(initialised);
+    aio_counts.cancel++;
+    prev = NULL;
+    curr = used_list;
+    for (curr = used_list;; curr = next) {
+       while (curr != NULL) {
+           if (curr->fd == fd)
+               break;
+           prev = curr;
+           curr = curr->next;
+       }
+       if (curr == NULL)
+           break;
+
+       aio_cancel(&curr->result);
+
+       if ((done_handler = curr->done_handler)) {
+           their_data = curr->done_handler_data;
+           curr->done_handler = NULL;
+           curr->done_handler_data = NULL;
+           debug(0, 0) ("this be aioCancel\n");
+           if (cbdataValid(their_data))
+               done_handler(fd, their_data, -2, -2);
+           cbdataUnlock(their_data);
+       }
+       next = curr->next;
+       if (prev == NULL)
+           used_list = next;
+       else
+           prev->next = next;
+
+       memPoolFree(aio_ctrl_pool, curr);
+    }
+}
+
+
+void
+aioWrite(int fd, int offset, char *bufp, int len, AIOCB * callback, void *callback_data, FREE * free_func)
+{
+    aio_ctrl_t *ctrlp;
+    int seekmode;
+
+    assert(initialised);
+    aio_counts.write++;
+    for (ctrlp = used_list; ctrlp != NULL; ctrlp = ctrlp->next)
+       if (ctrlp->fd == fd)
+           break;
+    if (ctrlp != NULL) {
+       debug(0, 0) ("aioWrite: EWOULDBLOCK\n");
+       errno = EWOULDBLOCK;
+       if (callback)
+           (callback) (fd, callback_data, -1, errno);
+       if (free_func)
+            free_func(bufp);
+       return;
+    }
+    ctrlp = memPoolAlloc(aio_ctrl_pool);
+    ctrlp->fd = fd;
+    ctrlp->done_handler = callback;
+    ctrlp->done_handler_data = callback_data;
+    ctrlp->operation = _AIO_WRITE;
+    if (offset >= 0)
+       seekmode = SEEK_SET;
+    else {
+       seekmode = SEEK_END;
+       offset = 0;
+    }
+    cbdataLock(callback_data);
+    if (aio_write(fd, bufp, len, offset, seekmode, &ctrlp->result) < 0) {
+       if (errno == ENOMEM || errno == EAGAIN || errno == EINVAL)
+           errno = EWOULDBLOCK;
+       if (callback)
+           (callback) (fd, callback_data, -1, errno);
+       cbdataUnlock(callback_data);
+       memPoolFree(aio_ctrl_pool, ctrlp);
+    } else {
+       ctrlp->next = used_list;
+       used_list = ctrlp;
+    }
+    /*
+     * aio_write copies the buffer so we can free it here
+     */
+    if (free_func)
+        free_func(bufp);
+}                              /* aioWrite */
+
+
+void
+aioRead(int fd, int offset, char *bufp, int len, AIOCB * callback, void *callback_data)
+{
+    aio_ctrl_t *ctrlp;
+    int seekmode;
+
+    assert(initialised);
+    aio_counts.read++;
+    for (ctrlp = used_list; ctrlp != NULL; ctrlp = ctrlp->next)
+       if (ctrlp->fd == fd)
+           break;
+    if (ctrlp != NULL) {
+       errno = EWOULDBLOCK;
+       if (callback)
+           (callback) (fd, callback_data, -1, errno);
+       return;
+    }
+    ctrlp = memPoolAlloc(aio_ctrl_pool);
+    ctrlp->fd = fd;
+    ctrlp->done_handler = callback;
+    ctrlp->done_handler_data = callback_data;
+    ctrlp->operation = _AIO_READ;
+    if (offset >= 0)
+       seekmode = SEEK_SET;
+    else {
+       seekmode = SEEK_CUR;
+       offset = 0;
+    }
+    cbdataLock(callback_data);
+    if (aio_read(fd, bufp, len, offset, seekmode, &ctrlp->result) < 0) {
+       if (errno == ENOMEM || errno == EAGAIN || errno == EINVAL)
+           errno = EWOULDBLOCK;
+       if (callback)
+           (callback) (fd, callback_data, -1, errno);
+       cbdataUnlock(callback_data);
+       memPoolFree(aio_ctrl_pool, ctrlp);
+       return;
+    }
+    ctrlp->next = used_list;
+    used_list = ctrlp;
+    return;
+}                              /* aioRead */
+
+void
+aioStat(char *path, struct stat *sb, AIOCB * callback, void *callback_data)
+{
+    aio_ctrl_t *ctrlp;
+
+    assert(initialised);
+    aio_counts.stat++;
+    ctrlp = memPoolAlloc(aio_ctrl_pool);
+    ctrlp->fd = -2;
+    ctrlp->done_handler = callback;
+    ctrlp->done_handler_data = callback_data;
+    ctrlp->operation = _AIO_STAT;
+    cbdataLock(callback_data);
+    if (aio_stat(path, sb, &ctrlp->result) < 0) {
+       if (errno == ENOMEM || errno == EAGAIN || errno == EINVAL)
+           errno = EWOULDBLOCK;
+       if (callback)
+           (callback) (ctrlp->fd, callback_data, -1, errno);
+       cbdataUnlock(callback_data);
+       memPoolFree(aio_ctrl_pool, ctrlp);
+       return;
+    }
+    ctrlp->next = used_list;
+    used_list = ctrlp;
+    return;
+}                              /* aioStat */
+
+void
+aioUnlink(const char *pathname, AIOCB * callback, void *callback_data)
+{
+    aio_ctrl_t *ctrlp;
+    char *path;
+    assert(initialised);
+    aio_counts.unlink++;
+    ctrlp = memPoolAlloc(aio_ctrl_pool);
+    ctrlp->fd = -2;
+    ctrlp->done_handler = callback;
+    ctrlp->done_handler_data = callback_data;
+    ctrlp->operation = _AIO_UNLINK;
+    path = xstrdup(pathname);
+    cbdataLock(callback_data);
+    if (aio_unlink(path, &ctrlp->result) < 0) {
+       int ret = unlink(path);
+       (callback) (ctrlp->fd, callback_data, ret, errno);
+       cbdataUnlock(callback_data);
+       memPoolFree(aio_ctrl_pool, ctrlp);
+       xfree(path);
+       return;
+    }
+    ctrlp->next = used_list;
+    used_list = ctrlp;
+    xfree(path);
+}                              /* aioUnlink */
+
+
+void
+aioCheckCallbacks(SwapDir *SD)
+{
+    aio_result_t *resultp;
+    aio_ctrl_t *ctrlp;
+    aio_ctrl_t *prev;
+    AIOCB *done_handler;
+    void *their_data;
+
+    assert(initialised);
+    aio_counts.check_callback++;
+    for (;;) {
+       if ((resultp = aio_poll_done()) == NULL)
+           break;
+       prev = NULL;
+       for (ctrlp = used_list; ctrlp != NULL; prev = ctrlp, ctrlp = ctrlp->next)
+           if (&ctrlp->result == resultp)
+               break;
+       if (ctrlp == NULL)
+           continue;
+       if (prev == NULL)
+           used_list = ctrlp->next;
+       else
+           prev->next = ctrlp->next;
+       if ((done_handler = ctrlp->done_handler)) {
+           their_data = ctrlp->done_handler_data;
+           ctrlp->done_handler = NULL;
+           ctrlp->done_handler_data = NULL;
+           if (cbdataValid(their_data))
+               done_handler(ctrlp->fd, their_data,
+                   ctrlp->result.aio_return, ctrlp->result.aio_errno);
+           cbdataUnlock(their_data);
+       }
+       if (ctrlp->operation == _AIO_CLOSE)
+           aioFDWasClosed(ctrlp->fd);
+       memPoolFree(aio_ctrl_pool, ctrlp);
+    }
+}
+
+void
+aioStats(StoreEntry * sentry)
+{
+    storeAppendPrintf(sentry, "ASYNC IO Counters:\n");
+    storeAppendPrintf(sentry, "open\t%d\n", aio_counts.open);
+    storeAppendPrintf(sentry, "close\t%d\n", aio_counts.close);
+    storeAppendPrintf(sentry, "cancel\t%d\n", aio_counts.cancel);
+    storeAppendPrintf(sentry, "write\t%d\n", aio_counts.write);
+    storeAppendPrintf(sentry, "read\t%d\n", aio_counts.read);
+    storeAppendPrintf(sentry, "stat\t%d\n", aio_counts.stat);
+    storeAppendPrintf(sentry, "unlink\t%d\n", aio_counts.unlink);
+    storeAppendPrintf(sentry, "check_callback\t%d\n", aio_counts.check_callback);
+    storeAppendPrintf(sentry, "queue\t%d\n", aio_get_queue_len());
+}
+
+/* Flush all pending I/O */
+void
+aioSync(SwapDir *SD)
+{
+    if (!initialised)
+       return;                 /* nothing to do then */
+    /* Flush all pending operations */
+    debug(32, 1) ("aioSync: flushing pending I/O operations\n");
+    do {
+       aioCheckCallbacks(SD);
+    } while (aio_sync());
+    debug(32, 1) ("aioSync: done\n");
+}
+
+int
+aioQueueSize(void)
+{
+    return memPoolInUseCount(aio_ctrl_pool);
+}
diff --git a/src/fs/aufs/store_asyncufs.h b/src/fs/aufs/store_asyncufs.h
new file mode 100644 (file)
index 0000000..012427e
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * store_aufs.h
+ *
+ * Internal declarations for the aufs routines
+ */
+
+#ifndef __STORE_ASYNCUFS_H__
+#define __STORE_ASYNCUFS_H__
+
+#ifdef ASYNC_IO_THREADS
+#define NUMTHREADS ASYNC_IO_THREADS
+#else
+#define NUMTHREADS 16
+#endif
+
+#define MAGIC1 (NUMTHREADS/Config.cacheSwap.n_configured/2)
+
+struct _aio_result_t {
+    int aio_return;
+    int aio_errno;
+};
+
+typedef struct _aio_result_t aio_result_t;
+
+typedef void AIOCB(int fd, void *, int aio_return, int aio_errno);
+
+int aio_cancel(aio_result_t *);
+int aio_open(const char *, int, mode_t, aio_result_t *);
+int aio_read(int, char *, int, off_t, int, aio_result_t *);
+int aio_write(int, char *, int, off_t, int, aio_result_t *);
+int aio_close(int, aio_result_t *);
+int aio_stat(const char *, struct stat *, aio_result_t *);
+int aio_unlink(const char *, aio_result_t *);
+int aio_opendir(const char *, aio_result_t *);
+aio_result_t *aio_poll_done(void);
+int aio_operations_pending(void);
+int aio_overloaded(void);
+int aio_sync(void);
+int aio_get_queue_len(void);
+
+void aioInit(void);
+void aioDone(void);
+void aioCancel(int); 
+void aioOpen(const char *, int, mode_t, AIOCB *, void *);  
+void aioClose(int);
+void aioWrite(int, int offset, char *, int size, AIOCB *, void *, FREE *);
+void aioRead(int, int offset, char *, int size, AIOCB *, void *);
+void aioStat(char *, struct stat *, AIOCB *, void *);
+void aioUnlink(const char *, AIOCB *, void *);
+void aioCheckCallbacks(SwapDir *);
+void aioSync(SwapDir *);
+int aioQueueSize(void); 
+
+struct _aioinfo_t {
+    int swaplog_fd;
+    int l1;
+    int l2;
+    fileMap *map;
+    int suggest;
+};
+
+struct _aiostate_t {
+    int fd;
+    struct {
+       unsigned int close_request:1;
+       unsigned int reading:1;
+       unsigned int writing:1;
+       unsigned int opening:1;
+    } flags;
+    const char *read_buf;
+    link_list *pending_writes;
+    link_list *pending_reads;
+};
+
+typedef struct _aioinfo_t aioinfo_t;
+typedef struct _aiostate_t aiostate_t;
+
+/* The aio_state memory pool */
+extern MemPool * aio_state_pool;
+
+extern void storeAufsDirMapBitReset(SwapDir *, sfileno);
+extern int storeAufsDirMapBitAllocate(SwapDir *);
+
+extern char * storeAufsDirFullPath(SwapDir *SD, sfileno filn, char *fullpath);
+extern void storeAufsDirUnlinkFile(SwapDir *, sfileno);
+extern void storeAufsDirReplAdd(SwapDir *SD, StoreEntry *);
+extern void storeAufsDirReplRemove(StoreEntry *);
+
+/*
+ * Store IO stuff
+ */
+extern STOBJCREATE storeAufsCreate;
+extern STOBJOPEN storeAufsOpen;
+extern STOBJCLOSE storeAufsClose;
+extern STOBJREAD storeAufsRead;
+extern STOBJWRITE storeAufsWrite;
+extern STOBJUNLINK storeAufsUnlink;
+
+#endif
diff --git a/src/fs/aufs/store_dir_aufs.cc b/src/fs/aufs/store_dir_aufs.cc
new file mode 100644 (file)
index 0000000..7e443a5
--- /dev/null
@@ -0,0 +1,1896 @@
+
+/*
+ * $Id: store_dir_aufs.cc,v 1.1 2000/05/03 17:15:46 adrian Exp $
+ *
+ * DEBUG: section 47    Store Directory Routines
+ * AUTHOR: Duane Wessels
+ *
+ * SQUID Internet Object Cache  http://squid.nlanr.net/Squid/
+ * ----------------------------------------------------------
+ *
+ *  Squid is the result of efforts by numerous individuals from the
+ *  Internet community.  Development is led by Duane Wessels of the
+ *  National Laboratory for Applied Network Research and funded by the
+ *  National Science Foundation.  Squid is Copyrighted (C) 1998 by
+ *  Duane Wessels and the University of California San Diego.  Please
+ *  see the COPYRIGHT file for full details.  Squid incorporates
+ *  software developed and/or copyrighted by other sources.  Please see
+ *  the CREDITS file for full details.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *  
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *  
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
+ *
+ */
+
+#include "squid.h"
+#if HAVE_STATVFS
+#if HAVE_SYS_STATVFS_H
+#include <sys/statvfs.h>
+#endif
+#endif
+
+#include "store_asyncufs.h"
+
+#define DefaultLevelOneDirs     16
+#define DefaultLevelTwoDirs     256
+#define STORE_META_BASYNCUFSZ 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;
+
+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 *, int *sfileno, int *size);
+static StoreEntry *storeAufsDirAddDiskRestore(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_num32 refcount,
+    u_short flags,
+    int clean);
+static void storeAufsDirRebuild(SwapDir * sd);
+static void storeAufsDirCloseTmpSwapLog(SwapDir * sd);
+static FILE *storeAufsDirOpenTmpSwapLog(SwapDir *, int *, int *);
+static STLOGOPEN storeAufsDirOpenSwapLog;
+static STINIT storeAufsDirInit;
+static STFREE storeAufsDirFree;
+static STLOGCLEANOPEN storeAufsDirWriteCleanOpen;
+static void storeAufsDirWriteCleanClose(SwapDir * sd);
+static STLOGCLEANWRITE storeAufsDirWriteCleanEntry;
+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);
+static int storeAufsDirCheckExpired(SwapDir *, StoreEntry *);
+#if !HEAP_REPLACEMENT
+static time_t storeAufsDirExpiredReferenceAge(SwapDir *);
+#endif
+
+/*
+ * 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!
+ */
+
+int
+storeAufsDirMapBitTest(SwapDir *SD, int fn)
+{
+    sfileno filn = fn;
+    aioinfo_t *aioinfo;
+    aioinfo = (aioinfo_t *)SD->fsdata;
+    return file_map_bit_test(aioinfo->map, filn);
+}
+void
+storeAufsDirMapBitSet(SwapDir *SD, int fn)
+{  
+    sfileno filn = fn;
+    aioinfo_t *aioinfo;
+    aioinfo = (aioinfo_t *)SD->fsdata;
+    file_map_bit_set(aioinfo->map, filn);
+}
+void
+storeAufsDirMapBitReset(SwapDir *SD, int fn)
+{ 
+    sfileno filn = fn;
+    aioinfo_t *aioinfo;
+    aioinfo = (aioinfo_t *)SD->fsdata;
+    file_map_bit_reset(aioinfo->map, filn);
+}
+
+int
+storeAufsDirMapBitAllocate(SwapDir *SD)
+{
+    aioinfo_t *aioinfo = (aioinfo_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)
+{
+    aioinfo_t *aioinfo = (aioinfo_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)
+{
+    aioinfo_t *aioinfo = (aioinfo_t *)sd->fsdata;
+
+    LOCAL_ARRAY(char, fullfilename, SQUID_MAXPATHLEN);
+    assert(0 <= subdirn && subdirn < aioinfo->l1);
+    snprintf(fullfilename, SQUID_MAXPATHLEN, "%s/%02X", sd->path, subdirn);
+    return fullfilename;
+}
+
+static int
+storeAufsDirCreateDirectory(const char *path, int should_exist)
+{
+    int created = 0;
+    struct stat st;
+    getCurrentTime();
+    if (0 == stat(path, &st)) {
+       if (S_ISDIR(st.st_mode)) {
+           debug(20, should_exist ? 3 : 1) ("%s exists\n", path);
+       } else {
+           fatalf("Swap directory %s is not a directory.", path);
+       }
+    } else if (0 == mkdir(path, 0755)) {
+       debug(20, should_exist ? 1 : 3) ("%s created\n", path);
+       created = 1;
+    } else {
+       fatalf("Failed to make swap directory %s: %s",
+           path, xstrerror());
+    }
+    return created;
+}
+
+static int
+storeAufsDirVerifyDirectory(const char *path)
+{
+    struct stat sb;
+    if (stat(path, &sb) < 0) {
+       debug(20, 0) ("%s: %s\n", path, xstrerror());
+       return -1;
+    }
+    if (S_ISDIR(sb.st_mode) == 0) {
+       debug(20, 0) ("%s is not a directory\n", path);
+       return -1;
+    }
+    return 0;
+}
+
+/*
+ * This function is called by storeAufsDirInit().  If this returns < 0,
+ * then Squid exits, complains about swap directories not
+ * existing, and instructs the admin to run 'squid -z'
+ */
+static int
+storeAufsDirVerifyCacheDirs(SwapDir * sd)
+{
+    aioinfo_t *aioinfo = (aioinfo_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)
+{
+    aioinfo_t *aioinfo = (aioinfo_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);
+        while (index(pathtmp,'/'))
+            *index(pathtmp,'/')='.';
+        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)
+{
+    aioinfo_t *aioinfo = (aioinfo_t *)sd->fsdata;
+    char *path;
+    int fd;
+    path = storeAufsDirSwapLogFile(sd, NULL);
+    fd = file_open(path, O_WRONLY | O_CREAT);
+    if (fd < 0) {
+       debug(50, 1) ("%s: %s\n", path, xstrerror());
+       fatal("storeAufsDirOpenSwapLog: Failed to open swap log.");
+    }
+    debug(47, 3) ("Cache Dir #%d log opened on FD %d\n", sd->index, fd);
+    aioinfo->swaplog_fd = fd;
+    if (0 == n_asyncufs_dirs)
+       assert(NULL == asyncufs_dir_index);
+    n_asyncufs_dirs++;
+    assert(n_asyncufs_dirs <= Config.cacheSwap.n_configured);
+}
+
+static void
+storeAufsDirCloseSwapLog(SwapDir * sd)
+{
+    aioinfo_t *aioinfo = (aioinfo_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;
+    }
+}
+
+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];
+    int sfileno = 0;
+    int count;
+    int size;
+    struct stat sb;
+    int swap_hdr_len;
+    int fd = -1;
+    tlv *tlv_list;
+    tlv *t;
+    assert(rb != NULL);
+    debug(20, 3) ("storeAufsDirRebuildFromDirectory: DIR #%d\n", rb->sd->index);
+    for (count = 0; count < rb->speed; count++) {
+       assert(fd == -1);
+       fd = storeAufsDirGetNextFile(rb, &sfileno, &size);
+       if (fd == -2) {
+           debug(20, 1) ("Done scanning %s swaplog (%d entries)\n",
+               rb->sd->path, rb->n_read);
+           store_dirs_rebuilding--;
+           storeAufsDirCloseTmpSwapLog(rb->sd);
+           storeRebuildComplete(&rb->counts);
+           cbdataFree(rb);
+           return;
+       } else if (fd < 0) {
+           continue;
+       }
+       assert(fd > -1);
+       /* lets get file stats here */
+       if (fstat(fd, &sb) < 0) {
+           debug(20, 1) ("storeAufsDirRebuildFromDirectory: fstat(FD %d): %s\n",
+               fd, xstrerror());
+           file_close(fd);
+           store_open_disk_fd--;
+           fd = -1;
+           continue;
+       }
+       if ((++rb->counts.scancount & 0xFFFF) == 0)
+           debug(20, 3) ("  %s %7d files opened so far.\n",
+               rb->sd->path, rb->counts.scancount);
+       debug(20, 9) ("file_in: fd=%d %08X\n", fd, sfileno);
+       Counter.syscalls.disk.reads++;
+       if (read(fd, hdr_buf, SM_PAGE_SIZE) < 0) {
+           debug(20, 1) ("storeAufsDirRebuildFromDirectory: read(FD %d): %s\n",
+               fd, xstrerror());
+           file_close(fd);
+           store_open_disk_fd--;
+           fd = -1;
+           continue;
+       }
+       file_close(fd);
+       store_open_disk_fd--;
+       fd = -1;
+       swap_hdr_len = 0;
+#if USE_TRUNCATE
+       if (sb.st_size == 0)
+           continue;
+#endif
+       tlv_list = storeSwapMetaUnpack(hdr_buf, &swap_hdr_len);
+       if (tlv_list == NULL) {
+           debug(20, 1) ("storeAufsDirRebuildFromDirectory: failed to get meta data\n");
+           /* XXX shouldn't this be a call to storeAufsUnlink ? */
+           storeAufsDirUnlinkFile(SD, sfileno);
+           continue;
+       }
+       debug(20, 3) ("storeAufsDirRebuildFromDirectory: successful swap meta unpacking\n");
+       memset(key, '\0', MD5_DIGEST_CHARS);
+       memset(&tmpe, '\0', sizeof(StoreEntry));
+       for (t = tlv_list; t; t = t->next) {
+           switch (t->type) {
+           case STORE_META_KEY:
+               assert(t->length == MD5_DIGEST_CHARS);
+               xmemcpy(key, t->value, MD5_DIGEST_CHARS);
+               break;
+           case STORE_META_STD:
+               assert(t->length == STORE_HDR_METASIZE);
+               xmemcpy(&tmpe.timestamp, t->value, STORE_HDR_METASIZE);
+               break;
+           default:
+               break;
+           }
+       }
+       storeSwapTLVFree(tlv_list);
+       tlv_list = NULL;
+       if (storeKeyNull(key)) {
+           debug(20, 1) ("storeAufsDirRebuildFromDirectory: NULL key\n");
+           storeAufsDirUnlinkFile(SD, sfileno);
+           continue;
+       }
+       tmpe.key = key;
+       /* check sizes */
+       if (tmpe.swap_file_sz == 0) {
+           tmpe.swap_file_sz = sb.st_size;
+       } else if (tmpe.swap_file_sz == sb.st_size - swap_hdr_len) {
+           tmpe.swap_file_sz = sb.st_size;
+       } else if (tmpe.swap_file_sz != sb.st_size) {
+           debug(20, 1) ("storeAufsDirRebuildFromDirectory: SIZE MISMATCH %d!=%d\n",
+               tmpe.swap_file_sz, (int) sb.st_size);
+           storeAufsDirUnlinkFile(SD, sfileno);
+           continue;
+       }
+       if (EBIT_TEST(tmpe.flags, KEY_PRIVATE)) {
+           storeAufsDirUnlinkFile(SD, sfileno);
+           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,
+           sfileno,
+           tmpe.swap_file_sz,
+           tmpe.expires,
+           tmpe.timestamp,
+           tmpe.lastref,
+           tmpe.lastmod,
+           tmpe.refcount,      /* refcount */
+           tmpe.flags,         /* flags */
+           (int) rb->flags.clean);
+       storeDirSwapLog(e, SWAP_LOG_ADD);
+    }
+    eventAdd("storeRebuild", storeAufsDirRebuildFromDirectory, rb, 0.0, 1);
+}
+
+static void
+storeAufsDirRebuildFromSwapLog(void *data)
+{
+    RebuildState *rb = data;
+    SwapDir *SD = rb->sd;
+    StoreEntry *e = NULL;
+    storeSwapLogData s;
+    size_t ss = sizeof(storeSwapLogData);
+    int count;
+    int used;                  /* is swapfile already in use? */
+    int disk_entry_newer;      /* is the log entry newer than current entry? */
+    double x;
+    assert(rb != NULL);
+    /* load a number of objects per invocation */
+    for (count = 0; count < rb->speed; count++) {
+       if (fread(&s, ss, 1, rb->log) != 1) {
+           debug(20, 1) ("Done reading %s swaplog (%d entries)\n",
+               rb->sd->path, rb->n_read);
+           fclose(rb->log);
+           rb->log = NULL;
+           store_dirs_rebuilding--;
+           storeAufsDirCloseTmpSwapLog(rb->sd);
+           storeRebuildComplete(&rb->counts);
+           cbdataFree(rb);
+           return;
+       }
+       rb->n_read++;
+       if (s.op <= SWAP_LOG_NOP)
+           continue;
+       if (s.op >= SWAP_LOG_MAX)
+           continue;
+       debug(20, 3) ("storeAufsDirRebuildFromSwapLog: %s %s %08X\n",
+           swap_log_op_str[(int) s.op],
+           storeKeyText(s.key),
+           s.swap_filen);
+       if (s.op == SWAP_LOG_ADD) {
+           (void) 0;
+       } else if (s.op == SWAP_LOG_DEL) {
+           if ((e = storeGet(s.key)) != NULL) {
+               /*
+                * Make sure we don't unlink the file, it might be
+                * in use by a subsequent entry.  Also note that
+                * we don't have to subtract from store_swap_size
+                * because adding to store_swap_size happens in
+                * the cleanup procedure.
+                */
+                storeExpireNow(e);
+                storeReleaseRequest(e);
+                storeAufsDirReplRemove(e);
+               storeRelease(e);
+               if (e->swap_filen > -1) {
+                   /* Fake a unlink here, this is a bad hack :( */
+                   e->swap_filen = -1;
+                    e->swap_dirn = -1;
+               }
+               rb->counts.objcount--;
+               rb->counts.cancelcount++;
+           }
+           continue;
+       } else {
+           x = log(++rb->counts.bad_log_op) / log(10.0);
+           if (0.0 == x - (double) (int) x)
+               debug(20, 1) ("WARNING: %d invalid swap log entries found\n",
+                   rb->counts.bad_log_op);
+           rb->counts.invalid++;
+           continue;
+       }
+       if ((++rb->counts.scancount & 0xFFFF) == 0)
+           debug(20, 3) ("  %7d %s Entries read so far.\n",
+               rb->counts.scancount, rb->sd->path);
+       if (!storeAufsDirValidFileno(SD, s.swap_filen)) {
+           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;
+#if HEAP_REPLACEMENT
+               storeHeapPositionUpdate(e, SD);
+               storeAufsDirUnrefObj(SD, e);
+#endif
+           } else {
+               debug_trap("storeAufsDirRebuildFromSwapLog: bad condition");
+               debug(20, 1) ("\tSee %s:%d\n", __FILE__, __LINE__);
+           }
+           continue;
+       } else if (used) {
+           /* swapfile in use, not by this URL, log entry is newer */
+           /* This is sorta bad: the log entry should NOT be newer at this
+            * point.  If the log is dirty, the filesize check should have
+            * caught this.  If the log is clean, there should never be a
+            * newer entry. */
+           debug(20, 1) ("WARNING: newer swaplog entry for dirno %d, fileno %08X\n",
+               SD->index, s.swap_filen);
+           /* I'm tempted to remove the swapfile here just to be safe,
+            * but there is a bad race condition in the NOVM version if
+            * the swapfile has recently been opened for writing, but
+            * not yet opened for reading.  Because we can't map
+            * swapfiles back to StoreEntrys, we don't know the state
+            * of the entry using that file.  */
+           /* We'll assume the existing entry is valid, probably because
+            * were in a slow rebuild and the the swap file number got taken
+            * and the validation procedure hasn't run. */
+           assert(rb->flags.need_to_validate);
+           rb->counts.clashcount++;
+           continue;
+       } else if (e && !disk_entry_newer) {
+           /* key already exists, current entry is newer */
+           /* keep old, ignore new */
+           rb->counts.dupcount++;
+           continue;
+       } else if (e) {
+           /* key already exists, this swapfile not being used */
+           /* junk old, load new */
+            storeExpireNow(e);
+            storeReleaseRequest(e);
+            storeAufsDirReplRemove(e);
+           storeRelease(e);
+           if (e->swap_filen > -1) {
+               /* Fake a unlink here, this is a bad hack :( */
+               e->swap_filen = -1;
+                e->swap_dirn = -1;
+           }
+           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, int *sfileno, int *size)
+{
+    SwapDir *SD = rb->sd;
+    aioinfo_t *aioinfo = (aioinfo_t *)SD->fsdata;
+    int fd = -1;
+    int used = 0;
+    int dirs_opened = 0;
+    debug(20, 3) ("storeAufsDirGetNextFile: flag=%d, %d: /%02X/%02X\n",
+       rb->flags.init,
+       rb->sd->index,
+       rb->curlvl1,
+       rb->curlvl2);
+    if (rb->done)
+       return -2;
+    while (fd < 0 && rb->done == 0) {
+       fd = -1;
+       if (0 == rb->flags.init) {      /* initialize, open first file */
+           rb->done = 0;
+           rb->curlvl1 = 0;
+           rb->curlvl2 = 0;
+           rb->in_dir = 0;
+           rb->flags.init = 1;
+           assert(Config.cacheSwap.n_configured > 0);
+       }
+       if (0 == rb->in_dir) {  /* we need to read in a new directory */
+           snprintf(rb->fullpath, SQUID_MAXPATHLEN, "%s/%02X/%02X",
+               rb->sd->path,
+               rb->curlvl1, rb->curlvl2);
+           if (rb->flags.init && rb->td != NULL)
+               closedir(rb->td);
+           rb->td = NULL;
+           if (dirs_opened)
+               return -1;
+           rb->td = opendir(rb->fullpath);
+           dirs_opened++;
+           if (rb->td == NULL) {
+               debug(50, 1) ("storeAufsDirGetNextFile: opendir: %s: %s\n",
+                   rb->fullpath, xstrerror());
+           } else {
+               rb->entry = readdir(rb->td);    /* skip . and .. */
+               rb->entry = readdir(rb->td);
+               if (rb->entry == NULL && errno == ENOENT)
+                   debug(20, 1) ("storeAufsDirGetNextFile: directory does not exist!.\n");
+               debug(20, 3) ("storeAufsDirGetNextFile: Directory %s\n", rb->fullpath);
+           }
+       }
+       if (rb->td != NULL && (rb->entry = readdir(rb->td)) != NULL) {
+           rb->in_dir++;
+           if (sscanf(rb->entry->d_name, "%x", &rb->fn) != 1) {
+               debug(20, 3) ("storeAufsDirGetNextFile: invalid %s\n",
+                   rb->entry->d_name);
+               continue;
+           }
+           if (!storeAufsFilenoBelongsHere(rb->fn, rb->sd->index, rb->curlvl1, rb->curlvl2)) {
+               debug(20, 3) ("storeAufsDirGetNextFile: %08X does not belong in %d/%d/%d\n",
+                   rb->fn, rb->sd->index, rb->curlvl1, rb->curlvl2);
+               continue;
+           }
+           used = storeAufsDirMapBitTest(SD, rb->fn);
+           if (used) {
+               debug(20, 3) ("storeAufsDirGetNextFile: Locked, continuing with next.\n");
+               continue;
+           }
+           snprintf(rb->fullfilename, SQUID_MAXPATHLEN, "%s/%s",
+               rb->fullpath, rb->entry->d_name);
+           debug(20, 3) ("storeAufsDirGetNextFile: Opening %s\n", rb->fullfilename);
+           fd = file_open(rb->fullfilename, O_RDONLY);
+           if (fd < 0)
+               debug(50, 1) ("storeAufsDirGetNextFile: %s: %s\n", rb->fullfilename, xstrerror());
+           else
+               store_open_disk_fd++;
+           continue;
+       }
+       rb->in_dir = 0;
+       if (++rb->curlvl2 < aioinfo->l2)
+           continue;
+       rb->curlvl2 = 0;
+       if (++rb->curlvl1 < aioinfo->l1)
+           continue;
+       rb->curlvl1 = 0;
+       rb->done = 1;
+    }
+    *sfileno = 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,
+    int file_number,
+    size_t swap_file_sz,
+    time_t expires,
+    time_t timestamp,
+    time_t lastref,
+    time_t lastmod,
+    u_num32 refcount,
+    u_short flags,
+    int clean)
+{
+    StoreEntry *e = NULL;
+    debug(20, 5) ("storeAufsAddDiskRestore: %s, fileno=%08X\n", storeKeyText(key), file_number);
+    /* if you call this you'd better be sure file_number is not 
+     * already in use! */
+    e = new_StoreEntry(STORE_ENTRY_WITHOUT_MEMOBJ, NULL, NULL);
+    e->store_status = STORE_OK;
+    storeSetMemStatus(e, NOT_IN_MEMORY);
+    e->swap_status = SWAPOUT_DONE;
+    e->swap_filen = file_number;
+    e->swap_dirn = SD->index;
+    e->swap_file_sz = swap_file_sz;
+    e->lock_count = 0;
+#if !HEAP_REPLACEMENT
+    e->refcount = 0;
+#endif
+    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;
+}
+
+static void
+storeAufsDirRebuild(SwapDir * sd)
+{
+    RebuildState *rb = xcalloc(1, sizeof(*rb));
+    int clean = 0;
+    int zero = 0;
+    FILE *fp;
+    EVH *func = NULL;
+    rb->sd = sd;
+    rb->speed = opt_foreground_rebuild ? 1 << 30 : 50;
+    /*
+     * If the swap.state file exists in the cache_dir, then
+     * we'll use storeAufsDirRebuildFromSwapLog(), otherwise we'll
+     * use storeAufsDirRebuildFromDirectory() to open up each file
+     * and suck in the meta data.
+     */
+    fp = storeAufsDirOpenTmpSwapLog(sd, &clean, &zero);
+    if (fp == NULL || zero) {
+       if (fp != NULL)
+           fclose(fp);
+       func = storeAufsDirRebuildFromDirectory;
+    } else {
+       func = storeAufsDirRebuildFromSwapLog;
+       rb->log = fp;
+       rb->flags.clean = (unsigned int) clean;
+    }
+    if (!clean)
+       rb->flags.need_to_validate = 1;
+    debug(20, 1) ("Rebuilding storage in %s (%s)\n",
+       sd->path, clean ? "CLEAN" : "DIRTY");
+    store_dirs_rebuilding++;
+    cbdataAdd(rb, cbdataXfree, 0);
+    eventAdd("storeRebuild", func, rb, 0.0, 1);
+}
+
+static void
+storeAufsDirCloseTmpSwapLog(SwapDir * sd)
+{
+    aioinfo_t *aioinfo = (aioinfo_t *)sd->fsdata;
+    char *swaplog_path = xstrdup(storeAufsDirSwapLogFile(sd, NULL));
+    char *new_path = xstrdup(storeAufsDirSwapLogFile(sd, ".new"));
+    int fd;
+    file_close(aioinfo->swaplog_fd);
+#ifdef _SQUID_OS2_
+    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);
+    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)
+{
+    aioinfo_t *aioinfo = (aioinfo_t *)sd->fsdata;
+    char *swaplog_path = xstrdup(storeAufsDirSwapLogFile(sd, NULL));
+    char *clean_path = xstrdup(storeAufsDirSwapLogFile(sd, ".last-clean"));
+    char *new_path = xstrdup(storeAufsDirSwapLogFile(sd, ".new"));
+    struct stat log_sb;
+    struct stat clean_sb;
+    FILE *fp;
+    int fd;
+    if (stat(swaplog_path, &log_sb) < 0) {
+       debug(47, 1) ("Cache Dir #%d: No log file\n", sd->index);
+       safe_free(swaplog_path);
+       safe_free(clean_path);
+       safe_free(new_path);
+       return NULL;
+    }
+    *zero_flag = log_sb.st_size == 0 ? 1 : 0;
+    /* close the existing write-only FD */
+    if (aioinfo->swaplog_fd >= 0)
+       file_close(aioinfo->swaplog_fd);
+    /* open a write-only FD for the new log */
+    fd = file_open(new_path, O_WRONLY | O_CREAT | O_TRUNC);
+    if (fd < 0) {
+       debug(50, 1) ("%s: %s\n", new_path, xstrerror());
+       fatal("storeDirOpenTmpSwapLog: Failed to open swap log.");
+    }
+    aioinfo->swaplog_fd = fd;
+    /* open a read-only stream of the old log */
+    fp = fopen(swaplog_path, "r");
+    if (fp == NULL) {
+       debug(50, 0) ("%s: %s\n", swaplog_path, xstrerror());
+       fatal("Failed to open swap log for reading");
+    }
+    memset(&clean_sb, '\0', sizeof(struct stat));
+    if (stat(clean_path, &clean_sb) < 0)
+       *clean_flag = 0;
+    else if (clean_sb.st_mtime < log_sb.st_mtime)
+       *clean_flag = 0;
+    else
+       *clean_flag = 1;
+    safeunlink(clean_path, 1);
+    safe_free(swaplog_path);
+    safe_free(clean_path);
+    safe_free(new_path);
+    return fp;
+}
+
+struct _clean_state {
+    char *cur;
+    char *new;
+    char *cln;
+    char *outbuf;
+    off_t outbuf_offset;
+    int fd;
+};
+
+#define CLEAN_BUF_SZ 16384
+/*
+ * Begin the process to write clean cache state.  For ASYNCUFS 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
+storeAufsDirWriteCleanOpen(SwapDir * sd)
+{
+    struct _clean_state *state = xcalloc(1, sizeof(*state));
+    struct stat sb;
+    sd->log.clean.write = NULL;
+    sd->log.clean.state = NULL;
+    state->cur = xstrdup(storeAufsDirSwapLogFile(sd, NULL));
+    state->new = xstrdup(storeAufsDirSwapLogFile(sd, ".clean"));
+    state->cln = xstrdup(storeAufsDirSwapLogFile(sd, ".last-clean"));
+    state->outbuf = xcalloc(CLEAN_BUF_SZ, 1);
+    state->outbuf_offset = 0;
+    unlink(state->new);
+    unlink(state->cln);
+    state->fd = file_open(state->new, O_WRONLY | O_CREAT | O_TRUNC);
+    if (state->fd < 0)
+       return -1;
+    debug(20, 3) ("storeDirWriteCleanLogs: opened %s, FD %d\n",
+       state->new, state->fd);
+#if HAVE_FCHMOD
+    if (stat(state->cur, &sb) == 0)
+       fchmod(state->fd, sb.st_mode);
+#endif
+    sd->log.clean.write = storeAufsDirWriteCleanEntry;
+    sd->log.clean.state = state;
+    return 0;
+}
+
+/*
+ * "write" an entry to the clean log file.
+ */
+static void
+storeAufsDirWriteCleanEntry(const StoreEntry * e, SwapDir * sd)
+{
+    storeSwapLogData s;
+    static size_t ss = sizeof(storeSwapLogData);
+    struct _clean_state *state = sd->log.clean.state;
+    if (NULL == e) {
+       storeAufsDirWriteCleanClose(sd);
+       return;
+    }
+    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->key, MD5_DIGEST_CHARS);
+    xmemcpy(state->outbuf + state->outbuf_offset, &s, ss);
+    state->outbuf_offset += ss;
+    /* buffered write */
+    if (state->outbuf_offset + ss > CLEAN_BUF_SZ) {
+       if (write(state->fd, state->outbuf, state->outbuf_offset) < 0) {
+           debug(50, 0) ("storeDirWriteCleanLogs: %s: write: %s\n",
+               state->new, xstrerror());
+           debug(20, 0) ("storeDirWriteCleanLogs: Current swap logfile not replaced.\n");
+           file_close(state->fd);
+           state->fd = -1;
+           unlink(state->new);
+           safe_free(state);
+           sd->log.clean.state = NULL;
+           sd->log.clean.write = NULL;
+       }
+       state->outbuf_offset = 0;
+    }
+}
+
+static void
+storeAufsDirWriteCleanClose(SwapDir * sd)
+{
+    struct _clean_state *state = sd->log.clean.state;
+    if (state->fd < 0)
+       return;
+    if (write(state->fd, state->outbuf, state->outbuf_offset) < 0) {
+       debug(50, 0) ("storeDirWriteCleanLogs: %s: write: %s\n",
+           state->new, xstrerror());
+       debug(20, 0) ("storeDirWriteCleanLogs: Current swap logfile "
+           "not replaced.\n");
+       file_close(state->fd);
+       state->fd = -1;
+       unlink(state->new);
+    }
+    safe_free(state->outbuf);
+    /*
+     * You can't rename open files on Microsoft "operating systems"
+     * so we have to close before renaming.
+     */
+    storeAufsDirCloseSwapLog(sd);
+    /* 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
+storeAufsDirSwapLog(const SwapDir * sd, const StoreEntry * e, int op)
+{
+    aioinfo_t *aioinfo = (aioinfo_t *)sd->fsdata;
+    storeSwapLogData *s = xcalloc(1, sizeof(storeSwapLogData));
+    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->key, MD5_DIGEST_CHARS);
+    file_write(aioinfo->swaplog_fd,
+       -1,
+       s,
+       sizeof(storeSwapLogData),
+       NULL,
+       NULL,
+       xfree);
+}
+
+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;
+    aioinfo_t *aioinfo;
+    N0 = n_asyncufs_dirs;
+    D0 = asyncufs_dir_index[swap_index % N0];
+    SD = &Config.cacheSwap.swapDirs[D0];
+    aioinfo = (aioinfo_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))
+           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
+       Counter.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 ASYNCUFS cache_dirs configured, otherwise
+     * we should never be called.
+     */
+    assert(n_asyncufs_dirs);
+    if (NULL == asyncufs_dir_index) {
+       SwapDir *sd;
+        aioinfo_t *aioinfo;
+       /*
+        * Initialize the little array that translates ASYNCUFS 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 = (aioinfo_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 ASYNCUFS 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", 3) == 0)
+       return 1;
+    return 0;
+}
+
+/*
+ * Does swapfile number 'fn' belong in cachedir #F0,
+ * level1 dir #F1, level2 dir #F2?
+ */
+static int
+storeAufsFilenoBelongsHere(int fn, int F0, int F1, int F2)
+{
+    int D1, D2;
+    int L1, L2;
+    int filn = fn;
+    aioinfo_t *aioinfo;
+    assert(F0 < Config.cacheSwap.n_configured);
+    aioinfo = (aioinfo_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)
+{
+    aioinfo_t *aioinfo = (aioinfo_t *)SD->fsdata;
+    if (filn < 0)
+        return 0;
+    if (filn > aioinfo->map->max_n_files)  
+        return 0;
+    return 1;
+}
+
+void
+storeAufsDirMaintain(SwapDir *SD)
+{
+    StoreEntry *e = NULL;
+    int scanned = 0;
+    int locked = 0;
+    int expired = 0;
+    int max_scan;
+    int max_remove;
+    double f;
+    static time_t last_warn_time = 0;
+#if !HEAP_REPLACEMENT
+    dlink_node *m;
+    dlink_node *prev = NULL;
+#else
+    heap_key age;
+    heap_key min_age = 0.0;
+    link_list *locked_entries = NULL;
+#if HEAP_REPLACEMENT_DEBUG
+    if (!verify_heap_property(SD->repl.heap.heap)) {
+        debug(20, 1) ("Heap property violated!\n");
+    }
+#endif
+#endif
+    /* We can't delete objects while rebuilding swap */
+    if (store_dirs_rebuilding) {
+        return;
+    } else {
+        f = (double) (store_swap_size - store_swap_low) / (store_swap_high - store_swap_low);
+        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?
+         */
+#if 0
+        eventAdd("MaintainSwapSpace", storeMaintainSwapSpace, NULL, 1.0 - f, 1);
+#endif
+    }
+    debug(20, 3) ("storeMaintainSwapSpace: f=%f, max_scan=%d, max_remove=%d\n", f, max_scan, max_remove);
+#if HEAP_REPLACEMENT
+    while (heap_nodes(SD->repl.heap.heap) > 0) {
+        if (store_swap_size < store_swap_low)
+            break;
+        if (expired >= max_remove)
+            break;
+        if (scanned >= max_scan)
+            break;
+        age = heap_peepminkey(SD->repl.heap.heap);
+        e = heap_extractmin(SD->repl.heap.heap);
+        e->repl.node = NULL;         /* no longer in the heap */
+        scanned++;
+        if (storeEntryLocked(e)) {
+            /*
+             * Entry is in use ... put it in a linked list to ignore it.
+             */
+            if (!EBIT_TEST(e->flags, ENTRY_SPECIAL)) {
+                /*
+                 * If this was a "SPECIAL" do not add it back into the heap.
+                 * It will always be "SPECIAL" and therefore never removed.
+                 */
+                debug(20, 4) ("storeAufsDirMaintain: locked url %s\n",
+                    (e->mem_obj && e->mem_obj->url) ? e->mem_obj->url : storeKeyText(e->
+key));
+                linklistPush(&locked_entries, e);
+            }
+            locked++;
+            continue;
+        } else if (storeAufsDirCheckExpired(SD, e)) {
+            /*
+             * Note: This will not check the reference age ifdef
+             * HEAP_REPLACEMENT, but it does some other useful
+             * checks...
+             */
+            expired++;
+            debug(20, 3) ("Released store object age %f size %d refs %d key %s\n",
+                age, e->swap_file_sz, e->refcount, storeKeyText(e->key));
+            min_age = age;
+            storeRelease(e);
+        } else {
+            /*
+             * Did not expire the object so we need to add it back
+             * into the heap!
+             */
+            debug(20, 5) ("storeMaintainSwapSpace: non-expired %s\n",
+                storeKeyText(e->key));
+            linklistPush(&locked_entries, e);
+            continue;
+        }
+        if (store_swap_size < store_swap_low)
+            break;
+        else if (expired >= max_remove)
+            break;
+        else if (scanned >= max_scan)
+            break;
+    }
+    /*
+     * Bump the heap age factor.
+     */
+    if (min_age > 0.0)
+        SD->repl.heap.heap->age = min_age;
+    /*
+     * Reinsert all bumped locked entries back into heap...
+     */
+    while ((e = linklistShift(&locked_entries)))
+        e->repl.node = heap_insert(SD->repl.heap.heap, e);
+#else
+    for (m = SD->repl.lru.list.tail; m; m = prev) {
+        prev = m->prev;
+        e = m->data;
+        scanned++;
+        if (storeEntryLocked(e)) {
+            /*
+             * If there is a locked entry at the tail of the LRU list,
+             * move it to the beginning to get it out of the way.
+             * Theoretically, we might have all locked objects at the
+             * tail, and then we'll never remove anything here and the
+             * LRU age will go to zero.
+             */
+            if (memInUse(MEM_STOREENTRY) > max_scan) {
+                dlinkDelete(&e->repl.lru, &SD->repl.lru.list);
+                dlinkAdd(e, &e->repl.lru, &SD->repl.lru.list);
+            }
+            locked++;
+
+        } else if (storeAufsDirCheckExpired(SD, e)) {
+            expired++;
+            storeRelease(e);
+        }
+        if (expired >= max_remove)
+            break;
+        if (scanned >= max_scan)
+            break;
+    }
+#endif
+    debug(20, (expired ? 2 : 3)) ("storeMaintainSwapSpace: scanned %d/%d removed %d/%d l
+ocked %d f=%.03f\n",
+        scanned, max_scan, expired, max_remove, locked, f);
+    debug(20, 3) ("storeMaintainSwapSpace stats:\n");
+    debug(20, 3) ("  %6d objects\n", memInUse(MEM_STOREENTRY));
+    debug(20, 3) ("  %6d were scanned\n", scanned);
+    debug(20, 3) ("  %6d were locked\n", locked);
+    debug(20, 3) ("  %6d were expired\n", expired);
+    if (store_swap_size < Config.Swap.maxSize)
+        return;
+    if (squid_curtime - last_warn_time < 10)
+        return;
+    debug(20, 0) ("WARNING: Disk space over limit: %d KB > %d KB\n",
+        store_swap_size, Config.Swap.maxSize);
+    last_warn_time = squid_curtime;
+}
+
+/*
+ * storeAufsDirCheckObj
+ *
+ * This routine is called by storeDirSelectSwapDir to see if the given
+ * object is able to be stored on this filesystem. AUFS filesystems will
+ * happily store anything as long as the LRU time isn't too small.
+ */
+int
+storeAufsDirCheckObj(SwapDir *SD, const StoreEntry *e)
+{
+    int loadav;
+    int ql;
+
+#if !HEAP_REPLACEMENT
+    if (storeAufsDirExpiredReferenceAge(SD) < 300) {
+       debug(20, 3) ("storeAufsDirCheckObj: NO: LRU Age = %d\n",
+           storeAufsDirExpiredReferenceAge(SD));
+       /* store_check_cachable_hist.no.lru_age_too_low++; */
+       return -1;
+    }
+#endif
+    ql = aioQueueSize();
+    if (ql == 0)
+        loadav = 0;
+    else if (ql >= MAGIC1) /* Queue is too long, don't even consider it */
+        loadav = -1;
+    else
+        loadav = MAGIC1 * 1000 / ql;
+    return loadav;
+}
+
+/*
+ * storeAufsDirRefObj
+ *
+ * This routine is called whenever an object is referenced, so we can
+ * maintain replacement information within the storage fs.
+ */
+void
+storeAufsDirRefObj(SwapDir *SD, StoreEntry *e)
+{
+    debug(1, 3) ("storeAufsDirRefObj: referencing %d/%d\n", e->swap_dirn,
+      e->swap_filen);
+#if HEAP_REPLACEMENT
+    /* Nothing to do here */
+#else
+    /* Reference the object */
+    if (!EBIT_TEST(e->flags, RELEASE_REQUEST) &&
+        !EBIT_TEST(e->flags, ENTRY_SPECIAL)) {
+        dlinkDelete(&e->repl.lru, &SD->repl.lru.list);
+        dlinkAdd(e, &e->repl.lru, &SD->repl.lru.list);
+    }
+#endif
+}
+
+/*
+ * storeAufsDirUnrefObj
+ * This routine is called whenever the last reference to an object is
+ * removed, to maintain replacement information within the storage fs.
+ */
+void
+storeAufsDirUnrefObj(SwapDir *SD, StoreEntry *e)
+{
+    debug(1, 3) ("storeAufsDirUnrefObj: referencing %d/%d\n", e->swap_dirn,
+      e->swap_filen);
+#if HEAP_REPLACEMENT
+    if (e->repl.node)
+       heap_update(SD->repl.heap.heap, e->repl.node, e);
+#endif
+}
+
+/*
+ * storeAufsDirUnlinkFile
+ *
+ * This routine unlinks a file and pulls it out of the bitmap.
+ * It used to be in storeAufsUnlink(), however an interface change
+ * forced this bit of code here. Eeek.
+ */
+void
+storeAufsDirUnlinkFile(SwapDir *SD, sfileno f)
+{
+    debug(79, 3) ("storeAufsDirUnlinkFile: unlinking fileno %08X\n", f);
+    storeAufsDirMapBitReset(SD, f);
+    aioUnlink(storeAufsDirFullPath(SD, f, NULL), NULL, NULL);
+}
+
+#if !HEAP_REPLACEMENT
+/*
+ * storeAufsDirExpiredReferenceAge
+ *
+ * The LRU age is scaled exponentially between 1 minute and
+ * Config.referenceAge , when store_swap_low < store_swap_size <
+ * store_swap_high.  This keeps store_swap_size within the low and high
+ * water marks.  If the cache is very busy then store_swap_size stays
+ * closer to the low water mark, if it is not busy, then it will stay
+ * near the high water mark.  The LRU age value can be examined on the
+ * cachemgr 'info' page.
+ */
+static time_t
+storeAufsDirExpiredReferenceAge(SwapDir *SD)
+{
+    double x;
+    double z;
+    time_t age;
+    long store_high, store_low;
+    
+    store_high = (long) (((float) SD->max_size *
+           (float) Config.Swap.highWaterMark) / (float) 100);
+    store_low = (long) (((float) SD->max_size *
+           (float) Config.Swap.lowWaterMark) / (float) 100);
+    debug(20, 20) ("RA: Dir %s, hi=%d, lo=%d, cur=%d\n", SD->path, store_high, store_low, SD->cur_size);
+
+    x = (double) (store_high - SD->cur_size) /
+       (store_high - store_low);
+    x = x < 0.0 ? 0.0 : x > 1.0 ? 1.0 : x;
+    z = pow((double) (Config.referenceAge / 60), x);
+    age = (time_t) (z * 60.0);
+    if (age < 60)
+       age = 60;
+    else if (age > Config.referenceAge)
+       age = Config.referenceAge;
+    return age;
+}
+#endif
+
+/*
+ * storeAufsDirCheckExpired
+ *
+ * Check whether the given object is expired or not
+ * It breaks layering a little by calling the upper layers to find
+ * out whether the object is locked or not, but we can't help this
+ * right now.
+ */
+static int
+storeAufsDirCheckExpired(SwapDir *SD, StoreEntry *e)
+{
+    if (storeEntryLocked(e))
+       return 0;
+    if (EBIT_TEST(e->flags, RELEASE_REQUEST))
+       return 1;
+    if (EBIT_TEST(e->flags, ENTRY_NEGCACHED) && squid_curtime >= e->expires)
+       return 1;
+
+#if HEAP_REPLACEMENT
+    /*
+     * with HEAP_REPLACEMENT we are not using the LRU reference age, the heap
+     * controls the replacement of objects.
+     */
+    return 1;
+#else
+    if (squid_curtime - e->lastref > storeAufsDirExpiredReferenceAge(SD))
+       return 1;
+    return 0;
+#endif
+}
+
+/*
+ * Add and remove the given StoreEntry from the replacement policy in
+ * use.
+ */
+
+void
+storeAufsDirReplAdd(SwapDir *SD, StoreEntry *e)
+{
+    debug(20, 4) ("storeUfsDirReplAdd: added node %p to dir %d\n", e,
+      SD->index);
+#if HEAP_REPLACEMENT
+    if (EBIT_TEST(e->flags, ENTRY_SPECIAL)) {
+        (void) 0;
+    } else {
+        e->repl.node = heap_insert(SD->repl.heap.heap, e);
+        debug(20, 4) ("storeUfsDirReplAdd: inserted node 0x%x\n", e->repl.node);
+    }
+#else
+    /* Shouldn't we not throw special objects into the lru ? */
+    dlinkAdd(e, &e->repl.lru, &SD->repl.lru.list);
+#endif
+}
+
+
+void
+storeAufsDirReplRemove(StoreEntry *e)
+{
+    SwapDir *SD = INDEXSD(e->swap_dirn);
+    debug(20, 4) ("storeUfsDirReplRemove: remove node %p from dir %d\n", e,
+      SD->index);
+#if HEAP_REPLACEMENT
+    /* And now, release the object from the replacement policy */
+    if (e->repl.node) {
+        debug(20, 4) ("storeUfsDirReplRemove: deleting node 0x%x\n",
+          e->repl.node);
+        heap_delete(SD->repl.heap.heap, e->repl.node);
+        e->repl.node = NULL;
+    }
+#else
+    dlinkDelete(&e->repl.lru, &SD->repl.lru.list);
+#endif
+}
+
+
+/* ========== LOCAL FUNCTIONS ABOVE, GLOBAL FUNCTIONS BELOW ========== */
+
+void
+storeAufsDirStats(SwapDir *SD, StoreEntry * sentry)
+{
+    aioinfo_t *aioinfo;
+#if HAVE_STATVFS
+    struct statvfs sfs;
+#endif
+    aioinfo = (aioinfo_t *)SD->fsdata;
+    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));
+#if HAVE_STATVFS
+#define fsbtoblk(num, fsbs, bs) \
+    (((fsbs) != 0 && (fsbs) < (bs)) ? \
+            (num) / ((bs) / (fsbs)) : (num) * ((fsbs) / (bs)))
+       if (!statvfs(SD->path, &sfs)) {
+            storeAppendPrintf(sentry, "Filesystem Space in use: %d/%d KB (%d%%)\n",
+            fsbtoblk((sfs.f_blocks - sfs.f_bfree), sfs.f_frsize, 1024),
+            fsbtoblk(sfs.f_blocks, sfs.f_frsize, 1024),
+            percent(sfs.f_blocks - sfs.f_bfree, sfs.f_blocks));
+            storeAppendPrintf(sentry, "Filesystem Inodes in use: %d/%d (%d%%)\n",
+            sfs.f_files - sfs.f_ffree, sfs.f_files,
+            percent(sfs.f_files - sfs.f_ffree, sfs.f_files));
+    }
+#endif
+    storeAppendPrintf(sentry, "Flags:");
+    if (SD->flags.selected)
+        storeAppendPrintf(sentry, " SELECTED");
+    if (SD->flags.read_only)
+        storeAppendPrintf(sentry, " READ-ONLY");
+    storeAppendPrintf(sentry, "\n");
+#if !HEAP_REPLACEMENT
+    storeAppendPrintf(sentry, "LRU Expiration Age: %6.2f days\n",
+       (double) storeAufsDirExpiredReferenceAge(SD) / 86400.0);
+#else
+#if 0
+    storeAppendPrintf(sentry, "Storage Replacement Threshold:\t%f\n",
+       heap_peepminkey(sd.repl.heap.heap));
+#endif
+#endif
+}
+
+/*
+ * storeAufsDirReconfigure
+ *
+ * This routine is called when the given swapdir needs reconfiguring 
+ */
+void
+storeAufsDirReconfigure(SwapDir *sd, int index, char *path)
+{
+    char *token;
+    int i;
+    int size;
+    int l1;
+    int l2;
+    unsigned int read_only = 0;
+
+    i = GetInteger();
+    size = i << 10;            /* Mbytes to kbytes */
+    if (size <= 0)
+       fatal("storeAufsDirReconfigure: invalid size value");
+    i = GetInteger();
+    l1 = i;
+    if (l1 <= 0)
+       fatal("storeAufsDirReconfigure: invalid level 1 directories value");
+    i = GetInteger();
+    l2 = i;
+    if (l2 <= 0)
+       fatal("storeAufsDirReconfigure: invalid level 2 directories value");
+    if ((token = strtok(NULL, w_space)))
+       if (!strcasecmp(token, "read-only"))
+           read_only = 1;
+
+    /* 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;
+    if (sd->flags.read_only != read_only)
+       debug(3, 1) ("Cache dir '%s' now %s\n",
+           path, read_only ? "Read-Only" : "Read-Write");
+    sd->flags.read_only = read_only;
+    return;
+}
+
+void
+storeAufsDirDump(StoreEntry * entry, const char *name, SwapDir * s)
+{
+    aioinfo_t *aioinfo = (aioinfo_t *)s->fsdata;
+    storeAppendPrintf(entry, "%s %s %s %d %d %d\n",
+       name,
+        "asyncufs",
+       s->path,
+       s->max_size >> 10,
+       aioinfo->l1,
+       aioinfo->l2);
+}
+
+/*
+ * Only "free" the filesystem specific stuff here
+ */
+static void
+storeAufsDirFree(SwapDir * s)
+{
+    aioinfo_t *aioinfo = (aioinfo_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);
+    aioinfo_t *aioinfo = (aioinfo_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(20, 0) ("storeAufsCleanupDoubleCheck: MISSING SWAP FILE\n");
+        debug(20, 0) ("storeAufsCleanupDoubleCheck: FILENO %08X\n", e->swap_filen);
+        debug(20, 0) ("storeAufsCleanupDoubleCheck: PATH %s\n",
+            storeAufsDirFullPath(sd, e->swap_filen, NULL));
+        storeEntryDump(e, 0);
+        return -1;
+    }       
+    if (e->swap_file_sz != sb.st_size) {
+        debug(20, 0) ("storeAufsCleanupDoubleCheck: SIZE MISMATCH\n");
+        debug(20, 0) ("storeAufsCleanupDoubleCheck: FILENO %08X\n", e->swap_filen);
+        debug(20, 0) ("storeAufsCleanupDoubleCheck: PATH %s\n",
+            storeAufsDirFullPath(sd, e->swap_filen, NULL));
+        debug(20, 0) ("storeAufsCleanupDoubleCheck: ENTRY SIZE: %d, FILE SIZE: %d\n",
+            e->swap_file_sz, (int) sb.st_size); 
+        storeEntryDump(e, 0); 
+        return -1;
+    }       
+    return 0;
+}
+
+/*
+ * storeAufsDirParse
+ *
+ * Called when a *new* fs is being setup.
+ */
+void
+storeAufsDirParse(SwapDir *sd, int index, char *path)
+{
+    char *token;
+    int i;
+    int size;
+    int l1;
+    int l2;
+    unsigned int read_only = 0;
+    aioinfo_t *aioinfo;
+
+    i = GetInteger();
+    size = i << 10;            /* Mbytes to kbytes */
+    if (size <= 0)
+       fatal("storeAufsDirParse: invalid size value");
+    i = GetInteger();
+    l1 = i;
+    if (l1 <= 0)
+       fatal("storeAufsDirParse: invalid level 1 directories value");
+    i = GetInteger();
+    l2 = i;
+    if (l2 <= 0)
+       fatal("storeAufsDirParse: invalid level 2 directories value");
+    if ((token = strtok(NULL, w_space)))
+       if (!strcasecmp(token, "read-only"))
+           read_only = 1;
+
+    aioinfo = xmalloc(sizeof(aioinfo_t));
+    if (aioinfo == NULL)
+        fatal("storeAufsDirParse: couldn't xmalloc() aioinfo_t!\n");
+
+    sd->index = index;
+    sd->path = xstrdup(path);
+    sd->max_size = size;
+    sd->fsdata = aioinfo;
+    aioinfo->l1 = l1;
+    aioinfo->l2 = l2;
+    aioinfo->swaplog_fd = -1;
+    aioinfo->map = NULL; /* Debugging purposes */
+    aioinfo->suggest = 0;
+    sd->flags.read_only = read_only;
+    sd->init = storeAufsDirInit;
+    sd->newfs = storeAufsDirNewfs;
+    sd->dump = storeAufsDirDump;
+    sd->freefs = storeAufsDirFree;
+    sd->dblcheck = storeAufsCleanupDoubleCheck;
+    sd->statfs = storeAufsDirStats;
+    sd->maintainfs = storeAufsDirMaintain;
+    sd->checkobj = storeAufsDirCheckObj;
+    sd->refobj = storeAufsDirRefObj;
+    sd->unrefobj = storeAufsDirUnrefObj;
+    sd->callback = aioCheckCallbacks;
+    sd->sync = aioSync;
+    sd->obj.create = storeAufsCreate;
+    sd->obj.open = storeAufsOpen;
+    sd->obj.close = storeAufsClose;
+    sd->obj.read = storeAufsRead;
+    sd->obj.write = storeAufsWrite;
+    sd->obj.unlink = storeAufsUnlink;
+    sd->log.open = storeAufsDirOpenSwapLog;
+    sd->log.close = storeAufsDirCloseSwapLog;
+    sd->log.write = storeAufsDirSwapLog;
+    sd->log.clean.open = storeAufsDirWriteCleanOpen;
+
+    /* Initialise replacement policy stuff */
+#if HEAP_REPLACEMENT
+    /*
+     * Create new heaps with cache replacement policies attached to them.
+     * The cache replacement policy is specified as either GDSF or LFUDA in
+     * the squid.conf configuration file.  Note that the replacement policy
+     * applies only to the disk replacement algorithm.  Memory replacement
+     * always uses GDSF since we want to maximize object hit rate.
+     */
+    if (Config.replPolicy) {
+        if (tolower(Config.replPolicy[0]) == 'g') {
+            debug(20, 1) ("Using GDSF disk replacement policy\n");
+            sd->repl.heap.heap = new_heap(10000, HeapKeyGen_StoreEntry_GDSF);
+        } else if (tolower(Config.replPolicy[0]) == 'l') {
+            if (tolower(Config.replPolicy[1]) == 'f') {
+                debug(20, 1) ("Using LFUDA disk replacement policy\n");
+                sd->repl.heap.heap = new_heap(10000, HeapKeyGen_StoreEntry_LFUDA);
+            } else if (tolower(Config.replPolicy[1]) == 'r') {
+                debug(20, 1) ("Using LRU heap disk replacement policy\n");
+                sd->repl.heap.heap = new_heap(10000, HeapKeyGen_StoreEntry_LRU);
+            }
+        } else {
+            debug(20, 1) ("Unrecognized replacement_policy; using GDSF\n");
+            sd->repl.heap.heap = new_heap(10000, HeapKeyGen_StoreEntry_GDSF);
+        }
+    } else {
+        debug(20, 1) ("Using default disk replacement policy (GDSF)\n");
+        sd->repl.heap.heap = new_heap(10000, HeapKeyGen_StoreEntry_GDSF);
+    }
+#else
+    sd->repl.lru.list.head = NULL;
+    sd->repl.lru.list.tail = NULL;
+#endif
+}
+
+void
+storeFsSetup_aufs(storefs_entry_t *storefs)
+{
+    storefs->parsefunc = storeAufsDirParse;
+    storefs->reconfigurefunc = storeAufsDirReconfigure;
+    storefs->donefunc = aioDone;
+    aioInit();
+}
diff --git a/src/fs/aufs/store_io_aufs.cc b/src/fs/aufs/store_io_aufs.cc
new file mode 100644 (file)
index 0000000..70aa147
--- /dev/null
@@ -0,0 +1,381 @@
+
+/*
+ * DEBUG 78
+ */
+
+#include "squid.h"
+#include "store_asyncufs.h"
+
+static AIOCB storeAufsReadDone;
+static AIOCB storeAufsWriteDone;
+static void storeAufsIOCallback(storeIOState * sio, int errflag);
+static AIOCB storeAufsOpenDone;
+static int storeAufsSomethingPending(storeIOState *);
+static int storeAufsKickWriteQueue(storeIOState * sio);
+static void storeAufsIOFreeEntry(void *, int);
+
+struct _queued_write {
+    char *buf;
+    size_t size;
+    off_t offset;
+    FREE *free_func;
+};
+
+struct _queued_read {
+    char *buf;
+    size_t size;
+    off_t offset;
+    STRCB *callback;
+    void *callback_data;
+};
+
+/* === PUBLIC =========================================================== */
+
+/* open for reading */
+storeIOState *
+storeAufsOpen(SwapDir *SD, StoreEntry *e, STFNCB * file_callback,
+  STIOCB * callback, void *callback_data)
+{
+    sfileno f = e->swap_filen;
+    char *path = storeAufsDirFullPath(SD, f, NULL);
+    storeIOState *sio;
+    debug(78, 3) ("storeAufsOpen: fileno %08X\n", f);
+    /*
+     * we should detect some 'too many files open' condition and return
+     * NULL here.
+     */
+    while (aioQueueSize() > MAGIC1)
+       return NULL;
+    sio = memAllocate(MEM_STORE_IO);
+    cbdataAdd(sio, storeAufsIOFreeEntry, MEM_STORE_IO);
+    sio->fsstate = memPoolAlloc(aio_state_pool);
+    ((aiostate_t *)(sio->fsstate))->fd = -1;
+    ((aiostate_t *)(sio->fsstate))->flags.opening = 1;
+    sio->swap_filen = f;
+    sio->swap_dirn = SD->index;
+    sio->mode = O_RDONLY;
+    sio->callback = callback;
+    sio->callback_data = callback_data;
+    sio->e = e;
+    cbdataLock(callback_data);
+    aioOpen(path, O_RDONLY, 0644, storeAufsOpenDone, sio);
+    Opening_FD++;
+    store_open_disk_fd++;
+    return sio;
+}
+
+/* open for creating */
+storeIOState *
+storeAufsCreate(SwapDir *SD, StoreEntry *e, STFNCB *file_callback, STIOCB *callback, void *callback_data)
+{
+    char *path ;
+    storeIOState *sio;
+    sfileno filn;
+    sdirno dirn;
+
+    /* Allocate a number */
+    dirn = SD->index;
+    filn = storeAufsDirMapBitAllocate(SD);
+    path = storeAufsDirFullPath(SD, filn, NULL);
+
+    debug(78, 3) ("storeAufsCreate: fileno %08X\n", filn);
+    /*
+     * we should detect some 'too many files open' condition and return
+     * NULL here.
+     */
+    while (aioQueueSize() > MAGIC1)
+       return NULL;
+    sio = memAllocate(MEM_STORE_IO);
+    cbdataAdd(sio, storeAufsIOFreeEntry, MEM_STORE_IO);
+    sio->fsstate = memPoolAlloc(aio_state_pool);
+    ((aiostate_t *)(sio->fsstate))->fd = -1;
+    ((aiostate_t *)(sio->fsstate))->flags.opening = 1;
+    sio->swap_filen = filn;
+    sio->swap_dirn = dirn;
+    sio->mode = O_WRONLY;
+    sio->callback = callback;
+    sio->callback_data = callback_data;
+    sio->e = (StoreEntry *) e;
+    cbdataLock(callback_data);
+    aioOpen(path, O_WRONLY | O_CREAT | O_TRUNC, 0644, storeAufsOpenDone, sio);
+    Opening_FD++;
+    store_open_disk_fd++;
+
+    /* now insert into the replacement policy */
+    storeAufsDirReplAdd(SD, e);
+    return sio;
+
+}
+
+
+
+/* Close */
+void
+storeAufsClose(SwapDir *SD, storeIOState * sio)
+{
+    aiostate_t *aiostate = (aiostate_t *)sio->fsstate;
+    debug(78, 3) ("storeAufsClose: dirno %d, fileno %08X, FD %d\n",
+       sio->swap_dirn, sio->swap_filen, aiostate->fd);
+    if (storeAufsSomethingPending(sio)) {
+       aiostate->flags.close_request = 1;
+       return;
+    }
+    storeAufsIOCallback(sio, 0);
+}
+
+
+/* Read */
+void
+storeAufsRead(SwapDir *SD, storeIOState * sio, char *buf, size_t size, off_t offset, STRCB * callback, void *callback_data)
+{
+    aiostate_t *aiostate = (aiostate_t *)sio->fsstate;
+    assert(sio->read.callback == NULL);
+    assert(sio->read.callback_data == NULL);
+    assert(!aiostate->flags.reading);
+    if (aiostate->fd < 0) {
+       struct _queued_read *q;
+       debug(78, 3) ("storeAufsRead: queueing read because FD < 0\n");
+       assert(aiostate->flags.opening);
+       assert(aiostate->pending_reads == NULL);
+       q = xcalloc(1, sizeof(*q));
+       q->buf = buf;
+       q->size = size;
+       q->offset = offset;
+       q->callback = callback;
+       q->callback_data = callback_data;
+       linklistPush(&(aiostate->pending_reads), q);
+       return;
+    }
+    sio->read.callback = callback;
+    sio->read.callback_data = callback_data;
+    aiostate->read_buf = buf;
+    cbdataLock(callback_data);
+    debug(78, 3) ("storeAufsRead: dirno %d, fileno %08X, FD %d\n",
+       sio->swap_dirn, sio->swap_filen, aiostate->fd);
+    sio->offset = offset;
+    aiostate->flags.reading = 1;
+    aioRead(aiostate->fd, offset, buf, size, storeAufsReadDone, sio);
+}
+
+
+/* Write */
+void
+storeAufsWrite(SwapDir *SD, storeIOState * sio, char *buf, size_t size, off_t offset, FREE * free_func)
+{
+    aiostate_t *aiostate = (aiostate_t *)sio->fsstate;
+    debug(78, 3) ("storeAufsWrite: dirno %d, fileno %08X, FD %d\n",
+       sio->swap_dirn, sio->swap_filen, aiostate->fd);
+    if (aiostate->fd < 0) {
+       /* disk file not opened yet */
+       struct _queued_write *q;
+       assert(aiostate->flags.opening);
+       q = xcalloc(1, sizeof(*q));
+       q->buf = buf;
+       q->size = size;
+       q->offset = offset;
+       q->free_func = free_func;
+       linklistPush(&(aiostate->pending_writes), q);
+       return;
+    }
+    if (aiostate->flags.writing) {
+       struct _queued_write *q;
+       debug(78, 3) ("storeAufsWrite: queuing write\n");
+       q = xcalloc(1, sizeof(*q));
+       q->buf = buf;
+       q->size = size;
+       q->offset = offset;
+       q->free_func = free_func;
+       linklistPush(&(aiostate->pending_writes), q);
+       return;
+    }
+    aiostate->flags.writing = 1;
+    /*
+     * XXX it might be nice if aioWrite() gave is immediate
+     * feedback here about EWOULDBLOCK instead of in the
+     * callback function
+     */
+    aioWrite(aiostate->fd, offset, buf, size, storeAufsWriteDone, sio,
+       free_func);
+}
+
+/* Unlink */
+void
+storeAufsUnlink(SwapDir *SD, StoreEntry *e)
+{
+    debug(78, 3) ("storeAufsUnlink: dirno %d, fileno %08X\n", SD->index, e->swap_filen);
+    storeAufsDirReplRemove(e);
+    storeAufsDirUnlinkFile(SD, e->swap_filen);
+}
+
+/*  === STATIC =========================================================== */
+
+static int
+storeAufsKickWriteQueue(storeIOState * sio)
+{
+    aiostate_t *aiostate = (aiostate_t *)sio->fsstate;
+    struct _queued_write *q = linklistShift(&aiostate->pending_writes);
+    if (NULL == q)
+       return 0;
+    debug(78, 3) ("storeAufsKickWriteQueue: writing queued chunk of %d bytes\n",
+       q->size);
+    storeAufsWrite(INDEXSD(sio->swap_dirn), sio, q->buf, q->size, q->offset, q->free_func);
+    xfree(q);
+    return 1;
+}
+
+static int
+storeAufsKickReadQueue(storeIOState * sio)
+{
+    aiostate_t *aiostate = (aiostate_t *)sio->fsstate;
+    struct _queued_read *q = linklistShift(&(aiostate->pending_reads));
+    if (NULL == q)
+       return 0;
+    debug(78, 3) ("storeAufsKickReadQueue: reading queued request of %d bytes\n",
+       q->size);
+    storeAufsRead(INDEXSD(sio->swap_dirn), sio, q->buf, q->size, q->offset, q->callback, q->callback_data);
+    xfree(q);
+    return 1;
+}
+
+static void
+storeAufsOpenDone(int unused, void *my_data, int fd, int errflag)
+{
+    storeIOState *sio = my_data;
+    aiostate_t *aiostate = (aiostate_t *)sio->fsstate;
+    debug(78, 3) ("storeAufsOpenDone: FD %d, errflag %d\n", fd, errflag);
+    Opening_FD--;
+    aiostate->flags.opening = 0;
+    if (errflag || fd < 0) {
+       errno = errflag;
+       debug(78, 0) ("storeAufsOpenDone: %s\n", xstrerror());
+       debug(78, 1) ("\t%s\n", storeAufsDirFullPath(INDEXSD(sio->swap_dirn), sio->swap_filen, NULL));
+       storeAufsIOCallback(sio, errflag);
+       return;
+    }
+    aiostate->fd = fd;
+    commSetCloseOnExec(fd);
+    fd_open(fd, FD_FILE, storeAufsDirFullPath(INDEXSD(sio->swap_dirn), sio->swap_filen, NULL));
+    if (sio->mode == O_WRONLY)
+       storeAufsKickWriteQueue(sio);
+    else if (sio->mode == O_RDONLY)
+       storeAufsKickReadQueue(sio);
+    debug(78, 3) ("storeAufsOpenDone: exiting\n");
+}
+
+static void
+storeAufsReadDone(int fd, void *my_data, int len, int errflag)
+{
+    storeIOState *sio = my_data;
+    aiostate_t *aiostate = (aiostate_t *)sio->fsstate;
+    STRCB *callback = sio->read.callback;
+    void *their_data = sio->read.callback_data;
+    ssize_t rlen;
+    debug(78, 3) ("storeAufsReadDone: dirno %d, fileno %08X, FD %d, len %d\n",
+       sio->swap_dirn, sio->swap_filen, fd, len);
+    aiostate->flags.reading = 0;
+    if (errflag) {
+       debug(78, 3) ("storeAufsReadDone: got failure (%d)\n", errflag);
+       rlen = -1;
+    } else {
+       rlen = (ssize_t) len;
+       sio->offset += len;
+    }
+    assert(callback);
+    assert(their_data);
+    sio->read.callback = NULL;
+    sio->read.callback_data = NULL;
+    if (cbdataValid(their_data))
+       callback(their_data, aiostate->read_buf, rlen);
+    cbdataUnlock(their_data);
+    /* 
+     * XXX is this safe?  The above callback may have caused sio
+     * to be freed/closed already? Philip Guenther <guenther@gac.edu>
+     * says it fixes his FD leaks, with no side effects.
+     */
+    if (aiostate->flags.close_request)
+        storeAufsIOCallback(sio, DISK_OK);
+}
+
+/*
+ * XXX TODO
+ * if errflag == EWOULDBLOCK, then we'll need to re-queue the
+ * chunk at the beginning of the write_pending list and try
+ * again later.
+ */
+static void
+storeAufsWriteDone(int fd, void *my_data, int len, int errflag)
+{
+    static int loop_detect = 0;
+    storeIOState *sio = my_data;
+    aiostate_t *aiostate = (aiostate_t *)sio->fsstate;
+    debug(78, 3) ("storeAufsWriteDone: dirno %d, fileno %08X, FD %d, len %d\n",
+       sio->swap_dirn, sio->swap_filen, fd, len);
+    assert(++loop_detect < 10);
+    aiostate->flags.writing = 0;
+    if (errflag) {
+       debug(78, 0) ("storeAufsWriteDone: got failure (%d)\n", errflag);
+       storeAufsIOCallback(sio, errflag);
+       loop_detect--;
+       return;
+    }
+    sio->offset += len;
+    if (storeAufsKickWriteQueue(sio))
+       (void) 0;
+    else if (aiostate->flags.close_request)
+       storeAufsIOCallback(sio, errflag);
+    loop_detect--;
+}
+
+static void
+storeAufsIOCallback(storeIOState * sio, int errflag)
+{
+    STIOCB *callback = sio->callback;
+    void *their_data = sio->callback_data;
+    aiostate_t *aiostate = (aiostate_t *)sio->fsstate;
+    int fd = aiostate->fd;
+    debug(78, 3) ("storeAufsIOCallback: errflag=%d\n", errflag);
+    sio->callback = NULL;
+    sio->callback_data = NULL;
+    debug(78, 3) ("%s:%d\n", __FILE__, __LINE__);
+    if (callback)
+       if (NULL == their_data || cbdataValid(their_data))
+           callback(their_data, errflag, sio);
+    debug(78, 3) ("%s:%d\n", __FILE__, __LINE__);
+    cbdataUnlock(their_data);
+    aiostate->fd = -1;
+    cbdataFree(sio);
+    if (fd < 0)
+       return;
+    debug(78, 3) ("%s:%d\n", __FILE__, __LINE__);
+    aioClose(fd);
+    fd_close(fd);
+    store_open_disk_fd--;
+    debug(78, 3) ("%s:%d\n", __FILE__, __LINE__);
+}
+
+
+static int
+storeAufsSomethingPending(storeIOState * sio)
+{
+    aiostate_t *aiostate = (aiostate_t *)sio->fsstate;
+    if (aiostate->flags.reading)
+       return 1;
+    if (aiostate->flags.writing)
+       return 1;
+    if (aiostate->flags.opening)
+       return 1;
+    return 0;
+}
+
+
+/*      
+ * We can't pass memFree() as a free function here, because we need to free
+ * the fsstate variable ..
+ */ 
+static void
+storeAufsIOFreeEntry(void *sio, int foo)
+{
+        memPoolFree(aio_state_pool, ((storeIOState *)sio)->fsstate);
+        memFree(sio, MEM_STORE_IO);
+}
+
diff --git a/src/fs/coss/Makefile.in b/src/fs/coss/Makefile.in
new file mode 100644 (file)
index 0000000..059ab5a
--- /dev/null
@@ -0,0 +1,56 @@
+#
+#  Makefile for the COSS storage driver for the Squid Object Cache server
+#
+#  $Id: Makefile.in,v 1.1 2000/05/03 17:15:47 adrian Exp $
+#
+
+FS             = coss
+
+top_srcdir     = @top_srcdir@
+VPATH          = @srcdir@
+
+CC             = @CC@
+MAKEDEPEND     = @MAKEDEPEND@
+AR_R           = @AR_R@
+RANLIB         = @RANLIB@
+AC_CFLAGS      = @CFLAGS@
+SHELL          = /bin/sh
+
+INCLUDE                = -I../../../include -I$(top_srcdir)/include -I$(top_srcdir)/src/
+CFLAGS                 = $(AC_CFLAGS) $(INCLUDE) $(DEFINES)
+
+OUT            = ../$(FS).a
+
+OBJS           = \
+               store_dir_coss.o \
+               store_io_coss.o
+
+
+all: $(OUT)
+
+$(OUT): $(OBJS)
+       @rm -f ../stamp
+       $(AR_R) $(OUT) $(OBJS)
+       $(RANLIB) $(OUT)
+
+$(OBJS): $(top_srcdir)/include/version.h ../../../include/autoconf.h
+
+.c.o:
+       @rm -f ../stamp
+       $(CC) $(CFLAGS) -c $<
+
+clean: 
+       -rm -rf *.o *pure_* core ../$(FS).a
+
+distclean:     clean
+       -rm -f Makefile
+       -rm -f Makefile.bak
+       -rm -f tags
+
+install:
+
+tags:
+       ctags *.[ch] ../../*.[ch] ../../../include/*.h ../lib/*.[ch]
+
+depend:
+       $(MAKEDEPEND) $(INCLUDE) -fMakefile *.c
diff --git a/src/fs/coss/store_coss.h b/src/fs/coss/store_coss.h
new file mode 100644 (file)
index 0000000..6e4f963
--- /dev/null
@@ -0,0 +1,82 @@
+#ifndef __COSS_H__
+#define __COSS_H__
+
+#ifndef COSS_MEMBUF_SZ
+#define        COSS_MEMBUF_SZ  1048576
+#endif
+
+#define COSS_ALLOC_NOTIFY              0
+#define COSS_ALLOC_ALLOCATE            1
+#define COSS_ALLOC_REALLOC             2
+
+struct _cossmembuf {
+    size_t diskstart;
+    size_t diskend;
+    SwapDir *SD;
+    int lockcount;
+    char buffer[COSS_MEMBUF_SZ];
+    struct _cossmembuf_flags {
+        unsigned int full:1;
+        unsigned int writing:1;
+    } flags;
+    struct _cossmembuf *next;
+};
+
+
+/* Per-storedir info */
+struct _cossinfo {
+    struct _cossmembuf *membufs;
+    struct _cossmembuf *current_membuf;
+    size_t current_offset;
+    int fd;
+    int swaplog_fd;
+    int numcollisions;
+};
+
+
+/* Per-storeiostate info */
+struct _cossstate {
+    char *readbuffer;
+    char *requestbuf;
+    size_t requestlen;
+    size_t requestoffset;
+    sfileno reqdiskoffset;
+    struct {
+        unsigned int reading:1;
+        unsigned int writing:1;
+    } flags;
+};
+
+typedef struct _cossmembuf CossMemBuf;
+typedef struct _cossinfo CossInfo;
+typedef struct _cossstate CossState;
+
+/* Whether the coss system has been setup or not */
+extern int coss_initialised;
+extern MemPool * coss_membuf_pool;
+extern MemPool * coss_state_pool;
+
+
+/*
+ * Store IO stuff
+ */
+extern STOBJCREATE storeCossCreate;
+extern STOBJOPEN storeCossOpen;
+extern STOBJCLOSE storeCossClose;
+extern STOBJREAD storeCossRead;
+extern STOBJWRITE storeCossWrite;
+extern STOBJUNLINK storeCossUnlink;
+
+extern off_t storeCossAllocate(SwapDir *SD, const StoreEntry *e, int which);
+extern void storeCossFree(StoreEntry *e);
+extern void storeCossMaintainSwapSpace(SwapDir *SD);
+extern void storeCossDirStats(SwapDir *, StoreEntry *);
+extern void storeCossDirDump(StoreEntry * entry, const char *name, SwapDir* s);
+extern void storeCossDirFree(SwapDir *);
+extern SwapDir *storeCossDirPick(void);
+
+void storeFsSetup_ufs(storefs_entry_t *);
+
+
+
+#endif
diff --git a/src/fs/coss/store_dir_coss.cc b/src/fs/coss/store_dir_coss.cc
new file mode 100644 (file)
index 0000000..1911e63
--- /dev/null
@@ -0,0 +1,846 @@
+/*
+ * $Id: store_dir_coss.cc,v 1.1 2000/05/03 17:15:47 adrian Exp $
+ *
+ * DEBUG: section 81    Store COSS Directory Routines
+ * AUTHOR: Eric Stern
+ *
+ * SQUID Internet Object Cache  http://squid.nlanr.net/Squid/
+ * ----------------------------------------------------------
+ *
+ *  Squid is the result of efforts by numerous individuals from the
+ *  Internet community.  Development is led by Duane Wessels of the
+ *  National Laboratory for Applied Network Research and funded by the
+ *  National Science Foundation.  Squid is Copyrighted (C) 1998 by
+ *  Duane Wessels and the University of California San Diego.  Please
+ *  see the COPYRIGHT file for full details.  Squid incorporates
+ *  software developed and/or copyrighted by other sources.  Please see
+ *  the CREDITS file for full details.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *  
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *  
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
+ *
+ */
+
+#include "squid.h"
+
+#include "store_coss.h"
+
+#define STORE_META_BUFSZ 4096
+
+extern void storeCossFlushMemBufs(SwapDir *SD);
+
+int n_coss_dirs = 0;
+/* static int last_coss_pick_index = -1; */
+int coss_initialised = 0;
+MemPool * coss_membuf_pool = NULL;
+MemPool * coss_state_pool = NULL;
+
+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 char *storeCossDirSwapLogFile(SwapDir *, const char *);
+static EVH storeCossRebuildFromSwapLog;
+static StoreEntry *storeCossAddDiskRestore(SwapDir *SD,const cache_key *key,
+    int file_number,
+    size_t swap_file_sz,
+    time_t expires,
+    time_t timestamp,
+    time_t lastref,
+    time_t lastmod,
+    u_num32 refcount,
+    u_short flags,
+    int clean);
+static void storeCossDirRebuild(SwapDir * sd);
+static void storeCossDirCloseTmpSwapLog(SwapDir * sd);
+static FILE *storeCossDirOpenTmpSwapLog(SwapDir *, int *, int *);
+static STLOGOPEN storeCossDirOpenSwapLog;
+static STINIT storeCossDirInit;
+static STLOGCLEANOPEN storeCossDirWriteCleanOpen;
+static void storeCossDirWriteCleanClose(SwapDir * sd);
+static STLOGCLEANWRITE storeCossDirWriteCleanEntry;
+static STLOGCLOSE storeCossDirCloseSwapLog;
+static STLOGWRITE storeCossDirSwapLog;
+static STNEWFS storeCossDirNewfs;
+static STCHECKOBJ storeCossDirCheckObj;
+static void storeCossDirShutdown(SwapDir * sd);
+static void storeCossDirParse(SwapDir *, int, char *);
+static void storeCossDirReconfigure(SwapDir *, int, char *);
+
+static char *
+storeCossDirSwapLogFile(SwapDir * sd, const char *ext)
+{
+    LOCAL_ARRAY(char, path, SQUID_MAXPATHLEN);
+    LOCAL_ARRAY(char, pathtmp, SQUID_MAXPATHLEN);
+    LOCAL_ARRAY(char, digit, 32);
+    char *pathtmp2;
+    if (Config.Log.swap) {
+        xstrncpy(pathtmp, sd->path, SQUID_MAXPATHLEN - 64);
+        while (index(pathtmp,'/'))
+            *index(pathtmp,'/')='.';
+        while (strlen(pathtmp) && pathtmp[strlen(pathtmp)-1]=='.')
+            pathtmp[strlen(pathtmp)-1]= '\0';
+        for(pathtmp2 = pathtmp; *pathtmp2 == '.'; pathtmp2++);
+        snprintf(path, SQUID_MAXPATHLEN-64, Config.Log.swap, pathtmp2);
+        if (strncmp(path, Config.Log.swap, SQUID_MAXPATHLEN - 64) == 0) {
+            strcat(path, ".");
+            snprintf(digit, 32, "%02d", sd->index);
+            strncat(path, digit, 3);
+        }
+    } else {
+        xstrncpy(path, sd->path, SQUID_MAXPATHLEN - 64);
+        strcat(path, "/swap.state");
+    }
+    if (ext)
+        strncat(path, ext, 16);
+    return path;
+}
+
+static void
+storeCossDirOpenSwapLog(SwapDir * sd)
+{
+    CossInfo *cs = (CossInfo *)sd->fsdata;
+    char *path;
+    int fd;
+    path = storeCossDirSwapLogFile(sd, NULL);
+    fd = file_open(path, O_WRONLY | O_CREAT);
+    if (fd < 0) {
+       debug(81, 1) ("%s: %s\n", path, xstrerror());
+       fatal("storeCossDirOpenSwapLog: Failed to open swap log.");
+    }
+    debug(81, 3) ("Cache COSS Dir #%d log opened on FD %d\n", sd->index, fd);
+    cs->swaplog_fd = fd;
+}
+
+static void
+storeCossDirCloseSwapLog(SwapDir * sd)
+{
+    CossInfo *cs = (CossInfo *)sd->fsdata;
+    if (cs->swaplog_fd < 0)    /* not open */
+       return;
+    file_close(cs->swaplog_fd);
+    debug(81, 3) ("Cache COSS Dir #%d log closed on FD %d\n",
+       sd->index, cs->swaplog_fd);
+    cs->swaplog_fd = -1;
+}
+
+static void
+storeCossDirInit(SwapDir * sd)
+{
+    CossInfo *cs = (CossInfo *)sd->fsdata;
+    storeCossDirOpenSwapLog(sd);
+    storeCossDirRebuild(sd);
+    cs->fd = file_open(sd->path, O_RDWR | O_CREAT);
+    n_coss_dirs++;
+}
+
+static void
+storeCossRebuildFromSwapLog(void *data)
+{
+    RebuildState *rb = data;
+    StoreEntry *e = NULL;
+    storeSwapLogData s;
+    size_t ss = sizeof(storeSwapLogData);
+    int count;
+    double x;
+    assert(rb != NULL);
+    /* load a number of objects per invocation */
+    for (count = 0; count < rb->speed; count++) {
+       if (fread(&s, ss, 1, rb->log) != 1) {
+           debug(81, 1) ("Done reading %s swaplog (%d entries)\n",
+               rb->sd->path, rb->n_read);
+           fclose(rb->log);
+           rb->log = NULL;
+           store_dirs_rebuilding--;
+           storeCossDirCloseTmpSwapLog(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;
+       debug(20, 3) ("storeCossRebuildFromSwapLog: %s %s %08X\n",
+           swap_log_op_str[(int) s.op],
+           storeKeyText(s.key),
+           s.swap_filen);
+       if (s.op == SWAP_LOG_ADD) {
+           (void) 0;
+       } else if (s.op == SWAP_LOG_DEL) {
+           if ((e = storeGet(s.key)) != NULL) {
+               /*
+                * Make sure we don't unlink the file, it might be
+                * in use by a subsequent entry.  Also note that
+                * we don't have to subtract from store_swap_size
+                * because adding to store_swap_size happens in
+                * the cleanup procedure.
+                */
+               storeExpireNow(e);
+               storeReleaseRequest(e);
+               if (e->swap_filen > -1) {
+                   e->swap_filen = -1;
+               }
+                storeRelease(e);
+                /* Fake an unlink here, this is a bad hack :( */
+               dlinkDelete(&e->repl.lru, &rb->sd->repl.lru.list);
+               rb->counts.objcount--;
+               rb->counts.cancelcount++;
+           }
+           continue;
+       } else {
+           x = log(++rb->counts.bad_log_op) / log(10.0);
+           if (0.0 == x - (double) (int) x)
+               debug(20, 1) ("WARNING: %d invalid swap log entries found\n",
+                   rb->counts.bad_log_op);
+           rb->counts.invalid++;
+           continue;
+       }
+       if ((++rb->counts.scancount & 0xFFFF) == 0)
+           debug(20, 3) ("  %7d %s Entries read so far.\n",
+               rb->counts.scancount, rb->sd->path);
+       if (EBIT_TEST(s.flags, KEY_PRIVATE)) {
+           rb->counts.badflags++;
+           continue;
+       }
+       e = storeGet(s.key);
+       if (e) {
+           /* key already exists, current entry is newer */
+           /* keep old, ignore new */
+           rb->counts.dupcount++;
+           continue;
+       }
+       /* update store_swap_size */
+       rb->counts.objcount++;
+       e = storeCossAddDiskRestore(rb->sd,s.key,
+           s.swap_filen,
+           s.swap_file_sz,
+           s.expires,
+           s.timestamp,
+           s.lastref,
+           s.lastmod,
+           s.refcount,
+           s.flags,
+           (int) rb->flags.clean);
+       storeDirSwapLog(e, SWAP_LOG_ADD);
+    }
+    eventAdd("storeCossRebuild", storeCossRebuildFromSwapLog, rb, 0.0, 1);
+}
+
+/* Add a new object to the cache with empty memory copy and pointer to disk
+ * use to rebuild store from disk. */
+static StoreEntry *
+storeCossAddDiskRestore(SwapDir *SD, const cache_key * key,
+    int file_number,
+    size_t swap_file_sz,
+    time_t expires,
+    time_t timestamp,
+    time_t lastref,
+    time_t lastmod,
+    u_num32 refcount,
+    u_short flags,
+    int clean)
+{
+    StoreEntry *e = NULL;
+    debug(20, 5) ("storeCossAddDiskRestore: %s, fileno=%08X\n", storeKeyText(key), file_number);
+    /* if you call this you'd better be sure file_number is not 
+     * already in use! */
+    e = new_StoreEntry(STORE_ENTRY_WITHOUT_MEMOBJ, NULL, NULL);
+    e->store_status = STORE_OK;
+    e->swap_dirn = SD->index;
+    storeSetMemStatus(e, NOT_IN_MEMORY);
+    e->swap_status = SWAPOUT_DONE;
+    e->swap_filen = file_number;
+    e->swap_file_sz = swap_file_sz;
+    e->lock_count = 0;
+#if !HEAP_REPLACEMENT
+    e->refcount = 0;
+#endif
+    e->lastref = lastref;
+    e->timestamp = timestamp;
+    e->expires = expires;
+    e->lastmod = lastmod;
+    e->refcount = refcount;
+    e->flags = flags;
+    EBIT_SET(e->flags, ENTRY_CACHABLE);
+    EBIT_CLR(e->flags, RELEASE_REQUEST);
+    EBIT_CLR(e->flags, KEY_PRIVATE);
+    e->ping_status = PING_NONE;
+    EBIT_CLR(e->flags, ENTRY_VALIDATED);
+    storeHashInsert(e, key);   /* do it after we clear KEY_PRIVATE */
+    dlinkAdd(e, &e->repl.lru, &SD->repl.lru.list);
+    e->swap_filen = storeCossAllocate(SD, e, COSS_ALLOC_NOTIFY);
+    return e;
+}
+
+static void
+storeCossDirRebuild(SwapDir * sd)
+{
+    RebuildState *rb = xcalloc(1, sizeof(*rb));
+    int clean = 0;
+    int zero = 0;
+    FILE *fp;
+    EVH *func = NULL;
+    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 storeCossRebuildFromSwapLog().
+     */
+    fp = storeCossDirOpenTmpSwapLog(sd, &clean, &zero);
+    debug(20, 1) ("Rebuilding COSS storage in %s (%s)\n",
+       sd->path, clean ? "CLEAN" : "DIRTY");
+    if (!clean || fp == NULL) {
+       /* COSS cannot yet rebuild from a dirty state. If the log
+        * is dirty then the COSS contents is thrown away.
+        * Why? I guess it is because some contents will be lost,
+        * and COSS cannot verify this..
+        */
+       if (fp != NULL)
+           fclose(fp);
+       storeCossDirCloseTmpSwapLog(rb->sd);
+        /*
+        * XXX Make sure we don't trigger an assertion if this is the first
+        * storedir, since if we are, this call will cause storeRebuildComplete
+        * to prematurely complete the rebuild process, and then some other
+        * storedir will try to rebuild and eventually die.
+        */
+        store_dirs_rebuilding++;
+        storeRebuildComplete(&rb->counts);
+        store_dirs_rebuilding--;
+       xfree(rb);
+        return;
+    }
+    func = storeCossRebuildFromSwapLog;
+    rb->log = fp;
+    rb->flags.clean = (unsigned int) clean;
+    store_dirs_rebuilding++;
+    cbdataAdd(rb, cbdataXfree, 0);
+    eventAdd("storeCossRebuild", func, rb, 0.0, 1);
+}
+
+static void
+storeCossDirCloseTmpSwapLog(SwapDir * sd)
+{
+    CossInfo *cs = (CossInfo *)sd->fsdata;
+    char *swaplog_path = xstrdup(storeCossDirSwapLogFile(sd, NULL));
+    char *new_path = xstrdup(storeCossDirSwapLogFile(sd, ".new"));
+    int fd;
+    file_close(cs->swaplog_fd);
+#ifdef _SQUID_OS2_
+    if (unlink(swaplog_path) < 0) {
+       debug(50, 0) ("%s: %s\n", swaplog_path, xstrerror());
+       fatal("storeCossDirCloseTmpSwapLog: unlink failed");
+    }
+#endif
+    if (xrename(new_path, swaplog_path) < 0) {
+       fatal("storeCossDirCloseTmpSwapLog: rename failed");
+    }
+    fd = file_open(swaplog_path, O_WRONLY | O_CREAT);
+    if (fd < 0) {
+       debug(50, 1) ("%s: %s\n", swaplog_path, xstrerror());
+       fatal("storeCossDirCloseTmpSwapLog: Failed to open swap log.");
+    }
+    safe_free(swaplog_path);
+    safe_free(new_path);
+    cs->swaplog_fd = fd;
+    debug(47, 3) ("Cache COSS Dir #%d log opened on FD %d\n", sd->index, fd);
+}
+
+static FILE *
+storeCossDirOpenTmpSwapLog(SwapDir * sd, int *clean_flag, int *zero_flag)
+{
+    CossInfo *cs = (CossInfo *)sd->fsdata;
+    char *swaplog_path = xstrdup(storeCossDirSwapLogFile(sd, NULL));
+    char *clean_path = xstrdup(storeCossDirSwapLogFile(sd, ".last-clean"));
+    char *new_path = xstrdup(storeCossDirSwapLogFile(sd, ".new"));
+    struct stat log_sb;
+    struct stat clean_sb;
+    FILE *fp;
+    int fd;
+    if (stat(swaplog_path, &log_sb) < 0) {
+       debug(47, 1) ("Cache COSS Dir #%d: No log file\n", sd->index);
+       safe_free(swaplog_path);
+       safe_free(clean_path);
+       safe_free(new_path);
+       return NULL;
+    }
+    *zero_flag = log_sb.st_size == 0 ? 1 : 0;
+    /* close the existing write-only FD */
+    if (cs->swaplog_fd >= 0)
+       file_close(cs->swaplog_fd);
+    /* open a write-only FD for the new log */
+    fd = file_open(new_path, O_WRONLY | O_CREAT | O_TRUNC);
+    if (fd < 0) {
+       debug(50, 1) ("%s: %s\n", new_path, xstrerror());
+       fatal("storeDirOpenTmpSwapLog: Failed to open swap log.");
+    }
+    cs->swaplog_fd = fd;
+    /* open a read-only stream of the old log */
+    fp = fopen(swaplog_path, "r");
+    if (fp == NULL) {
+       debug(50, 0) ("%s: %s\n", swaplog_path, xstrerror());
+       fatal("Failed to open swap log for reading");
+    }
+    memset(&clean_sb, '\0', sizeof(struct stat));
+    if (stat(clean_path, &clean_sb) < 0)
+       *clean_flag = 0;
+    else if (clean_sb.st_mtime < log_sb.st_mtime)
+       *clean_flag = 0;
+    else
+       *clean_flag = 1;
+    safeunlink(clean_path, 1);
+    safe_free(swaplog_path);
+    safe_free(clean_path);
+    safe_free(new_path);
+    return fp;
+}
+
+struct _clean_state {
+    char *cur;
+    char *new;
+    char *cln;
+    char *outbuf;
+    off_t outbuf_offset;
+    int fd;
+};
+
+#define CLEAN_BUF_SZ 16384
+/*
+ * Begin the process to write clean cache state.  For COSS this means
+ * opening some log files and allocating write buffers.  Return 0 if
+ * we succeed, and assign the 'func' and 'data' return pointers.
+ */
+static int
+storeCossDirWriteCleanOpen(SwapDir * sd)
+{
+    struct _clean_state *state = xcalloc(1, sizeof(*state));
+    struct stat sb;
+    sd->log.clean.write = NULL;
+    sd->log.clean.state = NULL;
+    state->cur = xstrdup(storeCossDirSwapLogFile(sd, NULL));
+    state->new = xstrdup(storeCossDirSwapLogFile(sd, ".clean"));
+    state->cln = xstrdup(storeCossDirSwapLogFile(sd, ".last-clean"));
+    state->outbuf = xcalloc(CLEAN_BUF_SZ, 1);
+    state->outbuf_offset = 0;
+    unlink(state->new);
+    unlink(state->cln);
+    state->fd = file_open(state->new, O_WRONLY | O_CREAT | O_TRUNC);
+    if (state->fd < 0)
+       return -1;
+    debug(20, 3) ("storeCOssDirWriteCleanLogs: opened %s, FD %d\n",
+       state->new, state->fd);
+#if HAVE_FCHMOD
+    if (stat(state->cur, &sb) == 0)
+       fchmod(state->fd, sb.st_mode);
+#endif
+    sd->log.clean.write = storeCossDirWriteCleanEntry;
+    sd->log.clean.state = state;
+    return 0;
+}
+
+/*
+ * "write" an entry to the clean log file.
+ */
+static void
+storeCossDirWriteCleanEntry(const StoreEntry * e, SwapDir * sd)
+{
+    storeSwapLogData s;
+    static size_t ss = sizeof(storeSwapLogData);
+    struct _clean_state *state = sd->log.clean.state;
+    if (NULL == e) {
+       storeCossDirWriteCleanClose(sd);
+       return;
+    }
+    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->key, MD5_DIGEST_CHARS);
+    xmemcpy(state->outbuf + state->outbuf_offset, &s, ss);
+    state->outbuf_offset += ss;
+    /* buffered write */
+    if (state->outbuf_offset + ss > CLEAN_BUF_SZ) {
+       if (write(state->fd, state->outbuf, state->outbuf_offset) < 0) {
+           debug(50, 0) ("storeCossDirWriteCleanLogs: %s: write: %s\n",
+               state->new, xstrerror());
+           debug(20, 0) ("storeCossDirWriteCleanLogs: Current swap logfile not replaced.\n");
+           file_close(state->fd);
+           state->fd = -1;
+           unlink(state->new);
+           safe_free(state);
+           sd->log.clean.state = NULL;
+           sd->log.clean.write = NULL;
+       }
+       state->outbuf_offset = 0;
+    }
+}
+
+static void
+storeCossDirWriteCleanClose(SwapDir * sd)
+{
+    struct _clean_state *state = sd->log.clean.state;
+    if (state->fd < 0)
+       return;
+    if (write(state->fd, state->outbuf, state->outbuf_offset) < 0) {
+       debug(50, 0) ("storeCossDirWriteCleanLogs: %s: write: %s\n",
+           state->new, xstrerror());
+       debug(20, 0) ("storeCossDirWriteCleanLogs: Current swap logfile "
+           "not replaced.\n");
+       file_close(state->fd);
+       state->fd = -1;
+       unlink(state->new);
+    }
+    safe_free(state->outbuf);
+    /*
+     * You can't rename open files on Microsoft "operating systems"
+     * so we have to close before renaming.
+     */
+    storeCossDirCloseSwapLog(sd);
+    /* rename */
+    if (state->fd >= 0) {
+#ifdef _SQUID_OS2_
+       file_close(state->fd);
+       state->fd = -1;
+       if (unlink(cur) < 0)
+           debug(50, 0) ("storeCossDirWriteCleanLogs: 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
+storeCossDirSwapLog(const SwapDir * sd, const StoreEntry * e, int op)
+{
+    CossInfo *cs = (CossInfo *)sd->fsdata;
+    storeSwapLogData *s = xcalloc(1, sizeof(storeSwapLogData));
+    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->key, MD5_DIGEST_CHARS);
+    file_write(cs->swaplog_fd,
+       -1,
+       s,
+       sizeof(storeSwapLogData),
+       NULL,
+       NULL,
+       xfree);
+}
+
+static void
+storeCossDirNewfs(SwapDir * sd)
+{
+    debug(47, 3) ("Creating swap space in %s\n", sd->path);
+}
+
+// we are shutting down, flush all membufs to disk
+static void
+storeCossDirShutdown(SwapDir *SD)
+{
+    CossInfo *cs = (CossInfo *)SD->fsdata;
+    CossMemBuf *t;
+
+    // we need to do this synchronously!
+    for (t = cs->membufs; t; t = t->next) {
+        lseek(cs->fd, t->diskstart, SEEK_SET);
+        write(cs->fd, t->buffer, COSS_MEMBUF_SZ);
+    }
+    file_close(cs->fd);
+    cs->fd = -1;
+
+    if (cs->swaplog_fd > -1) {
+       file_close(cs->swaplog_fd);
+       cs->swaplog_fd = -1;
+    }
+
+    n_coss_dirs--;
+}
+
+/*
+ * storeCossDirCheckObj
+ *
+ * This routine is called by storeDirSelectSwapDir to see if the given
+ * object is able to be stored on this filesystem. COSS filesystems will
+ * not store everything. We don't check for maxobjsize here since its
+ * done by the upper layers.
+ */
+int
+storeCossDirCheckObj(SwapDir *SD, const StoreEntry *e)
+{
+  /* Check if the object is a special object, we can't cache these */
+  if (EBIT_TEST(e->flags, ENTRY_SPECIAL))
+      return -1;
+
+  /* Otherwise, we're ok */
+  /* Return 900 (90%) load */
+  return 900;
+}
+     
+/* ========== LOCAL FUNCTIONS ABOVE, GLOBAL FUNCTIONS BELOW ========== */
+
+void
+storeCossDirStats(SwapDir *SD, StoreEntry * sentry)
+{
+    CossInfo *cs = (CossInfo *)SD->fsdata;
+    
+    storeAppendPrintf(sentry, "\n");
+    storeAppendPrintf(sentry, "Maximum Size: %d KB\n", SD->max_size);
+    storeAppendPrintf(sentry, "Current Size: %d KB\n", SD->cur_size);
+    storeAppendPrintf(sentry, "Percent Used: %0.2f%%\n",
+      100.0 * SD->cur_size / SD->max_size);
+    storeAppendPrintf(sentry, "Number of object collisions: %d\n", (int)cs->numcollisions);
+#if 0
+    /* is this applicable? I Hope not .. */
+    storeAppendPrintf(sentry, "Filemap bits in use: %d of %d (%d%%)\n",
+      SD->map->n_files_in_map, SD->map->max_n_files,
+      percent(SD->map->n_files_in_map, SD->map->max_n_files));
+#endif
+    storeAppendPrintf(sentry, "Flags:");
+    if (SD->flags.selected)
+        storeAppendPrintf(sentry, " SELECTED");
+    if (SD->flags.read_only)
+        storeAppendPrintf(sentry, " READ-ONLY");
+    storeAppendPrintf(sentry, "\n");
+}
+
+static void
+storeCossDirParse(SwapDir *sd, int index, char *path)
+{
+    char *token;
+    unsigned int i;
+    unsigned int size;
+    unsigned int read_only = 0;
+    CossInfo *cs;
+
+    i = GetInteger();
+    size = i << 10;            /* Mbytes to Kbytes */
+    if (size <= 0)
+       fatal("storeCossDirParse: invalid size value");
+    if ((token = strtok(NULL, w_space)))
+       if (!strcasecmp(token, "read-only"))
+           read_only = 1;
+
+    cs = xmalloc(sizeof(CossInfo));
+    if (cs == NULL)
+       fatal("storeCossDirParse: couldn't xmalloc() CossInfo!\n");
+
+    sd->index = index;
+    sd->path = xstrdup(path);
+    sd->max_size = size;
+    sd->fsdata = cs;
+
+    cs->fd = -1;
+    cs->swaplog_fd = -1;
+    sd->flags.read_only = read_only;
+
+    sd->init = storeCossDirInit;
+    sd->newfs = storeCossDirNewfs;
+    sd->dump = storeCossDirDump;
+    sd->freefs = storeCossDirShutdown;
+    sd->dblcheck = NULL;
+    sd->statfs = storeCossDirStats;
+    sd->maintainfs = NULL;
+    sd->checkobj = storeCossDirCheckObj;
+    sd->refobj = NULL;                 /* LRU is done in storeCossRead */
+    sd->unrefobj = NULL;
+    sd->callback = NULL;
+    sd->sync = NULL; /* should we make it call the coss sync? */
+
+    sd->obj.create = storeCossCreate;
+    sd->obj.open = storeCossOpen;
+    sd->obj.close = storeCossClose;
+    sd->obj.read = storeCossRead;
+    sd->obj.write = storeCossWrite;
+    sd->obj.unlink = storeCossUnlink;
+
+    sd->log.open = storeCossDirOpenSwapLog;
+    sd->log.close = storeCossDirCloseSwapLog;
+    sd->log.write = storeCossDirSwapLog;
+    sd->log.clean.open = storeCossDirWriteCleanOpen;
+    sd->log.clean.write = storeCossDirWriteCleanEntry;
+
+    cs->current_offset = 0;
+    cs->fd = -1;
+    cs->swaplog_fd = -1;
+    cs->numcollisions = 0;
+    cs->membufs = memPoolAlloc(coss_membuf_pool);
+    cs->membufs->diskstart = 0;
+    cs->membufs->diskend = COSS_MEMBUF_SZ;
+    cs->membufs->lockcount = 0;
+    cs->membufs->flags.full = 0;
+    cs->membufs->flags.writing = 0;
+    cs->membufs->next = NULL;
+    cs->membufs->SD = sd;
+    cs->current_membuf = cs->membufs;
+
+    sd->repl.lru.list.head = NULL;
+    sd->repl.lru.list.tail = NULL;
+}
+
+
+static void
+storeCossDirReconfigure(SwapDir *sd, int index, char *path)
+{
+    char *token;
+    unsigned int i;
+    unsigned int size;
+    unsigned int read_only = 0;
+
+    i = GetInteger();
+    size = i << 10;            /* Mbytes to Kbytes */
+    if (size <= 0)
+       fatal("storeCossDirParse: invalid size value");
+    if ((token = strtok(NULL, w_space)))
+       if (!strcasecmp(token, "read-only"))
+           read_only = 1;
+
+    if (size == sd->max_size)
+        debug (3, 1) ("Cache COSS dir '%s' size remains unchanged at %d KB\n", path, size);
+    else {
+        debug (3, 1) ("Cache COSS dir '%s' size changed to %d KB\n", path, size);
+        sd->max_size = size;
+    }
+
+    if (read_only != sd->flags.read_only) {
+        debug (3, 1) ("Cache COSS dir '%s' now %s\n", path, read_only ? "Read-Only" : "Read-Write");
+        sd->flags.read_only = read_only;
+    }
+}
+
+void
+storeCossDirDump(StoreEntry * entry, const char *name, SwapDir * s)
+{
+    storeAppendPrintf(entry, "%s %s %s %d\n",
+       name,
+        s->type,
+       s->path,
+       s->max_size >> 20);
+}
+
+#if 0
+SwapDir *
+storeCossDirPick(void)
+{
+    int i,choosenext = 0;
+    SwapDir *SD;
+  
+    if (n_coss_dirs == 0)
+        return NULL;
+    for (i = 0; i < Config.cacheSwap.n_configured; i++) {
+       SD = &Config.cacheSwap.swapDirs[i];
+        if (SD->type == SWAPDIR_COSS) {
+            if ((last_coss_pick_index == -1) || (n_coss_dirs == 1)) {
+                last_coss_pick_index = i;
+                return SD;
+            } else if (choosenext) {
+                last_coss_pick_index = i;
+                return SD;
+            } else if (last_coss_pick_index == i) {
+                choosenext = 1;
+            }
+        }
+    }
+    for (i = 0; i < Config.cacheSwap.n_configured; i++) {
+       SD = &Config.cacheSwap.swapDirs[i];
+        if (SD->type == SWAPDIR_COSS) {
+            if ((last_coss_pick_index == -1) || (n_coss_dirs == 1)) {
+                last_coss_pick_index = i;
+                return SD;
+            } else if (choosenext) {
+                last_coss_pick_index = i;
+                return SD;
+            } else if (last_coss_pick_index == i) {
+                choosenext = 1;
+            }
+        }
+    }
+    return NULL;               
+}
+#endif
+
+/*
+ * initial setup/done code
+ */
+static void
+storeCossDirDone(void)
+{
+    memPoolDestroy(coss_membuf_pool);
+    memPoolDestroy(coss_state_pool);
+    coss_initialised = 0;
+}
+
+void
+storeFsSetup_coss(storefs_entry_t *storefs)
+{
+    assert(!coss_initialised);
+
+    storefs->parsefunc = storeCossDirParse;
+    storefs->reconfigurefunc = storeCossDirReconfigure;
+    storefs->donefunc = storeCossDirDone;
+    coss_membuf_pool = memPoolCreate("COSS Membuf data", sizeof(CossMemBuf));
+    coss_state_pool = memPoolCreate("COSS IO State data", sizeof(CossState));
+    coss_initialised = 1;
+}
+
diff --git a/src/fs/coss/store_io_coss.cc b/src/fs/coss/store_io_coss.cc
new file mode 100644 (file)
index 0000000..5aa2fb0
--- /dev/null
@@ -0,0 +1,518 @@
+
+/*
+ * $Id: store_io_coss.cc,v 1.1 2000/05/03 17:15:47 adrian Exp $
+ *
+ * DEBUG: section 81    Storage Manager COSS Interface
+ * AUTHOR: Eric Stern
+ *
+ * SQUID Internet Object Cache  http://squid.nlanr.net/Squid/
+ * ----------------------------------------------------------
+ *
+ *  Squid is the result of efforts by numerous individuals from the
+ *  Internet community.  Development is led by Duane Wessels of the
+ *  National Laboratory for Applied Network Research and funded by the
+ *  National Science Foundation.  Squid is Copyrighted (C) 1998 by
+ *  Duane Wessels and the University of California San Diego.  Please
+ *  see the COPYRIGHT file for full details.  Squid incorporates
+ *  software developed and/or copyrighted by other sources.  Please see
+ *  the CREDITS file for full details.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
+ *
+ */
+
+#include "squid.h"
+#include "store_coss.h"
+
+static DWCB storeCossWriteMemBufDone;
+static DRCB storeCossReadDone;
+static void storeCossIOCallback(storeIOState * sio, int errflag);
+static char *storeCossMemPointerFromDiskOffset(SwapDir *SD, size_t offset, CossMemBuf **mb);
+static void storeCossMemBufLock(SwapDir *SD, storeIOState *e);
+static void storeCossMemBufUnlock(SwapDir *SD, storeIOState *e);
+static void storeCossWriteMemBuf(SwapDir *SD,CossMemBuf *t);
+static void storeCossWriteMemBufDone(int fd, int errflag, size_t len, void *my_data);
+static CossMemBuf *storeCossCreateMemBuf(SwapDir *SD, size_t start,
+  sfileno curfn, int *collision);
+static void storeCossIOFreeEntry(void *, int);
+static void storeCossMembufFree(void *, int);
+
+/* === PUBLIC =========================================================== */
+
+/*
+ * This routine sucks. I want to rewrite it when possible, and I also think
+ * that we should check after creatmembuf() to see if the object has a
+ * RELEASE_REQUEST set on it (thanks Eric!) rather than this way which seems
+ * to work..
+ * -- Adrian
+ */
+off_t
+storeCossAllocate(SwapDir *SD, const StoreEntry *e, int which)
+{
+    CossInfo *cs = (CossInfo *)SD->fsdata;
+    CossMemBuf *newmb;
+    off_t retofs;
+    size_t allocsize;
+    int coll = 0;
+    sfileno checkf;
+
+     /* Make sure we chcek collisions if reallocating */
+    if (which == COSS_ALLOC_REALLOC)
+       checkf = e->swap_filen;
+    else
+       checkf = -1;
+
+    retofs = e->swap_filen; /* Just for defaults */
+
+    if (e->swap_file_sz > 0)
+        allocsize = e->swap_file_sz;
+    else
+        allocsize = objectLen(e) + e->mem_obj->swap_hdr_sz;
+
+    if (which != COSS_ALLOC_NOTIFY) {
+        if ((cs->current_offset + allocsize) > (SD->max_size << 10)) {
+            // tried to allocate past the end of the disk, so wrap
+            // back to the beginning
+            cs->current_membuf->flags.full = 1;
+            cs->current_membuf->diskend = cs->current_offset-1;
+           cs->current_offset = 0; // wrap back to beginning
+            newmb = storeCossCreateMemBuf(SD, 0, checkf, &coll);
+            cs->current_membuf = newmb;
+        } else if ((cs->current_offset + allocsize) > cs->current_membuf->diskend) {
+            cs->current_membuf->flags.full = 1;
+            cs->current_membuf->diskend = cs->current_offset-1;
+            newmb = storeCossCreateMemBuf(SD, cs->current_offset,
+              checkf, &coll);
+            cs->current_membuf = newmb;
+       }
+        if (coll == 0) {
+            retofs = cs->current_offset;
+        } else {
+            debug(81, 3) ("storeCossAllocate: Collision\n");
+        }
+    }
+    if (coll == 0) {
+        cs->current_offset += allocsize;
+        return retofs;
+    } else {
+        return -1;
+    }
+}
+
+void
+storeCossUnlink(SwapDir *SD, StoreEntry *e)
+{
+    debug(81, 3) ("storeCossUnlink: offset %d\n", e->swap_filen);
+    dlinkDelete(&e->repl.lru, &SD->repl.lru.list);
+}
+
+
+storeIOState *
+storeCossCreate(SwapDir *SD, StoreEntry *e, STFNCB *file_callback, STIOCB *callback, void *callback_data)
+{
+    CossState *cstate;
+    storeIOState *sio;
+
+    sio = memAllocate(MEM_STORE_IO);
+    cbdataAdd(sio, storeCossIOFreeEntry, MEM_STORE_IO);
+    cstate = memPoolAlloc(coss_state_pool);
+    sio->fsstate = cstate;
+    sio->offset = 0;
+    sio->mode = O_WRONLY;
+
+    /*
+     * this one is kinda strange - Eric called storeCossAllocate(), then
+     * storeCossOpen(O_RDONLY) .. weird. Anyway, I'm allocating this now.
+     */
+    sio->st_size = objectLen(e) + e->mem_obj->swap_hdr_sz;
+    sio->swap_dirn = SD->index;
+    sio->swap_filen = storeCossAllocate(SD, e, COSS_ALLOC_ALLOCATE);
+    debug(81, 3) ("storeCossCreate: offset %d, size %d, end %d\n", sio->swap_filen, sio->st_size, sio->swap_filen + sio->st_size);
+
+    sio->callback = callback;
+    sio->callback_data = callback_data;
+    cbdataLock(callback_data);
+    sio->e = (StoreEntry *)e;
+    sio->st_size = -1; // we won't know this until we read the metadata
+
+    cstate->flags.writing = 0;
+    cstate->flags.reading = 0;
+    cstate->readbuffer = NULL;
+    cstate->reqdiskoffset = -1;
+
+    /* Now add it into the LRU */
+    dlinkAdd(e, &e->repl.lru, &SD->repl.lru.list);
+
+    storeCossMemBufLock(SD, sio);
+    return sio;
+}
+
+storeIOState *
+storeCossOpen(SwapDir *SD, StoreEntry *e, STFNCB * file_callback,
+  STIOCB * callback, void *callback_data)
+{
+    storeIOState *sio;
+    char *p;
+    CossState *cstate;
+    sfileno f = e->swap_filen;
+    CossInfo *cs = (CossInfo *)SD->fsdata;
+
+    debug(81, 3) ("storeCossOpen: offset %d\n", f);
+
+    sio = memAllocate(MEM_STORE_IO);
+    cbdataAdd(sio, storeCossIOFreeEntry, MEM_STORE_IO);
+    cstate = memPoolAlloc(coss_state_pool);
+
+    sio->fsstate = cstate;
+    sio->swap_filen = f;
+    sio->swap_dirn = SD->index;
+    sio->offset = 0;
+    sio->mode = O_RDONLY;
+    sio->callback = callback;
+    sio->file_callback = file_callback;
+    sio->callback_data = callback_data;
+    cbdataLock(callback_data);
+    sio->st_size = e->swap_file_sz;
+    sio->e = e;
+
+    cstate->flags.writing = 0;
+    cstate->flags.reading = 0;
+    cstate->readbuffer = NULL;
+    cstate->reqdiskoffset = -1;
+    p = storeCossMemPointerFromDiskOffset(SD, f, NULL);
+    // make local copy so we don't have to lock membuf
+    if (p) {
+        cstate->readbuffer = xmalloc(sio->st_size);
+        memcpy(cstate->readbuffer, p, sio->st_size);
+    } else {
+        /* Do the allocation */
+       /* this is the first time we've been called on a new sio
+           read the whole object into memory, then return the 
+           requested amount
+         */
+        /*
+         * This bit of code actually does the LRU disk thing - we realloc
+         * a place for the object here, and the file_read() reads the object
+         * into the cossmembuf for later writing ..
+         */
+        cstate->reqdiskoffset = sio->swap_filen;
+        sio->swap_filen = -1;
+        sio->swap_filen = storeCossAllocate(SD, e, COSS_ALLOC_REALLOC);
+       if (sio->swap_filen == -1) {
+            /* We have to clean up neatly .. */
+           cbdataFree(sio);
+           cs->numcollisions++;
+           debug(81, 2) ("storeCossOpen: Reallocation of %d/%d failed\n", e->swap_dirn, e->swap_filen);
+            /* XXX XXX XXX Will squid call storeUnlink for this object? */
+           return NULL;
+       }
+        /* Notify the upper levels that we've changed file number */
+       sio->file_callback(sio->callback_data, 0, sio);
+        
+        /*
+         * lock the buffer so it doesn't get swapped out on us
+         * this will get unlocked in storeCossReadDone
+         */
+        storeCossMemBufLock(SD, sio);
+
+        /*
+         * Do the LRU magic to keep the disk and memory LRUs identical
+         */
+        dlinkDelete(&sio->e->repl.lru, &SD->repl.lru.list);
+        dlinkAdd(sio->e, &sio->e->repl.lru, &SD->repl.lru.list);
+
+        /*
+         * Since we've reallocated a spot for this object, we need to
+         * write it to the cossmembuf *and* return it in the read ..
+         */
+        cstate->readbuffer = NULL;
+    }
+    return sio;
+}
+
+void
+storeCossClose(SwapDir *SD, storeIOState * sio)
+{
+    debug(81, 3) ("storeCossClose: offset %d\n",sio->swap_filen);
+    if (sio->mode == O_WRONLY)
+        storeCossMemBufUnlock(SD, sio);
+    storeCossIOCallback(sio, 0);
+}
+
+void
+storeCossRead(SwapDir *SD, storeIOState * sio, char *buf, size_t size, off_t offset, STRCB * callback, void *callback_data)
+{
+    char *p;
+    CossState *cstate = (CossState *)sio->fsstate;
+    CossInfo *cs = (CossInfo *)SD->fsdata;
+
+    assert(sio->read.callback == NULL);
+    assert(sio->read.callback_data == NULL);
+    sio->read.callback = callback;
+    sio->read.callback_data = callback_data;
+    cbdataLock(callback_data);
+    debug(81, 3) ("storeCossRead: offset %d\n", offset);
+    sio->offset = offset;
+    cstate->flags.reading = 1;
+    if ((offset + size) > sio->st_size)
+        size = sio->st_size - offset;
+    cstate->requestlen = size;
+    cstate->requestbuf = buf;
+    cstate->requestoffset = offset;
+    if (cstate->readbuffer == NULL) {
+        p = storeCossMemPointerFromDiskOffset(SD, sio->swap_filen, NULL);
+        file_read(cs->fd,
+           p,
+           sio->st_size,
+           cstate->reqdiskoffset,
+           storeCossReadDone,
+           sio);
+        cstate->reqdiskoffset = 0; /* XXX */
+    } else {
+        storeCossReadDone(cs->fd,
+            cstate->readbuffer,
+            sio->st_size,
+            0,
+            sio);
+    }            
+}
+
+void
+storeCossWrite(SwapDir *SD, storeIOState * sio, char *buf, size_t size, off_t offset, FREE * free_func)
+{
+    char *dest;
+    CossMemBuf *membuf;
+    off_t diskoffset;
+    
+    debug(81, 3) ("storeCossWrite: offset %d, len %d\n",sio->offset, size);
+    diskoffset = sio->swap_filen + sio->offset;
+    dest = storeCossMemPointerFromDiskOffset(SD, diskoffset, &membuf);
+    assert(dest != NULL);
+    memcpy(dest, buf, size);
+    sio->offset += size;
+    if (free_func)
+        (free_func)(buf);
+}
+
+
+/*  === STATIC =========================================================== */
+
+static void
+storeCossReadDone(int fd, const char *buf, int len, int errflag, void *my_data)
+{
+    storeIOState *sio = my_data;
+    char *p;
+    STRCB *callback = sio->read.callback;
+    void *their_data = sio->read.callback_data;
+    SwapDir *SD = INDEXSD(sio->swap_dirn);
+    CossState *cstate = (CossState *)sio->fsstate;
+    size_t rlen;
+
+    debug(81, 3) ("storeCossReadDone: fileno %d, FD %d, len %d\n",
+       sio->swap_filen, fd, len);
+    cstate->flags.reading = 0;
+    if (errflag) {
+       debug(81, 3) ("storeCossReadDone: got failure (%d)\n", errflag);
+        rlen = -1;
+    } else {
+        if (cstate->readbuffer == NULL) {
+            cstate->readbuffer = xmalloc(sio->st_size);
+            p = storeCossMemPointerFromDiskOffset(SD, sio->swap_filen, NULL);
+            memcpy(cstate->readbuffer, p, sio->st_size);
+            storeCossMemBufUnlock(SD, sio);
+        }
+        sio->offset += len;
+        memcpy(cstate->requestbuf, &cstate->readbuffer[cstate->requestoffset],
+          cstate->requestlen);
+        rlen = (size_t) cstate->requestlen;
+    }
+    assert(callback);
+    assert(their_data);
+    sio->read.callback = NULL;
+    sio->read.callback_data = NULL;
+    if (cbdataValid(their_data))
+       callback(their_data, cstate->requestbuf, rlen);
+    cbdataUnlock(their_data);
+}
+
+static void
+storeCossIOCallback(storeIOState * sio, int errflag)
+{
+    CossState *cstate = (CossState *)sio->fsstate;
+    debug(81, 3) ("storeCossIOCallback: errflag=%d\n", errflag);
+    xfree(cstate->readbuffer);
+    if (cbdataValid(sio->callback_data))
+       sio->callback(sio->callback_data, errflag, sio);
+    sio->callback_data = NULL;
+    cbdataFree(sio);
+}
+
+static char *
+storeCossMemPointerFromDiskOffset(SwapDir *SD, size_t offset, CossMemBuf **mb)
+{
+    CossMemBuf *t;
+    CossInfo *cs = (CossInfo *)SD->fsdata;
+
+    for (t=cs->membufs; t; t = t->next)
+        if ((offset >= t->diskstart) && (offset <= t->diskend)) {
+            if (mb)
+                *mb = t;
+            return &t->buffer[offset - t->diskstart];
+        }
+    if (mb)
+        *mb = NULL;
+    return NULL;
+}
+
+static void
+storeCossMemBufLock(SwapDir *SD, storeIOState *e)
+{
+    CossMemBuf *t;
+    CossInfo *cs = (CossInfo *)SD->fsdata;
+
+    for (t = cs->membufs; t; t = t->next)
+        if ((e->swap_filen >= t->diskstart) && (e->swap_filen <= t->diskend)) {
+            debug(81, 3) ("storeCossMemBufLock: locking %08X, lockcount %d\n",t, t->lockcount);
+            t->lockcount++;
+            return;
+        }
+    debug(81, 3) ("storeCossMemBufLock: FAILED to lock %08X\n",e);
+}
+
+static void
+storeCossMemBufUnlock(SwapDir *SD, storeIOState *e)
+{
+    CossMemBuf *t;
+    CossInfo *cs = (CossInfo *)SD->fsdata;
+
+    for (t = cs->membufs; t; t = t->next) {
+        if ((e->swap_filen >= t->diskstart) && (e->swap_filen <= t->diskend)) {
+            t->lockcount--;
+            debug(81, 3) ("storeCossMemBufUnlock: unlocking %08X, lockcount %d\n",t, t->lockcount);
+        }
+        if (t->flags.full && !t->flags.writing && !t->lockcount) 
+            storeCossWriteMemBuf(SD, t);
+    }
+}
+
+
+static void
+storeCossWriteMemBuf(SwapDir *SD, CossMemBuf *t)
+{
+    CossInfo *cs = (CossInfo *)SD->fsdata;
+    debug(81, 3) ("storeCossWriteMemBuf: offset %d, len %d\n",
+       t->diskstart, t->diskend - t->diskstart);
+    cbdataAdd(t, storeCossMembufFree, 0);
+    file_write(cs->fd, t->diskstart, &t->buffer,
+      t->diskend - t->diskstart, storeCossWriteMemBufDone, t, NULL);
+    t->flags.writing = 1;
+}
+
+
+static void
+storeCossWriteMemBufDone(int fd, int errflag, size_t len, void *my_data)
+{
+    CossMemBuf *t = my_data;
+    CossMemBuf *p,*prev;
+    CossInfo *cs = (CossInfo *)t->SD->fsdata;
+
+    debug(81, 3) ("storeCossWriteMemBufDone: len %d\n",len);
+    if (errflag) {
+       debug(81, 0) ("storeCossMemBufWriteDone: got failure (%d)\n", errflag);
+        cbdataFree(t);
+       return;
+    }
+
+    if (t == cs->membufs) {
+        cs->membufs = t->next;
+        cbdataFree(t);
+        return;
+    }
+    prev = t;
+    for (p = cs->membufs; p; p = p->next) {
+        if (t == p) {
+            prev->next = t->next;
+            cbdataFree(t);
+            return;
+        }
+        prev = p;
+    }    
+    cbdataFree(t);
+}
+
+static CossMemBuf *storeCossCreateMemBuf(SwapDir *SD, size_t start,
+  sfileno curfn, int *collision)
+{
+    CossMemBuf *newmb,*t;
+    StoreEntry *e;
+    dlink_node *m,*prev;
+    int numreleased=0;
+    CossInfo *cs = (CossInfo *)SD->fsdata;
+
+    newmb = memPoolAlloc(coss_membuf_pool);
+    newmb->diskstart = start;
+    debug(81, 3) ("storeCossCreateMemBuf: creating new membuf at %d\n",newmb->diskstart);
+    newmb->diskend = newmb->diskstart + COSS_MEMBUF_SZ - 1;
+    newmb->flags.full = 0;
+    newmb->flags.writing = 0;
+    newmb->lockcount = 0;
+    newmb->SD = SD;
+    newmb->next = cs->membufs;
+    cs->membufs = newmb;
+    for (t = cs->membufs; t; t = t->next) 
+        debug(81, 3) ("storeCossCreateMemBuf: membuflist %d lockcount %d\n",t->diskstart,t->lockcount);
+
+    /*
+     * XXX more evil LRU specific code. This needs to be replaced.
+     */
+    for (m = SD->repl.lru.list.tail; m; m = prev) {
+        prev = m->prev;
+        e = m->data;
+        if (curfn == e->swap_filen)
+            *collision = 1; /* Mark an object alloc collision */
+        if ((e->swap_filen >= newmb->diskstart) &&
+          (e->swap_filen <= newmb->diskend)) {
+            storeRelease(e);
+            numreleased++;         
+        } else
+            break;    
+    }
+    if (numreleased > 0)
+       debug(81, 3) ("storeCossCreateMemBuf: this allocation released %d storeEntries\n",numreleased);
+    return newmb; 
+}
+
+/*
+ * We can't pass memFree() as a free function here, because we need to free
+ * the fsstate variable ..
+ */
+static void
+storeCossIOFreeEntry(void *sio, int foo)
+{
+        memPoolFree(coss_state_pool, ((storeIOState *)sio)->fsstate);
+        memFree(sio, MEM_STORE_IO);
+}
+
+/*
+ * We can't pass memFree() as a free function here, since we have to pass it
+ * an int to memFree(), and we aren't using static memory pool allocation here.
+ * So we have this hack here ..
+ */
+static void
+storeCossMembufFree(void *mb, int foo)
+{
+    memPoolFree(coss_membuf_pool, mb);
+}
+
diff --git a/src/fs/diskd/Makefile.in b/src/fs/diskd/Makefile.in
new file mode 100644 (file)
index 0000000..59358f6
--- /dev/null
@@ -0,0 +1,93 @@
+#
+#  Makefile for the DISKD storage driver for the Squid Object Cache server
+#
+#  $Id: Makefile.in,v 1.1 2000/05/03 17:15:47 adrian Exp $
+#
+
+FS             = diskd
+
+prefix         = @prefix@
+exec_prefix    = @exec_prefix@
+exec_suffix    = @exec_suffix@
+cgi_suffix     = @cgi_suffix@
+top_srcdir     = @top_srcdir@
+bindir         = @bindir@
+libexecdir      = @libexecdir@
+sysconfdir     = @sysconfdir@
+localstatedir   = @localstatedir@
+srcdir         = @srcdir@
+VPATH          = @srcdir@
+
+CC             = @CC@
+MAKEDEPEND     = @MAKEDEPEND@
+AR_R           = @AR_R@
+RANLIB         = @RANLIB@
+AC_CFLAGS      = @CFLAGS@
+SHELL          = /bin/sh
+LDFLAGS                = @LDFLAGS@
+INSTALL         = @INSTALL@
+INSTALL_BIN     = @INSTALL_PROGRAM@
+MV             = @MV@
+RM             = @RM@
+
+INCLUDE                = -I../../../include -I$(top_srcdir)/include -I$(top_srcdir)/src/
+CFLAGS                 = $(AC_CFLAGS) $(INCLUDE) $(DEFINES)
+
+OUT            = ../$(FS).a
+DISKD_EXE      = diskd$(exec_suffix)
+
+
+OBJS           = \
+               store_dir_diskd.o \
+               store_io_diskd.o
+
+UTILS          = \
+               $(DISKD_EXE)
+
+all: $(OUT) $(UTILS)
+
+$(OUT): $(OBJS)
+       @rm -f ../stamp
+       $(AR_R) $(OUT) $(OBJS)
+       $(RANLIB) $(OUT)
+
+$(OBJS): $(top_srcdir)/include/version.h ../../../include/autoconf.h
+
+.c.o:
+       @rm -f ../stamp
+       $(CC) -DSQUID_PREFIX=\"$(prefix)\" $(CFLAGS) -c $<
+
+clean: 
+       -rm -rf *.o *pure_* core ../$(FS).a diskd
+
+distclean:     clean
+       -rm -f Makefile
+       -rm -f Makefile.bak
+       -rm -f tags
+
+tags:
+       ctags *.[ch] $(top_srcdir)/src/*.[ch] $(top_srcdir)/include/*.h $(top_srcdir)/lib/*.[ch]
+
+depend:
+       $(MAKEDEPEND) $(INCLUDE) -fMakefile *.c
+
+diskd.o: $(srcdir)/diskd.c
+       $(CC) $(CFLAGS) $(srcdir)/diskd.c -c -o diskd.o
+
+$(DISKD_EXE): diskd.o
+       $(CC) $(LDFLAGS) -L../../../lib/ $(CFLAGS) diskd.o -o $(DISKD_EXE) -lmiscutil -lm
+
+install: $(UTILS)
+       @for f in $(UTILS); do \
+               if test -f $(libexecdir)/$$f; then \
+                       echo $(MV) $(libexecdir)/$$f $(libexecdir)/-$$f; \
+                       $(MV) $(libexecdir)/$$f $(libexecdir)/-$$f; \
+               fi; \
+               echo $(INSTALL_BIN) $$f $(libexecdir); \
+               $(INSTALL_BIN) $$f $(libexecdir); \
+               if test -f $(libexecdir)/-$$f; then \
+                       echo $(RM) -f $(libexecdir)/-$$f; \
+                       $(RM) -f $(libexecdir)/-$$f; \
+               fi; \
+       done
+
diff --git a/src/fs/diskd/diskd.cc b/src/fs/diskd/diskd.cc
new file mode 100644 (file)
index 0000000..478337a
--- /dev/null
@@ -0,0 +1,310 @@
+
+#include "config.h"
+#include "squid.h"
+
+
+#include "store_diskd.h"
+
+#include <sys/ipc.h>
+#include <sys/msg.h>
+#include <sys/shm.h>
+
+#undef assert
+#include <assert.h>
+
+
+#define STDERR_DEBUG 0
+
+typedef struct _file_state file_state;
+
+struct _file_state {
+    void *key;
+    file_state *next;
+    int id;
+    int fd;
+    off_t offset;
+};
+
+static hash_table *hash = NULL;
+static pid_t mypid;
+static char *shmbuf;
+
+static int
+do_open(diomsg * r, int len, const char *buf)
+{
+    int fd;
+    file_state *fs;
+    /*
+     * note r->offset holds open() flags
+     */
+    fd = open(buf, r->offset, 0600);
+    if (fd < 0) {
+       fprintf(stderr, "%d %s: ", (int) mypid, buf);
+       perror("open");
+       return -errno;
+    }
+    fs = xcalloc(1, sizeof(*fs));
+    fs->id = r->id;
+    fs->key = &fs->id;         /* gack */
+    fs->fd = fd;
+    hash_join(hash, (hash_link *) fs);
+#if STDERR_DEBUG
+    fprintf(stderr, "%d OPEN  id %d, FD %d, fs %p\n",
+       (int) mypid,
+       fs->id,
+       fs->fd,
+       fs);
+#endif
+    return fd;
+}
+
+static int
+do_close(diomsg * r, int len)
+{
+    int fd;
+    file_state *fs;
+    fs = (file_state *) hash_lookup(hash, &r->id);
+    if (NULL == fs) {
+       errno = EBADF;
+       fprintf(stderr, "%d CLOSE id %d: ", (int) mypid, r->id);
+       perror("do_close");
+       return -EBADF;
+    }
+    fd = fs->fd;
+    hash_remove_link(hash, (hash_link *) fs);
+#if STDERR_DEBUG
+    fprintf(stderr, "%d CLOSE id %d, FD %d, fs %p\n",
+       (int) mypid,
+       r->id,
+       fs->fd,
+       fs);
+#endif
+    xfree(fs);
+    return close(fd);
+}
+
+static int
+do_read(diomsg * r, int len, char *buf)
+{
+    int x;
+    int readlen = r->size;
+    file_state *fs;
+    fs = (file_state *) hash_lookup(hash, &r->id);
+    if (NULL == fs) {
+       errno = EBADF;
+       fprintf(stderr, "%d READ  id %d: ", (int) mypid, r->id);
+       perror("do_read");
+       return -EBADF;
+    }
+    if (r->offset > -1 && r->offset != fs->offset) {
+#if STDERR_DEBUG
+       fprintf(stderr, "seeking to %d\n", r->offset);
+#endif
+       if (lseek(fs->fd, r->offset, SEEK_SET) < 0) {
+           fprintf(stderr, "%d FD %d, offset %d: ", (int) mypid, fs->fd, r->offset);
+           perror("lseek");
+       }
+    }
+    x = read(fs->fd, buf, readlen);
+#if STDERR_DEBUG
+    fprintf(stderr, "%d READ %d,%d,%d ret %d\n", (int) mypid,
+       fs->fd, readlen, r->offset, x);
+#endif
+    if (x < 0) {
+       fprintf(stderr, "%d FD %d: ", (int) mypid, fs->fd);
+       perror("read");
+       return -errno;
+    }
+    fs->offset = r->offset + x;
+    return x;
+}
+
+static int
+do_write(diomsg * r, int len, const char *buf)
+{
+    int wrtlen = r->size;
+    int x;
+    file_state *fs;
+    fs = (file_state *) hash_lookup(hash, &r->id);
+    if (NULL == fs) {
+       errno = EBADF;
+       fprintf(stderr, "%d WRITE id %d: ", (int) mypid, r->id);
+       perror("do_write");
+       return -EBADF;
+    }
+    if (r->offset > -1 && r->offset != fs->offset) {
+       if (lseek(fs->fd, r->offset, SEEK_SET) < 0) {
+           fprintf(stderr, "%d FD %d, offset %d: ", (int) mypid, fs->fd, r->offset);
+           perror("lseek");
+       }
+    }
+#if STDERR_DEBUG
+    fprintf(stderr, "%d WRITE %d,%d,%d\n", (int) mypid,
+       fs->fd, wrtlen, r->offset);
+#endif
+    x = write(fs->fd, buf, wrtlen);
+    if (x < 0) {
+       fprintf(stderr, "%d FD %d: ", (int) mypid, fs->fd);
+       perror("write");
+       return -errno;
+    }
+    fs->offset = r->offset + x;
+    return x;
+}
+
+static int
+do_unlink(diomsg * r, int len, const char *buf)
+{
+    if (truncate(buf, 0) < 0) {
+       fprintf(stderr, "%d UNLNK id %d %s: ", (int) mypid, r->id, buf);
+       perror("truncate");
+       return -errno;
+    }
+#if STDERR_DEBUG
+    fprintf(stderr, "%d UNLNK %s\n", (int) mypid, buf);
+#endif
+    return 0;
+}
+
+static void
+msg_handle(diomsg * r, int rl, diomsg * s)
+{
+    char *buf = NULL;
+    s->mtype = r->mtype;
+    s->callback_data = r->callback_data;
+    s->shm_offset = r->shm_offset;
+    s->id = r->id;
+    if (s->shm_offset > -1)
+       buf = shmbuf + s->shm_offset;
+    switch (r->mtype) {
+    case _MQD_OPEN:
+       s->status = do_open(r, rl, buf);
+       break;
+    case _MQD_CLOSE:
+       s->status = do_close(r, rl);
+       break;
+    case _MQD_READ:
+       s->status = do_read(r, rl, buf);
+       break;
+    case _MQD_WRITE:
+       s->status = do_write(r, rl, buf);
+       break;
+    case _MQD_UNLINK:
+       s->status = do_unlink(r, rl, buf);
+       break;
+    default:
+       assert(0);
+       break;
+    }
+}
+
+int
+fsCmp(const void *a, const void *b)
+{
+    const int *A = a;
+    const int *B = b;
+    return *A != *B;
+}
+
+unsigned int
+fsHash(const void *key, unsigned int n)
+{
+    /* note, n must be a power of 2! */
+    const int *k = key;
+    return (*k & (--n));
+}
+
+static void
+alarm_handler(int sig)
+{
+    (void) 0;
+}
+
+int
+main(int argc, char *argv[])
+{
+    int key;
+    int rmsgid;
+    int smsgid;
+    int shmid;
+    diomsg rmsg;
+    diomsg smsg;
+    int rlen;
+    char rbuf[512];
+    struct sigaction sa;
+    setbuf(stdout, NULL);
+    setbuf(stderr, NULL);
+    mypid = getpid();
+    assert(4 == argc);
+    key = atoi(argv[1]);
+    rmsgid = msgget(key, 0600);
+    if (rmsgid < 0) {
+       perror("msgget");
+       return 1;
+    }
+    key = atoi(argv[2]);
+    smsgid = msgget(key, 0600);
+    if (smsgid < 0) {
+       perror("msgget");
+       return 1;
+    }
+    key = atoi(argv[3]);
+    shmid = shmget(key, 0, 0600);
+    if (shmid < 0) {
+       perror("shmget");
+       return 1;
+    }
+    shmbuf = shmat(shmid, NULL, 0);
+    if (shmbuf == (void *) -1) {
+       perror("shmat");
+       return 1;
+    }
+    hash = hash_create(fsCmp, 1 << 4, fsHash);
+    assert(hash);
+    fcntl(0, F_SETFL, SQUID_NONBLOCK);
+    memset(&sa, '\0', sizeof(sa));
+    sa.sa_handler = alarm_handler;
+    sa.sa_flags = SA_RESTART;
+    sigaction(SIGALRM, &sa, NULL);
+    for (;;) {
+       alarm(1);
+       memset(&rmsg, '\0', sizeof(rmsg));
+       rlen = msgrcv(rmsgid, &rmsg, msg_snd_rcv_sz, 0, 0);
+       if (rlen < 0) {
+           if (EINTR == errno) {
+               if (read(0, rbuf, 512) <= 0) {
+                   if (EWOULDBLOCK == errno)
+                       (void) 0;
+                   else if (EAGAIN == errno)
+                       (void) 0;
+                   else
+                       break;
+               }
+           }
+           if (EAGAIN == errno) {
+               continue;
+           }
+           perror("msgrcv");
+           break;
+       }
+       alarm(0);
+       msg_handle(&rmsg, rlen, &smsg);
+       if (msgsnd(smsgid, &smsg, msg_snd_rcv_sz, 0) < 0) {
+           perror("msgsnd");
+           break;
+       }
+    }
+#if STDERR_DEBUG
+    fprintf(stderr, "%d diskd exiting\n", (int) mypid);
+#endif
+    if (msgctl(rmsgid, IPC_RMID, 0) < 0)
+       perror("msgctl IPC_RMID");
+    if (msgctl(smsgid, IPC_RMID, 0) < 0)
+       perror("msgctl IPC_RMID");
+    if (shmdt(shmbuf) < 0)
+       perror("shmdt");
+    if (shmctl(shmid, IPC_RMID, 0) < 0)
+       perror("shmctl IPC_RMID");
+    return 0;
+}
+
diff --git a/src/fs/diskd/store_dir_diskd.cc b/src/fs/diskd/store_dir_diskd.cc
new file mode 100644 (file)
index 0000000..5fbb7d1
--- /dev/null
@@ -0,0 +1,2125 @@
+
+/*
+ * $Id: store_dir_diskd.cc,v 1.1 2000/05/03 17:15:47 adrian Exp $
+ *
+ * DEBUG: section 47    Store Directory Routines
+ * AUTHOR: Duane Wessels
+ *
+ * SQUID Internet Object Cache  http://squid.nlanr.net/Squid/
+ * ----------------------------------------------------------
+ *
+ *  Squid is the result of efforts by numerous individuals from the
+ *  Internet community.  Development is led by Duane Wessels of the
+ *  National Laboratory for Applied Network Research and funded by the
+ *  National Science Foundation.  Squid is Copyrighted (C) 1998 by
+ *  Duane Wessels and the University of California San Diego.  Please
+ *  see the COPYRIGHT file for full details.  Squid incorporates
+ *  software developed and/or copyrighted by other sources.  Please see
+ *  the CREDITS file for full details.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *  
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *  
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
+ *
+ */
+
+#include "squid.h"
+#if HAVE_STATVFS
+#if HAVE_SYS_STATVFS_H
+#include <sys/statvfs.h>
+#endif
+#endif
+
+#include <sys/ipc.h>
+#include <sys/msg.h>
+#include <sys/shm.h>
+
+#include "store_diskd.h"
+
+#define DefaultLevelOneDirs     16
+#define DefaultLevelTwoDirs     256
+#define STORE_META_BDISKDZ 4096
+
+#ifndef SQUID_PREFIX
+#error "SQUID_PREFIX needs defining!"
+#endif
+
+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 *, int *sfileno, int *size);
+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_num32 refcount,
+    u_short 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 STLOGCLEANOPEN storeDiskdDirWriteCleanOpen;
+static void storeDiskdDirWriteCleanClose(SwapDir * sd);
+static STLOGCLEANWRITE storeDiskdDirWriteCleanEntry;
+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);
+static int storeDiskdDirCheckExpired(SwapDir *, StoreEntry *);
+#if !HEAP_REPLACEMENT
+static time_t storeDiskdDirExpiredReferenceAge(SwapDir *);
+#endif
+static void storeDiskdStats(StoreEntry * sentry);
+static void storeDiskdDirSync(SwapDir *);
+static void storeDiskdDirCallback(SwapDir *);
+
+
+/*
+ * 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!
+ */
+
+int
+storeDiskdDirMapBitTest(SwapDir *SD, int fn)
+{
+    sfileno filn = fn;
+    diskdinfo_t *diskdinfo;
+    diskdinfo = (diskdinfo_t *)SD->fsdata;
+    return file_map_bit_test(diskdinfo->map, filn);
+}
+void
+storeDiskdDirMapBitSet(SwapDir *SD, int fn)
+{  
+    sfileno filn = fn;
+    diskdinfo_t *diskdinfo;
+    diskdinfo = (diskdinfo_t *)SD->fsdata;
+    file_map_bit_set(diskdinfo->map, filn);
+}
+void
+storeDiskdDirMapBitReset(SwapDir *SD, int fn)
+{ 
+    sfileno filn = fn;
+    diskdinfo_t *diskdinfo;
+    diskdinfo = (diskdinfo_t *)SD->fsdata;
+    file_map_bit_reset(diskdinfo->map, filn);
+}
+
+int
+storeDiskdDirMapBitAllocate(SwapDir *SD)
+{
+    diskdinfo_t *diskdinfo = (diskdinfo_t *)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 = (diskdinfo_t *)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 = (diskdinfo_t *)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(20, should_exist ? 3 : 1) ("%s exists\n", path);
+       } else {
+           fatalf("Swap directory %s is not a directory.", path);
+       }
+    } else if (0 == mkdir(path, 0755)) {
+       debug(20, should_exist ? 1 : 3) ("%s created\n", path);
+       created = 1;
+    } else {
+       fatalf("Failed to make swap directory %s: %s",
+           path, xstrerror());
+    }
+    return created;
+}
+
+static int
+storeDiskdDirVerifyDirectory(const char *path)
+{
+    struct stat sb;
+    if (stat(path, &sb) < 0) {
+       debug(20, 0) ("%s: %s\n", path, xstrerror());
+       return -1;
+    }
+    if (S_ISDIR(sb.st_mode) == 0) {
+       debug(20, 0) ("%s is not a directory\n", path);
+       return -1;
+    }
+    return 0;
+}
+
+/*
+ * This function is called by 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 = (diskdinfo_t *)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 = (diskdinfo_t *)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);
+        while (index(pathtmp,'/'))
+            *index(pathtmp,'/')='.';
+        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 = (diskdinfo_t *)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 = (diskdinfo_t *)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;
+    int ikey = (getpid() << 16) + (sd->index << 4);
+    char *args[5];
+    char skey1[32];
+    char skey2[32];
+    char skey3[32];
+    diskdinfo_t *diskdinfo = (diskdinfo_t *) 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.";
+
+    diskdinfo->smsgid = msgget((key_t) ikey, 0700 | IPC_CREAT);
+    if (diskdinfo->smsgid < 0) {
+        debug(50, 0) ("storeDiskdInit: msgget: %s\n", xstrerror());
+        fatal("msgget failed");
+    }
+    diskdinfo->rmsgid = msgget((key_t) (ikey + 1), 0700 | IPC_CREAT);
+    if (diskdinfo->rmsgid < 0) {
+        debug(50, 0) ("storeDiskdInit: msgget: %s\n", xstrerror());
+        fatal("msgget failed");
+    }
+    diskdinfo->shm.id = shmget((key_t) (ikey + 2),
+        SHMBUFS * SHMBUF_BLKSZ, 0600 | IPC_CREAT);
+    if (diskdinfo->shm.id < 0) {
+        debug(50, 0) ("storeDiskdInit: shmget: %s\n", xstrerror());
+        fatal("shmget failed");
+    }
+    diskdinfo->shm.buf = shmat(diskdinfo->shm.id, NULL, 0);
+    if (diskdinfo->shm.buf == (void *) -1) {
+        debug(50, 0) ("storeDiskdInit: shmat: %s\n", xstrerror());
+        fatal("shmat failed");
+    }
+    diskd_stats.shmbuf_count += SHMBUFS;
+    for (i = 0; i < SHMBUFS; i++)
+        storeDiskdShmPut(sd, i * SHMBUF_BLKSZ);
+    snprintf(skey1, 32, "%d", ikey);
+    snprintf(skey2, 32, "%d", ikey + 1);
+    snprintf(skey3, 32, "%d", ikey + 2);
+    args[0] = "diskd";
+    args[1] = skey1;
+    args[2] = skey2;
+    args[3] = skey3;
+    args[4] = NULL;
+#if HAVE_POLL && defined(_SQUID_OSF_)
+    /* pipes and poll() don't get along on DUNIX -DW */
+    x = ipcCreate(IPC_TCP_SOCKET,
+#else
+    x = ipcCreate(IPC_FIFO,
+#endif
+        SQUID_PREFIX "/bin/diskd",
+        args,
+        "diskd",
+        &rfd,
+        &diskdinfo->wfd);
+    if (x < 0)
+        fatal("execl " SQUID_PREFIX "/bin/diskd failed");
+    if (rfd != diskdinfo->wfd)
+        comm_close(rfd);
+    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;
+    }
+}
+
+
+static void
+storeDiskdStats(StoreEntry * sentry)
+{
+    storeAppendPrintf(sentry, "sent_count: %d\n", diskd_stats.sent_count);
+    storeAppendPrintf(sentry, "recv_count: %d\n", diskd_stats.recv_count);
+    storeAppendPrintf(sentry, "max_away: %d\n", diskd_stats.max_away);
+    storeAppendPrintf(sentry, "max_shmuse: %d\n", diskd_stats.max_shmuse);
+    storeAppendPrintf(sentry, "open_fail_queue_len: %d\n", diskd_stats.open_fail_queue_len);
+    storeAppendPrintf(sentry, "block_queue_len: %d\n", diskd_stats.block_queue_len);
+    diskd_stats.max_away = diskd_stats.max_shmuse = 0;
+}
+
+/*
+ * storeDiskdDirSync
+ *
+ * Sync any pending data. We just sit around and read the queue
+ * until the data has finished writing.
+ */
+static void
+storeDiskdDirSync(SwapDir *SD)
+{
+    /* XXX NOT DONE YET! */
+#warning "storeDiskdSync() needs to be written"
+}
+
+
+/*
+ * storeDiskdDirCallback
+ *
+ * Handle callbacks. If we have more than magic2 requests away, we block
+ * until the queue is below magic2. Otherwise, we simply return when we
+ * don't get a message.
+ */
+static void
+storeDiskdDirCallback(SwapDir *SD)
+{
+    diomsg M;
+    int x;
+    diskdinfo_t *diskdinfo = (diskdinfo_t *)SD->fsdata;
+
+    if (diskdinfo->away >= diskdinfo->magic2)
+       diskd_stats.block_queue_len++;
+
+    if (diskd_stats.sent_count - diskd_stats.recv_count >
+      diskd_stats.max_away) {
+        diskd_stats.max_away = diskd_stats.sent_count - diskd_stats.recv_count;
+        diskd_stats.max_shmuse = diskd_stats.shmbuf_count;
+    }
+
+    /* if we are above magic2, we do not break under any reason */
+    while (1) {
+        memset(&M, '\0', sizeof(M));
+        x = msgrcv(diskdinfo->rmsgid, &M, msg_snd_rcv_sz, 0, IPC_NOWAIT);
+       if (x < 0) {
+           if (diskdinfo->away >= diskdinfo->magic2)
+               continue;
+            else
+                break;
+       } else if (x != msg_snd_rcv_sz) {
+            debug(81, 1) ("storeDiskdReadIndividualQueue: msgget returns %d\n",
+             x);
+            break;
+       }
+       diskd_stats.recv_count++;
+       diskdinfo->away--;
+       storeDiskdHandle(&M);
+       if (M.shm_offset > -1)
+           storeDiskdShmPut(SD, M.shm_offset);
+    }
+}
+
+
+
+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];
+    int sfileno = 0;
+    int count;
+    int size;
+    struct stat sb;
+    int swap_hdr_len;
+    int fd = -1;
+    tlv *tlv_list;
+    tlv *t;
+    assert(rb != NULL);
+    debug(20, 3) ("storeDiskdDirRebuildFromDirectory: DIR #%d\n", rb->sd->index);
+    for (count = 0; count < rb->speed; count++) {
+       assert(fd == -1);
+       fd = storeDiskdDirGetNextFile(rb, &sfileno, &size);
+       if (fd == -2) {
+           debug(20, 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(20, 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(20, 3) ("  %s %7d files opened so far.\n",
+               rb->sd->path, rb->counts.scancount);
+       debug(20, 9) ("file_in: fd=%d %08X\n", fd, sfileno);
+       Counter.syscalls.disk.reads++;
+       if (read(fd, hdr_buf, SM_PAGE_SIZE) < 0) {
+           debug(20, 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(20, 1) ("storeDiskdDirRebuildFromDirectory: failed to get meta data\n");
+            /* XXX shouldn't this be a call to storeDiskdUnlink ? */
+           storeDiskdDirUnlinkFile(SD, sfileno);
+           continue;
+       }
+       debug(20, 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(20, 1) ("storeDiskdDirRebuildFromDirectory: NULL key\n");
+           storeDiskdDirUnlinkFile(SD, sfileno);
+           continue;
+       }
+       tmpe.key = key;
+       /* check sizes */
+       if (tmpe.swap_file_sz == 0) {
+           tmpe.swap_file_sz = sb.st_size;
+       } else if (tmpe.swap_file_sz == sb.st_size - swap_hdr_len) {
+           tmpe.swap_file_sz = sb.st_size;
+       } else if (tmpe.swap_file_sz != sb.st_size) {
+           debug(20, 1) ("storeDiskdDirRebuildFromDirectory: SIZE MISMATCH %d!=%d\n",
+               tmpe.swap_file_sz, (int) sb.st_size);
+           storeDiskdDirUnlinkFile(SD, sfileno);
+           continue;
+       }
+       if (EBIT_TEST(tmpe.flags, KEY_PRIVATE)) {
+           storeDiskdDirUnlinkFile(SD, sfileno);
+           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,
+           sfileno,
+           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(20, 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;
+       debug(20, 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) {
+           (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);
+                storeDiskdDirReplRemove(e);
+               if (e->swap_filen > -1) {
+                   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(20, 1) ("WARNING: %d invalid swap log entries found\n",
+                   rb->counts.bad_log_op);
+           rb->counts.invalid++;
+           continue;
+       }
+       if ((++rb->counts.scancount & 0xFFFF) == 0)
+           debug(20, 3) ("  %7d %s Entries read so far.\n",
+               rb->counts.scancount, rb->sd->path);
+       if (!storeDiskdDirValidFileno(SD, s.swap_filen)) {
+           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;
+#if HEAP_REPLACEMENT
+               storeHeapPositionUpdate(e, SD);
+                storeDiskdDirUnrefObj(SD, e);
+#endif
+           } else {
+               debug_trap("storeDiskdDirRebuildFromSwapLog: bad condition");
+               debug(20, 1) ("\tSee %s:%d\n", __FILE__, __LINE__);
+           }
+           continue;
+       } else if (used) {
+           /* swapfile in use, not by this URL, log entry is newer */
+           /* This is sorta bad: the log entry should NOT be newer at this
+            * point.  If the log is dirty, the filesize check should have
+            * caught this.  If the log is clean, there should never be a
+            * newer entry. */
+           debug(20, 1) ("WARNING: newer swaplog entry for dirno %d, fileno %08X\n",
+               SD->index, s.swap_filen);
+           /* I'm tempted to remove the swapfile here just to be safe,
+            * but there is a bad race condition in the NOVM version if
+            * the swapfile has recently been opened for writing, but
+            * not yet opened for reading.  Because we can't map
+            * swapfiles back to StoreEntrys, we don't know the state
+            * of the entry using that file.  */
+           /* We'll assume the existing entry is valid, probably because
+            * were in a slow rebuild and the the swap file number got taken
+            * and the validation procedure hasn't run. */
+           assert(rb->flags.need_to_validate);
+           rb->counts.clashcount++;
+           continue;
+       } else if (e && !disk_entry_newer) {
+           /* key already exists, current entry is newer */
+           /* keep old, ignore new */
+           rb->counts.dupcount++;
+           continue;
+       } else if (e) {
+           /* key already exists, this swapfile not being used */
+           /* junk old, load new */
+            storeExpireNow(e);
+            storeReleaseRequest(e);
+            storeDiskdDirReplRemove(e);
+           if (e->swap_filen > -1) {
+                /* 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, int *sfileno, int *size)
+{
+    SwapDir *SD = rb->sd;
+    diskdinfo_t *diskdinfo = (diskdinfo_t *)SD->fsdata;
+    int fd = -1;
+    int used = 0;
+    int dirs_opened = 0;
+    debug(20, 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 (rb->flags.init && rb->td != NULL)
+               closedir(rb->td);
+           rb->td = NULL;
+           if (dirs_opened)
+               return -1;
+           rb->td = opendir(rb->fullpath);
+           dirs_opened++;
+           if (rb->td == NULL) {
+               debug(50, 1) ("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(20, 1) ("storeDiskdDirGetNextFile: directory does not exist!.\n");
+               debug(20, 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(20, 3) ("storeDiskdDirGetNextFile: invalid %s\n",
+                   rb->entry->d_name);
+               continue;
+           }
+           if (!storeDiskdFilenoBelongsHere(rb->fn, rb->sd->index, rb->curlvl1, rb->curlvl2)) {
+               debug(20, 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(20, 3) ("storeDiskdDirGetNextFile: Locked, continuing with next.\n");
+               continue;
+           }
+           snprintf(rb->fullfilename, SQUID_MAXPATHLEN, "%s/%s",
+               rb->fullpath, rb->entry->d_name);
+           debug(20, 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;
+       }
+       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;
+    }
+    *sfileno = 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_num32 refcount,
+    u_short flags,
+    int clean)
+{
+    StoreEntry *e = NULL;
+    debug(20, 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;
+#if !HEAP_REPLACEMENT
+    e->refcount = 0;
+#endif
+    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;
+}
+
+static void
+storeDiskdDirRebuild(SwapDir * sd)
+{
+    RebuildState *rb = xcalloc(1, sizeof(*rb));
+    int clean = 0;
+    int zero = 0;
+    FILE *fp;
+    EVH *func = NULL;
+    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(20, 1) ("Rebuilding storage in %s (%s)\n",
+       sd->path, clean ? "CLEAN" : "DIRTY");
+    store_dirs_rebuilding++;
+    cbdataAdd(rb, cbdataXfree, 0);
+    eventAdd("storeRebuild", func, rb, 0.0, 1);
+}
+
+static void
+storeDiskdDirCloseTmpSwapLog(SwapDir * sd)
+{
+    diskdinfo_t *diskdinfo = (diskdinfo_t *)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 = (diskdinfo_t *)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, "r");
+    if (fp == NULL) {
+       debug(50, 0) ("%s: %s\n", swaplog_path, xstrerror());
+       fatal("Failed to open swap log for reading");
+    }
+    memset(&clean_sb, '\0', sizeof(struct stat));
+    if (stat(clean_path, &clean_sb) < 0)
+       *clean_flag = 0;
+    else if (clean_sb.st_mtime < log_sb.st_mtime)
+       *clean_flag = 0;
+    else
+       *clean_flag = 1;
+    safeunlink(clean_path, 1);
+    safe_free(swaplog_path);
+    safe_free(clean_path);
+    safe_free(new_path);
+    return fp;
+}
+
+struct _clean_state {
+    char *cur;
+    char *new;
+    char *cln;
+    char *outbuf;
+    off_t outbuf_offset;
+    int fd;
+};
+
+#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
+storeDiskdDirWriteCleanOpen(SwapDir * sd)
+{
+    struct _clean_state *state = xcalloc(1, sizeof(*state));
+    struct stat sb;
+    sd->log.clean.write = NULL;
+    sd->log.clean.state = NULL;
+    state->cur = xstrdup(storeDiskdDirSwapLogFile(sd, NULL));
+    state->new = xstrdup(storeDiskdDirSwapLogFile(sd, ".clean"));
+    state->cln = xstrdup(storeDiskdDirSwapLogFile(sd, ".last-clean"));
+    state->outbuf = xcalloc(CLEAN_BUF_SZ, 1);
+    state->outbuf_offset = 0;
+    unlink(state->new);
+    unlink(state->cln);
+    state->fd = file_open(state->new, O_WRONLY | O_CREAT | O_TRUNC);
+    if (state->fd < 0)
+       return -1;
+    debug(20, 3) ("storeDirWriteCleanLogs: opened %s, FD %d\n",
+       state->new, state->fd);
+#if HAVE_FCHMOD
+    if (stat(state->cur, &sb) == 0)
+       fchmod(state->fd, sb.st_mode);
+#endif
+    sd->log.clean.write = storeDiskdDirWriteCleanEntry;
+    sd->log.clean.state = state;
+    return 0;
+}
+
+/*
+ * "write" an entry to the clean log file.
+ */
+static void
+storeDiskdDirWriteCleanEntry(const StoreEntry * e, SwapDir * sd)
+{
+    storeSwapLogData s;
+    static size_t ss = sizeof(storeSwapLogData);
+    struct _clean_state *state = sd->log.clean.state;
+    if (NULL == e) {
+       storeDiskdDirWriteCleanClose(sd);
+       return;
+    }
+    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->key, MD5_DIGEST_CHARS);
+    xmemcpy(state->outbuf + state->outbuf_offset, &s, ss);
+    state->outbuf_offset += ss;
+    /* buffered write */
+    if (state->outbuf_offset + ss > CLEAN_BUF_SZ) {
+       if (write(state->fd, state->outbuf, state->outbuf_offset) < 0) {
+           debug(50, 0) ("storeDirWriteCleanLogs: %s: write: %s\n",
+               state->new, xstrerror());
+           debug(20, 0) ("storeDirWriteCleanLogs: Current swap logfile not replaced.\n");
+           file_close(state->fd);
+           state->fd = -1;
+           unlink(state->new);
+           safe_free(state);
+           sd->log.clean.state = NULL;
+           sd->log.clean.write = NULL;
+       }
+       state->outbuf_offset = 0;
+    }
+}
+
+static void
+storeDiskdDirWriteCleanClose(SwapDir * sd)
+{
+    struct _clean_state *state = sd->log.clean.state;
+    if (state->fd < 0)
+       return;
+    if (write(state->fd, state->outbuf, state->outbuf_offset) < 0) {
+       debug(50, 0) ("storeDirWriteCleanLogs: %s: write: %s\n",
+           state->new, xstrerror());
+       debug(20, 0) ("storeDirWriteCleanLogs: Current swap logfile "
+           "not replaced.\n");
+       file_close(state->fd);
+       state->fd = -1;
+       unlink(state->new);
+    }
+    safe_free(state->outbuf);
+    /*
+     * You can't rename open files on Microsoft "operating systems"
+     * so we have to close before renaming.
+     */
+    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
+storeDiskdDirSwapLog(const SwapDir * sd, const StoreEntry * e, int op)
+{
+    diskdinfo_t *diskdinfo = (diskdinfo_t *)sd->fsdata;
+    storeSwapLogData *s = xcalloc(1, sizeof(storeSwapLogData));
+    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->key, MD5_DIGEST_CHARS);
+    file_write(diskdinfo->swaplog_fd,
+       -1,
+       s,
+       sizeof(storeSwapLogData),
+       NULL,
+       NULL,
+       xfree);
+}
+
+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 = (diskdinfo_t *)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))
+           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
+       Counter.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 = (diskdinfo_t *)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 = (diskdinfo_t *)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)
+{
+    diskdinfo_t *diskdinfo = (diskdinfo_t *)SD->fsdata;
+    if (filn < 0)
+        return 0;
+    if (filn > diskdinfo->map->max_n_files)  
+        return 0;
+    return 1;
+}
+
+void
+storeDiskdDirMaintain(SwapDir *SD)
+{
+    StoreEntry *e = NULL;
+    int scanned = 0;
+    int locked = 0;
+    int expired = 0;
+    int max_scan;
+    int max_remove;
+    double f;
+    static time_t last_warn_time = 0;
+#if !HEAP_REPLACEMENT
+    dlink_node *m;
+    dlink_node *prev = NULL;
+#else
+    heap_key age;
+    heap_key min_age = 0.0;
+    link_list *locked_entries = NULL;
+#if HEAP_REPLACEMENT_DEBUG
+    if (!verify_heap_property(SD->repl.heap.heap)) {
+        debug(20, 1) ("Heap property violated!\n");
+    }
+#endif
+#endif
+    /* We can't delete objects while rebuilding swap */
+    if (store_dirs_rebuilding) {
+        return;
+    } else {
+        f = (double) (store_swap_size - store_swap_low) / (store_swap_high - store_swap_low);
+        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?
+         */
+#if 0
+        eventAdd("MaintainSwapSpace", storeMaintainSwapSpace, NULL, 1.0 - f, 1);
+#endif
+    }
+    debug(20, 3) ("storeMaintainSwapSpace: f=%f, max_scan=%d, max_remove=%d\n", f, max_scan, max_remove);
+#if HEAP_REPLACEMENT
+    while (heap_nodes(SD->repl.heap.heap) > 0) {
+        if (store_swap_size < store_swap_low)
+            break;
+        if (expired >= max_remove)
+            break;
+        if (scanned >= max_scan)
+            break;
+        age = heap_peepminkey(SD->repl.heap.heap);
+        e = heap_extractmin(SD->repl.heap.heap);
+        e->repl.node = NULL;         /* no longer in the heap */
+        scanned++;
+        if (storeEntryLocked(e)) {
+            /*
+             * Entry is in use ... put it in a linked list to ignore it.
+             */
+            if (!EBIT_TEST(e->flags, ENTRY_SPECIAL)) {
+                /*
+                 * If this was a "SPECIAL" do not add it back into the heap.
+                 * It will always be "SPECIAL" and therefore never removed.
+                 */
+                debug(20, 4) ("storeDiskdDirMaintain: locked url %s\n",
+                    (e->mem_obj && e->mem_obj->url) ? e->mem_obj->url : storeKeyText(e->
+key));
+                linklistPush(&locked_entries, e);
+            }
+            locked++;
+            continue;
+        } else if (storeDiskdDirCheckExpired(SD, e)) {
+            /*
+             * Note: This will not check the reference age ifdef
+             * HEAP_REPLACEMENT, but it does some other useful
+             * checks...
+             */
+            expired++;
+            debug(20, 3) ("Released store object age %f size %d refs %d key %s\n",
+                age, e->swap_file_sz, e->refcount, storeKeyText(e->key));
+            min_age = age;
+            storeRelease(e);
+        } else {
+            /*
+             * Did not expire the object so we need to add it back
+             * into the heap!
+             */
+            debug(20, 5) ("storeMaintainSwapSpace: non-expired %s\n",
+                storeKeyText(e->key));
+            linklistPush(&locked_entries, e);
+            continue;
+        }
+        if (store_swap_size < store_swap_low)
+            break;
+        else if (expired >= max_remove)
+            break;
+        else if (scanned >= max_scan)
+            break;
+    }
+    /*
+     * Bump the heap age factor.
+     */
+    if (min_age > 0.0)
+        SD->repl.heap.heap->age = min_age;
+    /*
+     * Reinsert all bumped locked entries back into heap...
+     */
+    while ((e = linklistShift(&locked_entries)))
+        e->repl.node = heap_insert(SD->repl.heap.heap, e);
+#else
+    for (m = SD->repl.lru.list.tail; m; m = prev) {
+        prev = m->prev;
+        e = m->data;
+        scanned++;
+        if (storeEntryLocked(e)) {
+            /*
+             * If there is a locked entry at the tail of the LRU list,
+             * move it to the beginning to get it out of the way.
+             * Theoretically, we might have all locked objects at the
+             * tail, and then we'll never remove anything here and the
+             * LRU age will go to zero.
+             */
+            if (memInUse(MEM_STOREENTRY) > max_scan) {
+                dlinkDelete(&e->repl.lru, &SD->repl.lru.list);
+                dlinkAdd(e, &e->repl.lru, &SD->repl.lru.list);
+            }
+            locked++;
+
+        } else if (storeDiskdDirCheckExpired(SD, e)) {
+            expired++;
+            storeRelease(e);
+        }
+        if (expired >= max_remove)
+            break;
+        if (scanned >= max_scan)
+            break;
+    }
+#endif
+    debug(20, (expired ? 2 : 3)) ("storeMaintainSwapSpace: scanned %d/%d removed %d/%d l
+ocked %d f=%.03f\n",
+        scanned, max_scan, expired, max_remove, locked, f);
+    debug(20, 3) ("storeMaintainSwapSpace stats:\n");
+    debug(20, 3) ("  %6d objects\n", memInUse(MEM_STOREENTRY));
+    debug(20, 3) ("  %6d were scanned\n", scanned);
+    debug(20, 3) ("  %6d were locked\n", locked);
+    debug(20, 3) ("  %6d were expired\n", expired);
+    if (store_swap_size < Config.Swap.maxSize)
+        return;
+    if (squid_curtime - last_warn_time < 10)
+        return;
+    debug(20, 0) ("WARNING: Disk space over limit: %d KB > %d KB\n",
+        store_swap_size, Config.Swap.maxSize);
+    last_warn_time = squid_curtime;
+}
+
+/*
+ * storeDiskdDirCheckObj
+ *
+ * This routine is called by storeDirSelectSwapDir to see if the given
+ * object is able to be stored on this filesystem. DISKD filesystems will
+ * happily store anything as long as the LRU time isn't too small.
+ */
+int
+storeDiskdDirCheckObj(SwapDir *SD, const StoreEntry *e)
+{
+    int loadav;
+
+    diskdinfo_t *diskdinfo = (diskdinfo_t *)SD->fsdata;
+#if !HEAP_REPLACEMENT
+    if (storeDiskdDirExpiredReferenceAge(SD) < 300) {
+        debug(20, 3) ("storeDiskdDirCheckObj: NO: LRU Age = %d\n",
+            storeDiskdDirExpiredReferenceAge(SD));
+        /* store_check_cachable_hist.no.lru_age_too_low++; */
+        return -1;
+    }
+#endif
+
+    /* Check the queue length */
+    if (diskdinfo->away >= diskdinfo->magic1)
+        return -1;
+
+    /* Calculate the storedir load relative to magic2 on a scale of 0 .. 1000 */
+    if (diskdinfo->away == 0)
+        loadav = 0;
+    else
+        loadav = diskdinfo->magic2 * 1000 / diskdinfo->away;
+    return loadav;
+}
+
+/*
+ * 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(1, 3) ("storeDiskdDirRefObj: referencing %p %d/%d\n", e, e->swap_dirn,
+      e->swap_filen);
+#if HEAP_REPLACEMENT
+    /* Nothing to do here */
+#else
+    /* Reference the object */
+    if (!EBIT_TEST(e->flags, RELEASE_REQUEST) &&
+        !EBIT_TEST(e->flags, ENTRY_SPECIAL)) {
+        dlinkDelete(&e->repl.lru, &SD->repl.lru.list);
+        dlinkAdd(e, &e->repl.lru, &SD->repl.lru.list);
+    }
+#endif
+}
+
+/*
+ * 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(1, 3) ("storeDiskdDirUnrefObj: referencing %p %d/%d\n", e,
+      e->swap_dirn, e->swap_filen);
+#if HEAP_REPLACEMENT
+    if (e->repl.node)
+        heap_update(SD->repl.heap.heap, e->repl.node, e);
+#endif
+}
+
+/*
+ * 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)
+{
+    debug(79, 3) ("storeDiskdDirUnlinkFile: unlinking fileno %08X\n", f);
+    storeDiskdDirMapBitReset(SD, f);
+    unlinkdUnlink(storeDiskdDirFullPath(SD, f, NULL));
+}
+
+#if !HEAP_REPLACEMENT
+/*
+ * storeDiskdDirExpiredReferenceAge
+ *
+ * The LRU age is scaled exponentially between 1 minute and
+ * Config.referenceAge , when store_swap_low < store_swap_size <
+ * store_swap_high.  This keeps store_swap_size within the low and high
+ * water marks.  If the cache is very busy then store_swap_size stays
+ * closer to the low water mark, if it is not busy, then it will stay
+ * near the high water mark.  The LRU age value can be examined on the
+ * cachemgr 'info' page.
+ */
+static time_t
+storeDiskdDirExpiredReferenceAge(SwapDir *SD)
+{
+    double x;
+    double z;
+    time_t age;
+    long store_high, store_low;
+    store_high = (long) (((float) SD->max_size *
+            (float) Config.Swap.highWaterMark) / (float) 100);
+    store_low = (long) (((float) SD->max_size *
+            (float) Config.Swap.lowWaterMark) / (float) 100);
+    debug(20, 20) ("RA: Dir %s, hi=%d, lo=%d, cur=%d\n", SD->path, store_high, store_low, SD->cur_size);
+  
+    x = (double) (store_high - SD->cur_size) /
+        (store_high - store_low);
+    x = x < 0.0 ? 0.0 : x > 1.0 ? 1.0 : x;
+    z = pow((double) (Config.referenceAge / 60), x);
+    age = (time_t) (z * 60.0);
+    if (age < 60)
+        age = 60;
+    else if (age > Config.referenceAge)
+        age = Config.referenceAge;
+    return age;
+}
+#endif
+
+/*
+ * storeDiskdDirCheckExpired
+ *
+ * Check whether the given object is expired or not
+ * It breaks layering a little by calling the upper layers to find
+ * out whether the object is locked or not, but we can't help this
+ * right now.
+ */
+static int
+storeDiskdDirCheckExpired(SwapDir *SD, StoreEntry *e)
+{
+    if (storeEntryLocked(e))
+       return 0;
+    if (EBIT_TEST(e->flags, RELEASE_REQUEST))
+       return 1;
+    if (EBIT_TEST(e->flags, ENTRY_NEGCACHED) && squid_curtime >= e->expires)
+       return 1;
+
+#if HEAP_REPLACEMENT
+    /*
+     * with HEAP_REPLACEMENT we are not using the LRU reference age, the heap
+     * controls the replacement of objects.
+     */
+    return 1;
+#else
+    if (squid_curtime - e->lastref > storeDiskdDirExpiredReferenceAge(SD))
+       return 1;
+    return 0;
+#endif
+}
+
+/*
+ * Add and remove the given StoreEntry from the replacement policy in
+ * use.
+ */
+
+void
+storeDiskdDirReplAdd(SwapDir *SD, StoreEntry *e)
+{
+    debug(20, 4) ("storeDiskdDirReplAdd: added node %p to dir %d\n", e,
+      SD->index);
+#if HEAP_REPLACEMENT
+    if (EBIT_TEST(e->flags, ENTRY_SPECIAL)) {
+        (void) 0;
+    } else {
+        e->repl.node = heap_insert(SD->repl.heap.heap, e);
+        debug(20, 4) ("storeDiskdDirReplAdd: inserted node 0x%x\n", e->repl.node);
+    }
+#else
+    /* Shouldn't we not throw special objects into the lru ? */
+    dlinkAdd(e, &e->repl.lru, &SD->repl.lru.list);
+#endif
+}
+
+
+void
+storeDiskdDirReplRemove(StoreEntry *e)
+{
+    SwapDir *SD = INDEXSD(e->swap_dirn);
+    debug(20, 4) ("storeDiskdDirReplRemove: remove node %p from dir %d\n", e,
+      SD->index);
+#if HEAP_REPLACEMENT
+    /* And now, release the object from the replacement policy */
+    if (e->repl.node) {
+        debug(20, 4) ("storeDiskdDirReplRemove: deleting node 0x%x\n",
+          e->repl.node);
+        heap_delete(SD->repl.heap.heap, e->repl.node);
+        e->repl.node = NULL;
+    }
+#else
+    dlinkDelete(&e->repl.lru, &SD->repl.lru.list);
+#endif
+}
+
+
+
+/*
+ * SHM manipulation routines
+ */
+
+void *
+storeDiskdShmGet(SwapDir * sd, int *shm_offset)
+{
+    char *buf;
+    diskdinfo_t *diskdinfo = (diskdinfo_t *)sd->fsdata;
+    buf = linklistShift(&diskdinfo->shm.stack);
+    assert(buf);
+    *shm_offset = buf - diskdinfo->shm.buf;
+    assert(0 <= *shm_offset && *shm_offset < SHMBUFS * SHMBUF_BLKSZ);
+    diskd_stats.shmbuf_count++;
+    return buf;
+}
+
+void
+storeDiskdShmPut(SwapDir * sd, int offset)
+{
+    char *buf;
+    diskdinfo_t *diskdinfo = (diskdinfo_t *)sd->fsdata;
+    assert(offset >= 0);
+    assert(offset < SHMBUFS * SHMBUF_BLKSZ);
+    buf = diskdinfo->shm.buf + offset;
+    linklistPush(&diskdinfo->shm.stack, buf);
+    diskd_stats.shmbuf_count--;
+}
+
+
+
+
+/* ========== LOCAL FUNCTIONS ABOVE, GLOBAL FUNCTIONS BELOW ========== */
+
+void
+storeDiskdDirStats(SwapDir *SD, StoreEntry * sentry)
+{
+    diskdinfo_t *diskdinfo;
+#if HAVE_STATVFS
+    struct statvfs sfs;
+#endif
+    diskdinfo = (diskdinfo_t *)SD->fsdata;
+    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));
+#if HAVE_STATVFS
+#define fsbtoblk(num, fsbs, bs) \
+    (((fsbs) != 0 && (fsbs) < (bs)) ? \
+            (num) / ((bs) / (fsbs)) : (num) * ((fsbs) / (bs)))
+       if (!statvfs(SD->path, &sfs)) {
+            storeAppendPrintf(sentry, "Filesystem Space in use: %d/%d KB (%d%%)\n",
+            fsbtoblk((sfs.f_blocks - sfs.f_bfree), sfs.f_frsize, 1024),
+            fsbtoblk(sfs.f_blocks, sfs.f_frsize, 1024),
+            percent(sfs.f_blocks - sfs.f_bfree, sfs.f_blocks));
+            storeAppendPrintf(sentry, "Filesystem Inodes in use: %d/%d (%d%%)\n",
+            sfs.f_files - sfs.f_ffree, sfs.f_files,
+            percent(sfs.f_files - sfs.f_ffree, sfs.f_files));
+    }
+#endif
+    storeAppendPrintf(sentry, "Flags:");
+    if (SD->flags.selected)
+        storeAppendPrintf(sentry, " SELECTED");
+    if (SD->flags.read_only)
+        storeAppendPrintf(sentry, " READ-ONLY");
+    storeAppendPrintf(sentry, "\n");
+#if !HEAP_REPLACEMENT
+    storeAppendPrintf(sentry, "LRU Expiration Age: %6.2f days\n",
+      (double) storeDiskdDirExpiredReferenceAge(SD) / 86400.0); 
+#else
+#if 0
+    storeAppendPrintf(sentry, "Storage Replacement Threshold:\t%f\n",
+        heap_peepminkey(sd.repl.heap.heap));
+#endif
+#endif
+    storeAppendPrintf(sentry, "Pending operations: %d\n", diskdinfo->away);
+}
+
+/*
+ * storeDiskdDirReconfigure
+ *
+ * This routine is called when the given swapdir needs reconfiguring 
+ */
+void
+storeDiskdDirReconfigure(SwapDir *sd, int index, char *path)
+{
+    char *token;
+    int i;
+    int size;
+    int l1;
+    int l2;
+    int magic1, magic2;
+    unsigned int read_only = 0;
+    diskdinfo_t *diskdinfo;
+
+    i = GetInteger();
+    size = i << 10;            /* Mbytes to kbytes */
+    if (size <= 0)
+       fatal("storeDiskdDirReconfigure: invalid size value");
+    i = GetInteger();
+    l1 = i;
+    if (l1 <= 0)
+       fatal("storeDiskdDirReconfigure: invalid level 1 directories value");
+    i = GetInteger();
+    l2 = i;
+    if (l2 <= 0)
+       fatal("storeDiskdDirReconfigure: invalid level 2 directories value");
+    i = GetInteger();
+    magic1 = i;
+    if (magic1 <= 0)
+       fatal("storeDiskdDirParse: invalid magic1 value");
+    i = GetInteger();
+    magic2 = i;
+    if (magic2 <= 0)
+       fatal("storeDiskdDirParse: invalid magic2 value");
+    if ((token = strtok(NULL, w_space)))
+       if (!strcasecmp(token, "read-only"))
+           read_only = 1;
+
+    /* 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;
+    if (sd->flags.read_only != read_only)
+       debug(3, 1) ("Cache dir '%s' now %s\n",
+           path, read_only ? "Read-Only" : "Read-Write");
+    diskdinfo = (diskdinfo_t *)sd->fsdata;
+    diskdinfo->magic1 = magic1;
+    diskdinfo->magic2 = magic2;
+    sd->flags.read_only = read_only;
+    return;
+}
+
+void
+storeDiskdDirDump(StoreEntry * entry, const char *name, SwapDir * s)
+{
+    diskdinfo_t *diskdinfo = (diskdinfo_t *)s->fsdata;
+    storeAppendPrintf(entry, "%s %s %s %d %d %d\n",
+       name,
+        "diskd",
+       s->path,
+       s->max_size >> 10,
+       diskdinfo->l1,
+       diskdinfo->l2);
+}
+
+/*
+ * Only "free" the filesystem specific stuff here
+ */
+static void
+storeDiskdDirFree(SwapDir * s)
+{
+    diskdinfo_t *diskdinfo = (diskdinfo_t *)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 = (diskdinfo_t *)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(20, 0) ("storeDiskdCleanupDoubleCheck: MISSING SWAP FILE\n");
+        debug(20, 0) ("storeDiskdCleanupDoubleCheck: FILENO %08X\n", e->swap_filen);
+        debug(20, 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(20, 0) ("storeDiskdCleanupDoubleCheck: SIZE MISMATCH\n");
+        debug(20, 0) ("storeDiskdCleanupDoubleCheck: FILENO %08X\n", e->swap_filen);
+        debug(20, 0) ("storeDiskdCleanupDoubleCheck: PATH %s\n",
+            storeDiskdDirFullPath(sd, e->swap_filen, NULL));
+        debug(20, 0) ("storeDiskdCleanupDoubleCheck: ENTRY SIZE: %d, FILE SIZE: %d\n",
+            e->swap_file_sz, (int) sb.st_size); 
+        storeEntryDump(e, 0); 
+        return -1;
+    }       
+    return 0;
+}
+
+/*
+ * storeDiskdDirParse
+ *
+ * Called when a *new* fs is being setup.
+ */
+void
+storeDiskdDirParse(SwapDir *sd, int index, char *path)
+{
+    char *token;
+    int i;
+    int size;
+    int l1;
+    int l2;
+    int magic1, magic2;
+    unsigned int read_only = 0;
+    diskdinfo_t *diskdinfo;
+
+    i = GetInteger();
+    size = i << 10;            /* Mbytes to kbytes */
+    if (size <= 0)
+       fatal("storeDiskdDirParse: invalid size value");
+    i = GetInteger();
+    l1 = i;
+    if (l1 <= 0)
+       fatal("storeDiskdDirParse: invalid level 1 directories value");
+    i = GetInteger();
+    l2 = i;
+    if (l2 <= 0)
+       fatal("storeDiskdDirParse: invalid level 2 directories value");
+    i = GetInteger();
+    magic1 = i;
+    if (magic1 <= 0)
+       fatal("storeDiskdDirParse: invalid magic1 value");
+    i = GetInteger();
+    magic2 = i;
+    if (magic2 <= 0)
+       fatal("storeDiskdDirParse: invalid magic2 value");
+
+
+    if ((token = strtok(NULL, w_space)))
+       if (!strcasecmp(token, "read-only"))
+           read_only = 1;
+
+    diskdinfo = xmalloc(sizeof(diskdinfo_t));
+    if (diskdinfo == NULL)
+        fatal("storeDiskdDirParse: couldn't xmalloc() diskdinfo_t!\n");
+
+    sd->index = index;
+    sd->path = xstrdup(path);
+    sd->max_size = size;
+    sd->fsdata = diskdinfo;
+    diskdinfo->l1 = l1;
+    diskdinfo->l2 = l2;
+    diskdinfo->swaplog_fd = -1;
+    diskdinfo->map = NULL; /* Debugging purposes */
+    diskdinfo->suggest = 0;
+    diskdinfo->magic1 = magic1;
+    diskdinfo->magic2 = magic2;
+    sd->flags.read_only = read_only;
+    sd->init = storeDiskdDirInit;
+    sd->newfs = storeDiskdDirNewfs;
+    sd->dump = storeDiskdDirDump;
+    sd->freefs = storeDiskdDirFree;
+    sd->dblcheck = storeDiskdCleanupDoubleCheck;
+    sd->statfs = storeDiskdDirStats;
+    sd->maintainfs = storeDiskdDirMaintain;
+    sd->checkobj = storeDiskdDirCheckObj;
+    sd->refobj = storeDiskdDirRefObj;
+    sd->unrefobj = storeDiskdDirUnrefObj;
+    sd->callback = storeDiskdDirCallback;
+    sd->sync = storeDiskdDirSync;
+    sd->obj.create = storeDiskdCreate;
+    sd->obj.open = storeDiskdOpen;
+    sd->obj.close = storeDiskdClose;
+    sd->obj.read = storeDiskdRead;
+    sd->obj.write = storeDiskdWrite;
+    sd->obj.unlink = storeDiskdUnlink;
+    sd->log.open = storeDiskdDirOpenSwapLog;
+    sd->log.close = storeDiskdDirCloseSwapLog;
+    sd->log.write = storeDiskdDirSwapLog;
+    sd->log.clean.open = storeDiskdDirWriteCleanOpen;
+
+    /* Initialise replacement policy stuff */
+#if HEAP_REPLACEMENT
+    /*
+     * Create new heaps with cache replacement policies attached to them.
+     * The cache replacement policy is specified as either GDSF or LFUDA in
+     * the squid.conf configuration file.  Note that the replacement policy
+     * applies only to the disk replacement algorithm.  Memory replacement
+     * always uses GDSF since we want to maximize object hit rate.
+     */
+    if (Config.replPolicy) {
+        if (tolower(Config.replPolicy[0]) == 'g') {
+            debug(20, 1) ("Using GDSF disk replacement policy\n");
+            sd->repl.heap.heap = new_heap(10000, HeapKeyGen_StoreEntry_GDSF);
+        } else if (tolower(Config.replPolicy[0]) == 'l') {
+            if (tolower(Config.replPolicy[1]) == 'f') {
+                debug(20, 1) ("Using LFUDA disk replacement policy\n");
+                sd->repl.heap.heap = new_heap(10000, HeapKeyGen_StoreEntry_LFUDA);
+            } else if (tolower(Config.replPolicy[1]) == 'r') {
+                debug(20, 1) ("Using LRU heap disk replacement policy\n");
+                sd->repl.heap.heap = new_heap(10000, HeapKeyGen_StoreEntry_LRU);
+            }
+        } else {
+            debug(20, 1) ("Unrecognized replacement_policy; using GDSF\n");
+            sd->repl.heap.heap = new_heap(10000, HeapKeyGen_StoreEntry_GDSF);
+        }
+    } else {
+        debug(20, 1) ("Using default disk replacement policy (GDSF)\n");
+        sd->repl.heap.heap = new_heap(10000, HeapKeyGen_StoreEntry_GDSF);
+    }
+#else
+    sd->repl.lru.list.head = NULL;
+    sd->repl.lru.list.tail = NULL;
+#endif
+}
+
+/*
+ * Initial setup / end destruction
+ */
+void
+storeDiskdDirDone(void)
+{
+    memPoolDestroy(diskd_state_pool);
+    diskd_initialised = 0;
+}
+
+void
+storeFsSetup_diskd(storefs_entry_t *storefs)
+{
+    assert(!diskd_initialised);
+    storefs->parsefunc = storeDiskdDirParse;
+    storefs->reconfigurefunc = storeDiskdDirReconfigure;
+    storefs->donefunc = storeDiskdDirDone;
+    diskd_state_pool = memPoolCreate("DISKD IO State data", sizeof(diskdstate_t));
+    memset(&diskd_stats, '\0', sizeof(diskd_stats));
+    cachemgrRegister("diskd", "DISKD Stats", storeDiskdStats, 0, 1);
+    debug(81, 1) ("diskd started\n");
+    diskd_initialised = 1;
+}
+
diff --git a/src/fs/diskd/store_diskd.h b/src/fs/diskd/store_diskd.h
new file mode 100644 (file)
index 0000000..f397fdd
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * store_diskd.h
+ *
+ * Internal declarations for the diskd routines
+ */
+
+#ifndef __STORE_DISKD_H__
+#define __STORE_DISKD_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
+ * queue until the level falls below MAGIC2.  Recommended value
+ * is 75% of SHMBUFS. MAGIC1 is the number of messages away which we
+ * stop allowing open/create for.
+ */
+
+struct _diskdinfo_t {
+    int swaplog_fd;
+    int l1;
+    int l2;
+    fileMap *map;
+    int suggest;
+    int smsgid;
+    int rmsgid;
+    int wfd;
+    int away;
+    struct {
+       char *buf;
+       link_list *stack;
+       int id;
+    } shm;
+    int magic1;
+    int magic2;
+};
+
+struct _diskdstate_t {
+    int id;
+    struct {
+       unsigned int close_request:1;
+       unsigned int reading:1;
+       unsigned int writing:1;
+    } flags;
+    char *read_buf;
+};
+
+enum {
+    _MQD_NOP,
+    _MQD_OPEN,
+    _MQD_CLOSE,
+    _MQD_READ,
+    _MQD_WRITE,
+    _MQD_UNLINK
+};
+
+typedef struct _diomsg {
+    mtyp_t mtype;
+    int id;
+    int seq_no;
+    void *callback_data;
+    int size;
+    int offset;
+    int status;
+    int shm_offset;
+} diomsg;
+
+struct _diskd_stats {
+    int open_fail_queue_len;
+    int block_queue_len;
+    int max_away;
+    int max_shmuse;
+    int shmbuf_count;
+    int sent_count;
+    int recv_count;
+    int sio_id;
+};
+
+typedef struct _diskd_stats diskd_stats_t;
+typedef struct _diskdinfo_t diskdinfo_t;
+typedef struct _diskdstate_t diskdstate_t;
+
+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 *, int);
+extern void *storeDiskdShmGet(SwapDir *, int *);
+extern void storeDiskdHandle(diomsg *M);
+
+
+/*
+ * Store IO stuff
+ */
+extern STOBJCREATE storeDiskdCreate;
+extern STOBJOPEN storeDiskdOpen;
+extern STOBJCLOSE storeDiskdClose;
+extern STOBJREAD storeDiskdRead;
+extern STOBJWRITE storeDiskdWrite;
+extern STOBJUNLINK storeDiskdUnlink;
+
+/*
+ * SHMBUFS is the number of shared memory buffers to allocate for
+ * Each SwapDir.
+ */
+#define SHMBUFS 96
+#define SHMBUF_BLKSZ SM_PAGE_SIZE
+/*
+ * 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
+ * queue until the level falls below MAGIC2.  Recommended value
+ * is 75% of SHMBUFS.
+ */
+#define MAGIC1 Config.diskd.magic1
+#define MAGIC2 Config.diskd.magic2
+
+
+extern diskd_stats_t diskd_stats;
+
+#endif
diff --git a/src/fs/diskd/store_io_diskd.cc b/src/fs/diskd/store_io_diskd.cc
new file mode 100644 (file)
index 0000000..19362a5
--- /dev/null
@@ -0,0 +1,464 @@
+
+#include "config.h"
+#include "squid.h"
+#include "store_diskd.h"
+
+#include <sys/ipc.h>
+#include <sys/msg.h>
+#include <sys/shm.h>
+
+#undef assert
+#include <assert.h>
+
+
+/*
+ * DEBUG: section 81  Diskd Interface functions
+ */
+
+
+static int storeDiskdSend(int, SwapDir *, int, storeIOState *, int, int, int);
+static void storeDiskdIOCallback(storeIOState * sio, int errflag);
+static void storeDiskdIOFreeEntry(void *sio, int foo);
+
+/*
+ * SHMBUFS is the number of shared memory buffers to allocate for
+ * Each SwapDir.
+ */
+#define SHMBUFS 96
+#define SHMBUF_BLKSZ SM_PAGE_SIZE
+
+
+/* === PUBLIC =========================================================== */
+
+storeIOState *
+storeDiskdOpen(SwapDir *SD, StoreEntry *e, STFNCB *file_callback,
+  STIOCB *callback, void *callback_data)
+{
+    sfileno f = e->swap_filen;
+    int x;
+    storeIOState *sio;
+    char *buf;
+    int shm_offset;
+    diskdinfo_t *diskdinfo = (diskdinfo_t *) SD->fsdata;
+    debug(81, 3) ("storeDiskdOpen: fileno %08X\n", f);
+    /*
+     * XXX Eventually there should be an option here to fail on open()
+     * If there are too many requests queued.
+     */
+    if (diskdinfo->away > diskdinfo->magic1) {
+        debug(81, 3) ("storeDiskdOpen: FAILING, too many requests away\n");
+       diskd_stats.open_fail_queue_len++;
+       return NULL;
+    }
+    sio = memAllocate(MEM_STORE_IO);
+    cbdataAdd(sio, storeDiskdIOFreeEntry, MEM_STORE_IO);
+    sio->fsstate = memPoolAlloc(diskd_state_pool);
+
+    sio->swap_filen = f;
+    sio->swap_dirn = SD->index;
+    sio->mode = O_RDONLY;
+    sio->callback = callback;
+    sio->callback_data = callback_data;
+    sio->e = e;
+    cbdataLock(callback_data);
+
+    ((diskdstate_t *)(sio->fsstate))->flags.writing = 0;
+    ((diskdstate_t *)(sio->fsstate))->flags.reading = 0;
+    ((diskdstate_t *)(sio->fsstate))->flags.close_request = 0;
+    ((diskdstate_t *)(sio->fsstate))->id = diskd_stats.sio_id++;
+
+    buf = storeDiskdShmGet(SD, &shm_offset);
+    /* XXX WRONG!!! :) */
+    strcpy(buf, storeDiskdDirFullPath(SD, f, NULL));
+    x = storeDiskdSend(_MQD_OPEN,
+       SD,
+       ((diskdstate_t *)(sio->fsstate))->id,
+       sio,
+       strlen(buf) + 1,
+       O_RDONLY,
+       shm_offset);
+    if (x < 0) {
+       debug(50, 1) ("storeDiskdSend OPEN: %s\n", xstrerror());
+       storeDiskdShmPut(SD, shm_offset);
+       cbdataUnlock(sio->callback_data);
+       cbdataFree(sio);
+       return NULL;
+    }
+    return sio;
+}
+
+storeIOState *
+storeDiskdCreate(SwapDir *SD, StoreEntry *e, STFNCB *file_callback,
+  STIOCB *callback, void *callback_data)
+{
+    sfileno f;
+    int x;
+    storeIOState *sio;
+    char *buf;
+    int shm_offset;
+    diskdinfo_t *diskdinfo = (diskdinfo_t *) SD->fsdata;
+    /*
+     * XXX Eventually there should be an option here to fail on open()
+     * If there are too many requests queued.
+     */
+    if (diskdinfo->away > diskdinfo->magic1) {
+       diskd_stats.open_fail_queue_len++;
+       return NULL;
+    }
+
+    /* Allocate a number */
+    f = storeDiskdDirMapBitAllocate(SD);
+    debug(81, 3) ("storeDiskdCreate: fileno %08X\n", f);
+
+    sio = memAllocate(MEM_STORE_IO);
+    cbdataAdd(sio, storeDiskdIOFreeEntry, MEM_STORE_IO);
+    sio->fsstate = memPoolAlloc(diskd_state_pool);
+
+    sio->swap_filen = f;
+    sio->swap_dirn = SD->index;
+    sio->mode = O_WRONLY | O_CREAT | O_TRUNC;
+    sio->callback = callback;
+    sio->callback_data = callback_data;
+    sio->e = e;
+    cbdataLock(callback_data);
+
+    ((diskdstate_t *)(sio->fsstate))->flags.writing = 0;
+    ((diskdstate_t *)(sio->fsstate))->flags.reading = 0;
+    ((diskdstate_t *)(sio->fsstate))->flags.close_request = 0;
+    ((diskdstate_t *)(sio->fsstate))->id = diskd_stats.sio_id++;
+
+    buf = storeDiskdShmGet(SD, &shm_offset);
+    /* XXX WRONG!!! :) */
+    strcpy(buf, storeDiskdDirFullPath(SD, f, NULL));
+    x = storeDiskdSend(_MQD_OPEN,
+       SD,
+       ((diskdstate_t *)(sio->fsstate))->id,
+       sio,
+       strlen(buf) + 1,
+       sio->mode,
+       shm_offset);
+    if (x < 0) {
+       debug(50, 1) ("storeDiskdSend OPEN: %s\n", xstrerror());
+       storeDiskdShmPut(SD, shm_offset);
+       cbdataUnlock(sio->callback_data);
+       cbdataFree(sio);
+       return NULL;
+    }
+    storeDiskdDirReplAdd(SD, e);
+    return sio;
+}
+
+
+void
+storeDiskdClose(SwapDir *SD, storeIOState * sio)
+{
+    int x;
+    diskdstate_t *diskdstate = (diskdstate_t *)sio->fsstate;
+    debug(81, 3) ("storeDiskdClose: dirno %d, fileno %08X\n", SD->index,
+      sio->swap_filen);
+    x = storeDiskdSend(_MQD_CLOSE,
+       SD,
+       diskdstate->id,
+       sio,
+       0,
+       0,
+       -1);
+    if (x < 0) {
+       debug(50, 1) ("storeDiskdSend CLOSE: %s\n", xstrerror());
+       storeDiskdIOCallback(sio, DISK_ERROR);
+    }
+}
+
+void
+storeDiskdRead(SwapDir *SD, storeIOState * sio, char *buf, size_t size, off_t offset, STRCB * callback, void *callback_data)
+{
+    int x;
+    int shm_offset;
+    char *rbuf;
+    diskdstate_t *diskdstate = (diskdstate_t *)sio->fsstate;
+    if (!cbdataValid(sio))
+       return;
+    if (diskdstate->flags.reading) {
+       debug(81, 1) ("storeDiskdRead: already reading!\n");
+       return;
+    }
+    assert(sio->read.callback == NULL);
+    assert(sio->read.callback_data == NULL);
+    sio->read.callback = callback;
+    sio->read.callback_data = callback_data;
+    diskdstate->read_buf = buf;        /* the one passed from above */
+    cbdataLock(sio->read.callback_data);
+    debug(81, 3) ("storeDiskdRead: dirno %d, fileno %08X\n", sio->swap_dirn, sio->swap_filen);
+    sio->offset = offset;
+    diskdstate->flags.reading = 1;
+    rbuf = storeDiskdShmGet(SD, &shm_offset);
+    assert(rbuf);
+    x = storeDiskdSend(_MQD_READ,
+       SD,
+       diskdstate->id,
+       sio,
+       (int) size,
+       (int) offset,
+       shm_offset);
+    if (x < 0) {
+       debug(50, 1) ("storeDiskdSend READ: %s\n", xstrerror());
+       storeDiskdShmPut(SD, shm_offset);
+       storeDiskdIOCallback(sio, DISK_ERROR);
+    }
+}
+
+void
+storeDiskdWrite(SwapDir *SD, storeIOState * sio, char *buf, size_t size, off_t offset, FREE * free_func)
+{
+    int x;
+    char *sbuf;
+    int shm_offset;
+    diskdstate_t *diskdstate = (diskdstate_t *)sio->fsstate;
+    debug(81, 3) ("storeDiskdWrite: dirno %d, fileno %08X\n", SD->index, sio->swap_filen);
+    if (!cbdataValid(sio)) {
+       free_func(buf);
+       return;
+    }
+    diskdstate->flags.writing = 1;
+    sbuf = storeDiskdShmGet(SD, &shm_offset);
+    xmemcpy(sbuf, buf, size);
+    if (free_func)
+        free_func(buf);
+    x = storeDiskdSend(_MQD_WRITE,
+       SD,
+       diskdstate->id,
+       sio,
+       (int) size,
+       (int) offset,
+       shm_offset);
+    if (x < 0) {
+       debug(50, 1) ("storeDiskdSend WRITE: %s\n", xstrerror());
+       storeDiskdShmPut(SD, shm_offset);
+       storeDiskdIOCallback(sio, DISK_ERROR);
+    }
+}
+
+void
+storeDiskdUnlink(SwapDir *SD, StoreEntry *e)
+{
+    int x;
+    int shm_offset;
+    char *buf;
+    diskdinfo_t *diskdinfo = (diskdinfo_t *)SD->fsdata;
+
+    debug(81, 3) ("storeDiskdUnlink: dirno %d, fileno %08X\n", SD->index,
+      e->swap_filen);
+    storeDiskdDirReplRemove(e);
+    storeDiskdDirMapBitReset(SD, e->swap_filen);
+    if (diskdinfo->away >= diskdinfo->magic1) {
+        /* Damn, we need to issue a sync unlink here :( */
+        debug(50, 2) ("storeDiskUnlink: Out of queue space, sync unlink\n");
+        storeDiskdDirUnlinkFile(SD, e->swap_filen);
+        return;
+    }
+
+    /* We can attempt a diskd unlink */
+    buf = storeDiskdShmGet(SD, &shm_offset);
+    strcpy(buf, storeDiskdDirFullPath(SD, e->swap_filen, NULL));
+    x = storeDiskdSend(_MQD_UNLINK,
+       SD,
+       e->swap_filen,
+       NULL,
+       0,
+       0,
+       shm_offset);
+    if (x < 0) {
+       debug(50, 1) ("storeDiskdSend UNLINK: %s\n", xstrerror());
+       unlink(buf); /* XXX EWW! */
+       storeDiskdShmPut(SD, shm_offset);
+    }
+}
+
+
+/*  === STATIC =========================================================== */
+
+static void
+storeDiskdOpenDone(diomsg * M)
+{
+    storeIOState *sio = M->callback_data;
+    Counter.syscalls.disk.opens++;
+    debug(81, 3) ("storeDiskdOpenDone: dirno %d, fileno %08x status %d\n",
+       sio->swap_dirn, sio->swap_filen, M->status);
+    if (M->status < 0) {
+       storeDiskdIOCallback(sio, DISK_ERROR);
+    }
+}
+
+static void
+storeDiskdCloseDone(diomsg * M)
+{
+    storeIOState *sio = M->callback_data;
+    Counter.syscalls.disk.closes++;
+    debug(81, 3) ("storeDiskdCloseDone: dirno %d, fileno %08x status %d\n",
+       sio->swap_dirn, sio->swap_filen, M->status);
+    if (M->status < 0) {
+       storeDiskdIOCallback(sio, DISK_ERROR);
+       return;
+    }
+    storeDiskdIOCallback(sio, DISK_OK);
+}
+
+static void
+storeDiskdReadDone(diomsg * M)
+{
+    storeIOState *sio = M->callback_data;
+    STRCB *callback = sio->read.callback;
+    SwapDir *sd = INDEXSD(sio->swap_dirn);
+    diskdstate_t *diskdstate = sio->fsstate;
+    diskdinfo_t *diskdinfo = sd->fsdata;
+    void *their_data = sio->read.callback_data;
+    char *their_buf = diskdstate->read_buf;
+    char *sbuf;
+    size_t len;
+    int valid;
+    Counter.syscalls.disk.reads++;
+    diskdstate->flags.reading = 0;
+    valid = cbdataValid(sio->read.callback_data);
+    cbdataUnlock(sio->read.callback_data);
+    debug(81, 3) ("storeDiskdReadDone: dirno %d, fileno %08x status %d\n",
+       sio->swap_dirn, sio->swap_filen, M->status);
+    if (M->status < 0) {
+       storeDiskdIOCallback(sio, DISK_ERROR);
+       return;
+    }
+    sbuf = diskdinfo->shm.buf + M->shm_offset;
+    len = M->status;
+    xmemcpy(their_buf, sbuf, len);     /* yucky copy */
+    sio->offset += len;
+    assert(callback);
+    assert(their_data);
+    sio->read.callback = NULL;
+    sio->read.callback_data = NULL;
+    if (valid)
+       callback(their_data, their_buf, len);
+}
+
+static void
+storeDiskdWriteDone(diomsg * M)
+{
+    storeIOState *sio = M->callback_data;
+    diskdstate_t *diskdstate = (diskdstate_t *)sio->fsstate;
+    Counter.syscalls.disk.writes++;
+    diskdstate->flags.writing = 0;
+    debug(81, 3) ("storeDiskdWriteDone: dirno %d, fileno %08x status %d\n",
+       sio->swap_dirn, sio->swap_filen, M->status);
+    if (M->status < 0) {
+       storeDiskdIOCallback(sio, DISK_ERROR);
+       return;
+    }
+    sio->offset += M->status;
+}
+
+static void
+storeDiskdUnlinkDone(diomsg * M)
+{
+    debug(81, 3) ("storeDiskdUnlinkDone: fileno %08x status %d\n",
+       M->id, M->status);
+    Counter.syscalls.disk.unlinks++;
+}
+
+void
+storeDiskdHandle(diomsg * M)
+{
+    int valid = M->callback_data ? cbdataValid(M->callback_data) : 1;
+    if (M->callback_data)
+       cbdataUnlock(M->callback_data);
+    if (!valid) {
+       debug(81, 3) ("storeDiskdHandle: Invalid callback_data %p\n",
+           M->callback_data);
+       /*
+        * The read operation has its own callback.  If we don't
+        * call storeDiskdReadDone(), then we must make sure the
+        * callback_data gets unlocked!
+        */
+       if (_MQD_READ == M->mtype) {
+           storeIOState *sio = M->callback_data;
+           cbdataUnlock(sio->read.callback_data);
+       }
+       return;
+    }
+    switch (M->mtype) {
+    case _MQD_OPEN:
+       storeDiskdOpenDone(M);
+       break;
+    case _MQD_CLOSE:
+       storeDiskdCloseDone(M);
+       break;
+    case _MQD_READ:
+       storeDiskdReadDone(M);
+       break;
+    case _MQD_WRITE:
+       storeDiskdWriteDone(M);
+       break;
+    case _MQD_UNLINK:
+       storeDiskdUnlinkDone(M);
+       break;
+    default:
+       assert(0);
+       break;
+    }
+}
+
+static void
+storeDiskdIOCallback(storeIOState * sio, int errflag)
+{
+    int valid = cbdataValid(sio->callback_data);
+    debug(81, 3) ("storeUfsIOCallback: errflag=%d\n", errflag);
+    cbdataUnlock(sio->callback_data);
+    if (valid)
+       sio->callback(sio->callback_data, errflag, sio);
+    cbdataFree(sio);
+}
+
+static int
+storeDiskdSend(int mtype, SwapDir * sd, int id, storeIOState * sio, int size, int offset, int shm_offset)
+{
+    int x;
+    diomsg M;
+    static int send_errors = 0;
+    static int last_seq_no = 0;
+    static int seq_no = 0;
+    diskdinfo_t *diskdinfo = (diskdinfo_t *)sd->fsdata;
+    M.mtype = mtype;
+    M.callback_data = sio;
+    M.size = size;
+    M.offset = offset;
+    M.status = -1;
+    M.shm_offset = shm_offset;
+    M.id = id;
+    M.seq_no = ++seq_no;
+    if (M.callback_data)
+       cbdataLock(M.callback_data);
+    if (M.seq_no < last_seq_no)
+       debug(81, 1) ("WARNING: sequencing out of order\n");
+    x = msgsnd(diskdinfo->smsgid, &M, msg_snd_rcv_sz, IPC_NOWAIT);
+    last_seq_no = M.seq_no;
+    if (0 == x) {
+       diskd_stats.sent_count++;
+       diskdinfo->away++;
+    } else {
+       debug(50, 1) ("storeDiskdSend: msgsnd: %s\n", xstrerror());
+       if (M.callback_data)
+           cbdataUnlock(M.callback_data);
+       assert(++send_errors < 100);
+    }
+    return x;
+}
+
+
+/*
+ * We can't pass memFree() as a free function here, because we need to free
+ * the fsstate variable ..
+ */
+static void
+storeDiskdIOFreeEntry(void *sio, int foo)
+{
+    memPoolFree(diskd_state_pool, ((storeIOState *)sio)->fsstate);
+    memFree(sio, MEM_STORE_IO);
+}
+
diff --git a/src/fs/ufs/Makefile.in b/src/fs/ufs/Makefile.in
new file mode 100644 (file)
index 0000000..c9b90c6
--- /dev/null
@@ -0,0 +1,56 @@
+#
+#  Makefile for the UFS storage driver for the Squid Object Cache server
+#
+#  $Id: Makefile.in,v 1.1 2000/05/03 17:15:47 adrian Exp $
+#
+
+FS             = ufs
+
+top_srcdir     = @top_srcdir@
+VPATH          = @srcdir@
+
+CC             = @CC@
+MAKEDEPEND     = @MAKEDEPEND@
+AR_R           = @AR_R@
+RANLIB         = @RANLIB@
+AC_CFLAGS      = @CFLAGS@
+SHELL          = /bin/sh
+
+INCLUDE                = -I../../../include -I$(top_srcdir)/include -I$(top_srcdir)/src/
+CFLAGS                 = $(AC_CFLAGS) $(INCLUDE) $(DEFINES)
+
+OUT            = ../$(FS).a
+
+OBJS           = \
+               store_dir_ufs.o \
+               store_io_ufs.o
+
+
+all: $(OUT)
+
+$(OUT): $(OBJS)
+       @rm -f ../stamp
+       $(AR_R) $(OUT) $(OBJS)
+       $(RANLIB) $(OUT)
+
+$(OBJS): $(top_srcdir)/include/version.h ../../../include/autoconf.h
+
+.c.o:
+       @rm -f ../stamp
+       $(CC) $(CFLAGS) -c $<
+
+clean: 
+       -rm -rf *.o *pure_* core ../$(FS).a
+
+distclean:     clean
+       -rm -f Makefile
+       -rm -f Makefile.bak
+       -rm -f tags
+
+install:
+
+tags:
+       ctags *.[ch] $(top_srcdir)/src/*.[ch] $(top_srcdir)/include/*.h $(top_srcdir)/lib/*.[ch]
+
+depend:
+       $(MAKEDEPEND) $(INCLUDE) -fMakefile *.c
diff --git a/src/fs/ufs/store_dir_ufs.cc b/src/fs/ufs/store_dir_ufs.cc
new file mode 100644 (file)
index 0000000..c6493bd
--- /dev/null
@@ -0,0 +1,1903 @@
+
+/*
+ * $Id: store_dir_ufs.cc,v 1.1 2000/05/03 17:15:47 adrian Exp $
+ *
+ * DEBUG: section 47    Store Directory Routines
+ * AUTHOR: Duane Wessels
+ *
+ * SQUID Internet Object Cache  http://squid.nlanr.net/Squid/
+ * ----------------------------------------------------------
+ *
+ *  Squid is the result of efforts by numerous individuals from the
+ *  Internet community.  Development is led by Duane Wessels of the
+ *  National Laboratory for Applied Network Research and funded by the
+ *  National Science Foundation.  Squid is Copyrighted (C) 1998 by
+ *  Duane Wessels and the University of California San Diego.  Please
+ *  see the COPYRIGHT file for full details.  Squid incorporates
+ *  software developed and/or copyrighted by other sources.  Please see
+ *  the CREDITS file for full details.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *  
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *  
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
+ *
+ */
+
+#include "squid.h"
+#if HAVE_STATVFS
+#if HAVE_SYS_STATVFS_H
+#include <sys/statvfs.h>
+#endif
+#endif
+
+#include "store_ufs.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 *, int *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_num32 refcount,
+    u_short 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 STLOGCLEANOPEN storeUfsDirWriteCleanOpen;
+static void storeUfsDirWriteCleanClose(SwapDir * sd);
+static STLOGCLEANWRITE storeUfsDirWriteCleanEntry;
+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);
+static int storeUfsDirCheckExpired(SwapDir *, StoreEntry *);
+#if !HEAP_REPLACEMENT
+static time_t storeUfsDirExpiredReferenceAge(SwapDir *);
+#endif
+
+/*
+ * 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!
+ */
+
+int
+storeUfsDirMapBitTest(SwapDir *SD, int fn)
+{
+    sfileno filn = fn;
+    ufsinfo_t *ufsinfo;
+    ufsinfo = (ufsinfo_t *)SD->fsdata;
+    return file_map_bit_test(ufsinfo->map, filn);
+}
+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;
+    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(20, should_exist ? 3 : 1) ("%s exists\n", path);
+       } else {
+           fatalf("Swap directory %s is not a directory.", path);
+       }
+    } else if (0 == mkdir(path, 0755)) {
+       debug(20, should_exist ? 1 : 3) ("%s created\n", path);
+       created = 1;
+    } else {
+       fatalf("Failed to make swap directory %s: %s",
+           path, xstrerror());
+    }
+    return created;
+}
+
+static int
+storeUfsDirVerifyDirectory(const char *path)
+{
+    struct stat sb;
+    if (stat(path, &sb) < 0) {
+       debug(20, 0) ("%s: %s\n", path, xstrerror());
+       return -1;
+    }
+    if (S_ISDIR(sb.st_mode) == 0) {
+       debug(20, 0) ("%s is not a directory\n", path);
+       return -1;
+    }
+    return 0;
+}
+
+/*
+ * This function is called by 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);
+       while (index(pathtmp,'/'))
+           *index(pathtmp,'/')='.';
+       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);
+    if (fd < 0) {
+       debug(50, 1) ("%s: %s\n", path, xstrerror());
+       fatal("storeUfsDirOpenSwapLog: Failed to open swap log.");
+    }
+    debug(47, 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;
+    }
+}
+
+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];
+    int sfileno = 0;
+    int count;
+    int size;
+    struct stat sb;
+    int swap_hdr_len;
+    int fd = -1;
+    tlv *tlv_list;
+    tlv *t;
+    assert(rb != NULL);
+    debug(20, 3) ("storeUfsDirRebuildFromDirectory: DIR #%d\n", rb->sd->index);
+    for (count = 0; count < rb->speed; count++) {
+       assert(fd == -1);
+       fd = storeUfsDirGetNextFile(rb, &sfileno, &size);
+       if (fd == -2) {
+           debug(20, 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(20, 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(20, 3) ("  %s %7d files opened so far.\n",
+               rb->sd->path, rb->counts.scancount);
+       debug(20, 9) ("file_in: fd=%d %08X\n", fd, sfileno);
+       Counter.syscalls.disk.reads++;
+       if (read(fd, hdr_buf, SM_PAGE_SIZE) < 0) {
+           debug(20, 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(20, 1) ("storeUfsDirRebuildFromDirectory: failed to get meta data\n");
+            /* XXX shouldn't this be a call to storeUfsUnlink ? */
+           storeUfsDirUnlinkFile(SD, sfileno);
+           continue;
+       }
+       debug(20, 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(20, 1) ("storeUfsDirRebuildFromDirectory: NULL key\n");
+           storeUfsDirUnlinkFile(SD, sfileno);
+           continue;
+       }
+       tmpe.key = key;
+       /* check sizes */
+       if (tmpe.swap_file_sz == 0) {
+           tmpe.swap_file_sz = sb.st_size;
+       } else if (tmpe.swap_file_sz == sb.st_size - swap_hdr_len) {
+           tmpe.swap_file_sz = sb.st_size;
+       } else if (tmpe.swap_file_sz != sb.st_size) {
+           debug(20, 1) ("storeUfsDirRebuildFromDirectory: SIZE MISMATCH %d!=%d\n",
+               tmpe.swap_file_sz, (int) sb.st_size);
+           storeUfsDirUnlinkFile(SD, sfileno);
+           continue;
+       }
+       if (EBIT_TEST(tmpe.flags, KEY_PRIVATE)) {
+           storeUfsDirUnlinkFile(SD, sfileno);
+           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,
+           sfileno,
+           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(20, 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;
+       debug(20, 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);
+                storeUfsDirReplRemove(e);
+               if (e->swap_filen > -1) {
+                   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(20, 1) ("WARNING: %d invalid swap log entries found\n",
+                   rb->counts.bad_log_op);
+           rb->counts.invalid++;
+           continue;
+       }
+       if ((++rb->counts.scancount & 0xFFFF) == 0)
+           debug(20, 3) ("  %7d %s Entries read so far.\n",
+               rb->counts.scancount, rb->sd->path);
+       if (!storeUfsDirValidFileno(SD, s.swap_filen)) {
+           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;
+#if HEAP_REPLACEMENT
+               storeHeapPositionUpdate(e, SD);
+                storeUfsDirUnrefObj(SD, e);
+#endif
+           } else {
+               debug_trap("storeUfsDirRebuildFromSwapLog: bad condition");
+               debug(20, 1) ("\tSee %s:%d\n", __FILE__, __LINE__);
+           }
+           continue;
+       } else if (used) {
+           /* swapfile in use, not by this URL, log entry is newer */
+           /* This is sorta bad: the log entry should NOT be newer at this
+            * point.  If the log is dirty, the filesize check should have
+            * caught this.  If the log is clean, there should never be a
+            * newer entry. */
+           debug(20, 1) ("WARNING: newer swaplog entry for dirno %d, fileno %08X\n",
+               SD->index, s.swap_filen);
+           /* I'm tempted to remove the swapfile here just to be safe,
+            * but there is a bad race condition in the NOVM version if
+            * the swapfile has recently been opened for writing, but
+            * not yet opened for reading.  Because we can't map
+            * swapfiles back to StoreEntrys, we don't know the state
+            * of the entry using that file.  */
+           /* We'll assume the existing entry is valid, probably because
+            * were in a slow rebuild and the the swap file number got taken
+            * and the validation procedure hasn't run. */
+           assert(rb->flags.need_to_validate);
+           rb->counts.clashcount++;
+           continue;
+       } else if (e && !disk_entry_newer) {
+           /* key already exists, current entry is newer */
+           /* keep old, ignore new */
+           rb->counts.dupcount++;
+           continue;
+       } else if (e) {
+           /* key already exists, this swapfile not being used */
+           /* junk old, load new */
+            storeExpireNow(e);
+            storeReleaseRequest(e);
+            storeUfsDirReplRemove(e);
+           if (e->swap_filen > -1) {
+                /* 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, int *sfileno, int *size)
+{
+    SwapDir *SD = rb->sd;
+    ufsinfo_t *ufsinfo = (ufsinfo_t *)SD->fsdata;
+    int fd = -1;
+    int used = 0;
+    int dirs_opened = 0;
+    debug(20, 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 (rb->flags.init && rb->td != NULL)
+               closedir(rb->td);
+           rb->td = NULL;
+           if (dirs_opened)
+               return -1;
+           rb->td = opendir(rb->fullpath);
+           dirs_opened++;
+           if (rb->td == NULL) {
+               debug(50, 1) ("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(20, 1) ("storeUfsDirGetNextFile: directory does not exist!.\n");
+               debug(20, 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(20, 3) ("storeUfsDirGetNextFile: invalid %s\n",
+                   rb->entry->d_name);
+               continue;
+           }
+           if (!storeUfsFilenoBelongsHere(rb->fn, rb->sd->index, rb->curlvl1, rb->curlvl2)) {
+               debug(20, 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(20, 3) ("storeUfsDirGetNextFile: Locked, continuing with next.\n");
+               continue;
+           }
+           snprintf(rb->fullfilename, SQUID_MAXPATHLEN, "%s/%s",
+               rb->fullpath, rb->entry->d_name);
+           debug(20, 3) ("storeUfsDirGetNextFile: Opening %s\n", rb->fullfilename);
+           fd = file_open(rb->fullfilename, O_RDONLY);
+           if (fd < 0)
+               debug(50, 1) ("storeUfsDirGetNextFile: %s: %s\n", rb->fullfilename, xstrerror());
+           else
+               store_open_disk_fd++;
+           continue;
+       }
+       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;
+    }
+    *sfileno = 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_num32 refcount,
+    u_short flags,
+    int clean)
+{
+    StoreEntry *e = NULL;
+    debug(20, 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;
+#if !HEAP_REPLACEMENT
+    e->refcount = 0;
+#endif
+    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;
+}
+
+static void
+storeUfsDirRebuild(SwapDir * sd)
+{
+    RebuildState *rb = xcalloc(1, sizeof(*rb));
+    int clean = 0;
+    int zero = 0;
+    FILE *fp;
+    EVH *func = NULL;
+    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(20, 1) ("Rebuilding storage in %s (%s)\n",
+       sd->path, clean ? "CLEAN" : "DIRTY");
+    store_dirs_rebuilding++;
+    cbdataAdd(rb, cbdataXfree, 0);
+    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);
+#ifdef _SQUID_OS2_
+    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);
+    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);
+    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, "r");
+    if (fp == NULL) {
+       debug(50, 0) ("%s: %s\n", swaplog_path, xstrerror());
+       fatal("Failed to open swap log for reading");
+    }
+    memset(&clean_sb, '\0', sizeof(struct stat));
+    if (stat(clean_path, &clean_sb) < 0)
+       *clean_flag = 0;
+    else if (clean_sb.st_mtime < log_sb.st_mtime)
+       *clean_flag = 0;
+    else
+       *clean_flag = 1;
+    safeunlink(clean_path, 1);
+    safe_free(swaplog_path);
+    safe_free(clean_path);
+    safe_free(new_path);
+    return fp;
+}
+
+struct _clean_state {
+    char *cur;
+    char *new;
+    char *cln;
+    char *outbuf;
+    off_t outbuf_offset;
+    int fd;
+};
+
+#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
+storeUfsDirWriteCleanOpen(SwapDir * sd)
+{
+    struct _clean_state *state = xcalloc(1, sizeof(*state));
+    struct stat sb;
+    sd->log.clean.write = NULL;
+    sd->log.clean.state = NULL;
+    state->cur = xstrdup(storeUfsDirSwapLogFile(sd, NULL));
+    state->new = xstrdup(storeUfsDirSwapLogFile(sd, ".clean"));
+    state->cln = xstrdup(storeUfsDirSwapLogFile(sd, ".last-clean"));
+    state->outbuf = xcalloc(CLEAN_BUF_SZ, 1);
+    state->outbuf_offset = 0;
+    unlink(state->new);
+    unlink(state->cln);
+    state->fd = file_open(state->new, O_WRONLY | O_CREAT | O_TRUNC);
+    if (state->fd < 0)
+       return -1;
+    debug(20, 3) ("storeDirWriteCleanLogs: opened %s, FD %d\n",
+       state->new, state->fd);
+#if HAVE_FCHMOD
+    if (stat(state->cur, &sb) == 0)
+       fchmod(state->fd, sb.st_mode);
+#endif
+    sd->log.clean.write = storeUfsDirWriteCleanEntry;
+    sd->log.clean.state = state;
+    return 0;
+}
+
+/*
+ * "write" an entry to the clean log file.
+ */
+static void
+storeUfsDirWriteCleanEntry(const StoreEntry * e, SwapDir * sd)
+{
+    storeSwapLogData s;
+    static size_t ss = sizeof(storeSwapLogData);
+    struct _clean_state *state = sd->log.clean.state;
+    if (NULL == e) {
+       storeUfsDirWriteCleanClose(sd);
+       return;
+    }
+    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->key, MD5_DIGEST_CHARS);
+    xmemcpy(state->outbuf + state->outbuf_offset, &s, ss);
+    state->outbuf_offset += ss;
+    /* buffered write */
+    if (state->outbuf_offset + ss > CLEAN_BUF_SZ) {
+       if (write(state->fd, state->outbuf, state->outbuf_offset) < 0) {
+           debug(50, 0) ("storeDirWriteCleanLogs: %s: write: %s\n",
+               state->new, xstrerror());
+           debug(20, 0) ("storeDirWriteCleanLogs: Current swap logfile not replaced.\n");
+           file_close(state->fd);
+           state->fd = -1;
+           unlink(state->new);
+           safe_free(state);
+           sd->log.clean.state = NULL;
+           sd->log.clean.write = NULL;
+       }
+       state->outbuf_offset = 0;
+    }
+}
+
+static void
+storeUfsDirWriteCleanClose(SwapDir * sd)
+{
+    struct _clean_state *state = sd->log.clean.state;
+    if (state->fd < 0)
+       return;
+    if (write(state->fd, state->outbuf, state->outbuf_offset) < 0) {
+       debug(50, 0) ("storeDirWriteCleanLogs: %s: write: %s\n",
+           state->new, xstrerror());
+       debug(20, 0) ("storeDirWriteCleanLogs: Current swap logfile "
+           "not replaced.\n");
+       file_close(state->fd);
+       state->fd = -1;
+       unlink(state->new);
+    }
+    safe_free(state->outbuf);
+    /*
+     * You can't rename open files on Microsoft "operating systems"
+     * so we have to close before renaming.
+     */
+    storeUfsDirCloseSwapLog(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
+storeUfsDirSwapLog(const SwapDir * sd, const StoreEntry * e, int op)
+{
+    ufsinfo_t *ufsinfo = (ufsinfo_t *)sd->fsdata;
+    storeSwapLogData *s = xcalloc(1, sizeof(storeSwapLogData));
+    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->key, MD5_DIGEST_CHARS);
+    file_write(ufsinfo->swaplog_fd,
+       -1,
+       s,
+       sizeof(storeSwapLogData),
+       NULL,
+       NULL,
+       xfree);
+}
+
+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))
+           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
+       Counter.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)
+{
+    ufsinfo_t *ufsinfo = (ufsinfo_t *)SD->fsdata;
+    if (filn < 0)
+        return 0;
+    if (filn > ufsinfo->map->max_n_files)  
+        return 0;
+    return 1;
+}
+
+void
+storeUfsDirMaintain(SwapDir *SD)
+{
+    StoreEntry *e = NULL;
+    int scanned = 0;
+    int locked = 0;
+    int expired = 0;
+    int max_scan;
+    int max_remove;
+    double f;
+    static time_t last_warn_time = 0;
+#if !HEAP_REPLACEMENT
+    dlink_node *m;
+    dlink_node *prev = NULL;
+#else
+    heap_key age;
+    heap_key min_age = 0.0;
+    link_list *locked_entries = NULL;
+#if HEAP_REPLACEMENT_DEBUG
+    if (!verify_heap_property(SD->repl.heap.heap)) {
+        debug(20, 1) ("Heap property violated!\n");
+    }
+#endif
+#endif
+    /* We can't delete objects while rebuilding swap */
+    if (store_dirs_rebuilding) {
+        return;
+    } else {
+        f = (double) (store_swap_size - store_swap_low) / (store_swap_high - store_swap_low);
+        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?
+         */
+#if 0
+        eventAdd("MaintainSwapSpace", storeMaintainSwapSpace, NULL, 1.0 - f, 1);
+#endif
+    }
+    debug(20, 3) ("storeMaintainSwapSpace: f=%f, max_scan=%d, max_remove=%d\n", f, max_scan, max_remove);
+#if HEAP_REPLACEMENT
+    while (heap_nodes(SD->repl.heap.heap) > 0) {
+        if (store_swap_size < store_swap_low)
+            break;
+        if (expired >= max_remove)
+            break;
+        if (scanned >= max_scan)
+            break;
+        age = heap_peepminkey(SD->repl.heap.heap);
+        e = heap_extractmin(SD->repl.heap.heap);
+        e->repl.node = NULL;         /* no longer in the heap */
+        scanned++;
+        if (storeEntryLocked(e)) {
+            /*
+             * Entry is in use ... put it in a linked list to ignore it.
+             */
+            if (!EBIT_TEST(e->flags, ENTRY_SPECIAL)) {
+                /*
+                 * If this was a "SPECIAL" do not add it back into the heap.
+                 * It will always be "SPECIAL" and therefore never removed.
+                 */
+                debug(20, 4) ("storeUfsDirMaintain: locked url %s\n",
+                    (e->mem_obj && e->mem_obj->url) ? e->mem_obj->url : storeKeyText(e->
+key));
+                linklistPush(&locked_entries, e);
+            }
+            locked++;
+            continue;
+        } else if (storeUfsDirCheckExpired(SD, e)) {
+            /*
+             * Note: This will not check the reference age ifdef
+             * HEAP_REPLACEMENT, but it does some other useful
+             * checks...
+             */
+            expired++;
+            debug(20, 3) ("Released store object age %f size %d refs %d key %s\n",
+                age, e->swap_file_sz, e->refcount, storeKeyText(e->key));
+            min_age = age;
+            storeRelease(e);
+        } else {
+            /*
+             * Did not expire the object so we need to add it back
+             * into the heap!
+             */
+            debug(20, 5) ("storeMaintainSwapSpace: non-expired %s\n",
+                storeKeyText(e->key));
+            linklistPush(&locked_entries, e);
+            continue;
+        }
+        if (store_swap_size < store_swap_low)
+            break;
+        else if (expired >= max_remove)
+            break;
+        else if (scanned >= max_scan)
+            break;
+    }
+    /*
+     * Bump the heap age factor.
+     */
+    if (min_age > 0.0)
+        SD->repl.heap.heap->age = min_age;
+    /*
+     * Reinsert all bumped locked entries back into heap...
+     */
+    while ((e = linklistShift(&locked_entries)))
+        e->repl.node = heap_insert(SD->repl.heap.heap, e);
+#else
+    for (m = SD->repl.lru.list.tail; m; m = prev) {
+        prev = m->prev;
+        e = m->data;
+        scanned++;
+        if (storeEntryLocked(e)) {
+            /*
+             * If there is a locked entry at the tail of the LRU list,
+             * move it to the beginning to get it out of the way.
+             * Theoretically, we might have all locked objects at the
+             * tail, and then we'll never remove anything here and the
+             * LRU age will go to zero.
+             */
+            if (memInUse(MEM_STOREENTRY) > max_scan) {
+                dlinkDelete(&e->repl.lru, &SD->repl.lru.list);
+                dlinkAdd(e, &e->repl.lru, &SD->repl.lru.list);
+            }
+            locked++;
+
+        } else if (storeUfsDirCheckExpired(SD, e)) {
+            expired++;
+            storeRelease(e);
+        }
+        if (expired >= max_remove)
+            break;
+        if (scanned >= max_scan)
+            break;
+    }
+#endif
+    debug(20, (expired ? 2 : 3)) ("storeMaintainSwapSpace: scanned %d/%d removed %d/%d l
+ocked %d f=%.03f\n",
+        scanned, max_scan, expired, max_remove, locked, f);
+    debug(20, 3) ("storeMaintainSwapSpace stats:\n");
+    debug(20, 3) ("  %6d objects\n", memInUse(MEM_STOREENTRY));
+    debug(20, 3) ("  %6d were scanned\n", scanned);
+    debug(20, 3) ("  %6d were locked\n", locked);
+    debug(20, 3) ("  %6d were expired\n", expired);
+    if (store_swap_size < Config.Swap.maxSize)
+        return;
+    if (squid_curtime - last_warn_time < 10)
+        return;
+    debug(20, 0) ("WARNING: Disk space over limit: %d KB > %d KB\n",
+        store_swap_size, Config.Swap.maxSize);
+    last_warn_time = squid_curtime;
+}
+
+/*
+ * storeUfsDirCheckObj
+ *
+ * This routine is called by storeDirSelectSwapDir to see if the given
+ * object is able to be stored on this filesystem. UFS filesystems will
+ * happily store anything as long as the LRU time isn't too small.
+ */
+int
+storeUfsDirCheckObj(SwapDir *SD, const StoreEntry *e)
+{
+#if !HEAP_REPLACEMENT
+    if (storeUfsDirExpiredReferenceAge(SD) < 300) {
+        debug(20, 3) ("storeUfsDirCheckObj: NO: LRU Age = %d\n",
+            storeUfsDirExpiredReferenceAge(SD));
+        /* store_check_cachable_hist.no.lru_age_too_low++; */
+        return -1;
+    }
+#endif
+    /* Return 999 (99.9%) constant load */
+    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(1, 3) ("storeUfsDirRefObj: referencing %p %d/%d\n", e, e->swap_dirn,
+      e->swap_filen);
+#if HEAP_REPLACEMENT
+    /* Nothing to do here */
+#else
+    /* Reference the object */
+    if (!EBIT_TEST(e->flags, RELEASE_REQUEST) &&
+        !EBIT_TEST(e->flags, ENTRY_SPECIAL)) {
+        dlinkDelete(&e->repl.lru, &SD->repl.lru.list);
+        dlinkAdd(e, &e->repl.lru, &SD->repl.lru.list);
+    }
+#endif
+}
+
+/*
+ * 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)
+{
+    debug(1, 3) ("storeUfsDirUnrefObj: referencing %p %d/%d\n", e, e->swap_dirn,
+      e->swap_filen);
+#if HEAP_REPLACEMENT
+    if (e->repl.node)
+        heap_update(SD->repl.heap.heap, e->repl.node, e);
+#endif
+}
+
+/*
+ * 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);
+    unlinkdUnlink(storeUfsDirFullPath(SD, f, NULL));
+}
+
+#if !HEAP_REPLACEMENT
+/*
+ * storeUfsDirExpiredReferenceAge
+ *
+ * The LRU age is scaled exponentially between 1 minute and
+ * Config.referenceAge , when store_swap_low < store_swap_size <
+ * store_swap_high.  This keeps store_swap_size within the low and high
+ * water marks.  If the cache is very busy then store_swap_size stays
+ * closer to the low water mark, if it is not busy, then it will stay
+ * near the high water mark.  The LRU age value can be examined on the
+ * cachemgr 'info' page.
+ */
+static time_t
+storeUfsDirExpiredReferenceAge(SwapDir *SD)
+{
+    double x;
+    double z;
+    time_t age;
+    long store_high, store_low;
+    store_high = (long) (((float) SD->max_size *
+            (float) Config.Swap.highWaterMark) / (float) 100);
+    store_low = (long) (((float) SD->max_size *
+            (float) Config.Swap.lowWaterMark) / (float) 100);
+    debug(20, 20) ("RA: Dir %s, hi=%d, lo=%d, cur=%d\n", SD->path, store_high, store_low, SD->cur_size);
+  
+    x = (double) (store_high - SD->cur_size) /
+        (store_high - store_low);
+    x = x < 0.0 ? 0.0 : x > 1.0 ? 1.0 : x;
+    z = pow((double) (Config.referenceAge / 60), x);
+    age = (time_t) (z * 60.0);
+    if (age < 60)
+        age = 60;
+    else if (age > Config.referenceAge)
+        age = Config.referenceAge;
+    return age;
+}
+#endif
+
+/*
+ * storeUfsDirCheckExpired
+ *
+ * Check whether the given object is expired or not
+ * It breaks layering a little by calling the upper layers to find
+ * out whether the object is locked or not, but we can't help this
+ * right now.
+ */
+static int
+storeUfsDirCheckExpired(SwapDir *SD, StoreEntry *e)
+{
+    if (storeEntryLocked(e))
+       return 0;
+    if (EBIT_TEST(e->flags, RELEASE_REQUEST))
+       return 1;
+    if (EBIT_TEST(e->flags, ENTRY_NEGCACHED) && squid_curtime >= e->expires)
+       return 1;
+
+#if HEAP_REPLACEMENT
+    /*
+     * with HEAP_REPLACEMENT we are not using the LRU reference age, the heap
+     * controls the replacement of objects.
+     */
+    return 1;
+#else
+    if (squid_curtime - e->lastref > storeUfsDirExpiredReferenceAge(SD))
+       return 1;
+    return 0;
+#endif
+}
+
+/*
+ * Add and remove the given StoreEntry from the replacement policy in
+ * use.
+ */
+
+void
+storeUfsDirReplAdd(SwapDir *SD, StoreEntry *e)
+{
+    debug(20, 4) ("storeUfsDirReplAdd: added node %p to dir %d\n", e,
+      SD->index);
+#if HEAP_REPLACEMENT
+    if (EBIT_TEST(e->flags, ENTRY_SPECIAL)) {
+        (void) 0;
+    } else {
+        e->repl.node = heap_insert(SD->repl.heap.heap, e);
+        debug(20, 4) ("storeUfsDirReplAdd: inserted node 0x%x\n", e->repl.node);
+    }
+#else
+    /* Shouldn't we not throw special objects into the lru ? */
+    dlinkAdd(e, &e->repl.lru, &SD->repl.lru.list);
+#endif
+}
+
+
+void
+storeUfsDirReplRemove(StoreEntry *e)
+{
+    SwapDir *SD = INDEXSD(e->swap_dirn);
+    debug(20, 4) ("storeUfsDirReplRemove: remove node %p from dir %d\n", e,
+      SD->index);
+#if HEAP_REPLACEMENT
+    /* And now, release the object from the replacement policy */
+    if (e->repl.node) {
+        debug(20, 4) ("storeUfsDirReplRemove: deleting node 0x%x\n",
+          e->repl.node);
+        heap_delete(SD->repl.heap.heap, e->repl.node);
+        e->repl.node = NULL;
+    }
+#else
+    dlinkDelete(&e->repl.lru, &SD->repl.lru.list);
+#endif
+}
+
+
+
+/* ========== LOCAL FUNCTIONS ABOVE, GLOBAL FUNCTIONS BELOW ========== */
+
+void
+storeUfsDirStats(SwapDir *SD, StoreEntry * sentry)
+{
+    ufsinfo_t *ufsinfo;
+#if HAVE_STATVFS
+    struct statvfs sfs;
+#endif
+    ufsinfo = (ufsinfo_t *)SD->fsdata;
+    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));
+#if HAVE_STATVFS
+#define fsbtoblk(num, fsbs, bs) \
+    (((fsbs) != 0 && (fsbs) < (bs)) ? \
+            (num) / ((bs) / (fsbs)) : (num) * ((fsbs) / (bs)))
+       if (!statvfs(SD->path, &sfs)) {
+            storeAppendPrintf(sentry, "Filesystem Space in use: %d/%d KB (%d%%)\n",
+            fsbtoblk((sfs.f_blocks - sfs.f_bfree), sfs.f_frsize, 1024),
+            fsbtoblk(sfs.f_blocks, sfs.f_frsize, 1024),
+            percent(sfs.f_blocks - sfs.f_bfree, sfs.f_blocks));
+            storeAppendPrintf(sentry, "Filesystem Inodes in use: %d/%d (%d%%)\n",
+            sfs.f_files - sfs.f_ffree, sfs.f_files,
+            percent(sfs.f_files - sfs.f_ffree, sfs.f_files));
+    }
+#endif
+    storeAppendPrintf(sentry, "Flags:");
+    if (SD->flags.selected)
+        storeAppendPrintf(sentry, " SELECTED");
+    if (SD->flags.read_only)
+        storeAppendPrintf(sentry, " READ-ONLY");
+    storeAppendPrintf(sentry, "\n");
+#if !HEAP_REPLACEMENT
+    storeAppendPrintf(sentry, "LRU Expiration Age: %6.2f days\n",
+      (double) storeUfsDirExpiredReferenceAge(SD) / 86400.0); 
+#else
+#if 0
+    storeAppendPrintf(sentry, "Storage Replacement Threshold:\t%f\n",
+        heap_peepminkey(sd.repl.heap.heap));
+#endif
+#endif
+}
+
+/*
+ * storeUfsDirReconfigure
+ *
+ * This routine is called when the given swapdir needs reconfiguring 
+ */
+void
+storeUfsDirReconfigure(SwapDir *sd, int index, char *path)
+{
+    char *token;
+    int i;
+    int size;
+    int l1;
+    int l2;
+    unsigned int read_only = 0;
+
+    i = GetInteger();
+    size = i << 10;            /* Mbytes to kbytes */
+    if (size <= 0)
+       fatal("storeUfsDirReconfigure: invalid size value");
+    i = GetInteger();
+    l1 = i;
+    if (l1 <= 0)
+       fatal("storeUfsDirReconfigure: invalid level 1 directories value");
+    i = GetInteger();
+    l2 = i;
+    if (l2 <= 0)
+       fatal("storeUfsDirReconfigure: invalid level 2 directories value");
+    if ((token = strtok(NULL, w_space)))
+       if (!strcasecmp(token, "read-only"))
+           read_only = 1;
+
+    /* 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;
+    if (sd->flags.read_only != read_only)
+       debug(3, 1) ("Cache dir '%s' now %s\n",
+           path, read_only ? "Read-Only" : "Read-Write");
+    sd->flags.read_only = read_only;
+    return;
+}
+
+void
+storeUfsDirDump(StoreEntry * entry, const char *name, SwapDir * s)
+{
+    ufsinfo_t *ufsinfo = (ufsinfo_t *)s->fsdata;
+    storeAppendPrintf(entry, "%s %s %s %d %d %d\n",
+       name,
+        "ufs",
+       s->path,
+       s->max_size >> 10,
+       ufsinfo->l1,
+       ufsinfo->l2);
+}
+
+/*
+ * 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(20, 0) ("storeUfsCleanupDoubleCheck: MISSING SWAP FILE\n");
+        debug(20, 0) ("storeUfsCleanupDoubleCheck: FILENO %08X\n", e->swap_filen);
+        debug(20, 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(20, 0) ("storeUfsCleanupDoubleCheck: SIZE MISMATCH\n");
+        debug(20, 0) ("storeUfsCleanupDoubleCheck: FILENO %08X\n", e->swap_filen);
+        debug(20, 0) ("storeUfsCleanupDoubleCheck: PATH %s\n",
+            storeUfsDirFullPath(sd, e->swap_filen, NULL));
+        debug(20, 0) ("storeUfsCleanupDoubleCheck: ENTRY SIZE: %d, FILE SIZE: %d\n",
+            e->swap_file_sz, (int) sb.st_size); 
+        storeEntryDump(e, 0); 
+        return -1;
+    }       
+    return 0;
+}
+
+/*
+ * storeUfsDirParse
+ *
+ * Called when a *new* fs is being setup.
+ */
+void
+storeUfsDirParse(SwapDir *sd, int index, char *path)
+{
+    char *token;
+    int i;
+    int size;
+    int l1;
+    int l2;
+    unsigned int read_only = 0;
+    ufsinfo_t *ufsinfo;
+
+    i = GetInteger();
+    size = i << 10;            /* Mbytes to kbytes */
+    if (size <= 0)
+       fatal("storeUfsDirParse: invalid size value");
+    i = GetInteger();
+    l1 = i;
+    if (l1 <= 0)
+       fatal("storeUfsDirParse: invalid level 1 directories value");
+    i = GetInteger();
+    l2 = i;
+    if (l2 <= 0)
+       fatal("storeUfsDirParse: invalid level 2 directories value");
+    if ((token = strtok(NULL, w_space)))
+       if (!strcasecmp(token, "read-only"))
+           read_only = 1;
+
+    ufsinfo = xmalloc(sizeof(ufsinfo_t));
+    if (ufsinfo == NULL)
+        fatal("storeUfsDirParse: couldn't xmalloc() ufsinfo_t!\n");
+
+    sd->index = index;
+    sd->path = xstrdup(path);
+    sd->max_size = size;
+    sd->fsdata = ufsinfo;
+    ufsinfo->l1 = l1;
+    ufsinfo->l2 = l2;
+    ufsinfo->swaplog_fd = -1;
+    ufsinfo->map = NULL; /* Debugging purposes */
+    ufsinfo->suggest = 0;
+    sd->flags.read_only = read_only;
+    sd->init = storeUfsDirInit;
+    sd->newfs = storeUfsDirNewfs;
+    sd->dump = storeUfsDirDump;
+    sd->freefs = storeUfsDirFree;
+    sd->dblcheck = storeUfsCleanupDoubleCheck;
+    sd->statfs = storeUfsDirStats;
+    sd->maintainfs = storeUfsDirMaintain;
+    sd->checkobj = storeUfsDirCheckObj;
+    sd->refobj = storeUfsDirRefObj;
+    sd->unrefobj = storeUfsDirUnrefObj;
+    sd->callback = NULL;
+    sd->sync = NULL;
+    sd->obj.create = storeUfsCreate;
+    sd->obj.open = storeUfsOpen;
+    sd->obj.close = storeUfsClose;
+    sd->obj.read = storeUfsRead;
+    sd->obj.write = storeUfsWrite;
+    sd->obj.unlink = storeUfsUnlink;
+    sd->log.open = storeUfsDirOpenSwapLog;
+    sd->log.close = storeUfsDirCloseSwapLog;
+    sd->log.write = storeUfsDirSwapLog;
+    sd->log.clean.open = storeUfsDirWriteCleanOpen;
+
+    /* Initialise replacement policy stuff */
+#if HEAP_REPLACEMENT
+    /*
+     * Create new heaps with cache replacement policies attached to them.
+     * The cache replacement policy is specified as either GDSF or LFUDA in
+     * the squid.conf configuration file.  Note that the replacement policy
+     * applies only to the disk replacement algorithm.  Memory replacement
+     * always uses GDSF since we want to maximize object hit rate.
+     */
+    if (Config.replPolicy) {
+        if (tolower(Config.replPolicy[0]) == 'g') {
+            debug(20, 1) ("Using GDSF disk replacement policy\n");
+            sd->repl.heap.heap = new_heap(10000, HeapKeyGen_StoreEntry_GDSF);
+        } else if (tolower(Config.replPolicy[0]) == 'l') {
+            if (tolower(Config.replPolicy[1]) == 'f') {
+                debug(20, 1) ("Using LFUDA disk replacement policy\n");
+                sd->repl.heap.heap = new_heap(10000, HeapKeyGen_StoreEntry_LFUDA);
+            } else if (tolower(Config.replPolicy[1]) == 'r') {
+                debug(20, 1) ("Using LRU heap disk replacement policy\n");
+                sd->repl.heap.heap = new_heap(10000, HeapKeyGen_StoreEntry_LRU);
+            }
+        } else {
+            debug(20, 1) ("Unrecognized replacement_policy; using GDSF\n");
+            sd->repl.heap.heap = new_heap(10000, HeapKeyGen_StoreEntry_GDSF);
+        }
+    } else {
+        debug(20, 1) ("Using default disk replacement policy (GDSF)\n");
+        sd->repl.heap.heap = new_heap(10000, HeapKeyGen_StoreEntry_GDSF);
+    }
+#else
+    sd->repl.lru.list.head = NULL;
+    sd->repl.lru.list.tail = NULL;
+#endif
+}
+
+/*
+ * Initial setup / end destruction
+ */
+void
+storeUfsDirDone(void)
+{
+    memPoolDestroy(ufs_state_pool);
+    ufs_initialised = 0;
+}
+
+void
+storeFsSetup_ufs(storefs_entry_t *storefs)
+{
+    assert(!ufs_initialised);
+    storefs->parsefunc = storeUfsDirParse;
+    storefs->reconfigurefunc = storeUfsDirReconfigure;
+    storefs->donefunc = storeUfsDirDone;
+    ufs_state_pool = memPoolCreate("UFS IO State data", sizeof(ufsstate_t));
+    ufs_initialised = 1;
+}
+
diff --git a/src/fs/ufs/store_io_ufs.cc b/src/fs/ufs/store_io_ufs.cc
new file mode 100644 (file)
index 0000000..3e241b9
--- /dev/null
@@ -0,0 +1,267 @@
+
+/*
+ * $Id: store_io_ufs.cc,v 1.1 2000/05/03 17:15:48 adrian Exp $
+ *
+ * DEBUG: section 79    Storage Manager UFS Interface
+ * AUTHOR: Duane Wessels
+ *
+ * SQUID Internet Object Cache  http://squid.nlanr.net/Squid/
+ * ----------------------------------------------------------
+ *
+ *  Squid is the result of efforts by numerous individuals from the
+ *  Internet community.  Development is led by Duane Wessels of the
+ *  National Laboratory for Applied Network Research and funded by the
+ *  National Science Foundation.  Squid is Copyrighted (C) 1998 by
+ *  Duane Wessels and the University of California San Diego.  Please
+ *  see the COPYRIGHT file for full details.  Squid incorporates
+ *  software developed and/or copyrighted by other sources.  Please see
+ *  the CREDITS file for full details.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
+ *
+ */
+
+#include "squid.h"
+#include "store_ufs.h"
+
+
+static DRCB storeUfsReadDone;
+static DWCB storeUfsWriteDone;
+static void storeUfsIOCallback(storeIOState * sio, int errflag);
+static void storeUfsIOFreeEntry(void *, int);
+
+/* === PUBLIC =========================================================== */
+
+storeIOState *
+storeUfsOpen(SwapDir *SD, StoreEntry *e, STFNCB * file_callback, 
+  STIOCB * callback, void *callback_data)
+{
+    sfileno f = e->swap_filen;
+    char *path = storeUfsDirFullPath(SD, f, NULL);
+    storeIOState *sio;
+    struct stat sb;
+    int fd;
+    debug(79, 3) ("storeUfsOpen: fileno %08X\n", f);
+    fd = file_open(path, O_RDONLY);
+    if (fd < 0) {
+       debug(79, 3) ("storeUfsOpen: got failure (%d)\n", errno);
+       return NULL;
+    }
+    debug(79, 3) ("storeUfsOpen: opened FD %d\n", fd);
+    sio = memAllocate(MEM_STORE_IO);
+    cbdataAdd(sio, storeUfsIOFreeEntry, MEM_STORE_IO);
+    sio->fsstate = memPoolAlloc(ufs_state_pool);
+
+    sio->swap_filen = f;
+    sio->swap_dirn = SD->index;
+    sio->mode = O_RDONLY;
+    sio->callback = callback;
+    sio->callback_data = callback_data;
+    cbdataLock(callback_data);
+    sio->e = e;
+    ((ufsstate_t *)(sio->fsstate))->fd = fd;
+    ((ufsstate_t *)(sio->fsstate))->flags.writing = 0;
+    ((ufsstate_t *)(sio->fsstate))->flags.reading = 0;
+    ((ufsstate_t *)(sio->fsstate))->flags.close_request = 0;
+    if (fstat(fd, &sb) == 0)
+        sio->st_size = sb.st_size;
+    store_open_disk_fd++;
+
+    /* We should update the heap/dlink position here ! */
+    return sio;
+}
+
+storeIOState *
+storeUfsCreate(SwapDir *SD, StoreEntry *e, STFNCB *file_callback, STIOCB *callback, void *callback_data)
+{
+    storeIOState *sio;
+    int fd;
+    int mode = (O_WRONLY | O_CREAT | O_TRUNC);
+    char *path;
+    ufsinfo_t *ufsinfo = (ufsinfo_t *)SD->fsdata;
+    sfileno filn;
+    sdirno dirn;
+
+    /* Allocate a number */
+    dirn = SD->index;
+    filn = storeUfsDirMapBitAllocate(SD);
+    ufsinfo->suggest = filn + 1;
+    /* Shouldn't we handle a 'bitmap full' error here? */
+    path = storeUfsDirFullPath(SD, filn, NULL);
+
+    debug(79, 3) ("storeUfsCreate: fileno %08X\n", filn);
+    fd = file_open(path, mode);
+    if (fd < 0) {
+        debug(79, 3) ("storeUfsCreate: got failure (%d)\n", errno);
+        return NULL;
+    }
+    debug(79, 3) ("storeUfsCreate: opened FD %d\n", fd);
+    sio = memAllocate(MEM_STORE_IO);
+    cbdataAdd(sio, storeUfsIOFreeEntry, MEM_STORE_IO);
+    sio->fsstate = memPoolAlloc(ufs_state_pool);
+
+    sio->swap_filen = filn;
+    sio->swap_dirn = dirn;
+    sio->mode = mode;
+    sio->callback = callback;
+    sio->callback_data = callback_data;
+    cbdataLock(callback_data);
+    sio->e = (StoreEntry *) e;
+    ((ufsstate_t *)(sio->fsstate))->fd = fd;
+    ((ufsstate_t *)(sio->fsstate))->flags.writing = 0;
+    ((ufsstate_t *)(sio->fsstate))->flags.reading = 0;
+    ((ufsstate_t *)(sio->fsstate))->flags.close_request = 0;
+    store_open_disk_fd++;
+
+    /* now insert into the replacement policy */
+    storeUfsDirReplAdd(SD, e);
+    return sio;
+}
+
+void
+storeUfsClose(SwapDir *SD, storeIOState * sio)
+{
+    ufsstate_t *ufsstate = (ufsstate_t *)sio->fsstate;
+
+    debug(79, 3) ("storeUfsClose: dirno %d, fileno %08X, FD %d\n",
+       sio->swap_dirn, sio->swap_filen, ufsstate->fd);
+    if (ufsstate->flags.reading || ufsstate->flags.writing) {
+       ufsstate->flags.close_request = 1;
+       return;
+    }
+    storeUfsIOCallback(sio, 0);
+}
+
+void
+storeUfsRead(SwapDir *SD, storeIOState * sio, char *buf, size_t size, off_t offset, STRCB * callback, void *callback_data)
+{
+    ufsstate_t *ufsstate = (ufsstate_t *)sio->fsstate;
+
+    assert(sio->read.callback == NULL);
+    assert(sio->read.callback_data == NULL);
+    sio->read.callback = callback;
+    sio->read.callback_data = callback_data;
+    cbdataLock(callback_data);
+    debug(79, 3) ("storeUfsRead: dirno %d, fileno %08X, FD %d\n",
+       sio->swap_dirn, sio->swap_filen, ufsstate->fd);
+    sio->offset = offset;
+    ufsstate->flags.reading = 1;
+    file_read(ufsstate->fd,
+       buf,
+       size,
+       offset,
+       storeUfsReadDone,
+       sio);
+}
+
+void
+storeUfsWrite(SwapDir *SD, storeIOState * sio, char *buf, size_t size, off_t offset, FREE * free_func)
+{
+    ufsstate_t *ufsstate = (ufsstate_t *)sio->fsstate;
+    debug(79, 3) ("storeUfsWrite: dirn %d, fileno %08X, FD %d\n", sio->swap_dirn, sio->swap_filen, ufsstate->fd);
+    ufsstate->flags.writing = 1;
+    file_write(ufsstate->fd,
+       offset,
+       buf,
+       size,
+       storeUfsWriteDone,
+       sio,
+       free_func);
+}
+
+void
+storeUfsUnlink(SwapDir *SD, StoreEntry *e)
+{
+    debug(79, 3) ("storeUfsUnlink: fileno %08X\n", e->swap_filen);
+    storeUfsDirReplRemove(e);
+    storeUfsDirUnlinkFile(SD, e->swap_filen);
+}
+
+/*  === STATIC =========================================================== */
+
+static void
+storeUfsReadDone(int fd, const char *buf, int len, int errflag, void *my_data)
+{
+    storeIOState *sio = my_data;
+    ufsstate_t *ufsstate = (ufsstate_t *)sio->fsstate;
+    STRCB *callback = sio->read.callback;
+    void *their_data = sio->read.callback_data;
+    ssize_t rlen;
+
+    debug(79, 3) ("storeUfsReadDone: dirno %d, fileno %08X, FD %d, len %d\n",
+       sio->swap_dirn, sio->swap_filen, fd, len);
+    ufsstate->flags.reading = 0;
+    if (errflag) {
+       debug(79, 3) ("storeUfsReadDone: got failure (%d)\n", errflag);
+        rlen = -1;
+    } else {
+        rlen = (ssize_t) len;
+        sio->offset += len;
+    }
+    assert(callback);
+    assert(their_data);
+    sio->read.callback = NULL;
+    sio->read.callback_data = NULL;
+    if (cbdataValid(their_data))
+       callback(their_data, buf, (size_t) rlen);
+    cbdataUnlock(their_data);
+}
+
+static void
+storeUfsWriteDone(int fd, int errflag, size_t len, void *my_data)
+{
+    storeIOState *sio = my_data;
+    ufsstate_t *ufsstate = (ufsstate_t *)sio->fsstate;
+    debug(79, 3) ("storeUfsWriteDone: dirno %d, fileno %08X, FD %d, len %d\n",
+       sio->swap_dirn, sio->swap_filen, fd, len);
+    ufsstate->flags.writing = 0;
+    if (errflag) {
+       debug(79, 0) ("storeUfsWriteDone: got failure (%d)\n", errflag);
+       storeUfsIOCallback(sio, errflag);
+       return;
+    }
+    sio->offset += len;
+    if (ufsstate->flags.close_request)
+       storeUfsIOCallback(sio, errflag);
+}
+
+static void
+storeUfsIOCallback(storeIOState * sio, int errflag)
+{
+    ufsstate_t *ufsstate = (ufsstate_t *)sio->fsstate;
+    debug(79, 3) ("storeUfsIOCallback: errflag=%d\n", errflag);
+    if (ufsstate->fd > -1) {
+       file_close(ufsstate->fd);
+       store_open_disk_fd--;
+    }
+    if (cbdataValid(sio->callback_data))
+       sio->callback(sio->callback_data, errflag, sio);
+    cbdataUnlock(sio->callback_data);
+    sio->callback_data = NULL;
+    sio->callback = NULL;
+    cbdataFree(sio);
+}
+
+
+/*
+ * We can't pass memFree() as a free function here, because we need to free
+ * the fsstate variable ..
+ */
+static void
+storeUfsIOFreeEntry(void *sio, int foo)
+{
+    memPoolFree(ufs_state_pool, ((storeIOState *)sio)->fsstate);
+    memFree(sio, MEM_STORE_IO);
+}
diff --git a/src/fs/ufs/store_ufs.h b/src/fs/ufs/store_ufs.h
new file mode 100644 (file)
index 0000000..94ab590
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * store_ufs.h
+ *
+ * Internal declarations for the ufs routines
+ */
+
+#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 {
+       unsigned int close_request:1;
+       unsigned int reading:1;
+       unsigned int writing:1;
+    } 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
+ */
+extern STOBJCREATE storeUfsCreate;
+extern STOBJOPEN storeUfsOpen;
+extern STOBJCLOSE storeUfsClose;
+extern STOBJREAD storeUfsRead;
+extern STOBJWRITE storeUfsWrite;
+extern STOBJUNLINK storeUfsUnlink;
+
+#endif
index 9dc83cadfda8bdfe465c3d1c324bc94e6c1fbc82..f5765564f62e3d790c47649cabf6b09b124eaa27 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * $Id: globals.h,v 1.87 2000/03/06 16:23:31 wessels Exp $
+ * $Id: globals.h,v 1.88 2000/05/03 17:15:42 adrian Exp $
  *
  *
  * SQUID Internet Object Cache  http://squid.nlanr.net/Squid/
@@ -126,10 +126,8 @@ extern double current_dtime;
 extern int store_hash_buckets; /* 0 */
 extern hash_table *store_table;        /* NULL */
 #if HEAP_REPLACEMENT
-extern heap *store_heap;
 extern heap *inmem_heap;
 #else
-extern dlink_list store_list;
 #endif
 extern dlink_list ClientActiveRequests;
 extern const String StringNull;        /* { 0, 0, NULL } */
@@ -151,3 +149,8 @@ extern int refresh_nocache_hack;    /* 0 */
 extern request_flags null_request_flags;
 extern int store_open_disk_fd; /* 0 */
 extern const char *SwapDirType[];
+extern storefs_entry_t *storefs_list; /* NULL */
+extern int store_swap_low;
+extern int store_swap_high;
+extern int store_pages_max;
+extern size_t store_maxobjsize;
index 9140b7d45a847bc89115ca5f05e4c62e53806786..314d3fb55b72117302f9cfef42baa7c75a898400 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * $Id: main.cc,v 1.309 2000/05/02 20:13:58 hno Exp $
+ * $Id: main.cc,v 1.310 2000/05/03 17:15:42 adrian Exp $
  *
  * DEBUG: section 1     Startup and Main Loop
  * AUTHOR: Harvest Derived
@@ -101,6 +101,7 @@ usage(void)
        "       -F        Don't serve any requests until store is rebuilt.\n"
        "       -N        No daemon mode.\n"
        "       -R        Do not set REUSEADDR on port.\n"
+       "       -S        Double-check swap during rebuild.\n"
        "       -V        Virtual host httpd-accelerator.\n"
        "       -X        Force full debugging.\n"
        "       -Y        Only return UDP_HIT or UDP_MISS_NOFETCH during fast reload.\n",
@@ -493,9 +494,6 @@ mainInitialize(void)
 #endif
 
     if (!configured_once) {
-#if USE_ASYNC_IO
-       aioInit();
-#endif
        unlinkdInit();
        urlInitialize();
        cachemgrInit();
@@ -618,6 +616,7 @@ main(int argc, char **argv)
 #endif
        memInit();              /* memInit is required for config parsing */
        eventInit();            /* eventInit() is required for config parsing */
+       storeFsInit();          /* required for config parsing */
        parse_err = parseConfigFile(ConfigFile);
 
        if (opt_parse_cfg_only)
@@ -907,21 +906,16 @@ SquidShutdown(void *unused)
     releaseServerSockets();
     commCloseAllSockets();
     unlinkdClose();
-#if USE_ASYNC_IO
-    aioSync();                 /* flush pending object writes / unlinks */
-#endif
+    storeDirSync();            /* Flush pending object writes/unlinks */
     storeDirWriteCleanLogs(0);
     PrintRusage();
     dumpMallocStats();
-#if USE_ASYNC_IO
-    aioSync();                 /* flush log writes */
-#endif
+    storeDirSync();            /* Flush log writes */
     storeLogClose();
     accessLogClose();
-#if USE_ASYNC_IO
-    aioSync();                 /* flush log close */
-#endif
+    storeDirSync();            /* Flush log close */
 #if PURIFY || XMALLOC_TRACE
+    storeFsDone();
     configFreeMemory();
     storeFreeMemory();
     /*stmemFreeMemory(); */
index 051c9b6f5144ec598fa6a8e090f1e4f79eac40bc..8827758ae2fcc056ee06d36d46b48d6aa25ae758 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * $Id: mem.cc,v 1.44 2000/05/02 20:58:30 hno Exp $
+ * $Id: mem.cc,v 1.45 2000/05/03 17:15:42 adrian Exp $
  *
  * DEBUG: section 13    High Level Memory Pool Management
  * AUTHOR: Harvest Derived
@@ -203,7 +203,6 @@ memInit(void)
     memDataInit(MEM_ACL_TIME_DATA, "acl_time_data", sizeof(acl_time_data), 0);
     memDataInit(MEM_ACL_PROXY_AUTH_USER, "acl_proxy_auth_user",
        sizeof(acl_proxy_auth_user), 0);
-    memDataInit(MEM_AIO_RESULT_T, "aio_result_t", sizeof(aio_result_t), 0);
     memDataInit(MEM_CACHEMGR_PASSWD, "cachemgr_passwd",
        sizeof(cachemgr_passwd), 0);
 #if USE_CACHE_DIGESTS
@@ -218,7 +217,6 @@ memInit(void)
 #if USE_CACHE_DIGESTS
     memDataInit(MEM_DIGEST_FETCH_STATE, "DigestFetchState", sizeof(DigestFetchState), 0);
 #endif
-    memDataInit(MEM_DISK_BUF, "Disk I/O Buffer", DISK_PAGE_SIZE, 200);
     memDataInit(MEM_DLINK_LIST, "dlink_list", sizeof(dlink_list), 10);
     memDataInit(MEM_DLINK_NODE, "dlink_node", sizeof(dlink_node), 10);
     memDataInit(MEM_DNSSERVER_T, "dnsserver_t", sizeof(dnsserver_t), 0);
@@ -352,8 +350,3 @@ memFree8K(void *p)
     memFree(p, MEM_8K_BUF);
 }
 
-void
-memFreeDISK(void *p)
-{
-    memFree(p, MEM_DISK_BUF);
-}
index 65732597469ab73e29054f5a1d73e7ea8ee02352..9d06e61fd4085609e40fc20a585ee48bbcd7bd1e 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * $Id: protos.h,v 1.362 2000/05/02 20:41:23 hno Exp $
+ * $Id: protos.h,v 1.363 2000/05/03 17:15:42 adrian Exp $
  *
  *
  * SQUID Internet Object Cache  http://squid.nlanr.net/Squid/
@@ -68,34 +68,6 @@ extern const char *aclTypeToStr(squid_acl);
 extern wordlist *aclDumpGeneric(const acl *);
 extern int aclPurgeMethodInUse(acl_access *);
 
-#if USE_ASYNC_IO
-extern int aio_cancel(aio_result_t *);
-extern int aio_open(const char *, int, mode_t, aio_result_t *);
-extern int aio_read(int, char *, int, off_t, int, aio_result_t *);
-extern int aio_write(int, char *, int, off_t, int, aio_result_t *);
-extern int aio_close(int, aio_result_t *);
-extern int aio_stat(const char *, struct stat *, aio_result_t *);
-extern int aio_unlink(const char *, aio_result_t *);
-extern int aio_opendir(const char *, aio_result_t *);
-extern aio_result_t *aio_poll_done(void);
-extern int aio_operations_pending(void);
-extern int aio_overloaded(void);
-extern int aio_sync(void);
-extern int aio_get_queue_len(void);
-
-extern void aioInit(void);
-extern void aioCancel(int);
-extern void aioOpen(const char *, int, mode_t, AIOCB *, void *);
-extern void aioClose(int);
-extern void aioWrite(int, int offset, char *, int size, AIOCB *, void *, FREE *);
-extern void aioRead(int, int offset, char *, int size, AIOCB *, void *);
-extern void aioStat(char *, struct stat *, AIOCB *, void *);
-extern void aioUnlink(const char *, AIOCB *, void *);
-extern void aioCheckCallbacks(void);
-extern void aioSync(void);
-extern int aioQueueSize(void);
-#endif
-
 /*
  * cache_cf.c
  */
@@ -190,6 +162,7 @@ extern int comm_select(int);
 #endif
 extern void commUpdateReadBits(int, PF *);
 extern void commUpdateWriteBits(int, PF *);
+extern void comm_quick_poll_required();
 
 extern void packerToStoreInit(Packer * p, StoreEntry * e);
 extern void packerToMemInit(Packer * p, MemBuf * mb);
@@ -219,7 +192,7 @@ extern void xassert(const char *, const char *, int);
 /* packs, then prints an object using debug() */
 extern void debugObj(int section, int level, const char *label, void *obj, ObjPackMethod pm);
 
-
+/* disk.c */
 extern int file_open(const char *path, int mode);
 extern void file_close(int fd);
 extern void file_write(int, off_t, void *, int len, DWCB *, void *, FREE *);
@@ -227,6 +200,19 @@ extern void file_write_mbuf(int fd, off_t, MemBuf mb, DWCB * handler, void *hand
 extern void file_read(int, char *, int, off_t, DRCB *, void *);
 extern void disk_init(void);
 
+/* diskd.c */
+extern diskd_queue * afile_create_queue(void);
+extern void afile_destroy_queue(diskd_queue *);
+extern void afile_sync_queue(diskd_queue *);
+extern void afile_sync(void);
+extern void afile_open(const char *path, int mode, DOCB *, void *);
+extern void afile_close(int fd, DCCB *callback, void *data);
+extern void afile_write(int, off_t, void *, int len, DWCB *, void *, FREE *);
+extern void afile_write_mbuf(int fd, off_t, MemBuf, DWCB *, void *);
+extern void afile_read(int, char *, int, off_t, DRCB *, void *);
+extern void afile_unlink(const char *path, DUCB *, void *);
+extern void afile_truncate(const char *path, DTCB *, void *);
+
 extern void dnsShutdown(void);
 extern void dnsInit(void);
 extern void dnsSubmit(const char *lookup, HLPCB * callback, void *data);
@@ -740,7 +726,7 @@ extern double statRequestHitRatio(int minutes);
 extern double statRequestHitMemoryRatio(int minutes);
 extern double statRequestHitDiskRatio(int minutes);
 extern double statByteHitRatio(int minutes);
-
+extern int storeEntryLocked(const StoreEntry *);
 
 
 /* StatHist */
@@ -778,7 +764,6 @@ extern void memFreeBuf(size_t size, void *);
 extern void memFree2K(void *);
 extern void memFree4K(void *);
 extern void memFree8K(void *);
-extern void memFreeDISK(void *);
 extern int memInUse(mem_type);
 extern size_t memTotalAllocated(void);
 extern void memDataInit(mem_type, const char *, size_t, int);
@@ -843,9 +828,6 @@ extern int storeClientCopyPending(StoreEntry *, void *);
 extern void InvokeHandlers(StoreEntry *);
 extern int storeEntryValidToSend(StoreEntry *);
 extern void storeTimestampsSet(StoreEntry *);
-#if !HEAP_REPLACEMENT
-extern time_t storeExpiredReferenceAge(void);
-#endif
 extern void storeRegisterAbort(StoreEntry * e, STABH * cb, void *);
 extern void storeUnregisterAbort(StoreEntry * e);
 extern void storeMemObjectDump(MemObject * mem);
@@ -870,50 +852,31 @@ extern int contentLen(const StoreEntry * e);
 extern HttpReply *storeEntryReply(StoreEntry *);
 extern int storeTooManyDiskFilesOpen(void);
 extern void storeEntryReset(StoreEntry *);
-extern void storeHeapPositionUpdate(StoreEntry *);
+extern void storeHeapPositionUpdate(StoreEntry *, SwapDir *);
 extern void storeSwapFileNumberSet(StoreEntry * e, sfileno filn);
+extern void storeFsInit(void);
+extern void storeFsDone(void);
+extern void storeFsAdd(char *, STSETUP *);
+
+/* store_heap_replacement.c */
+#ifdef HEAP_REPLACEMENT
+extern heap_key HeapKeyGen_StoreEntry_LFUDA(void *, double);
+extern heap_key HeapKeyGen_StoreEntry_GDSF(void *, double);
+extern heap_key HeapKeyGen_StoreEntry_LRU(void *, double);
+#endif
+
+/* store_modules.c */
+extern void storeFsSetup(void);
 
 /* store_io.c */
-extern STOBJOPEN storeOpen;
-extern STOBJCLOSE storeClose;
-extern STOBJREAD storeRead;
-extern STOBJWRITE storeWrite;
-extern STOBJUNLINK storeUnlink;
+extern storeIOState * storeCreate(StoreEntry *, STFNCB *, STIOCB *, void *);
+extern storeIOState * storeOpen(StoreEntry *, STFNCB *, STIOCB *, void *);
+extern void storeClose(storeIOState *);
+extern void storeRead(storeIOState *, char *, size_t, off_t, STRCB *, void *);
+extern void storeWrite(storeIOState *, char *, size_t, off_t, FREE *);
+extern void storeUnlink(StoreEntry *);
 extern off_t storeOffset(storeIOState *);
 
-/*
- * store_io_ufs.c
- */
-extern storeIOState *storeUfsOpen(sfileno, mode_t, STIOCB *, void *);
-extern void storeUfsClose(storeIOState * sio);
-extern void storeUfsRead(storeIOState *, char *, size_t, off_t, STRCB *, void *);
-extern void storeUfsWrite(storeIOState *, char *, size_t, off_t, FREE *);
-extern void storeUfsUnlink(int fileno);
-
-#if USE_ASYNC_IO
-/*
- * store_io_ufs.c
- */
-extern storeIOState *storeAufsOpen(sfileno, mode_t, STIOCB *, void *);
-extern void storeAufsClose(storeIOState * sio);
-extern void storeAufsRead(storeIOState *, char *, size_t, off_t, STRCB *, void *);
-extern void storeAufsWrite(storeIOState *, char *, size_t, off_t, FREE *);
-extern void storeAufsUnlink(int fileno);
-#endif
-
-#if USE_DISKD
-/*
- * diskd.c
- */
-extern storeIOState *storeDiskdOpen(sfileno, mode_t, STIOCB *, void *);
-extern void storeDiskdClose(storeIOState * sio);
-extern void storeDiskdRead(storeIOState *, char *, size_t, off_t, STRCB *, void *);
-extern void storeDiskdWrite(storeIOState *, char *, size_t, off_t, FREE *);
-extern void storeDiskdUnlink(int fileno);
-extern STINIT storeDiskdInit;
-extern void storeDiskdReadQueue(void);
-#endif
-
 /*
  * store_log.c
  */
@@ -957,45 +920,23 @@ extern char *storeSwapDir(int);
 extern char *storeSwapFullPath(int, char *);
 extern char *storeSwapSubSubDir(int, char *);
 extern const char *storeSwapPath(int);
-extern int storeDirMapAllocate(void);
-extern int storeDirMapBitTest(int fn);
-extern int storeDirMapBitsInUse(void);
-extern int storeDirNumber(int fileno);
-extern int storeDirProperFileno(int dirn, int fn);
-extern int storeDirValidFileno(int fn, int);
 extern int storeDirWriteCleanLogs(int reopen);
+extern int storeDirSelectSwapDir(const StoreEntry *);
 extern int storeVerifySwapDirs(void);
 extern void storeCreateSwapDirectories(void);
 extern void storeDirCloseSwapLogs(void);
 extern void storeDirCloseTmpSwapLog(int dirn);
 extern void storeDirConfigure(void);
-extern void storeDirDiskFull(int fn);
+extern void storeDirDiskFull(sdirno);
 extern void storeDirInit(void);
-extern void storeDirMapBitReset(int fn);
-extern void storeDirMapBitSet(int fn);
 extern void storeDirOpenSwapLogs(void);
 extern void storeDirSwapLog(const StoreEntry *, int op);
-extern void storeDirUpdateSwapSize(int fn, size_t size, int sign);
+extern void storeDirUpdateSwapSize(SwapDir *, size_t size, int sign);
+extern void storeDirSync(void);
+extern void storeDirCallback(void);
 extern void storeDirLRUDelete(StoreEntry *);
 extern void storeDirLRUAdd(StoreEntry *);
 
-/*
- * store_dir_ufs.c
- */
-extern OBJH storeUfsDirStats;
-extern void storeUfsDirParse(cacheSwap * swap);
-extern void storeUfsDirDump(StoreEntry * entry, const char *name, SwapDir * s);
-extern void storeUfsDirFree(SwapDir *);
-extern char *storeUfsFullPath(sfileno fn, char *fullpath);
-extern STINIT storeUfsDirInit;
-#if USE_ASYNC_IO
-extern void storeAufsDirParse(cacheSwap * swap);
-#endif
-#if USE_DISKD
-extern void storeDiskdDirParse(cacheSwap *);
-#endif
-
-
 /*
  * store_swapmeta.c
  */
index a63075292605b6e74cb6de15d1205646f329cd96..b135e36cf55a5b38f475d9b25331431a944b5a7d 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * $Id: snmp_agent.cc,v 1.73 2000/03/06 16:23:34 wessels Exp $
+ * $Id: snmp_agent.cc,v 1.74 2000/05/03 17:15:42 adrian Exp $
  *
  * DEBUG: section 49     SNMP Interface
  * AUTHOR: Kostas Anagnostakis
@@ -279,12 +279,9 @@ snmp_prfSysFn(variable_list * Var, snint * ErrP)
            ASN_INTEGER);
        break;
     case PERF_SYS_CURLRUEXP:
+        /* No global LRU info anymore */
        Answer = snmp_var_new_integer(Var->name, Var->name_length,
-#if !HEAP_REPLACEMENT
-           (snint) (storeExpiredReferenceAge() * 100),
-#else
-           0,
-#endif
+            0,
            SMI_TIMETICKS);
        break;
     case PERF_SYS_CURUNLREQ:
index 85c9c04c0cbbec3487e4a09d1d427ddaa44a88fc..ee3c6fc752be4b24c4f667049a8b54b67e17245f 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * $Id: squid.h,v 1.200 2000/05/02 20:58:30 hno Exp $
+ * $Id: squid.h,v 1.201 2000/05/03 17:15:42 adrian Exp $
  *
  * AUTHOR: Duane Wessels
  *
@@ -420,7 +420,13 @@ struct rusage {
 #define SQUID_NONBLOCK O_NDELAY
 #endif
 
-#define SWAP_DIR_SHIFT 24
-#define SWAP_FILE_MASK 0x00FFFFFF
+#include <sys/param.h>
+#include <sys/mount.h>
+
+/*
+ * I'm sick of having to keep doing this ..
+ */
+
+#define INDEXSD(i)   (&Config.cacheSwap.swapDirs[(i)])
 
 #endif /* SQUID_H */
index 9637c962f2f36867ea010491876bf58b80e9bba1..ce61d332e4b29bb0b7b6bd9396ae4c5cce813af0 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * $Id: stat.cc,v 1.324 2000/03/06 16:23:34 wessels Exp $
+ * $Id: stat.cc,v 1.325 2000/05/03 17:15:42 adrian Exp $
  *
  * DEBUG: section 18    Cache Manager Statistics
  * AUTHOR: Harvest Derived
@@ -264,8 +264,8 @@ statStoreEntry(StoreEntry * s, StoreEntry * e)
        (int) e->lock_count,
        storePendingNClients(e),
        (int) e->refcount);
-    storeAppendPrintf(s, "\tSwap File %#08X\n",
-       e->swap_file_number);
+    storeAppendPrintf(s, "\tSwap Dir %d, File %#08X\n",
+       e->swap_dirn, e->swap_filen);
     if (mem != NULL) {
        storeAppendPrintf(s, "\tinmem_lo: %d\n", (int) mem->inmem_lo);
        storeAppendPrintf(s, "\tinmem_hi: %d\n", (int) mem->inmem_hi);
@@ -495,13 +495,6 @@ info_get(StoreEntry * sentry)
        store_swap_size);
     storeAppendPrintf(sentry, "\tStorage Mem size:\t%d KB\n",
        (int) (store_mem_size >> 10));
-#if HEAP_REPLACEMENT
-    storeAppendPrintf(sentry, "\tStorage Replacement Threshold:\t%f\n",
-       heap_peepminkey(store_heap));
-#else
-    storeAppendPrintf(sentry, "\tStorage LRU Expiration Age:\t%6.2f days\n",
-       (double) storeExpiredReferenceAge() / 86400.0);
-#endif
     storeAppendPrintf(sentry, "\tMean Object Size:\t%0.2f KB\n",
        n_disk_objects ? (double) store_swap_size / n_disk_objects : 0.0);
     storeAppendPrintf(sentry, "\tRequests given to unlinkd:\t%d\n",
@@ -616,8 +609,6 @@ info_get(StoreEntry * sentry)
        memInUse(MEM_MEMOBJECT));
     storeAppendPrintf(sentry, "\t%6d Hot Object Cache Items\n",
        hot_obj_count);
-    storeAppendPrintf(sentry, "\t%6d Filemap bits set\n",
-       storeDirMapBitsInUse());
     storeAppendPrintf(sentry, "\t%6d on-disk objects\n",
        n_disk_objects);
 
index f709d213513f93e4cbbcbbbdee49454e594019b5..1e8713a26681866d061a619d4fa02c5c51c9534b 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * $Id: store.cc,v 1.519 2000/05/02 21:07:36 hno Exp $
+ * $Id: store.cc,v 1.520 2000/05/03 17:15:42 adrian Exp $
  *
  * DEBUG: section 20    Storage Manager
  * AUTHOR: Harvest Derived
@@ -74,8 +74,6 @@ typedef struct lock_ctrl_t {
 /*
  * local function prototypes
  */
-static int storeCheckExpired(const StoreEntry *);
-static int storeEntryLocked(const StoreEntry *);
 static int storeEntryValidLength(const StoreEntry *);
 static void storeGetMemSpace(int);
 static void storeHashDelete(StoreEntry *);
@@ -87,11 +85,6 @@ static int getKeyCounter(void);
 static int storeKeepInMemory(const StoreEntry *);
 static OBJH storeCheckCachableStats;
 static EVH storeLateRelease;
-#if HEAP_REPLACEMENT
-static heap_key_func HeapKeyGen_StoreEntry_LFUDA;
-static heap_key_func HeapKeyGen_StoreEntry_GDSF;
-static heap_key_func HeapKeyGen_StoreEntry_LRU;
-#endif
 
 /*
  * local variables
@@ -104,9 +97,9 @@ static heap_key_func HeapKeyGen_StoreEntry_LRU;
 #else
 static dlink_list inmem_list;
 #endif
-static int store_pages_max = 0;
-static int store_swap_high = 0;
-static int store_swap_low = 0;
+int store_pages_max = 0;
+int store_swap_high = 0;
+int store_swap_low = 0;
 static Stack LateReleaseStack;
 
 #if URL_CHECKSUM_DEBUG
@@ -150,7 +143,8 @@ new_StoreEntry(int mem_obj_flag, const char *url, const char *log_url)
        e->mem_obj = new_MemObject(url, log_url);
     debug(20, 3) ("new_StoreEntry: returning %p\n", e);
     e->expires = e->lastmod = e->lastref = e->timestamp = -1;
-    e->swap_file_number = -1;
+    e->swap_filen = -1;
+    e->swap_dirn = -1;
     return e;
 }
 
@@ -204,27 +198,12 @@ storeHashInsert(StoreEntry * e, const cache_key * key)
        e, storeKeyText(key));
     e->key = storeKeyDup(key);
     hash_join(store_table, (hash_link *) e);
-#if HEAP_REPLACEMENT
-    if (EBIT_TEST(e->flags, ENTRY_SPECIAL)) {
-       (void) 0;
-    } else {
-       e->node = heap_insert(store_heap, e);
-       debug(20, 4) ("storeHashInsert: inserted node %p\n", e->node);
-    }
-#endif
 }
 
 static void
 storeHashDelete(StoreEntry * e)
 {
     hash_remove_link(store_table, (hash_link *) e);
-#if HEAP_REPLACEMENT
-    if (e->node) {
-       debug(20, 4) ("storeHashDelete: deleting node %p\n", e->node);
-       heap_delete(store_heap, e->node);
-       e->node = NULL;
-    }
-#endif
     storeKeyFree(e->key);
     e->key = NULL;
 }
@@ -250,24 +229,20 @@ storePurgeMem(StoreEntry * e)
 void
 storeLockObject(StoreEntry * e)
 {
-    if (e->lock_count++ == 0) {
-#if HEAP_REPLACEMENT
-       /*
-        * There is no reason to take any action here.  Squid by
-        * default is moving locked objects to the end of the LRU
-        * list to keep them from getting bumped into by the
-        * replacement algorithm.  We can't do that so we will just
-        * have to handle them.
-        */
-       debug(20, 4) ("storeLockObject: just locked node %p\n", e->node);
-#else
-       storeDirLRUDelete(e);
-       storeDirLRUAdd(e);
-#endif
-    }
+    SwapDir *SD;
+
+    if (e->swap_dirn > -1)
+      SD = INDEXSD(e->swap_dirn);
+    else
+      SD = NULL;
+
+    e->lock_count++;
     debug(20, 3) ("storeLockObject: key '%s' count=%d\n",
        storeKeyText(e->key), (int) e->lock_count);
     e->lastref = squid_curtime;
+    /* Notify the fs that we're referencing this object again */
+    if (SD != NULL && SD->refobj != NULL)
+        SD->refobj(SD, e);
 }
 
 void
@@ -291,6 +266,7 @@ storeReleaseRequest(StoreEntry * e)
 int
 storeUnlockObject(StoreEntry * e)
 {
+    SwapDir *SD;
     e->lock_count--;
     debug(20, 3) ("storeUnlockObject: key '%s' count=%d\n",
        storeKeyText(e->key), e->lock_count);
@@ -299,11 +275,21 @@ storeUnlockObject(StoreEntry * e)
     if (e->store_status == STORE_PENDING)
        EBIT_SET(e->flags, RELEASE_REQUEST);
     assert(storePendingNClients(e) == 0);
+    /* Notify the fs that we're not referencing this object any more */
+    if (e->swap_filen > -1)
+        SD = INDEXSD(e->swap_dirn);
+    else
+        SD = NULL;
+    if (SD != NULL && SD->unrefobj != NULL)
+        SD->unrefobj(SD, e);
 #if HEAP_REPLACEMENT
-    storeHeapPositionUpdate(e);
+    storeHeapPositionUpdate(e, SD);
 #else
+#if 0
+    /* Note: From 2.4. Not sure how this relates to the unrefobj() call above */
     storeDirLRUDelete(e);
     storeDirLRUAdd(e);
+#endif
 #endif
     if (EBIT_TEST(e->flags, RELEASE_REQUEST))
        storeRelease(e);
@@ -351,7 +337,7 @@ storeSetPrivateKey(StoreEntry * e)
     if (e->key && EBIT_TEST(e->flags, KEY_PRIVATE))
        return;                 /* is already private */
     if (e->key) {
-       if (e->swap_file_number > -1)
+       if (e->swap_filen > -1)
            storeDirSwapLog(e, SWAP_LOG_DEL);
        storeHashDelete(e);
     }
@@ -402,7 +388,7 @@ storeSetPublicKey(StoreEntry * e)
        storeHashDelete(e);
     EBIT_CLR(e->flags, KEY_PRIVATE);
     storeHashInsert(e, newkey);
-    if (e->swap_file_number > -1)
+    if (e->swap_filen > -1)
        storeDirSwapLog(e, SWAP_LOG_ADD);
 }
 
@@ -431,7 +417,8 @@ storeCreateEntry(const char *url, const char *log_url, request_flags flags, meth
     e->store_status = STORE_PENDING;
     storeSetMemStatus(e, NOT_IN_MEMORY);
     e->swap_status = SWAPOUT_NONE;
-    e->swap_file_number = -1;
+    e->swap_filen = -1;
+    e->swap_dirn = -1;
     e->refcount = 0;
     e->lastref = squid_curtime;
     e->timestamp = 0;          /* set in storeTimestampsSet() */
@@ -676,11 +663,8 @@ storeAbort(StoreEntry * e)
     }
     /* Notify the client side */
     InvokeHandlers(e);
-    /* Do we need to close the swapout file? */
-    /* Not if we never started swapping out */
-    if (e->swap_file_number > -1) {
-       storeSwapOutFileClose(e);
-    }
+    /* Close any swapout file */
+    storeSwapOutFileClose(e);
     storeUnlockObject(e);      /* unlock */
 }
 
@@ -779,173 +763,23 @@ storeGetMemSpace(int size)
 void
 storeMaintainSwapSpace(void *datanotused)
 {
-    StoreEntry *e = NULL;
-    int scanned = 0;
-    int locked = 0;
-    int expired = 0;
-    int max_scan;
-    int max_remove;
     int i;
-    int j;
-    static int ndir = 0;
-    double f;
-    static time_t last_warn_time = 0;
-#if !HEAP_REPLACEMENT
-    SwapDir *sd;
-#else
-    heap_key age;
-    heap_key min_age = 0.0;
-    link_list *locked_entries = NULL;
-#if HEAP_REPLACEMENT_DEBUG
-    if (!verify_heap_property(store_heap)) {
-       debug(20, 1) ("Heap property violated!\n");
-    }
-#endif
-#endif
-    /* We can't delete objects while rebuilding swap */
-    if (store_dirs_rebuilding) {
-       eventAdd("MaintainSwapSpace", storeMaintainSwapSpace, NULL, 1.0, 1);
-       return;
-    } else {
-       f = (double) (store_swap_size - store_swap_low) / (store_swap_high - store_swap_low);
-       f = f < 0.0 ? 0.0 : f > 1.0 ? 1.0 : f;
-       max_scan = (int) (f * 400.0 + 100.0);
-       if ((max_remove = stat5minClientRequests()) < 10)
-           max_remove = 10;
-       eventAdd("MaintainSwapSpace", storeMaintainSwapSpace, NULL, 1.0 - f, 1);
-    }
-    debug(20, 3) ("storeMaintainSwapSpace: f=%f, max_scan=%d, max_remove=%d\n",
-       f, max_scan, max_remove);
-#if HEAP_REPLACEMENT
-    while (heap_nodes(store_heap) > 0) {
-       if (store_swap_size < store_swap_low)
-           break;
-       if (expired >= max_remove)
-           break;
-       if (scanned >= max_scan)
-           break;
-       age = heap_peepminkey(store_heap);
-       e = heap_extractmin(store_heap);
-       e->node = NULL;         /* no longer in the heap */
-       scanned++;
-       if (storeEntryLocked(e)) {
-           /*
-            * Entry is in use ... put it in a linked list to ignore it.
-            */
-           if (!EBIT_TEST(e->flags, ENTRY_SPECIAL)) {
-               /*
-                * If this was a "SPECIAL" do not add it back into the heap.
-                * It will always be "SPECIAL" and therefore never removed.
-                */
-               debug(20, 4) ("storeMaintainSwapSpace: locked url %s\n",
-                   (e->mem_obj && e->mem_obj->url) ? e->mem_obj->url : storeKeyText(e->key));
-               linklistPush(&locked_entries, e);
-           }
-           locked++;
-           continue;
-       } else if (storeCheckExpired(e)) {
-           /*
-            * Note: This will not check the reference age ifdef
-            * HEAP_REPLACEMENT, but it does some other useful
-            * checks...
-            */
-           expired++;
-           debug(20, 3) ("Released store object age %f size %d refs %d key %s\n",
-               age, e->swap_file_sz, e->refcount, storeKeyText(e->key));
-           min_age = age;
-           storeRelease(e);
-       } else {
-           /*
-            * Did not expire the object so we need to add it back
-            * into the heap!
-            */
-           debug(20, 5) ("storeMaintainSwapSpace: non-expired %s\n",
-               storeKeyText(e->key));
-           linklistPush(&locked_entries, e);
-           continue;
-       }
-       if (store_swap_size < store_swap_low)
-           break;
-       else if (expired >= max_remove)
-           break;
-       else if (scanned >= max_scan)
-           break;
-    }
-    /*
-     * Bump the heap age factor.
-     */
-    if (min_age > 0.0)
-       store_heap->age = min_age;
-    /*
-     * Reinsert all bumped locked entries back into heap...
-     */
-    while ((e = linklistShift(&locked_entries)))
-       e->node = heap_insert(store_heap, e);
-#else
+    SwapDir *SD;
+
+    /* walk each fs */
     for (i = 0; i < Config.cacheSwap.n_configured; i++) {
-       sd = &Config.cacheSwap.swapDirs[i];
-       sd->lru_walker = sd->lru_list.tail;
+       /* call the maintain function .. */
+       SD = INDEXSD(i); 
+        if (SD->maintainfs != NULL)
+           SD->maintainfs(SD);
     }
-    do {
-       j = 0;
-       for (i = 0; i < Config.cacheSwap.n_configured; i++) {
-           if (ndir >= Config.cacheSwap.n_configured)
-               ndir = ndir % Config.cacheSwap.n_configured;
-           sd = &Config.cacheSwap.swapDirs[ndir++];
-           if (sd->cur_size < sd->low_size)
-               continue;
-           if (NULL == sd->lru_walker)
-               continue;
-           e = sd->lru_walker->data;
-           sd->lru_walker = sd->lru_walker->prev;
-           j++;
-           scanned++;
-           sd->scanned++;
-           if (storeEntryLocked(e)) {
-               /*
-                * If there is a locked entry at the tail of the LRU list,
-                * move it to the beginning to get it out of the way.
-                * Theoretically, we might have all locked objects at the
-                * tail, and then we'll never remove anything here and the
-                * LRU age will go to zero.
-                */
-               if (memInUse(MEM_STOREENTRY) > max_scan) {
-                   storeDirLRUDelete(e);
-                   if (!EBIT_TEST(e->flags, ENTRY_SPECIAL))
-                       storeDirLRUAdd(e);
-               }
-               locked++;
-           } else if (storeCheckExpired(e)) {
-               expired++;
-               sd->removals++;
-               storeRelease(e);
-           }
-           if (expired >= max_remove)
-               break;
-           if (scanned >= max_scan)
-               break;
-       }
-    } while (j > 0 && expired < max_remove && scanned < max_scan);
-#endif
-    debug(20, (expired ? 2 : 3)) ("storeMaintainSwapSpace: scanned %d/%d removed %d/%d locked %d f=%.03f\n",
-       scanned, max_scan, expired, max_remove, locked, f);
-    debug(20, 3) ("storeMaintainSwapSpace stats:\n");
-    debug(20, 3) ("  %6d objects\n", memInUse(MEM_STOREENTRY));
-    debug(20, 3) ("  %6d were scanned\n", scanned);
-    debug(20, 3) ("  %6d were locked\n", locked);
-    debug(20, 3) ("  %6d were expired\n", expired);
-    if (store_swap_size < Config.Swap.maxSize)
-       return;
-    if (squid_curtime - last_warn_time < 10)
-       return;
-    debug(20, 0) ("WARNING: Disk space over limit: %d KB > %d KB\n",
-       store_swap_size, Config.Swap.maxSize);
-    last_warn_time = squid_curtime;
+
+    /* Reregister a maintain event .. */
+    eventAdd("MaintainSwapSpace", storeMaintainSwapSpace, NULL, 1.0, 1);
 }
 
 
 /* release an object from a cache */
-/* return number of objects released. */
 void
 storeRelease(StoreEntry * e)
 {
@@ -958,13 +792,13 @@ storeRelease(StoreEntry * e)
        storeReleaseRequest(e);
        return;
     }
-    if (store_dirs_rebuilding && e->swap_file_number > -1) {
+    if (store_dirs_rebuilding && e->swap_filen > -1) {
        storeSetPrivateKey(e);
        if (e->mem_obj) {
            storeSetMemStatus(e, NOT_IN_MEMORY);
            destroy_MemObject(e);
        }
-       if (e->swap_file_number > -1) {
+       if (e->swap_filen > -1) {
            /*
             * Fake a call to storeLockObject().  When rebuilding is done,
             * we'll just call storeUnlockObject() on these.
@@ -978,14 +812,17 @@ storeRelease(StoreEntry * e)
        }
     }
     storeLog(STORE_LOG_RELEASE, e);
-    if (e->swap_file_number > -1) {
-       storeUnlink(e->swap_file_number);
+    if (e->swap_filen > -1) {
+       storeUnlink(e);
        if (e->swap_status == SWAPOUT_DONE)
            if (EBIT_TEST(e->flags, ENTRY_VALIDATED))
-               storeDirUpdateSwapSize(e->swap_file_number, e->swap_file_sz, -1);
+               storeDirUpdateSwapSize(&Config.cacheSwap.swapDirs[e->swap_dirn], e->swap_file_sz, -1);
        if (!EBIT_TEST(e->flags, KEY_PRIVATE))
            storeDirSwapLog(e, SWAP_LOG_DEL);
+#if 0
+       /* From 2.4. I think we do this in storeUnlink? */
        storeSwapFileNumberSet(e, -1);
+#endif
     }
     storeSetMemStatus(e, NOT_IN_MEMORY);
     destroy_StoreEntry(e);
@@ -1015,7 +852,7 @@ storeLateRelease(void *unused)
 }
 
 /* return 1 if a store entry is locked */
-static int
+int
 storeEntryLocked(const StoreEntry * e)
 {
     if (e->lock_count)
@@ -1094,10 +931,6 @@ storeInitHashValues(void)
     debug(20, 1) ("Max Swap size: %d KB\n", Config.Swap.maxSize);
 }
 
-#if HEAP_REPLACEMENT
-#include "store_heap_replacement.c"
-#endif
-
 void
 storeInit(void)
 {
@@ -1108,36 +941,7 @@ storeInit(void)
     storeDigestInit();
     storeLogOpen();
 #if HEAP_REPLACEMENT
-    /*
-     * Create new heaps with cache replacement policies attached to them.
-     * The cache replacement policy is specified as either GDSF or LFUDA in
-     * the squid.conf configuration file.  Note that the replacement policy
-     * applies only to the disk replacement algorithm.  Memory replacement
-     * always uses GDSF since we want to maximize object hit rate.
-     */
     inmem_heap = new_heap(1000, HeapKeyGen_StoreEntry_GDSF);
-    if (Config.replPolicy) {
-       if (tolower(Config.replPolicy[0]) == 'g') {
-           debug(20, 1) ("Using GDSF disk replacement policy\n");
-           store_heap = new_heap(10000, HeapKeyGen_StoreEntry_GDSF);
-       } else if (tolower(Config.replPolicy[0]) == 'l') {
-           if (tolower(Config.replPolicy[1]) == 'f') {
-               debug(20, 1) ("Using LFUDA disk replacement policy\n");
-               store_heap = new_heap(10000, HeapKeyGen_StoreEntry_LFUDA);
-           } else if (tolower(Config.replPolicy[1]) == 'r') {
-               debug(20, 1) ("Using LRU heap disk replacement policy\n");
-               store_heap = new_heap(10000, HeapKeyGen_StoreEntry_LRU);
-           }
-       } else {
-           debug(20, 1) ("Unrecognized replacement_policy; using GDSF\n");
-           store_heap = new_heap(10000, HeapKeyGen_StoreEntry_GDSF);
-       }
-    } else {
-       debug(20, 1) ("Using default disk replacement policy (GDSF)\n");
-       store_heap = new_heap(10000, HeapKeyGen_StoreEntry_GDSF);
-    }
-#else
-    inmem_list.head = inmem_list.tail = NULL;
 #endif
     stackInit(&LateReleaseStack);
     eventAdd("storeLateRelease", storeLateRelease, NULL, 1.0, 1);
@@ -1172,48 +976,6 @@ storeKeepInMemory(const StoreEntry * e)
     return mem->inmem_lo == 0;
 }
 
-static int
-storeCheckExpired(const StoreEntry * e)
-{
-    if (storeEntryLocked(e))
-       return 0;
-    if (EBIT_TEST(e->flags, RELEASE_REQUEST))
-       return 1;
-    if (EBIT_TEST(e->flags, ENTRY_NEGCACHED) && squid_curtime >= e->expires)
-       return 1;
-    return 1;
-}
-
-#if !HEAP_REPLACEMENT
-/*
- * storeExpiredReferenceAge
- *
- * The LRU age is scaled exponentially between 1 minute and
- * Config.referenceAge , when store_swap_low < store_swap_size <
- * store_swap_high.  This keeps store_swap_size within the low and high
- * water marks.  If the cache is very busy then store_swap_size stays
- * closer to the low water mark, if it is not busy, then it will stay
- * near the high water mark.  The LRU age value can be examined on the
- * cachemgr 'info' page.
- */
-time_t
-storeExpiredReferenceAge(void)
-{
-    double x;
-    double z;
-    time_t age;
-    x = (double) (store_swap_high - store_swap_size) / (store_swap_high - store_swap_low);
-    x = x < 0.0 ? 0.0 : x > 1.0 ? 1.0 : x;
-    z = pow((double) (Config.referenceAge / 60), x);
-    age = (time_t) (z * 60.0);
-    if (age < 60)
-       age = 60;
-    else if (age > 31536000)
-       age = 31536000;
-    return age;
-}
-#endif
-
 void
 storeNegativeCache(StoreEntry * e)
 {
@@ -1343,7 +1105,8 @@ storeEntryDump(const StoreEntry * e, int l)
     debug(20, l) ("StoreEntry->swap_file_sz: %d\n", (int) e->swap_file_sz);
     debug(20, l) ("StoreEntry->refcount: %d\n", e->refcount);
     debug(20, l) ("StoreEntry->flags: %s\n", storeEntryFlags(e));
-    debug(20, l) ("StoreEntry->swap_file_number: %d\n", (int) e->swap_file_number);
+    debug(20, l) ("StoreEntry->swap_dirn: %d\n", (int) e->swap_dirn);
+    debug(20, l) ("StoreEntry->swap_filen: %d\n", (int) e->swap_filen);
     debug(20, l) ("StoreEntry->lock_count: %d\n", (int) e->lock_count);
     debug(20, l) ("StoreEntry->mem_status: %d\n", (int) e->mem_status);
     debug(20, l) ("StoreEntry->ping_status: %d\n", (int) e->ping_status);
@@ -1471,16 +1234,65 @@ storeEntryReset(StoreEntry * e)
 }
 
 #if HEAP_REPLACEMENT
+/*
+ * This routine only handles memory updates these days
+ */
 void
-storeHeapPositionUpdate(StoreEntry * e)
+storeHeapPositionUpdate(StoreEntry * e, SwapDir * SD)
 {
-    if (e->node)
-       heap_update(store_heap, e->node, e);
     if (e->mem_obj && e->mem_obj->node)
        heap_update(inmem_heap, e->mem_obj->node, e);
 }
 #endif
 
+/*
+ * storeFsInit
+ *
+ * This routine calls the SETUP routine for each fs type.
+ * I don't know where the best place for this is, and I'm not going to shuffle
+ * around large chunks of code right now (that can be done once its working.)
+ */
+void
+storeFsInit(void)
+{
+    storeFsSetup();
+}
+
+
+/*
+ * similar to above, but is called when a graceful shutdown is to occur
+ * of each fs module.
+ */
+void
+storeFsDone(void)
+{
+    int i = 0;
+
+    while (storefs_list[i].typestr != NULL) {
+        storefs_list[i].donefunc();
+       i++;
+    }
+}
+
+/*
+ * called to add another store fs module
+ */
+void storeFsAdd(char *type, STSETUP *setup)
+{
+    int i;
+    /* find the number of currently known storefs types */
+    for (i = 0; storefs_list && storefs_list[i].typestr; i++) {
+       assert(strcmp(storefs_list[i].typestr, type)!=0);
+    }
+    /* add the new type */
+    storefs_list = xrealloc(storefs_list, (i + 2) * sizeof(storefs_entry_t));
+    memset(&storefs_list[i+1],0,sizeof(storefs_entry_t));
+    storefs_list[i].typestr = type;
+    /* Call the FS to set up capabilities and initialize the FS driver */
+    setup(&storefs_list[i]);
+}
+
+#if 0
 void
 storeSwapFileNumberSet(StoreEntry * e, sfileno filn)
 {
@@ -1497,3 +1309,4 @@ storeSwapFileNumberSet(StoreEntry * e, sfileno filn)
        storeDirLRUAdd(e);
     }
 }
+#endif
index 700bfe26249cdf36339525bb6cfdb05465471583..78a4598d5bb3996569bb2876f535b2d1fbccb126 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * $Id: store_client.cc,v 1.86 2000/04/18 06:06:17 wessels Exp $
+ * $Id: store_client.cc,v 1.87 2000/05/03 17:15:43 adrian Exp $
  *
  * DEBUG: section 20    Storage Manager Client-Side Interface
  * AUTHOR: Duane Wessels
@@ -138,7 +138,7 @@ storeClientListAdd(StoreEntry * e, void *data)
     if (sc->type == STORE_DISK_CLIENT)
        /* assert we'll be able to get the data we want */
        /* maybe we should open swapin_fd here */
-       assert(e->swap_file_number > -1 || storeSwapOutAble(e));
+       assert(e->swap_filen > -1 || storeSwapOutAble(e));
     for (T = &mem->clients; *T; T = &(*T)->next);
     *T = sc;
 #if DELAY_POOLS
@@ -152,7 +152,6 @@ storeClientCallback(store_client * sc, ssize_t sz)
     STCB *callback = sc->callback;
     char *buf = sc->copy_buf;
     assert(sc->callback);
-    assert(sc->copy_buf);
     sc->callback = NULL;
     sc->copy_buf = NULL;
     if (cbdataValid(sc->callback_data))
@@ -259,6 +258,7 @@ storeClientCopy3(StoreEntry * e, store_client * sc)
 {
     MemObject *mem = e->mem_obj;
     size_t sz;
+
     if (storeClientNoMoreToSend(e, sc)) {
        /* There is no more to send! */
        storeClientCallback(sc, 0);
@@ -307,10 +307,11 @@ storeClientCopy3(StoreEntry * e, store_client * sc)
        /* What the client wants is in memory */
        debug(20, 3) ("storeClientCopy3: Copying from memory\n");
        sz = stmemCopy(&mem->data_hdr,
-           sc->copy_offset, sc->copy_buf, sc->copy_size);
-       storeClientCallback(sc, sz);
-       return;
+       sc->copy_offset, sc->copy_buf, sc->copy_size);
+        storeClientCallback(sc, sz);
+        return;
     }
+    /* What the client wants is not in memory. Schedule a disk read */
     assert(STORE_DISK_CLIENT == sc->type);
     assert(!sc->flags.disk_io_pending);
     debug(20, 3) ("storeClientCopy3: reading from STORE\n");
@@ -386,7 +387,7 @@ storeClientReadHeader(void *data, const char *buf, ssize_t len)
        return;
     }
     if (tlv_list == NULL) {
-       debug(20, 1) ("WRNING: failed to unpack meta data\n");
+       debug(20, 1) ("WARNING: failed to unpack meta data\n");
        storeClientCallback(sc, -1);
        return;
     }
@@ -527,6 +528,8 @@ storeLowestMemReaderOffset(const StoreEntry * entry)
        nx = sc->next;
        if (sc->callback_data == NULL)  /* open slot */
            continue;
+       if (sc->type != STORE_MEM_CLIENT)
+           continue;
        if (sc->type == STORE_DISK_CLIENT)
            if (NULL != sc->swapin_sio)
                continue;
index 37ce8bc92bef9b47437050fcf4d3dfdd77798f77..c6c92366ce38a7545e636689394e0f15d888b564 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * $Id: store_dir.cc,v 1.106 2000/03/09 04:50:13 wessels Exp $
+ * $Id: store_dir.cc,v 1.107 2000/05/03 17:15:43 adrian Exp $
  *
  * DEBUG: section 47    Store Directory Routines
  * AUTHOR: Duane Wessels
 
 #include "squid.h"
 
+static int storeDirValidSwapDirSize(int, size_t);
 static void storeDirLRUWalkInitHead(SwapDir * sd);
 static void *storeDirLRUWalkNext(SwapDir * sd);
 
-const char *SwapDirType[] =
-{
-    "ufs",
-    "!ERROR!"
-};
-
 void
 storeDirInit(void)
 {
@@ -78,6 +73,33 @@ storeCreateSwapDirectories(void)
     } while (pid > 0 || (pid < 0 && errno == EINTR));
 }
 
+/*
+ * Determine whether the given directory can handle this object
+ * size
+ *
+ * Note: if the object size is -1, then the only swapdirs that
+ * will return true here are ones that have max_obj_size = -1,
+ * ie any-sized-object swapdirs. This is a good thing.
+ */
+static int
+storeDirValidSwapDirSize(int swapdir, size_t objsize)
+{
+    /*
+     * If the swapdir's max_obj_size is -1, then it definitely can
+     */
+    if (Config.cacheSwap.swapDirs[swapdir].max_objsize == -1)
+        return 1;
+    /*
+     * Else, make sure that the max object size is larger than objsize
+     */
+    if (Config.cacheSwap.swapDirs[swapdir].max_objsize > objsize)
+        return 1;
+    else
+        return 0;
+}
+
+
+#if UNUSED /* Squid-2..4.DEVEL3 code */
 /*
  * This new selection scheme simply does round-robin on all SwapDirs.
  * A SwapDir is skipped if it is over the max_size (100%) limit.  If
@@ -141,154 +163,72 @@ storeDirSelectSwapDir(void)
 }
 #endif
 
-#if OLD
+#endif /* Squid-2.4.DEVEL3 code */
+
 /*
- * This is Stew Forster's selection algorithm.
- * Spread load across least 3/4 of the store directories
+ * Spread load across all of the store directories
+ *
+ * Note: We should modify this later on to prefer sticking objects
+ * in the *tightest fit* swapdir to conserve space, along with the
+ * actual swapdir usage. But for now, this hack will do while  
+ * testing, so you should order your swapdirs in the config file
+ * from smallest maxobjsize to unlimited (-1) maxobjsize.
+ *
+ * We also have to choose nleast == nconf since we need to consider
+ * ALL swapdirs, regardless of state. Again, this is a hack while
+ * we sort out the real usefulness of this algorithm.
  */
-static int
-storeDirSelectSwapDir(void)
+int
+storeDirSelectSwapDir(const StoreEntry *e)
 {
-    double least_used = 1.0;
-    double high = (double) Config.Swap.highWaterMark / 100.0;
-    double u;
-    int dirn;
-    int i, j;
+    size_t objsize;
+    size_t least_size;
+    size_t least_objsize;
+    int least_load = 1000;
+    int load;
+    int dirn = -1;
+    int i;
     SwapDir *SD;
-    static int nleast = 0;
-    static int nconf = 0;
-    static int *dirq = NULL;
-    static double *diru = NULL;
-    /*
-     * Handle simplest case of a single swap directory immediately
-     */
-    if (Config.cacheSwap.n_configured == 1)
-       return 0;
-    /*
-     * Initialise dirq on the first call or on change of number of dirs
-     */
-    if (nconf != Config.cacheSwap.n_configured) {
-       nconf = Config.cacheSwap.n_configured;
-       nleast = (nconf * 3) / 4;
-       safe_free(dirq);
-       dirq = (int *) xmalloc(sizeof(int) * nleast);
-       safe_free(diru);
-       diru = (double *) xmalloc(sizeof(double) * nconf);
-       for (j = 0; j < nleast; j++)
-           dirq[j] = -1;
-    }
-    /*
-     * Scan for a non-negative dirn in the dirq array and return that one
-     */
-    dirn = -1;
-    for (j = 0; j < nleast; j++) {
-       dirn = dirq[j];
-       if (dirn < 0)
-           continue;
-       dirq[j] = -1;
-       break;
-    }
-    /*
-     * If we found a valid dirn return it
-     */
-    if (dirn >= 0)
-       return dirn;
-    /*
-     * Now for the real guts of the algorithm - building the dirq array
-     */
-    for (i = 0; i < nconf; i++) {
-       diru[i] = 1.1;
-       SD = &Config.cacheSwap.swapDirs[i];
+
+    /* Calculate the object size */
+    objsize = objectLen(e);
+    if (objsize != -1)
+       objsize += e->mem_obj->swap_hdr_sz;
+    /* Initial defaults */
+    least_size = Config.cacheSwap.swapDirs[0].cur_size;
+    least_objsize = Config.cacheSwap.swapDirs[0].max_objsize;
+    for (i = 0; i < Config.cacheSwap.n_configured; i++) {
+        SD = &Config.cacheSwap.swapDirs[i];
        SD->flags.selected = 0;
-       if (SD->flags.read_only)
-           continue;
-       u = (double) SD->cur_size / SD->max_size;
-       if (u > high)
+        if (SD->flags.read_only)
+            continue;
+        /* Valid for object size check */
+        if (!storeDirValidSwapDirSize(i, objsize))
+            continue;
+        load = SD->checkobj(SD, e);
+        if (load < 0)
            continue;
-       diru[i] = u;
+        if (SD->cur_size > SD->max_size)
+            continue;
+        if (load > least_load)
+            continue;
+        if ((least_objsize > 0) && (objsize > least_objsize))
+            continue;
+        /* Only use leastsize if the load is equal */
+        if ((load == least_load) && (SD->cur_size > least_size))
+            continue;
+        least_load = load;
+        least_size = SD->cur_size;
+        dirn = i;
     }
-    for (j = 0; j < nleast; j++) {
-       dirq[j] = -1;
-       least_used = 1.0;
-       dirn = -1;
-       for (i = 0; i < nconf; i++) {
-           if (diru[i] < least_used) {
-               least_used = diru[i];
-               dirn = i;
-           }
-       }
-       if (dirn < 0)
-           break;
-       dirq[j] = dirn;
-       diru[dirn] = 1.1;
-       /* set selected flag for debugging/cachemgr only */
-       Config.cacheSwap.swapDirs[dirn].flags.selected = 1;
-    }
-    /*
-     * Setup default return of 0 if no least found
-     */
-    if (dirq[0] < 0)
-       dirq[0] = 0;
-    dirn = dirq[0];
-    dirq[0] = -1;
-    return dirn;
-}
-#endif
 
-int
-storeDirValidFileno(int fn, int flag)
-{
-    int dirn = fn >> SWAP_DIR_SHIFT;
-    int filn = fn & SWAP_FILE_MASK;
-    if (dirn > Config.cacheSwap.n_configured)
-       return 0;
-    if (dirn < 0)
-       return 0;
-    if (filn < 0)
-       return 0;
-    /*
-     * If flag is set it means out-of-range file number should
-     * be considered invalid.
-     */
-    if (flag)
-       if (filn > Config.cacheSwap.swapDirs[dirn].map->max_n_files)
-           return 0;
-    return 1;
-}
-
-int
-storeDirMapBitTest(int fn)
-{
-    int dirn = fn >> SWAP_DIR_SHIFT;
-    int filn = fn & SWAP_FILE_MASK;
-    return file_map_bit_test(Config.cacheSwap.swapDirs[dirn].map, filn);
-}
+    if (dirn >= 0)
+       Config.cacheSwap.swapDirs[dirn].flags.selected = 1;
 
-void
-storeDirMapBitSet(int fn)
-{
-    int dirn = fn >> SWAP_DIR_SHIFT;
-    int filn = fn & SWAP_FILE_MASK;
-    file_map_bit_set(Config.cacheSwap.swapDirs[dirn].map, filn);
+    return dirn;
 }
 
-void
-storeDirMapBitReset(int fn)
-{
-    int dirn = fn >> SWAP_DIR_SHIFT;
-    int filn = fn & SWAP_FILE_MASK;
-    file_map_bit_reset(Config.cacheSwap.swapDirs[dirn].map, filn);
-}
 
-int
-storeDirMapAllocate(void)
-{
-    int dirn = storeDirSelectSwapDir();
-    SwapDir *SD = &Config.cacheSwap.swapDirs[dirn];
-    int filn = file_map_allocate(SD->map, SD->suggest);
-    SD->suggest = filn + 1;
-    return (dirn << SWAP_DIR_SHIFT) | (filn & SWAP_FILE_MASK);
-}
 
 char *
 storeSwapDir(int dirn)
@@ -297,18 +237,6 @@ storeSwapDir(int dirn)
     return Config.cacheSwap.swapDirs[dirn].path;
 }
 
-int
-storeDirNumber(int swap_file_number)
-{
-    return swap_file_number >> SWAP_DIR_SHIFT;
-}
-
-int
-storeDirProperFileno(int dirn, int fn)
-{
-    return (dirn << SWAP_DIR_SHIFT) | (fn & SWAP_FILE_MASK);
-}
-
 /*
  * An entry written to the swap log MUST have the following
  * properties.
@@ -316,36 +244,34 @@ storeDirProperFileno(int dirn, int fn)
  *       a public ADD, change the key, then log a private
  *       DEL.  So we need to log a DEL before we change a
  *       key from public to private.
- *   2.  It MUST have a valid (> -1) swap_file_number.
+ *   2.  It MUST have a valid (> -1) swap_filen.
  */
 void
 storeDirSwapLog(const StoreEntry * e, int op)
 {
-    int dirn = e->swap_file_number >> SWAP_DIR_SHIFT;
     SwapDir *sd;
-    assert(dirn < Config.cacheSwap.n_configured);
     assert(!EBIT_TEST(e->flags, KEY_PRIVATE));
-    assert(e->swap_file_number >= 0);
+    assert(e->swap_filen >= 0);
     /*
      * icons and such; don't write them to the swap log
      */
     if (EBIT_TEST(e->flags, ENTRY_SPECIAL))
        return;
     assert(op > SWAP_LOG_NOP && op < SWAP_LOG_MAX);
-    debug(20, 3) ("storeDirSwapLog: %s %s %08X\n",
+    debug(20, 3) ("storeDirSwapLog: %s %s %d %08X\n",
        swap_log_op_str[op],
        storeKeyText(e->key),
-       e->swap_file_number);
-    sd = &Config.cacheSwap.swapDirs[dirn];
+        e->swap_dirn,
+       e->swap_filen);
+    sd = &Config.cacheSwap.swapDirs[e->swap_dirn];
     sd->log.write(sd, e, op);
 }
 
 void
-storeDirUpdateSwapSize(int fn, size_t size, int sign)
+storeDirUpdateSwapSize(SwapDir *SD, size_t size, int sign)
 {
-    int dirn = (fn >> SWAP_DIR_SHIFT) % Config.cacheSwap.n_configured;
     int k = ((size + 1023) >> 10) * sign;
-    Config.cacheSwap.swapDirs[dirn].cur_size += k;
+    SD->cur_size += k;
     store_swap_size += k;
     if (sign > 0)
        n_disk_objects++;
@@ -356,6 +282,9 @@ storeDirUpdateSwapSize(int fn, size_t size, int sign)
 void
 storeDirStats(StoreEntry * sentry)
 {
+    int i;
+    SwapDir *SD;
+
     storeAppendPrintf(sentry, "Store Directory Statistics:\n");
     storeAppendPrintf(sentry, "Store Entries          : %d\n",
        memInUse(MEM_STOREENTRY));
@@ -366,17 +295,15 @@ storeDirStats(StoreEntry * sentry)
     storeAppendPrintf(sentry, "Current Capacity       : %d%% used, %d%% free\n",
        percent((int) store_swap_size, (int) Config.Swap.maxSize),
        percent((int) (Config.Swap.maxSize - store_swap_size), (int) Config.Swap.maxSize));
-    storeUfsDirStats(sentry);  /* XXX */
-}
 
-int
-storeDirMapBitsInUse(void)
-{
-    int i;
-    int n = 0;
-    for (i = 0; i < Config.cacheSwap.n_configured; i++)
-       n += Config.cacheSwap.swapDirs[i].map->n_files_in_map;
-    return n;
+    /* Now go through each swapdir, calling its statfs routine */
+    for (i = 0; i < Config.cacheSwap.n_configured; i++) {
+        storeAppendPrintf(sentry, "\n");
+        SD = &(Config.cacheSwap.swapDirs[i]);
+        storeAppendPrintf(sentry, "Store Directory #%d (%s): %s\n", i, SD->type,
+          storeSwapDir(i));
+        SD->statfs(SD, sentry);
+    }
 }
 
 void
@@ -388,17 +315,14 @@ storeDirConfigure(void)
     for (i = 0; i < Config.cacheSwap.n_configured; i++) {
        SD = &Config.cacheSwap.swapDirs[i];;
        Config.Swap.maxSize += SD->max_size;
-       if (NULL == SD->map)
-           SD->map = file_map_create();
        SD->low_size = (int) (((float) SD->max_size *
                (float) Config.Swap.lowWaterMark) / 100.0);
     }
 }
 
 void
-storeDirDiskFull(int fn)
+storeDirDiskFull(sdirno dirn)
 {
-    int dirn = fn >> SWAP_DIR_SHIFT;
     SwapDir *SD = &Config.cacheSwap.swapDirs[dirn];
     assert(0 <= dirn && dirn < Config.cacheSwap.n_configured);
     SD->max_size = SD->cur_size;
@@ -432,6 +356,10 @@ storeDirCloseSwapLogs(void)
  *  storeDirWriteCleanLogs
  * 
  *  Writes a "clean" swap log file from in-memory metadata.
+ *  This is a rewrite of the original function to troll each
+ *  StoreDir and write the logs, and flush at the end of
+ *  the run. Thanks goes to Eric Stern, since this solution
+ *  came out of his COSS code.
  */
 #define CLEAN_BUF_SZ 16384
 int
@@ -445,8 +373,6 @@ storeDirWriteCleanLogs(int reopen)
     int dirn;
 #if HEAP_REPLACEMENT
     int node;
-#else
-    int j;
 #endif
     if (store_dirs_rebuilding) {
        debug(20, 1) ("Not currently OK to rewrite swap log.\n");
@@ -462,58 +388,23 @@ storeDirWriteCleanLogs(int reopen)
            debug(20, 1) ("log.clean.open() failed for dir #%d\n", sd->index);
            continue;
        }
-#if !HEAP_REPLACEMENT
-       storeDirLRUWalkInitHead(sd);
-#endif
-    }
-#if HEAP_REPLACEMENT
-    if (NULL == store_heap)
-       return 0;
-    for (node = 0; node < heap_nodes(store_heap); node++) {
-       e = (StoreEntry *) heap_peep(store_heap, node);
-       if (e->swap_file_number < 0)
-           continue;
-       if (e->swap_status != SWAPOUT_DONE)
-           continue;
-       if (e->swap_file_sz <= 0)
-           continue;
-       if (EBIT_TEST(e->flags, RELEASE_REQUEST))
-           continue;
-       if (EBIT_TEST(e->flags, KEY_PRIVATE))
-           continue;
-       if (EBIT_TEST(e->flags, ENTRY_SPECIAL))
-           continue;
-       dirn = storeDirNumber(e->swap_file_number);
-       sd = &Config.cacheSwap.swapDirs[dirn];
-       if (NULL == sd->log.clean.write)
-           continue;
-       sd->log.clean.write(e, sd);
-       if ((++n & 0xFFFF) == 0) {
-           getCurrentTime();
-           debug(20, 1) ("  %7d entries written so far.\n", n);
-       }
-    }
-    /* flush */
-    for (dirn = 0; dirn < N; dirn++) {
-       sd = &Config.cacheSwap.swapDirs[dirn];
        if (NULL == sd->log.clean.write)
            continue;
-       sd->log.clean.write(NULL, sd);
-    }
+#if HEAP_REPLACEMENT
+        if (NULL == sd->repl.heap.heap)
+            continue;
+#endif
+#if HEAP_REPLACEMENT
+        for (node = 0; node < heap_nodes(sd->repl.heap.heap); node++)
 #else
-    do {
-       j = 0;
-       for (dirn = 0; dirn < Config.cacheSwap.n_configured; dirn++) {
-           sd = &Config.cacheSwap.swapDirs[dirn];
-           if (NULL == sd->log.clean.write)
-               continue;
-           e = storeDirLRUWalkNext(sd);
-           if (NULL == e) {
-               sd->log.clean.write(NULL, sd);
-               continue;
-           }
-           j++;
-           if (e->swap_file_number < 0)
+       storeDirLRUWalkInitHead(sd);
+        while ((e = storeDirLRUWalkNext(sd)) != NULL)
+#endif
+        {
+#if HEAP_REPLACEMENT
+           e = (StoreEntry *) heap_peep(sd->repl.heap.heap, node);
+#endif
+           if (e->swap_filen < 0)
                continue;
            if (e->swap_status != SWAPOUT_DONE)
                continue;
@@ -531,8 +422,9 @@ storeDirWriteCleanLogs(int reopen)
                debug(20, 1) ("  %7d entries written so far.\n", n);
            }
        }
-    } while (j > 0);
-#endif
+        /* Flush */
+        sd->log.clean.write(NULL, sd);
+    }
     if (reopen)
        storeDirOpenSwapLogs();
     getCurrentTime();
@@ -544,14 +436,47 @@ storeDirWriteCleanLogs(int reopen)
 }
 #undef CLEAN_BUF_SZ
 
+/*
+ * sync all avaliable fs'es ..
+ */
+void
+storeDirSync(void)
+{
+    int i;
+    SwapDir *SD;
+
+    for (i = 0; i < Config.cacheSwap.n_configured; i++) {
+        SD = &Config.cacheSwap.swapDirs[i];
+        if (SD->sync != NULL)
+            SD->sync(SD);
+    }
+}
+
+/*
+ * handle callbacks all avaliable fs'es ..
+ */
+void
+storeDirCallback(void)
+{
+    int i;
+    SwapDir *SD;
+
+    for (i = 0; i < Config.cacheSwap.n_configured; i++) {
+        SD = &Config.cacheSwap.swapDirs[i];
+        if (SD->callback != NULL)
+            SD->callback(SD);
+    }
+}
+
+#if 0 /* from Squid-2.4.DEVEL3 */
 void
 storeDirLRUDelete(StoreEntry * e)
 {
     SwapDir *sd;
-    if (e->swap_file_number < 0)
+    if (e->swap_filen < 0)
        return;
-    sd = &Config.cacheSwap.swapDirs[e->swap_file_number >> SWAP_DIR_SHIFT];
-    dlinkDelete(&e->lru, &sd->lru_list);
+    sd = &Config.cacheSwap.swapDirs[e->swap_dirn];
+    dlinkDelete(&e->lru, &sd->repl.lru.list);
 }
 
 void
@@ -560,23 +485,24 @@ storeDirLRUAdd(StoreEntry * e)
     SwapDir *sd;
     if (e->swap_file_number < 0)
        return;
-    sd = &Config.cacheSwap.swapDirs[e->swap_file_number >> SWAP_DIR_SHIFT];
-    dlinkAdd(e, &e->lru, &sd->lru_list);
+    sd = &Config.cacheSwap.swapDirs[e->swap_dirn];
+    dlinkAdd(e, &e->lru, &sd->repl.lru.list);
 }
+#endif /* from Squid-2.4.DEVEL3 */
 
 static void
 storeDirLRUWalkInitHead(SwapDir * sd)
 {
-    sd->lru_walker = sd->lru_list.head;
+    sd->repl.lru.walker = sd->repl.lru.list.head;
 }
 
 static void *
 storeDirLRUWalkNext(SwapDir * sd)
 {
     void *p;
-    if (NULL == sd->lru_walker)
+    if (NULL == sd->repl.lru.walker)
        return NULL;
-    p = sd->lru_walker->data;
-    sd->lru_walker = sd->lru_walker->next;
+    p = sd->repl.lru.walker->data;
+    sd->repl.lru.walker = sd->repl.lru.walker->next;
     return p;
 }
index fcf6ae369607a1fc56f0094339f0cf87d249461c..a9d7396430a276c13c113ccf8b3db8a3055366c2 100644 (file)
@@ -1,44 +1,83 @@
 #include "squid.h"
 
 
+/*
+ * submit a request to create a cache object for writing.
+ * The StoreEntry structure is sent as a hint to the filesystem
+ * to what will be stored in this object, to allow the filesystem
+ * to select different polices depending on object size or type.
+ */
+storeIOState *
+storeCreate(StoreEntry *e, STIOCB *file_callback, STIOCB *close_callback, void *callback_data)
+{
+    size_t objsize;
+    sdirno dirn;
+    SwapDir *SD;
+
+    /* This is just done for logging purposes */
+    objsize = objectLen(e);
+    if (objsize != -1)
+        objsize += e->mem_obj->swap_hdr_sz;
+
+    /*
+     * Pick the swapdir
+     * We assume that the header has been packed by now ..
+     */
+    dirn = storeDirSelectSwapDir(e);
+    if (dirn == -1) {
+        debug(20, 2) ("storeCreate: no valid swapdirs for this object\n");
+        return NULL;
+    }
+    debug (20, 2) ("storeCreate: Selected dir '%d' for obj size '%d'\n", dirn, objsize);
+    SD = &Config.cacheSwap.swapDirs[dirn];
+
+    /* Now that we have a fs to use, call its storeCreate function */
+    return(SD->obj.create(SD, e, file_callback, close_callback, callback_data));
+
+    /* Done */
+}
+
 
+/*
+ * storeOpen() is purely for reading ..
+ */
 storeIOState *
-storeOpen(sfileno f, mode_t mode, STIOCB * callback, void *callback_data)
+storeOpen(StoreEntry *e, STFNCB * file_callback, STIOCB * callback,
+  void *callback_data)
 {
-    SwapDir *SD = &Config.cacheSwap.swapDirs[f >> SWAP_DIR_SHIFT];
-    assert(mode == O_RDONLY || mode == O_WRONLY);
-    return SD->obj.open(f, mode, callback, callback_data);
+    SwapDir *SD = &Config.cacheSwap.swapDirs[e->swap_dirn];
+    return SD->obj.open(SD, e, file_callback, callback, callback_data);
 }
 
 void
 storeClose(storeIOState * sio)
 {
-    SwapDir *SD = &Config.cacheSwap.swapDirs[sio->swap_file_number >> SWAP_DIR_SHIFT];
+    SwapDir *SD = &Config.cacheSwap.swapDirs[sio->swap_dirn];
     if (sio->flags.closing)
        return;
     sio->flags.closing = 1;
-    SD->obj.close(sio);
+    SD->obj.close(SD, sio);
 }
 
 void
 storeRead(storeIOState * sio, char *buf, size_t size, off_t offset, STRCB * callback, void *callback_data)
 {
-    SwapDir *SD = &Config.cacheSwap.swapDirs[sio->swap_file_number >> SWAP_DIR_SHIFT];
-    SD->obj.read(sio, buf, size, offset, callback, callback_data);
+    SwapDir *SD = &Config.cacheSwap.swapDirs[sio->swap_dirn];
+    SD->obj.read(SD, sio, buf, size, offset, callback, callback_data);
 }
 
 void
 storeWrite(storeIOState * sio, char *buf, size_t size, off_t offset, FREE * free_func)
 {
-    SwapDir *SD = &Config.cacheSwap.swapDirs[sio->swap_file_number >> SWAP_DIR_SHIFT];
-    SD->obj.write(sio, buf, size, offset, free_func);
+    SwapDir *SD = &Config.cacheSwap.swapDirs[sio->swap_dirn];
+    SD->obj.write(SD, sio, buf, size, offset, free_func);
 }
 
 void
-storeUnlink(sfileno f)
+storeUnlink(StoreEntry *e)
 {
-    SwapDir *SD = &Config.cacheSwap.swapDirs[f >> SWAP_DIR_SHIFT];
-    SD->obj.unlink(f);
+    SwapDir *SD = INDEXSD(e->swap_dirn);
+    SD->obj.unlink(SD, e);
 }
 
 off_t
index 23855e613a3c9c57e366f94e3f40215573748222..5f4de6803a9e21838bd1c8b651080411f754af1c 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * $Id: store_log.cc,v 1.12 2000/03/24 20:40:29 wessels Exp $
+ * $Id: store_log.cc,v 1.13 2000/05/03 17:15:44 adrian Exp $
  *
  * DEBUG: section 20    Storage Manager Logging Functions
  * AUTHOR: Duane Wessels
@@ -62,11 +62,17 @@ storeLog(int tag, const StoreEntry * e)
        mem->log_url = xstrdup(mem->url);
     }
     reply = mem->reply;
-    logfilePrintf(storelog, "%9d.%03d %-7s %08X %4d %9d %9d %9d %s %d/%d %s %s\n",
+    /*
+     * XXX Ok, where should we print the dir number here?
+     * Because if we print it before the swap file number, it'll break
+     * the existing log format.
+     */
+    logfilePrintf(storelog, "%9d.%03d %-7s %02d %08X %4d %9d %9d %9d %s %d/%d %s %s\n",
        (int) current_time.tv_sec,
        (int) current_time.tv_usec / 1000,
        storeLogTags[tag],
-       e->swap_file_number,
+        e->swap_dirn,
+       e->swap_filen,
        reply->sline.status,
        (int) reply->date,
        (int) reply->last_modified,
index a8892cf61291c65c0c1b858e09e018414ce16c96..5e41e60cade281815578ec025a26aad740d48e79 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * $Id: store_rebuild.cc,v 1.69 2000/03/06 16:23:35 wessels Exp $
+ * $Id: store_rebuild.cc,v 1.70 2000/05/03 17:15:44 adrian Exp $
  *
  * DEBUG: section 20    Store Rebuild Routines
  * AUTHOR: Duane Wessels
@@ -40,36 +40,10 @@ static struct timeval rebuild_start;
 static void storeCleanup(void *);
 
 static int
-storeCleanupDoubleCheck(const StoreEntry * e)
+storeCleanupDoubleCheck(StoreEntry * e)
 {
-    /* XXX too UFS specific */
-    struct stat sb;
-    int dirn = e->swap_file_number >> SWAP_DIR_SHIFT;
-    if (Config.cacheSwap.swapDirs[dirn].type == SWAPDIR_UFS)
-       (void) 0;
-    if (Config.cacheSwap.swapDirs[dirn].type == SWAPDIR_ASYNCUFS)
-       (void) 0;
-    else
-       return 0;
-    if (stat(storeUfsFullPath(e->swap_file_number, NULL), &sb) < 0) {
-       debug(20, 0) ("storeCleanup: MISSING SWAP FILE\n");
-       debug(20, 0) ("storeCleanup: FILENO %08X\n", e->swap_file_number);
-       debug(20, 0) ("storeCleanup: PATH %s\n",
-           storeUfsFullPath(e->swap_file_number, NULL));
-       storeEntryDump(e, 0);
-       return -1;
-    }
-    if (e->swap_file_sz != sb.st_size) {
-       debug(20, 0) ("storeCleanup: SIZE MISMATCH\n");
-       debug(20, 0) ("storeCleanup: FILENO %08X\n", e->swap_file_number);
-       debug(20, 0) ("storeCleanup: PATH %s\n",
-           storeUfsFullPath(e->swap_file_number, NULL));
-       debug(20, 0) ("storeCleanup: ENTRY SIZE: %d, FILE SIZE: %d\n",
-           e->swap_file_sz, (int) sb.st_size);
-       storeEntryDump(e, 0);
-       return -1;
-    }
-    return 0;
+    SwapDir *SD = &Config.cacheSwap.swapDirs[e->swap_dirn];
+    return (SD->dblcheck(SD, e));
 }
 
 static void
@@ -106,7 +80,7 @@ storeCleanup(void *datanotused)
             * Calling storeRelease() has no effect because we're
             * still in 'store_rebuilding' state
             */
-           if (e->swap_file_number < 0)
+           if (e->swap_filen < 0)
                continue;
            if (opt_store_doublecheck)
                if (storeCleanupDoubleCheck(e))
@@ -116,7 +90,7 @@ storeCleanup(void *datanotused)
             * Only set the file bit if we know its a valid entry
             * otherwise, set it in the validation procedure
             */
-           storeDirUpdateSwapSize(e->swap_file_number, e->swap_file_sz, 1);
+           storeDirUpdateSwapSize(&Config.cacheSwap.swapDirs[e->swap_dirn], e->swap_file_sz, 1);
            if ((++validnum & 0x3FFFF) == 0)
                debug(20, 1) ("  %7d Entries Validated so far.\n", validnum);
        }
index 8269bc9ab2f2ee97518339cdf7736720c99e0f03..394f8153c5b5d07dd68276329dab8aae6b47c3ff 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * $Id: store_swapin.cc,v 1.22 2000/03/06 16:23:35 wessels Exp $
+ * $Id: store_swapin.cc,v 1.23 2000/05/03 17:15:44 adrian Exp $
  *
  * DEBUG: section 20    Storage Manager Swapin Functions
  * AUTHOR: Duane Wessels
@@ -36,6 +36,7 @@
 #include "squid.h"
 
 static STIOCB storeSwapInFileClosed;
+static STFNCB storeSwapInFileNotify;
 
 void
 storeSwapInStart(store_client * sc)
@@ -46,24 +47,22 @@ storeSwapInStart(store_client * sc)
        /* We're still reloading and haven't validated this entry yet */
        return;
     }
-    debug(20, 3) ("storeSwapInStart: called for %08X %s \n",
-       e->swap_file_number, storeKeyText(e->key));
+    debug(20, 3) ("storeSwapInStart: called for %d %08X %s \n",
+       e->swap_dirn, e->swap_filen, storeKeyText(e->key));
     if (e->swap_status != SWAPOUT_WRITING && e->swap_status != SWAPOUT_DONE) {
        debug(20, 1) ("storeSwapInStart: bad swap_status (%s)\n",
            swapStatusStr[e->swap_status]);
        return;
     }
-    if (e->swap_file_number < 0) {
-       debug(20, 1) ("storeSwapInStart: swap_file_number < 0\n");
+    if (e->swap_filen < 0) {
+       debug(20, 1) ("storeSwapInStart: swap_filen < 0\n");
        return;
     }
     assert(e->mem_obj != NULL);
     debug(20, 3) ("storeSwapInStart: Opening fileno %08X\n",
-       e->swap_file_number);
-    sc->swapin_sio = storeOpen(e->swap_file_number,
-       O_RDONLY,
-       storeSwapInFileClosed,
-       sc);
+       e->swap_filen);
+    sc->swapin_sio = storeOpen(e, storeSwapInFileNotify, storeSwapInFileClosed,
+      sc);
     cbdataLock(sc->swapin_sio);
 }
 
@@ -82,3 +81,15 @@ storeSwapInFileClosed(void *data, int errflag, storeIOState * sio)
        callback(sc->callback_data, sc->copy_buf, errflag);
     }
 }
+
+static void
+storeSwapInFileNotify(void *data, int errflag, storeIOState * sio)
+{
+  store_client *sc = data;
+  StoreEntry *e = sc->entry;
+  debug(1, 3) ("storeSwapInFileNotify: changing %d/%d to %d/%d\n", e->swap_filen, e->swap_dirn, sio->swap_filen, sio->swap_dirn);
+
+  e->swap_filen = sio->swap_filen;
+  e->swap_dirn = sio->swap_dirn;
+}
index c1ab144f6748b592fd46f60c304e0ccf4727962a..c2de97109b56e7f19bcfeb25534646aaf8b5e974 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * $Id: store_swapout.cc,v 1.64 2000/04/18 06:06:17 wessels Exp $
+ * $Id: store_swapout.cc,v 1.65 2000/05/03 17:15:44 adrian Exp $
  *
  * DEBUG: section 20    Storage Manager Swapout Functions
  * AUTHOR: Duane Wessels
@@ -38,6 +38,7 @@
 static off_t storeSwapOutObjectBytesOnDisk(const MemObject *);
 static void storeSwapOutStart(StoreEntry * e);
 static STIOCB storeSwapOutFileClosed;
+static STIOCB storeSwapOutFileNotify;
 
 /* start swapping object to disk */
 static void
@@ -49,31 +50,51 @@ storeSwapOutStart(StoreEntry * e)
     tlv *tlv_list;
     char *buf;
     assert(mem);
-    storeLockObject(e);
-    storeSwapFileNumberSet(e, storeDirMapAllocate());
+    /* Build the swap metadata, so the filesystem will know how much
+     * metadata there is to store
+     */
+    debug(20, 5) ("storeSwapOutStart: Begin SwapOut '%s' to dirno %d, fileno %08X\n",
+       storeUrl(e), e->swap_dirn, e->swap_filen);
+    e->swap_status = SWAPOUT_WRITING;
+    tlv_list = storeSwapMetaBuild(e);
+    buf = storeSwapMetaPack(tlv_list, &swap_hdr_sz);
+    storeSwapTLVFree(tlv_list);
+    mem->swap_hdr_sz = (size_t) swap_hdr_sz;
+    /* Create the swap file */
     c = xcalloc(1, sizeof(*c));
     c->data = e;
     cbdataAdd(c, cbdataXfree, 0);
-    mem->swapout.sio = storeOpen(e->swap_file_number,
-       O_WRONLY, storeSwapOutFileClosed, c);
+    mem->swapout.sio = storeCreate(e, storeSwapOutFileNotify, storeSwapOutFileClosed, c);
     if (NULL == mem->swapout.sio) {
        e->swap_status = SWAPOUT_NONE;
-       storeSwapFileNumberSet(e, -1);
        cbdataFree(c);
-       storeUnlockObject(e);
+       xfree(buf);
        return;
     }
-    e->swap_status = SWAPOUT_WRITING;
+    storeLockObject(e); /* Don't lock until after create, or the replacement
+                         * code might get confused */
+    /* Pick up the file number if it was assigned immediately */
+    e->swap_filen = mem->swapout.sio->swap_filen;
+    e->swap_dirn = mem->swapout.sio->swap_dirn;
+    /* write out the swap metadata */
     cbdataLock(mem->swapout.sio);
-    debug(20, 5) ("storeSwapOutStart: Begin SwapOut '%s' to fileno %08X\n",
-       storeUrl(e), e->swap_file_number);
-    tlv_list = storeSwapMetaBuild(e);
-    buf = storeSwapMetaPack(tlv_list, &swap_hdr_sz);
-    storeSwapTLVFree(tlv_list);
-    mem->swap_hdr_sz = (size_t) swap_hdr_sz;
     storeWrite(mem->swapout.sio, buf, mem->swap_hdr_sz, 0, xfree);
 }
 
+static void
+storeSwapOutFileNotify(void *data, int errflag, storeIOState * sio)
+{
+    generic_cbdata *c = data;
+    StoreEntry *e = c->data;
+    MemObject *mem = e->mem_obj;
+    assert(e->swap_status == SWAPOUT_WRITING);
+    assert(mem);
+    assert(mem->swapout.sio == sio);
+    assert(errflag == 0);
+    e->swap_filen = mem->swapout.sio->swap_filen;
+    e->swap_dirn = mem->swapout.sio->swap_dirn;
+}
+
 void
 storeSwapOut(StoreEntry * e)
 {
@@ -82,7 +103,6 @@ storeSwapOut(StoreEntry * e)
     off_t new_mem_lo;
     off_t on_disk = 0;
     size_t swapout_size;
-    char *swap_buf;
     ssize_t swap_buf_len;
     if (mem == NULL)
        return;
@@ -108,13 +128,22 @@ storeSwapOut(StoreEntry * e)
     lowest_offset = storeLowestMemReaderOffset(e);
     debug(20, 7) ("storeSwapOut: lowest_offset = %d\n",
        (int) lowest_offset);
+    /*
+     * Grab the swapout_size and check to see whether we're going to defer
+     * the swapout based upon size
+     */
+    swapout_size = (size_t) (mem->inmem_hi - mem->swapout.queue_offset);
+    if ((e->store_status != STORE_OK) && (swapout_size < store_maxobjsize)) {
+        debug (20, 5) ("storeSwapOut: Deferring starting swapping out\n"); 
+        return;
+    }
     /*
      * Careful.  lowest_offset can be greater than inmem_hi, such
      * as in the case of a range request.
      */
     if (mem->inmem_hi < lowest_offset)
        new_mem_lo = lowest_offset;
-    else if (mem->inmem_hi - lowest_offset > DISK_PAGE_SIZE)
+    else if (mem->inmem_hi - lowest_offset > SM_PAGE_SIZE)
        new_mem_lo = lowest_offset;
     else
        new_mem_lo = mem->inmem_lo;
@@ -142,7 +171,6 @@ storeSwapOut(StoreEntry * e)
        assert(mem->inmem_lo <= on_disk);
     if (!storeSwapOutAble(e))
        return;
-    swapout_size = (size_t) (mem->inmem_hi - mem->swapout.queue_offset);
     debug(20, 7) ("storeSwapOut: swapout_size = %d\n",
        (int) swapout_size);
     if (swapout_size == 0) {
@@ -152,7 +180,7 @@ storeSwapOut(StoreEntry * e)
     }
     if (e->store_status == STORE_PENDING) {
        /* wait for a full block to write */
-       if (swapout_size < DISK_PAGE_SIZE)
+       if (swapout_size < SM_PAGE_SIZE)
            return;
        /*
         * Wait until we are below the disk FD limit, only if the
@@ -161,6 +189,14 @@ storeSwapOut(StoreEntry * e)
        if (storeTooManyDiskFilesOpen() && !fwdCheckDeferRead(-1, e))
            return;
     }
+    /*
+     * Don't start swapping out until its either all in memory, or bigger
+     * than the maximum object size (so we pick a -1 maxobjsize fs)
+     */
+    if ((e->store_status != STORE_OK) && (swapout_size < store_maxobjsize)) {
+        debug (20, 5) ("storeSwapOut: Deferring starting swapping out\n"); 
+        return;
+    }
     /* Ok, we have stuff to swap out.  Is there a swapout.sio open? */
     if (e->swap_status == SWAPOUT_NONE) {
        assert(mem->swapout.sio == NULL);
@@ -174,35 +210,42 @@ storeSwapOut(StoreEntry * e)
     if (NULL == mem->swapout.sio)
        return;
     do {
-       if (swapout_size > DISK_PAGE_SIZE)
-           swapout_size = DISK_PAGE_SIZE;
-       swap_buf = memAllocate(MEM_DISK_BUF);
-       swap_buf_len = stmemCopy(&mem->data_hdr,
-           mem->swapout.queue_offset,
-           swap_buf,
-           swapout_size);
-       if (swap_buf_len < 0) {
-           debug(20, 1) ("stmemCopy returned %d for '%s'\n", swap_buf_len, storeKeyText(e->key));
-           storeUnlink(e->swap_file_number);
-           storeSwapFileNumberSet(e, -1);
-           e->swap_status = SWAPOUT_NONE;
-           memFree(swap_buf, MEM_DISK_BUF);
-           storeReleaseRequest(e);
-           storeSwapOutFileClose(e);
-           return;
+        /*
+        * Evil hack time.
+        * We are paging out to disk in page size chunks. however, later on when
+        * we update the queue position, we might not have a page (I *think*),
+        * so we do the actual page update here.
+         */
+
+        if (mem->swapout.memnode == NULL) {
+            /* We need to swap out the first page */
+           mem->swapout.memnode = mem->data_hdr.head;
+       } else {
+            /* We need to swap out the next page */
+           mem->swapout.memnode = mem->swapout.memnode->next;
        }
+       /*
+        * Get the length of this buffer. We are assuming(!) that the buffer
+        * length won't change on this buffer, or things are going to be very
+        * strange. I think that after the copy to a buffer is done, the buffer
+        * size should stay fixed regardless so that this code isn't confused,
+        * but we can look at this at a later date or whenever the code results
+        * in bad swapouts, whichever happens first. :-)
+        */
+       swap_buf_len = mem->swapout.memnode->len;
+
        debug(20, 3) ("storeSwapOut: swap_buf_len = %d\n", (int) swap_buf_len);
        assert(swap_buf_len > 0);
        debug(20, 3) ("storeSwapOut: swapping out %d bytes from %d\n",
            swap_buf_len, (int) mem->swapout.queue_offset);
        mem->swapout.queue_offset += swap_buf_len;
-       storeWrite(mem->swapout.sio, swap_buf, swap_buf_len, -1, memFreeDISK);
+       storeWrite(mem->swapout.sio, mem->swapout.memnode->data, swap_buf_len, -1, NULL);
        /* the storeWrite() call might generate an error */
        if (e->swap_status != SWAPOUT_WRITING)
            break;
        swapout_size = (size_t) (mem->inmem_hi - mem->swapout.queue_offset);
        if (e->store_status == STORE_PENDING)
-           if (swapout_size < DISK_PAGE_SIZE)
+           if (swapout_size < SM_PAGE_SIZE)
                break;
     } while (swapout_size > 0);
     if (NULL == mem->swapout.sio)
@@ -240,30 +283,26 @@ storeSwapOutFileClosed(void *data, int errflag, storeIOState * sio)
     assert(e->swap_status == SWAPOUT_WRITING);
     cbdataFree(c);
     if (errflag) {
-       sfileno bad = e->swap_file_number;
-       debug(20, 1) ("storeSwapOutFileClosed: swapfile %08X, errflag=%d\n\t%s\n",
-           bad, errflag, xstrerror());
-       storeSwapFileNumberSet(e, -1);
-       /*
-        * yuck.  re-set the filemap bit for some errors so that
-        * we don't try re-using it over and over
-        */
-       if (errno == EPERM)
-           storeDirMapBitSet(bad);
+       debug(20, 1) ("storeSwapOutFileClosed: dirno %d, swapfile %08X, errflag=%d\n\t%s\n",
+           e->swap_dirn, e->swap_filen, errflag, xstrerror());
        if (errflag == DISK_NO_SPACE_LEFT) {
-           storeDirDiskFull(bad);
+           storeDirDiskFull(e->swap_dirn);
            storeDirConfigure();
            storeConfigure();
        }
-       storeReleaseRequest(e);
+       if (e->swap_filen > 0)
+           storeUnlink(e);
+        e->swap_filen = -1;
+        e->swap_dirn = -1;
        e->swap_status = SWAPOUT_NONE;
+       storeReleaseRequest(e);
     } else {
        /* swapping complete */
-       debug(20, 3) ("storeSwapOutFileClosed: SwapOut complete: '%s' to %08X\n",
-           storeUrl(e), e->swap_file_number);
+       debug(20, 3) ("storeSwapOutFileClosed: SwapOut complete: '%s' to %d, %08X\n",
+           storeUrl(e), e->swap_dirn, e->swap_filen);
        e->swap_file_sz = objectLen(e) + mem->swap_hdr_sz;
        e->swap_status = SWAPOUT_DONE;
-       storeDirUpdateSwapSize(e->swap_file_number, e->swap_file_sz, 1);
+       storeDirUpdateSwapSize(&Config.cacheSwap.swapDirs[e->swap_dirn], e->swap_file_sz, 1);
        if (storeCheckCachable(e)) {
            storeLog(STORE_LOG_SWAPOUT, e);
            storeDirSwapLog(e, SWAP_LOG_ADD);
index 919d626af8ce923fd4afe39c9fe21ef867a809ff..566b811e371b119dda47645ffa626b6cd91511bb 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * $Id: structs.h,v 1.328 2000/05/02 21:35:24 hno Exp $
+ * $Id: structs.h,v 1.329 2000/05/03 17:15:44 adrian Exp $
  *
  *
  * SQUID Internet Object Cache  http://squid.nlanr.net/Squid/
@@ -153,11 +153,6 @@ struct _aclCheck_t {
     void *callback_data;
 };
 
-struct _aio_result_t {
-    int aio_return;
-    int aio_errno;
-};
-
 struct _wordlist {
     char *key;
     wordlist *next;
@@ -316,9 +311,7 @@ struct _SquidConfig {
 #if USE_ICMP
        char *pinger;
 #endif
-#if USE_UNLINKD
        char *unlinkd;
-#endif
     } Program;
 #if USE_DNSSERVERS
     int dnsChildren;
@@ -1284,6 +1277,7 @@ struct _MemObject {
     int nclients;
     struct {
        off_t queue_offset;     /* relative to in-mem data */
+        mem_node *memnode;     /* which node we're currently paging out */
        storeIOState *sio;
     } swapout;
     HttpReply *reply;
@@ -1325,12 +1319,14 @@ struct _StoreEntry {
     size_t swap_file_sz;
     u_short refcount;
     u_short flags;
-    sfileno swap_file_number;
-#if HEAP_REPLACEMENT
-    heap_node *node;
-#else
-    dlink_node lru;
+    sdirno swap_dirn;
+    sfileno swap_filen;
+    union {
+#ifdef HEAP_REPLACEMENT
+        heap_node *node;
 #endif
+        dlink_node lru;
+    } repl;
     u_short lock_count;                /* Assume < 65536! */
     mem_status_t mem_status:3;
     ping_status_t ping_status:3;
@@ -1339,23 +1335,46 @@ struct _StoreEntry {
 };
 
 struct _SwapDir {
-    swapdir_t type;
-    fileMap *map;
+    char *type;
     int cur_size;
     int low_size;
     int max_size;
     char *path;
     int index;                 /* This entry's index into the swapDirs array */
-    sfileno suggest;
+    int suggest;
+    size_t max_objsize;
+    union {
+#ifdef HEAP_REPLACEMENT
+        struct {
+          heap *heap;
+       } heap;
+#endif
+        struct {
+          dlink_list list;
+          dlink_node *walker;
+       } lru;
+    } repl;
     int removals;
     int scanned;
     struct {
        unsigned int selected:1;
        unsigned int read_only:1;
     } flags;
-    STINIT *init;
-    STNEWFS *newfs;
-    struct {
+    STINIT *init;              /* Initialise the fs */
+    STNEWFS *newfs;            /* Create a new fs */
+    STDUMP *dump;              /* Dump fs config snippet */
+    STFREE *freefs;            /* Free the fs data */
+    STDBLCHECK *dblcheck;      /* Double check the obj integrity */
+    STSTATFS *statfs;          /* Dump fs statistics */
+    STMAINTAINFS *maintainfs;  /* Replacement maintainence */
+    STCHECKOBJ *checkobj;      /* Check if the fs will store an object */
+    /* These two are notifications */
+    STREFOBJ *refobj;          /* Reference this object */
+    STUNREFOBJ *unrefobj;      /* Unreference this object */
+    STCALLBACK *callback;      /* Handle pending callbacks */
+    STSYNC *sync;              /* Sync the directory */
+    struct {
+        STOBJCREATE *create;
        STOBJOPEN *open;
        STOBJCLOSE *close;
        STOBJREAD *read;
@@ -1372,33 +1391,7 @@ struct _SwapDir {
            void *state;
        } clean;
     } log;
-#if !HEAP_REPLACEMENT
-    dlink_list lru_list;
-    dlink_node *lru_walker;
-#endif
-    union {
-       struct {
-           int l1;
-           int l2;
-           int swaplog_fd;
-       } ufs;
-#if USE_DISKD
-       struct {
-           int l1;
-           int l2;
-           int swaplog_fd;
-           int smsgid;
-           int rmsgid;
-           int wfd;
-           int away;
-           struct {
-               char *buf;
-               link_list *stack;
-               int id;
-           } shm;
-       } diskd;
-#endif
-    } u;
+    void *fsdata;
 };
 
 struct _request_flags {
@@ -1428,10 +1421,13 @@ struct _link_list {
 };
 
 struct _storeIOState {
-    sfileno swap_file_number;
+    sdirno swap_dirn;
+    sfileno swap_filen;
+    StoreEntry *e;             /* Need this so the FS layers can play god */
     mode_t mode;
     size_t st_size;            /* do stat(2) after read open */
-    off_t offset;              /* current offset pointer */
+    off_t offset;              /* current on-disk offset pointer */
+    STFNCB *file_callback;     /* called on delayed sfileno assignments */
     STIOCB *callback;
     void *callback_data;
     struct {
@@ -1441,38 +1437,7 @@ struct _storeIOState {
     struct {
        unsigned int closing:1; /* debugging aid */
     } flags;
-    union {
-       struct {
-           int fd;
-           struct {
-               unsigned int close_request:1;
-               unsigned int reading:1;
-               unsigned int writing:1;
-           } flags;
-       } ufs;
-       struct {
-           int fd;
-           struct {
-               unsigned int close_request:1;
-               unsigned int reading:1;
-               unsigned int writing:1;
-               unsigned int opening:1;
-           } flags;
-           const char *read_buf;
-           link_list *pending_writes;
-           link_list *pending_reads;
-       } aufs;
-#if USE_DISKD
-       struct {
-           int id;
-           struct {
-               unsigned int reading:1;
-               unsigned int writing:1;
-           } flags;
-           char *read_buf;
-       } diskd;
-#endif
-    } type;
+    void *fsstate;
 };
 
 struct _request_t {
@@ -1714,9 +1679,13 @@ struct _tlv {
     tlv *next;
 };
 
+/*
+ * Do we need to have the dirn in here? I don't think so, since we already
+ * know the dirn .. 
+ */
 struct _storeSwapLogData {
     char op;
-    sfileno swap_file_number;
+    sfileno swap_filen;
     time_t timestamp;
     time_t lastref;
     time_t expires;
@@ -1886,6 +1855,35 @@ struct _store_rebuild_data {
     int zero_object_sz;
 };
 
+/*
+ * This defines an fs type
+ */
+
+struct _storefs_entry {
+    char *typestr;
+    STFSPARSE *parsefunc;
+    STFSRECONFIGURE *reconfigurefunc;
+    STFSSHUTDOWN *donefunc;
+};
+
+/*
+ * Async disk IO - this defines a async disk io queue
+ */
+
+struct _diskd_queue {
+    int smsgid;                        /* send sysvmsg id */
+    int rmsgid;                        /* recv sysvmsg id */
+    int wfd;                   /* queue file descriptor ? */
+    int away;                  /* number of requests away */
+    int sent_count;            /* number of messages sent */
+    int recv_count;            /* number of messages received */
+    struct {
+       char *buf;              /* shm buffer */
+       link_list *stack;       
+       int id;                 /* sysvshm id */
+    } shm;
+};
+
 struct _Logfile {
     int fd;
     char path[MAXPATHLEN];
index c53d979515aa940b240bc54ade7d07009d79e5ef..43e20e8a74f919a229a767d85e47704fbcf32919 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * $Id: typedefs.h,v 1.101 2000/03/14 23:07:51 wessels Exp $
+ * $Id: typedefs.h,v 1.102 2000/05/03 17:15:44 adrian Exp $
  *
  *
  * SQUID Internet Object Cache  http://squid.nlanr.net/Squid/
@@ -39,6 +39,7 @@ typedef unsigned int mem_status_t;
 typedef unsigned int ping_status_t;
 typedef unsigned int swap_status_t;
 typedef int sfileno;
+typedef int sdirno;
 
 typedef struct {
     size_t bytes;
@@ -68,7 +69,6 @@ typedef struct _acl_snmp_comm acl_snmp_comm;
 typedef struct _acl_list acl_list;
 typedef struct _acl_access acl_access;
 typedef struct _aclCheck_t aclCheck_t;
-typedef struct _aio_result_t aio_result_t;
 typedef struct _wordlist wordlist;
 typedef struct _intlist intlist;
 typedef struct _intrange intrange;
@@ -168,6 +168,8 @@ typedef struct _helper_request helper_request;
 typedef struct _generic_cbdata generic_cbdata;
 typedef struct _storeIOState storeIOState;
 typedef struct _link_list link_list;
+typedef struct _storefs_entry storefs_entry_t;
+typedef struct _diskd_queue diskd_queue;
 typedef struct _Logfile Logfile;
 
 #if SQUID_SNMP
@@ -181,8 +183,6 @@ typedef struct _delaySpecSet delaySpecSet;
 typedef struct _delaySpec delaySpec;
 #endif
 
-/* define AIOCB even without USE_ASYNC_IO */
-typedef void AIOCB(int fd, void *, int aio_return, int aio_errno);
 typedef void CWCB(int fd, char *, size_t size, int flag, void *data);
 typedef void CNCB(int fd, int status, void *);
 
@@ -191,8 +191,16 @@ typedef void CBDUNL(void *, int);
 typedef void FOCB(void *, int fd, int errcode);
 typedef void EVH(void *);
 typedef void PF(int, void *);
-typedef void DRCB(int fd, const char *buf, int size, int errflag, void *data);
-typedef void DWCB(int, int, size_t, void *);
+
+/* disk.c / diskd.c callback typedefs */
+typedef void DRCB(int, const char *buf, int size, int errflag, void *data);
+                                                       /* Disk read CB */
+typedef void DWCB(int, int, size_t, void *);           /* disk write CB */
+typedef void DOCB(int, int errflag, void *data);       /* disk open CB */
+typedef void DCCB(int, int errflag, void *data);       /* disk close CB */
+typedef void DUCB(int errflag, void *data);            /* disk unlink CB */
+typedef void DTCB(int errflag, void *data);            /* disk trunc CB */
+
 typedef void FQDNH(const char *, void *);
 typedef void IDCB(const char *ident, void *data);
 typedef void IPH(const ipcache_addrs *, void *);
@@ -203,6 +211,7 @@ typedef void UH(void *data, wordlist *);
 typedef int DEFER(int fd, void *data);
 
 typedef void STIOCB(void *their_data, int errflag, storeIOState *);
+typedef void STFNCB(void *their_data, int errflag, storeIOState *);
 typedef void STRCB(void *their_data, const char *buf, ssize_t len);
 
 typedef void SIH(storeIOState *, void *);      /* swap in */
@@ -219,17 +228,39 @@ typedef void IDNSCB(void *, rfc1035_rr *, int);
 
 typedef void STINIT(SwapDir *);
 typedef void STNEWFS(SwapDir *);
-typedef storeIOState *STOBJOPEN(sfileno, mode_t, STIOCB *, void *);
-typedef void STOBJCLOSE(storeIOState *);
-typedef void STOBJREAD(storeIOState *, char *, size_t, off_t, STRCB *, void *);
-typedef void STOBJWRITE(storeIOState *, char *, size_t, off_t, FREE *);
-typedef void STOBJUNLINK(sfileno);
+typedef void STDUMP(StoreEntry *, const char *, SwapDir *);
+typedef void STFREE(SwapDir *);
+typedef int STDBLCHECK(SwapDir *, StoreEntry *);
+typedef void STSTATFS(SwapDir *, StoreEntry *);
+typedef void STMAINTAINFS(SwapDir *);
+typedef int STCHECKOBJ(SwapDir *, const StoreEntry *);
+typedef void STREFOBJ(SwapDir *, StoreEntry *);
+typedef void STUNREFOBJ(SwapDir *, StoreEntry *);
+typedef void STSETUP(storefs_entry_t *);
+typedef void STDONE(void);
+typedef void STCALLBACK(SwapDir *);
+typedef void STSYNC(SwapDir *);
+
+typedef storeIOState *STOBJCREATE(SwapDir *, StoreEntry *, STFNCB *, STIOCB *, void *);
+typedef storeIOState *STOBJOPEN(SwapDir *, StoreEntry *, STFNCB *, STIOCB *, void *);
+typedef void STOBJCLOSE(SwapDir *, storeIOState *);
+typedef void STOBJREAD(SwapDir *, storeIOState *, char *, size_t, off_t, STRCB *, void *);
+typedef void STOBJWRITE(SwapDir *, storeIOState *, char *, size_t, off_t, FREE *);
+typedef void STOBJUNLINK(SwapDir *, StoreEntry *);
+
 typedef void STLOGOPEN(SwapDir *);
 typedef void STLOGCLOSE(SwapDir *);
 typedef void STLOGWRITE(const SwapDir *, const StoreEntry *, int);
 typedef int STLOGCLEANOPEN(SwapDir *);
 typedef void STLOGCLEANWRITE(const StoreEntry *, SwapDir *);
 
+/* Store dir configuration routines */
+/* SwapDir *sd, char *path ( + char *opt later when the strtok mess is gone) */
+typedef void STFSPARSE(SwapDir *, int, char *);
+typedef void STFSRECONFIGURE(SwapDir *, int, char *);
+typedef void STFSSTARTUP(void);
+typedef void STFSSHUTDOWN(void);
+
 typedef double hbase_f(double);
 typedef void StatHistBinDumper(StoreEntry *, int idx, double val, double size, int count);
 
index 189f7355e10cf508c0a2a635e8db75f5a7cbfea8..61a644b47f56874931aaee9a38d8e9d4c6e8e8e9 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * $Id: unlinkd.cc,v 1.35 2000/05/02 20:13:58 hno Exp $
+ * $Id: unlinkd.cc,v 1.36 2000/05/03 17:15:44 adrian Exp $
  *
  * DEBUG: section 12    Unlink Daemon
  * AUTHOR: Duane Wessels
@@ -70,17 +70,14 @@ main(int argc, char *argv[])
 
 /* This code gets linked to Squid */
 
-#if USE_UNLINKD
 static int unlinkd_wfd = -1;
 static int unlinkd_rfd = -1;
-#endif
 
 #define UNLINKD_QUEUE_LIMIT 20
 
 void
 unlinkdUnlink(const char *path)
 {
-#if USE_UNLINKD
     char buf[MAXPATHLEN];
     int l;
     int x;
@@ -143,13 +140,11 @@ unlinkdUnlink(const char *path)
     }
     Counter.unlink.requests++;
     queuelen++;
-#endif
 }
 
 void
 unlinkdClose(void)
 {
-#if USE_UNLINKD
     assert(unlinkd_wfd > -1);
     debug(12, 1) ("Closing unlinkd pipe on FD %d\n", unlinkd_wfd);
     file_close(unlinkd_wfd);
@@ -157,13 +152,11 @@ unlinkdClose(void)
        file_close(unlinkd_rfd);
     unlinkd_wfd = -1;
     unlinkd_rfd = -1;
-#endif
 }
 
 void
 unlinkdInit(void)
 {
-#if USE_UNLINKD
     int x;
     char *args[2];
     struct timeval slp;
@@ -200,9 +193,6 @@ unlinkdInit(void)
     if (FD_PIPE == fd_table[unlinkd_wfd].type)
        commUnsetNonBlocking(unlinkd_wfd);
     debug(12, 1) ("Unlinkd pipe opened on FD %d\n", unlinkd_wfd);
-#else
-    debug(12, 1) ("Unlinkd is disabled\n");
-#endif
 }
 
 #endif /* ndef UNLINK_DAEMON */
index bb781bfa00cab17024f9c863567caef24c0456f3..c06322bcd19abe6a38e3679d80997339413a69ff 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * $Id: url.cc,v 1.122 2000/05/02 20:22:55 hno Exp $
+ * $Id: url.cc,v 1.123 2000/05/03 17:15:44 adrian Exp $
  *
  * DEBUG: section 23    URL Parsing
  * AUTHOR: Duane Wessels
@@ -243,10 +243,10 @@ urlParse(method_t method, char *url)
        port = urlDefaultPort(protocol);
        /* Is there any login informaiton? */
        if ((t = strrchr(host, '@'))) {
-           strcpy(login, host);
+           strcpy((char *)login, (char *)host);
            t = strrchr(login, '@');
            *t = 0;
-           strcpy(host, t + 1);
+           strcpy((char *)host, t + 1);
        }
        if ((t = strrchr(host, ':'))) {
            *t++ = '\0';