dafs-20060317
authorTom Keiser <tkeiser@sinenomine.net>
Fri, 17 Mar 2006 19:54:26 +0000 (19:54 +0000)
committerDerrick Brashear <shadow@dementia.org>
Fri, 17 Mar 2006 19:54:26 +0000 (19:54 +0000)
FIXES 26648

demand attach/fast restart fileserver

71 files changed:
Makefile.in
acinclude.m4
configure.in
src/auth/Makefile.in
src/bozo/bos.c
src/bozo/bosserver.c
src/bozo/fsbnodeops.c
src/cf/osconf.m4
src/config/param.rs_aix51.h
src/config/param.rs_aix52.h
src/config/param.rs_aix53.h
src/config/stds.h
src/rx/rx_queue.h
src/tsalvaged/Makefile.in [new file with mode: 0644]
src/tsalvaged/salvsync-debug.c [new file with mode: 0644]
src/tviced/Makefile.in
src/tviced/NTMakefile
src/tviced/serialize_state.c [new file with mode: 0644]
src/tviced/serialize_state.h [new file with mode: 0644]
src/tviced/state_analyzer.c [new file with mode: 0644]
src/tvolser/Makefile.in
src/util/Makefile.in
src/util/afsutil_prototypes.h
src/util/dirpath.c
src/util/dirpath.hin
src/util/dirpath_nt.h
src/util/errors.h
src/util/strnlen.c [new file with mode: 0644]
src/viced/Makefile.in
src/viced/NTMakefile
src/viced/afsfileprocs.c
src/viced/callback.c
src/viced/callback.h [new file with mode: 0644]
src/viced/host.c
src/viced/host.h
src/viced/viced.c
src/viced/viced.h
src/viced/viced_prototypes.h
src/vol/Makefile.in
src/vol/NTMakefile
src/vol/daemon_com.c [new file with mode: 0644]
src/vol/daemon_com.h [new file with mode: 0644]
src/vol/fssync-client.c [new file with mode: 0644]
src/vol/fssync-debug.c [new file with mode: 0644]
src/vol/fssync-server.c [new file with mode: 0644]
src/vol/fssync.c [deleted file]
src/vol/fssync.h
src/vol/nuke.c
src/vol/partition.c
src/vol/partition.h
src/vol/purge.c
src/vol/salvage.h
src/vol/salvaged.c [new file with mode: 0644]
src/vol/salvager.c [new file with mode: 0644]
src/vol/salvsync-client.c [new file with mode: 0644]
src/vol/salvsync-server.c [new file with mode: 0644]
src/vol/salvsync.h [new file with mode: 0644]
src/vol/test/listVicepx.c
src/vol/test/updateDirInode.c
src/vol/vnode.c
src/vol/vnode.h
src/vol/vol-salvage.c
src/vol/vol-salvage.h [new file with mode: 0644]
src/vol/voldefs.h
src/vol/volinodes.h
src/vol/volume.c
src/vol/volume.h
src/volser/NTMakefile
src/volser/dumpstuff.c
src/volser/volprocs.c
src/volser/volser.p.h

index 7e8033d..209d9b2 100644 (file)
@@ -213,6 +213,24 @@ sgiefs:
 vol: cmd comerr dir afs sgiefs
        ${COMPILE_PART1} vol ${COMPILE_PART2}
 
+tsalvaged: vol libafsrpc libafsauthent cmd util
+       set -x; \
+       if test "@DEMAND_ATTACH@" = "yes" ; then \
+               case ${SYS_NAME} in \
+               alpha_dux*|sgi_*|sun*_5*|rs_aix*|*linux*|hp_ux11*|ia64_hpux*|*fbsd*|*nbsd2*) \
+                       ${COMPILE_PART1} tsalvaged ${COMPILE_PART2} ;; \
+               *_darwin_[1-6][0-9]) \
+                       echo Not building MT tsalvaged for ${SYS_NAME} ;; \
+               *_darwin_*) \
+                       ${COMPILE_PART1} tsalvaged  ${COMPILE_PART2} ;; \
+               *) \
+                       echo Not building MT tsalvaged for ${SYS_NAME} ;; \
+               esac \
+       else \
+               echo skipping tsalvaged ; \
+       fi
+
+
 vlserver: cmd comerr vol audit vlserver_depinstall
        ${COMPILE_PART1} vlserver ${COMPILE_PART2}
 
@@ -569,13 +587,13 @@ jafs: libjafs
 jafsadm: libjafsadm
 
 finale: project cmd comerr afsd butc tbutc @ENABLE_KERNEL_MODULE@ libuafs audit kauth log package \
-       ptserver scout bu_utils ubik uss bozo vfsck volser tvolser \
+       ptserver scout bu_utils ubik uss bozo vfsck volser tvolser tsalvaged \
        venus update xstat afsmonitor dauth rxdebug libafsrpc \
        libafsauthent shlibafsrpc shlibafsauthent libadmin login man-pages
        ${COMPILE_PART1} finale ${COMPILE_PART2}
 
 finale_nolibafs: project cmd comerr afsd butc tbutc libuafs audit kauth log package \
-       ptserver scout bu_utils ubik uss bozo vfsck volser tvolser \
+       ptserver scout bu_utils ubik uss bozo vfsck volser tvolser tsalvaged \
        venus update xstat afsmonitor dauth rxdebug libafsrpc \
        libafsauthent shlibafsrpc shlibafsauthent libadmin login man-pages
        ${COMPILE_PART1} finale ${COMPILE_PART2}
@@ -633,6 +651,7 @@ clean2:
        -${COMPILE_PART1} tviced ${COMPILE_CLEAN}
        -${COMPILE_PART1} volser ${COMPILE_CLEAN}
        -${COMPILE_PART1} tvolser ${COMPILE_CLEAN}
+       -${COMPILE_PART1} tsalvaged ${COMPILE_CLEAN}
        -${COMPILE_PART1} venus ${COMPILE_CLEAN}
        -${COMPILE_PART1} venus/test ${COMPILE_CLEAN}
        -${COMPILE_PART1} afsd ${COMPILE_CLEAN}
@@ -791,6 +810,7 @@ distclean: clean
        src/tests/Makefile \
        src/tests/run-tests \
        src/tests/OpenAFS/Dirpath.pm \
+       src/tsalvaged/Makefile \
        src/tsm41/Makefile \
        src/tviced/Makefile \
        src/tvolser/Makefile \
index c9b8417..d33fec3 100644 (file)
@@ -33,6 +33,8 @@ AC_ARG_ENABLE( fast-restart,
 [  --enable-fast-restart               enable fast startup of file server without salvaging],, enable_fast_restart="no")
 AC_ARG_ENABLE( bitmap-later,
 [  --enable-bitmap-later               enable fast startup of file server by not reading bitmap till needed],, enable_bitmap_later="no")
+AC_ARG_ENABLE( demand-attach-fs,
+[  --enable-demand-attach-fs           enable Demand Attach Fileserver (please see documentation)],, enable_demand_attach_fs="no")
 AC_ARG_ENABLE( full-vos-listvol-switch,
 [  --disable-full-vos-listvol-switch    disable vos full listvol switch for formatted output],, enable_full_vos_listvol_switch="yes")
 AC_ARG_WITH(dux-kernel-headers,
@@ -948,6 +950,20 @@ if test "$enable_bitmap_later" = "yes"; then
        AC_DEFINE(BITMAP_LATER, 1, [define if you want to salvager to check bitmasks later])
 fi
 
+if test "$enable_demand_attach_fs" = "yes"; then
+       AC_DEFINE(DEMAND_ATTACH_ENABLE, 1, [define if you want the demand attach fileserver])
+       DEMAND_ATTACH="yes"
+else
+       DEMAND_ATTACH="no"
+fi
+AC_SUBST(DEMAND_ATTACH)
+
+if test "$enable_fast_restart" = "yes" &&
+   test "$enable_demand_attach_fs" = "yes" ; then
+       AC_MSG_ERROR([The Demand Attach and Fast Restart extensions are mutually exclusive.  Demand Attach fileservers automatically salvage volumes in the background, thereby making Fast Restart pointless.])
+       exit 1
+fi
+
 if test "$enable_full_vos_listvol_switch" = "yes"; then
        AC_DEFINE(FULL_LISTVOL_SWITCH, 1, [define if you want to want listvol switch])
 fi
index e96a93b..c20cce9 100644 (file)
@@ -106,6 +106,7 @@ src/tbutc/Makefile \
 src/tests/Makefile \
 src/tests/run-tests \
 src/tests/OpenAFS/Dirpath.pm \
+src/tsalvaged/Makefile \
 src/tsm41/Makefile \
 src/tviced/Makefile \
 src/tvolser/Makefile \
index 3379706..975775b 100644 (file)
@@ -96,7 +96,7 @@ test:
        cd test; $(MAKE)
 
 clean:
-       $(RM) -f *.o *.a copyauth setkey auth.h cellconfig.h acfg_errors.c ktc_errors.c core\
+       $(RM) -f *.o *.a copyauth setkey auth.h cellconfig.h acfg_errors.c ktc_errors.c core \
        AFS_component_version_number.c
 
 include ../config/Makefile.version
index ad5a00f..cca66c0 100644 (file)
@@ -52,10 +52,12 @@ static DoStat();
 
 #include "bosint.h"
 
-#define MRAFS_OFFSET  9
-#define ADDPARMOFFSET 26
+/* command offsets for bos salvage command */
+#define MRAFS_OFFSET  10
+#define ADDPARMOFFSET 27
 
-static struct SalvageParms {
+/* MR-AFS salvage parameters */
+struct MRAFSSalvageParms {
     afs_int32 Optdebug;
     afs_int32 Optnowrite;
     afs_int32 Optforce;
@@ -74,7 +76,7 @@ static struct SalvageParms {
     afs_int32 OptLogLevel;
     afs_int32 OptRxDebug;
     afs_uint32 OptResidencies;
-} mrafsParm;
+};
 
 /* dummy routine for the audit work.  It should do nothing since audits */
 /* occur at the server level and bos is not a server. */
@@ -1224,17 +1226,11 @@ StopServer(as)
 
 #define PARMBUFFERSSIZE 32
 
-static
-DoSalvage(aconn, aparm1, aparm2, aoutName, showlog, parallel, atmpDir,
-         orphans)
-     struct rx_connection *aconn;
-     char *aoutName;
-     char *aparm1;
-     char *aparm2;
-     afs_int32 showlog;
-     char *parallel;
-     char *atmpDir;
-     char *orphans;
+static afs_int32
+DoSalvage(struct rx_connection * aconn, char * aparm1, char * aparm2, 
+         char * aoutName, afs_int32 showlog, char * parallel, 
+         char * atmpDir, char * orphans, int dafs, 
+         struct MRAFSSalvageParms * mrafsParm)
 {
     register afs_int32 code;
     char *parms[6];
@@ -1285,19 +1281,43 @@ DoSalvage(aconn, aparm1, aparm2, aoutName, showlog, parallel, atmpDir,
        parms[code] = "";
     if (!aparm2)
        aparm2 = "";
+
     /* MUST pass canonical (wire-format) salvager path to bosserver */
-    strncpy(tbuffer, AFSDIR_CANONICAL_SERVER_SALVAGER_FILEPATH, BOZO_BSSIZE);
     if (*aparm2 != 0) {
-       if ((strlen(tbuffer) + 1 + strlen(partName) + 1 + strlen(aparm2) +
-            1) > BOZO_BSSIZE) {
-           printf("bos: command line too big\n");
-           return (E2BIG);
+       /* single volume salvage */
+       if (dafs) {
+           /* for DAFS, we call the salvagserver binary with special options.
+            * in this mode, it simply uses SALVSYNC to tell the currently
+            * running salvageserver to offline and salvage the volume in question */
+           strncpy(tbuffer, AFSDIR_CANONICAL_SERVER_SALSRV_FILEPATH, BOZO_BSSIZE);
+
+           if ((strlen(tbuffer) + 9 + strlen(partName) + 1 + strlen(aparm2) +
+                1) > BOZO_BSSIZE) {
+               printf("bos: command line too big\n");
+               return (E2BIG);
+           }
+
+           strcat(tbuffer, " -client ");
+           strcat(tbuffer, partName);
+           strcat(tbuffer, " ");
+           strcat(tbuffer, aparm2);
+       } else {
+           strncpy(tbuffer, AFSDIR_CANONICAL_SERVER_SALVAGER_FILEPATH, BOZO_BSSIZE);
+
+           if ((strlen(tbuffer) + 1 + strlen(partName) + 1 + strlen(aparm2) +
+                1) > BOZO_BSSIZE) {
+               printf("bos: command line too big\n");
+               return (E2BIG);
+           }
+
+           strcat(tbuffer, " ");
+           strcat(tbuffer, partName);
+           strcat(tbuffer, " ");
+           strcat(tbuffer, aparm2);
        }
-       strcat(tbuffer, " ");
-       strcat(tbuffer, partName);
-       strcat(tbuffer, " ");
-       strcat(tbuffer, aparm2);
     } else {
+       /* partition salvage */
+       strncpy(tbuffer, AFSDIR_CANONICAL_SERVER_SALVAGER_FILEPATH, BOZO_BSSIZE);
        if ((strlen(tbuffer) + 4 + strlen(partName) + 1) > BOZO_BSSIZE) {
            printf("bos: command line too big\n");
            return (E2BIG);
@@ -1306,75 +1326,82 @@ DoSalvage(aconn, aparm1, aparm2, aoutName, showlog, parallel, atmpDir,
        strcat(tbuffer, partName);
     }
 
-    /* add the parallel option if given */
-    if (parallel != NULL) {
-       if ((strlen(tbuffer) + 11 + strlen(parallel) + 1) > BOZO_BSSIZE) {
-           printf("bos: command line too big\n");
-           return (E2BIG);
+    /* For DAFS, specifying a single volume does not result in a standard
+     * salvager call.  Instead, it simply results in a SALVSYNC call to the
+     * online salvager daemon.  This interface does not give us the same rich
+     * set of call flags.  Thus, we skip these steps for DAFS single-volume 
+     * calls */
+    if (!dafs || (*aparm2 == 0)) {
+       /* add the parallel option if given */
+       if (parallel != NULL) {
+           if ((strlen(tbuffer) + 11 + strlen(parallel) + 1) > BOZO_BSSIZE) {
+               printf("bos: command line too big\n");
+               return (E2BIG);
+           }
+           strcat(tbuffer, " -parallel ");
+           strcat(tbuffer, parallel);
        }
-       strcat(tbuffer, " -parallel ");
-       strcat(tbuffer, parallel);
-    }
 
-    /* add the tmpdir option if given */
-    if (atmpDir != NULL) {
-       if ((strlen(tbuffer) + 9 + strlen(atmpDir) + 1) > BOZO_BSSIZE) {
-           printf("bos: command line too big\n");
-           return (E2BIG);
+       /* add the tmpdir option if given */
+       if (atmpDir != NULL) {
+           if ((strlen(tbuffer) + 9 + strlen(atmpDir) + 1) > BOZO_BSSIZE) {
+               printf("bos: command line too big\n");
+               return (E2BIG);
+           }
+           strcat(tbuffer, " -tmpdir ");
+           strcat(tbuffer, atmpDir);
        }
-       strcat(tbuffer, " -tmpdir ");
-       strcat(tbuffer, atmpDir);
-    }
 
-    /* add the orphans option if given */
-    if (orphans != NULL) {
-       if ((strlen(tbuffer) + 10 + strlen(orphans) + 1) > BOZO_BSSIZE) {
-           printf("bos: command line too big\n");
-           return (E2BIG);
+       /* add the orphans option if given */
+       if (orphans != NULL) {
+           if ((strlen(tbuffer) + 10 + strlen(orphans) + 1) > BOZO_BSSIZE) {
+               printf("bos: command line too big\n");
+               return (E2BIG);
+           }
+           strcat(tbuffer, " -orphans ");
+           strcat(tbuffer, orphans);
+       }
+
+       if (mrafsParm->Optdebug)
+           strcat(tbuffer, " -debug");
+       if (mrafsParm->Optnowrite)
+           strcat(tbuffer, " -nowrite");
+       if (mrafsParm->Optforce)
+           strcat(tbuffer, " -force");
+       if (mrafsParm->Optoktozap)
+           strcat(tbuffer, " -oktozap");
+       if (mrafsParm->Optrootfiles)
+           strcat(tbuffer, " -rootfiles");
+       if (mrafsParm->Optsalvagedirs)
+           strcat(tbuffer, " -salvagedirs");
+       if (mrafsParm->Optblockreads)
+           strcat(tbuffer, " -blockreads");
+       if (mrafsParm->OptListResidencies)
+           strcat(tbuffer, " -ListResidencies");
+       if (mrafsParm->OptSalvageRemote)
+           strcat(tbuffer, " -SalvageRemote");
+       if (mrafsParm->OptSalvageArchival)
+           strcat(tbuffer, " -SalvageArchival");
+       if (mrafsParm->OptIgnoreCheck)
+           strcat(tbuffer, " -IgnoreCheck");
+       if (mrafsParm->OptForceOnLine)
+           strcat(tbuffer, " -ForceOnLine");
+       if (mrafsParm->OptUseRootDirACL)
+           strcat(tbuffer, " -UseRootDirACL");
+       if (mrafsParm->OptTraceBadLinkCounts)
+           strcat(tbuffer, " -TraceBadLinkCounts");
+       if (mrafsParm->OptDontAskFS)
+           strcat(tbuffer, " -DontAskFS");
+       if (mrafsParm->OptLogLevel) {
+           sprintf(pbuffer, " -LogLevel %ld", mrafsParm->OptLogLevel);
+           strcat(tbuffer, pbuffer);
+       }
+       if (mrafsParm->OptRxDebug)
+           strcat(tbuffer, " -rxdebug");
+       if (mrafsParm->OptResidencies) {
+           sprintf(pbuffer, " -Residencies %lu", mrafsParm->OptResidencies);
+           strcat(tbuffer, pbuffer);
        }
-       strcat(tbuffer, " -orphans ");
-       strcat(tbuffer, orphans);
-    }
-
-    if (mrafsParm.Optdebug)
-       strcat(tbuffer, " -debug");
-    if (mrafsParm.Optnowrite)
-       strcat(tbuffer, " -nowrite");
-    if (mrafsParm.Optforce)
-       strcat(tbuffer, " -force");
-    if (mrafsParm.Optoktozap)
-       strcat(tbuffer, " -oktozap");
-    if (mrafsParm.Optrootfiles)
-       strcat(tbuffer, " -rootfiles");
-    if (mrafsParm.Optsalvagedirs)
-       strcat(tbuffer, " -salvagedirs");
-    if (mrafsParm.Optblockreads)
-       strcat(tbuffer, " -blockreads");
-    if (mrafsParm.OptListResidencies)
-       strcat(tbuffer, " -ListResidencies");
-    if (mrafsParm.OptSalvageRemote)
-       strcat(tbuffer, " -SalvageRemote");
-    if (mrafsParm.OptSalvageArchival)
-       strcat(tbuffer, " -SalvageArchival");
-    if (mrafsParm.OptIgnoreCheck)
-       strcat(tbuffer, " -IgnoreCheck");
-    if (mrafsParm.OptForceOnLine)
-       strcat(tbuffer, " -ForceOnLine");
-    if (mrafsParm.OptUseRootDirACL)
-       strcat(tbuffer, " -UseRootDirACL");
-    if (mrafsParm.OptTraceBadLinkCounts)
-       strcat(tbuffer, " -TraceBadLinkCounts");
-    if (mrafsParm.OptDontAskFS)
-       strcat(tbuffer, " -DontAskFS");
-    if (mrafsParm.OptLogLevel) {
-       sprintf(pbuffer, " -LogLevel %ld", mrafsParm.OptLogLevel);
-       strcat(tbuffer, pbuffer);
-    }
-    if (mrafsParm.OptRxDebug)
-       strcat(tbuffer, " -rxdebug");
-    if (mrafsParm.OptResidencies) {
-       sprintf(pbuffer, " -Residencies %lu", mrafsParm.OptResidencies);
-       strcat(tbuffer, pbuffer);
     }
 
     parms[0] = tbuffer;
@@ -1481,22 +1508,36 @@ SalvageCmd(as)
     char tname[BOZO_BSSIZE];
     afs_int32 newID;
     extern struct ubik_client *cstruct;
-    afs_int32 curGoal, showlog = 0, mrafs = 0;
+    afs_int32 curGoal, showlog = 0, dafs = 0, mrafs = 0;
     char *parallel;
     char *tmpDir;
     char *orphans;
     char *tp;
+    char * serviceName;
+    struct MRAFSSalvageParms mrafsParm;
 
     memset(&mrafsParm, 0, sizeof(mrafsParm));
 
     /* parm 0 is machine name, 1 is partition, 2 is volume, 3 is -all flag */
     tconn = GetConn(as, 0);
 
-    /* Find out whether fileserver is running MR-AFS (has a scanner instance) */
-    /* XXX this should really be done some other way, potentially by RPC */
     tp = &tname[0];
-    if (code = BOZO_GetInstanceParm(tconn, "fs", 3, &tp) == 0)
-       mrafs = 1;
+
+    /* find out whether fileserver is running demand attach fs */
+    if (code = BOZO_GetInstanceParm(tconn, "dafs", 0, &tp) == 0) {
+       dafs = 1;
+       serviceName = "dafs";
+       /* Find out whether fileserver is running MR-AFS (has a scanner instance) */
+       /* XXX this should really be done some other way, potentially by RPC */
+       if (code = BOZO_GetInstanceParm(tconn, serviceName, 4, &tp) == 0)
+           mrafs = 1;
+    } else {
+       serviceName = "fs";
+       /* Find out whether fileserver is running MR-AFS (has a scanner instance) */
+       /* XXX this should really be done some other way, potentially by RPC */
+       if (code = BOZO_GetInstanceParm(tconn, serviceName, 3, &tp) == 0)
+           mrafs = 1;
+    }
 
     /* we can do a volume, a partition or the whole thing, but not mixtures
      * thereof */
@@ -1542,6 +1583,14 @@ SalvageCmd(as)
        orphans = as->parms[8].items->data;
     }
 
+    if (dafs) {
+       if (!as->parms[9].items) { /* -forceDAFS flag */
+           printf("This is a demand attach fileserver.  Are you sure you want to proceed with a manual salvage?\n");
+           printf("must specify -forceDAFS flag in order to proceed.\n");
+           return EINVAL;
+       }
+    }
+
     if (mrafs) {
        if (as->parms[MRAFS_OFFSET].items)
            mrafsParm.Optdebug = 1;
@@ -1597,7 +1646,7 @@ SalvageCmd(as)
     } else {
        int stop = 0;
 
-       for (i = 9; i < ADDPARMOFFSET; i++) {
+       for (i = MRAFS_OFFSET; i < ADDPARMOFFSET; i++) {
            if (as->parms[i].items) {
                printf(" %s only possible for MR-AFS fileserver.\n",
                       as->parms[i].name);
@@ -1610,12 +1659,12 @@ SalvageCmd(as)
 
     if (as->parms[4].items) {
        /* salvage whole enchilada */
-       curGoal = GetServerGoal(tconn, "fs");
+       curGoal = GetServerGoal(tconn, serviceName);
        if (curGoal == BSTAT_NORMAL) {
-           printf("bos: shutting down fs.\n");
-           code = BOZO_SetTStatus(tconn, "fs", BSTAT_SHUTDOWN);
+           printf("bos: shutting down '%s'.\n", serviceName);
+           code = BOZO_SetTStatus(tconn, serviceName, BSTAT_SHUTDOWN);
            if (code) {
-               printf("bos: failed to stop 'fs' (%s)\n", em(code));
+               printf("bos: failed to stop '%s' (%s)\n", serviceName, em(code));
                return code;
            }
            code = BOZO_WaitAll(tconn); /* wait for shutdown to complete */
@@ -1626,12 +1675,12 @@ SalvageCmd(as)
        /* now do the salvage operation */
        printf("Starting salvage.\n");
        rc = DoSalvage(tconn, NULL, NULL, outName, showlog, parallel, tmpDir,
-                      orphans);
+                      orphans, dafs, &mrafsParm);
        if (curGoal == BSTAT_NORMAL) {
-           printf("bos: restarting fs.\n");
-           code = BOZO_SetTStatus(tconn, "fs", BSTAT_NORMAL);
+           printf("bos: restarting %s.\n", serviceName);
+           code = BOZO_SetTStatus(tconn, serviceName, BSTAT_NORMAL);
            if (code) {
-               printf("bos: failed to restart 'fs' (%s)\n", em(code));
+               printf("bos: failed to restart '%s' (%s)\n", serviceName, em(code));
                return code;
            }
        }
@@ -1651,13 +1700,13 @@ SalvageCmd(as)
                   as->parms[1].items->data);
            return -1;
        }
-       curGoal = GetServerGoal(tconn, "fs");
+       curGoal = GetServerGoal(tconn, serviceName);
        /* salvage a whole partition (specified by parms[1]) */
        if (curGoal == BSTAT_NORMAL) {
-           printf("bos: shutting down fs.\n");
-           code = BOZO_SetTStatus(tconn, "fs", BSTAT_SHUTDOWN);
+           printf("bos: shutting down '%s'.\n", serviceName);
+           code = BOZO_SetTStatus(tconn, serviceName, BSTAT_SHUTDOWN);
            if (code) {
-               printf("bos: can't stop 'fs' (%s)\n", em(code));
+               printf("bos: can't stop '%s' (%s)\n", serviceName, em(code));
                return code;
            }
            code = BOZO_WaitAll(tconn); /* wait for shutdown to complete */
@@ -1668,12 +1717,12 @@ SalvageCmd(as)
        /* now do the salvage operation */
        printf("Starting salvage.\n");
        rc = DoSalvage(tconn, as->parms[1].items->data, NULL, outName,
-                      showlog, parallel, tmpDir, orphans);
+                      showlog, parallel, tmpDir, orphans, dafs, &mrafsParm);
        if (curGoal == BSTAT_NORMAL) {
-           printf("bos: restarting fs.\n");
-           code = BOZO_SetTStatus(tconn, "fs", BSTAT_NORMAL);
+           printf("bos: restarting '%s'.\n", serviceName);
+           code = BOZO_SetTStatus(tconn, serviceName, BSTAT_NORMAL);
            if (code) {
-               printf("bos: failed to restart 'fs' (%s)\n", em(code));
+               printf("bos: failed to restart '%s' (%s)\n", serviceName, em(code));
                return code;
            }
        }
@@ -1723,7 +1772,7 @@ SalvageCmd(as)
        }
        printf("Starting salvage.\n");
        rc = DoSalvage(tconn, as->parms[1].items->data, tname, outName,
-                      showlog, parallel, tmpDir, orphans);
+                      showlog, parallel, tmpDir, orphans, dafs, &mrafsParm);
        if (rc)
            return rc;
     }
@@ -2153,6 +2202,8 @@ main(argc, argv)
                "directory to place tmp files");
     cmd_AddParm(ts, "-orphans", CMD_SINGLE, CMD_OPTIONAL,
                "ignore | remove | attach");
+    cmd_AddParm(ts, "-forceDAFS", CMD_FLAG, CMD_OPTIONAL,
+               "(DAFS) force salvage of demand attach fileserver");
     cmd_AddParm(ts, "-debug", CMD_FLAG, CMD_OPTIONAL,
                "(MR-AFS) Run in Debugging mode");
     cmd_AddParm(ts, "-nowrite", CMD_FLAG, CMD_OPTIONAL,
index 635a681..2351eeb 100644 (file)
@@ -51,7 +51,7 @@ RCSID
 #define BOZO_LWP_STACKSIZE     16000
 extern int BOZO_ExecuteRequest();
 extern int RXSTATS_ExecuteRequest();
-extern struct bnode_ops fsbnode_ops, ezbnode_ops, cronbnode_ops;
+extern struct bnode_ops fsbnode_ops, dafsbnode_ops, ezbnode_ops, cronbnode_ops;
 
 void bozo_Log();
 
@@ -895,6 +895,7 @@ main(int argc, char **argv, char **envp)
     }
 
     bnode_Register("fs", &fsbnode_ops, 3);
+    bnode_Register("dafs", &dafsbnode_ops, 4);
     bnode_Register("simple", &ezbnode_ops, 1);
     bnode_Register("cron", &cronbnode_ops, 2);
 
index 2ac65e4..e38670e 100644 (file)
@@ -41,13 +41,6 @@ RCSID
 #include <afs/afsutil.h>
 #include "bnode.h"
 
-static int fs_timeout(), fs_getstat(), fs_setstat(), fs_delete();
-static int fs_procexit(), fs_getstring(), fs_getparm(), fs_restartp();
-static int fs_hascore();
-struct bnode *fs_create();
-
-static SetNeedsClock();
-static NudgeProcs();
 
 static int emergency = 0;
 
@@ -76,49 +69,105 @@ static int emergency = 0;
     The needsSalvage flag is cleared when the salvager exits.
 */
 
-struct bnode_ops fsbnode_ops = {
-    fs_create,
-    fs_timeout,
-    fs_getstat,
-    fs_setstat,
-    fs_delete,
-    fs_procexit,
-    fs_getstring,
-    fs_getparm,
-    fs_restartp,
-    fs_hascore,
-};
-
 struct fsbnode {
     struct bnode b;
     afs_int32 timeSDStarted;   /* time shutdown operation started */
     char *filecmd;             /* command to start primary file server */
     char *volcmd;              /* command to start secondary vol server */
+    char *salsrvcmd;            /* command to start salvageserver (demand attach fs) */
     char *salcmd;              /* command to start salvager */
     char *scancmd;             /* command to start scanner (MR-AFS) */
     struct bnode_proc *fileProc;       /* process for file server */
     struct bnode_proc *volProc;        /* process for vol server */
+    struct bnode_proc *salsrvProc;     /* process for salvageserver (demand attach fs) */
     struct bnode_proc *salProc;        /* process for salvager */
     struct bnode_proc *scanProc;       /* process for scanner (MR-AFS) */
     afs_int32 lastFileStart;   /* last start for file */
     afs_int32 lastVolStart;    /* last start for vol */
+    afs_int32 lastSalsrvStart; /* last start for salvageserver (demand attach fs) */
     afs_int32 lastScanStart;   /* last start for scanner (MR-AFS) */
     char fileRunning;          /* file process is running */
     char volRunning;           /* volser is running */
+    char salsrvRunning;                /* salvageserver is running (demand attach fs) */
     char salRunning;           /* salvager is running */
     char scanRunning;          /* scanner is running (MR_AFS) */
     char fileSDW;              /* file shutdown wait */
     char volSDW;               /* vol shutdown wait */
+    char salsrvSDW;            /* salvageserver shutdown wait (demand attach fs) */
     char salSDW;               /* waiting for the salvager to shutdown */
     char scanSDW;              /* scanner shutdown wait (MR_AFS) */
     char fileKillSent;         /* kill signal has been sent */
     char volKillSent;
+    char salsrvKillSent;        /* kill signal has been sent (demand attach fs) */
     char salKillSent;
     char scanKillSent;         /* kill signal has been sent (MR_AFS) */
     char needsSalvage;         /* salvage before running */
     char needsClock;           /* do we need clock ticks */
 };
 
+
+
+struct bnode * fs_create(char *ainstance, char *afilecmd, char *avolcmd, 
+                        char *asalcmd, char *ascancmd);
+struct bnode * dafs_create(char *ainstance, char *afilecmd, char *avolcmd, 
+                          char * asalsrvcmd, char *asalcmd, char *ascancmd);
+
+static int fs_hascore(register struct ezbnode *abnode);
+static int fs_restartp(register struct fsbnode *abnode);
+static int SetSalFlag(register struct fsbnode *abnode, register int aflag);
+static int RestoreSalFlag(register struct fsbnode *abnode);
+static int fs_delete(struct fsbnode *abnode);
+static int fs_timeout(struct fsbnode *abnode);
+static int fs_getstat(struct fsbnode *abnode, afs_int32 * astatus);
+static int fs_setstat(register struct fsbnode *abnode, afs_int32 astatus);
+static int fs_procexit(struct fsbnode *abnode, struct bnode_proc *aproc);
+static int fs_getstring(struct fsbnode *abnode, char *abuffer, afs_int32 alen);
+
+
+static int fs_getparm(struct fsbnode *abnode, afs_int32 aindex, 
+                     char *abuffer, afs_int32 alen);
+static int dafs_getparm(struct fsbnode *abnode, afs_int32 aindex, 
+                       char *abuffer, afs_int32 alen);
+
+#ifdef AFS_NT40_ENV
+static void AppendExecutableExtension(char *cmd)
+#else
+#define AppendExecutableExtension(x)
+#endif
+
+static void SetNeedsClock(register struct fsbnode *ab);
+static int NudgeProcs(register struct fsbnode *abnode);
+
+
+
+struct bnode_ops fsbnode_ops = {
+    fs_create,
+    fs_timeout,
+    fs_getstat,
+    fs_setstat,
+    fs_delete,
+    fs_procexit,
+    fs_getstring,
+    fs_getparm,
+    fs_restartp,
+    fs_hascore,
+};
+
+/* demand attach fs bnode ops */
+struct bnode_ops dafsbnode_ops = {
+    dafs_create,
+    fs_timeout,
+    fs_getstat,
+    fs_setstat,
+    fs_delete,
+    fs_procexit,
+    fs_getstring,
+    dafs_getparm,
+    fs_restartp,
+    fs_hascore,
+};
+
+
 /* Function to tell whether this bnode has a core file or not.  You might
  * think that this could be in bnode.c, and decide what core files to check
  * for based on the bnode's coreName property, but that doesn't work because
@@ -140,6 +189,11 @@ fs_hascore(register struct ezbnode *abnode)
     if (access(tbuffer, 0) == 0)
        return 1;
 
+    /* see if salvageserver left a core file */
+    bnode_CoreName(abnode, "salsrv", tbuffer);
+    if (access(tbuffer, 0) == 0)
+       return 1;
+
     /* see if salvager left a core file */
     bnode_CoreName(abnode, "salv", tbuffer);
     if (access(tbuffer, 0) == 0)
@@ -198,6 +252,25 @@ fs_restartp(register struct fsbnode *abnode)
     if (code)
        return code;
 
+    if (abnode->salsrvcmd) {    /* only in demand attach fs */
+       /* now do same for salsrvcmd (demand attach fs) */
+       code = bnode_ParseLine(abnode->salsrvcmd, &tt);
+       if (code)
+           return 0;
+       if (!tt)
+           return 0;
+       code = stat(tt->key, &tstat);
+       if (code) {
+           bnode_FreeTokens(tt);
+           return 0;
+       }
+       if (tstat.st_ctime > abnode->lastScanStart)
+           code = 1;
+       else
+           code = 0;
+       bnode_FreeTokens(tt);
+    }
+
     if (abnode->scancmd) {     /* Only in MR-AFS */
        /* now do same for scancmd (MR-AFS) */
        code = bnode_ParseLine(abnode->scancmd, &tt);
@@ -228,14 +301,17 @@ SetSalFlag(register struct fsbnode *abnode, register int aflag)
     char tbuffer[AFSDIR_PATH_MAX];
     int fd;
 
-    abnode->needsSalvage = aflag;
-    strcompose(tbuffer, AFSDIR_PATH_MAX, AFSDIR_SERVER_LOCAL_DIRPATH, "/",
-              SALFILE, abnode->b.name, NULL);
-    if (aflag) {
-       fd = open(tbuffer, O_CREAT | O_TRUNC | O_RDWR, 0666);
-       close(fd);
-    } else {
-       unlink(tbuffer);
+    /* don't use the salvage flag for demand attach fs */
+    if (abnode->salsrvcmd == NULL) {
+       abnode->needsSalvage = aflag;
+       strcompose(tbuffer, AFSDIR_PATH_MAX, AFSDIR_SERVER_LOCAL_DIRPATH, "/",
+                  SALFILE, abnode->b.name, NULL);
+       if (aflag) {
+           fd = open(tbuffer, O_CREAT | O_TRUNC | O_RDWR, 0666);
+           close(fd);
+       } else {
+           unlink(tbuffer);
+       }
     }
     return 0;
 }
@@ -246,13 +322,18 @@ RestoreSalFlag(register struct fsbnode *abnode)
 {
     char tbuffer[AFSDIR_PATH_MAX];
 
-    strcompose(tbuffer, AFSDIR_PATH_MAX, AFSDIR_SERVER_LOCAL_DIRPATH, "/",
-              SALFILE, abnode->b.name, NULL);
-    if (access(tbuffer, 0) == 0) {
-       /* file exists, so need to salvage */
-       abnode->needsSalvage = 1;
-    } else {
+    /* never set needs salvage flag for demand attach fs */
+    if (abnode->salsrvcmd != NULL) {
        abnode->needsSalvage = 0;
+    } else {
+       strcompose(tbuffer, AFSDIR_PATH_MAX, AFSDIR_SERVER_LOCAL_DIRPATH, "/",
+                  SALFILE, abnode->b.name, NULL);
+       if (access(tbuffer, 0) == 0) {
+           /* file exists, so need to salvage */
+           abnode->needsSalvage = 1;
+       } else {
+           abnode->needsSalvage = 0;
+       }
     }
     return 0;
 }
@@ -272,6 +353,8 @@ fs_delete(struct fsbnode *abnode)
     free(abnode->filecmd);
     free(abnode->volcmd);
     free(abnode->salcmd);
+    if (abnode->salsrvcmd)
+       free(abnode->salsrvcmd);
     if (abnode->scancmd)
        free(abnode->scancmd);
     free(abnode);
@@ -304,95 +387,235 @@ fs_create(char *ainstance, char *afilecmd, char *avolcmd, char *asalcmd,
     char *fileCmdpath, *volCmdpath, *salCmdpath, *scanCmdpath;
     int bailout = 0;
 
-    fileCmdpath = volCmdpath = salCmdpath = NULL;
+    te = fileCmdpath = volCmdpath = salCmdpath = scanCmdpath = NULL;
 
     /* construct local paths from canonical (wire-format) paths */
     if (ConstructLocalBinPath(afilecmd, &fileCmdpath)) {
        bozo_Log("BNODE: command path invalid '%s'\n", afilecmd);
        bailout = 1;
+       goto done;
     }
     if (ConstructLocalBinPath(avolcmd, &volCmdpath)) {
        bozo_Log("BNODE: command path invalid '%s'\n", avolcmd);
        bailout = 1;
+       goto done;
     }
     if (ConstructLocalBinPath(asalcmd, &salCmdpath)) {
        bozo_Log("BNODE: command path invalid '%s'\n", asalcmd);
        bailout = 1;
+       goto done;
     }
 
     if (ascancmd && strlen(ascancmd)) {
        if (ConstructLocalBinPath(ascancmd, &scanCmdpath)) {
            bozo_Log("BNODE: command path invalid '%s'\n", ascancmd);
            bailout = 1;
+           goto done;
        }
     }
 
     if (!bailout) {
        sscanf(fileCmdpath, "%s", cmdname);
-#ifdef AFS_NT40_ENV
        AppendExecutableExtension(cmdname);
-#endif
        if (stat(cmdname, &tstat)) {
            bozo_Log("BNODE: file server binary '%s' not found\n", cmdname);
            bailout = 1;
+           goto done;
        }
 
        sscanf(volCmdpath, "%s", cmdname);
-#ifdef AFS_NT40_ENV
        AppendExecutableExtension(cmdname);
-#endif
        if (stat(cmdname, &tstat)) {
            bozo_Log("BNODE: volume server binary '%s' not found\n", cmdname);
            bailout = 1;
+           goto done;
        }
 
        sscanf(salCmdpath, "%s", cmdname);
-#ifdef AFS_NT40_ENV
        AppendExecutableExtension(cmdname);
-#endif
        if (stat(cmdname, &tstat)) {
            bozo_Log("BNODE: salvager binary '%s' not found\n", cmdname);
            bailout = 1;
+           goto done;
        }
 
        if (ascancmd && strlen(ascancmd)) {
            sscanf(scanCmdpath, "%s", cmdname);
-#ifdef AFS_NT40_ENV
            AppendExecutableExtension(cmdname);
-#endif
            if (stat(cmdname, &tstat)) {
                bozo_Log("BNODE: scanner binary '%s' not found\n", cmdname);
                bailout = 1;
+               goto done;
            }
        }
     }
 
+    te = (struct fsbnode *)malloc(sizeof(struct fsbnode));
+    if (te == NULL) {
+       bailout = 1;
+       goto done;
+    }
+    memset(te, 0, sizeof(struct fsbnode));
+    te->filecmd = fileCmdpath;
+    te->volcmd = volCmdpath;
+    te->salsrvcmd = NULL;
+    te->salcmd = salCmdpath;
+    if (ascancmd && strlen(ascancmd))
+       te->scancmd = scanCmdpath;
+    else
+       te->scancmd = NULL;
+    if (bnode_InitBnode(te, &fsbnode_ops, ainstance) != 0) {
+       bailout = 1;
+       goto done;
+    }
+    bnode_SetTimeout(te, POLLTIME);    /* ask for timeout activations every 10 seconds */
+    RestoreSalFlag(te);                /* restore needsSalvage flag based on file's existence */
+    SetNeedsClock(te);         /* compute needsClock field */
+
+ done:
     if (bailout) {
-       free(fileCmdpath);
-       free(volCmdpath);
-       free(salCmdpath);
+       if (te)
+           free(te);
+       if (fileCmdpath)
+           free(fileCmdpath);
+       if (volCmdpath)
+           free(volCmdpath);
+       if (salCmdpath)
+           free(salCmdpath);
+       if (scanCmdpath)
+           free(scanCmdpath);
        return NULL;
     }
 
+    return (struct bnode *)te;
+}
+
+/* create a demand attach fs bnode */
+struct bnode *
+dafs_create(char *ainstance, char *afilecmd, char *avolcmd, 
+           char * asalsrvcmd, char *asalcmd, char *ascancmd)
+{
+    struct stat tstat;
+    register struct fsbnode *te;
+    char cmdname[AFSDIR_PATH_MAX];
+    char *fileCmdpath, *volCmdpath, *salsrvCmdpath, *salCmdpath, *scanCmdpath;
+    int bailout = 0;
+
+    te = fileCmdpath = volCmdpath = salsrvCmdpath = salCmdpath = scanCmdpath = NULL;
+
+    /* construct local paths from canonical (wire-format) paths */
+    if (ConstructLocalBinPath(afilecmd, &fileCmdpath)) {
+       bozo_Log("BNODE: command path invalid '%s'\n", afilecmd);
+       bailout = 1;
+       goto done;
+    }
+    if (ConstructLocalBinPath(avolcmd, &volCmdpath)) {
+       bozo_Log("BNODE: command path invalid '%s'\n", avolcmd);
+       bailout = 1;
+       goto done;
+    }
+    if (ConstructLocalBinPath(asalsrvcmd, &salsrvCmdpath)) {
+       bozo_Log("BNODE: command path invalid '%s'\n", asalsrvcmd);
+       bailout = 1;
+       goto done;
+    }
+    if (ConstructLocalBinPath(asalcmd, &salCmdpath)) {
+       bozo_Log("BNODE: command path invalid '%s'\n", asalcmd);
+       bailout = 1;
+       goto done;
+    }
+
+    if (ascancmd && strlen(ascancmd)) {
+       if (ConstructLocalBinPath(ascancmd, &scanCmdpath)) {
+           bozo_Log("BNODE: command path invalid '%s'\n", ascancmd);
+           bailout = 1;
+           goto done;
+       }
+    }
+
+    if (!bailout) {
+       sscanf(fileCmdpath, "%s", cmdname);
+       AppendExecutableExtension(cmdname);
+       if (stat(cmdname, &tstat)) {
+           bozo_Log("BNODE: file server binary '%s' not found\n", cmdname);
+           bailout = 1;
+           goto done;
+       }
+
+       sscanf(volCmdpath, "%s", cmdname);
+       AppendExecutableExtension(cmdname);
+       if (stat(cmdname, &tstat)) {
+           bozo_Log("BNODE: volume server binary '%s' not found\n", cmdname);
+           bailout = 1;
+           goto done;
+       }
+
+       sscanf(salsrvCmdpath, "%s", cmdname);
+       AppendExecutableExtension(cmdname);
+       if (stat(cmdname, &tstat)) {
+           bozo_Log("BNODE: salvageserver binary '%s' not found\n", cmdname);
+           bailout = 1;
+           goto done;
+       }
+
+       sscanf(salCmdpath, "%s", cmdname);
+       AppendExecutableExtension(cmdname);
+       if (stat(cmdname, &tstat)) {
+           bozo_Log("BNODE: salvager binary '%s' not found\n", cmdname);
+           bailout = 1;
+           goto done;
+       }
+
+       if (ascancmd && strlen(ascancmd)) {
+           sscanf(scanCmdpath, "%s", cmdname);
+           AppendExecutableExtension(cmdname);
+           if (stat(cmdname, &tstat)) {
+               bozo_Log("BNODE: scanner binary '%s' not found\n", cmdname);
+               bailout = 1;
+               goto done;
+           }
+       }
+    }
+
     te = (struct fsbnode *)malloc(sizeof(struct fsbnode));
+    if (te == NULL) {
+       bailout = 1;
+       goto done;
+    }
     memset(te, 0, sizeof(struct fsbnode));
     te->filecmd = fileCmdpath;
     te->volcmd = volCmdpath;
+    te->salsrvcmd = salsrvCmdpath;
     te->salcmd = salCmdpath;
     if (ascancmd && strlen(ascancmd))
        te->scancmd = scanCmdpath;
     else
        te->scancmd = NULL;
-    if (bnode_InitBnode(te, &fsbnode_ops, ainstance) != 0) {
-       free(te);
-       free(fileCmdpath);
-       free(volCmdpath);
-       free(salCmdpath);
-       return NULL;
+    if (bnode_InitBnode(te, &dafsbnode_ops, ainstance) != 0) {
+       bailout = 1;
+       goto done;
     }
     bnode_SetTimeout(te, POLLTIME);    /* ask for timeout activations every 10 seconds */
     RestoreSalFlag(te);                /* restore needsSalvage flag based on file's existence */
     SetNeedsClock(te);         /* compute needsClock field */
+
+ done:
+    if (bailout) {
+       if (te)
+           free(te);
+       if (fileCmdpath)
+           free(fileCmdpath);
+       if (volCmdpath)
+           free(volCmdpath);
+       if (salsrvCmdpath)
+           free(salsrvCmdpath);
+       if (salCmdpath)
+           free(salCmdpath);
+       if (scanCmdpath)
+           free(scanCmdpath);
+       return NULL;
+    }
+
     return (struct bnode *)te;
 }
 
@@ -431,6 +654,15 @@ fs_timeout(struct fsbnode *abnode)
                 FSSDTIME);
        }
     }
+    if (abnode->salsrvSDW) {
+       if (!abnode->salsrvKillSent && now - abnode->timeSDStarted > SDTIME) {
+           bnode_StopProc(abnode->salsrvProc, SIGKILL);
+           abnode->salsrvKillSent = 1;
+           bozo_Log
+               ("bos shutdown: salvageserver failed to shutdown within %d seconds\n",
+                SDTIME);
+       }
+    }
     if (abnode->scanSDW) {
        if (!abnode->scanKillSent && now - abnode->timeSDStarted > SDTIME) {
            bnode_StopProc(abnode->scanProc, SIGKILL);
@@ -449,15 +681,17 @@ fs_getstat(struct fsbnode *abnode, afs_int32 * astatus)
 {
     register afs_int32 temp;
     if (abnode->volSDW || abnode->fileSDW || abnode->salSDW
-       || abnode->scanSDW)
+       || abnode->scanSDW || abnode->salsrvSDW)
        temp = BSTAT_SHUTTINGDOWN;
     else if (abnode->salRunning)
        temp = BSTAT_NORMAL;
     else if (abnode->volRunning && abnode->fileRunning
-            && (!abnode->scancmd || abnode->scanRunning))
+            && (!abnode->scancmd || abnode->scanRunning)
+            && (!abnode->salsrvcmd || abnode->salsrvRunning))
        temp = BSTAT_NORMAL;
     else if (!abnode->salRunning && !abnode->volRunning
-            && !abnode->fileRunning && !abnode->scanRunning)
+            && !abnode->fileRunning && !abnode->scanRunning
+            && !abnode->salsrvRunning)
        temp = BSTAT_SHUTDOWN;
     else
        temp = BSTAT_STARTINGUP;
@@ -508,6 +742,11 @@ fs_procexit(struct fsbnode *abnode, struct bnode_proc *aproc)
        abnode->scanRunning = 0;
        abnode->scanSDW = 0;
        abnode->scanKillSent = 0;
+    } else if (aproc == abnode->salsrvProc) {
+       abnode->salsrvProc = 0;
+       abnode->salsrvRunning = 0;
+       abnode->salsrvSDW = 0;
+       abnode->salsrvKillSent = 0;
     }
 
     /* now restart anyone who needs to restart */
@@ -515,14 +754,15 @@ fs_procexit(struct fsbnode *abnode, struct bnode_proc *aproc)
 }
 
 /* make sure we're periodically checking the state if we need to */
-static int
+static void
 SetNeedsClock(register struct fsbnode *ab)
 {
     if (ab->b.goal == 1 && ab->fileRunning && ab->volRunning
-       && (!ab->scancmd || ab->scanRunning))
+       && (!ab->scancmd || ab->scanRunning)
+       && (!ab->salsrvcmd || ab->salsrvRunning))
        ab->needsClock = 0;     /* running normally */
     else if (ab->b.goal == 0 && !ab->fileRunning && !ab->volRunning
-            && !ab->salRunning && !ab->scanRunning)
+            && !ab->salRunning && !ab->scanRunning && !ab->salsrvRunning)
        ab->needsClock = 0;     /* halted normally */
     else
        ab->needsClock = 1;     /* other */
@@ -562,6 +802,18 @@ NudgeProcs(register struct fsbnode *abnode)
                    abnode->volRunning = 1;
                }
            }
+           if (abnode->salsrvcmd) {
+               if (!abnode->salsrvRunning) {
+                   abnode->lastSalsrvStart = FT_ApproxTime();
+                   code =
+                       bnode_NewProc(abnode, abnode->salsrvcmd, "salsrv",
+                                     &tp);
+                   if (code == 0) {
+                       abnode->salsrvProc = tp;
+                       abnode->salsrvRunning = 1;
+                   }
+               }
+           }
            if (abnode->scancmd) {
                if (!abnode->scanRunning) {
                    abnode->lastScanStart = FT_ApproxTime();
@@ -576,7 +828,8 @@ NudgeProcs(register struct fsbnode *abnode)
            }
        } else {                /* file is not running */
            /* see how to start */
-           if (!abnode->needsSalvage) {
+           /* for demand attach fs, needsSalvage flag is ignored */
+           if (!abnode->needsSalvage || abnode->salsrvcmd) {
                /* no crash apparent, just start up normally */
                if (!abnode->fileRunning) {
                    abnode->lastFileStart = FT_ApproxTime();
@@ -596,6 +849,16 @@ NudgeProcs(register struct fsbnode *abnode)
                        abnode->volRunning = 1;
                    }
                }
+               if (abnode->salsrvcmd && !abnode->salsrvRunning) {
+                   abnode->lastSalsrvStart = FT_ApproxTime();
+                   code =
+                       bnode_NewProc(abnode, abnode->salsrvcmd, "salsrv",
+                                     &tp);
+                   if (code == 0) {
+                       abnode->salsrvProc = tp;
+                       abnode->salsrvRunning = 1;
+                   }
+               }
                if (abnode->scancmd && !abnode->scanRunning) {
                    abnode->lastScanStart = FT_ApproxTime();
                    code =
@@ -656,6 +919,11 @@ NudgeProcs(register struct fsbnode *abnode)
            abnode->volSDW = 1;
            abnode->timeSDStarted = now;
        }
+       if (abnode->salsrvRunning && !abnode->salsrvSDW) {
+           bnode_StopProc(abnode->salsrvProc, SIGTERM);
+           abnode->salsrvSDW = 1;
+           abnode->timeSDStarted = now;
+       }
        if (abnode->scanRunning && !abnode->scanSDW) {
            bnode_StopProc(abnode->scanProc, SIGTERM);
            abnode->scanSDW = 1;
@@ -724,3 +992,22 @@ fs_getparm(struct fsbnode *abnode, afs_int32 aindex, char *abuffer,
        return BZDOM;
     return 0;
 }
+
+static int
+dafs_getparm(struct fsbnode *abnode, afs_int32 aindex, char *abuffer,
+            afs_int32 alen)
+{
+    if (aindex == 0)
+       strcpy(abuffer, abnode->filecmd);
+    else if (aindex == 1)
+       strcpy(abuffer, abnode->volcmd);
+    else if (aindex == 2)
+       strcpy(abuffer, abnode->salsrvcmd);
+    else if (aindex == 3)
+       strcpy(abuffer, abnode->salcmd);
+    else if (aindex == 4 && abnode->scancmd)
+       strcpy(abuffer, abnode->scancmd);
+    else
+       return BZDOM;
+    return 0;
+}
index 9fe6161..22daf81 100644 (file)
@@ -971,6 +971,18 @@ case $AFS_SYSNAME in
        ;;
 esac
 
+
+
+dnl pthreads fixes
+case $AFS_SYSNAME in
+dnl we'll go ahead and turn on XOPEN2K and ISO_C99
+dnl if this causes problems, we should scale back to _XOPEN_SOURCE=500
+       *linux*)
+               MT_CFLAGS="${MT_CFLAGS} -D_XOPEN_SOURCE=600 -D_BSD_SOURCE"
+       ;;
+esac
+
+
 dnl Disable the default for debugging/optimization if not enabled
 if test "x$enable_debug_kernel" = "xno"; then
   KERN_DBG=
index ecfe978..cd49793 100644 (file)
@@ -25,8 +25,6 @@
 #ifdef AFS_NAMEI_ENV
 #define AFS_64BIT_IOPS_ENV     1
 #endif
-#define BITMAP_LATER           1
-#define FAST_RESTART           1
 
 #define AFS_HAVE_FLOCK_SYSID    1
 
index 0ee9986..b20bb37 100644 (file)
@@ -26,8 +26,6 @@
 #ifdef AFS_NAMEI_ENV
 #define AFS_64BIT_IOPS_ENV     1
 #endif
-#define BITMAP_LATER           1
-#define FAST_RESTART           1
 
 #define AFS_HAVE_FLOCK_SYSID    1
 
index ba4f151..ecfb367 100644 (file)
@@ -27,8 +27,6 @@
 #ifdef AFS_NAMEI_ENV
 #define AFS_64BIT_IOPS_ENV     1
 #endif
-#define BITMAP_LATER           1
-#define FAST_RESTART           1
 
 #define AFS_HAVE_FLOCK_SYSID    1
 
index 7b256b6..9266b0c 100644 (file)
@@ -56,8 +56,16 @@ typedef unsigned __int64 afs_uint64;
 typedef long long afs_int64;
 typedef unsigned long long afs_uint64;
 #endif
-#define ZeroInt64(a)       (a) = 0
+#define ZeroInt64(a)       (a = 0)
 #define AssignInt64(a, b) *(b) = (a) 
+#define IncInt64(a) (*(a))++
+#define IncUInt64(a) (*(a))++
+#define DecInt64(a) (*(a))--
+#define DecUInt64(a) (*(a))--
+#define GTInt64(a,b) ((a) > (b))
+#define GEInt64(a,b) ((a) >= (b))
+#define LEInt64(a,b) ((a) <= (b))
+#define LTInt64(a,b) ((a) < (b))
 #define AddInt64(a,b,c) *(c) = (afs_int64)(a) + (afs_int64)(b)
 #define AddUInt64(a,b,c) *(c) = (afs_uint64)(a) + (afs_uint64)(b)
 #define SubtractInt64(a,b,c) *(c) = (afs_int64)(a) - (afs_int64)(b)
@@ -83,8 +91,16 @@ struct u_Int64 {
     afs_uint32 low;
 };
 typedef struct u_Int64 afs_uint64;
-#define ZeroInt64(a) (a).high = (a).low = 0
+#define ZeroInt64(a) ((a).high = (a).low = 0)
 #define AssignInt64(a, b) (b)->high = (a).high; (b)->low = (a).low
+#define IncInt64(a) ((++((a)->low)) ? 0 : (a)->high++ )
+#define IncUInt64(a) ((++((a)->low)) ? 0 : (a)->high++ )
+#define DecInt64(a) (((a)->low)-- ? 0 : (a)->high-- )
+#define DecUInt64(a) (((a)->low)-- ? 0 : (a)->high-- )
+#define GTInt64(a,b) (((a).high > (b).high) || (((a).high == (b).high) && ((a).low > (b).low)))
+#define GEInt64(a,b) (((a).high > (b).high) || (((a).high == (b).high) && ((a).low >= (b).low)))
+#define LEInt64(a,b) (((a).high < (b).high) || (((a).high == (b).high) && ((a).low <= (b).low)))
+#define LTInt64(a,b) (((a).high < (b).high) || (((a).high == (b).high) && ((a).low < (b).low)))
 #define CompareInt64(a,b) (((afs_int32)(a).high - (afs_int32)(b).high) || (((a).high == (b).high) && ((a).low - (b).low))) 
 #define AddInt64(a, b, c) {  afs_int64 _a, _b; _a = a; _b = b; (c)->low = _a.low + _b.low; (c)->high = _a.high + _b.high + ((c)->low < _b.low); } 
 #define SubtractInt64(a, b, c) { afs_int64 _a, _b; _a = a; _b = b; (c)->low = _a.low - _b.low;  (c)->high = _a.high - _b.high - (_a.low < _b.low); } 
@@ -246,4 +262,9 @@ struct afsUUID {
 };
 typedef struct afsUUID afsUUID;
 
+/* for now, demand attach fileserver is only support on unix pthreads builds */
+#if defined(DEMAND_ATTACH_ENABLE) && defined(AFS_PTHREAD_ENV) && !defined(AFS_NT40_ENV)
+#define AFS_DEMAND_ATTACH_FS 1
+#endif
+
 #endif /* OPENAFS_CONFIG_AFS_STDS_H */
index fcd813c..1e930a6 100644 (file)
@@ -78,6 +78,13 @@ for (n=0, queue_Scan(&myqueue, qe, nqe, myelement), n++) {}
 #define _RXQSP(q1,q2,i,a,b,c,d,x,y) if (!queue_IsEnd(q1,i->c)) \
     (((y->b->a=q2->a)->b=y->b), ((x->a->b=q2)->a=x->a), ((i->c=q1)->d=i))
 
+/* This one moves a chain of elements from (s) to (e) from its
+ * current position to either before or after element (i)
+ * if (a,b,x,y) is (prev,next,s,e) then chain is moved before (i)
+ * if (a,b,x,y) is (next,prev,e,s) then chain is moved after (i) */
+#define _RXQMV(i, s, e, a, b, x, y) if (i->a != y) \
+    (((e->next->prev=s->prev)->next=e->next), ((i->a->b=x)->a=i->a), ((y->b=i)->a=y))
+
 /* Basic remove operation.  Doesn't update the queue item to indicate it's been removed */
 #define _RXQR(i) ((_RXQ(i)->prev->next=_RXQ(i)->next)->prev=_RXQ(i)->prev)
 
@@ -120,6 +127,12 @@ for (n=0, queue_Scan(&myqueue, qe, nqe, myelement), n++) {}
 #define queue_Replace(q1,q2) if (queue_IsEmpty(q2)) queue_Init(q1); else \
     (*_RXQ(q1) = *_RXQ(q2), _RXQ(q1)->next->prev = _RXQ(q1)->prev->next = _RXQ(q1), queue_Init(q2))
 
+/* move a chain of elements beginning at (s) and ending at (e) before node (i) */
+#define queue_MoveChainBefore(i, s, e) _RXQMV(_RXQ(i),_RXQ(s),_RXQ(e),prev,next,_RXQ(s),_RXQ(e))
+
+/* move a chain of elements beginning at (s) and ending at (e) after node (i) */
+#define queue_MoveChainAfter(i, s, e) _RXQMV(_RXQ(i),_RXQ(s),_RXQ(e),next,prev,_RXQ(e),_RXQ(s))
+
 /* Remove a queue element (*i) from it's queue.  The next field is 0'd, so that any further use of this q entry will hopefully cause a core dump.  Multiple removes of the same queue item are not supported */
 #define queue_Remove(i) (_RXQR(i), _RXQ(i)->next = 0)
 
@@ -155,6 +168,10 @@ for (n=0, queue_Scan(&myqueue, qe, nqe, myelement), n++) {}
 /* Returns false if the item was removed from a queue OR is uninitialized (zero) */
 #define queue_IsOnQueue(i) (_RXQ(i)->next != 0)
 
+/* Returns true if the item was removed from a queue OR is uninitialized (zero) */
+/* Return false if the queue item is currently in a queue */
+#define queue_IsNotOnQueue(i) (_RXQ(i)->next == 0)
+
 /* Returns true if the queue item (i) is the first element of the queue (q) */
 #define queue_IsFirst(q,i) (_RXQ(q)->first == _RXQ(i))
 
@@ -164,6 +181,9 @@ for (n=0, queue_Scan(&myqueue, qe, nqe, myelement), n++) {}
 /* Returns true if the queue item (i) is the end of the queue (q), that is, i is the head of the queue */
 #define queue_IsEnd(q,i) (_RXQ(q) == _RXQ(i))
 
+/* Returns false if the queue item (i) is the end of the queue (q), that is, i is the head of the queue */
+#define queue_IsNotEnd(q,i) (_RXQ(q) != _RXQ(i))
+
 /* Prototypical loop to scan an entire queue forwards.  q is the queue
  * head, qe is the loop variable, next is a variable used to store the
  * queue entry for the next iteration of the loop, s is the user's
@@ -180,12 +200,24 @@ for (n=0, queue_Scan(&myqueue, qe, nqe, myelement), n++) {}
        !queue_IsEnd(q, qe);                            \
        (qe) = (next), next = queue_Next(qe, s)
 
+/* similar to queue_Scan except start at element 'start' instead of the beginning */
+#define        queue_ScanFrom(q, start, qe, next, s)      \
+    (qe) = (struct s*)(start), next = queue_Next(qe, s);  \
+       !queue_IsEnd(q, qe);                               \
+       (qe) = (next), next = queue_Next(qe, s)
+
 /* This is similar to queue_Scan, but scans from the end of the queue to the beginning.  Next is the previous queue entry.  */
 #define        queue_ScanBackwards(q, qe, prev, s)             \
     (qe) = queue_Last(q, s), prev = queue_Prev(qe, s); \
        !queue_IsEnd(q, qe);                            \
        (qe) = prev, prev = queue_Prev(qe, s)
 
+/* This is similar to queue_ScanBackwards, but start at element 'start' instead of the end.  Next is the previous queue entry.  */
+#define        queue_ScanBackwardsFrom(q, start, qe, prev, s)  \
+    (qe) = (struct s*)(start), prev = queue_Prev(qe, s);       \
+       !queue_IsEnd(q, qe);                                    \
+       (qe) = prev, prev = queue_Prev(qe, s)
+
 #define queue_Count(q, qe, nqe, s, n)                  \
     for (n=0, queue_Scan(q, qe, nqe, s), n++) {}
 #endif /* _RX_QUEUE_ */
diff --git a/src/tsalvaged/Makefile.in b/src/tsalvaged/Makefile.in
new file mode 100644 (file)
index 0000000..1f4ccc6
--- /dev/null
@@ -0,0 +1,200 @@
+# Copyright 2000, International Business Machines Corporation and others.
+# All Rights Reserved.
+# 
+# This software has been released under the terms of the IBM Public
+# License.  For details, see the LICENSE file in the top-level source
+# directory or online at http://www.openafs.org/dl/license10.html
+#
+# Portions Copyright (c) 2003 Apple Computer, Inc.
+# Portions Copyright (c) 2006 Sine Nomine Associates
+
+srcdir=@srcdir@
+include @TOP_OBJDIR@/src/config/Makefile.config
+
+CC=${MT_CC}
+CFLAGS=${COMMON_CFLAGS} -I.. -DNINTERFACE ${MT_CFLAGS} -DRXDEBUG -DFSSYNC_BUILD_CLIENT \
+       -DSALVSYNC_BUILD_SERVER -DSALVSYNC_BUILD_CLIENT
+
+CCRULE=${CC} ${CFLAGS} -c $?
+
+VICED=../viced
+VLSERVER=../vlserver
+LWP=../lwp
+LIBACL=../libacl
+UTIL=../util
+DIR=../dir
+VOL=../vol
+FSINT=../fsint
+
+SALVAGEDOBJS=salvaged.o vol-salvage.o physio.o
+
+DIROBJS=buffer.o dir.o salvage.o
+
+LWPOBJS=lock.o threadname.o
+
+UTILOBJS=assert.o uuid.o serverLog.o fileutil.o netutils.o dirpath.o volparse.o flipbase64.o softsig.o fstab.o
+
+VLIBOBJS=vnode.o volume.o vutil.o partition.o fssync-client.o \
+        clone.o nuke.o devname.o listinodes.o ihandle.o \
+        namei_ops.o salvsync-server.o salvsync-client.o \
+        daemon_com.o
+
+OBJECTS= ${SALVAGEDOBJS} ${UTILOBJS} ${VLIBOBJS} ${DIROBJS} ${LWPOBJS}
+
+FSSDEBUG_OBJS = fssync-debug.o physio.o common.o ${UTILOBJS} ${VLIBOBJS} ${DIROBJS} ${LWPOBJS}
+
+SSSDEBUG_OBJS = salvsync-debug.o physio.o common.o ${UTILOBJS} ${VLIBOBJS} ${DIROBJS} ${LWPOBJS}
+
+LIBS=${TOP_LIBDIR}/libafsauthent.a ${TOP_LIBDIR}/libafsrpc.a ${TOP_LIBDIR}/util.a ${TOP_LIBDIR}/libcmd.a
+
+INSTALL_TARGS = ${DESTDIR}${afssrvlibexecdir}/salvageserver \
+               ${DESTDIR}${afssrvsbindir}/fssync-debug \
+               ${DESTDIR}${afssrvsbindir}/salvsync-debug
+
+DEST_TARGS =   ${DEST}/root.server/usr/afs/bin/salvageserver \
+               ${DEST}/root.server/usr/afs/bin/fssync-debug \
+               ${DEST}/root.server/usr/afs/bin/salvsync-debug
+
+all: salvageserver fssync-debug salvsync-debug
+
+salvaged.o: ${VOL}/salvaged.c
+       ${CCRULE}
+
+vol-salvage.o: ${VOL}/vol-salvage.c
+       ${CCRULE}
+
+physio.o: ${VOL}/physio.c
+       ${CCRULE}
+
+fssync-debug.o: ${VOL}/fssync-debug.c
+       ${CCRULE}
+
+salvsync-debug.o: salvsync-debug.c
+       ${CCRULE}
+
+assert.o: ${UTIL}/assert.c
+       ${CCRULE}
+
+uuid.o: ${UTIL}/uuid.c
+       ${CCRULE}
+
+serverLog.o: ${UTIL}/serverLog.c
+       ${CCRULE}
+
+fileutil.o: ${UTIL}/fileutil.c
+       ${CCRULE}
+
+volparse.o: ${UTIL}/volparse.c
+       ${CCRULE}
+
+flipbase64.o: ${UTIL}/flipbase64.c
+       ${CCRULE}
+
+netutils.o: ${UTIL}/netutils.c
+       ${CCRULE}
+
+dirpath.o: ${UTIL}/dirpath.c
+       ${CCRULE}
+
+softsig.o: ${UTIL}/softsig.c
+       ${CCRULE}
+
+buffer.o: ${DIR}/buffer.c
+       ${CCRULE}
+
+dir.o: ${DIR}/dir.c
+       ${CCRULE}
+
+salvage.o: ${DIR}/salvage.c
+       ${CCRULE}
+
+lock.o: ${LWP}/lock.c
+       ${CCRULE}
+
+threadname.o: ${LWP}/threadname.c
+       ${CCRULE}
+
+vnode.o: ${VOL}/vnode.c
+       ${CCRULE}
+
+volume.o: ${VOL}/volume.c
+       ${CCRULE}
+
+vutil.o: ${VOL}/vutil.c
+       ${CCRULE}
+
+partition.o: ${VOL}/partition.c
+       ${CCRULE}
+
+fssync-client.o: ${VOL}/fssync-client.c
+       ${CCRULE}
+
+salvsync-server.o: ${VOL}/salvsync-server.c
+       ${CCRULE}
+
+salvsync-client.o: ${VOL}/salvsync-client.c
+       ${CCRULE}
+
+daemon_com.o: ${VOL}/daemon_com.c
+       ${CCRULE}
+
+clone.o: ${VOL}/clone.c
+       ${CCRULE}
+
+nuke.o: ${VOL}/nuke.c
+       ${CCRULE}
+
+devname.o: ${VOL}/devname.c
+       ${CCRULE}
+
+# only for darwin?
+fstab.o: ${UTIL}/fstab.c
+       ${CCRULE}
+
+common.o: ${VOL}/common.c
+       ${CCRULE}
+
+listinodes.o: ${VOL}/listinodes.c
+       ${CCRULE}
+
+ihandle.o: ${VOL}/ihandle.c
+       ${CCRULE}
+
+namei_ops.o: ${VOL}/namei_ops.c
+       ${CCRULE}
+
+salvageserver: ${OBJECTS} ${LIBS}
+       ${CC} ${LDFLAGS} -o salvageserver ${OBJECTS} ${LIBS} ${MT_LIBS} ${XLIBS}
+
+fssync-debug: ${FSSDEBUG_OBJS} ${LIBS}
+       ${CC} ${LDFLAGS} -o fssync-debug ${FSSDEBUG_OBJS} ${LIBS} ${MT_LIBS} ${XLIBS}
+
+salvsync-debug: ${SSSDEBUG_OBJS} ${LIBS}
+       ${CC} ${LDFLAGS} -o salvsync-debug ${SSSDEBUG_OBJS} ${LIBS} ${MT_LIBS} ${XLIBS}
+
+${DEST}/root.server/usr/afs/bin/salvageserver: salvageserver
+       ${INSTALL} -ns $? $@
+
+${DEST}/root.server/usr/afs/bin/fssync-debug: fssync-debug
+       ${INSTALL} -s $? $@
+
+${DEST}/root.server/usr/afs/bin/salvsync-debug: salvsync-debug
+       ${INSTALL} -s $? $@
+
+install: ${INSTALL_TARGS}
+
+clean:
+       $(RM) -f *.o salvageserver core AFS_component_version_number.c
+
+include ../config/Makefile.version
+
+${DESTDIR}${afssrvlibexecdir}/salvageserver: salvageserver
+       ${INSTALL} -ns $? $@
+
+${DESTDIR}${afssrvsbindir}/fssync-debug: fssync-debug
+       ${INSTALL} -s $? $@
+
+${DESTDIR}${afssrvsbindir}/salvsync-debug: salvsync-debug
+       ${INSTALL} -s $? $@
+
+dest: ${DEST_TARGS}
diff --git a/src/tsalvaged/salvsync-debug.c b/src/tsalvaged/salvsync-debug.c
new file mode 100644 (file)
index 0000000..4d4949a
--- /dev/null
@@ -0,0 +1,475 @@
+/*
+ * Copyright 2006, Sine Nomine Associates and others.
+ * All Rights Reserved.
+ * 
+ * This software has been released under the terms of the IBM Public
+ * License.  For details, see the LICENSE file in the top-level source
+ * directory or online at http://www.openafs.org/dl/license10.html
+ */
+
+/* Main program file. Define globals. */
+#define MAIN 1
+
+/*
+ * salvsync debug tool
+ */
+
+
+#include <afsconfig.h>
+#include <afs/param.h>
+
+RCSID
+    ("$Header$");
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <errno.h>
+#ifdef AFS_NT40_ENV
+#include <io.h>
+#include <WINNT/afsevent.h>
+#else
+#include <sys/param.h>
+#include <sys/file.h>
+#ifndef ITIMER_REAL
+#include <sys/time.h>
+#endif /* ITIMER_REAL */
+#endif
+#include <rx/xdr.h>
+#include <afs/afsint.h>
+#include <afs/assert.h>
+
+
+#include <fcntl.h>
+
+#ifndef AFS_NT40_ENV
+#include <afs/osi_inode.h>
+#endif
+
+#include <afs/cmd.h>
+#include <afs/afsutil.h>
+#include <afs/fileutil.h>
+
+#include "nfs.h"
+#include "lwp.h"
+#include "lock.h"
+#include "ihandle.h"
+#include "vnode.h"
+#include "volume.h"
+#include "partition.h"
+#include "daemon_com.h"
+#include "salvsync.h"
+#ifdef AFS_NT40_ENV
+#include <pthread.h>
+#endif
+
+int VolumeChanged; /* hack to make dir package happy */
+
+
+#ifndef AFS_DEMAND_ATTACH_FS
+int
+main(int argc, char ** argv)
+{
+    fprintf(stderr, "*** salvsync-debug is only supported for OpenAFS builds with the demand-attach fileserver extension\n");
+    return -1;
+}
+#else /* AFS_DEMAND_ATTACH_FS */
+
+struct salv_state {
+    afs_uint32 prio;
+    afs_uint32 volume;
+    char partName[16];
+};
+
+struct state {
+    afs_int32 reason;
+    struct salv_state * sop;
+};
+
+static int common_prolog(struct cmd_syndesc *, struct state *);
+static int common_salv_prolog(struct cmd_syndesc *, struct state *);
+
+static int do_salvop(struct state *, afs_int32 command, SYNC_response * res);
+
+static char * response_code_to_string(afs_int32);
+static char * command_code_to_string(afs_int32);
+static char * reason_code_to_string(afs_int32);
+static char * program_type_to_string(afs_int32);
+static char * state_code_to_string(afs_int32);
+
+
+static int OpStats(struct cmd_syndesc * as, char * rock);
+static int OpSalvage(struct cmd_syndesc * as, char * rock);
+static int OpCancel(struct cmd_syndesc * as, char * rock);
+static int OpCancelAll(struct cmd_syndesc * as, char * rock);
+static int OpRaisePrio(struct cmd_syndesc * as, char * rock);
+static int OpQuery(struct cmd_syndesc * as, char * rock);
+
+
+#ifndef AFS_NT40_ENV
+#include "AFS_component_version_number.c"
+#endif
+#define MAX_ARGS 128
+
+#define COMMON_PARMS_OFFSET    13
+#define COMMON_PARMS(ts) \
+    cmd_Seek(ts, COMMON_PARMS_OFFSET); \
+    cmd_AddParm(ts, "-reason", CMD_SINGLE, CMD_OPTIONAL, "sync protocol reason code"); \
+    cmd_AddParm(ts, "-programtype", CMD_SINGLE, CMD_OPTIONAL, "program type code")
+
+#define COMMON_SALV_PARMS_OFFSET    10
+#define COMMON_SALV_PARMS(ts) \
+    cmd_Seek(ts, COMMON_SALV_PARMS_OFFSET); \
+    cmd_AddParm(ts, "-volumeid", CMD_SINGLE, 0, "volume id"); \
+    cmd_AddParm(ts, "-partition", CMD_SINGLE, CMD_OPTIONAL, "partition name"); \
+    cmd_AddParm(ts, "-priority", CMD_SINGLE, CMD_OPTIONAL, "priority")
+
+#define SALV_PARMS_DECL(ts) \
+    COMMON_SALV_PARMS(ts); \
+    COMMON_PARMS(ts)
+
+#define COMMON_PARMS_DECL(ts) \
+    COMMON_PARMS(ts)
+
+int
+main(int argc, char **argv)
+{
+    struct cmd_syndesc *ts;
+    int err = 0;
+    int i;
+    extern char cml_version_number[];
+
+    /* Initialize directory paths */
+    if (!(initAFSDirPath() & AFSDIR_SERVER_PATHS_OK)) {
+#ifdef AFS_NT40_ENV
+       ReportErrorEventAlt(AFSEVT_SVR_NO_INSTALL_DIR, 0, argv[0], 0);
+#endif
+       fprintf(stderr, "%s: Unable to obtain AFS server directory.\n",
+               argv[0]);
+       exit(2);
+    }
+
+
+    ts = cmd_CreateSyntax("stats", OpStats, 0, "get salvageserver statistics (SALVSYNC_NOP opcode)");
+    COMMON_PARMS_DECL(ts);
+    cmd_CreateAlias(ts, "nop");
+
+    ts = cmd_CreateSyntax("salvage", OpSalvage, 0, "schedule a salvage (SALVSYNC_SALVAGE opcode)");
+    SALV_PARMS_DECL(ts);
+
+    ts = cmd_CreateSyntax("cancel", OpCancel, 0, "cancel a salvage (SALVSYNC_CANCEL opcode)");
+    SALV_PARMS_DECL(ts);
+
+    ts = cmd_CreateSyntax("raiseprio", OpRaisePrio, 0, "raise a salvage priority (SALVSYNC_RAISEPRIO opcode)");
+    SALV_PARMS_DECL(ts);
+    cmd_CreateAlias(ts, "rp");
+
+    ts = cmd_CreateSyntax("query", OpQuery, 0, "query salvage status (SALVSYNC_QUERY opcode)");
+    SALV_PARMS_DECL(ts);
+    cmd_CreateAlias(ts, "qry");
+
+    ts = cmd_CreateSyntax("kill", OpCancelAll, 0, "cancel all scheduled salvages (SALVSYNC_CANCELALL opcode)");
+    COMMON_PARMS_DECL(ts);
+
+    err = cmd_Dispatch(argc, argv);
+    exit(err);
+}
+
+static int
+common_prolog(struct cmd_syndesc * as, struct state * state)
+{
+    register struct cmd_item *ti;
+
+#ifdef AFS_NT40_ENV
+    if (afs_winsockInit() < 0) {
+       Exit(1);
+    }
+#endif
+
+    VInitVolumePackage(debugUtility, 1, 1,
+                      DONT_CONNECT_FS, 0);
+    DInit(1);
+
+    if ((ti = as->parms[COMMON_PARMS_OFFSET].items)) { /* -reason */
+       state->reason = atoi(ti->data);
+    }
+    if ((ti = as->parms[COMMON_PARMS_OFFSET+1].items)) {       /* -programtype */
+       if (!strcmp(ti->data, "fileServer")) {
+           programType = fileServer;
+       } else if (!strcmp(ti->data, "volumeUtility")) {
+           programType = volumeUtility;
+       } else if (!strcmp(ti->data, "salvager")) {
+           programType = salvager;
+       } else if (!strcmp(ti->data, "salvageServer")) {
+           programType = salvageServer;
+       } else {
+           programType = (ProgramType) atoi(ti->data);
+       }
+    }
+
+    VConnectSALV();
+
+    return 0;
+}
+
+static int
+common_salv_prolog(struct cmd_syndesc * as, struct state * state)
+{
+    register struct cmd_item *ti;
+    char pname[100], *temp;
+
+    state->sop = (struct salv_state *) calloc(1, sizeof(struct salv_state));
+    assert(state->sop != NULL);
+
+    if ((ti = as->parms[COMMON_SALV_PARMS_OFFSET].items)) {    /* -volumeid */
+       state->sop->volume = atoi(ti->data);
+    } else {
+       fprintf(stderr, "required argument -volumeid not given\n");
+    }
+
+    if ((ti = as->parms[COMMON_SALV_PARMS_OFFSET+1].items)) {  /* -partition */
+       strlcpy(state->sop->partName, ti->data, sizeof(state->sop->partName));
+    } else {
+       memset(state->sop->partName, 0, sizeof(state->sop->partName));
+    }
+
+    if ((ti = as->parms[COMMON_SALV_PARMS_OFFSET+2].items)) {  /* -prio */
+       state->sop->prio = atoi(ti->data);
+    } else {
+       state->sop->prio = 0;
+    }
+
+    return 0;
+}
+
+static int
+do_salvop(struct state * state, afs_int32 command, SYNC_response * res)
+{
+    afs_int32 code;
+    SALVSYNC_response_hdr hdr_l, *hdr;
+    SYNC_response res_l;
+
+    if (!res) {
+       res = &res_l;
+       res->payload.len = sizeof(hdr_l);
+       res->payload.buf = hdr = &hdr_l;
+    } else {
+       hdr = (SALVSYNC_response_hdr *) res->payload.buf;
+    }
+
+    fprintf(stderr, "calling SALVSYNC_SalvageVolume with command code %d (%s)\n", 
+           command, command_code_to_string(command));
+
+    code = SALVSYNC_SalvageVolume(state->sop->volume,
+                                 state->sop->partName,
+                                 command,
+                                 state->reason,
+                                 state->sop->prio,
+                                 res);
+
+    switch (code) {
+    case SYNC_OK:
+    case SYNC_DENIED:
+       break;
+    default:
+       fprintf(stderr, "possible sync protocol error. return code was %d\n", code);
+    }
+
+    fprintf(stderr, "SALVSYNC_SalvageVolume returned %d (%s)\n", code, response_code_to_string(code));
+    fprintf(stderr, "protocol response code was %d (%s)\n", 
+           res->hdr.response, response_code_to_string(res->hdr.response));
+    fprintf(stderr, "protocol reason code was %d (%s)\n", 
+           res->hdr.reason, reason_code_to_string(res->hdr.reason));
+
+    printf("state = {\n");
+    if (res->hdr.flags & SALVSYNC_FLAG_VOL_STATS_VALID) {
+       printf("\tstate = %d (%s)\n",
+              hdr->state, state_code_to_string(hdr->state));
+       printf("\tprio = %d\n", hdr->prio);
+    }
+    printf("\tsq_len = %d\n", hdr->sq_len);
+    printf("\tpq_len = %d\n", hdr->pq_len);
+    printf("}\n");
+
+    VDisconnectSALV();
+}
+
+static char *
+response_code_to_string(afs_int32 response)
+{
+    switch (response) {
+    case SYNC_OK:
+       return "SYNC_OK";
+    case SYNC_DENIED:
+       return "SYNC_DENIED";
+    case SYNC_COM_ERROR:
+       return "SYNC_COM_ERROR";
+    case SYNC_BAD_COMMAND:
+       return "SYNC_BAD_COMMAND";
+    case SYNC_FAILED:
+       return "SYNC_FAILED";
+    default:
+       return "**UNKNOWN**";
+    }
+}
+
+static char *
+command_code_to_string(afs_int32 command)
+{
+    switch (command) {
+    case SYNC_COM_CHANNEL_CLOSE:
+       return "SYNC_COM_CHANNEL_CLOSE";
+    case SALVSYNC_NOP:
+       return "SALVSYNC_NOP";
+    case SALVSYNC_SALVAGE:
+       return "SALVSYNC_SALVAGE";
+    case SALVSYNC_CANCEL:
+       return "SALVSYNC_CANCEL";
+    case SALVSYNC_RAISEPRIO:
+       return "SALVSYNC_RAISEPRIO";
+    case SALVSYNC_QUERY:
+       return "SALVSYNC_QUERY";
+    case SALVSYNC_CANCELALL:
+       return "SALVSYNC_CANCELLALL";
+    default:
+       return "**UNKNOWN**";
+    }
+}
+
+static char *
+reason_code_to_string(afs_int32 reason)
+{
+    switch (reason) {
+    case SALVSYNC_WHATEVER:
+       return "SALVSYNC_WHATEVER";
+    case SALVSYNC_ERROR:
+       return "SALVSYNC_ERROR";
+    case SALVSYNC_OPERATOR:
+       return "SALVSYNC_OPERATOR";
+    case SALVSYNC_SHUTDOWN:
+       return "SALVSYNC_SHUTDOWN";
+    case SALVSYNC_NEEDED:
+       return "SALVSYNC_NEEDED";
+    default:
+       return "**UNKNOWN**";
+    }
+}
+
+static char *
+program_type_to_string(afs_int32 type)
+{
+    switch ((ProgramType)type) {
+    case fileServer:
+       return "fileServer";
+    case volumeUtility:
+       return "volumeUtility";
+    case salvager:
+       return "salvager";
+    case salvageServer:
+       return "salvageServer";
+    default:
+       return "**UNKNOWN**";
+    }
+}
+
+static char *
+state_code_to_string(afs_int32 state)
+{
+    switch (state) {
+    case SALVSYNC_STATE_UNKNOWN:
+       return "SALVSYNC_STATE_UNKNOWN";
+    case SALVSYNC_STATE_QUEUED:
+       return "SALVSYNC_STATE_QUEUED";
+    case SALVSYNC_STATE_SALVAGING:
+       return "SALVSYNC_STATE_SALVAGING";
+    case SALVSYNC_STATE_ERROR:
+       return "SALVSYNC_STATE_ERROR";
+    case SALVSYNC_STATE_DONE:
+       return "SALVSYNC_STATE_DONE";
+    default:
+       return "**UNKNOWN**";
+    }
+}
+
+static int
+OpStats(struct cmd_syndesc * as, char * rock)
+{
+    struct state state;
+
+    common_prolog(as, &state);
+    common_salv_prolog(as, &state);
+
+    do_salvop(&state, SALVSYNC_NOP, NULL);
+
+    return 0;
+}
+
+static int
+OpSalvage(struct cmd_syndesc * as, char * rock)
+{
+    struct state state;
+
+    common_prolog(as, &state);
+    common_salv_prolog(as, &state);
+
+    do_salvop(&state, SALVSYNC_SALVAGE, NULL);
+
+    return 0;
+}
+
+static int
+OpCancel(struct cmd_syndesc * as, char * rock)
+{
+    struct state state;
+
+    common_prolog(as, &state);
+    common_salv_prolog(as, &state);
+
+    do_salvop(&state, SALVSYNC_CANCEL, NULL);
+
+    return 0;
+}
+
+static int
+OpCancelAll(struct cmd_syndesc * as, char * rock)
+{
+    struct state state;
+
+    common_prolog(as, &state);
+    common_salv_prolog(as, &state);
+
+    do_salvop(&state, SALVSYNC_CANCELALL, NULL);
+
+    return 0;
+}
+
+static int
+OpRaisePrio(struct cmd_syndesc * as, char * rock)
+{
+    struct state state;
+
+    common_prolog(as, &state);
+    common_salv_prolog(as, &state);
+
+    do_salvop(&state, SALVSYNC_RAISEPRIO, NULL);
+
+    return 0;
+}
+
+static int
+OpQuery(struct cmd_syndesc * as, char * rock)
+{
+    struct state state;
+
+    common_prolog(as, &state);
+    common_salv_prolog(as, &state);
+
+    do_salvop(&state, SALVSYNC_QUERY, NULL);
+
+    return 0;
+}
+
+#endif /* AFS_DEMAND_ATTACH_FS */
index b10e1a4..68363fc 100644 (file)
@@ -11,7 +11,7 @@ srcdir=@srcdir@
 include @TOP_OBJDIR@/src/config/Makefile.config
 
 CC=${MT_CC}
-CFLAGS=${COMMON_CFLAGS} -I.. -DNINTERFACE ${MT_CFLAGS} -DRXDEBUG
+CFLAGS=${COMMON_CFLAGS} -I.. -DNINTERFACE ${MT_CFLAGS} -DRXDEBUG -DFSSYNC_BUILD_SERVER -DSALVSYNC_BUILD_CLIENT
 
 CCRULE=${CC} ${CFLAGS} -c $?
 
@@ -24,7 +24,7 @@ DIR=../dir
 VOL=../vol
 FSINT=../fsint
 
-VICEDOBJS=viced.o afsfileprocs.o host.o physio.o callback.o    
+VICEDOBJS=viced.o afsfileprocs.o host.o physio.o callback.o serialize_state.o  
 
 VLSERVEROBJS=vldbint.cs.o vldbint.xdr.o
 
@@ -36,18 +36,20 @@ UTILOBJS=assert.o uuid.o serverLog.o fileutil.o netutils.o dirpath.o volparse.o
 
 DIROBJS=buffer.o dir.o salvage.o
 
-VOLOBJS= vnode.o volume.o vutil.o partition.o fssync.o purge.o \
+VOLOBJS= vnode.o volume.o vutil.o partition.o fssync-server.o \
         clone.o devname.o common.o ihandle.o listinodes.o namei_ops.o \
-        fstab.o
+        fstab.o salvsync-client.o daemon_com.o
 
 FSINTOBJS= afsaux.o afscbint.cs.o afsint.ss.o afsint.xdr.o
 
 objects= ${VICEDOBJS} ${VLSERVEROBJS} ${LWPOBJS} ${LIBACLOBJS} \
         ${UTILOBJS} ${DIROBJS} ${VOLOBJS} ${FSINTOBJS}
 
+SDBGOBJS = state_analyzer.o uuid.o dirpath.o fileutil.o ${TOP_LIBDIR}/util.a
+
 LIBS=${TOP_LIBDIR}/libafsauthent.a ${TOP_LIBDIR}/libafsrpc.a ${TOP_LIBDIR}/util.a
 
-all: fileserver
+all: fileserver state_analyzer
 
 viced.o: ${VICED}/viced.c
        ${CCRULE}
@@ -64,6 +66,9 @@ physio.o: ${VICED}/physio.c
 callback.o: ${VICED}/callback.c
        ${CCRULE}
 
+serialize_state.o: ./serialize_state.c
+       ${CCRULE}
+
 assert.o: ${UTIL}/assert.c
        ${CCRULE}
 
@@ -130,10 +135,16 @@ vutil.o: ${VOL}/vutil.c
 partition.o: ${VOL}/partition.c
        ${CCRULE}
 
-fssync.o: ${VOL}/fssync.c
+fssync-server.o: ${VOL}/fssync-server.c
+       ${CCRULE}
+
+fssync-client.o: ${VOL}/fssync-client.c
+       ${CCRULE}
+
+salvsync-client.o: ${VOL}/salvsync-client.c
        ${CCRULE}
 
-purge.o: ${VOL}/purge.c
+daemon_com.o: ${VOL}/daemon_com.c
        ${CCRULE}
 
 clone.o: ${VOL}/clone.c
@@ -179,21 +190,33 @@ afsint.ss.o: ${FSINT}/afsint.ss.c
 afsint.xdr.o: ${FSINT}/afsint.xdr.c
        ${CCRULE}
 
+state_analyzer.o: state_analyzer.c
+       ${CCRULE}
+
 fileserver: ${objects} ${LIBS}
        ${CC} ${LDFLAGS} -o fileserver ${objects} ${LIBS} ${MT_LIBS} ${XLIBS}
 
+state_analyzer: ${SDBGOBJS}
+       ${CC} ${LDFLAGS} -o state_analyzer ${SDBGOBJS} ${MT_LIBS} ${XLIBS}
+
 ${DEST}/root.server/usr/afs/bin/fileserver: fileserver
        ${INSTALL} -ns $? $@
 
-install: ${DESTDIR}${afssrvlibexecdir}/fileserver
+${DEST}/root.server/usr/afs/bin/state_analyzer: state_analyzer
+       ${INSTALL} $? $@
+
+install: ${DESTDIR}${afssrvlibexecdir}/fileserver ${DESTDIR}${afssrvsbindir}/state_analyzer
 
 clean:
-       $(RM) -f *.o fileserver core AFS_component_version_number.c
+       $(RM) -f *.o fileserver state_analyzer core AFS_component_version_number.c
 
 include ../config/Makefile.version
 
 ${DESTDIR}${afssrvlibexecdir}/fileserver: fileserver
        ${INSTALL} -ns $? $@
 
-dest: ${DEST}/root.server/usr/afs/bin/fileserver
+${DESTDIR}${afssrvsbindir}/state_analyzer: state_analyzer
+       ${INSTALL} $? $@
+
+dest: ${DEST}/root.server/usr/afs/bin/fileserver ${DEST}/root.server/usr/afs/bin/state_analyzer
 
index e9e2c27..e58c5cc 100644 (file)
@@ -5,7 +5,7 @@
 # License.  For details, see the LICENSE file in the top-level source
 # directory or online at http://www.openafs.org/dl/license10.html
 
-AFSDEV_AUXCDEFINES = -DAFS_PTHREAD_ENV -DRXDEBUG
+AFSDEV_AUXCDEFINES = -DAFS_PTHREAD_ENV -DRXDEBUG -DFSSYNC_BUILD_SERVER
 
 RELDIR=tviced
 !INCLUDE ..\config\NTMakefile.$(SYS_NAME)
diff --git a/src/tviced/serialize_state.c b/src/tviced/serialize_state.c
new file mode 100644 (file)
index 0000000..c1b4583
--- /dev/null
@@ -0,0 +1,1120 @@
+/*
+ * Copyright 2006, Sine Nomine Associates and others.
+ * All Rights Reserved.
+ * 
+ * This software has been released under the terms of the IBM Public
+ * License.  For details, see the LICENSE file in the top-level source
+ * directory or online at http://www.openafs.org/dl/license10.html
+ */
+
+/*
+ * demand attach fs
+ * fileserver state serialization
+ */
+
+#include <afsconfig.h>
+#include <afs/param.h>
+
+RCSID
+    ("$Header$");
+
+#include <stdio.h>
+#include <stdlib.h>            /* for malloc() */
+#include <time.h>              /* ANSI standard location for time stuff */
+#ifdef AFS_NT40_ENV
+#include <fcntl.h>
+#include <io.h>
+#else
+#include <sys/time.h>
+#include <sys/file.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#else
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+#endif
+#include <afs/assert.h>
+#include <sys/stat.h>
+
+#include <afs/stds.h>
+
+#include <rx/xdr.h>
+#include <lwp.h>
+#include <lock.h>
+#include <afs/afsint.h>
+#include <afs/rxgen_consts.h>
+#include <afs/nfs.h>
+#include <afs/errors.h>
+#include <afs/ihandle.h>
+#include <afs/vnode.h>
+#include <afs/volume.h>
+#include <afs/acl.h>
+#include <afs/ptclient.h>
+#include <afs/prs_fs.h>
+#include <afs/auth.h>
+#include <afs/afsutil.h>
+#include <rx/rx.h>
+#include <afs/cellconfig.h>
+#include <stdlib.h>
+
+#include "../viced/viced_prototypes.h"
+#include "../viced/viced.h"
+#include "../viced/host.h"
+#include "../viced/callback.h"
+#include "serialize_state.h"
+
+/*@+fcnmacros +macrofcndecl@*/
+#ifdef O_LARGEFILE
+#ifdef S_SPLINT_S
+extern off64_t afs_lseek(int FD, off64_t O, int F);
+#endif /*S_SPLINT_S */
+#define afs_lseek(FD, O, F)    lseek64(FD, (off64_t)(O), F)
+#define afs_stat               stat64
+#define afs_fstat              fstat64
+#define afs_open               open64
+#define afs_fopen              fopen64
+#define afs_ftruncate           ftruncate64
+#define afs_mmap                mmap64
+#ifdef AFS_AIX_ENV
+extern void * mmap64();  /* ugly hack since aix build env appears to be somewhat broken */
+#endif
+#else /* !O_LARGEFILE */
+#ifdef S_SPLINT_S
+extern off_t afs_lseek(int FD, off_t O, int F);
+#endif /*S_SPLINT_S */
+#define afs_lseek(FD, O, F)    lseek(FD, (off_t)(O), F)
+#define afs_stat               stat
+#define afs_fstat              fstat
+#define afs_open               open
+#define afs_fopen              fopen
+#define afs_ftruncate           ftruncate
+#define afs_mmap                mmap
+#endif /* !O_LARGEFILE */
+/*@=fcnmacros =macrofcndecl@*/
+
+
+#ifdef AFS_DEMAND_ATTACH_FS
+
+/*
+ * demand attach fs
+ * state dump routines
+ *
+ * in order to make state dump/restore as fast as possible,
+ * we use memory mapped files
+ *
+ * if this causes problems on certain platforms, the APIs
+ * have been written so that it will be very simple to go
+ * back to standard I/O for just those poorly written platforms
+ */
+#define FS_STATE_USE_MMAP
+
+
+#ifdef FS_STATE_USE_MMAP
+#define FS_STATE_INIT_FILESIZE (8 * 1024 * 1024)  /* truncate to 8MB initially */
+#include <sys/mman.h>
+#endif
+
+static int fs_stateCreateDump(struct fs_dump_state * state);
+static int fs_stateLoadDump(struct fs_dump_state * state);
+static int fs_stateInvalidateDump(struct fs_dump_state * state);
+static int fs_stateCommitDump(struct fs_dump_state * state);
+static int fs_stateCloseDump(struct fs_dump_state * state);
+
+#ifdef FS_STATE_USE_MMAP
+static int fs_stateSizeFile(struct fs_dump_state * state);
+static int fs_stateResizeFile(struct fs_dump_state * state, size_t min_add);
+static int fs_stateTruncateFile(struct fs_dump_state * state);
+
+static int fs_stateMapFile(struct fs_dump_state * state);
+static int fs_stateUnmapFile(struct fs_dump_state * state);
+
+static int fs_stateIncCursor(struct fs_dump_state * state, size_t len);
+static int fs_stateCheckIOSafety(struct fs_dump_state * state,
+                                size_t len);
+#endif
+
+static int fs_stateFillHeader(struct fs_state_header * hdr);
+static int fs_stateCheckHeader(struct fs_state_header * hdr);
+
+static int fs_stateAlloc(struct fs_dump_state * state);
+static int fs_stateFree(struct fs_dump_state * state);
+
+extern afsUUID FS_HostUUID;
+extern char cml_version_number[];
+
+/*
+ * demand attach fs
+ * save all fileserver state 
+ */
+int
+fs_stateSave(void)
+{
+    int ret = 0, verified = 1;
+    struct fs_dump_state state;
+
+    /* save and restore need to be atomic wrt other host package operations */
+    H_LOCK; 
+
+    ViceLog(0, ("fs_stateSave: commencing fileserver state dump\n"));
+
+    if (fs_stateAlloc(&state)) {
+       ViceLog(0, ("fs_stateSave: memory allocation failed; dump aborted\n"));
+       ret = 1;
+       goto done;
+    }
+
+    /* XXX
+     * on busy servers, these checks will inevitably fail since stuff drops H_LOCK
+     * all over the place (with structs left in inconsistent states) while RPCs to
+     * clients happen (grumble, grumble, the host package needs to be rewritten...)
+     *
+     * the current hack is to force the background threads that deal with host and
+     * callback state offline early in the shutdown process, do VShutdown, come
+     * back and wait for those threads to die, THEN do the state dump
+     *
+     * BUT, this still has one flaw -- what do we do about rx worker threads that
+     * are blocked in the host package making an RPC call to a cm???
+     *
+     * perhaps we need a refcounter that keeps track of threads blocked in rpc calls
+     * with H_LOCK dropped (and the host struct likely left in an inconsistent state)
+     *
+     * or better yet, we need to associate a state machine with each host object
+     * (kind of like demand attach Volume structures).
+     *
+     * sigh. I suspect we'll need to revisit this issue
+     */
+
+    if (fs_state.options.fs_state_verify_before_save) {
+       ViceLog(0, ("fs_stateSave: performing internal consistency checks before proceeding with state dump\n"));
+
+       if (h_stateVerify(&state)) {
+           ViceLog(0, ("fs_stateSave: error: host table consistency checks failed; state dump will not be marked clean\n"));
+           verified = 0;
+           ret = 1;
+       }
+
+       if (cb_stateVerify(&state)) {
+           ViceLog(0, ("fs_stateSave: error: callback table consistency checks failed; state dump will not be marked clean\n"));
+           verified = 0;
+           ret = 1;
+       }
+
+       /* if a consistency check asserted the bail flag, reset it */
+       state.bail = 0;
+
+       ViceLog(0, ("fs_stateSave: proceeding with dump\n"));
+    }
+
+    if (fs_stateCreateDump(&state)) {
+       ViceLog(0, ("fs_stateSave: error: dump create failed\n"));
+       ret = 1;
+       goto done;
+    }
+
+    if (h_stateSave(&state)) {
+       ViceLog(0, ("fs_stateSave: error: host state dump failed\n"));
+       ret = 1;
+       goto done;
+    }
+
+    if (cb_stateSave(&state)) {
+       ViceLog(0, ("fs_stateSave: error: callback state dump failed\n"));
+       ret = 1;
+       goto done;
+    }
+
+    if (!verified) {
+       state.bail = 1;
+    }
+
+    if (fs_stateCommitDump(&state)) {
+       ViceLog(0, ("fs_stateSave: error: dump commit failed\n"));
+       ret = 1; 
+       goto done;
+    }
+
+    if (verified) {
+       ViceLog(0, ("fs_stateSave: fileserver state dump completed successfully\n"));
+    } else {
+       ViceLog(0, ("fs_stateSave: fileserver state dump completed, but not marked clean.\n"));
+       ViceLog(0, ("fs_stateSave: please save a copy of '%s' for use by technical support\n",
+                   state.fn));
+    }
+
+ done:
+    if (state.fd >= 0)
+       fs_stateCloseDump(&state);
+    fs_stateFree(&state);
+    H_UNLOCK;
+    return ret;
+}
+
+/*
+ * demand attach fs
+ * restore all fileserver state
+ *
+ * this function must appear as one atomic operation to the host and callback
+ * packages, hence H_LOCK is held for the entirety of the process.
+ */
+int
+fs_stateRestore(void)
+{
+    int ret = 0;
+    struct fs_dump_state state;
+
+    /* save and restore need to be atomic wrt other host package operations */
+    H_LOCK;
+
+    ViceLog(0, ("fs_stateRestore: commencing fileserver state restore\n"));
+
+    if (fs_stateAlloc(&state)) {
+       ViceLog(0, ("fs_stateRestore: memory allocation failed\n"));
+       ret = 1;
+       goto done;
+    }
+
+    if (fs_stateLoadDump(&state)) {
+       ViceLog(0, ("fs_stateRestore: failed to load dump file '%s'\n", state.fn));
+       ret = 1;
+       goto done;
+    }
+
+    if (fs_stateInvalidateDump(&state)) {
+       ViceLog(0, ("fs_stateRestore: failed to invalidate dump file '%s'\n", state.fn));
+       ret = 1;
+       goto done;
+    }
+
+
+    if (state.flags.do_host_restore) {
+       if (h_stateRestore(&state)) {
+           ViceLog(0, ("fs_stateRestore: error: host state restore failed. exiting avoid further corruption\n"));
+           exit(0);
+       }
+       ViceLog(0, ("fs_stateRestore: host table restored\n"));
+
+       if (cb_stateRestore(&state)) {
+           ViceLog(0, ("fs_stateRestore: error: callback state restore failed. exiting to avoid further corruption\n"));
+           exit(0);
+       }
+       ViceLog(0, ("fs_stateRestore: FileEntry and CallBack tables restored\n"));
+
+       if (h_stateRestoreIndices(&state)) {
+           ViceLog(0, ("fs_stateRestore: error: host index remapping failed. exiting to avoid further corruption\n"));
+           exit(0);
+       }
+       ViceLog(0, ("fs_stateRestore: host table indices remapped\n"));
+
+       if (cb_stateRestoreIndices(&state)) {
+           ViceLog(0, ("fs_stateRestore: error: callback index remapping failed. exiting to avoid further corruption\n"));
+           exit(0);
+       }
+       ViceLog(0, ("fs_stateRestore: FileEntry and CallBack indices remapped\n"));
+    }
+
+    ViceLog(0, ("fs_stateRestore: restore phase complete\n"));
+
+    if (fs_state.options.fs_state_verify_after_restore) {
+       ViceLog(0, ("fs_stateRestore: beginning state verification phase\n"));
+
+       if (state.flags.do_host_restore) {
+           if (h_stateVerify(&state)) {
+               ViceLog(0, ("fs_stateRestore: error: host table consistency checks failed; exiting to avoid further corruption\n"));
+               exit(0);
+           }
+
+           if (cb_stateVerify(&state)) {
+               ViceLog(0, ("fs_stateRestore: error: callback table consistency checks failed; exiting to avoid further corruption\n"));
+               exit(0);
+           }
+       }
+
+       ViceLog(0, ("fs_stateRestore: fileserver state verification complete\n"));
+    }
+
+    ViceLog(0, ("fs_stateRestore: restore was successful\n"));
+
+ done:
+    if (state.fd >= 0) {
+       fs_stateInvalidateDump(&state);
+       fs_stateCloseDump(&state);
+    }
+    fs_stateFree(&state);
+    H_UNLOCK;
+    return ret;
+}
+
+static int
+fs_stateCreateDump(struct fs_dump_state * state)
+{
+    int fd, ret = 0;
+    char savedump[MAXPATHLEN];
+    struct afs_stat status;
+
+    afs_snprintf(savedump, sizeof(savedump), "%s.old", state->fn);
+
+    if (afs_stat(state->fn, &status) == 0) {
+       renamefile(state->fn, savedump);
+    }
+
+    if (((fd = afs_open(state->fn, 
+                       O_RDWR | O_CREAT | O_TRUNC, 
+                       S_IRUSR | S_IWUSR)) == -1) ||
+       (afs_fstat(fd, &status) == -1)) {
+       ViceLog(0, ("fs_stateCreateDump: failed to create state dump file '%s'\n",
+                   state->fn));
+       ret = 1;
+       goto done;
+    }
+
+    state->fd = fd;
+    state->mode = FS_STATE_DUMP_MODE;
+    memset(state->hdr, 0, sizeof(struct fs_state_header));
+    fs_stateIncEOF(state, sizeof(struct fs_state_header));
+
+#ifdef FS_STATE_USE_MMAP
+    if (fs_stateSizeFile(state)) {
+       ViceLog(0, ("fs_stateCreateDump: failed to resize state dump file '%s'\n",
+                   state->fn));
+       ret = 1;
+       goto done;
+    }
+
+    if (fs_stateMapFile(state)) {
+       ViceLog(0, ("fs_stateCreateDump: failed to memory map state dump file '%s'\n",
+                   state->fn));
+       ret = 1;
+       goto done;
+    }
+#endif
+
+    ret = fs_stateInvalidateDump(state);
+
+ done:
+    return ret;
+}
+
+static int
+fs_stateInvalidateDump(struct fs_dump_state * state)
+{
+    afs_uint64 z;
+    int ret = 0;
+    struct fs_state_header hdr;
+
+#ifdef FS_STATE_USE_MMAP
+    if (state->mmap.map == NULL) {
+       return 1;
+    }
+#endif
+
+    memcpy(&hdr, state->hdr, sizeof(hdr));
+    hdr.valid = 0;
+    ZeroInt64(z);
+
+    /* write a bogus header to flag dump in progress */
+    if (fs_stateWriteHeader(state, &z, &hdr, sizeof(hdr))) {
+       ViceLog(0, ("fs_stateInvalidateDump: failed to invalidate old dump file header '%s'\n",
+                   state->fn));
+       ret = 1;
+       goto done;
+    }
+    if (fs_stateSync(state)) {
+       ViceLog(0, ("fs_stateInvalidateDump: failed to sync changes to disk\n"));
+       ret = 1;
+       goto done;
+    }
+
+ done:
+    return ret;
+}
+
+static int
+fs_stateCommitDump(struct fs_dump_state * state)
+{
+    afs_uint64 z;
+    int ret = 0;
+
+    ZeroInt64(z);
+
+#ifdef FS_STATE_USE_MMAP
+    if (fs_stateTruncateFile(state)) {
+       ViceLog(0, ("fs_stateCommitDump: failed to truncate dump file to proper size\n"));
+       ret = 1;
+       goto done;
+    }
+#endif
+
+    /* ensure that all pending data I/Os for the state file have been committed 
+     * _before_ we make the metadata I/Os */
+    if (fs_stateSync(state)) {
+       ViceLog(0, ("fs_stateCommitDump: failed to sync changes to disk\n"));
+       ret = 1;
+       goto done;
+    }
+
+#ifdef FS_STATE_USE_MMAP
+    /* XXX madvise may not exist on all platforms, so
+     * we may need to add some ifdefs at some point... */
+    {
+       madvise((((char *)state->mmap.map) + sizeof(struct fs_state_header)), 
+               state->mmap.size - sizeof(struct fs_state_header), 
+               MADV_DONTNEED);
+    }
+#endif
+
+    /* build the header, and write it to disk */
+    fs_stateFillHeader(state->hdr);
+    if (state->bail) {
+       state->hdr->valid = 0;
+    }
+    if (fs_stateWriteHeader(state, &z, state->hdr, sizeof(struct fs_state_header))) {
+       ViceLog(0, ("fs_stateCommitDump: failed to write header to dump file '%s'\n",
+                   state->fn));
+       ret = 1;
+       goto done;
+    }
+    if (fs_stateSync(state)) {
+       ViceLog(0, ("fs_stateCommitDump: failed to sync new header to disk\n"));
+       ret = 1;
+       goto done;
+    }
+
+ done:
+    return ret;
+}
+
+static int
+fs_stateLoadDump(struct fs_dump_state * state)
+{
+    afs_uint64 z;
+    int fd, ret = 0;
+    struct afs_stat status;
+    afs_int32 now = FT_ApproxTime();
+
+    ZeroInt64(z);
+
+    if ((fd = afs_open(state->fn, O_RDWR)) == -1 ||
+       (afs_fstat(fd, &status) == -1)) {
+       ViceLog(0, ("fs_stateLoadDump: failed to load state dump file '%s'\n",
+                   state->fn));
+       ret = 1;
+       goto done;
+    }
+    state->fd = fd;
+    state->mode = FS_STATE_LOAD_MODE;
+    state->file_len = status.st_size;
+
+#ifdef FS_STATE_USE_MMAP
+    if (fs_stateMapFile(state)) {
+       ViceLog(0, ("fs_stateLoadDump: failed to memory map state dump file '%s'\n",
+                   state->fn));
+       ret = 1;
+       goto done;
+    }
+#endif
+
+    if (fs_stateReadHeader(state, &z, state->hdr, sizeof(struct fs_state_header))) {
+       ViceLog(0, ("fs_stateLoadDump: failed to read header from dump file '%s'\n",
+                   state->fn));
+       ret = 1;
+       goto done;
+    }
+
+    /* check the validity of the header */
+    if (fs_stateCheckHeader(state->hdr)) {
+       ViceLog(1, ("fs_stateLoadDump: header failed validity checks; not restoring '%s'\n",
+                   state->fn));
+       ret = 1;
+       goto done;
+    }
+
+    if ((state->hdr->timestamp + HOST_STATE_VALID_WINDOW) >= now) {
+       state->flags.do_host_restore = 1;
+    } else {
+       ViceLog(0, ("fs_stateLoadDump: warning: dump is too old for host and callback restore; skipping those steps\n"));
+    }
+
+ done:
+    return ret;
+}
+
+static int
+fs_stateCloseDump(struct fs_dump_state * state)
+{
+#ifdef FS_STATE_USE_MMAP
+    fs_stateUnmapFile(state);
+#endif
+    close(state->fd);
+    return 0;
+}
+
+int
+fs_stateWrite(struct fs_dump_state * state,
+             void * buf, size_t len)
+{
+    int ret = 0;
+
+#ifdef FS_STATE_USE_MMAP
+    if (fs_stateCheckIOSafety(state, len)) {
+       if (fs_stateResizeFile(state, len)) {
+           ViceLog(0, ("fs_stateWrite: could not resize dump file '%s'\n",
+                       state->fn));
+           ret = 1;
+           goto done;
+       }
+    }
+           
+    memcpy(state->mmap.cursor, buf, len);
+    fs_stateIncCursor(state, len);
+#else
+    if (write(state->fd, buf, len) != len) {
+       ViceLog(0, ("fs_stateWrite: write failed\n"));
+       ret = 1;
+       goto done;
+    }
+#endif
+
+ done:
+    return ret;
+}
+
+int
+fs_stateRead(struct fs_dump_state * state,
+            void * buf, size_t len)
+{
+    int ret = 0;
+
+#ifdef FS_STATE_USE_MMAP
+    if (fs_stateCheckIOSafety(state, len)) {
+       ViceLog(0, ("fs_stateRead: read beyond EOF for dump file '%s'\n",
+                   state->fn));
+       ret = 1;
+       goto done;
+    }
+
+    memcpy(buf, state->mmap.cursor, len);
+    fs_stateIncCursor(state, len);
+#else
+    if (read(state->fd, buf, len) != len) {
+       ViceLog(0, ("fs_stateRead: read failed\n"));
+       ret = 1;
+       goto done;
+    }
+#endif
+
+ done:
+    return ret;
+}
+
+int
+fs_stateWriteV(struct fs_dump_state * state,
+              struct iovec * iov, int niov)
+{
+    int i, ret = 0;
+    size_t len = 0;
+
+    for (i=0; i < niov; i++) {
+       len += iov[i].iov_len;
+    }
+
+#ifdef FS_STATE_USE_MMAP
+    if (fs_stateCheckIOSafety(state, len)) {
+       if (fs_stateResizeFile(state, len)) {
+           ViceLog(0, ("fs_stateWrite: could not resize dump file '%s'\n",
+                       state->fn));
+           ret = 1;
+           goto done;
+       }
+    }
+
+    for (i=0; i < niov; i++) {
+       memcpy(state->mmap.cursor, iov[i].iov_base, iov[i].iov_len);
+       fs_stateIncCursor(state, iov[i].iov_len);
+    }
+#else
+    if (writev(state->fd, iov, niov) != len) {
+       ViceLog(0, ("fs_stateWriteV: write failed\n"));
+       ret = 1;
+       goto done;
+    }
+#endif
+
+ done:
+    return ret;
+}
+
+int
+fs_stateReadV(struct fs_dump_state * state,
+             struct iovec * iov, int niov)
+{
+    int i, ret = 0;
+    size_t len = 0;
+
+    for (i=0; i < niov; i++) {
+       len += iov[i].iov_len;
+    }
+
+#ifdef FS_STATE_USE_MMAP
+    if (fs_stateCheckIOSafety(state, len)) {
+       ViceLog(0, ("fs_stateRead: read beyond EOF for dump file '%s'\n",
+                   state->fn));
+       ret = 1;
+       goto done;
+    }
+
+    for (i=0; i < niov; i++) {
+       memcpy(iov[i].iov_base, state->mmap.cursor, iov[i].iov_len);
+       fs_stateIncCursor(state, iov[i].iov_len);
+    }
+#else
+    if (readv(state->fd, iov, niov) != len) {
+       ViceLog(0, ("fs_stateReadV: read failed\n"));
+       ret = 1;
+       goto done;
+    }
+#endif
+
+ done:
+    return ret;
+}
+
+int
+fs_stateWriteHeader(struct fs_dump_state * state,
+                   afs_uint64 * offset,
+                   void * hdr, size_t len)
+{
+    int ret = 0;
+
+    if (fs_stateSeek(state, offset)) {
+       ViceLog(0, ("fs_stateWriteHeader: could not seek to correct position in dump file '%s'\n",
+                   state->fn));
+       ret = 1;
+       goto done;
+    }
+
+    if (fs_stateWrite(state, hdr, len)) {
+       ViceLog(0, ("fs_stateWriteHeader: write failed\n"));
+       ret = 1;
+       goto done;
+    }
+
+ done:
+    return ret;
+}
+
+int
+fs_stateReadHeader(struct fs_dump_state * state,
+                  afs_uint64 * offset,
+                  void * hdr, size_t len)
+{
+    int ret = 0;
+
+    if (fs_stateSeek(state, offset)) {
+       ViceLog(0, ("fs_stateReadHeader: could not seek to correct position in dump file '%s'\n",
+                   state->fn));
+       ret = 1;
+       goto done;
+    }
+
+    if (fs_stateRead(state, hdr,len)) {
+       ViceLog(0, ("fs_stateReadHeader: read failed\n"));
+       ret = 1;
+       goto done;
+    }
+
+ done:
+    return ret;
+}
+
+#ifdef FS_STATE_USE_MMAP
+static int
+fs_stateSizeFile(struct fs_dump_state * state)
+{
+    int ret = 0;
+    state->file_len = FS_STATE_INIT_FILESIZE;
+    if (afs_ftruncate(state->fd, state->file_len) != 0)
+       ret = 1;
+    return ret;
+}
+
+static int
+fs_stateResizeFile(struct fs_dump_state * state, size_t min_add)
+{
+    int ret = 0;
+    afs_foff_t inc;
+
+#ifdef FS_STATE_USE_MMAP
+    fs_stateUnmapFile(state);
+#endif
+
+    inc = ((min_add / FS_STATE_INIT_FILESIZE)+1) * FS_STATE_INIT_FILESIZE;
+    state->file_len += inc;
+
+    if (afs_ftruncate(state->fd, state->file_len) != 0) {
+       ViceLog(0, ("fs_stateResizeFile: truncate failed\n"));
+       ret = 1;
+       goto done;
+    }
+
+#ifdef FS_STATE_USE_MMAP
+    if (fs_stateMapFile(state)) {
+       ViceLog(0, ("fs_stateResizeFile: remapping memory mapped file failed\n"));
+       ret = 1;
+       goto done;
+    }
+#endif
+
+ done:
+    return ret;
+}
+
+static int
+fs_stateTruncateFile(struct fs_dump_state * state)
+{
+    int ret = 0;
+
+#ifdef AFS_LARGEFILE_ENV
+    if (afs_ftruncate(state->fd, state->eof_offset) != 0) {
+       ret = 1;
+    }
+#else
+    afs_uint32 hi, lo;
+    SplitInt64(state->eof_offset, hi, lo);
+    if (afs_ftruncate(state->fd, lo) != 0) {
+       ret = 1;
+    }
+#endif
+
+    return ret;
+}
+#endif
+
+#ifdef FS_STATE_USE_MMAP
+static int
+fs_stateMapFile(struct fs_dump_state * state)
+{
+    int ret = 0, flags;
+
+    switch(state->mode) {
+    case FS_STATE_LOAD_MODE:
+       flags = PROT_READ | PROT_WRITE;   /* loading involves a header invalidation */
+       break;
+    case FS_STATE_DUMP_MODE:
+       flags = PROT_WRITE;
+       break;
+    default:
+       ViceLog(0, ("fs_stateMapFile: invalid dump state mode\n"));
+       return 1;
+    }
+
+    state->mmap.map = afs_mmap(NULL, 
+                              state->file_len, 
+                              flags, 
+                              MAP_SHARED,
+                              state->fd, 
+                              0);
+
+    if (state->mmap.map == MAP_FAILED) {
+       state->mmap.size = 0;
+       state->mmap.map = NULL;
+       ViceLog(0, ("fs_stateMapFile: failed to memory map file '%s'\n",
+                   state->fn));
+       ret = 1;
+       goto done;
+    }
+
+    state->mmap.size = state->file_len;
+    state->mmap.cursor = state->mmap.map;
+    state->mmap.offset = 0;
+
+    /* for state loading, accesses will be sequential, so let's give
+     * the VM subsystem a heads up */
+    if (state->mode == FS_STATE_LOAD_MODE) {
+       /* XXX madvise may not exist on all platforms, so
+        * we may need to add some ifdefs at some point... */
+       flags = MADV_SEQUENTIAL | MADV_WILLNEED;
+#ifdef AFS_SUN510_ENV
+       flags |= MADV_ACCESS_LWP;   /* added in solaris 9 12/02 */
+#endif
+       madvise(state->mmap.map, state->mmap.size, flags);
+    }
+
+ done:
+    return ret;
+}
+
+static int
+fs_stateUnmapFile(struct fs_dump_state * state)
+{
+    int ret = 0;
+
+    if (munmap(state->mmap.map, state->mmap.size) == -1) {
+       ViceLog(0, ("fs_stateUnmapFile: failed to unmap dump file '%s'\n",
+                   state->fn));
+       ret = 1;
+       goto done;
+    }
+
+ done:
+    return ret;
+}
+#endif /* FS_STATE_USE_MMAP */
+
+#ifdef FS_STATE_USE_MMAP
+int
+fs_stateSync(struct fs_dump_state * state)
+{
+    int ret = 0;
+
+    msync(state->mmap.map, state->mmap.size, MS_SYNC);
+
+ done:
+    return ret;
+}
+#else /* !FS_STATE_USE_MMAP */
+int
+fs_stateSync(struct fs_dump_state * state)
+{
+    int ret = 0;
+
+    if (fsync(state->fd) == -1)
+       ret = 1;
+
+ done:
+    return ret;
+}
+#endif /* !FS_STATE_USE_MMAP */
+
+int
+fs_stateIncEOF(struct fs_dump_state * state, afs_int32 len)
+{
+    afs_uint64 temp;
+    FillInt64(temp, 0, len);
+    AddUInt64(state->eof_offset, temp, &state->eof_offset);
+    return 0;
+}
+
+#ifdef FS_STATE_USE_MMAP
+static int
+fs_stateIncCursor(struct fs_dump_state * state, size_t len)
+{
+    char * p;
+
+    state->mmap.offset += len;
+
+    p = (char *) state->mmap.cursor;
+    p += len;
+    state->mmap.cursor = (void *) p;
+
+    return 0;
+}
+
+static int
+fs_stateCheckIOSafety(struct fs_dump_state * state, size_t len)
+{
+    int ret = 0;
+
+    if ((state->mmap.offset + len) > state->mmap.size) {
+       ret = 1;
+    }
+    return ret;
+}
+#endif /* FS_STATE_USE_MMAP */
+
+#ifdef FS_STATE_USE_MMAP
+int
+fs_stateSeek(struct fs_dump_state * state, afs_uint64 * offset)
+{
+    int ret = 0;
+    char * p;
+    afs_uint32 hi, lo;
+
+    SplitInt64(*offset, hi, lo);
+
+    /* update cursor */
+    p = (char *) state->mmap.map;
+#ifdef AFS_64BIT_ENV
+    p += *offset;
+#else
+    p += lo;
+#endif
+    state->mmap.cursor = (void *) p;
+
+    /* update offset */
+#ifdef AFS_LARGEFILE_ENV
+    state->mmap.offset = *offset;
+#else
+    if (hi)
+       ret = 1;
+    state->mmap.offset = lo;
+#endif
+
+    return ret;
+}
+#else /* !FS_STATE_USE_MMAP */
+int
+fs_stateSeek(struct fs_dump_state * state, afs_uint64 * offset)
+{
+    int ret = 0;
+#ifndef AFS_LARGEFILE_ENV
+    afs_uint32 high, low;
+    
+    SplitInt64(*offset, high, low);
+    if (high) {
+       ret = 1;
+       goto done;
+    }
+    
+    if (afs_lseek(state->fd, low, SEEK_SET) == -1)
+       ret = 1;
+#else
+    if (afs_lseek(state->fd, *offset, SEEK_SET) == -1)
+       ret = 1;
+#endif
+    return ret;
+}
+#endif /* !FS_STATE_USE_MMAP */
+
+static int
+fs_stateFillHeader(struct fs_state_header * hdr)
+{
+    hdr->stamp.magic = FS_STATE_MAGIC;
+    hdr->stamp.version = FS_STATE_VERSION;
+#ifdef SYS_NAME_ID
+    hdr->sys_name = SYS_NAME_ID;
+#else
+    hdr->sys_name = 0xFFFFFFFF;
+#endif
+    hdr->timestamp = FT_ApproxTime();
+    hdr->server_uuid = FS_HostUUID;
+    hdr->valid = 1;
+#ifdef AFSBIG_ENDIAN
+    hdr->endianness = 1;
+#else
+    hdr->endianness = 0;
+#endif
+#ifdef FS_STATS_DETAILED
+    hdr->stats_detailed = 1;
+#else
+    hdr->stats_detailed = 0;
+#endif
+    if (strlcpy(hdr->server_version_string, cml_version_number, sizeof(hdr->server_version_string))
+       >= sizeof(hdr->server_version_string)) {
+       ViceLog(0, ("fs_stateFillHeader: WARNING -- cml_version_number field truncated\n"));
+    }
+    return 0;
+}
+
+static int
+fs_stateCheckHeader(struct fs_state_header * hdr)
+{
+    int ret = 0;
+
+    if (!hdr->valid) {
+       ViceLog(0, ("fs_stateCheckHeader: dump was previously flagged invalid\n"));
+       ret = 1;
+    }
+#ifdef AFSBIG_ENDIAN
+    else if (!hdr->endianness) {
+       ViceLog(0, ("fs_stateCheckHeader: wrong endianness\n"));
+       ret = 1;
+    }
+#else /* AFSLITTLE_ENDIAN */
+    else if (hdr->endianness) {
+       ViceLog(0, ("fs_stateCheckHeader: wrong endianness\n"));
+       ret = 1;
+    }
+#endif /* AFSLITTLE_ENDIAN */
+
+    else if (hdr->stamp.magic != FS_STATE_MAGIC) {
+       ViceLog(0, ("fs_stateCheckHeader: invalid dump header\n"));
+       ret = 1;
+    }
+    else if (hdr->stamp.version != FS_STATE_VERSION) {
+       ViceLog(0, ("fs_stateCheckHeader: unknown dump format version number\n"));
+       ret = 1;
+    }
+
+#ifdef FS_STATS_DETAILED
+    else if (!hdr->stats_detailed) {
+       ViceLog(0, ("fs_stateCheckHeader: wrong config flags\n"));
+       ret = 1;
+    }
+#else /* FS_STATS_DETAILED */
+    else if (hdr->stats_detailed) {
+       ViceLog(0, ("fs_stateCheckHeader: wrong config flags\n"));
+       ret = 1;
+    }
+#endif /* FS_STATS_DETAILED */
+
+    else if (!afs_uuid_equal(&hdr->server_uuid, &FS_HostUUID)) {
+       ViceLog(0, ("fs_stateCheckHeader: server UUID does not match this server's UUID\n"));
+       ret = 1;
+    }
+
+    /* the cml_version_string is included for informational purposes only.  If someone ever
+     * wants to limit state dump reloading based upon the contents of this string, just
+     * uncomment the following code.  uncommenting this code is _strongly discouraged_ because
+     * we already make use of the version stamps in the various dump headers to deal with
+     * data structure version incompatabilities.
+    else if (strncmp(hdr->server_version_string, cml_version_number, 
+                    sizeof(hdr->server_version_string)) != 0) {
+       ViceLog(0, ("fs_stateCheckHeader: dump from different server version\n"));
+       ret = 1;
+    }
+    */
+
+    else if (strncmp(hdr->server_version_string, cml_version_number, 
+                    sizeof(hdr->server_version_string)) != 0) {
+       ViceLog(0, ("fs_stateCheckHeader: dump from different server version ; attempting state reload anyway\n"));
+    }
+
+
+    return ret;
+}
+
+static int
+fs_stateAlloc(struct fs_dump_state * state)
+{
+    int ret = 0;
+    memset(state, 0, sizeof(struct fs_dump_state));
+    state->fd = -1;
+    state->fn = AFSDIR_SERVER_FSSTATE_FILEPATH;
+    state->hdr = (struct fs_state_header *)malloc(sizeof(struct fs_state_header));
+    state->h_hdr = (struct host_state_header *)malloc(sizeof(struct host_state_header));
+    state->cb_hdr = (struct callback_state_header *)malloc(sizeof(struct callback_state_header));
+    state->cb_timeout_hdr = (struct callback_state_timeout_header *)
+      malloc(sizeof(struct callback_state_timeout_header));
+    state->cb_fehash_hdr = (struct callback_state_fehash_header *)
+      malloc(sizeof(struct callback_state_fehash_header));
+    if ((state->hdr == NULL) || (state->h_hdr == NULL) || (state->cb_hdr == NULL) ||
+       (state->cb_timeout_hdr == NULL) || (state->cb_fehash_hdr == NULL))
+       ret = 1;
+    return ret;
+}
+
+static int
+fs_stateFree(struct fs_dump_state * state)
+{
+    if (state->hdr)
+       free(state->hdr);
+    if (state->h_hdr)
+       free(state->h_hdr);
+    if (state->cb_hdr)
+       free(state->cb_hdr);
+    if (state->cb_timeout_hdr)
+       free(state->cb_timeout_hdr);
+    if (state->cb_fehash_hdr)
+       free(state->cb_fehash_hdr);
+    if (state->h_map.entries)
+       free(state->h_map.entries);
+    if (state->fe_map.entries)
+       free(state->fe_map.entries);
+    if (state->cb_map.entries)
+       free(state->cb_map.entries);
+    return 0;
+}
+
+#endif /* AFS_DEMAND_ATTACH_FS */
diff --git a/src/tviced/serialize_state.h b/src/tviced/serialize_state.h
new file mode 100644 (file)
index 0000000..c1a08c0
--- /dev/null
@@ -0,0 +1,311 @@
+/*
+ * Copyright 2006, Sine Nomine Associates and others.
+ * All Rights Reserved.
+ * 
+ * This software has been released under the terms of the IBM Public
+ * License.  For details, see the LICENSE file in the top-level source
+ * directory or online at http://www.openafs.org/dl/license10.html
+ */
+
+/*
+ * demand attach fs
+ * fileserver state serialization
+ */
+
+#ifndef _AFS_TVICED_SERIALIZE_STATE_H
+#define _AFS_TVICED_SERIALIZE_STATE_H
+
+#ifdef AFS_DEMAND_ATTACH_FS
+
+#define FS_STATE_MAGIC 0x62FA841C
+#define FS_STATE_VERSION 2
+
+#define HOST_STATE_MAGIC 0x7B8C9DAE
+#define HOST_STATE_VERSION 2
+
+#define HOST_STATE_ENTRY_MAGIC 0xA8B9CADB
+
+#define CALLBACK_STATE_MAGIC 0x89DE67BC
+#define CALLBACK_STATE_VERSION 1
+
+#define CALLBACK_STATE_TIMEOUT_MAGIC 0x99DD5511
+#define CALLBACK_STATE_FEHASH_MAGIC 0x77BB33FF
+#define CALLBACK_STATE_ENTRY_MAGIC 0x54637281
+
+#define ACTIVE_VOLUME_STATE_MAGIC 0xAC7557CA
+#define ACTIVE_VOLUME_STATE_VERSION 1
+
+#define ACTIVE_VOLUME_STATE_AVEHASH_MAGIC 0xBADDF00D
+
+#define HOST_STATE_VALID_WINDOW 1800 /* 30 minutes */
+
+/*
+ * on-disk structures
+ */
+struct disk_version_stamp {
+    afs_uint32 magic;
+    afs_uint32 version;
+};
+
+/* 1024 byte header structure */
+struct fs_state_header {
+    struct disk_version_stamp stamp;  /* version stamp */
+    afs_uint32 timestamp;             /* timestamp of save */
+    afs_uint32 sys_name;              /* sys name id for this machine */
+    afsUUID server_uuid;              /* server's UUID */
+    byte valid;                       /* whether header contents are valid */
+    byte endianness;                  /* endianness sanity check (0 for LE, 1 for BE) */
+    byte stats_detailed;              /* fs stats detailed sanity check */
+    byte padding1[1];                 /* padding */
+    afs_uint32 reserved1[23];         /* for expansion */
+    afs_uint64 avol_offset;           /* offset of active volumes structure */
+    afs_uint64 h_offset;              /* offset of host_state_header structure */
+    afs_uint64 cb_offset;             /* offset of callback_state_header structure */
+    afs_uint64 vlru_offset;           /* offset of vlru state structure */
+    afs_uint32 reserved2[56];         /* for expansion */
+    char server_version_string[128];  /* version string from AFS_component_version_number.c */
+    afs_uint32 reserved3[128];        /* for expansion */
+};
+
+/*
+ * host package serialization
+ */
+
+/* 256 byte header for the host state data */
+struct host_state_header {
+    struct disk_version_stamp stamp;  /* host state version stamp */
+    afs_uint32 records;               /* number of stored host records */
+    afs_uint32 index_max;             /* max index value encountered */
+    afs_uint32 reserved[60];          /* for expansion */
+};
+
+/* 32 byte host entry header */
+struct host_state_entry_header {
+    afs_uint32 magic;         /* stamp */
+    afs_uint32 len;           /* number of bytes in this record */
+    afs_uint32 interfaces;    /* number of interfaces included in record */
+    afs_uint32 hcps;          /* number of hcps entries in record */
+    afs_uint32 reserved[4];
+};
+
+/* 36 byte host entry structure */
+struct hostDiskEntry {
+    afs_uint32 host;           /* IP address of host interface that is
+                                * currently being used, in network
+                                * byte order */
+    afs_uint16 port;           /* port address of host */
+    afs_uint16 hostFlags;       /*  bit map */
+    byte Console;              /* XXXX This host is a console */
+    byte hcpsfailed;           /* Retry the cps call next time */
+    byte hcps_valid;            /* prlist_val not null */
+#if FS_STATS_DETAILED
+    byte InSameNetwork;                /*Is host's addr in the same network as
+                                * the File Server's? */
+#else
+    byte padding1[1];          /* for padding */
+#endif                         /* FS_STATS_DETAILED */
+    afs_uint32 hcps_len;        /* length of hcps */
+    afs_uint32 LastCall;       /* time of last call from host */
+    afs_uint32 ActiveCall;     /* time of any call but gettime */
+    afs_uint32 cpsCall;                /* time of last cps call from this host */
+    afs_uint32 cblist;         /* Call back list for this host */
+    afs_uint32 index;           /* index for correlating w/ callback dumps */
+};
+
+/*
+ * callback package serialization
+ */
+
+/* 512 byte header */
+struct callback_state_header {
+    struct disk_version_stamp stamp;    /* callback state version stamp */
+    afs_uint32 nFEs;                    /* number of FileEntry records */
+    afs_uint32 nCBs;                    /* number of CallBack records */
+    afs_uint32 fe_max;                  /* max FileEntry index */
+    afs_uint32 cb_max;                  /* max CallBack index */
+    afs_int32 tfirst;                   /* first valid timeout */
+    afs_uint32 reserved[115];           /* for expansion */
+    afs_uint64 timeout_offset;          /* offset of timeout queue heads */
+    afs_uint64 fehash_offset;           /* offset of file entry hash buckets */
+    afs_uint64 fe_offset;               /* offset of first file entry */
+};
+
+/* 32 byte header */
+struct callback_state_timeout_header {
+    afs_uint32 magic;         /* magic number for timeout header */
+    afs_uint32 len;           /* total length of header and timeout records */
+    afs_uint32 records;       /* number of timeout records */
+    afs_uint32 reserved[5];
+};
+
+/* 32 byte header */
+struct callback_state_fehash_header {
+    afs_uint32 magic;         /* magic number for fehash header */
+    afs_uint32 len;           /* total length of header and fehash bucket heads */
+    afs_uint32 records;       /* number of hash buckets */
+    afs_uint32 reserved[5];
+};
+
+/* 32 byte header */
+struct callback_state_entry_header {
+    afs_uint32 magic;         /* magic number for FE entry */
+    afs_uint32 len;           /* number of bytes in this record */
+    afs_uint32 nCBs;          /* number of callbacks for this FE */
+    afs_uint32 reserved[5];
+};
+
+struct FEDiskEntry {
+    struct FileEntry fe;
+    afs_uint32 index;
+};
+
+struct CBDiskEntry {
+    struct CallBack cb;
+    afs_uint32 index;
+};
+
+/*
+ * active volumes state serialization
+ *
+ * these structures are meant to support
+ * automated salvaging of active volumes
+ * in the event of a fileserver crash
+ */
+
+/* 512 byte header */
+struct active_volume_state_header {
+    struct disk_version_stamp stamp;    /* callback state version stamp */
+    afs_uint32 nAVEs;                   /* number of ActiveVolumeEntry records */
+    afs_uint32 init_timestamp;          /* timestamp of AVE initialization */
+    afs_uint32 update_timetamp;         /* timestamp of last AVE update */
+    afs_uint32 reserved[119];           /* for expansion */
+    afs_uint64 avehash_offset;          /* offset of active volume entry hash buckets */
+    afs_uint64 ave_offset;              /* offset of first active volume entry */
+};
+
+/* 32 byte header */
+struct active_volume_state_avehash_header {
+    afs_uint32 magic;         /* magic number for avehash header */
+    afs_uint32 len;           /* total length of header and avehash bucket heads */
+    afs_uint32 records;       /* number of hash buckets */
+    afs_uint32 reserved[5];
+};
+
+typedef afs_uint32 active_volume_state_avehash_entry;
+
+/* active volume entry */
+struct AVDiskEntry {
+    afs_uint32 volume;
+    afs_uint32 partition;
+    afs_uint32 hash_next;
+};
+
+
+/*
+ * dump runtime state
+ */
+struct idx_map_entry_t {
+    afs_uint32 old_idx;                    /* host hash id from last runtime */
+    afs_uint32 new_idx;                    /* host hash id for this runtime */
+};
+
+
+/* verification process sanity check constants
+ *
+ * make them fairly large so we don't get 
+ * false positives 
+ */
+#define FS_STATE_H_MAX_UUID_HASH_CHAIN_LEN    100000     /* max elements in a host uuid-hash chain */
+#define FS_STATE_H_MAX_ADDR_HASH_CHAIN_LEN    2000000    /* max elements in a host ipv4-hash chain */
+#define FS_STATE_FE_MAX_HASH_CHAIN_LEN        100000     /* max elements in a FE fid-hash chain */
+#define FS_STATE_FCB_MAX_LIST_LEN             100000     /* max elements in a per-FE CB list */
+#define FS_STATE_HCB_MAX_LIST_LEN             100000     /* max elements in a per-host CB list */
+#define FS_STATE_TCB_MAX_LIST_LEN             100000     /* max elements in a per-timeout CB list */
+
+
+/*
+ * main state serialization state structure
+ */
+
+struct fs_dump_state {
+    enum {
+       FS_STATE_DUMP_MODE,
+       FS_STATE_LOAD_MODE
+    } mode;
+    struct {
+       byte do_host_restore;              /* whether host restore should be done */
+       byte some_steps_skipped;           /* whether some steps were skipped */
+       byte warnings_generated;           /* whether any warnings were generated during restore */
+    } flags;
+    afs_fsize_t file_len;
+    int fd;                                /* fd of the current dump file */
+    int bail;                              /* non-zero if something went wrong */
+    char * fn;                             /* name of the current dump file */
+    struct {                               /* memory map of dump file */
+       void * map;
+       void * cursor;
+       afs_foff_t offset;
+       afs_fsize_t size;
+    } mmap;
+    struct fs_state_header * hdr;          /* main header */
+    struct host_state_header * h_hdr;      /* header for host state data */
+    struct callback_state_header * cb_hdr; /* header for callback state data */
+    struct callback_state_timeout_header * cb_timeout_hdr;
+    struct callback_state_fehash_header * cb_fehash_hdr;
+    afs_uint64 eof_offset;                 /* current end of file offset */
+    struct {
+       int len;                           /* number of host entries in map */
+       struct idx_map_entry_t * entries;
+    } h_map;
+    struct {
+       int len;
+       struct idx_map_entry_t * entries;
+    } fe_map;
+    struct {
+       int len;
+       struct idx_map_entry_t * entries;
+    } cb_map;
+};
+
+
+/* prototypes */
+
+/* serialize_state.c */
+extern int fs_stateWrite(struct fs_dump_state * state,
+                        void * buf, size_t len);
+extern int fs_stateRead(struct fs_dump_state * state,
+                       void * buf, size_t len);
+extern int fs_stateWriteV(struct fs_dump_state * state,
+                         struct iovec * iov, int niov);
+extern int fs_stateReadV(struct fs_dump_state * state,
+                        struct iovec * iov, int niov);
+extern int fs_stateSync(struct fs_dump_state * state);
+extern int fs_stateWriteHeader(struct fs_dump_state * state,
+                              afs_uint64 * offset,
+                              void * hdr, size_t len);
+extern int fs_stateReadHeader(struct fs_dump_state * state,
+                             afs_uint64 * offset,
+                             void * hdr, size_t len);
+extern int fs_stateIncEOF(struct fs_dump_state * state,
+                         afs_int32 len);
+extern int fs_stateSeek(struct fs_dump_state * state,
+                       afs_uint64 * offset);
+
+/* host.c */
+extern int h_stateSave(struct fs_dump_state * state);
+extern int h_stateRestore(struct fs_dump_state * state);
+extern int h_stateRestoreIndices(struct fs_dump_state * state);
+extern int h_stateVerify(struct fs_dump_state * state);
+extern int h_OldToNew(struct fs_dump_state * state, afs_uint32 old, afs_uint32 * new);
+
+/* callback.c */
+extern int cb_stateSave(struct fs_dump_state * state);
+extern int cb_stateRestore(struct fs_dump_state * state);
+extern int cb_stateRestoreIndices(struct fs_dump_state * state);
+extern int cb_stateVerify(struct fs_dump_state * state);
+extern int cb_stateVerifyHCBList(struct fs_dump_state * state, struct host * host);
+extern int fe_OldToNew(struct fs_dump_state * state, afs_uint32 old, afs_uint32 * new);
+extern int cb_OldToNew(struct fs_dump_state * state, afs_uint32 old, afs_uint32 * new);
+
+#endif /* AFS_DEMAND_ATTACH_FS */
+#endif /* _AFS_TVICED_SERIALIZE_STATE_H */
diff --git a/src/tviced/state_analyzer.c b/src/tviced/state_analyzer.c
new file mode 100644 (file)
index 0000000..ae8c3ff
--- /dev/null
@@ -0,0 +1,2004 @@
+/*
+ * Copyright 2006, Sine Nomine Associates and others.
+ * All Rights Reserved.
+ * 
+ * This software has been released under the terms of the IBM Public
+ * License.  For details, see the LICENSE file in the top-level source
+ * directory or online at http://www.openafs.org/dl/license10.html
+ */
+
+/*
+ * demand attach fs
+ * fileserver state serialization
+ *
+ * state analyzer
+ */
+
+#include <afsconfig.h>
+#include <afs/param.h>
+
+RCSID
+    ("$Header$");
+
+#include <stdio.h>
+#include <errno.h>
+#include <sys/file.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <time.h>
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#else
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+#endif
+
+#include <afs/stds.h>
+#include <rx/xdr.h>
+#include <afs/assert.h>
+#include <lwp.h>
+#include <lock.h>
+#include <afs/afsint.h>
+#include <afs/rxgen_consts.h>
+#include <afs/nfs.h>
+#include <afs/errors.h>
+#include <afs/ihandle.h>
+#include <afs/vnode.h>
+#include <afs/volume.h>
+#ifdef AFS_ATHENA_STDENV
+#include <krb.h>
+#endif
+#include <afs/acl.h>
+#include <afs/ptclient.h>
+#include <afs/prs_fs.h>
+#include <afs/auth.h>
+#include <afs/afsutil.h>
+#include <rx/rx.h>
+#include <afs/cellconfig.h>
+#include <stdlib.h>
+#include "../util/afsutil_prototypes.h"
+#include "../viced/viced.h"
+#include "../viced/host.h"
+#include "../viced/callback.h"
+#include "serialize_state.h"
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+/*@+fcnmacros +macrofcndecl@*/
+#ifdef O_LARGEFILE
+#ifdef S_SPLINT_S
+extern off64_t afs_lseek(int FD, off64_t O, int F);
+#endif /*S_SPLINT_S */
+#define afs_lseek(FD, O, F)    lseek64(FD, (off64_t)(O), F)
+#define afs_stat               stat64
+#define afs_fstat              fstat64
+#define afs_open               open64
+#define afs_fopen              fopen64
+#define afs_mmap                mmap64
+#ifdef AFS_AIX_ENV
+extern void * mmap64();  /* ugly hack since aix build env appears to be somewhat broken */
+#endif
+#else /* !O_LARGEFILE */
+#ifdef S_SPLINT_S
+extern off_t afs_lseek(int FD, off_t O, int F);
+#endif /*S_SPLINT_S */
+#define afs_lseek(FD, O, F)    lseek(FD, (off_t)(O), F)
+#define afs_stat               stat
+#define afs_fstat              fstat
+#define afs_open               open
+#define afs_fopen              fopen
+#define afs_mmap                mmap
+#endif /* !O_LARGEFILE */
+/*@=fcnmacros =macrofcndecl@*/
+
+
+#ifndef AFS_DEMAND_ATTACH_FS
+int
+main (int argc, char ** argv)
+{
+    fprintf(stderr, "%s is only supported for demand attach fileservers\n",
+           argv[0] ? argv[0] : "state analyzer");
+    return 1;
+}
+#else /* AFS_DEMAND_ATTACH_FS */
+
+static void usage(char * prog);
+static int openFile(char * path);
+static void initState(void);
+
+static void banner(void);
+static void prompt(void);
+
+static void print_help(void);
+static void print_global_help(void);
+static void print_h_help(void);
+static void print_fe_help(void);
+static void print_cb_help(void);
+
+static void dump_hdr(void);
+static void dump_h_hdr(void);
+static void dump_cb_hdr(void);
+
+static void dump_cb_timeout(void);
+static void dump_cb_fehash(void);
+
+static void dump_all_hes(void);
+static void dump_all_fes(void);
+static void dump_all_cbs(void);
+
+static void dump_he(afs_uint32 idx);
+static void dump_fe(afs_uint32 idx);
+static void dump_cb(afs_uint32 idx);
+static void dump_this_he(void);
+static void dump_this_fe(void);
+static void dump_this_cb(void);
+static void dump_next_he(void);
+static void dump_next_fe(void);
+static void dump_next_cb(void);
+static void dump_prev_he(void);
+static void dump_prev_fe(void);
+static void dump_prev_cb(void);
+static void dump_first_he(void);
+static void dump_first_fe(void);
+static void dump_first_cb(void);
+static void dump_last_he(void);
+static void dump_last_fe(void);
+static void dump_last_cb(void);
+static void dump_he_hdr(void);
+static void dump_he_entry(void);
+static void dump_he_interfaces(void);
+static void dump_he_hcps(void);
+static void dump_fe_hdr(void);
+static void dump_fe_entry(void);
+static void dump_cb_entry(void);
+
+static void hexdump_map(afs_uint32 offset, afs_uint32 len);
+
+static int get_hdr(void);
+static int get_h_hdr(void);
+static int get_cb_hdr(void);
+static int get_cb_timeout_hdr(void);
+static int get_cb_timeout(void);
+static int get_cb_fehash_hdr(void);
+static int get_cb_fehash(void);
+static int get_he(afs_uint32 idx);
+static int get_he_hdr(void);
+static int get_he_entry(void);
+static int get_fe(afs_uint32 idx);
+static int get_fe_hdr(void);
+static int get_fe_entry(void);
+static int get_cb(afs_uint32 idx);
+static int get_cb_entry(void);
+
+static int find_fe_by_index(afs_uint32 idx);
+static int find_cb_by_index(afs_uint32 idx);
+static int find_fe_by_fid(afs_uint32 vol, afs_uint32 vn, afs_uint32 uniq);
+
+
+static int dump_fd = -1;
+static void * map = NULL;
+static size_t map_len;
+
+static struct {
+    struct fs_state_header hdr;
+    struct host_state_header h_hdr;
+    struct callback_state_header cb_hdr;
+    struct callback_state_timeout_header timeout_hdr;
+    struct callback_state_fehash_header fehash_hdr;
+    afs_uint32 * timeout;
+    afs_uint32 * fehash;
+
+    /* pointers into the memory map */
+    void * hdr_p;
+    void * h_hdr_p;
+    void * cb_hdr_p;
+    void * timeout_hdr_p;
+    void * timeout_p;
+    void * fehash_hdr_p;
+    void * fehash_p;
+
+    byte hdr_valid;
+    byte h_hdr_valid;
+    byte cb_hdr_valid;
+    byte timeout_hdr_valid;
+    byte fehash_hdr_valid;
+} hdrs;
+
+static struct {
+    void * fh;
+    void * cursor;
+    void * ifp;
+    void * hcps;
+    struct host_state_entry_header hdr;
+    struct hostDiskEntry he;
+    afs_uint32 idx;
+    byte hdr_valid;
+    byte he_valid;
+} he_cursor;
+
+static struct {
+    void ** cursor;
+} he_cache;
+
+static struct {
+    void * ffe;
+    void * cursor;
+    void * fcb;
+    struct callback_state_entry_header hdr;
+    struct FEDiskEntry fe;
+    afs_uint32 idx;
+    byte hdr_valid;
+    byte fe_valid;
+} fe_cursor;
+
+static struct {
+    void ** cursor;
+} fe_cache;
+
+static struct {
+    void * cursor;
+    struct CBDiskEntry cb;
+    afs_uint32 idx;
+    byte cb_valid;
+} cb_cursor;
+
+static struct {
+    void ** cursor;
+} cb_cache;
+
+static void
+usage(char * prog)
+{
+    fprintf(stderr, "usage: %s [<state dump file>]\n");
+}
+
+int
+main(int argc, char ** argv)
+{
+    banner();
+
+    if (argc > 2 || (argc == 2 && !strcmp(argv[1], "-h"))) {
+       usage(argv[0]);
+       return 1;
+    }
+
+    initState();
+
+    if (argc > 1) {
+       if (openFile(argv[1]))
+           return 1;
+    } else {
+       if (openFile(AFSDIR_SERVER_FSSTATE_FILEPATH))
+           return 1;
+    }
+
+    prompt();
+    return 0;
+}
+
+
+static int
+openFile(char * path)
+{
+    int ret = 0;
+    struct afs_stat status;
+    
+    dump_fd = afs_open(path, O_RDWR);
+    if (dump_fd == -1) {
+       fprintf(stderr, "dump file '%s' failed to open\n", path);
+       ret = 1;
+       goto done;
+    }
+
+    printf("opened dump file '%s'\n", path);
+
+    if (afs_fstat(dump_fd, &status) == -1) {
+       fprintf(stderr, "failed to stat file\n");
+       ret = 1;
+       goto done;
+    }
+
+    map_len = status.st_size;
+
+    map = afs_mmap(NULL, map_len, PROT_READ, MAP_SHARED, dump_fd, 0);
+    if (map == MAP_FAILED) {
+       fprintf(stderr, "failed to mmap file\n");
+       ret = 1;
+       goto done;
+    }
+
+    printf("mapped %d bytes at 0x%x\n", map_len, map);
+
+ done:
+    if (ret) {
+       if (map) {
+           munmap(map, map_len);
+           map = NULL;
+       }
+       if (dump_fd != -1) {
+           close(dump_fd);
+           dump_fd = -1;
+       }
+    }
+    return ret;
+}
+
+static void
+initState(void)
+{
+    hdrs.hdr_valid = hdrs.h_hdr_valid = hdrs.cb_hdr_valid = 0;
+    he_cursor.cursor = fe_cursor.cursor = cb_cursor.cursor = NULL;
+    he_cursor.fh = fe_cursor.ffe = fe_cursor.fcb = NULL;
+    he_cache.cursor = fe_cache.cursor = NULL;
+}
+
+static void
+banner(void)
+{
+    fprintf(stderr, "demand attach fs\n");
+    fprintf(stderr, "fileserver state analyzer\n");
+    fprintf(stderr, "version 0.1\n");
+}
+
+#define PROGNAME "fs state analyzer"
+
+static void
+prompt(void)
+{
+    char input[256];
+    char prev_input[256];
+    char * tok = NULL;
+    afs_uint32 x, y, z;
+    enum {
+       PR_GLOBAL_MODE,
+       PR_H_MODE,
+       PR_FE_MODE,
+       PR_CB_MODE
+    } mode = PR_GLOBAL_MODE, next_mode;
+
+    next_mode = mode;
+    input[0] = prev_input[0] = '\0';
+
+    while (1) {
+       if (!tok) {
+           switch(mode) {
+           case PR_GLOBAL_MODE:
+               printf(PROGNAME "> ");
+               break;
+           case PR_H_MODE:
+               printf(PROGNAME ": h(%d)> ", he_cursor.idx);
+               break;
+           case PR_FE_MODE:
+               printf(PROGNAME ": fe(%d)> ", fe_cursor.idx);
+               break;
+           case PR_CB_MODE:
+               printf(PROGNAME ": fe(%d):cb(%d)> ", fe_cursor.idx, cb_cursor.idx);
+               break;
+           default:
+               fprintf(stderr, "prompt state broken; aborting\n");
+               return;
+           }
+           gets(input);
+
+           if (!strcmp(input, "")) {
+               /* repeat last command */
+               if (!strcmp(prev_input, "")) {
+                   continue;
+               }
+               strlcpy(input, prev_input, sizeof(input));
+           } else {
+               /* save command for repetition */
+               strlcpy(prev_input, input, sizeof(prev_input));
+           }
+
+           tok = strtok(input, " \t");
+       }
+       while (tok && !strcmp(tok, ";")) {
+           tok = strtok(NULL, "; \t");
+       }
+
+       if (!tok) {
+           continue;
+       }
+
+       if (!strcasecmp(tok, "exit")) {
+           return;
+       } else if (!strcasecmp(tok, "quit")) {
+           switch(mode) {
+           case PR_CB_MODE:
+               next_mode = PR_FE_MODE;
+               break;
+           case PR_FE_MODE:
+           case PR_H_MODE:
+               next_mode = PR_GLOBAL_MODE;
+               break;
+           default:
+               return;
+           }
+       } else if (!strcasecmp(tok, "h")) {
+           tok = strtok(NULL, " \t");
+           mode = PR_H_MODE;
+           if (!tok) {
+               next_mode = mode;
+           }
+           continue;
+       } else if (!strcasecmp(tok, "fe")) {
+           tok = strtok(NULL, " \t");
+           mode = PR_FE_MODE;
+           if (!tok) {
+               next_mode = mode;
+           }
+           continue;
+       } else if (!strcasecmp(tok, "fs")) {
+           tok = strtok(NULL, " \t");
+           mode = PR_GLOBAL_MODE;
+           if (!tok) {
+               next_mode = mode;
+           }
+           continue;
+       } else if (!strcasecmp(tok, "cb")) {
+           tok = strtok(NULL, " \t");
+           mode = PR_CB_MODE;
+           if (!tok) {
+               next_mode = mode;
+           }
+           continue;
+       } else if (!strcasecmp(tok, "help")) {
+           switch(mode) {
+           case PR_H_MODE:
+               print_h_help();
+               break;
+           case PR_FE_MODE:
+               print_fe_help();
+               break;
+           case PR_CB_MODE:
+               print_cb_help();
+               break;
+           default:
+               print_global_help();
+           }
+           print_help();
+       } else if (!strcasecmp(tok, "hexdump")) {
+           tok = strtok(NULL, " \t");
+           if (!tok) {
+               hexdump_map(0, map_len);
+               continue;
+           }
+           if (sscanf(tok, "%u", &x) != 1) {
+               fprintf(stderr, "hexdump parse error 1\n");
+               tok = NULL;
+               continue;
+           }
+           tok = strtok(NULL, " \t");
+           if (!tok) {
+               hexdump_map(x, map_len - x);
+               continue;
+           }
+           if (sscanf(tok, "%u", &y) != 1) {
+               fprintf(stderr, "hexdump parse error 2\n");
+               continue;
+           }
+           hexdump_map(x,y);
+       } else if (!strcasecmp(tok, "hdr")) {
+           switch(mode) {
+           case PR_H_MODE:
+               dump_h_hdr();
+               break;
+           case PR_FE_MODE:
+               dump_cb_hdr();
+               break;
+           case PR_CB_MODE:
+               dump_this_fe();
+               break;
+           default:
+               dump_hdr();
+           }
+       } else if (!strcasecmp(tok, "this")) {
+           switch(mode) {
+           case PR_H_MODE:
+               dump_this_he();
+               break;
+           case PR_FE_MODE:
+               dump_this_fe();
+               break;
+           case PR_CB_MODE:
+               dump_this_cb();
+               break;
+           default:
+               fprintf(stderr, "command not valid for this mode\n");
+           }
+       } else if (!strcasecmp(tok, "next")) {
+           switch(mode) {
+           case PR_H_MODE:
+               dump_next_he();
+               break;
+           case PR_FE_MODE:
+               dump_next_fe();
+               break;
+           case PR_CB_MODE:
+               dump_next_cb();
+               break;
+           default:
+               fprintf(stderr, "command not valid for this mode\n");
+           }
+       } else if (!strcasecmp(tok, "prev")) {
+           switch(mode) {
+           case PR_H_MODE:
+               dump_prev_he();
+               break;
+           case PR_FE_MODE:
+               dump_prev_fe();
+               break;
+           case PR_CB_MODE:
+               dump_prev_cb();
+               break;
+           default:
+               fprintf(stderr, "command not valid for this mode\n");
+           }
+       } else if (!strcasecmp(tok, "first")) {
+           switch(mode) {
+           case PR_H_MODE:
+               dump_first_he();
+               break;
+           case PR_FE_MODE:
+               dump_first_fe();
+               break;
+           case PR_CB_MODE:
+               dump_first_cb();
+               break;
+           default:
+               fprintf(stderr, "command not valid for this mode\n");
+           }
+       } else if (!strcasecmp(tok, "last")) {
+           switch(mode) {
+           case PR_H_MODE:
+               dump_last_he();
+               break;
+           case PR_FE_MODE:
+               dump_last_fe();
+               break;
+           case PR_CB_MODE:
+               dump_last_cb();
+               break;
+           default:
+               fprintf(stderr, "command not valid for this mode\n");
+           }
+       } else if (!strcasecmp(tok, "dump")) {
+           switch(mode) {
+           case PR_H_MODE:
+               dump_all_hes();
+               break;
+           case PR_FE_MODE:
+               dump_all_fes();
+               break;
+           case PR_CB_MODE:
+               dump_all_cbs();
+               break;
+           default:
+               fprintf(stderr, "command not valid for this mode\n");
+           }
+       } else if (!strcasecmp(tok, "find")) {
+           tok = strtok(NULL, " \t");
+           if (!tok || strcasecmp(tok, "by")) {
+               tok = NULL;
+               fprintf(stderr, "find syntax error 1 (%s)\n", 
+                       (tok) ? tok : "nil");
+               continue;
+           }
+           tok = strtok(NULL, " \t");
+           if (!tok) {
+               fprintf(stderr, "find syntax error 2\n");
+               continue;
+           }
+           switch(mode) {
+           case PR_H_MODE:
+               fprintf(stderr, "not implemented yet\n");
+               break;
+           case PR_FE_MODE:
+               if (!strcasecmp(tok, "index")) {
+                   tok = strtok(NULL, " \t");
+                   if (!tok || sscanf(tok, "%u", &x) != 1) {
+                       tok = NULL;
+                       fprintf(stderr, "find syntax error 3\n");
+                       continue;
+                   }
+                   if (find_fe_by_index(x)) {
+                       fprintf(stderr, "find returned no results\n");
+                   }
+               } else if (!strcasecmp(tok, "fid")) {
+                   tok = strtok(NULL, "(), \t");
+                   if (!tok || sscanf(tok, "%u", &x) != 1) {
+                       tok = NULL;
+                       fprintf(stderr, "find syntax error 4\n");
+                       continue;
+                   }
+                   tok = strtok(NULL, "(), \t");
+                   if (!tok || sscanf(tok, "%u", &y) != 1) {
+                       tok = NULL;
+                       fprintf(stderr, "find syntax error 5\n");
+                       continue;
+                   }
+                   tok = strtok(NULL, "(), \t");
+                   if (!tok || sscanf(tok, "%u", &z) != 1) {
+                       tok = NULL;
+                       fprintf(stderr, "find syntax error 6\n");
+                       continue;
+                   }
+                   if (find_fe_by_fid(x,y,z)) {
+                       fprintf(stderr, "find returned no results\n");
+                   }
+               } else {
+                   fprintf(stderr, "unsupported filter type\n");
+               }
+               break;
+           case PR_CB_MODE:
+               if (!strcasecmp(tok, "index")) {
+                   tok = strtok(NULL, " \t");
+                   if (!tok || sscanf(tok, "%u", &x) != 1) {
+                       tok = NULL;
+                       fprintf(stderr, "find syntax error 3\n");
+                       continue;
+                   }
+                   if (find_cb_by_index(x)) {
+                       fprintf(stderr, "find returned no results\n");
+                   }
+               } else {
+                   fprintf(stderr, "unsupported filter type\n");
+               }
+               break;
+           default:
+               fprintf(stderr, "find not supported for this menu\n");
+           }
+       } else if (!strcspn(tok, "0123456789")) {
+           if (sscanf(tok, "%u", &x) == 1) {
+               switch(mode) {
+               case PR_H_MODE:
+                   dump_he(x);
+                   break;
+               case PR_FE_MODE:
+                   dump_fe(x);
+                   break;
+               case PR_CB_MODE:
+                   dump_cb(x);
+                   break;
+               default:
+                   fprintf(stderr, "command not available from this menu\n");
+               }
+           } else {
+               fprintf(stderr, "input parse error ('%s')\n", tok);
+           }
+       } else if (mode == PR_FE_MODE) {
+           if (!strcmp(tok, "timeout")) {
+               dump_cb_timeout();
+           } else if (!strcmp(tok, "hash")) {
+               dump_cb_fehash();
+           }
+       } else {
+           fprintf(stderr, "unknown command\n");
+       }
+       tok = strtok(NULL, " \t");
+       mode = next_mode;
+    }
+}
+
+static void
+print_help(void)
+{
+    printf("\th <...>  -- host menu commands\n");
+    printf("\tfe <...> -- FileEntry menu commands\n");
+    printf("\tcb <...> -- CallBack menu commands\n");
+    printf("\thexdump [<offset> [<len>]]\n\t\t -- hex dump the raw data\n");
+    printf("\tquit     -- quit this menu\n");
+    printf("\texit     -- exit the debugger\n");
+    printf("\thelp     -- this help message\n");
+}
+
+static void
+print_global_help(void)
+{
+    printf("\thdr      -- display the fs_state_header struct\n");
+}
+
+static void
+print_h_help(void)
+{
+    printf("\thdr      -- display the host_state_header struct\n");
+    printf("\tfirst    -- display the first host\n");
+    printf("\tprev     -- display the previous host\n");
+    printf("\tthis     -- display this host\n");
+    printf("\tnext     -- display the next host\n");
+    printf("\tlast     -- display the last host\n");
+    printf("\tdump     -- display all hosts\n");
+}
+
+static void
+print_fe_help(void)
+{
+    printf("\thdr      -- display the callback_state_header struct\n");
+    printf("\tfirst    -- display the first FE\n");
+    printf("\tprev     -- display the previous FE\n");
+    printf("\tthis     -- display this FE\n");
+    printf("\tnext     -- display the next FE\n");
+    printf("\tlast     -- display the last FE\n");
+    printf("\tdump     -- display all FEs\n");
+    printf("\ttimeout  -- display the timeout queue heads\n");
+    printf("\thash   -- display the file entry hash buckets\n");
+    printf("\tfind by index <id>\n\t\t -- find an fe by its array index\n");
+    printf("\tfind by fid <(vol,vnode,unique)>\n\t\t -- find an fe by its AFSFid\n");
+}
+
+static void
+print_cb_help(void)
+{
+    printf("\thdr      -- display the callback_state_entry_header struct\n");
+    printf("\tfirst    -- display the first CB\n");
+    printf("\tprev     -- display the previous CB\n");
+    printf("\tthis     -- display this CB\n");
+    printf("\tnext     -- display the next CB\n");
+    printf("\tlast     -- display the last CB\n");
+    printf("\tdump     -- display all CBs\n");
+}
+
+#define DPFTB0 "\t"
+#define DPFTB1 "\t\t"
+#define DPFTB2 "\t\t\t"
+
+#define DPFOFF(addr) \
+    do { \
+        char * _p = (char *)addr; \
+        char * _m = (char *)map; \
+        printf("loading structure from address 0x%x (offset %u)\n", \
+               addr, _p-_m); \
+    } while (0)
+
+/* structs */
+#define DPFSO(T, name) printf(T "%s = {\n", name)
+#define DPFSO0(name) DPFSO(DPFTB0, name)
+#define DPFSO1(name) DPFSO(DPFTB1, name)
+#define DPFSC(T) printf(T "}\n")
+#define DPFSC0 DPFSC(DPFTB0)
+#define DPFSC1 DPFSC(DPFTB1)
+
+/* arrays */
+#define DPFAO(T1, T2, name) printf(T1 "%s =\n" T2 "{ ", name)
+#define DPFAO0(name) DPFAO(DPFTB0, DPFTB1, name)
+#define DPFAO1(name) DPFAO(DPFTB1, DPFTB2, name)
+#define DPFAC0 printf(" }\n")
+#define DPFAC1 DPFAC0
+#define DPFA1 printf(DPFTB1 "  ")
+#define DPFA2 printf(DPFTB2 "  ")
+#define DPFAN printf("\n")
+#define DPFALE(type, var) printf("%" type, var)
+#define DPFAE(type, var) printf("%" type ",\t", var)
+
+/* normal vars */
+#define DPFV(T, name, type, var) printf(T "%s = %" type "\n", name, var)
+#define DPFV1(name, type, var) DPFV(DPFTB1, name, type, var)
+#define DPFV2(name, type, var) DPFV(DPFTB2, name, type, var)
+
+/* hex */
+#define DPFX(T, name, var) printf(T "%s = 0x%x\n", name, var)
+#define DPFX1(name, var) DPFX(DPFTB1, name, var)
+#define DPFX2(name, var) DPFX(DPFTB2, name, var)
+
+/* strings */
+#define DPFS(T, name, var) printf(T "%s = \"%s\"\n", name, var)
+#define DPFS1(name, var) DPFS(DPFTB1, name, var)
+#define DPFS2(name, var) DPFS(DPFTB2, name, var)
+
+/* time */
+#define DPFT(T, name, var) \
+    do { \
+        char * last; \
+        printf(T "%s = \"%s\"\n", name, strtok_r(ctime(&(var)), "\r\n", &last)); \
+    } while(0)
+#define DPFT1(name, var) DPFT(DPFTB1, name, var)
+#define DPFT2(name, var) DPFT(DPFTB2, name, var)
+
+static void
+dump_hdr(void)
+{
+    char uuid_str[40];
+    afs_uint32 hi, lo;
+
+    if (get_hdr())
+       return;
+
+    DPFOFF(map);
+    DPFSO0("fs_state_header");
+    DPFSO1("stamp");
+    DPFX2("magic", hdrs.hdr.stamp.magic);
+    DPFV2("version", "u", hdrs.hdr.stamp.version);
+    DPFSC1;
+    DPFT1("timestamp", hdrs.hdr.timestamp);
+    DPFV1("sys_name", "u", hdrs.hdr.sys_name);
+
+    afsUUID_to_string(&hdrs.hdr.server_uuid, uuid_str, sizeof(uuid_str));
+    DPFS1("server_uuid", uuid_str);
+    DPFV1("valid", "d", hdrs.hdr.valid);
+    DPFV1("endianness", "d", hdrs.hdr.endianness);
+    DPFV1("stats_detailed", "d", hdrs.hdr.stats_detailed);
+
+    SplitInt64(hdrs.hdr.h_offset, hi, lo);
+    DPFSO1("h_offset");
+    DPFV2("hi", "u", hi);
+    DPFV2("lo", "u", lo);
+    DPFSC1;
+
+    SplitInt64(hdrs.hdr.cb_offset, hi, lo);
+    DPFSO1("cb_offset");
+    DPFV2("hi", "u", hi);
+    DPFV2("lo", "u", lo);
+    DPFSC1;
+
+    DPFS1("server_version_string", hdrs.hdr.server_version_string);
+    DPFSC0;
+
+    if (hdrs.hdr.stamp.magic != FS_STATE_MAGIC) {
+       fprintf(stderr, "* magic check failed\n");
+    }
+    if (hdrs.hdr.stamp.version != FS_STATE_VERSION) {
+       fprintf(stderr, "* version check failed\n");
+    }
+}
+
+static void
+dump_h_hdr(void)
+{
+    if (get_h_hdr())
+       return;
+
+    DPFOFF(hdrs.h_hdr_p);
+    DPFSO0("host_state_header");
+    DPFSO1("stamp");
+    DPFX2("magic", hdrs.h_hdr.stamp.magic);
+    DPFV2("version", "u", hdrs.h_hdr.stamp.version);
+    DPFSC1;
+    DPFV1("records", "u", hdrs.h_hdr.records);
+    DPFV1("index_max", "u", hdrs.h_hdr.index_max);
+    DPFSC0;
+
+    if (hdrs.h_hdr.stamp.magic != HOST_STATE_MAGIC) {
+       fprintf(stderr, "* magic check failed\n");
+    }
+    if (hdrs.h_hdr.stamp.version != HOST_STATE_VERSION) {
+       fprintf(stderr, "* version check failed\n");
+    }
+}
+
+static void
+dump_cb_hdr(void)
+{
+    afs_uint32 hi, lo;
+
+    if (get_cb_hdr())
+       return;
+
+    DPFOFF(hdrs.cb_hdr_p);
+    DPFSO0("callback_state_header");
+    DPFSO1("stamp");
+    DPFX2("magic", hdrs.cb_hdr.stamp.magic);
+    DPFV2("version", "u", hdrs.cb_hdr.stamp.version);
+    DPFSC1;
+    DPFV1("nFEs", "u", hdrs.cb_hdr.nFEs);
+    DPFV1("nCBs", "u", hdrs.cb_hdr.nCBs);
+    DPFV1("fe_max", "u", hdrs.cb_hdr.fe_max);
+    DPFV1("cb_max", "u", hdrs.cb_hdr.cb_max);
+    DPFV1("tfirst", "d", hdrs.cb_hdr.tfirst);
+
+    SplitInt64(hdrs.cb_hdr.timeout_offset, hi, lo);
+    DPFSO1("timeout_offset");
+    DPFV2("hi", "u", hi);
+    DPFV2("lo", "u", lo);
+    DPFSC1;
+
+    SplitInt64(hdrs.cb_hdr.fehash_offset, hi, lo);
+    DPFSO1("fehash_offset");
+    DPFV2("hi", "u", hi);
+    DPFV2("lo", "u", lo);
+    DPFSC1;
+
+    SplitInt64(hdrs.cb_hdr.fe_offset, hi, lo);
+    DPFSO1("fe_offset");
+    DPFV2("hi", "u", hi);
+    DPFV2("lo", "u", lo);
+    DPFSC1;
+
+    DPFSC0;
+
+    if (hdrs.cb_hdr.stamp.magic != CALLBACK_STATE_MAGIC) {
+       fprintf(stderr, "* magic check failed\n");
+    }
+    if (hdrs.cb_hdr.stamp.version != CALLBACK_STATE_VERSION) {
+       fprintf(stderr, "* version check failed\n");
+    }
+}
+
+static void
+dump_cb_timeout(void)
+{
+    int i;
+
+    if (get_cb_hdr())
+       return;
+
+    if (get_cb_timeout_hdr())
+       return;
+
+    if (get_cb_timeout())
+       return;
+
+    DPFOFF(hdrs.timeout_hdr_p);
+    DPFSO0("callback_state_timeout_header");
+    DPFX1("magic", hdrs.timeout_hdr.magic);
+    DPFV1("len", "u", hdrs.timeout_hdr.len);
+    DPFV1("records", "u", hdrs.timeout_hdr.records);
+    DPFSC0;
+
+    if (hdrs.timeout_hdr.magic != CALLBACK_STATE_TIMEOUT_MAGIC) {
+       fprintf(stderr, "* magic check failed\n");
+    }
+
+    DPFOFF(hdrs.timeout_p);
+    DPFAO0("timeout");
+    for (i = 0; i < 127; i++) {
+       DPFAE("u", hdrs.timeout[i]);
+       if ((i % 8) == 7) {
+           DPFAN;
+           DPFA1;
+       }
+    }
+    DPFALE("u", hdrs.timeout[127]);
+    DPFAC0;
+}
+
+static void
+dump_cb_fehash(void)
+{
+    int i;
+
+    if (get_cb_hdr())
+       return;
+
+    if (get_cb_fehash_hdr())
+       return;
+
+    if (get_cb_fehash())
+       return;
+
+    DPFOFF(hdrs.fehash_hdr_p);
+    DPFSO0("callback_state_fehash_header");
+    DPFX1("magic", hdrs.fehash_hdr.magic);
+    DPFV1("len", "u", hdrs.fehash_hdr.len);
+    DPFV1("records", "u", hdrs.fehash_hdr.records);
+    DPFSC0;
+
+    if (hdrs.fehash_hdr.magic != CALLBACK_STATE_FEHASH_MAGIC) {
+       fprintf(stderr, "* magic check failed\n");
+    }
+
+    DPFOFF(hdrs.fehash_p);
+    DPFAO0("fehash");
+    for (i = 0; i < hdrs.fehash_hdr.records - 1; i++) {
+       DPFAE("u", hdrs.fehash[i]);
+       if ((i % 8) == 7) {
+           DPFAN;
+           DPFA1;
+       }
+    }
+    DPFALE("u", hdrs.fehash[hdrs.fehash_hdr.records-1]);
+    DPFAC0;
+}
+
+static void
+dump_all_hes(void)
+{
+    int i;
+
+    if (get_h_hdr()) {
+       fprintf(stderr, "error getting host_state_header\n");
+       return;
+    }
+
+    for (i = 0; i < hdrs.h_hdr.records; i++) {
+       dump_he(i);
+    }
+}
+
+static void
+dump_all_fes(void)
+{
+    int i;
+
+    if (get_cb_hdr()) {
+       fprintf(stderr, "error getting callback_state_header\n");
+       return;
+    }
+
+    for (i = 0; i < hdrs.cb_hdr.nFEs; i++) {
+       dump_fe(i);
+    }
+}
+
+static void
+dump_all_cbs(void)
+{
+    int i;
+
+    if (get_fe_hdr()) {
+       fprintf(stderr, "error getting callback_state_entry_header\n");
+       return;
+    }
+
+    for (i = 0; i < fe_cursor.hdr.nCBs; i++) {
+       dump_cb(i);
+    }
+}
+
+static void
+dump_he(afs_uint32 idx)
+{
+    if (get_he(idx)) {
+       fprintf(stderr, "error getting he %d\n", idx);
+       return;
+    }
+
+    DPFOFF(he_cursor.cursor);
+    dump_he_hdr();
+    dump_he_entry();
+    dump_he_interfaces();
+    dump_he_hcps();
+}
+
+static void
+dump_fe(afs_uint32 idx)
+{
+    if (get_fe(idx)) {
+       fprintf(stderr, "error getting fe %d\n", idx);
+       return;
+    }
+
+    DPFOFF(fe_cursor.cursor);
+    dump_fe_hdr();
+    dump_fe_entry();
+}
+
+static void
+dump_cb(afs_uint32 idx)
+{
+    if (get_cb(idx)) {
+       fprintf(stderr, "error getting cb %d\n", idx);
+       return;
+    }
+
+    DPFOFF(cb_cursor.cursor);
+    dump_cb_entry();
+}
+
+static void
+dump_this_he(void)
+{
+    dump_he(he_cursor.idx);
+}
+
+static void
+dump_this_fe(void)
+{
+    dump_fe(fe_cursor.idx);
+}
+
+static void
+dump_this_cb(void)
+{
+    dump_cb(cb_cursor.idx);
+}
+
+static void
+dump_next_he(void)
+{
+    if (get_h_hdr()) {
+       fprintf(stderr, "error getting host_state_header\n");
+       return;
+    }
+
+    if ((he_cursor.idx + 1) >= hdrs.h_hdr.records) {
+       fprintf(stderr, "no more HEs\n");
+       return;
+    }
+    
+    dump_he(he_cursor.idx+1);
+}
+
+static void
+dump_next_fe(void)
+{
+    if (get_cb_hdr()) {
+       fprintf(stderr, "error getting callback_state_header\n");
+       return;
+    }
+
+    if ((fe_cursor.idx + 1) >= hdrs.cb_hdr.nFEs) {
+       fprintf(stderr, "no more FEs\n");
+       return;
+    }
+    
+    dump_fe(fe_cursor.idx+1);
+}
+
+static void
+dump_next_cb(void)
+{
+    if (get_fe_hdr()) {
+       fprintf(stderr, "error getting callback_state_entry_header\n");
+       return;
+    }
+
+    if ((cb_cursor.idx + 1) >= fe_cursor.hdr.nCBs) {
+       fprintf(stderr, "no more CBs\n");
+       return;
+    }
+    
+    dump_cb(cb_cursor.idx+1);
+}
+
+static void
+dump_prev_he(void)
+{
+    if (!he_cursor.idx) {
+       fprintf(stderr, "no more HEs\n");
+       return;
+    }
+    
+    dump_he(he_cursor.idx-1);
+}
+
+static void
+dump_prev_fe(void)
+{
+    if (!fe_cursor.idx) {
+       fprintf(stderr, "no more FEs\n");
+       return;
+    }
+    
+    dump_fe(fe_cursor.idx-1);
+}
+
+static void
+dump_prev_cb(void)
+{
+    if (!cb_cursor.idx) {
+       fprintf(stderr, "no more CBs\n");
+       return;
+    }
+    
+    dump_cb(cb_cursor.idx-1);
+}
+
+static void
+dump_first_fe(void)
+{
+    if (get_cb_hdr()) {
+       fprintf(stderr, "error getting callback_state_header\n");
+       return;
+    }
+
+    if (!hdrs.cb_hdr.nFEs) {
+       fprintf(stderr, "no FEs present\n");
+       return;
+    }
+    
+    dump_fe(0);
+}
+
+static void
+dump_first_he(void)
+{
+    if (get_h_hdr()) {
+       fprintf(stderr, "error getting host_state_header\n");
+       return;
+    }
+
+    if (!hdrs.h_hdr.records) {
+       fprintf(stderr, "no HEs present\n");
+       return;
+    }
+    
+    dump_he(0);
+}
+
+static void
+dump_first_cb(void)
+{
+    if (get_fe_hdr()) {
+       fprintf(stderr, "error getting callback_state_entry_header\n");
+       return;
+    }
+
+    if (!fe_cursor.hdr.nCBs) {
+       fprintf(stderr, "no CBs present\n");
+       return;
+    }
+    
+    dump_cb(0);
+}
+
+static void
+dump_last_he(void)
+{
+    if (get_h_hdr()) {
+       fprintf(stderr, "error getting host_state_header\n");
+       return;
+    }
+
+    if (!hdrs.h_hdr.records) {
+       fprintf(stderr, "no HEs present\n");
+       return;
+    }
+    
+    dump_he(hdrs.h_hdr.records-1);
+}
+
+static void
+dump_last_fe(void)
+{
+    if (get_cb_hdr()) {
+       fprintf(stderr, "error getting callback_state_header\n");
+       return;
+    }
+
+    if (!hdrs.cb_hdr.nFEs) {
+       fprintf(stderr, "no FEs present\n");
+       return;
+    }
+    
+    dump_fe(hdrs.cb_hdr.nFEs-1);
+}
+
+static void
+dump_last_cb(void)
+{
+    if (get_fe_hdr()) {
+       fprintf(stderr, "error getting callback_state_entry_header\n");
+       return;
+    }
+
+    if (!fe_cursor.hdr.nCBs) {
+       fprintf(stderr, "no CBs present\n");
+       return;
+    }
+
+    dump_cb(fe_cursor.hdr.nCBs-1);
+}
+
+static void
+dump_he_hdr(void)
+{
+    DPFSO0("host_state_entry_header");
+    DPFX1("magic", he_cursor.hdr.magic);
+    DPFV1("len", "u", he_cursor.hdr.len);
+    DPFV1("interfaces", "u", he_cursor.hdr.interfaces);
+    DPFV1("hcps", "u", he_cursor.hdr.hcps);
+    DPFSC0;
+
+    if (he_cursor.hdr.magic != HOST_STATE_ENTRY_MAGIC) {
+       fprintf(stderr, "* magic check failed\n");
+    }
+}
+
+static void
+dump_he_entry(void)
+{
+    DPFSO0("hostDiskEntry");
+    DPFS1("host", afs_inet_ntoa(he_cursor.he.host));
+    DPFV1("port", "u", he_cursor.he.port);
+    DPFX1("hostFlags", he_cursor.he.hostFlags);
+    DPFV1("Console", "u", he_cursor.he.Console);
+    DPFV1("hcpsfailed", "u", he_cursor.he.hcpsfailed);
+    DPFV1("hcps_valid", "u", he_cursor.he.hcps_valid);
+    if (hdrs.hdr.stats_detailed) {
+#ifdef FS_STATS_DETAILED
+       DPFV1("InSameNetwork", "u", he_cursor.he.InSameNetwork);
+#else
+       DPFV1("InSameNetwork", "u", he_cursor.he.padding1[0]);
+#endif
+    }
+    DPFV1("hcps_len", "u", he_cursor.he.hcps_len);
+    DPFT1("LastCall", he_cursor.he.LastCall);
+    DPFT1("ActiveCall", he_cursor.he.ActiveCall);
+    DPFT1("cpsCall", he_cursor.he.cpsCall);
+    DPFV1("cblist", "u", he_cursor.he.cblist);
+    DPFV1("index", "u", he_cursor.he.index);
+    DPFSC0;
+}
+
+static void
+dump_he_interfaces(void)
+{
+    char temp_str[40];
+    struct Interface * ifp;
+    int len, i;
+
+    if (!he_cursor.hdr.interfaces)
+       return;
+
+    len = sizeof(struct Interface) + ((he_cursor.hdr.interfaces-1)*sizeof(struct AddrPort));
+    ifp = (struct Interface *) malloc(len);
+    assert(ifp != NULL);
+
+    memcpy(ifp, he_cursor.ifp, len);
+
+    DPFSO0("Interface");
+    DPFV1("numberOfInterfaces", "u", ifp->numberOfInterfaces);
+
+    afsUUID_to_string(&ifp->uuid, temp_str, sizeof(temp_str));
+    DPFS1("uuid", temp_str);
+    for (i = 0; i < he_cursor.hdr.interfaces; i++) {
+       snprintf(temp_str, sizeof(temp_str), "interface[%d]", i);
+       DPFSO1(temp_str);
+       DPFS2("addr", afs_inet_ntoa(ifp->interface[i].addr));
+       DPFV2("port", "u", ifp->interface[i].port);
+       DPFSC1;
+    }
+
+    DPFSC0;
+
+    if (he_cursor.hdr.interfaces != ifp->numberOfInterfaces) {
+       fprintf(stderr, "* interface count mismatch between header and Interface struct\n");
+    }
+    free(ifp);
+}
+
+static void
+dump_he_hcps(void)
+{
+    char temp_str[40];
+    afs_int32 * hcps;
+    int len, i;
+
+    if (!he_cursor.hdr.hcps)
+       return;
+
+    len = (he_cursor.hdr.hcps)*sizeof(afs_uint32);
+    hcps = (afs_int32 *) malloc(len);
+    assert(hcps != NULL);
+    memcpy(hcps, he_cursor.hcps, len);
+
+    DPFSO0("hcps");
+    DPFAO1("prlist_val");
+    for (i = 0; i < he_cursor.hdr.hcps - 1; i++) {
+       DPFAE("d", hcps[i]);
+       if ((i % 8) == 7) {
+           DPFAN;
+           DPFA2;
+       }
+    }
+    DPFALE("d", hcps[he_cursor.hdr.hcps-1]);
+    DPFAC1;
+    DPFSC0;
+    free(hcps);
+}
+
+static void
+dump_fe_hdr(void)
+{
+    DPFSO0("callback_state_entry_header");
+    DPFX1("magic", fe_cursor.hdr.magic);
+    DPFV1("len", "u", fe_cursor.hdr.len);
+    DPFV1("nCBs", "u", fe_cursor.hdr.nCBs);
+    DPFSC0;
+
+    if (fe_cursor.hdr.magic != CALLBACK_STATE_ENTRY_MAGIC) {
+       fprintf(stderr, "* magic check failed\n");
+    }
+}
+
+static void
+dump_fe_entry(void)
+{
+    DPFSO0("FEDiskEntry");
+    DPFSO1("fe");
+    DPFV2("vnode", "u", fe_cursor.fe.fe.vnode);
+    DPFV2("unique", "u", fe_cursor.fe.fe.unique);
+    DPFV2("volid", "u", fe_cursor.fe.fe.volid);
+    DPFV2("fnext", "u", fe_cursor.fe.fe.fnext);
+    DPFV2("ncbs", "u", fe_cursor.fe.fe.ncbs);
+    DPFV2("firstcb", "u", fe_cursor.fe.fe.firstcb);
+    DPFV2("status", "u", fe_cursor.fe.fe.status);
+    DPFSC1;
+    DPFV1("index", "u", fe_cursor.fe.index);
+    DPFSC0;
+}
+
+static void
+dump_cb_entry(void)
+{
+    DPFSO0("CBDiskEntry");
+    DPFSO1("cb");
+    DPFV2("cnext", "u", cb_cursor.cb.cb.cnext);
+    DPFV2("fhead", "u", cb_cursor.cb.cb.fhead);
+    DPFV2("thead", "u", (afs_uint32)cb_cursor.cb.cb.thead);
+    DPFV2("status", "u", (afs_uint32)cb_cursor.cb.cb.status);
+    DPFV2("hhead", "u", cb_cursor.cb.cb.hhead);
+    DPFV2("tprev", "u", cb_cursor.cb.cb.tprev);
+    DPFV2("tnext", "u", cb_cursor.cb.cb.tnext);
+    DPFV2("hprev", "u", cb_cursor.cb.cb.hprev);
+    DPFV2("hnext", "u", cb_cursor.cb.cb.hnext);
+    DPFSC1;
+    DPFV1("index", "u", cb_cursor.cb.index);
+    DPFSC0;
+}
+
+#define DPFHMS printf("  ")
+#define DPFHS printf("    ")
+#define DPFHN(offset) printf("\n%u\t", offset)
+#define DPFHD(x) printf("%02X  ", x)
+#define DPFHE printf("\n")
+
+static void
+hexdump_map(afs_uint32 offset, afs_uint32 len)
+{
+    int i;
+    unsigned char * p = (unsigned char *)map;
+    afs_uint32 c32;
+
+    if (!len)
+       return;
+
+    if ((offset + len) > map_len) {
+       fprintf(stderr, "offset + length exceeds memory map size (%u > %u)\n",
+               offset+len, map_len);
+       return;
+    }
+
+    p += offset;
+    DPFOFF(p);
+    DPFHN(offset);
+
+    for (i = offset % 16; i > 0; i--) {
+       DPFHS;
+    }
+
+    for (i=0; i < len; i++, p++, offset++) {
+       if (!(offset % 16)) {
+           DPFHN(offset);
+       } else if (!(offset % 8)) {
+           DPFHMS;
+       }
+       DPFHD(*p);
+    }
+    DPFHE;
+}
+
+static int
+get_hdr(void)
+{
+    if (!hdrs.hdr_valid) {
+       if (map_len < sizeof(struct fs_state_header)) {
+           fprintf(stderr, "corrupt state dump: fs_state_header larger than memory map\n");
+           return 1;
+       }
+       memcpy(&hdrs.hdr, map, sizeof(hdrs.hdr));
+       hdrs.hdr_p = map;
+       hdrs.hdr_valid = 1;
+    }
+    return 0;
+}
+
+static int
+get_h_hdr(void)
+{
+    char * buf;
+    afs_uint32 hi, lo;
+
+    if (hdrs.h_hdr_valid)
+       return 0;
+
+    if (get_hdr())
+       return 1;
+
+    SplitInt64(hdrs.hdr.h_offset, hi, lo);
+
+    if (hi) {
+       fprintf(stderr, "hi offset bits set in h_offset; can't get host_state_header\n");
+       return 1;
+    }
+    if ((lo >= map_len) || 
+       ((lo + sizeof(struct host_state_header)) > map_len) ||
+       (lo + sizeof(struct host_state_header) < lo)) {
+       fprintf(stderr, "h_offset puts host_state_header beyond end of memory map\n");
+       return 1;
+    }
+
+    buf = (char *) map;
+    buf += lo;
+    memcpy(&hdrs.h_hdr, buf, sizeof(struct host_state_header));
+    hdrs.h_hdr_p = buf;
+    buf += sizeof(struct host_state_header);
+    he_cursor.fh = (void *)buf;
+    return 0;
+}
+
+static int
+get_cb_hdr(void)
+{
+    char * buf;
+    afs_uint32 hi, lo;
+
+    if (hdrs.cb_hdr_valid)
+       return 0;
+
+    if (get_hdr())
+       return 1;
+
+    SplitInt64(hdrs.hdr.cb_offset, hi, lo);
+
+    if (hi) {
+       fprintf(stderr, "hi offset bits set in cb_offset; can't get callback_state_header\n");
+       return 1;
+    }
+    if ((lo >= map_len) || 
+       ((lo + sizeof(struct callback_state_header)) > map_len) ||
+       (lo + sizeof(struct callback_state_header) < lo)) {
+       fprintf(stderr, "cb_offset puts callback_state_header beyond end of memory map\n");
+       return 1;
+    }
+
+    buf = (char *) map;
+    buf += lo;
+    memcpy(&hdrs.cb_hdr, buf, sizeof(struct callback_state_header));
+    hdrs.cb_hdr_p = buf;
+    hdrs.cb_hdr_valid = 1;
+
+    SplitInt64(hdrs.cb_hdr.fe_offset, hi, lo);
+
+    if (hi) {
+       fprintf(stderr, "hi offset bits set in fe_offset; can't get callback_state_entry_header\n");
+       return 1;
+    }
+    hi = lo + (hdrs.cb_hdr.nFEs * (sizeof(struct callback_state_entry_header) +
+                                 sizeof(struct FEDiskEntry)) +
+              hdrs.cb_hdr.nCBs * sizeof(struct CBDiskEntry));
+    if ((hi > map_len) ||
+       (lo > hi)) {
+       fprintf(stderr, "fe_offset puts callback_state_entry_header beyond end of memory map\n");
+       return 1;
+    }
+
+    buf = (char *) map;
+    buf += lo;
+    fe_cursor.ffe = (void *)buf;
+
+    return 0;
+}
+
+static int
+get_cb_timeout_hdr(void)
+{
+    char * buf;
+    afs_uint32 hi, lo;
+
+    if (hdrs.timeout_hdr_valid)
+       return 0;
+
+    if (get_cb_hdr())
+       return 1;
+
+    SplitInt64(hdrs.cb_hdr.timeout_offset, hi, lo);
+
+    if (hi) {
+       fprintf(stderr, "hi offset bits set in timeout_offset; can't get callback_state_timeout_header\n");
+       return 1;
+    }
+    if ((lo >= map_len) || 
+       ((lo + sizeof(struct callback_state_timeout_header)) > map_len) ||
+       (lo + sizeof(struct callback_state_timeout_header) < lo)) {
+       fprintf(stderr, "timeout_offset puts callback_state_timeout_header beyond end of memory map\n");
+       return 1;
+    }
+
+    buf = (char *) map;
+    buf += lo;
+    memcpy(&hdrs.timeout_hdr, buf, sizeof(struct callback_state_timeout_header));
+    hdrs.timeout_hdr_p = buf;
+    hdrs.timeout_hdr_valid = 1;
+    buf += sizeof(struct callback_state_timeout_header);
+    hdrs.timeout_p = buf;
+
+    return 0;
+}
+
+static int
+get_cb_timeout(void)
+{
+    char * buf;
+
+    if (hdrs.timeout)
+       return 0;
+
+    if (get_cb_timeout_hdr())
+       return 1;
+
+    hdrs.timeout = (afs_uint32 *) calloc(hdrs.timeout_hdr.records, sizeof(afs_uint32));
+    assert(hdrs.timeout != NULL);
+    memcpy(hdrs.timeout, hdrs.timeout_p, hdrs.timeout_hdr.records * sizeof(afs_uint32));
+    return 0;
+}
+
+static int
+get_cb_fehash_hdr(void)
+{
+    char * buf;
+    afs_uint32 hi, lo;
+
+    if (hdrs.fehash_hdr_valid)
+       return 0;
+
+    if (get_cb_hdr())
+       return 1;
+
+    SplitInt64(hdrs.cb_hdr.fehash_offset, hi, lo);
+
+    if (hi) {
+       fprintf(stderr, "hi offset bits set in fehash_offset; can't get callback_state_fehash_header\n");
+       return 1;
+    }
+    if ((lo >= map_len) || 
+       ((lo + sizeof(struct callback_state_fehash_header)) > map_len) ||
+       (lo + sizeof(struct callback_state_fehash_header) < lo)) {
+       fprintf(stderr, "timeout_offset puts callback_state_fehash_header beyond end of memory map\n");
+       return 1;
+    }
+
+    buf = (char *) map;
+    buf += lo;
+    memcpy(&hdrs.fehash_hdr, buf, sizeof(struct callback_state_fehash_header));
+    hdrs.fehash_hdr_p = buf;
+    hdrs.fehash_hdr_valid = 1;
+    buf += sizeof(struct callback_state_fehash_header);
+    hdrs.fehash_p = buf;
+
+    return 0;
+}
+
+static int
+get_cb_fehash(void)
+{
+    char * buf;
+
+    if (hdrs.fehash)
+       return 0;
+
+    if (get_cb_fehash_hdr())
+       return 1;
+
+    hdrs.fehash = (afs_uint32 *) calloc(hdrs.fehash_hdr.records, sizeof(afs_uint32));
+    assert(hdrs.fehash != NULL);
+    memcpy(hdrs.fehash, hdrs.fehash_p, hdrs.fehash_hdr.records * sizeof(afs_uint32));
+    return 0;
+}
+
+static int
+get_he(afs_uint32 idx)
+{
+    int i;
+    char * p;
+
+    if (get_h_hdr())
+       return 1;
+
+    if (idx >= hdrs.h_hdr.records)
+       return 1;
+
+    if (he_cursor.idx == idx && he_cursor.hdr_valid && he_cursor.he_valid)
+       return 0;
+
+    he_cursor.hdr_valid = he_cursor.he_valid = 0;
+
+    if (he_cache.cursor == NULL) {
+       he_cache.cursor = (void **) calloc(hdrs.h_hdr.records, sizeof(void *));
+       assert(he_cache.cursor != NULL);
+    }
+
+    if (idx && he_cache.cursor[idx-1] == NULL) {
+       for (i = 0; i < idx; i++) {
+           if (he_cache.cursor[i] == NULL) {
+               get_he(i);
+           }
+       }
+    }
+
+    if (!idx) {
+       he_cursor.cursor = he_cursor.fh;
+    } else if (he_cursor.cursor == he_cache.cursor[idx-1]) {
+       p = (char *)he_cursor.cursor;
+       p += he_cursor.hdr.len;
+       he_cursor.cursor = (void *)p;
+    } else {
+       he_cursor.cursor = he_cache.cursor[idx-1];
+       if (get_he_hdr())
+           return 1;
+       p = (char *)he_cursor.cursor;
+       p += he_cursor.hdr.len;
+       he_cursor.cursor = (void *)p;
+    }
+
+    he_cursor.idx = idx;
+    he_cache.cursor[idx] = he_cursor.cursor;
+
+    if (get_he_hdr())
+       return 1;
+    if (get_he_entry())
+       return 1;
+
+    return 0;
+}
+
+static int
+get_he_hdr(void)
+{
+    memcpy(&he_cursor.hdr, he_cursor.cursor, sizeof(struct host_state_entry_header));
+    he_cursor.hdr_valid = 1;
+    return 0;
+}
+
+static int
+get_he_entry(void)
+{
+    char * p;
+
+    if (!he_cursor.hdr_valid) {
+       if (get_he_hdr()) {
+           return 1;
+       }
+    }
+
+    p = (char *) he_cursor.cursor;
+    p += sizeof(struct host_state_entry_header);
+
+    memcpy(&he_cursor.he, p, sizeof(struct hostDiskEntry));
+
+    he_cursor.he_valid = 1;
+    p += sizeof(struct hostDiskEntry);
+    he_cursor.ifp = (void *)p;
+    if (he_cursor.hdr.interfaces) {
+       p += sizeof(struct Interface) + ((he_cursor.hdr.interfaces-1)*sizeof(struct AddrPort));
+       he_cursor.hcps = (void *)p;
+    } else {
+       he_cursor.hcps = he_cursor.ifp;
+    }
+    return 0;
+}
+
+static int
+get_fe(afs_uint32 idx)
+{
+    int i;
+    char * p;
+
+    cb_cursor.cb_valid = 0;
+
+    if (get_cb_hdr())
+       return 1;
+
+    if (idx >= hdrs.cb_hdr.nFEs)
+       return 1;
+
+    if (fe_cursor.idx == idx && fe_cursor.hdr_valid && fe_cursor.fe_valid)
+       return 0;
+
+    fe_cursor.hdr_valid = fe_cursor.fe_valid = 0;
+
+    if (fe_cache.cursor == NULL) {
+       fe_cache.cursor = (void **) calloc(hdrs.cb_hdr.nFEs, sizeof(void *));
+       assert(fe_cache.cursor != NULL);
+    }
+
+    if (idx && fe_cache.cursor[idx-1] == NULL) {
+       for (i = 0; i < idx; i++) {
+           if (fe_cache.cursor[i] == NULL) {
+               get_fe(i);
+           }
+       }
+    }
+
+    if (!idx) {
+       fe_cursor.cursor = fe_cursor.ffe;
+    } else if (fe_cursor.cursor == fe_cache.cursor[idx-1]) {
+       p = (char *)fe_cursor.cursor;
+       p += fe_cursor.hdr.len;
+       fe_cursor.cursor = (void *)p;
+    } else {
+       fe_cursor.cursor = fe_cache.cursor[idx-1];
+       if (get_fe_hdr())
+           return 1;
+       p = (char *)fe_cursor.cursor;
+       p += fe_cursor.hdr.len;
+       fe_cursor.cursor = (void *)p;
+    }
+
+    fe_cursor.idx = idx;
+    fe_cache.cursor[idx] = fe_cursor.cursor;
+
+    if (get_fe_hdr())
+       return 1;
+    if (get_fe_entry())
+       return 1;
+
+    return 0;
+}
+
+static int
+get_fe_hdr(void)
+{
+    memcpy(&fe_cursor.hdr, fe_cursor.cursor, sizeof(struct callback_state_entry_header));
+    fe_cursor.hdr_valid = 1;
+    return 0;
+}
+
+static int
+get_fe_entry(void)
+{
+    char * p;
+
+    if (!fe_cursor.hdr_valid) {
+       if (get_fe_hdr()) {
+           return 1;
+       }
+    }
+
+    p = (char *) fe_cursor.cursor;
+    p += sizeof(struct callback_state_entry_header);
+
+    memcpy(&fe_cursor.fe, p, sizeof(struct FEDiskEntry));
+
+    fe_cursor.fe_valid = 1;
+    p += sizeof(struct FEDiskEntry);
+    fe_cursor.fcb = (void *)p;
+    return 0;
+}
+
+static int
+get_cb(afs_uint32 idx)
+{
+    int i;
+    char * p;
+
+    if (get_fe(fe_cursor.idx))
+       return 1;
+
+    if (idx >= fe_cursor.hdr.nCBs)
+       return 1;
+
+    if (idx == cb_cursor.idx && cb_cursor.cb_valid)
+       return 0;
+
+    cb_cursor.cb_valid = 0;
+
+    p = (char *)fe_cursor.fcb;
+    p += idx * sizeof(struct CBDiskEntry);
+    cb_cursor.cursor = (void *)p;
+
+    cb_cursor.idx = idx;
+
+    if (get_cb_entry())
+       return 1;
+
+    return 0;
+}
+
+static int
+get_cb_entry(void)
+{
+    memcpy(&cb_cursor.cb, cb_cursor.cursor, sizeof(struct CBDiskEntry));
+    cb_cursor.cb_valid = 1;
+    return 0;
+}
+
+static int
+find_he_by_index(afs_uint32 idx)
+{
+    int i;
+
+    if (get_h_hdr()) {
+       return 1;
+    }
+
+    for (i = 0; i < hdrs.h_hdr.records; i++) {
+       if (get_he(i)) {
+           fprintf(stderr, "error getting he %d\n", i);
+           return 1;
+       }
+       if (he_cursor.he.index == idx)
+           break;
+    }
+
+    if (i < hdrs.h_hdr.records) {
+       dump_this_he();
+       return 0;
+    }
+    return 1;
+}
+
+static int
+find_fe_by_index(afs_uint32 idx)
+{
+    int i;
+
+    if (get_cb_hdr()) {
+       return 1;
+    }
+
+    for (i = 0; i < hdrs.cb_hdr.nFEs; i++) {
+       if (get_fe(i)) {
+           fprintf(stderr, "error getting fe %d\n", i);
+           return 1;
+       }
+       if (fe_cursor.fe.index == idx)
+           break;
+    }
+
+    if (i < hdrs.cb_hdr.nFEs) {
+       dump_this_fe();
+       return 0;
+    }
+    return 1;
+}
+
+static int
+find_fe_by_fid(afs_uint32 volid, afs_uint32 vnode, afs_uint32 unique)
+{
+    int i;
+
+    if (get_cb_hdr()) {
+       return 1;
+    }
+
+    for (i = 0; i < hdrs.cb_hdr.nFEs; i++) {
+       if (get_fe(i)) {
+           fprintf(stderr, "error getting fe %d\n", i);
+           return 1;
+       }
+       if ((fe_cursor.fe.fe.unique == unique) &&
+           (fe_cursor.fe.fe.volid == volid) &&
+           (fe_cursor.fe.fe.vnode == vnode))
+           break;
+    }
+
+    if (i < hdrs.cb_hdr.nFEs) {
+       dump_this_fe();
+       return 0;
+    }
+    return 1;
+}
+
+static int
+find_cb_by_index(afs_uint32 idx)
+{
+    int i;
+
+    if (get_fe_hdr()) {
+       return 1;
+    }
+
+    for (i = 0; i < fe_cursor.hdr.nCBs; i++) {
+       if (get_cb(i)) {
+           fprintf(stderr, "error getting cb %d\n", i);
+           return 1;
+       }
+       if (cb_cursor.cb.index == idx)
+           break;
+    }
+
+    if (i < fe_cursor.hdr.nCBs) {
+       dump_this_cb();
+       return 0;
+    }
+    return 1;
+}
+
+#endif /* AFS_DEMAND_ATTACH_FS */
index 8b8b1a7..bfeb3a2 100644 (file)
@@ -10,7 +10,7 @@ include @TOP_OBJDIR@/src/config/Makefile.config
 HELPER_SPLINT=@HELPER_SPLINT@
 
 CC=${MT_CC}
-CFLAGS=${COMMON_CFLAGS} -I.. -DNINTERFACE ${MT_CFLAGS} -DRXDEBUG
+CFLAGS=${COMMON_CFLAGS} -I.. -DNINTERFACE ${MT_CFLAGS} -DRXDEBUG -DFSSYNC_BUILD_CLIENT
 
 CCRULE=${CC} ${CFLAGS} -c $?
 
@@ -36,8 +36,9 @@ UTILOBJS=assert.o uuid.o serverLog.o fileutil.o netutils.o dirpath.o volparse.o
 
 DIROBJS=buffer.o dir.o salvage.o
 
-VOLOBJS= vnode.o volume.o vutil.o partition.o fssync.o purge.o \
-        clone.o devname.o common.o ihandle.o listinodes.o namei_ops.o nuke.o
+VOLOBJS= vnode.o volume.o vutil.o partition.o fssync-client.o purge.o \
+        clone.o devname.o common.o ihandle.o listinodes.o \
+        namei_ops.o nuke.o salvsync-client.o daemon_com.o
 
 FSINTOBJS=# afsaux.o afscbint.cs.o afsint.ss.o afsint.xdr.o
 
@@ -138,7 +139,13 @@ partition.o: ${VOL}/partition.c
 nuke.o: ${VOL}/nuke.c
        ${COMPILE}
 
-fssync.o: ${VOL}/fssync.c
+fssync-client.o: ${VOL}/fssync-client.c
+       ${COMPILE}
+
+salvsync-client.o: ${VOL}/salvsync-client.c
+       ${COMPILE}
+
+daemon_com.o: ${VOL}/daemon_com.c
        ${COMPILE}
 
 purge.o: ${VOL}/purge.c
index 7b8c36e..ccf3446 100644 (file)
@@ -13,7 +13,7 @@ HELPER_SPLINT=@HELPER_SPLINT@
 objects = assert.o base64.o casestrcpy.o ktime.o volparse.o hostparse.o \
         hputil.o kreltime.o isathing.o get_krbrlm.o uuid.o serverLog.o \
         dirpath.o fileutil.o netutils.o flipbase64.o fstab.o \
-        afs_atomlist.o afs_lhash.o snprintf.o strlcat.o strlcpy.o \
+        afs_atomlist.o afs_lhash.o snprintf.o strlcat.o strlcpy.o strnlen.o \
         daemon.o rxkstats.o ${REGEX_OBJ}
 
 includes = \
@@ -134,6 +134,9 @@ strlcat.o: ${srcdir}/strlcat.c ${includes}
 strlcpy.o: ${srcdir}/strlcpy.c ${includes}
        ${CCOBJ} ${CFLAGS} -c ${srcdir}/strlcpy.c
 
+strnlen.o: ${srcdir}/strnlen.c ${includes}
+       ${CCOBJ} ${CFLAGS} -c ${srcdir}/strnlen.c
+
 daemon.o: ${srcdir}/daemon.c ${includes}
        ${CCOBJ} ${CFLAGS} -c ${srcdir}/daemon.c
 
index 89f0536..2848da3 100644 (file)
@@ -173,6 +173,9 @@ extern size_t strlcpy(char *dst, const char *src, size_t siz);
 extern size_t strlcat(char *dst, const char *src, size_t siz);
 #endif
 
+/* strn */
+extern size_t afs_strnlen(char * buf, size_t len);
+
 
 /* sys.c */
 
@@ -184,6 +187,10 @@ extern void afs_htonuuid(afsUUID * uuidp);
 extern void afs_ntohuuid(afsUUID * uuidp);
 extern afs_int32 afs_uuid_create(afsUUID * uuid);
 extern u_short afs_uuid_hash(afsUUID * uuid);
+#if !defined(KERNEL) && !defined(UKERNEL)
+extern int afsUUID_from_string(const char *str, afsUUID * uuid);
+extern int afsUUID_to_string(const afsUUID * uuid, char *str, size_t strsz);
+#endif
 
 /* volparse.c */
 extern afs_int32 volutil_GetPartitionID(char *aname);
index ff856f9..1e9d78d 100644 (file)
@@ -292,10 +292,17 @@ initDirPathArray(void)
     pathp = dirPathArray[AFSDIR_SERVER_SLVGLOG_FILEPATH_ID];
     AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_LOGS_DIR, AFSDIR_SLVGLOG_FILE);
 
+    pathp = dirPathArray[AFSDIR_SERVER_SALSRVLOG_FILEPATH_ID];
+    AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_LOGS_DIR, AFSDIR_SALSRVLOG_FILE);
+
     pathp = dirPathArray[AFSDIR_SERVER_SALVAGER_FILEPATH_ID];
     AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_SERVER_BIN_DIR,
                           AFSDIR_SALVAGER_FILE);
 
+    pathp = dirPathArray[AFSDIR_SERVER_SALSRV_FILEPATH_ID];
+    AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_SERVER_BIN_DIR,
+                          AFSDIR_SALSRV_FILE);
+
     pathp = dirPathArray[AFSDIR_SERVER_SLVGLOCK_FILEPATH_ID];
     AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_LOCAL_DIR, AFSDIR_SLVGLOCK_FILE);
 
@@ -368,6 +375,9 @@ initDirPathArray(void)
     pathp = dirPathArray[AFSDIR_SERVER_KRB_EXCL_FILEPATH_ID];
     AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_SERVER_ETC_DIR, AFSDIR_KRB_EXCL_FILE);
 
+    pathp = dirPathArray[AFSDIR_SERVER_FSSTATE_FILEPATH_ID];
+    AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_LOCAL_DIR, AFSDIR_FSSTATE_FILE);
+
     /* client file paths */
 #ifdef AFS_NT40_ENV
     strcpy(dirPathArray[AFSDIR_CLIENT_THISCELL_FILEPATH_ID],
index 23590ad..ae1c46a 100644 (file)
@@ -135,7 +135,9 @@ ConstructLocalLogPath(const char *cpath,
 #define AFSDIR_VLOG_FILE        "VLLog"
 #define AFSDIR_CORE_FILE        "core"
 #define AFSDIR_SLVGLOG_FILE     "SalvageLog"
+#define AFSDIR_SALSRVLOG_FILE   "SalsrvLog"
 #define AFSDIR_SALVAGER_FILE    "salvager"
+#define AFSDIR_SALSRV_FILE      "salvageserver"
 #define AFSDIR_SLVGLOCK_FILE    "salvage.lock"
 #define AFSDIR_BOZCONF_FILE     "BosConfig"
 #define AFSDIR_BOZCONFNEW_FILE  "BosConfig.new"
@@ -155,6 +157,8 @@ ConstructLocalLogPath(const char *cpath,
 #define AFSDIR_FILELOG_FILE     "FileLog"
 #define AFSDIR_MIGRATE_LOGNAME  "wtlog."
 
+#define AFSDIR_FSSTATE_FILE     "fsstate.dat"
+
 #define AFSDIR_CELLSERVDB_FILE_NTCLIENT  "afsdcell.ini"
 
 #define AFSDIR_NETINFO_FILE     "NetInfo"
@@ -194,9 +198,15 @@ AFSDIR_CANONICAL_SERVER_AFS_DIRPATH "/local"
 #define AFSDIR_CANONICAL_SERVER_SALVAGER_FILEPATH \
 AFSDIR_CANONICAL_SERVER_BIN_DIRPATH "/" AFSDIR_SALVAGER_FILE
 
+#define AFSDIR_CANONICAL_SERVER_SALSRV_FILEPATH \
+AFSDIR_CANONICAL_SERVER_BIN_DIRPATH "/" AFSDIR_SALSRV_FILE
+
 #define AFSDIR_CANONICAL_SERVER_SLVGLOG_FILEPATH \
 AFSDIR_CANONICAL_SERVER_LOGS_DIRPATH "/" AFSDIR_SLVGLOG_FILE
 
+#define AFSDIR_CANONICAL_SERVER_SALSRVLOG_FILEPATH \
+AFSDIR_CANONICAL_SERVER_LOGS_DIRPATH "/" AFSDIR_SALSRVLOG_FILE
+
 
 /* ---------------------  Local path macros ---------------------- */
 
@@ -264,6 +274,9 @@ typedef enum afsdir_id {
       AFSDIR_SERVER_BIN_FILE_DIRPATH_ID,
       AFSDIR_CLIENT_CELLALIAS_FILEPATH_ID,
       AFSDIR_SERVER_KRB_EXCL_FILEPATH_ID,
+      AFSDIR_SERVER_SALSRV_FILEPATH_ID,
+      AFSDIR_SERVER_SALSRVLOG_FILEPATH_ID,
+      AFSDIR_SERVER_FSSTATE_FILEPATH_ID,
       AFSDIR_PATHSTRING_MAX } afsdir_id_t;
 
 /* getDirPath() returns a pointer to a string from an internal array of path strings 
@@ -310,7 +323,9 @@ const char *getDirPath(afsdir_id_t string_id);
 #define AFSDIR_SERVER_VLOG_FILEPATH getDirPath(AFSDIR_SERVER_VLOG_FILEPATH_ID)
 #define AFSDIR_SERVER_CORELOG_FILEPATH getDirPath(AFSDIR_SERVER_CORELOG_FILEPATH_ID)
 #define AFSDIR_SERVER_SLVGLOG_FILEPATH getDirPath(AFSDIR_SERVER_SLVGLOG_FILEPATH_ID)
+#define AFSDIR_SERVER_SALSRVLOG_FILEPATH getDirPath(AFSDIR_SERVER_SALSRVLOG_FILEPATH_ID)
 #define AFSDIR_SERVER_SALVAGER_FILEPATH getDirPath(AFSDIR_SERVER_SALVAGER_FILEPATH_ID)
+#define AFSDIR_SERVER_SALSRV_FILEPATH getDirPath(AFSDIR_SERVER_SALSRV_FILEPATH_ID)
 #define AFSDIR_SERVER_BOZCONF_FILEPATH getDirPath(AFSDIR_SERVER_BOZCONF_FILEPATH_ID)
 #define AFSDIR_SERVER_BOZCONFNEW_FILEPATH getDirPath(AFSDIR_SERVER_BOZCONFNEW_FILEPATH_ID)
 #define AFSDIR_SERVER_BOZINIT_FILEPATH getDirPath(AFSDIR_SERVER_BOZINIT_FILEPATH_ID)
@@ -332,6 +347,7 @@ const char *getDirPath(afsdir_id_t string_id);
 #define AFSDIR_SERVER_THRESHOLD_CONSTANTS_FILEPATH getDirPath(AFSDIR_SERVER_THRESHOLD_CONSTANTS_FILEPATH_ID)
 #define AFSDIR_SERVER_MIGRATELOG_FILEPATH getDirPath(AFSDIR_SERVER_MIGRATELOG_FILEPATH_ID)
 #define AFSDIR_SERVER_KRB_EXCL_FILEPATH getDirPath(AFSDIR_SERVER_KRB_EXCL_FILEPATH_ID)
+#define AFSDIR_SERVER_FSSTATE_FILEPATH getDirPath(AFSDIR_SERVER_FSSTATE_FILEPATH_ID)
 
 /* client file paths */
 #define AFSDIR_CLIENT_THISCELL_FILEPATH getDirPath(AFSDIR_CLIENT_THISCELL_FILEPATH_ID)
index b0c62bc..1d49d81 100644 (file)
@@ -126,7 +126,9 @@ extern int
 #define AFSDIR_VLOG_FILE        "VLLog"
 #define AFSDIR_CORE_FILE        "core"
 #define AFSDIR_SLVGLOG_FILE     "SalvageLog"
+#define AFSDIR_SALSRVLOG_FILE   "SalsrvLog"
 #define AFSDIR_SALVAGER_FILE    "salvager"
+#define AFSDIR_SALSRV_FILE      "salvageserver"
 #define AFSDIR_SLVGLOCK_FILE    "salvage.lock"
 #define AFSDIR_BOZCONF_FILE     "BosConfig"
 #define AFSDIR_BOZCONFNEW_FILE  "BosConfig.new"
@@ -146,6 +148,8 @@ extern int
 #define AFSDIR_FILELOG_FILE     "FileLog"
 #define AFSDIR_MIGRATE_LOGNAME  "wtlog."
 
+#define AFSDIR_FSSTATE_FILE     "fsstate.dat"
+
 #ifdef COMMENT
 #define AFSDIR_CELLSERVDB_FILE_NTCLIENT  "afsdcell.ini"
 #else
@@ -189,9 +193,15 @@ AFSDIR_LOCAL_DIR
 #define AFSDIR_CANONICAL_SERVER_SALVAGER_FILEPATH \
 AFSDIR_CANONICAL_SERVER_BIN_DIRPATH "/" AFSDIR_SALVAGER_FILE
 
+#define AFSDIR_CANONICAL_SERVER_SALSRV_FILEPATH \
+AFSDIR_CANONICAL_SERVER_BIN_DIRPATH "/" AFSDIR_SALSRV_FILE
+
 #define AFSDIR_CANONICAL_SERVER_SLVGLOG_FILEPATH \
 AFSDIR_CANONICAL_SERVER_LOGS_DIRPATH "/" AFSDIR_SLVGLOG_FILE
 
+#define AFSDIR_CANONICAL_SERVER_SALSRVLOG_FILEPATH \
+AFSDIR_CANONICAL_SERVER_LOGS_DIRPATH "/" AFSDIR_SALSRVLOG_FILE
+
 
 /* ---------------------  Local path macros ---------------------- */
 
@@ -259,6 +269,9 @@ typedef enum afsdir_id {
     AFSDIR_SERVER_BIN_FILE_DIRPATH_ID,
     AFSDIR_CLIENT_CELLALIAS_FILEPATH_ID,
     AFSDIR_SERVER_KRB_EXCL_FILEPATH_ID,
+    AFSDIR_SERVER_SALSRV_FILEPATH_ID,
+    AFSDIR_SERVER_SALSRVLOG_FILEPATH_ID,
+    AFSDIR_SERVER_FSSTATE_FILEPATH_ID,
     AFSDIR_PATHSTRING_MAX
 } afsdir_id_t;
 
@@ -306,7 +319,9 @@ const char *getDirPath(afsdir_id_t string_id);
 #define AFSDIR_SERVER_VLOG_FILEPATH getDirPath(AFSDIR_SERVER_VLOG_FILEPATH_ID)
 #define AFSDIR_SERVER_CORELOG_FILEPATH getDirPath(AFSDIR_SERVER_CORELOG_FILEPATH_ID)
 #define AFSDIR_SERVER_SLVGLOG_FILEPATH getDirPath(AFSDIR_SERVER_SLVGLOG_FILEPATH_ID)
+#define AFSDIR_SERVER_SALSRVLOG_FILEPATH getDirPath(AFSDIR_SERVER_SALSRVLOG_FILEPATH_ID)
 #define AFSDIR_SERVER_SALVAGER_FILEPATH getDirPath(AFSDIR_SERVER_SALVAGER_FILEPATH_ID)
+#define AFSDIR_SERVER_SALSRV_FILEPATH getDirPath(AFSDIR_SERVER_SALSRV_FILEPATH_ID)
 #define AFSDIR_SERVER_BOZCONF_FILEPATH getDirPath(AFSDIR_SERVER_BOZCONF_FILEPATH_ID)
 #define AFSDIR_SERVER_BOZCONFNEW_FILEPATH getDirPath(AFSDIR_SERVER_BOZCONFNEW_FILEPATH_ID)
 #define AFSDIR_SERVER_BOZINIT_FILEPATH getDirPath(AFSDIR_SERVER_BOZINIT_FILEPATH_ID)
@@ -328,6 +343,7 @@ const char *getDirPath(afsdir_id_t string_id);
 #define AFSDIR_SERVER_THRESHOLD_CONSTANTS_FILEPATH getDirPath(AFSDIR_SERVER_THRESHOLD_CONSTANTS_FILEPATH_ID)
 #define AFSDIR_SERVER_MIGRATELOG_FILEPATH getDirPath(AFSDIR_SERVER_MIGRATELOG_FILEPATH_ID)
 #define AFSDIR_SERVER_KRB_EXCL_FILEPATH getDirPath(AFSDIR_SERVER_KRB_EXCL_FILEPATH_ID)
+#define AFSDIR_SERVER_FSSTATE_FILEPATH getDirPath(AFSDIR_SERVER_FSSTATE_FILEPATH_ID)
 
 /* client file paths */
 #define AFSDIR_CLIENT_THISCELL_FILEPATH getDirPath(AFSDIR_CLIENT_THISCELL_FILEPATH_ID)
index aa805d2..bc16dd6 100644 (file)
@@ -50,6 +50,7 @@
                                 * to THIS server to find out where */
 #define VIO            112     /* Vnode temporarily unaccessible, but not known 
                                 * to be permanently bad. */
+#define VSALVAGING      113     /* Volume is being salvaged (demand attach fs) */
 #define VRESTRICTED     120    /* Volume is restricted from using one or more
                                 * of the given residencies; do a
                                 * vos examine to find out the current
diff --git a/src/util/strnlen.c b/src/util/strnlen.c
new file mode 100644 (file)
index 0000000..6c350df
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2006, Sine Nomine Associates and others.
+ * All Rights Reserved.
+ * 
+ * This software has been released under the terms of the IBM Public
+ * License.  For details, see the LICENSE file in the top-level source
+ * directory or online at http://www.openafs.org/dl/license10.html
+ */
+
+/* strnlen.c - fixed length string length */
+
+#include <afsconfig.h>
+#include <afs/param.h>
+
+RCSID
+    ("$Header$");
+
+#include <sys/types.h>
+#include <stdarg.h>
+#include <ctype.h>
+
+
+size_t
+afs_strnlen(char * buf, size_t len)
+{
+    size_t i;
+
+    for (i = 0; i < len; i++) {
+       if (buf[i] == '\0')
+           break;
+    }
+
+    return i;
+}
+
index 1b7d23f..6de7605 100644 (file)
@@ -50,6 +50,7 @@ headers=${TOP_INCDIR}/lwp.h           \
        ${TOP_INCDIR}/afs/afsint.h      \
        viced.h                         \
        host.h                          \
+       callback.h                      \
        fs_stats.h
 
 objects=viced.o                \
index 125d1ca..0ffb6b7 100644 (file)
@@ -5,6 +5,8 @@
 # License.  For details, see the LICENSE file in the top-level source
 # directory or online at http://www.openafs.org/dl/license10.html
 
+AFSDEV_AUXCDEFINES = -DFSSYNC_BUILD_SERVER
+
 RELDIR=viced
 !INCLUDE ..\config\NTMakefile.$(SYS_NAME)
 !INCLUDE ..\config\NTMakefile.version
index 4743a2c..429a7de 100644 (file)
@@ -112,6 +112,7 @@ RCSID
 #include "viced_prototypes.h"
 #include "viced.h"
 #include "host.h"
+#include "callback.h"
 #include <afs/unified_afs.h>
 #include <afs/audit.h>
 #include <afs/afsutil.h>
@@ -209,7 +210,7 @@ extern afs_int32 readonlyServer;
 /*
  * Externals used by the xstat code.
  */
-extern int VolumeCacheSize, VolumeGets, VolumeReplacements;
+extern VolPkgStats VStats;
 extern int CEs, CEBlocks;
 
 extern int HTs, HTBlocks;
@@ -438,7 +439,7 @@ static afs_int32
 CheckVnode(AFSFid * fid, Volume ** volptr, Vnode ** vptr, int lock)
 {
     int fileCode = 0;
-    int errorCode = -1;
+    afs_int32 local_errorCode, errorCode = -1;
     static struct timeval restartedat = { 0, 0 };
 
     if (fid->Volume == 0 || fid->Vnode == 0)   /* not: || fid->Unique == 0) */
@@ -448,7 +449,7 @@ CheckVnode(AFSFid * fid, Volume ** volptr, Vnode ** vptr, int lock)
 
        while (1) {
            errorCode = 0;
-           *volptr = VGetVolume(&errorCode, (afs_int32) fid->Volume);
+           *volptr = VGetVolume(&local_errorCode, &errorCode, (afs_int32) fid->Volume);
            if (!errorCode) {
                assert(*volptr);
                break;
@@ -525,8 +526,10 @@ CheckVnode(AFSFid * fid, Volume ** volptr, Vnode ** vptr, int lock)
                    }
                }
            }
-           /* allow read operations on busy volume */
-           else if (errorCode == VBUSY && lock == READ_LOCK) {
+           /* allow read operations on busy volume. 
+            * must check local_errorCode because demand attach fs
+            * can have local_errorCode == VSALVAGING, errorCode == VBUSY */
+           else if (local_errorCode == VBUSY && lock == READ_LOCK) {
                errorCode = 0;
                break;
            } else if (errorCode)
@@ -1151,6 +1154,8 @@ CopyOnWrite(Vnode * targetptr, Volume * volptr)
                         wrlen, errno));
 #ifdef FAST_RESTART            /* if running in no-salvage, don't core the server */
                ViceLog(0, ("CopyOnWrite failed: taking volume offline\n"));
+#elif defined(AFS_DEMAND_ATTACH_FS)
+               ViceLog(0, ("CopyOnWrite failed: requesting salvage\n"));
 #else /* Avoid further corruption and try to get a core. */
                assert(0);
 #endif
@@ -5564,7 +5569,7 @@ SRXAFS_XStatsVersion(struct rx_call * a_call, afs_int32 * a_versionP)
 static void
 FillPerfValues(struct afs_PerfStats *a_perfP)
 {                              /*FillPerfValues */
-
+    afs_uint32 hi, lo;
     int dir_Buffers;           /*# buffers in use by dir package */
     int dir_Calls;             /*# read calls in dir package */
     int dir_IOs;               /*# I/O ops in dir package */
@@ -5582,9 +5587,11 @@ FillPerfValues(struct afs_PerfStats *a_perfP)
     a_perfP->vcache_S_Gets = VnodeClassInfo[vSmall].gets;
     a_perfP->vcache_S_Reads = VnodeClassInfo[vSmall].reads;
     a_perfP->vcache_S_Writes = VnodeClassInfo[vSmall].writes;
-    a_perfP->vcache_H_Entries = VolumeCacheSize;
-    a_perfP->vcache_H_Gets = VolumeGets;
-    a_perfP->vcache_H_Replacements = VolumeReplacements;
+    a_perfP->vcache_H_Entries = VStats.hdr_cache_size;
+    SplitInt64(VStats.hdr_gets, hi, lo);
+    a_perfP->vcache_H_Gets = lo;
+    SplitInt64(VStats.hdr_loads, hi, lo);
+    a_perfP->vcache_H_Replacements = lo;
 
     /*
      * Directory section.
index 8c3040d..44b4523 100644 (file)
@@ -5,6 +5,8 @@
  * This software has been released under the terms of the IBM Public
  * License.  For details, see the LICENSE file in the top-level source
  * directory or online at http://www.openafs.org/dl/license10.html
+ *
+ * Portions Copyright (c) 2006 Sine Nomine Associates
  */
 
 /*
@@ -120,94 +122,24 @@ RCSID
 
 #include <afs/ptclient.h>      /* need definition of prlist for host.h */
 #include "host.h"
+#include "callback.h"
+#ifdef AFS_DEMAND_ATTACH_FS
+#include "../tviced/serialize_state.h"
+#endif /* AFS_DEMAND_ATTACH_FS */
+
 
 extern afsUUID FS_HostUUID;
 extern int hostCount;
-int ShowProblems = 1;
-
-/* Maximum number of call backs to break at once, single fid */
-/* There is some debate as to just how large this value should be */
-/* Ideally, it would be very very large, but I am afraid that the */
-/* cache managers will all send in their responses simultaneously, */
-/* thereby swamping the file server.  As a result, something like */
-/* 10 or 15 might be a better bet. */
-#define MAX_CB_HOSTS   10
-
-/* max time to break a callback, otherwise client is dead or net is hosed */
-#define MAXCBT 25
-
-#define u_byte unsigned char
+static int ShowProblems = 1;
 
 struct cbcounters cbstuff;
 
-struct cbstruct {
-    struct host *hp;
-    afs_uint32 thead;
-};
-
-struct FileEntry {
-    afs_uint32 vnode;
-    afs_uint32 unique;
-    afs_uint32 volid;
-    afs_uint32 fnext;
-    afs_uint32 ncbs;
-    afs_uint32 firstcb;
-    afs_uint32 status;
-    afs_uint32 spare;
-} *FE;                         /* Don't use FE[0] */
-#define FE_LATER 0x1
-
-struct CallBack {
-    afs_uint32 cnext;          /* Next call back entry */
-    afs_uint32 fhead;          /* Head of this call back chain */
-    u_byte thead;              /* Head of timeout chain */
-    u_byte status;             /* Call back status; see definitions, below */
-    afs_uint32 hhead;          /* Head of host table chain */
-    afs_uint32 tprev, tnext;   /* Timeout chain */
-    afs_uint32 hprev, hnext;   /* Chain from host table */
-    unsigned short spare;      /* make it a multiple of 32 bits. */
-} *CB;                         /* Don't use CB[0] */
-
-/* status values for status field of CallBack structure */
-#define CB_NORMAL   1          /* Normal call back */
-#define CB_DELAYED  2          /* Delayed call back due to rpc problems.
-                                * The call back entry will be added back to the
-                                * host list at the END of the list, so that
-                                * searching backwards in the list will find all
-                                * the (consecutive)host. delayed call back entries */
-#define CB_VOLUME   3          /* Callback for a volume */
-#define CB_BULK     4          /* Normal callbacks, handed out from FetchBulkStatus */
-
-/* call back indices to pointers, and vice-versa */
-#define itocb(i)    ((i)?CB+(i):0)
-#define cbtoi(cbp)  (!(cbp)?0:(cbp)-CB)
-
-/* file entry indices to pointers, and vice-versa */
-#define itofe(i)    ((i)?FE+(i):0)
-#define fetoi(fep)  (!(fep)?0:(fep)-FE)
-
-/* Timeouts:  there are 128 possible timeout values in effect at any
- * given time.  Each timeout represents timeouts in an interval of 128
- * seconds.  So the maximum timeout for a call back is 128*128=16384
- * seconds, or 4 1/2 hours.  The timeout cleanup stuff is called only
- * if space runs out or by the file server every 5 minutes.  This 5
- * minute slack should be allowed for--so a maximum time of 4 hours
- * is safer.
- *
- * Timeouts must be chosen to correspond to an exact multiple
- * of 128, because all times are truncated to a 128 multiple, and
- * timed out if the current truncated time is <= to the truncated time
- * corresponding to the timeout queue.
- */
+static struct FileEntry * FE = NULL;    /* don't use FE[0] */
+static struct CallBack * CB = NULL;     /* don't use CB[0] */
 
-/* Unix time to Call Back time, and vice-versa.  Call back time is
-   in units of 128 seconds, corresponding to time queues. */
-#define CBtime(uxtime) ((uxtime)>>7)
-#define UXtime(cbtime) ((cbtime)<<7)
+static struct CallBack * CBfree = NULL;
+static struct FileEntry * FEfree = NULL;
 
-/* Given a Unix time, compute the closest Unix time that corresponds to
-   a time queue, rounding up */
-#define TimeCeiling(uxtime)    (((uxtime)+127)&~127)
 
 /* Time to live for call backs depends upon number of users of the file.
  * TimeOuts is indexed by this number/8 (using TimeOut macro).  Times
@@ -229,52 +161,17 @@ static int TimeOuts[] = {
 /* minimum time given for a call back */
 static int MinTimeOut = (7 * 60);
 
-#define TimeOutCutoff   ((sizeof(TimeOuts)/sizeof(TimeOuts[0]))*8)
-#define TimeOut(nusers)  ((nusers)>=TimeOutCutoff? MinTimeOut: TimeOuts[(nusers)>>3])
-
-/* time out at server is 3 minutes more than ws */
-#define ServerBias       (3*60)
-
 /* Heads of CB queues; a timeout index is 1+index into this array */
-static afs_uint32 timeout[128];
-
-/* Convert cbtime to timeout queue index */
-#define TIndex(cbtime)  (((cbtime)&127)+1)
-
-/* Convert cbtime to pointer to timeout queue head */
-#define THead(cbtime)  (&timeout[TIndex(cbtime)-1])
+static afs_uint32 timeout[CB_NUM_TIMEOUT_QUEUES];
 
 static afs_int32 tfirst;       /* cbtime of oldest unexpired call back time queue */
 
-/* Normalize index into timeout array so that two such indices will be
-   ordered correctly, so that they can be compared to see which times
-   sooner, or so that the difference in time out times between them
-   can be computed. */
-#define TNorm(index)   ((index)<TIndex(tfirst)?(index)+128:(index))
-
-/* This converts a timeout index into the actual time it will expire */
-#define TIndexToTime(index) (UXtime(TNorm(index) - TIndex(tfirst) + tfirst))
-
-
-/* Convert pointer to timeout queue head to index, and vice versa */
-#define ttoi(t)                ((t-timeout)+1)
-#define itot(i)                ((timeout)+(i-1))
 
 /* 16 byte object get/free routines */
 struct object {
     struct object *next;
 };
 
-struct VCBParams {
-    struct cbstruct cba[MAX_CB_HOSTS]; /* re-entrant storage */
-    unsigned int ncbas;
-    afs_uint32 thead;          /* head of timeout queue for youngest callback */
-    struct AFSFid *fid;
-};
-
-struct CallBack *CBfree = 0;
-struct FileEntry *FEfree = 0;
-
 /* Prototypes for static routines */
 static struct FileEntry *FindFE(register AFSFid * fid);
 static struct CallBack *iGetCB(register int *nused);
@@ -308,12 +205,11 @@ static int ClearHostCallbacks_r(struct host *hp, int locked);
 #define FreeCB(cb) iFreeCB((struct CallBack *)cb, &cbstuff.nCBs)
 #define FreeFE(fe) iFreeFE((struct FileEntry *)fe, &cbstuff.nFEs)
 
+
 /* Other protos - move out sometime */
 void PrintCB(register struct CallBack *cb, afs_uint32 now);
 
-#define VHASH 512              /* Power of 2 */
-static afs_uint32 HashTable[VHASH];    /* File entry hash table */
-#define VHash(volume, unique) (((volume)+(unique))&(VHASH-1))
+static afs_uint32 HashTable[FEHASH_SIZE];      /* File entry hash table */
 
 static struct FileEntry *
 FindFE(register AFSFid * fid)
@@ -322,7 +218,7 @@ FindFE(register AFSFid * fid)
     register int fei;
     register struct FileEntry *fe;
 
-    hash = VHash(fid->Volume, fid->Unique);
+    hash = FEHash(fid->Volume, fid->Unique);
     for (fei = HashTable[hash]; fei; fei = fe->fnext) {
        fe = itofe(fei);
        if (fe->volid == fid->Volume && fe->unique == fid->Unique
@@ -421,11 +317,11 @@ HAdd(register struct CallBack *cb, register struct host *host)
     if (!host->cblist) {
        host->cblist = cb->hnext = cb->hprev = cbtoi(cb);
     } else {
-       register struct CallBack *hhp = itocb(host->cblist);
+       register struct CallBack *fcb = itocb(host->cblist);
 
-       cb->hprev = hhp->hprev;
-       cb->hnext = host->cblist;
-       hhp->hprev = (itocb(hhp->hprev)->hnext = cbtoi(cb));
+       cb->hprev = fcb->hprev;
+       cb->hnext = cbtoi(fcb);
+       fcb->hprev = (itocb(fcb->hprev)->hnext = cbtoi(cb));
     }
     return 0;
 }
@@ -475,7 +371,7 @@ CDel(struct CallBack *cb, int deletefe)
 /* N.B.  This one also deletes the CB, and also possibly parent FE, so
  * make sure that it is not on any other list before calling this
  * routine */
-int Ccdelpt = 0, CcdelB = 0;
+static int Ccdelpt = 0, CcdelB = 0;
 
 static int
 CDelPtr(register struct FileEntry *fe, register afs_uint32 * cbp,
@@ -522,7 +418,7 @@ static int
 FDel(register struct FileEntry *fe)
 {
     register int fei = fetoi(fe);
-    register afs_uint32 *p = &HashTable[VHash(fe->volid, fe->unique)];
+    register afs_uint32 *p = &HashTable[FEHash(fe->volid, fe->unique)];
 
     while (*p && *p != fei)
        p = &itofe(*p)->fnext;
@@ -532,6 +428,7 @@ FDel(register struct FileEntry *fe)
     return 0;
 }
 
+/* initialize the callback package */
 int
 InitCallBack(int nblks)
 {
@@ -539,19 +436,21 @@ InitCallBack(int nblks)
     tfirst = CBtime(FT_ApproxTime());
     /* N.B. The "-1", below, is because
      * FE[0] and CB[0] are not used--and not allocated */
-    FE = ((struct FileEntry *)(calloc(nblks, sizeof(struct FileEntry)))) - 1;
+    FE = ((struct FileEntry *)(calloc(nblks, sizeof(struct FileEntry))));
     if (!FE) {
        ViceLog(0, ("Failed malloc in InitCallBack\n"));
        assert(0);
     }
+    FE--;  /* FE[0] is supposed to point to junk */
     cbstuff.nFEs = nblks;
     while (cbstuff.nFEs)
        FreeFE(&FE[cbstuff.nFEs]);      /* This is correct */
-    CB = ((struct CallBack *)(calloc(nblks, sizeof(struct CallBack)))) - 1;
+    CB = ((struct CallBack *)(calloc(nblks, sizeof(struct CallBack))));
     if (!CB) {
        ViceLog(0, ("Failed malloc in InitCallBack\n"));
        assert(0);
     }
+    CB--;  /* CB[0] is supposed to point to junk */
     cbstuff.nCBs = nblks;
     while (cbstuff.nCBs)
        FreeCB(&CB[cbstuff.nCBs]);      /* This is correct */
@@ -696,7 +595,7 @@ AddCallBack1_r(struct host *host, AFSFid * fid, afs_uint32 * thead, int type,
        fe->unique = fid->Unique;
        fe->ncbs = 0;
        fe->status = 0;
-       hash = VHash(fid->Volume, fid->Unique);
+       hash = FEHash(fid->Volume, fid->Unique);
        fe->fnext = HashTable[hash];
        HashTable[hash] = fetoi(fe);
     }
@@ -1302,7 +1201,7 @@ BreakVolumeCallBacks(afs_uint32 volume)
 
     H_LOCK;
     fid.Volume = volume, fid.Vnode = fid.Unique = 0;
-    for (hash = 0; hash < VHASH; hash++) {
+    for (hash = 0; hash < FEHASH_SIZE; hash++) {
        for (feip = &HashTable[hash]; (fe = itofe(*feip));) {
            if (fe->volid == volume) {
                register struct CallBack *cbnext;
@@ -1360,7 +1259,7 @@ int
 BreakVolumeCallBacksLater(afs_uint32 volume)
 {
     int hash;
-    afs_int32 *feip;
+    afs_uint32 *feip;
     struct FileEntry *fe;
     struct CallBack *cb;
     struct host *host;
@@ -1368,7 +1267,7 @@ BreakVolumeCallBacksLater(afs_uint32 volume)
 
     ViceLog(25, ("Setting later on volume %u\n", volume));
     H_LOCK;
-    for (hash = 0; hash < VHASH; hash++) {
+    for (hash = 0; hash < FEHASH_SIZE; hash++) {
        for (feip = &HashTable[hash]; (fe = itofe(*feip)) != NULL; ) {
            if (fe->volid == volume) {
                register struct CallBack *cbnext;
@@ -1381,7 +1280,7 @@ BreakVolumeCallBacksLater(afs_uint32 volume)
                FSYNC_LOCK;
                fe->status |= FE_LATER;
                FSYNC_UNLOCK;
-               found++;
+               found = 1;
            }
            feip = &fe->fnext;
        }
@@ -1408,7 +1307,7 @@ BreakLaterCallBacks(void)
 {
     struct AFSFid fid;
     int hash;
-    afs_int32 *feip;
+    afs_uint32 *feip;
     struct CallBack *cb;
     struct FileEntry *fe = NULL;
     struct FileEntry *myfe = NULL;
@@ -1424,7 +1323,7 @@ BreakLaterCallBacks(void)
     /* Pick the first volume we see to clean up */
     fid.Volume = fid.Vnode = fid.Unique = 0;
 
-    for (hash = 0; hash < VHASH; hash++) {
+    for (hash = 0; hash < FEHASH_SIZE; hash++) {
        for (feip = &HashTable[hash]; (fe = itofe(*feip)) != NULL; ) {
            if (fe && (fe->status & FE_LATER)
                && (fid.Volume == 0 || fid.Volume == fe->volid)) {
@@ -1775,6 +1674,973 @@ PrintCallBackStats(void)
 
 #ifndef INTERPRET_DUMP
 
+#ifdef AFS_DEMAND_ATTACH_FS
+/*
+ * demand attach fs
+ * callback state serialization
+ */
+static int cb_stateSaveTimeouts(struct fs_dump_state * state);
+static int cb_stateSaveFEHash(struct fs_dump_state * state);
+static int cb_stateSaveFEs(struct fs_dump_state * state);
+static int cb_stateSaveFE(struct fs_dump_state * state, struct FileEntry * fe);
+static int cb_stateRestoreTimeouts(struct fs_dump_state * state);
+static int cb_stateRestoreFEHash(struct fs_dump_state * state);
+static int cb_stateRestoreFEs(struct fs_dump_state * state);
+static int cb_stateRestoreFE(struct fs_dump_state * state);
+static int cb_stateRestoreCBs(struct fs_dump_state * state, struct FileEntry * fe, 
+                             struct iovec * iov, int niovecs);
+
+static int cb_stateVerifyFEHash(struct fs_dump_state * state);
+static int cb_stateVerifyFE(struct fs_dump_state * state, struct FileEntry * fe);
+static int cb_stateVerifyFCBList(struct fs_dump_state * state, struct FileEntry * fe);
+static int cb_stateVerifyTimeoutQueues(struct fs_dump_state * state);
+
+static int cb_stateFEToDiskEntry(struct FileEntry *, struct FEDiskEntry *);
+static int cb_stateDiskEntryToFE(struct fs_dump_state * state,
+                                struct FEDiskEntry *, struct FileEntry *);
+
+static int cb_stateCBToDiskEntry(struct CallBack *, struct CBDiskEntry *);
+static int cb_stateDiskEntryToCB(struct fs_dump_state * state,
+                                struct CBDiskEntry *, struct CallBack *);
+
+static int cb_stateFillHeader(struct callback_state_header * hdr);
+static int cb_stateCheckHeader(struct callback_state_header * hdr);
+
+static int cb_stateAllocMap(struct fs_dump_state * state);
+
+int
+cb_stateSave(struct fs_dump_state * state)
+{
+    int ret = 0;
+
+    AssignInt64(state->eof_offset, &state->hdr->cb_offset);
+
+    /* invalidate callback state header */
+    memset(state->cb_hdr, 0, sizeof(struct callback_state_header));
+    if (fs_stateWriteHeader(state, &state->hdr->cb_offset, state->cb_hdr,
+                           sizeof(struct callback_state_header))) {
+       ret = 1;
+       goto done;
+    }
+
+    fs_stateIncEOF(state, sizeof(struct callback_state_header));
+
+    /* dump timeout state */
+    if (cb_stateSaveTimeouts(state)) {
+       ret = 1;
+       goto done;
+    }
+
+    /* dump fe hashtable state */
+    if (cb_stateSaveFEHash(state)) {
+       ret = 1;
+       goto done;
+    }
+
+    /* dump callback state */
+    if (cb_stateSaveFEs(state)) {
+       ret = 1;
+       goto done;
+    }
+
+    /* write the callback state header to disk */
+    cb_stateFillHeader(state->cb_hdr);
+    if (fs_stateWriteHeader(state, &state->hdr->cb_offset, state->cb_hdr,
+                           sizeof(struct callback_state_header))) {
+       ret = 1;
+       goto done;
+    }
+    
+ done:
+    return ret;
+}
+
+int
+cb_stateRestore(struct fs_dump_state * state)
+{
+    int ret = 0;
+
+    if (fs_stateReadHeader(state, &state->hdr->cb_offset, state->cb_hdr,
+                          sizeof(struct callback_state_header))) {
+       ret = 1;
+       goto done;
+    }
+
+    if (cb_stateCheckHeader(state->cb_hdr)) {
+       ret = 1;
+       goto done;
+    }
+
+    if (cb_stateAllocMap(state)) {
+       ret = 1;
+       goto done;
+    }
+
+    if (cb_stateRestoreTimeouts(state)) {
+       ret = 1;
+       goto done;
+    }
+
+    if (cb_stateRestoreFEHash(state)) {
+       ret = 1;
+       goto done;
+    }
+
+    /* restore FEs and CBs from disk */
+    if (cb_stateRestoreFEs(state)) {
+       ret = 1;
+       goto done;
+    }
+
+    /* restore the timeout queue heads */
+    tfirst = state->cb_hdr->tfirst;
+
+ done:
+    return ret;
+}
+
+int
+cb_stateRestoreIndices(struct fs_dump_state * state)
+{
+    int i, ret = 0;
+    struct FileEntry * fe;
+    struct CallBack * cb;
+
+    /* restore indices in the FileEntry structures */
+    for (i = 1; i < state->fe_map.len; i++) {
+       if (state->fe_map.entries[i].new_idx) {
+           fe = itofe(state->fe_map.entries[i].new_idx);
+
+           /* restore the fe->fnext entry */
+           if (fe_OldToNew(state, fe->fnext, &fe->fnext)) {
+               ret = 1;
+               goto done;
+           }
+
+           /* restore the fe->firstcb entry */
+           if (cb_OldToNew(state, fe->firstcb, &fe->firstcb)) {
+               ret = 1;
+               goto done;
+           }
+       }
+    }
+    
+    /* restore indices in the CallBack structures */
+    for (i = 1; i < state->cb_map.len; i++) {
+       if (state->cb_map.entries[i].new_idx) {
+           cb = itocb(state->cb_map.entries[i].new_idx);
+
+           /* restore the cb->cnext entry */
+           if (cb_OldToNew(state, cb->cnext, &cb->cnext)) {
+               ret = 1;
+               goto done;
+           }
+           
+           /* restore the cb->fhead entry */
+           if (fe_OldToNew(state, cb->fhead, &cb->fhead)) {
+               ret = 1;
+               goto done;
+           }
+
+           /* restore the cb->hhead entry */
+           if (h_OldToNew(state, cb->hhead, &cb->hhead)) {
+               ret = 1;
+               goto done;
+           }
+
+           /* restore the cb->tprev entry */
+           if (cb_OldToNew(state, cb->tprev, &cb->tprev)) {
+               ret = 1;
+               goto done;
+           }
+
+           /* restore the cb->tnext entry */
+           if (cb_OldToNew(state, cb->tnext, &cb->tnext)) {
+               ret = 1;
+               goto done;
+           }
+
+           /* restore the cb->hprev entry */
+           if (cb_OldToNew(state, cb->hprev, &cb->hprev)) {
+               ret = 1;
+               goto done;
+           }
+
+           /* restore the cb->hnext entry */
+           if (cb_OldToNew(state, cb->hnext, &cb->hnext)) {
+               ret = 1;
+               goto done;
+           }
+       }
+    }
+
+    /* restore the timeout queue head indices */
+    for (i = 0; i < state->cb_timeout_hdr->records; i++) {
+       if (cb_OldToNew(state, timeout[i], &timeout[i])) {
+           ret = 1;
+           goto done;
+       }
+    }
+
+    /* restore the FE hash table queue heads */
+    for (i = 0; i < state->cb_fehash_hdr->records; i++) {
+       if (fe_OldToNew(state, HashTable[i], &HashTable[i])) {
+           ret = 1;
+           goto done;
+       }
+    }
+
+ done:
+    return ret;
+}
+
+int
+cb_stateVerify(struct fs_dump_state * state)
+{
+    int ret = 0;
+
+    if (cb_stateVerifyFEHash(state)) {
+       ret = 1;
+    }
+
+    if (cb_stateVerifyTimeoutQueues(state)) {
+       ret = 1;
+    }
+
+ done:
+    return ret;
+}
+
+static int
+cb_stateVerifyFEHash(struct fs_dump_state * state)
+{
+    int ret = 0, i;
+    struct FileEntry * fe;
+    afs_uint32 fei, chain_len;
+
+    for (i = 0; i < FEHASH_SIZE; i++) {
+       chain_len = 0;
+       for (fei = HashTable[i], fe = itofe(fei);
+            fe;
+            fei = fe->fnext, fe = itofe(fei)) {
+           if (fei > cbstuff.nblks) {
+               ViceLog(0, ("cb_stateVerifyFEHash: error: index out of range (fei=%d)\n", fei));
+               ret = 1;
+               break;
+           }
+           if (cb_stateVerifyFE(state, fe)) {
+               ret = 1;
+           }
+           if (chain_len > FS_STATE_FE_MAX_HASH_CHAIN_LEN) {
+               ViceLog(0, ("cb_stateVerifyFEHash: error: hash chain %d length exceeds %d; assuming there's a loop\n",
+                           i, FS_STATE_FE_MAX_HASH_CHAIN_LEN));
+               ret = 1;
+               break;
+           }
+           chain_len++;
+       }
+    }
+
+ done:
+    return ret;
+}
+
+static int
+cb_stateVerifyFE(struct fs_dump_state * state, struct FileEntry * fe)
+{
+    int ret = 0;
+
+    if ((fe->firstcb && !fe->ncbs) ||
+       (!fe->firstcb && fe->ncbs)) {
+       ViceLog(0, ("cb_stateVerifyFE: error: fe->firstcb does not agree with fe->ncbs (fei=%d, fe->firstcb=%d, fe->ncbs=%d)\n",
+                   fetoi(fe), fe->firstcb, fe->ncbs));
+       ret = 1;
+    }
+    if (cb_stateVerifyFCBList(state, fe)) {
+       ViceLog(0, ("cb_stateVerifyFE: error: FCBList failed verification (fei=%d)\n", fetoi(fe)));
+       ret = 1;
+    }
+
+ done:
+    return ret;
+}
+
+static int
+cb_stateVerifyFCBList(struct fs_dump_state * state, struct FileEntry * fe)
+{
+    int ret = 0;
+    afs_uint32 cbi, fei, chain_len = 0;
+    struct CallBack * cb;
+
+    fei = fetoi(fe);
+
+    for (cbi = fe->firstcb, cb = itocb(cbi);
+        cb;
+        cbi = cb->cnext, cb = itocb(cbi)) {
+       if (cbi > cbstuff.nblks) {
+           ViceLog(0, ("cb_stateVerifyFCBList: error: list index out of range (cbi=%d, ncbs=%d)\n",
+                       cbi, cbstuff.nblks));
+           ret = 1;
+           goto done;
+       }
+       if (cb->fhead != fei) {
+           ViceLog(0, ("cb_stateVerifyFCBList: error: cb->fhead != fei (fei=%d, cb->fhead=%d)\n",
+                       fei, cb->fhead));
+           ret = 1;
+       }
+       if (chain_len > FS_STATE_FCB_MAX_LIST_LEN) {
+           ViceLog(0, ("cb_stateVerifyFCBList: error: list length exceeds %d (fei=%d); assuming there's a loop\n",
+                       FS_STATE_FCB_MAX_LIST_LEN, fei));
+           ret = 1;
+           goto done;
+       }
+       chain_len++;
+    }
+
+    if (fe->ncbs != chain_len) {
+       ViceLog(0, ("cb_stateVerifyFCBList: error: list length mismatch (len=%d, fe->ncbs=%d)\n",
+                   chain_len, fe->ncbs));
+       ret = 1;
+    }
+
+ done:
+    return ret;
+}
+
+int
+cb_stateVerifyHCBList(struct fs_dump_state * state, struct host * host)
+{
+    int ret = 0;
+    afs_uint32 hi, chain_len, cbi;
+    struct CallBack *cb, *ncb;
+
+    hi = h_htoi(host);
+    chain_len = 0;
+
+    for (cbi = host->cblist, cb = itocb(cbi);
+        cb;
+        cbi = cb->hnext, cb = ncb) {
+       if (chain_len && (host->cblist == cbi)) {
+           /* we've wrapped around the circular list, and everything looks ok */
+           break;
+       }
+       if (cb->hhead != hi) {
+           ViceLog(0, ("cb_stateVerifyHCBList: error: incorrect cb->hhead (cbi=%d, h->index=%d, cb->hhead=%d)\n",
+                       cbi, hi, cb->hhead));
+           ret = 1;
+       }
+       if (!cb->hprev || !cb->hnext) {
+           ViceLog(0, ("cb_stateVerifyHCBList: error: null index in circular list (cbi=%d, h->index=%d)\n",
+                       cbi, hi));
+           ret = 1;
+           goto done;
+       }
+       if ((cb->hprev > cbstuff.nblks) ||
+           (cb->hnext > cbstuff.nblks)) {
+           ViceLog(0, ("cb_stateVerifyHCBList: error: list index out of range (cbi=%d, h->index=%d, cb->hprev=%d, cb->hnext=%d, nCBs=%d)\n",
+                       cbi, hi, cb->hprev, cb->hnext, cbstuff.nblks));
+           ret = 1;
+           goto done;
+       }
+       ncb = itocb(cb->hnext);
+       if (cbi != ncb->hprev) {
+           ViceLog(0, ("cb_stateVerifyHCBList: error: corrupt linked list (cbi=%d, h->index=%d)\n",
+                       cbi, hi));
+           ret = 1;
+           goto done;
+       }
+       if (chain_len > FS_STATE_HCB_MAX_LIST_LEN) {
+           ViceLog(0, ("cb_stateVerifyFCBList: error: list length exceeds %d (h->index=%d); assuming there's a loop\n",
+                       FS_STATE_HCB_MAX_LIST_LEN, hi));
+           ret = 1;
+           goto done;
+       }
+       chain_len++;
+    }
+
+ done:
+    return ret;
+}
+
+static int
+cb_stateVerifyTimeoutQueues(struct fs_dump_state * state)
+{
+    int ret = 0, i;
+    afs_uint32 cbi, chain_len;
+    struct CallBack *cb, *ncb;
+
+    for (i = 0; i < CB_NUM_TIMEOUT_QUEUES; i++) {
+       chain_len = 0;
+       for (cbi = timeout[i], cb = itocb(cbi);
+            cb;
+            cbi = cb->tnext, cb = ncb) {
+           if (chain_len && (cbi == timeout[i])) {
+               /* we've wrapped around the circular list, and everything looks ok */
+               break;
+           }
+           if (cbi > cbstuff.nblks) {
+               ViceLog(0, ("cb_stateVerifyTimeoutQueues: error: list index out of range (cbi=%d, tindex=%d)\n",
+                           cbi, i));
+               ret = 1;
+               break;
+           }
+           if (itot(cb->thead) != &timeout[i]) {
+               ViceLog(0, ("cb_stateVerifyTimeoutQueues: error: cb->thead points to wrong timeout queue (tindex=%d, cbi=%d, cb->thead=%d)\n",
+                           i, cbi, cb->thead));
+               ret = 1;
+           }
+           if (!cb->tprev || !cb->tnext) {
+               ViceLog(0, ("cb_stateVerifyTimeoutQueues: null index in circular list (cbi=%d, tindex=%d)\n",
+                           cbi, i));
+               ret = 1;
+               break;
+           }
+           if ((cb->tprev > cbstuff.nblks) ||
+               (cb->tnext > cbstuff.nblks)) {
+               ViceLog(0, ("cb_stateVerifyTimeoutQueues: list index out of range (cbi=%d, tindex=%d, cb->tprev=%d, cb->tnext=%d, nCBs=%d)\n",
+                           cbi, i, cb->tprev, cb->tnext, cbstuff.nblks));
+               ret = 1;
+               break;
+           }
+           ncb = itocb(cb->tnext);
+           if (cbi != ncb->tprev) {
+               ViceLog(0, ("cb_stateVerifyTimeoutQueues: corrupt linked list (cbi=%d, tindex=%d)\n",
+                           cbi, i));
+               ret = 1;
+               break;
+           }
+           if (chain_len > FS_STATE_TCB_MAX_LIST_LEN) {
+               ViceLog(0, ("cb_stateVerifyTimeoutQueues: list length exceeds %d (tindex=%d); assuming there's a loop\n",
+                           FS_STATE_TCB_MAX_LIST_LEN, i));
+               ret = 1;
+               break;
+           }
+           chain_len++;
+       }
+    }
+
+ done:
+    return ret;
+}
+
+static int
+cb_stateSaveTimeouts(struct fs_dump_state * state)
+{
+    int ret = 0;
+    struct iovec iov[2];
+
+    AssignInt64(state->eof_offset, &state->cb_hdr->timeout_offset);
+
+    memset(state->cb_timeout_hdr, 0, sizeof(struct callback_state_fehash_header));
+    state->cb_timeout_hdr->magic = CALLBACK_STATE_TIMEOUT_MAGIC;
+    state->cb_timeout_hdr->records = CB_NUM_TIMEOUT_QUEUES;
+    state->cb_timeout_hdr->len = sizeof(struct callback_state_timeout_header) +
+       (state->cb_timeout_hdr->records * sizeof(afs_uint32));
+
+    iov[0].iov_base = (char *)state->cb_timeout_hdr;
+    iov[0].iov_len = sizeof(struct callback_state_timeout_header);
+    iov[1].iov_base = (char *)timeout;
+    iov[1].iov_len = sizeof(timeout);
+
+    if (fs_stateSeek(state, &state->cb_hdr->timeout_offset)) {
+       ret = 1;
+       goto done;
+    }
+
+    if (fs_stateWriteV(state, iov, 2)) {
+       ret = 1;
+       goto done;
+    }
+
+    fs_stateIncEOF(state, state->cb_timeout_hdr->len);
+
+ done:
+    return ret;
+}
+
+static int
+cb_stateRestoreTimeouts(struct fs_dump_state * state)
+{
+    int ret = 0, len;
+
+    if (fs_stateReadHeader(state, &state->cb_hdr->timeout_offset,
+                          state->cb_timeout_hdr, 
+                          sizeof(struct callback_state_timeout_header))) {
+       ret = 1;
+       goto done;
+    }
+
+    if (state->cb_timeout_hdr->magic != CALLBACK_STATE_TIMEOUT_MAGIC) {
+       ret = 1;
+       goto done;
+    }
+    if (state->cb_timeout_hdr->records != CB_NUM_TIMEOUT_QUEUES) {
+       ret = 1;
+       goto done;
+    }
+
+    len = state->cb_timeout_hdr->records * sizeof(afs_uint32);
+
+    if (state->cb_timeout_hdr->len !=
+       (sizeof(struct callback_state_timeout_header) + len)) {
+       ret = 1;
+       goto done;
+    }
+
+    if (fs_stateRead(state, timeout, len)) {
+       ret = 1;
+       goto done;
+    }
+
+ done:
+    return ret;
+}
+
+static int
+cb_stateSaveFEHash(struct fs_dump_state * state)
+{
+    int ret = 0;
+    struct iovec iov[2];
+
+    AssignInt64(state->eof_offset, &state->cb_hdr->fehash_offset);
+
+    memset(state->cb_fehash_hdr, 0, sizeof(struct callback_state_fehash_header));
+    state->cb_fehash_hdr->magic = CALLBACK_STATE_FEHASH_MAGIC;
+    state->cb_fehash_hdr->records = FEHASH_SIZE;
+    state->cb_fehash_hdr->len = sizeof(struct callback_state_fehash_header) +
+       (state->cb_fehash_hdr->records * sizeof(afs_uint32));
+
+    iov[0].iov_base = (char *)state->cb_fehash_hdr;
+    iov[0].iov_len = sizeof(struct callback_state_fehash_header);
+    iov[1].iov_base = (char *)HashTable;
+    iov[1].iov_len = sizeof(HashTable);
+
+    if (fs_stateSeek(state, &state->cb_hdr->fehash_offset)) {
+       ret = 1;
+       goto done;
+    }
+
+    if (fs_stateWriteV(state, iov, 2)) {
+       ret = 1;
+       goto done;
+    }
+
+    fs_stateIncEOF(state, state->cb_fehash_hdr->len);
+
+ done:
+    return ret;
+}
+
+static int
+cb_stateRestoreFEHash(struct fs_dump_state * state)
+{
+    int ret = 0, len;
+
+    if (fs_stateReadHeader(state, &state->cb_hdr->fehash_offset,
+                          state->cb_fehash_hdr, 
+                          sizeof(struct callback_state_fehash_header))) {
+       ret = 1;
+       goto done;
+    }
+
+    if (state->cb_fehash_hdr->magic != CALLBACK_STATE_FEHASH_MAGIC) {
+       ret = 1;
+       goto done;
+    }
+    if (state->cb_fehash_hdr->records != FEHASH_SIZE) {
+       ret = 1;
+       goto done;
+    }
+
+    len = state->cb_fehash_hdr->records * sizeof(afs_uint32);
+
+    if (state->cb_fehash_hdr->len !=
+       (sizeof(struct callback_state_fehash_header) + len)) {
+       ret = 1;
+       goto done;
+    }
+
+    if (fs_stateRead(state, HashTable, len)) {
+       ret = 1;
+       goto done;
+    }
+
+ done:
+    return ret;
+}
+
+static int
+cb_stateSaveFEs(struct fs_dump_state * state)
+{
+    int ret = 0;
+    register int fei, hash;
+    register struct FileEntry *fe;
+
+    AssignInt64(state->eof_offset, &state->cb_hdr->fe_offset);
+
+    for (hash = 0; hash < FEHASH_SIZE ; hash++) {
+       for (fei = HashTable[hash]; fei; fei = fe->fnext) {
+           fe = itofe(fei);
+           if (cb_stateSaveFE(state, fe)) {
+               ret = 1;
+               goto done;
+           }
+       }
+    }
+
+ done:
+    return ret;
+}
+
+static int
+cb_stateRestoreFEs(struct fs_dump_state * state)
+{
+    int count, nFEs, ret = 0;
+
+    nFEs = state->cb_hdr->nFEs;
+
+    for (count = 0; count < nFEs; count++) {
+       if (cb_stateRestoreFE(state)) {
+           ret = 1;
+           goto done;
+       }
+    }
+
+ done:
+    return ret;
+}
+
+static int
+cb_stateSaveFE(struct fs_dump_state * state, struct FileEntry * fe)
+{
+    int ret = 0, iovcnt, cbi, idx, len, written = 0;
+    afs_uint32 fei;
+    struct callback_state_entry_header hdr;
+    struct FEDiskEntry fedsk;
+    struct CBDiskEntry cbdsk[16];
+    struct iovec iov[16];
+    struct CallBack *cb;
+
+    fei = fetoi(fe);
+    if (fei > state->cb_hdr->fe_max) {
+       state->cb_hdr->fe_max = fei;
+    }
+
+    memset(&hdr, 0, sizeof(struct callback_state_entry_header));
+
+    if (cb_stateFEToDiskEntry(fe, &fedsk)) {
+       ret = 1;
+       goto done;
+    }
+
+    iov[0].iov_base = (char *)&hdr;
+    len = iov[0].iov_len = sizeof(hdr);
+    iov[1].iov_base = (char *)&fedsk;
+    len += iov[1].iov_len = sizeof(struct FEDiskEntry);
+    iovcnt = 2;
+
+    for (cbi = fe->firstcb, cb = itocb(cbi), idx = 2; 
+        cb != NULL; 
+        cbi = cb->cnext, cb = itocb(cbi), idx++, hdr.nCBs++) {
+       if (cbi > state->cb_hdr->cb_max) {
+           state->cb_hdr->cb_max = cbi;
+       }
+       if (cb_stateCBToDiskEntry(cb, &cbdsk[idx])) {
+           ret = 1;
+           goto done;
+       }
+       cbdsk[idx].index = cbi;
+       iov[idx].iov_base = (char *)&cbdsk[idx];
+       len += iov[idx].iov_len = sizeof(struct CBDiskEntry);
+       iovcnt++;
+       if ((iovcnt == 16) || (!cb->cnext)) {
+           if (fs_stateWriteV(state, iov, iovcnt)) {
+               ret = 1;
+               goto done;
+           }
+           written = 1;
+           iovcnt = 0;
+           len = 0;
+       }
+    }
+
+    hdr.magic = CALLBACK_STATE_ENTRY_MAGIC;
+    hdr.len = sizeof(hdr) + sizeof(struct FEDiskEntry) + 
+       (hdr.nCBs * sizeof(struct CBDiskEntry));
+
+    if (!written) {
+       if (fs_stateWriteV(state, iov, iovcnt)) {
+           ret = 1;
+           goto done;
+       }
+    } else {
+       if (fs_stateWriteHeader(state, &state->eof_offset, &hdr, sizeof(hdr))) {
+           ret = 1;
+           goto done;
+       }
+    }
+
+    fs_stateIncEOF(state, hdr.len);
+
+    if (written) {
+       if (fs_stateSeek(state, &state->eof_offset)) {
+           ret = 1;
+           goto done;
+       }
+    }
+
+    state->cb_hdr->nFEs++;
+    state->cb_hdr->nCBs += hdr.nCBs;
+
+ done:
+    return ret;
+}
+
+static int
+cb_stateRestoreFE(struct fs_dump_state * state)
+{
+    int ret = 0, iovcnt, len, nCBs, idx;
+    struct callback_state_entry_header hdr;
+    struct FEDiskEntry fedsk;
+    struct CBDiskEntry cbdsk[16];
+    struct iovec iov[16];
+    struct FileEntry * fe;
+    struct CallBack * cb;
+
+    iov[0].iov_base = (char *)&hdr;
+    len = iov[0].iov_len = sizeof(hdr);
+    iov[1].iov_base = (char *)&fedsk;
+    len += iov[1].iov_len = sizeof(fedsk);
+    iovcnt = 2;
+
+    if (fs_stateReadV(state, iov, iovcnt)) {
+       ret = 1;
+       goto done;
+    }
+
+    if (hdr.magic != CALLBACK_STATE_ENTRY_MAGIC) {
+       ret = 1;
+       goto done;
+    }
+
+    fe = GetFE();
+    if (fe == NULL) {
+       ViceLog(0, ("cb_stateRestoreFE: ran out of free FileEntry structures\n"));
+       ret = 1;
+       goto done;
+    }
+
+    if (cb_stateDiskEntryToFE(state, &fedsk, fe)) {
+       ret = 1;
+       goto done;
+    }
+
+    if (hdr.nCBs) {
+       for (iovcnt = 0, idx = 0, len = 0, nCBs = 0;
+            nCBs < hdr.nCBs;
+            idx++, nCBs++) {
+           iov[idx].iov_base = (char *)&cbdsk[idx];
+           len += iov[idx].iov_len = sizeof(struct CBDiskEntry);
+           iovcnt++;
+           if ((iovcnt == 16) || (nCBs == hdr.nCBs - 1)) {
+               if (fs_stateReadV(state, iov, iovcnt)) {
+                   ret = 1;
+                   goto done;
+               }
+               if (cb_stateRestoreCBs(state, fe, iov, iovcnt)) {
+                   ret = 1;
+                   goto done;
+               }
+               len = 0;
+               iovcnt = 0;
+           }
+       }
+    }
+    
+ done:
+    return ret;
+}
+
+static int
+cb_stateRestoreCBs(struct fs_dump_state * state, struct FileEntry * fe, 
+                  struct iovec * iov, int niovecs)
+{
+    int ret = 0, idx;
+    register struct CallBack * cb;
+    struct CBDiskEntry * cbdsk;
+    afs_uint32 fei;
+
+    fei = fetoi(fe);
+
+    for (idx = 0; idx < niovecs; idx++) {
+       cbdsk = (struct CBDiskEntry *) iov[idx].iov_base;
+       if ((cb = GetCB()) == NULL) {
+           ViceLog(0, ("cb_stateRestoreCBs: ran out of free CallBack structures\n"));
+           ret = 1;
+           goto done;
+       }
+       if (cb_stateDiskEntryToCB(state, cbdsk, cb)) {
+           ViceLog(0, ("cb_stateRestoreCBs: corrupt CallBack disk entry\n"));
+           ret = 1;
+           goto done;
+       }
+    }
+
+ done:
+    return ret;
+}
+
+
+static int
+cb_stateFillHeader(struct callback_state_header * hdr)
+{
+    hdr->stamp.magic = CALLBACK_STATE_MAGIC;
+    hdr->stamp.version = CALLBACK_STATE_VERSION;
+    hdr->tfirst = tfirst;
+    return 0;
+}
+
+static int
+cb_stateCheckHeader(struct callback_state_header * hdr)
+{
+    int ret = 0;
+
+    if (hdr->stamp.magic != CALLBACK_STATE_MAGIC) {
+       ret = 1;
+    } else if (hdr->stamp.version != CALLBACK_STATE_VERSION) {
+       ret = 1;
+    } else if ((hdr->nFEs > cbstuff.nblks) || (hdr->nCBs > cbstuff.nblks)) {
+       ViceLog(0, ("cb_stateCheckHeader: saved callback state larger than callback memory allocation\n"));
+       ret = 1;
+    }
+    return ret;
+}
+
+/* disk entry conversion routines */
+static int
+cb_stateFEToDiskEntry(struct FileEntry * in, struct FEDiskEntry * out)
+{
+    memcpy(&out->fe, in, sizeof(struct FileEntry));
+    out->index = fetoi(in);
+    return 0;
+}
+
+static int
+cb_stateDiskEntryToFE(struct fs_dump_state * state, 
+                     struct FEDiskEntry * in, struct FileEntry * out)
+{
+    int ret = 0;
+
+    memcpy(out, &in->fe, sizeof(struct FileEntry));
+
+    /* setup FE map entry */
+    if (!in->index || (in->index >= state->fe_map.len)) {
+       ViceLog(0, ("cb_stateDiskEntryToFE: index (%d) out of range",
+                   in->index));
+       ret = 1;
+       goto done;
+    }
+    state->fe_map.entries[in->index].old_idx = in->index;
+    state->fe_map.entries[in->index].new_idx = fetoi(out);
+
+ done:
+    return ret;
+}
+
+static int
+cb_stateCBToDiskEntry(struct CallBack * in, struct CBDiskEntry * out)
+{
+    memcpy(&out->cb, in, sizeof(struct CallBack));
+    out->index = cbtoi(in);
+    return 0;
+}
+
+static int
+cb_stateDiskEntryToCB(struct fs_dump_state * state,
+                     struct CBDiskEntry * in, struct CallBack * out)
+{
+    int ret = 0;
+
+    memcpy(out, &in->cb, sizeof(struct CallBack));
+
+    /* setup CB map entry */
+    if (!in->index || (in->index >= state->cb_map.len)) {
+       ViceLog(0, ("cb_stateDiskEntryToCB: index (%d) out of range\n",
+                   in->index));
+       ret = 1;
+       goto done;
+    }
+    state->cb_map.entries[in->index].old_idx = in->index;
+    state->cb_map.entries[in->index].new_idx = cbtoi(out);
+
+ done:
+    return ret;
+}
+
+/* index map routines */
+static int
+cb_stateAllocMap(struct fs_dump_state * state)
+{
+    state->fe_map.len = state->cb_hdr->fe_max + 1;
+    state->cb_map.len = state->cb_hdr->cb_max + 1;
+    state->fe_map.entries = (struct idx_map_entry_t *)
+       calloc(state->fe_map.len, sizeof(struct idx_map_entry_t));
+    state->cb_map.entries = (struct idx_map_entry_t *)
+       calloc(state->cb_map.len, sizeof(struct idx_map_entry_t));
+    return ((state->fe_map.entries != NULL) && (state->cb_map.entries != NULL)) ? 0 : 1;
+}
+
+int
+fe_OldToNew(struct fs_dump_state * state, afs_uint32 old, afs_uint32 * new)
+{
+    int ret = 0;
+
+    /* FEs use a one-based indexing system, so old==0 implies no mapping */
+    if (!old) {
+       *new = 0;
+       goto done;
+    }
+
+    if (old >= state->fe_map.len) {
+       ViceLog(0, ("fe_OldToNew: index %d is out of range\n", old));
+       ret = 1;
+    } else if (state->fe_map.entries[old].old_idx != old) { /* sanity check */
+       ViceLog(0, ("fe_OldToNew: index %d points to an invalid FileEntry record\n", old));
+       ret = 1;
+    } else {
+       *new = state->fe_map.entries[old].new_idx;
+    }
+
+ done:
+    return ret;
+}
+
+int
+cb_OldToNew(struct fs_dump_state * state, afs_uint32 old, afs_uint32 * new)
+{
+    int ret = 0;
+
+    /* CBs use a one-based indexing system, so old==0 implies no mapping */
+    if (!old) {
+       *new = 0;
+       goto done;
+    }
+
+    if (old >= state->cb_map.len) {
+       ViceLog(0, ("cb_OldToNew: index %d is out of range\n", old));
+       ret = 1;
+    } else if (state->cb_map.entries[old].old_idx != old) { /* sanity check */
+       ViceLog(0, ("cb_OldToNew: index %d points to an invalid CallBack record\n", old));
+       ret = 1;
+    } else {
+       *new = state->cb_map.entries[old].new_idx;
+    }
+
+ done:
+    return ret;
+}
+#endif /* AFS_DEMAND_ATTACH_FS */
+
 int
 DumpCallBackState(void)
 {
@@ -1807,7 +2673,7 @@ DumpCallBackState(void)
     return 0;
 }
 
-#endif
+#endif /* !INTERPRET_DUMP */
 
 #ifdef INTERPRET_DUMP
 
@@ -1931,7 +2797,7 @@ main(int argc, char **argv)
        struct CallBack *cb;
        struct FileEntry *fe;
 
-       for (hash = 0; hash < VHASH; hash++) {
+       for (hash = 0; hash < FEHASH_SIZE; hash++) {
            for (feip = &HashTable[hash]; fe = itofe(*feip);) {
                if (!vol || (fe->volid == vol)) {
                    register struct CallBack *cbnext;
@@ -2201,6 +3067,15 @@ MultiProbeAlternateAddress_r(struct host *host)
                 H_UNLOCK;
             }
         }
+#ifdef AFS_DEMAND_ATTACH_FS
+       /* try to bail ASAP if the fileserver is shutting down */
+       FS_STATE_RDLOCK;
+       if (fs_state.mode == FS_MODE_SHUTDOWN) {
+           FS_STATE_UNLOCK;
+           multi_Abort;
+       }
+       FS_STATE_UNLOCK;
+#endif
     }
     multi_End_Ignore;
     H_LOCK;
diff --git a/src/viced/callback.h b/src/viced/callback.h
new file mode 100644 (file)
index 0000000..2f4cca8
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2000, International Business Machines Corporation and others.
+ * All Rights Reserved.
+ * 
+ * This software has been released under the terms of the IBM Public
+ * License.  For details, see the LICENSE file in the top-level source
+ * directory or online at http://www.openafs.org/dl/license10.html
+ *
+ * Portions Copyright (c) 2006 Sine Nomine Associates
+ */
+
+#ifndef _AFS_VICED_CALLBACK_H
+#define _AFS_VICED_CALLBACK_H
+
+/* Maximum number of call backs to break at once, single fid
+ * There is some debate as to just how large this value should be
+ * Ideally, it would be very very large, but I am afraid that the
+ * cache managers will all send in their responses simultaneously,
+ * thereby swamping the file server.  As a result, something like
+ * 10 or 15 might be a better bet.
+ */
+#define MAX_CB_HOSTS   10
+
+/* max time to break a callback, otherwise client is dead or net is hosed */
+#define MAXCBT 25
+
+#define u_byte unsigned char
+
+struct cbcounters {
+    afs_int32 DeleteFiles;
+    afs_int32 DeleteCallBacks;
+    afs_int32 BreakCallBacks;
+    afs_int32 AddCallBacks;
+    afs_int32 GotSomeSpaces;
+    afs_int32 DeleteAllCallBacks;
+    afs_int32 nFEs, nCBs, nblks;
+    afs_int32 CBsTimedOut;
+    afs_int32 nbreakers;
+    afs_int32 GSS1, GSS2, GSS3, GSS4, GSS5;
+};
+extern struct cbcounters cbstuff;
+
+struct cbstruct {
+    struct host *hp;
+    afs_uint32 thead;
+};
+
+/* structure MUST be multiple of 8 bytes, otherwise the casts to
+ * struct object will have alignment issues on *P64 userspaces */
+struct FileEntry {
+    afs_uint32 vnode;
+    afs_uint32 unique;
+    afs_uint32 volid;
+    afs_uint32 fnext;           /* index of next FE in hash chain */
+    afs_uint32 ncbs;            /* number of callbacks for this FE */
+    afs_uint32 firstcb;         /* index of first cb in per-FE list */
+    afs_uint32 status;          /* status bits for this FE */
+    afs_uint32 spare;
+};
+#define FE_LATER 0x1
+
+/* structure MUST be multiple of 8 bytes, otherwise the casts to
+ * struct object will have alignment issues on *P64 userspaces */
+struct CallBack {
+    afs_uint32 cnext;          /* index of next cb in per-FE list */
+    afs_uint32 fhead;          /* index of associated FE */
+    u_byte thead;              /* Head of timeout chain */
+    u_byte status;             /* Call back status; see definitions, below */
+    unsigned short spare;      /* ensure proper alignment */
+    afs_uint32 hhead;          /* Head of host table chain */
+    afs_uint32 tprev, tnext;   /* per-timeout circular list of callbacks */
+    afs_uint32 hprev, hnext;   /* per-host circular list of callbacks */
+};
+
+struct VCBParams {
+    struct cbstruct cba[MAX_CB_HOSTS]; /* re-entrant storage */
+    unsigned int ncbas;
+    afs_uint32 thead;          /* head of timeout queue for youngest callback */
+    struct AFSFid *fid;
+};
+
+
+/* callback hash macros */
+#define FEHASH_SIZE 512                /* Power of 2 */
+#define FEHASH_MASK (FEHASH_SIZE-1)
+#define FEHash(volume, unique) (((volume)+(unique))&(FEHASH_MASK))
+
+#define CB_NUM_TIMEOUT_QUEUES 128
+
+
+/* status values for status field of CallBack structure */
+#define CB_NORMAL   1          /* Normal call back */
+#define CB_DELAYED  2          /* Delayed call back due to rpc problems.
+                                * The call back entry will be added back to the
+                                * host list at the END of the list, so that
+                                * searching backwards in the list will find all
+                                * the (consecutive)host. delayed call back entries */
+#define CB_VOLUME   3          /* Callback for a volume */
+#define CB_BULK     4          /* Normal callbacks, handed out from FetchBulkStatus */
+
+/* call back indices to pointers, and vice-versa */
+#define itocb(i)    ((i)?CB+(i):0)
+#define cbtoi(cbp)  (!(cbp)?0:(cbp)-CB)
+
+/* file entry indices to pointers, and vice-versa */
+#define itofe(i)    ((i)?FE+(i):0)
+#define fetoi(fep)  (!(fep)?0:(fep)-FE)
+
+/* Timeouts:  there are 128 possible timeout values in effect at any
+ * given time.  Each timeout represents timeouts in an interval of 128
+ * seconds.  So the maximum timeout for a call back is 128*128=16384
+ * seconds, or 4 1/2 hours.  The timeout cleanup stuff is called only
+ * if space runs out or by the file server every 5 minutes.  This 5
+ * minute slack should be allowed for--so a maximum time of 4 hours
+ * is safer.
+ *
+ * Timeouts must be chosen to correspond to an exact multiple
+ * of 128, because all times are truncated to a 128 multiple, and
+ * timed out if the current truncated time is <= to the truncated time
+ * corresponding to the timeout queue.
+ */
+
+/* Unix time to Call Back time, and vice-versa.  Call back time is
+   in units of 128 seconds, corresponding to time queues. */
+#define CBtime(uxtime) ((uxtime)>>7)
+#define UXtime(cbtime) ((cbtime)<<7)
+
+/* Given a Unix time, compute the closest Unix time that corresponds to
+   a time queue, rounding up */
+#define TimeCeiling(uxtime)    (((uxtime)+127)&~127)
+
+#define TimeOutCutoff   ((sizeof(TimeOuts)/sizeof(TimeOuts[0]))*8)
+#define TimeOut(nusers)  ((nusers)>=TimeOutCutoff? MinTimeOut: TimeOuts[(nusers)>>3])
+
+/* time out at server is 3 minutes more than ws */
+#define ServerBias       (3*60)
+
+/* Convert cbtime to timeout queue index */
+#define TIndex(cbtime)  (((cbtime)&127)+1)
+
+/* Convert cbtime to pointer to timeout queue head */
+#define THead(cbtime)  (&timeout[TIndex(cbtime)-1])
+
+/* Normalize index into timeout array so that two such indices will be
+   ordered correctly, so that they can be compared to see which times
+   sooner, or so that the difference in time out times between them
+   can be computed. */
+#define TNorm(index)   ((index)<TIndex(tfirst)?(index)+128:(index))
+
+/* This converts a timeout index into the actual time it will expire */
+#define TIndexToTime(index) (UXtime(TNorm(index) - TIndex(tfirst) + tfirst))
+
+
+/* Convert pointer to timeout queue head to index, and vice versa */
+#define ttoi(t)                ((t-timeout)+1)
+#define itot(i)                ((timeout)+(i-1))
+
+#endif /* _AFS_VICED_CALLBACK_H */
index 092a18d..5f2f940 100644 (file)
@@ -5,6 +5,8 @@
  * This software has been released under the terms of the IBM Public
  * License.  For details, see the LICENSE file in the top-level source
  * directory or online at http://www.openafs.org/dl/license10.html
+ *
+ * Portions Copyright (c) 2006 Sine Nomine Associates
  */
 
 #include <afsconfig.h>
@@ -59,7 +61,11 @@ RCSID
 #include "viced_prototypes.h"
 #include "viced.h"
 #include "host.h"
-
+#include "callback.h"
+#ifdef AFS_DEMAND_ATTACH_FS
+#include "../util/afsutil_prototypes.h"
+#include "../tviced/serialize_state.h"
+#endif /* AFS_DEMAND_ATTACH_FS */
 
 #ifdef AFS_PTHREAD_ENV
 pthread_mutex_t host_glock_mutex;
@@ -83,6 +89,13 @@ int hostCount = 0;           /* number of hosts in hostList */
 int rxcon_ident_key;
 int rxcon_client_key;
 
+static struct rx_securityClass *sc = NULL;
+
+static void h_SetupCallbackConn_r(struct host * host);
+static void h_AddHostToHashTable_r(afs_uint32 addr, afs_uint16 port, struct host * host);
+static void h_AddHostToUuidHashTable_r(afsUUID * uuid, struct host * host);
+static int h_DeleteHostFromHashTableByAddr_r(afs_uint32 addr, afs_uint16 port, struct host *host);
+
 #define CESPERBLOCK 73
 struct CEBlock {               /* block of CESPERBLOCK file entries */
     struct client entry[CESPERBLOCK];
@@ -232,9 +245,9 @@ GetHT()
 {
     register struct host *entry;
 
-    if (HTFree == 0)
+    if (HTFree == NULL)
        GetHTBlock();
-    assert(HTFree != 0);
+    assert(HTFree != NULL);
     entry = HTFree;
     HTFree = entry->next;
     HTs++;
@@ -448,7 +461,7 @@ h_gethostcps_r(register struct host *host, register afs_int32 now)
        free(host->hcps.prlist_val);    /* this is for hostaclRefresh */
     host->hcps.prlist_val = NULL;
     host->hcps.prlist_len = 0;
-    slept ? (host->cpsCall = FT_ApproxTime()) : (host->cpsCall = now);
+    host->cpsCall = slept ? (FT_ApproxTime()) : (now);
 
     H_UNLOCK;
     code = pr_GetHostCPS(ntohl(host->host), &host->hcps);
@@ -533,7 +546,6 @@ h_Alloc_r(register struct rx_connection *r_con)
 {
     struct servent *serverentry;
     struct host *host;
-    static struct rx_securityClass *sc = 0;
     afs_int32 now;
 #if FS_STATS_DETAILED
     afs_uint32 newHostAddr_HBO;        /*New host IP addr, in host byte order */
@@ -544,7 +556,7 @@ h_Alloc_r(register struct rx_connection *r_con)
     host->host = rxr_HostOf(r_con);
     host->port = rxr_PortOf(r_con);
 
-    hashInsert_r(host->host, host->port, host);
+    h_AddHostToHashTable_r(host->host, host->port, host);
 
     if (consolePort == 0) {    /* find the portal number for console */
 #if    defined(AFS_OSF_ENV)
@@ -561,24 +573,17 @@ h_Alloc_r(register struct rx_connection *r_con)
        host->Console = 1;
     /* Make a callback channel even for the console, on the off chance that it
      * makes a request that causes a break call back.  It shouldn't. */
-    {
-       if (!sc)
-           sc = rxnull_NewClientSecurityObject();
-       host->callback_rxcon =
-           rx_NewConnection(host->host, host->port, 1, sc, 0);
-       rx_SetConnDeadTime(host->callback_rxcon, 50);
-       rx_SetConnHardDeadTime(host->callback_rxcon, AFS_HARDDEADTIME);
-    }
+    h_SetupCallbackConn_r(host);
     now = host->LastCall = host->cpsCall = host->ActiveCall = FT_ApproxTime();
     host->hostFlags = 0;
     host->hcps.prlist_val = NULL;
     host->hcps.prlist_len = 0;
-    host->interface = 0;
+    host->interface = NULL;
 #ifdef undef
     host->hcpsfailed = 0;      /* save cycles */
     h_gethostcps(host);                /* do this under host hold/lock */
 #endif
-    host->FirstClient = 0;
+    host->FirstClient = NULL;
     h_Hold_r(host);
     h_Lock_r(host);
     h_InsertList_r(host);      /* update global host List */
@@ -596,6 +601,20 @@ h_Alloc_r(register struct rx_connection *r_con)
 }                              /*h_Alloc_r */
 
 
+
+/* Make a callback channel even for the console, on the off chance that it
+ * makes a request that causes a break call back.  It shouldn't. */
+static void
+h_SetupCallbackConn_r(struct host * host)
+{
+    if (!sc)
+       sc = rxnull_NewClientSecurityObject();
+    host->callback_rxcon =
+       rx_NewConnection(host->host, host->port, 1, sc, 0);
+    rx_SetConnDeadTime(host->callback_rxcon, 50);
+    rx_SetConnHardDeadTime(host->callback_rxcon, AFS_HARDDEADTIME);
+}
+
 /* Lookup a host given an IP address and UDP port number. */
 /* hostaddr and hport are in network order */
 /* Note: host should be released by caller if 0 == *heldp and non-null */
@@ -833,7 +852,7 @@ h_FreeConnection(struct rx_connection *tcon)
     if (client) {
        H_LOCK;
        if (client->tcon == tcon)
-           client->tcon = (struct rx_connection *)0;
+           client->tcon = NULL;
        H_UNLOCK;
     }
     return 0;
@@ -878,8 +897,11 @@ h_Enumerate(int (*proc) (), char *param)
     H_UNLOCK;
     for (i = 0; i < count; i++) {
        held[i] = (*proc) (list[i], held[i], param);
-       if (!held[i])
+       if (!H_ENUMERATE_ISSET_HELD(held[i]))
            h_Release(list[i]); /* this might free up the host */
+       /* bail out of the enumeration early */
+       if (H_ENUMERATE_ISSET_BAIL(held[i]))
+           break;
     }
     free((void *)list);
     free((void *)held);
@@ -908,17 +930,19 @@ h_Enumerate_r(int (*proc) (), struct host *enumstart, char *param)
        h_Hold_r(enumstart); 
     for (host = enumstart; host; host = next, held = nheld) {
        next = host->next;
-       if (next && !(nheld = h_Held_r(next)))
+       if (next && !(nheld = h_Held_r(next)) && !H_ENUMERATE_ISSET_BAIL(held))
            h_Hold_r(next);
        held = (*proc) (host, held, param);
-       if (!held)
+       if (!H_ENUMERATE_ISSET_HELD(held))
            h_Release_r(host); /* this might free up the host */
+       if (H_ENUMERATE_ISSET_BAIL(held))
+           break;
     }
 }                              /*h_Enumerate_r */
 
 /* inserts a new HashChain structure corresponding to this UUID */
-void
-hashInsertUuid_r(struct afsUUID *uuid, struct host *host)
+static void
+h_AddHostToUuidHashTable_r(struct afsUUID *uuid, struct host *host)
 {
     int index;
     struct h_hashChain *chain;
@@ -929,7 +953,7 @@ hashInsertUuid_r(struct afsUUID *uuid, struct host *host)
     /* insert into beginning of list for this bucket */
     chain = (struct h_hashChain *)malloc(sizeof(struct h_hashChain));
     if (!chain) {
-       ViceLog(0, ("Failed malloc in hashInsertUuid_r\n"));
+       ViceLog(0, ("Failed malloc in h_AddHostToUuidHashTable_r\n"));
        assert(0);
     }
     assert(chain);
@@ -940,8 +964,8 @@ hashInsertUuid_r(struct afsUUID *uuid, struct host *host)
 
 
 /* inserts a new HashChain structure corresponding to this address */
-void
-hashInsert_r(afs_uint32 addr, afs_uint16 port, struct host *host)
+static void
+h_AddHostToHashTable_r(afs_uint32 addr, afs_uint16 port, struct host *host)
 {
     int index;
     struct h_hashChain *chain;
@@ -952,7 +976,7 @@ hashInsert_r(afs_uint32 addr, afs_uint16 port, struct host *host)
     /* insert into beginning of list for this bucket */
     chain = (struct h_hashChain *)malloc(sizeof(struct h_hashChain));
     if (!chain) {
-       ViceLog(0, ("Failed malloc in hashInsert_r\n"));
+       ViceLog(0, ("Failed malloc in h_AddHostToHashTable_r\n"));
        assert(0);
     }
     chain->hostPtr = host;
@@ -1017,7 +1041,7 @@ addInterfaceAddr_r(struct host *host, afs_uint32 addr, afs_uint16 port)
     /*
      * Create a hash table entry for this address
      */
-    hashInsert_r(addr, port, host);
+    h_AddHostToHashTable_r(addr, port, host);
 
     return 0;
 }
@@ -1072,7 +1096,7 @@ removeInterfaceAddr_r(struct host *host, afs_uint32 addr, afs_uint16 port)
     /*
      * Remove the hash table entry for this address
      */
-    hashDelete_r(addr, port, host);
+    h_DeleteHostFromHashTableByAddr_r(addr, port, host);
 
     return 0;
 }
@@ -1394,7 +1418,7 @@ h_GetHost_r(struct rx_connection *tcon)
                    /* the new host is held and locked */
                } else {
                    /* This really is a new host */
-                   hashInsertUuid_r(&identP->uuid, host);
+                   h_AddHostToUuidHashTable_r(&identP->uuid, host);
                    cb_conn = host->callback_rxcon;
                    rx_GetConnection(cb_conn);          
                    H_UNLOCK;
@@ -1735,7 +1759,7 @@ h_FindClient_r(struct rx_connection *tcon)
            client->authClass = authClass;      /* rx only */
            client->sid = rxr_CidOf(tcon);
            client->VenusEpoch = rxr_GetEpoch(tcon);
-           client->CPS.prlist_val = 0;
+           client->CPS.prlist_val = NULL;
            client->CPS.prlist_len = 0;
            h_Unlock_r(host);
        }
@@ -2134,6 +2158,540 @@ h_DumpHosts()
 
 }                              /*h_DumpHosts */
 
+#ifdef AFS_DEMAND_ATTACH_FS
+/*
+ * demand attach fs
+ * host state serialization
+ */
+static int h_stateFillHeader(struct host_state_header * hdr);
+static int h_stateCheckHeader(struct host_state_header * hdr);
+static int h_stateAllocMap(struct fs_dump_state * state);
+static int h_stateSaveHost(register struct host * host, int held, struct fs_dump_state * state);
+static int h_stateRestoreHost(struct fs_dump_state * state);
+static int h_stateRestoreIndex(struct host * h, int held, struct fs_dump_state * state);
+static int h_stateVerifyHost(struct host * h, int held, struct fs_dump_state * state);
+static int h_stateVerifyAddrHash(struct fs_dump_state * state, struct host * h, afs_uint32 addr, afs_uint16 port);
+static int h_stateVerifyUuidHash(struct fs_dump_state * state, struct host * h);
+static void h_hostToDiskEntry_r(struct host * in, struct hostDiskEntry * out);
+static void h_diskEntryToHost_r(struct hostDiskEntry * in, struct host * out);
+
+
+/* this procedure saves all host state to disk for fast startup */
+int
+h_stateSave(struct fs_dump_state * state)
+{
+    AssignInt64(state->eof_offset, &state->hdr->h_offset);
+
+    /* XXX debug */
+    ViceLog(0, ("h_stateSave:  hostCount=%d\n", hostCount));
+
+    /* invalidate host state header */
+    memset(state->h_hdr, 0, sizeof(struct host_state_header));
+
+    if (fs_stateWriteHeader(state, &state->hdr->h_offset, state->h_hdr,
+                           sizeof(struct host_state_header))) {
+       state->bail = 1;
+       goto done;
+    }
+
+    fs_stateIncEOF(state, sizeof(struct host_state_header));
+
+    h_Enumerate_r(h_stateSaveHost, hostList, (char *)state);
+    if (state->bail) {
+       goto done;
+    }
+
+    h_stateFillHeader(state->h_hdr);
+
+    /* write the real header to disk */
+    state->bail = fs_stateWriteHeader(state, &state->hdr->h_offset, state->h_hdr,
+                                     sizeof(struct host_state_header));
+
+ done:
+    return state->bail;
+}
+
+/* demand attach fs
+ * host state serialization
+ *
+ * this procedure restores all host state from a disk for fast startup 
+ */
+int
+h_stateRestore(struct fs_dump_state * state)
+{
+    int i, records;
+
+    /* seek to the right position and read in the host state header */
+    if (fs_stateReadHeader(state, &state->hdr->h_offset, state->h_hdr,
+                          sizeof(struct host_state_header))) {
+       state->bail = 1;
+       goto done;
+    }
+
+    /* check the validity of the header */
+    if (h_stateCheckHeader(state->h_hdr)) {
+       state->bail = 1;
+       goto done;
+    }
+
+    records = state->h_hdr->records;
+
+    if (h_stateAllocMap(state)) {
+       state->bail = 1;
+       goto done;
+    }
+
+    /* iterate over records restoring host state */
+    for (i=0; i < records; i++) {
+       if (h_stateRestoreHost(state) != 0) {
+           state->bail = 1;
+           break;
+       }
+    }
+
+ done:
+    return state->bail;
+}
+
+int
+h_stateRestoreIndices(struct fs_dump_state * state)
+{
+    h_Enumerate_r(h_stateRestoreIndex, hostList, (char *)state);
+    return state->bail;
+}
+
+static int
+h_stateRestoreIndex(struct host * h, int held, struct fs_dump_state * state)
+{
+    if (cb_OldToNew(state, h->cblist, &h->cblist)) {
+       return H_ENUMERATE_BAIL(held);
+    }
+    return held;
+}
+
+int
+h_stateVerify(struct fs_dump_state * state)
+{
+    h_Enumerate_r(h_stateVerifyHost, hostList, (char *)state);
+    return state->bail;
+}
+
+static int
+h_stateVerifyHost(struct host * h, int held, struct fs_dump_state * state)
+{
+    int i;
+
+    if (h == NULL) {
+       ViceLog(0, ("h_stateVerifyHost: error: NULL host pointer in linked list\n"));
+       return H_ENUMERATE_BAIL(held);
+    }
+
+    if (h->interface) {
+       for (i = h->interface->numberOfInterfaces-1; i >= 0; i--) {
+           if (h_stateVerifyAddrHash(state, h, h->interface->interface[i].addr, 
+                                     h->interface->interface[i].port)) {
+               state->bail = 1;
+           }
+       }
+       if (h_stateVerifyUuidHash(state, h)) {
+           state->bail = 1;
+       }
+    } else if (h_stateVerifyAddrHash(state, h, h->host, h->port)) {
+       state->bail = 1;
+    }
+
+    if (cb_stateVerifyHCBList(state, h)) {
+       state->bail = 1;
+    }
+
+ done:
+    return held;
+}
+
+static int
+h_stateVerifyAddrHash(struct fs_dump_state * state, struct host * h, afs_uint32 addr, afs_uint16 port)
+{
+    int ret = 0, found = 0;
+    struct host *host = NULL;
+    struct h_hashChain *chain;
+    int index = h_HashIndex(addr);
+    char tmp[16];
+    int chain_len = 0;
+
+    for (chain = hostHashTable[index]; chain; chain = chain->next) {
+       host = chain->hostPtr;
+       if (host == NULL) {
+           afs_inet_ntoa_r(addr, tmp);
+           ViceLog(0, ("h_stateVerifyAddrHash: error: addr hash chain has NULL host ptr (lookup addr %s)\n", tmp));
+           ret = 1;
+           goto done;
+       }
+       if ((chain->addr == addr) && (chain->port == port)) {
+           if (host != h) {
+               ViceLog(0, ("h_stateVerifyAddrHash: warning: addr hash entry points to different host struct (%d, %d)\n", 
+                           h->index, host->index));
+               state->flags.warnings_generated = 1;
+           }
+           found = 1;
+           break;
+       }
+       if (chain_len > FS_STATE_H_MAX_ADDR_HASH_CHAIN_LEN) {
+           ViceLog(0, ("h_stateVerifyAddrHash: error: hash chain length exceeds %d; assuming there's a loop\n",
+                       FS_STATE_H_MAX_ADDR_HASH_CHAIN_LEN));
+           ret = 1;
+           goto done;
+       }
+       chain_len++;
+    }
+
+    if (!found) {
+       afs_inet_ntoa_r(addr, tmp);
+       if (state->mode == FS_STATE_LOAD_MODE) {
+           ViceLog(0, ("h_stateVerifyAddrHash: error: addr %s not found in hash\n", tmp));
+           ret = 1;
+           goto done;
+       } else {
+           ViceLog(0, ("h_stateVerifyAddrHash: warning: addr %s not found in hash\n", tmp));
+           state->flags.warnings_generated = 1;
+       }
+    }
+
+ done:
+    return ret;
+}
+
+static int
+h_stateVerifyUuidHash(struct fs_dump_state * state, struct host * h)
+{
+    int ret = 0, found = 0;
+    struct host *host = NULL;
+    struct h_hashChain *chain;
+    afsUUID * uuidp = &h->interface->uuid;
+    int index = h_UuidHashIndex(uuidp);
+    char tmp[40];
+    int chain_len = 0;
+
+    for (chain = hostUuidHashTable[index]; chain; chain = chain->next) {
+       host = chain->hostPtr;
+       if (host == NULL) {
+           afsUUID_to_string(uuidp, tmp, sizeof(tmp));
+           ViceLog(0, ("h_stateVerifyUuidHash: error: uuid hash chain has NULL host ptr (lookup uuid %s)\n", tmp));
+           ret = 1;
+           goto done;
+       }
+       if (host->interface &&
+           afs_uuid_equal(&host->interface->uuid, uuidp)) {
+           if (host != h) {
+               ViceLog(0, ("h_stateVerifyUuidHash: warning: uuid hash entry points to different host struct (%d, %d)\n", 
+                           h->index, host->index));
+               state->flags.warnings_generated = 1;
+           }
+           found = 1;
+           goto done;
+       }
+       if (chain_len > FS_STATE_H_MAX_UUID_HASH_CHAIN_LEN) {
+           ViceLog(0, ("h_stateVerifyUuidHash: error: hash chain length exceeds %d; assuming there's a loop\n",
+                       FS_STATE_H_MAX_UUID_HASH_CHAIN_LEN));
+           ret = 1;
+           goto done;
+       }
+       chain_len++;
+    }
+
+    if (!found) {
+       afsUUID_to_string(uuidp, tmp, sizeof(tmp));
+       if (state->mode == FS_STATE_LOAD_MODE) {
+           ViceLog(0, ("h_stateVerifyUuidHash: error: uuid %s not found in hash\n", tmp));
+           ret = 1;
+           goto done;
+       } else {
+           ViceLog(0, ("h_stateVerifyUuidHash: warning: uuid %s not found in hash\n", tmp));
+           state->flags.warnings_generated = 1;
+       }
+    }
+
+ done:
+    return ret;
+}
+
+/* create the host state header structure */
+static int
+h_stateFillHeader(struct host_state_header * hdr)
+{
+    hdr->stamp.magic = HOST_STATE_MAGIC;
+    hdr->stamp.version = HOST_STATE_VERSION;
+}
+
+/* check the contents of the host state header structure */
+static int
+h_stateCheckHeader(struct host_state_header * hdr)
+{
+    int ret=0;
+
+    if (hdr->stamp.magic != HOST_STATE_MAGIC) {
+       ViceLog(0, ("check_host_state_header: invalid state header\n"));
+       ret = 1;
+    }
+    else if (hdr->stamp.version != HOST_STATE_VERSION) {
+       ViceLog(0, ("check_host_state_header: unknown version number\n"));
+       ret = 1;
+    }
+    return ret;
+}
+
+/* allocate the host id mapping table */
+static int
+h_stateAllocMap(struct fs_dump_state * state)
+{
+    state->h_map.len = state->h_hdr->index_max + 1;
+    state->h_map.entries = (struct idx_map_entry_t *)
+       calloc(state->h_map.len, sizeof(struct idx_map_entry_t));
+    return (state->h_map.entries != NULL) ? 0 : 1;
+}
+
+/* function called by h_Enumerate to save a host to disk */
+static int
+h_stateSaveHost(register struct host * host, int held, struct fs_dump_state * state)
+{
+    int i, if_len=0, hcps_len=0;
+    struct hostDiskEntry hdsk;
+    struct host_state_entry_header hdr;
+    struct Interface * ifp = NULL;
+    afs_int32 * hcps = NULL;
+    struct iovec iov[4];
+    int iovcnt = 2;
+
+    memset(&hdr, 0, sizeof(hdr));
+
+    if (state->h_hdr->index_max < host->index) {
+       state->h_hdr->index_max = host->index;
+    }
+
+    h_hostToDiskEntry_r(host, &hdsk);
+    if (host->interface) {
+       if_len = sizeof(struct Interface) + 
+           ((host->interface->numberOfInterfaces-1) * sizeof(struct AddrPort));
+       ifp = (struct Interface *) malloc(if_len);
+       assert(ifp != NULL);
+       memcpy(ifp, host->interface, if_len);
+       hdr.interfaces = host->interface->numberOfInterfaces;
+       iov[iovcnt].iov_base = (char *) ifp;
+       iov[iovcnt].iov_len = if_len;
+       iovcnt++;
+    }
+    if (host->hcps.prlist_val) {
+       hdr.hcps = host->hcps.prlist_len;
+       hcps_len = hdr.hcps * sizeof(afs_int32);
+       hcps = (afs_int32 *) malloc(hcps_len);
+       assert(hcps != NULL);
+       memcpy(hcps, host->hcps.prlist_val, hcps_len);
+       iov[iovcnt].iov_base = (char *) hcps;
+       iov[iovcnt].iov_len = hcps_len;
+       iovcnt++;
+    }
+
+    if (hdsk.index > state->h_hdr->index_max)
+       state->h_hdr->index_max = hdsk.index;
+
+    hdr.len = sizeof(struct host_state_entry_header) + 
+       sizeof(struct hostDiskEntry) + if_len + hcps_len;
+    hdr.magic = HOST_STATE_ENTRY_MAGIC;
+
+    iov[0].iov_base = (char *) &hdr;
+    iov[0].iov_len = sizeof(hdr);
+    iov[1].iov_base = (char *) &hdsk;
+    iov[1].iov_len = sizeof(struct hostDiskEntry);
+    
+    if (fs_stateWriteV(state, iov, iovcnt)) {
+       ViceLog(0, ("h_stateSaveHost: failed to save host %d", host->index));
+       state->bail = 1;
+    }
+
+    fs_stateIncEOF(state, hdr.len);
+
+    state->h_hdr->records++;
+
+ done:
+    if (ifp)
+       free(ifp);
+    if (hcps)
+       free(hcps);
+    if (state->bail) {
+       return H_ENUMERATE_BAIL(held);
+    }
+    return held;
+}
+
+/* restores a host from disk */
+static int
+h_stateRestoreHost(struct fs_dump_state * state)
+{
+    int ifp_len=0, hcps_len=0, bail=0;
+    struct host_state_entry_header hdr;
+    struct hostDiskEntry hdsk;
+    struct host *host = NULL;
+    struct Interface *ifp = NULL;
+    afs_int32 * hcps = NULL;
+    struct iovec iov[3];
+    int iovcnt = 1;
+
+    if (fs_stateRead(state, &hdr, sizeof(hdr))) {
+       ViceLog(0, ("h_stateRestoreHost: failed to read host entry header from dump file '%s'\n",
+                   state->fn));
+       bail = 1;
+       goto done;
+    }
+
+    if (hdr.magic != HOST_STATE_ENTRY_MAGIC) {
+       ViceLog(0, ("h_stateRestoreHost: fileserver state dump file '%s' is corrupt.\n",
+                   state->fn));
+       bail = 1;
+       goto done;
+    }
+
+    iov[0].iov_base = (char *) &hdsk;
+    iov[0].iov_len = sizeof(struct hostDiskEntry);
+
+    if (hdr.interfaces) {
+       ifp_len = sizeof(struct Interface) +
+           ((hdr.interfaces-1) * sizeof(struct AddrPort));
+       ifp = (struct Interface *) malloc(ifp_len);
+       assert(ifp != NULL);
+       iov[iovcnt].iov_base = (char *) ifp;
+       iov[iovcnt].iov_len = ifp_len;
+       iovcnt++;
+    }
+    if (hdr.hcps) {
+       hcps_len = hdr.hcps * sizeof(afs_int32);
+       hcps = (afs_int32 *) malloc(hcps_len);
+       assert(hcps != NULL);
+       iov[iovcnt].iov_base = (char *) hcps;
+       iov[iovcnt].iov_len = hcps_len;
+       iovcnt++;
+    }
+
+    if ((ifp_len + hcps_len + sizeof(hdsk) + sizeof(hdr)) != hdr.len) {
+       ViceLog(0, ("h_stateRestoreHost: host entry header length fields are inconsistent\n"));
+       bail = 1;
+       goto done;
+    }
+
+    if (fs_stateReadV(state, iov, iovcnt)) {
+       ViceLog(0, ("h_stateRestoreHost: failed to read host entry\n"));
+       bail = 1;
+       goto done;
+    }
+
+    if (!hdr.hcps && hdsk.hcps_valid) {
+       /* valid, zero-length host cps ; does this ever happen? */
+       hcps = (afs_int32 *) malloc(sizeof(afs_int32));
+       assert(hcps != NULL);
+    }
+
+    host = GetHT();
+    assert(host != NULL);
+
+    if (ifp) {
+       host->interface = ifp;
+    }
+    if (hcps) {
+       host->hcps.prlist_val = hcps;
+       host->hcps.prlist_len = hdr.hcps;
+    }
+
+    h_diskEntryToHost_r(&hdsk, host);
+    h_SetupCallbackConn_r(host);
+
+    if (ifp) {
+       int i;
+       for (i = ifp->numberOfInterfaces-1; i >= 0; i--) {
+           h_AddHostToHashTable_r(ifp->interface[i].addr, 
+                                  ifp->interface[i].port, host);
+       }
+       h_AddHostToUuidHashTable_r(&ifp->uuid, host);
+    } else {
+       h_AddHostToHashTable_r(host->host, host->port, host);
+    }
+    h_InsertList_r(host);
+
+    /* setup host id map entry */
+    state->h_map.entries[hdsk.index].old_idx = hdsk.index;
+    state->h_map.entries[hdsk.index].new_idx = host->index;
+
+ done:
+    if (bail) {
+       if (ifp)
+           free(ifp);
+       if (hcps)
+           free(hcps);
+    }
+    return bail;
+}
+
+/* serialize a host structure to disk */
+static void
+h_hostToDiskEntry_r(struct host * in, struct hostDiskEntry * out)
+{
+    out->host = in->host;
+    out->port = in->port;
+    out->hostFlags = in->hostFlags;
+    out->Console = in->Console;
+    out->hcpsfailed = in->hcpsfailed;
+    out->LastCall = in->LastCall;
+    out->ActiveCall = in->ActiveCall;
+    out->cpsCall = in->cpsCall;
+    out->cblist = in->cblist;
+#ifdef FS_STATS_DETAILED
+    out->InSameNetwork = in->InSameNetwork;
+#endif
+
+    /* special fields we save, but are not memcpy'd back on restore */
+    out->index = in->index;
+    out->hcps_len = in->hcps.prlist_len;
+    out->hcps_valid = (in->hcps.prlist_val == NULL) ? 0 : 1;
+}
+
+/* restore a host structure from disk */
+static void
+h_diskEntryToHost_r(struct hostDiskEntry * in, struct host * out)
+{
+    out->host = in->host;
+    out->port = in->port;
+    out->hostFlags = in->hostFlags;
+    out->Console = in->Console;
+    out->hcpsfailed = in->hcpsfailed;
+    out->LastCall = in->LastCall;
+    out->ActiveCall = in->ActiveCall;
+    out->cpsCall = in->cpsCall;
+    out->cblist = in->cblist;
+#ifdef FS_STATS_DETAILED
+    out->InSameNetwork = in->InSameNetwork;
+#endif
+}
+
+/* index translation routines */
+int
+h_OldToNew(struct fs_dump_state * state, afs_uint32 old, afs_uint32 * new)
+{
+    int ret = 0;
+
+    /* hosts use a zero-based index, so old==0 is valid */
+
+    if (old >= state->h_map.len) {
+       ViceLog(0, ("h_OldToNew: index %d is out of range\n", old));
+       ret = 1;
+    } else if (state->h_map.entries[old].old_idx != old) { /* sanity check */
+       ViceLog(0, ("h_OldToNew: index %d points to an invalid host record\n", old));
+       ret = 1;
+    } else {
+       *new = state->h_map.entries[old].new_idx;
+    }
+
+ done:
+    return ret;
+}
+#endif /* AFS_DEMAND_ATTACH_FS */
+
 
 /*
  * This counts the number of workstations, the number of active workstations,
@@ -2348,13 +2906,23 @@ static struct AFSFid zerofid;
  * Since it can serialize them, and pile up, it should be a separate LWP
  * from other events.
  */
-int
+static int
 CheckHost(register struct host *host, int held)
 {
     register struct client *client;
     struct rx_connection *cb_conn = NULL;
     int code;
 
+#ifdef AFS_DEMAND_ATTACH_FS
+    /* kill the checkhost lwp ASAP during shutdown */
+    FS_STATE_RDLOCK;
+    if (fs_state.mode == FS_MODE_SHUTDOWN) {
+       FS_STATE_UNLOCK;
+       return H_ENUMERATE_BAIL(held);
+    }
+    FS_STATE_UNLOCK;
+#endif
+
     /* Host is held by h_Enumerate */
     H_LOCK;
     for (client = host->FirstClient; client; client = client->next) {
@@ -2455,7 +3023,7 @@ CheckHost(register struct host *host, int held)
  * This routine is called roughly every 5 minutes.
  */
 void
-h_CheckHosts()
+h_CheckHosts(void)
 {
     afs_uint32 now = FT_ApproxTime();
 
@@ -2570,7 +3138,7 @@ initInterfaceAddr_r(struct host *host, struct interfaceAddr *interf)
 /* deleted a HashChain structure for this address and host */
 /* returns 1 on success */
 static int
-hashDelete_r(afs_uint32 addr, afs_uint16 port, struct host *host)
+h_DeleteHostFromHashTableByAddr_r(afs_uint32 addr, afs_uint16 port, struct host *host)
 {
     int flag;
     register struct h_hashChain **hp, *th;
index bd17cfd..60df3bc 100644 (file)
@@ -5,8 +5,13 @@
  * This software has been released under the terms of the IBM Public
  * License.  For details, see the LICENSE file in the top-level source
  * directory or online at http://www.openafs.org/dl/license10.html
+ *
+ * Portions Copyright (c) 2006 Sine Nomine Associates
  */
 
+#ifndef _AFS_VICED_HOST_H
+#define _AFS_VICED_HOST_H
+
 #include "fs_stats.h"          /*File Server stats package */
 
 #ifdef AFS_PTHREAD_ENV
@@ -59,6 +64,7 @@ struct Interface {
     struct AddrPort interface[1];/* there are actually more than one here */
     /* in network byte order */
 };
+
 struct host {
     struct host *next, *prev;  /* linked list of all hosts */
     struct rx_connection *callback_rxcon;      /* rx callback connection */
@@ -85,7 +91,7 @@ struct host {
     struct client *FirstClient;        /* first connection from host */
     afs_uint32 cpsCall;                /* time of last cps call from this host */
     struct Interface *interface;       /* all alternate addr for client */
-    afs_uint32 cblist;         /* Call back list for this host */
+    afs_uint32 cblist;         /* index of a cb in the per-host circular CB list */
     /*
      * These don't get zeroed, keep them at the end. If index doesn't
      * follow an unsigned short then we need to pad to ensure that
@@ -142,6 +148,7 @@ struct client {
 /* Don't zero the lock */
 #define CLIENT_TO_ZERO(C)      ((int)(((char *)(&((C)->lock))-(char *)(C))))
 
+
 /*
  * key for the client structure stored in connection specific data
  */
@@ -245,6 +252,19 @@ extern void h_CheckHosts();
 struct Interface *MultiVerifyInterface_r();
 extern int initInterfaceAddr_r(struct host *host, struct interfaceAddr *interf);
 
+#ifdef AFS_DEMAND_ATTACH_FS
+/*
+ * demand attach fs
+ * state serialization
+ */
+extern int h_SaveState(void);
+extern int h_RestoreState(void);
+#endif
+
+#define H_ENUMERATE_BAIL(held)        ((held)|0x80000000)
+#define H_ENUMERATE_ISSET_BAIL(held)  ((held)&0x80000000)
+#define H_ENUMERATE_ISSET_HELD(held)  ((held)&0x7FFFFFFF)
+
 struct host *(hosttableptrs[h_MAXHOSTTABLES]); /* Used by h_itoh */
 #define h_htoi(host) ((host)->index)   /* index isn't zeroed, no need to lock */
 #define h_itoh(hostindex) (hosttableptrs[(hostindex)>>h_HTSHIFT]+((hostindex)&(h_HTSPERBLOCK-1)))
@@ -269,4 +289,4 @@ struct host *(hosttableptrs[h_MAXHOSTTABLES]);      /* Used by h_itoh */
 #define HFE_LATER                       0x80   /* host has FE_LATER callbacks */
 #define HERRORTRANS                    0x100   /* do error translation */
 
-
+#endif /* _AFS_VICED_HOST_H */
index 1202d93..1c7296b 100644 (file)
@@ -5,6 +5,8 @@
  * This software has been released under the terms of the IBM Public
  * License.  For details, see the LICENSE file in the top-level source
  * directory or online at http://www.openafs.org/dl/license10.html
+ *
+ * Portions Copyright (c) 2006 Sine Nomine Associates
  */
 
 /*  viced.c    - File Server main loop                                  */
@@ -215,6 +217,27 @@ afsUUID FS_HostUUID;
 
 static void FlagMsg();
 
+#ifdef AFS_DEMAND_ATTACH_FS
+/*
+ * demand attach fs
+ * fileserver mode support
+ *
+ * during fileserver shutdown, we have to track the graceful shutdown of
+ * certain background threads before we are allowed to dump state to
+ * disk
+ */
+struct fs_state fs_state = 
+    { FS_MODE_NORMAL, 
+      0, 
+      0, 
+      0, 
+      0,
+      { 1,1,1,1 },
+      PTHREAD_COND_INITIALIZER,
+      PTHREAD_RWLOCK_INITIALIZER
+    };
+#endif /* AFS_DEMAND_ATTACH_FS */
+
 /*
  * Home for the performance statistics.
  */
@@ -420,13 +443,31 @@ FiveMinuteCheckLWP()
 
     ViceLog(1, ("Starting five minute check process\n"));
     setThreadId("FiveMinuteCheckLWP");
+
+#ifdef AFS_DEMAND_ATTACH_FS
+    FS_STATE_WRLOCK;
+    while (fs_state.mode == FS_MODE_NORMAL) {
+       fs_state.FiveMinuteLWP_tranquil = 1;
+       FS_STATE_UNLOCK;
+#else
     while (1) {
+#endif
+
 #ifdef AFS_PTHREAD_ENV
        sleep(fiveminutes);
 #else /* AFS_PTHREAD_ENV */
        IOMGR_Sleep(fiveminutes);
 #endif /* AFS_PTHREAD_ENV */
 
+#ifdef AFS_DEMAND_ATTACH_FS
+       FS_STATE_WRLOCK;
+       if (fs_state.mode != FS_MODE_NORMAL) {
+           break;
+       }
+       fs_state.FiveMinuteLWP_tranquil = 0;
+       FS_STATE_UNLOCK;
+#endif
+
        /* close the log so it can be removed */
        ReOpenLog(AFSDIR_SERVER_FILELOG_FILEPATH);      /* don't trunc, just append */
        ViceLog(2, ("Cleaning up timed out callbacks\n"));
@@ -452,7 +493,17 @@ FiveMinuteCheckLWP()
                         afs_ctime(&now, tbuffer, sizeof(tbuffer))));
            }
        }
+#ifdef AFS_DEMAND_ATTACH_FS
+       FS_STATE_WRLOCK;
+#endif
     }
+#ifdef AFS_DEMAND_ATTACH_FS
+    fs_state.FiveMinuteLWP_tranquil = 1;
+    FS_LOCK;
+    assert(pthread_cond_broadcast(&fs_state.worker_done_cv)==0);
+    FS_UNLOCK;
+    FS_STATE_UNLOCK;
+#endif
 }                              /*FiveMinuteCheckLWP */
 
 
@@ -460,20 +511,50 @@ FiveMinuteCheckLWP()
  * other 5 minute activities because it may be delayed by timeouts when
  * it probes the workstations
  */
+
 static void
 HostCheckLWP()
 {
     ViceLog(1, ("Starting Host check process\n"));
     setThreadId("HostCheckLWP");
-    while (1) {
+#ifdef AFS_DEMAND_ATTACH_FS
+    FS_STATE_WRLOCK;
+    while (fs_state.mode == FS_MODE_NORMAL) {
+       fs_state.HostCheckLWP_tranquil = 1;
+       FS_STATE_UNLOCK;
+#else
+    while(1) {
+#endif
+
 #ifdef AFS_PTHREAD_ENV
        sleep(fiveminutes);
 #else /* AFS_PTHREAD_ENV */
        IOMGR_Sleep(fiveminutes);
 #endif /* AFS_PTHREAD_ENV */
+
+#ifdef AFS_DEMAND_ATTACH_FS
+       FS_STATE_WRLOCK;
+       if (fs_state.mode != FS_MODE_NORMAL) {
+           break;
+       }
+       fs_state.HostCheckLWP_tranquil = 0;
+       FS_STATE_UNLOCK;
+#endif
+
        ViceLog(2, ("Checking for dead venii & clients\n"));
        h_CheckHosts();
+
+#ifdef AFS_DEMAND_ATTACH_FS
+       FS_STATE_WRLOCK;
+#endif
     }
+#ifdef AFS_DEMAND_ATTACH_FS
+    fs_state.HostCheckLWP_tranquil = 1;
+    FS_LOCK;
+    assert(pthread_cond_broadcast(&fs_state.worker_done_cv)==0);
+    FS_UNLOCK;
+    FS_STATE_UNLOCK;
+#endif
 }                              /*HostCheckLWP */
 
 /* This LWP does fsync checks every 5 minutes:  it should not be used for
@@ -496,7 +577,14 @@ FsyncCheckLWP()
     assert(pthread_mutex_init(&fsync_glock_mutex, NULL) == 0);
 #endif
 
-    while (1) {
+#ifdef AFS_DEMAND_ATTACH_FS
+    FS_STATE_WRLOCK;
+    while (fs_state.mode == FS_MODE_NORMAL) {
+       fs_state.FsyncCheckLWP_tranquil = 1;
+       FS_STATE_UNLOCK;
+#else
+    while(1) {
+#endif
        FSYNC_LOCK;
 #ifdef AFS_PTHREAD_ENV
        /* rounding is fine */
@@ -513,11 +601,31 @@ FsyncCheckLWP()
            ViceLog(0, ("LWP_WaitProcess returned %d\n", code));
 #endif /* AFS_PTHREAD_ENV */
        FSYNC_UNLOCK;
+
+#ifdef AFS_DEMAND_ATTACH_FS
+       FS_STATE_WRLOCK;
+       if (fs_state.mode != FS_MODE_NORMAL) {
+           break;
+       }
+       fs_state.FsyncCheckLWP_tranquil = 0;
+       FS_STATE_UNLOCK;
+#endif /* AFS_DEMAND_ATTACH_FS */
+
        ViceLog(2, ("Checking for fsync events\n"));
        do {
            code = BreakLaterCallBacks();
        } while (code != 0);
+#ifdef AFS_DEMAND_ATTACH_FS
+       FS_STATE_WRLOCK;
+#endif
     }
+#ifdef AFS_DEMAND_ATTACH_FS
+    fs_state.FsyncCheckLWP_tranquil = 1;
+    FS_LOCK;
+    assert(pthread_cond_broadcast(&fs_state.worker_done_cv)==0);
+    FS_UNLOCK;
+    FS_STATE_UNLOCK;
+#endif /* AFS_DEMAND_ATTACH_FS */
 }
 
 /*------------------------------------------------------------------------
@@ -604,6 +712,11 @@ PrintCounters()
            ("Vice was last started at %s\n",
             afs_ctime(&StartTime, tbuffer, sizeof(tbuffer))));
 
+#ifdef AFS_DEMAND_ATTACH_FS
+    /* XXX perhaps set extended stats verbosity flags
+     * based upon LogLevel ?? */
+    VPrintExtendedCacheStats(VOL_STATS_PER_CHAIN2);
+#endif
     VPrintCacheStats();
     VPrintDiskStats();
     DStat(&dirbuff, &dircall, &dirio);
@@ -656,6 +769,16 @@ ShutDownAndCore(int dopanic)
     time_t now = time(0);
     char tbuffer[32];
 
+    /* do not allows new reqests to be served from now on, all new requests
+     * are returned with an error code of RX_RESTARTING ( transient failure ) */
+    rx_SetRxTranquil();                /* dhruba */
+
+#ifdef AFS_DEMAND_ATTACH_FS
+    FS_STATE_WRLOCK;
+    fs_state.mode = FS_MODE_SHUTDOWN;
+    FS_STATE_UNLOCK;
+#endif
+
     ViceLog(0,
            ("Shutting down file server at %s",
             afs_ctime(&now, tbuffer, sizeof(tbuffer))));
@@ -671,11 +794,34 @@ ShutDownAndCore(int dopanic)
     if (!dopanic)
        PrintCounters();
 
-    /* do not allows new reqests to be served from now on, all new requests
-     * are returned with an error code of RX_RESTARTING ( transient failure ) */
-    rx_SetRxTranquil();                /* dhruba */
+    /* shut down volume package */
     VShutdown();
 
+#ifdef AFS_DEMAND_ATTACH_FS
+    if (fs_state.options.fs_state_save) {
+       /* 
+        * demand attach fs
+        * save fileserver state to disk */
+
+       /* make sure background threads have finished all of their asynchronous 
+        * work on host and callback structures */
+       FS_STATE_RDLOCK;
+       while (!fs_state.FiveMinuteLWP_tranquil ||
+              !fs_state.HostCheckLWP_tranquil ||
+              !fs_state.FsyncCheckLWP_tranquil) {
+           FS_LOCK;
+           FS_STATE_UNLOCK;
+           ViceLog(0, ("waiting for background host/callback threads to quiesce before saving fileserver state...\n"));
+           assert(pthread_cond_wait(&fs_state.worker_done_cv, &fileproc_glock_mutex) == 0);
+           FS_UNLOCK;
+           FS_STATE_RDLOCK;
+       }
+
+       /* ok. it should now be fairly safe. let's do the state dump */
+       fs_stateSave();
+    }
+#endif /* AFS_DEMAND_ATTACH_FS */
+
     if (debugFile) {
        rx_PrintStats(debugFile);
        fflush(debugFile);
@@ -715,7 +861,7 @@ ShutDown(void)
 static void
 FlagMsg()
 {
-    char buffer[1024];
+    char buffer[2048];
 
     /* default supports help flag */
 
@@ -743,8 +889,18 @@ FlagMsg()
     strcat(buffer, "[-rxdbg (enable rx debugging)] ");
     strcat(buffer, "[-rxdbge (enable rxevent debugging)] ");
     strcat(buffer, "[-rxmaxmtu <bytes>] ");
-#if AFS_PTHREAD_ENV
-    strcat(buffer, "[-vattachpar <number of volume attach threads>] ");
+#ifdef AFS_DEMAND_ATTACH_FS
+    strcat(buffer, "[-fs-state-dont-save (disable state save during shutdown)] ");
+    strcat(buffer, "[-fs-state-dont-restore (disable state restore during startup)] ");
+    strcat(buffer, "[-fs-state-verify <none|save|restore|both> (default is both)] ");
+    strcat(buffer, "[-vattachpar <max number of volume attach/shutdown threads> (default is 1)] ");
+    strcat(buffer, "[-vhashsize <log(2) of number of volume hash buckets> (default is 8)] ");
+    strcat(buffer, "[-vlrudisable (disable VLRU functionality)] ");
+    strcat(buffer, "[-vlruthresh <minutes before unused volumes become eligible for soft detach> (default is 2 hours)] ");
+    strcat(buffer, "[-vlruinterval <seconds between VLRU scans> (default is 2 minutes)] ");
+    strcat(buffer, "[-vlrumax <max volumes to soft detach in one VLRU scan> (default is 8)] ");
+#elif AFS_PTHREAD_ENV
+    strcat(buffer, "[-vattachpar <number of volume attach threads> (default is 1)] ");
 #endif
 #ifdef AFS_AIX32_ENV
     strcat(buffer, "[-m <min percentage spare in partition>] ");
@@ -945,11 +1101,62 @@ ParseArgs(int argc, char *argv[])
 #ifdef AFS_PTHREAD_ENV
        } else if (!strcmp(argv[i], "-vattachpar")) {
             if ((i + 1) >= argc) {
-               fprintf(stderr, "missing argument for -vattachpar\n"); 
+               fprintf(stderr, "missing argument for %s\n", argv[i]); 
                return -1; 
            }
            vol_attach_threads = atoi(argv[++i]);
 #endif /* AFS_PTHREAD_ENV */
+#ifdef AFS_DEMAND_ATTACH_FS
+       } else if (!strcmp(argv[i], "-fs-state-dont-save")) {
+           fs_state.options.fs_state_save = 0;
+       } else if (!strcmp(argv[i], "-fs-state-dont-restore")) {
+           fs_state.options.fs_state_restore = 0;
+       } else if (!strcmp(argv[i], "-fs-state-verify")) {
+            if ((i + 1) >= argc) {
+               fprintf(stderr, "missing argument for %s\n", argv[i]); 
+               return -1; 
+           }
+           i++;
+           if (!strcmp(argv[i], "none")) {
+               fs_state.options.fs_state_verify_before_save = 0;
+               fs_state.options.fs_state_verify_after_restore = 0;
+           } else if (!strcmp(argv[i], "save")) {
+               fs_state.options.fs_state_verify_after_restore = 0;
+           } else if (!strcmp(argv[i], "restore")) {
+               fs_state.options.fs_state_verify_before_save = 0;
+           } else if (!strcmp(argv[i], "both")) {
+               /* default */
+           } else {
+               fprintf(stderr, "invalid argument for %s\n", argv[i-1]);
+               return -1;
+           }
+       } else if (!strcmp(argv[i], "-vhashsize")) {
+            if ((i + 1) >= argc) {
+               fprintf(stderr, "missing argument for %s\n", argv[i]); 
+               return -1; 
+           }
+           VSetVolHashSize(atoi(argv[++i]));
+       } else if (!strcmp(argv[i], "-vlrudisable")) {
+           VLRU_SetOptions(VLRU_SET_ENABLED, 0);
+       } else if (!strcmp(argv[i], "-vlruthresh")) {
+            if ((i + 1) >= argc) {
+               fprintf(stderr, "missing argument for %s\n", argv[i]); 
+               return -1; 
+           }
+           VLRU_SetOptions(VLRU_SET_THRESH, 60*atoi(argv[++i]));
+       } else if (!strcmp(argv[i], "-vlruinterval")) {
+            if ((i + 1) >= argc) {
+               fprintf(stderr, "missing argument for %s\n", argv[i]); 
+               return -1; 
+           }
+           VLRU_SetOptions(VLRU_SET_INTERVAL, atoi(argv[++i]));
+       } else if (!strcmp(argv[i], "-vlrumax")) {
+            if ((i + 1) >= argc) {
+               fprintf(stderr, "missing argument for %s\n", argv[i]); 
+               return -1; 
+           }
+           VLRU_SetOptions(VLRU_SET_MAX, atoi(argv[++i]));
+#endif /* AFS_DEMAND_ATTACH_FS */
        } else if (!strcmp(argv[i], "-s")) {
            Sawsmall = 1;
             if ((i + 1) >= argc) {
@@ -1923,6 +2130,15 @@ main(int argc, char *argv[])
        exit(1);
     }
 
+#ifdef AFS_DEMAND_ATTACH_FS
+    if (fs_state.options.fs_state_restore) {
+       /*
+        * demand attach fs
+        * restore fileserver state */
+       fs_stateRestore();
+    }
+#endif /* AFS_DEMAND_ATTACH_FS */
+
     /*
      * We are done calling fopen/fdopen. It is safe to use a large
      * of the file descriptor cache.
index 3b230e5..d8c837c 100644 (file)
@@ -5,6 +5,8 @@
  * This software has been released under the terms of the IBM Public
  * License.  For details, see the LICENSE file in the top-level source
  * directory or online at http://www.openafs.org/dl/license10.html
+ *
+ * Portions Copyright (c) 2006 Sine Nomine Associates
  */
 
 /*  file.h     - include file for the File Server                      */
@@ -20,6 +22,9 @@
  * Start with clean version to sync test and dev trees.
  * */
 
+#ifndef _AFS_VICED_VICED_H
+#define _AFS_VICED_VICED_H
+
 #include <afs/afssyscalls.h>
 #include <afs/afsutil.h>
 #include "fs_stats.h"          /*Defs for xstat-based statistics */
@@ -46,18 +51,6 @@ typedef struct DirHandle {
 } DirHandle;
 
 
-struct cbcounters {
-    int DeleteFiles;
-    int DeleteCallBacks;
-    int BreakCallBacks;
-    int AddCallBacks;
-    int GotSomeSpaces;
-    int DeleteAllCallBacks;
-    int nFEs, nCBs, nblks;
-    int CBsTimedOut;
-    int nbreakers;
-    int GSS1, GSS2, GSS3, GSS4, GSS5;
-};
 
 #define MAXCNTRS (AFS_HIGHEST_OPCODE+1)
 
@@ -219,3 +212,46 @@ extern pthread_mutex_t fsync_glock_mutex;
 #define FSYNC_LOCK
 #define FSYNC_UNLOCK
 #endif /* AFS_PTHREAD_ENV */
+
+
+#ifdef AFS_DEMAND_ATTACH_FS
+/*
+ * demand attach fs
+ * fileserver mode support
+ */
+struct fs_state {
+    volatile int mode;
+    volatile byte FiveMinuteLWP_tranquil;      /* five minute check thread is shutdown or sleeping */
+    volatile byte HostCheckLWP_tranquil;       /* host check thread is shutdown or sleeping */
+    volatile byte FsyncCheckLWP_tranquil;      /* fsync check thread is shutdown or sleeping */
+    volatile byte salvsync_fatal_error;        /* fatal error with salvsync comm */
+
+    /* some command-line options we use in 
+     * various places
+     *
+     * these fields are immutable once we
+     * go multithreaded */
+    struct {
+       byte fs_state_save;
+       byte fs_state_restore;
+       byte fs_state_verify_before_save;
+       byte fs_state_verify_after_restore;
+    } options;
+
+    pthread_cond_t worker_done_cv;
+    pthread_rwlock_t state_lock;
+};
+
+extern struct fs_state fs_state;
+
+/* this lock is defined to be directly above FS_LOCK in the locking hierarchy */
+#define FS_STATE_RDLOCK  assert(pthread_rwlock_rdlock(&fs_state.state_lock) == 0)
+#define FS_STATE_WRLOCK  assert(pthread_rwlock_wrlock(&fs_state.state_lock) == 0)
+#define FS_STATE_UNLOCK  assert(pthread_rwlock_unlock(&fs_state.state_lock) == 0)
+
+#define FS_MODE_NORMAL    0
+#define FS_MODE_SHUTDOWN  1
+#endif /* AFS_DEMAND_ATTACH_FS */
+
+
+#endif /* _AFS_VICED_VICED_H */
index df11f8a..556d350 100644 (file)
@@ -1,4 +1,27 @@
+/*
+ * Copyright 2000, International Business Machines Corporation and others.
+ * All Rights Reserved.
+ * 
+ * This software has been released under the terms of the IBM Public
+ * License.  For details, see the LICENSE file in the top-level source
+ * directory or online at http://www.openafs.org/dl/license10.html
+ */
+
+#ifndef _AFS_VICED_VICED_PROTOTYPES_H
+#define _AFS_VICED_VICED_PROTOTYPES_H
+
 extern int sendBufSize;
 afs_int32 sys_error_to_et(afs_int32 in);
 void init_sys_error_to_et(void);
+  
+#ifdef AFS_DEMAND_ATTACH_FS
+/*
+ * demand attach fs
+ * fileserver state serialization
+ */
+extern int fs_stateSave(void);
+extern int fs_stateRestore(void);
+#endif /* AFS_DEMAND_ATTACH_FS */
+
 
+#endif /* _AFS_VICED_VICED_PROTOTYPES_H */
index 114a304..33131a0 100644 (file)
@@ -16,22 +16,23 @@ LIBS=${TOP_LIBDIR}/libcmd.a vlib.a ${TOP_LIBDIR}/util.a \
        ${TOP_LIBDIR}/libsys.a ${TOP_LIBDIR}/libdir.a \
        ${TOP_LIBDIR}/liblwp.a  ${TOP_LIBDIR}/libacl.a
 
-CFLAGS = ${COMMON_CFLAGS} -D${SYS_NAME} ${FSINCLUDES} ${XCFLAGS} ${ARCHFLAGS}
+CFLAGS = ${COMMON_CFLAGS} -D${SYS_NAME} ${FSINCLUDES} ${XCFLAGS} ${ARCHFLAGS} -DFSSYNC_BUILD_SERVER -DFSSYNC_BUILD_CLIENT
 
-PUBLICHEADERS=nfs.h vnode.h viceinode.h volume.h voldefs.h partition.h\
-       fssync.h ihandle.h namei_ops.h
+PUBLICHEADERS=nfs.h vnode.h viceinode.h volume.h voldefs.h partition.h \
+       fssync.h ihandle.h namei_ops.h salvsync.h daemon_com.h
 
-VLIBOBJS=vnode.o volume.o vutil.o partition.o fssync.o purge.o \
-        clone.o nuke.o devname.o listinodes.o common.o ihandle.o \
-        namei_ops.o
+VLIBOBJS=vnode.o volume.o vutil.o partition.o fssync-server.o fssync-client.o \
+        clone.o nuke.o devname.o listinodes.o common.o ihandle.o purge.o \
+        namei_ops.o salvsync-server.o salvsync-client.o daemon_com.o
 
-OBJECTS=${VLIBOBJS} physio.o vol-salvage.o vol-info.o vol-dump.o vol-bless.o
+OBJECTS=${VLIBOBJS} physio.o vol-salvage.o vol-info.o vol-dump.o vol-bless.o fssync-debug.o
 
 all: gi \
        ${TOP_LIBDIR}/vlib.a \
        ${TOP_LIBDIR}/libvlib.a \
        salvager \
        volinfo \
+       fssync-debug \
        $(FS_CONV_OSF40D) \
        $(XFS_SIZE_CHECK) \
        $(FS_CONV_SOL26) \
@@ -42,6 +43,8 @@ all: gi \
        ${TOP_INCDIR}/afs/voldefs.h \
        ${TOP_INCDIR}/afs/partition.h \
        ${TOP_INCDIR}/afs/fssync.h \
+       ${TOP_INCDIR}/afs/salvsync.h \
+       ${TOP_INCDIR}/afs/daemon_com.h \
        ${TOP_INCDIR}/afs/ihandle.h \
        ${TOP_INCDIR}/afs/namei_ops.h
 
@@ -53,6 +56,7 @@ install: \
        ${DESTDIR}${libdir}/afs/libvlib.a \
        ${DESTDIR}${afssrvlibexecdir}/salvager \
        ${DESTDIR}${afssrvsbindir}/volinfo \
+       ${DESTDIR}${afssrvsbindir}/fssync-debug \
        $(install_FS_CONV_OSF40D) \
        $(install_XFS_SIZE_CHECK) \
        $(install_FS_CONV_SOL26) \
@@ -63,6 +67,8 @@ install: \
        ${DESTDIR}${includedir}/afs/voldefs.h \
        ${DESTDIR}${includedir}/afs/partition.h \
        ${DESTDIR}${includedir}/afs/fssync.h \
+       ${DESTDIR}${includedir}/afs/salvsync.h \
+       ${DESTDIR}${includedir}/afs/daemon_com.h \
        ${DESTDIR}${includedir}/afs/ihandle.h \
        ${DESTDIR}${includedir}/afs/namei_ops.h
 
@@ -72,6 +78,11 @@ ${DEST}/root.server/usr/afs/bin/salvager: salvager
 ${DEST}/root.server/usr/afs/bin/volinfo: volinfo
        ${INSTALL} -s $? $@
 
+${DEST}/root.server/usr/afs/bin/fssync-debug: fssync-debug
+       if test "@DEMAND_ATTACH@" = "no"; then \
+               ${INSTALL} -s $? $@ ; \
+       fi
+
 ${DEST}/lib/afs/vlib.a: vlib.a
        ${INSTALL} $? $@
 
@@ -117,6 +128,12 @@ ${DEST}/include/afs/partition.h: partition.h
 ${DEST}/include/afs/fssync.h: fssync.h
        ${INSTALL} $? $@
 
+${DEST}/include/afs/salvsync.h: salvsync.h
+       ${INSTALL} $? $@
+
+${DEST}/include/afs/daemon_com.h: daemon_com.h
+       ${INSTALL} $? $@
+
 ${DEST}/include/afs/ihandle.h: ihandle.h
        ${INSTALL} $? $@
 
@@ -129,6 +146,8 @@ ${DEST}/include/afs/namei_ops.h: namei_ops.h
 ${OBJECTS}: ${PUBLICHEADERS} ${TOP_INCDIR}/lwp.h ${TOP_INCDIR}/lock.h ${TOP_INCDIR}/afs/afsint.h vutils.h salvage.h AFS_component_version_number.c
 
 vol-salvage.o vutil.o: volinodes.h
+vol-salvage.o salvager.o: vol-salvage.h
+vol-salvage.o: salvsync.h daemon_com.h
 
 vlib.a:        ${VLIBOBJS} AFS_component_version_number.o
        $(RM) -f $@
@@ -136,8 +155,8 @@ vlib.a:     ${VLIBOBJS} AFS_component_version_number.o
        $(RANLIB) $@
 
 # new salvager:  remove references to /vice by linking with novice.o
-salvager: vol-salvage.o physio.o vlib.a
-       ${CC} ${LDFLAGS} -o salvager vol-salvage.o physio.o ${LIBS} ${XLIBS}
+salvager: vol-salvage.o physio.o vlib.a salvager.o ${LIBS}
+       ${CC} ${LDFLAGS} -o salvager vol-salvage.o physio.o salvager.o ${LIBS} ${XLIBS}
 
 vol-salvage: vol-salvage.o
 vol-info: vol-info.o physio.o ihandle.o
@@ -167,13 +186,16 @@ volinfo: vol-info.o physio.o ihandle.o ${LIBS}
        ${CC} ${CFLAGS} -o volinfo vol-info.o physio.o \
                ihandle.o ${LIBS} ${XLIBS}
 
+fssync-debug: fssync-debug.o physio.o AFS_component_version_number.c ${LIBS}
+       ${CC} ${LDFLAGS} -o fssync-debug fssync-debug.o physio.o ${LIBS} ${XLIBS}
+
 vol-bless: vol-bless.o physio.o ihandle.o ${LIBS}
        ${CC} ${CFLAGS} -o vol-bless vol-bless.o physio.o ${LIBS} ${XLIBS}
 
-fs_conv_dux40D: fs_conv_411.o
+fs_conv_dux40D: fs_conv_411.o ${LIBS}
        ${CC} ${CFLAGS} ${TOP_LIBDIR}/libcmd.a -o fs_conv_dux40D fs_conv_411.o  ${LIBS} ${XLIBS}
 
-fs_conv_sol26: fs_conv_411.o vlib.a 
+fs_conv_sol26: fs_conv_411.o ${LIBS}
        ${CC} ${CFLAGS} ${TOP_LIBDIR}/libcmd.a -o fs_conv_sol26 fs_conv_411.o  ${LIBS} ${XLIBS}
 
 fs_conv_411.o: fs_conv_411.c AFS_component_version_number.c
@@ -211,6 +233,11 @@ ${DESTDIR}${afssrvlibexecdir}/salvager: salvager
 ${DESTDIR}${afssrvsbindir}/volinfo: volinfo
        ${INSTALL} -s $? $@
 
+${DESTDIR}${afssrvsbindir}/fssync-debug: fssync-debug
+       if test "@DEMAND_ATTACH@" = "no" ; then \
+               ${INSTALL} -s $? $@ ; \
+       fi
+
 ${DESTDIR}${includedir}/afs/nfs.h: nfs.h
        ${INSTALL} $? $@
 
@@ -253,6 +280,18 @@ ${DESTDIR}${includedir}/afs/fssync.h: fssync.h
 ${TOP_INCDIR}/afs/fssync.h: fssync.h
        ${INSTALL} $? $@
 
+${DESTDIR}${includedir}/afs/salvsync.h: salvsync.h
+       ${INSTALL} $? $@
+
+${TOP_INCDIR}/afs/salvsync.h: salvsync.h
+       ${INSTALL} $? $@
+
+${DESTDIR}${includedir}/afs/daemon_com.h: daemon_com.h
+       ${INSTALL} $? $@
+
+${TOP_INCDIR}/afs/daemon_com.h: daemon_com.h
+       ${INSTALL} $? $@
+
 ${DESTDIR}${includedir}/afs/ihandle.h: ihandle.h
        ${INSTALL} $? $@
 
@@ -265,11 +304,24 @@ ${DESTDIR}${includedir}/afs/namei_ops.h: namei_ops.h
 ${TOP_INCDIR}/afs/namei_ops.h: namei_ops.h
        ${INSTALL} $? $@
 
+${DESTDIR}${includedir}/afs/salvage.h: salvage.h
+       ${INSTALL} $? $@
+
+${TOP_INCDIR}/afs/salvage.h: salvage.h
+       ${INSTALL} $? $@
+
+${DESTDIR}${includedir}/afs/vol-salvage.h: vol-salvage.h
+       ${INSTALL} $? $@
+
+${TOP_INCDIR}/afs/vol-salvage.h: vol-salvage.h
+       ${INSTALL} $? $@
+
 dest: \
        ${DEST}/lib/afs/vlib.a \
        ${DEST}/lib/afs/libvlib.a \
        ${DEST}/root.server/usr/afs/bin/salvager \
        ${DEST}/root.server/usr/afs/bin/volinfo \
+       ${DEST}/root.server/usr/afs/bin/fssync-debug \
        $(dest_FS_CONV_OSF40D) \
        $(dest_XFS_SIZE_CHECK) \
        $(dest_FS_CONV_SOL26) \
@@ -280,12 +332,14 @@ dest: \
        ${DEST}/include/afs/voldefs.h \
        ${DEST}/include/afs/partition.h \
        ${DEST}/include/afs/fssync.h \
+       ${DEST}/include/afs/salvsync.h \
+       ${DEST}/include/afs/daemon_com.h \
        ${DEST}/include/afs/ihandle.h \
        ${DEST}/include/afs/namei_ops.h
 
 check-splint::
        sh $(HELPER_SPLINT) $(CFLAGS) \
-           vnode.c volume.c vutil.c partition.c fssync.c purge.c \
+           vnode.c volume.c vutil.c partition.c fssync-server.c fssync-client.c \
            clone.c nuke.c devname.c listinodes.c common.c ihandle.c \
-           namei_ops.c \
-           physio.c vol-salvage.c vol-info.c vol-bless.c
+           namei_ops.c salvsync-server.c salvsync-client.c daemon_com.c purge.c \
+           physio.c vol-salvage.c vol-info.c vol-bless.c fssync-debug.c
index e09db2b..096026f 100644 (file)
@@ -5,6 +5,8 @@
 # License.  For details, see the LICENSE file in the top-level source
 # directory or online at http://www.openafs.org/dl/license10.html
 
+AFSDEV_AUXCDEFINES = -DFSSYNC_BUILD_SERVER -DFSSYNC_BUILD_CLIENT
+
 RELDIR=vol
 !INCLUDE ..\config\NTMakefile.$(SYS_NAME)
 !INCLUDE ..\config\NTMakefile.version
diff --git a/src/vol/daemon_com.c b/src/vol/daemon_com.c
new file mode 100644 (file)
index 0000000..26bddbf
--- /dev/null
@@ -0,0 +1,473 @@
+/*
+ * Copyright 2006, Sine Nomine Associates and others.
+ * All Rights Reserved.
+ * 
+ * This software has been released under the terms of the IBM Public
+ * License.  For details, see the LICENSE file in the top-level source
+ * directory or online at http://www.openafs.org/dl/license10.html
+ */
+
+/*
+ * localhost interprocess communication for servers
+ *
+ * currently handled by a localhost socket
+ * (yes, this needs to be replaced someday)
+ */
+
+#ifndef _WIN32
+#define FD_SETSIZE 65536
+#endif
+
+#include <afsconfig.h>
+#include <afs/param.h>
+
+RCSID
+    ("$Header$");
+
+#include <sys/types.h>
+#include <stdio.h>
+#ifdef AFS_NT40_ENV
+#include <winsock2.h>
+#include <time.h>
+#else
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <sys/time.h>
+#endif
+#include <errno.h>
+#include <assert.h>
+#include <signal.h>
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#else
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+#endif
+
+
+#include <rx/xdr.h>
+#include <afs/afsint.h>
+#include "nfs.h"
+#include <afs/errors.h>
+#include "daemon_com.h"
+#include "lwp.h"
+#include "lock.h"
+#include <afs/afssyscalls.h>
+#include "ihandle.h"
+#include "vnode.h"
+#include "volume.h"
+#include "partition.h"
+#include <rx/rx_queue.h>
+
+/*@printflike@*/ extern void Log(const char *format, ...);
+
+#ifdef osi_Assert
+#undef osi_Assert
+#endif
+#define osi_Assert(e) (void)(e)
+
+int (*V_BreakVolumeCallbacks) ();
+
+#define MAXHANDLERS    4       /* Up to 4 clients; must be at least 2, so that
+                                * move = dump+restore can run on single server */
+
+#define MAX_BIND_TRIES 5       /* Number of times to retry socket bind */
+
+static int getport(SYNC_client_state * state, struct sockaddr_in *addr);
+static int SYNC_ask_internal(SYNC_client_state * state, SYNC_command * com, SYNC_response * res);
+
+/* daemon com SYNC client interface */
+
+int
+SYNC_connect(SYNC_client_state * state)
+{
+    struct sockaddr_in addr;
+    /* I can't believe the following is needed for localhost connections!! */
+    static time_t backoff[] =
+       { 3, 3, 3, 5, 5, 5, 7, 15, 16, 24, 32, 40, 48, 0 };
+    time_t *timeout = &backoff[0];
+
+    if (state->fd >= 0) {
+       return 1;
+    }
+
+    for (;;) {
+       state->fd = getport(state, &addr);
+       if (connect(state->fd, (struct sockaddr *)&addr, sizeof(addr)) >= 0)
+           return 1;
+       if (!*timeout)
+           break;
+       if (!(*timeout & 1))
+           Log("SYNC_connect temporary failure (will retry)\n");
+       SYNC_disconnect(state);
+       sleep(*timeout++);
+    }
+    perror("SYNC_connect failed (giving up!)");
+    return 0;
+}
+
+int
+SYNC_disconnect(SYNC_client_state * state)
+{
+#ifdef AFS_NT40_ENV
+    closesocket(state->fd);
+#else
+    close(state->fd);
+#endif
+    state->fd = -1;
+    return 0;
+}
+
+afs_int32
+SYNC_closeChannel(SYNC_client_state * state)
+{
+    afs_int32 code;
+    SYNC_command com;
+    SYNC_response res;
+    SYNC_PROTO_BUF_DECL(ores);
+
+    if (state->fd == -1)
+       return SYNC_OK;
+
+    memset(&com, 0, sizeof(com));
+    memset(&res, 0, sizeof(res));
+
+    res.payload.len = SYNC_PROTO_MAX_LEN;
+    res.payload.buf = ores;
+
+    com.hdr.command = SYNC_COM_CHANNEL_CLOSE;
+    com.hdr.command_len = sizeof(SYNC_command_hdr);
+
+    /* in case the other end dropped, don't do any retries */
+    state->retry_limit = 0;
+    state->hard_timeout = 0;
+
+    code = SYNC_ask(state, &com, &res);
+
+    if (code == SYNC_OK) {
+       if (res.hdr.response != SYNC_OK) {
+           Log("SYNC_closeChannel:  channel shutdown request denied; closing socket anyway\n");
+       } else if (!(res.hdr.flags & SYNC_FLAG_CHANNEL_SHUTDOWN)) {
+           Log("SYNC_closeChannel:  channel shutdown request mishandled by server\n");
+       }
+    } else {
+       Log("SYNC_closeChannel: channel communications problem");
+    }
+
+    SYNC_disconnect(state);
+
+    return code;
+}
+
+int
+SYNC_reconnect(SYNC_client_state * state)
+{
+    SYNC_disconnect(state);
+    return SYNC_connect(state);
+}
+
+/* private function to fill in the sockaddr struct for us */
+static int
+getport(SYNC_client_state * state, struct sockaddr_in *addr)
+{
+    int sd;
+
+    memset(addr, 0, sizeof(*addr));
+    assert((sd = socket(AF_INET, SOCK_STREAM, 0)) >= 0);
+#ifdef STRUCT_SOCKADDR_HAS_SA_LEN
+    addr->sin_len = sizeof(struct sockaddr_in);
+#endif
+    addr->sin_addr.s_addr = htonl(0x7f000001);
+    addr->sin_family = AF_INET;        /* was localhost->h_addrtype */
+    addr->sin_port = htons(state->port);       /* XXXX htons not _really_ neccessary */
+
+    return sd;
+}
+
+afs_int32
+SYNC_ask(SYNC_client_state * state, SYNC_command * com, SYNC_response * res)
+{
+    int tries;
+    afs_uint32 now, timeout, code=SYNC_OK;
+
+    if (state->fatal_error) {
+       return SYNC_COM_ERROR;
+    }
+
+    if (state->fd == -1) {
+       SYNC_connect(state);
+    }
+
+    if (state->fd == -1) {
+       state->fatal_error = 1;
+       return SYNC_COM_ERROR;
+    }
+
+#ifdef AFS_DEMAND_ATTACH_FS
+    com->hdr.flags |= SYNC_FLAG_DAFS_EXTENSIONS;
+#endif
+
+    now = FT_ApproxTime();
+    timeout = now + state->hard_timeout;
+    for (tries = 0; 
+        (tries <= state->retry_limit) && (now <= timeout);
+        tries++, now = FT_ApproxTime()) {
+       code = SYNC_ask_internal(state, com, res);
+       if (code == SYNC_OK) {
+           break;
+       } else if (code == SYNC_BAD_COMMAND) {
+           Log("SYNC_ask: protocol mismatch; make sure fileserver, volserver, salvageserver and salvager are same version\n");
+           break;
+       } else if (code == SYNC_COM_ERROR) {
+           Log("SYNC_ask: protocol communications failure; attempting reconnect to server\n");
+           SYNC_reconnect(state);
+           /* try again */
+       } else {
+           /* unknown (probably protocol-specific) response code, pass it up to the caller, and let them deal with it */
+           break;
+       }
+    }
+
+    if (code == SYNC_COM_ERROR) {
+       Log("SYNC_ask: fatal protocol error; disabling sync protocol to server running on port %d until next server restart\n", 
+           state->port);
+       state->fatal_error = 1;
+    }
+
+    return code;
+}
+
+static afs_int32
+SYNC_ask_internal(SYNC_client_state * state, SYNC_command * com, SYNC_response * res)
+{
+    int n;
+    SYNC_PROTO_BUF_DECL(buf);
+#ifndef AFS_NT40_ENV
+    int iovcnt;
+    struct iovec iov[2];
+#endif
+
+    if (state->fd == -1) {
+       Log("SYNC_ask:  invalid sync file descriptor\n");
+       res->hdr.response = SYNC_COM_ERROR;
+       goto done;
+    }
+
+    if (com->hdr.command_len > SYNC_PROTO_MAX_LEN) {
+       Log("SYNC_ask:  internal SYNC buffer too small; please file a bug\n");
+       res->hdr.response = SYNC_COM_ERROR;
+       goto done;
+    }
+
+    com->hdr.proto_version = state->proto_version;
+
+    memcpy(buf, &com->hdr, sizeof(com->hdr));
+    if (com->payload.len) {
+       memcpy(buf + sizeof(com->hdr), com->payload.buf, 
+              com->hdr.command_len - sizeof(com->hdr));
+    }
+
+#ifdef AFS_NT40_ENV
+    n = send(state->fd, buf, com->hdr.command_len, 0);
+    if (n != com->hdr.command_len) {
+       Log("SYNC_ask:  write failed\n");
+       res->hdr.response = SYNC_COM_ERROR;
+       goto done;
+    }
+
+    n = recv(state->fd, buf, SYNC_PROTO_MAX_LEN, 0);
+    if (n == 0 || (n < 0 && WSAEINTR != WSAGetLastError())) {
+       Log("SYNC_ask:  No response\n");
+       res->hdr.response = SYNC_COM_ERROR;
+       goto done;
+    }
+#else /* !AFS_NT40_ENV */
+    n = write(state->fd, buf, com->hdr.command_len);
+    if (com->hdr.command_len != n) {
+       Log("SYNC_ask: write failed\n");
+       res->hdr.response = SYNC_COM_ERROR;
+       goto done;
+    }
+
+    /* receive the response */
+    iov[0].iov_base = (char *)&res->hdr;
+    iov[0].iov_len = sizeof(res->hdr);
+    if (res->payload.len) {
+       iov[1].iov_base = (char *)res->payload.buf;
+       iov[1].iov_len = res->payload.len;
+       iovcnt = 2;
+    } else {
+       iovcnt = 1;
+    }
+    n = readv(state->fd, iov, iovcnt);
+    if (n == 0 || (n < 0 && errno != EINTR)) {
+       Log("SYNC_ask: No response\n");
+       res->hdr.response = SYNC_COM_ERROR;
+       goto done;
+    }
+#endif /* !AFS_NT40_ENV */
+
+    res->recv_len = n;
+
+    if (n < sizeof(res->hdr)) {
+       Log("SYNC_ask:  response too short\n");
+       res->hdr.response = SYNC_COM_ERROR;
+       goto done;
+    }
+#ifdef AFS_NT40_ENV
+    memcpy(&res->hdr, buf, sizeof(res->hdr));
+#endif
+
+    if ((n - sizeof(res->hdr)) > res->payload.len) {
+       Log("SYNC_ask:  response too long\n");
+       res->hdr.response = SYNC_COM_ERROR;
+       goto done;
+    }
+#ifdef AFS_NT40_ENV
+    memcpy(res->payload.buf, buf + sizeof(res->hdr), n - sizeof(res->hdr));
+#endif
+
+    if (res->hdr.response_len != n) {
+       Log("SYNC_ask:  length field in response inconsistent\n");
+       res->hdr.response = SYNC_COM_ERROR;
+       goto done;
+    }
+    if (res->hdr.response == SYNC_DENIED) {
+       Log("SYNC_ask: negative response\n");
+    }
+
+  done:
+    return res->hdr.response;
+}
+
+
+/* 
+ * daemon com SYNC server-side interfaces 
+ */
+
+/* get a command */
+afs_int32
+SYNC_getCom(int fd, SYNC_command * com)
+{
+    int n;
+    afs_int32 code = SYNC_OK;
+#ifdef AFS_NT40_ENV
+    SYNC_PROTO_BUF_DECL(buf);
+#else
+    struct iovec iov[2];
+    int iovcnt;
+#endif
+
+#ifdef AFS_NT40_ENV
+    n = recv(fd, buf, SYNC_PROTO_MAX_LEN, 0);
+
+    if (n == 0 || (n < 0 && WSAEINTR != WSAGetLastError())) {
+       Log("SYNC_getCom:  error receiving command\n");
+       code = SYNC_COM_ERROR;
+       goto done;
+    }
+#else /* !AFS_NT40_ENV */
+    iov[0].iov_base = (char *)&com->hdr;
+    iov[0].iov_len = sizeof(com->hdr);
+    if (com->payload.len) {
+       iov[1].iov_base = (char *)com->payload.buf;
+       iov[1].iov_len = com->payload.len;
+       iovcnt = 2;
+    } else {
+       iovcnt = 1;
+    }
+
+    n = readv(fd, iov, iovcnt);
+    if (n == 0 || (n < 0 && errno != EINTR)) {
+       Log("SYNC_getCom:  error receiving command\n");
+       code = SYNC_COM_ERROR;
+       goto done;
+    }
+#endif /* !AFS_NT40_ENV */
+
+    com->recv_len = n;
+
+    if (n < sizeof(com->hdr)) {
+       Log("SYNC_getCom:  command too short\n");
+       code = SYNC_COM_ERROR;
+       goto done;
+    }
+#ifdef AFS_NT40_ENV
+    memcpy(&com->hdr, buf, sizeof(com->hdr));
+#endif
+
+    if ((n - sizeof(com->hdr)) > com->payload.len) {
+       Log("SYNC_getCom:  command too long\n");
+       code = SYNC_COM_ERROR;
+       goto done;
+    }
+#ifdef AFS_NT40_ENV
+    memcpy(com->payload.buf, buf + sizeof(com->hdr), n - sizeof(com->hdr));
+#endif
+
+ done:
+    return code;
+}
+
+/* put a response */
+afs_int32
+SYNC_putRes(int fd, SYNC_response * res)
+{
+    int n;
+    afs_int32 code = SYNC_OK;
+    SYNC_PROTO_BUF_DECL(buf);
+
+    if (res->hdr.response_len > (sizeof(res->hdr) + res->payload.len)) {
+       Log("SYNC_putRes:  response_len field in response header inconsistent\n");
+       code = SYNC_COM_ERROR;
+       goto done;
+    }
+
+    if (res->hdr.response_len > SYNC_PROTO_MAX_LEN) {
+       Log("SYNC_putRes:  internal SYNC buffer too small; please file a bug\n");
+       code = SYNC_COM_ERROR;
+       goto done;
+    }
+
+#ifdef AFS_DEMAND_ATTACH_FS
+    res->hdr.flags |= SYNC_FLAG_DAFS_EXTENSIONS;
+#endif
+
+    memcpy(buf, &res->hdr, sizeof(res->hdr));
+    if (res->payload.len) {
+       memcpy(buf + sizeof(res->hdr), res->payload.buf, 
+              res->hdr.response_len - sizeof(res->hdr));
+    }
+
+#ifdef AFS_NT40_ENV
+    n = send(fd, buf, res->hdr.response_len, 0);
+#else /* !AFS_NT40_ENV */
+    n = write(fd, buf, res->hdr.response_len);
+#endif /* !AFS_NT40_ENV */
+
+    if (res->hdr.response_len != n) {
+       Log("SYNC_putRes: write failed\n");
+       res->hdr.response = SYNC_COM_ERROR;
+       goto done;
+    }
+
+ done:
+    return code;
+}
+
+/* return 0 for legal (null-terminated) string,
+ * 1 for illegal (unterminated) string */
+int
+SYNC_verifyProtocolString(char * buf, size_t len)
+{
+    int ret = 0;
+    size_t s_len;
+
+    s_len = afs_strnlen(buf, len);
+
+    return (s_len == len) ? 1 : 0;
+}
diff --git a/src/vol/daemon_com.h b/src/vol/daemon_com.h
new file mode 100644 (file)
index 0000000..8464367
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2006, Sine Nomine Associates and others.
+ * All Rights Reserved.
+ * 
+ * This software has been released under the terms of the IBM Public
+ * License.  For details, see the LICENSE file in the top-level source
+ * directory or online at http://www.openafs.org/dl/license10.html
+ */
+
+#ifndef _AFS_VOL_DAEMON_COM_H
+#define _AFS_VOL_DAEMON_COM_H
+
+/* 
+ * SYNC protocol constants
+ */
+
+/* SYNC protocol command codes
+ *
+ * command codes 0-65535 are reserved for
+ * global SYNC package command codes
+ */
+#define SYNC_COM_CODE_USER_BASE 65536
+#define SYNC_COM_CODE_DECL(code) (SYNC_COM_CODE_USER_BASE+(code))
+
+/* general command codes */
+#define SYNC_COM_CHANNEL_CLOSE 0
+
+
+/* SYNC protocol response codes
+ *
+ * response codes 0-65535 are reserved for 
+ * global SYNC package response codes
+ */
+#define SYNC_RES_CODE_USER_BASE 65536
+#define SYNC_RES_CODE_DECL(code) (SYNC_RES_CODE_USER_BASE+(code))
+
+/* general response codes */
+#define SYNC_OK                0   /* sync call returned ok */
+#define SYNC_DENIED            1   /* sync request denied by server */
+#define SYNC_COM_ERROR         2   /* sync protocol communicaions error */
+#define SYNC_BAD_COMMAND       3   /* sync command code not implemented by server */
+#define SYNC_FAILED            4   /* sync server-side procedure failed */
+
+
+/* SYNC protocol reason codes
+ *
+ * reason codes 0-65535 are reserved for
+ * global SYNC package reason codes
+ */
+#define SYNC_REASON_CODE_USER_BASE 65536
+#define SYNC_REASON_CODE_DECL(code) (SYNC_REASON_CODE_USER_BASE+(code))
+
+/* general reason codes */
+#define SYNC_REASON_NONE                 0
+#define SYNC_REASON_MALFORMED_PACKET     1
+
+
+/* SYNC protocol flags
+ *
+ * flag bits 0-7 are reserved for
+ * global SYNC package flags
+ */
+#define SYNC_FLAG_CODE_USER_BASE 8
+#define SYNC_FLAG_CODE_DECL(code) (1 << (SYNC_FLAG_CODE_USER_BASE+(code)))
+
+/* general flag codes */
+#define SYNC_FLAG_CHANNEL_SHUTDOWN   0x1
+#define SYNC_FLAG_DAFS_EXTENSIONS    0x2   /* signal that other end of socket is compiled
+                                           * with demand attach extensions */
+
+/* SYNC protocol response buffers */
+#define SYNC_PROTO_MAX_LEN     768  /* maximum size of sync protocol message */
+
+/* use a large type to get proper buffer alignment so we can safely cast the pointer */