vldb-repair-tool-20061216
authorDerrick Brashear <shadow@dementia.org>
Sat, 16 Dec 2006 06:25:24 +0000 (06:25 +0000)
committerDerrick Brashear <shadow@dementia.org>
Sat, 16 Dec 2006 06:25:24 +0000 (06:25 +0000)
make vldb_check able to repair at least some kinds of damage

run it on a vldb which does not have a server operating live on it
verify the result with vldb_check before using.

src/vlserver/vldb_check.c

index ce574a7..8697d53 100644 (file)
 #define MHC 0x100              /* on multihomed chain */
 #define FRC 0x200              /* on free chain */
 
+#define REFRW 0x1000            /* linked from something (RW) */
+#define REFRO 0x2000            /* linked from something (RO) */
+#define REFBK 0x4000            /* linked from something (BK) */
+#define REFN  0x8000            /* linked from something (name) */
+
+#define vldbread(x,y,z) vldbio(x,y,z,0)
+#define vldbwrite(x,y,z) vldbio(x,y,z,1)
+
 #include <afsconfig.h>
 #include <afs/param.h>
 
@@ -55,15 +63,26 @@ RCSID
 #include <afs/afsutil.h>
 #include <afs/cmd.h>
 
+#define ADDR(x) (x/sizeof(struct nvlentry))
+
 int fd;
 int listentries, listservers, listheader, listuheader, verbose;
 
+int fix = 0;
+
 struct er {
     long addr;
     int type;
 } *record;
 int serveraddrs[MAXSERVERID + 2];
 
+#if 0
+int
+writeUbikHeader()
+{
+    /* Bump the version number?? We could cheat and push a new db... */
+}
+#endif
 
 #define HDRSIZE 64
 int
@@ -110,10 +129,7 @@ readUbikHeader()
 }
 
 int
-vldbread(position, buffer, size)
-     int position;
-     char *buffer;
-     int size;
+vldbio(int position, char *buffer, int size, int rdwr)
 {
     int offset, r, p;
 
@@ -125,18 +141,21 @@ vldbread(position, buffer, size)
        return (-1);
     }
 
-    /* now read the info */
-    r = read(fd, buffer, size);
+    if (rdwr == 1) 
+       r = write(fd, buffer, size);
+    else 
+       r = read(fd, buffer, size);
+
     if (r != size) {
-       printf("error: read of %d bytes failed: %d %d\n", size, r, errno);
+       printf("error: %s of %d bytes failed: %d %d\n", rdwr==1?"write":"read",
+              size, r, errno);
        return (-1);
     }
     return (0);
 }
 
 char *
-vtype(type)
-     int type;
+vtype(int type)
 {
     static char Type[3];
 
@@ -152,8 +171,7 @@ vtype(type)
 }
 
 afs_int32
-NameHash(volname)
-     char *volname;
+NameHash(char *volname)
 {
     unsigned int hash;
     char *vchar;
@@ -165,16 +183,14 @@ NameHash(volname)
 }
 
 afs_int32
-IdHash(volid)
-     afs_int32 volid;
+IdHash(afs_int32 volid)
 {
     return ((abs(volid)) % HASHSIZE);
 }
 
 #define LEGALCHARS ".ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
 int
-InvalidVolname(volname)
-     char *volname;
+InvalidVolname(char *volname)
 {
     char *map;
     size_t slen;
@@ -186,8 +202,8 @@ InvalidVolname(volname)
     return (slen != strspn(volname, map));
 }
 
-readheader(headerp)
-     struct vlheader *headerp;
+void
+readheader(struct vlheader *headerp)
 {
     int i, j;
 
@@ -251,9 +267,41 @@ readheader(headerp)
               headerp->vital_header.headersize, sizeof(*headerp));
 }
 
-readMH(addr, mhblockP)
-     afs_int32 addr;
-     struct extentaddr *mhblockP;
+void
+writeheader(struct vlheader *headerp)
+{
+    int i, j;
+
+    headerp->vital_header.vldbversion =
+       htonl(headerp->vital_header.vldbversion);
+    headerp->vital_header.headersize =
+       htonl(headerp->vital_header.headersize);
+    headerp->vital_header.freePtr = htonl(headerp->vital_header.freePtr);
+    headerp->vital_header.eofPtr = htonl(headerp->vital_header.eofPtr);
+    headerp->vital_header.allocs = htonl(headerp->vital_header.allocs);
+    headerp->vital_header.frees = htonl(headerp->vital_header.frees);
+    headerp->vital_header.MaxVolumeId =
+       htonl(headerp->vital_header.MaxVolumeId);
+    headerp->vital_header.totalEntries[0] =
+       htonl(headerp->vital_header.totalEntries[0]);
+    for (i = 0; i < MAXTYPES; i++)
+       headerp->vital_header.totalEntries[i] =
+           htonl(headerp->vital_header.totalEntries[1]);
+
+    headerp->SIT = htonl(headerp->SIT);
+    for (i = 0; i < MAXSERVERID; i++)
+       headerp->IpMappedAddr[i] = htonl(headerp->IpMappedAddr[i]);
+    for (i = 0; i < HASHSIZE; i++)
+       headerp->VolnameHash[i] = htonl(headerp->VolnameHash[i]);
+    for (i = 0; i < MAXTYPES; i++)
+       for (j = 0; j < HASHSIZE; j++)
+           headerp->VolidHash[i][j] = htonl(headerp->VolidHash[i][j]);
+
+    vldbwrite(0, headerp, sizeof(*headerp));
+}
+
+void
+readMH(afs_int32 addr, struct extentaddr *mhblockP)
 {
     int i, j;
     struct extentaddr *e;
@@ -275,10 +323,8 @@ readMH(addr, mhblockP)
     }
 }
 
-readentry(addr, vlentryp, type)
-     afs_int32 addr;
-     struct nvlentry *vlentryp;
-     afs_int32 *type;
+void
+readentry(afs_int32 addr, struct nvlentry *vlentryp, afs_int32 *type)
 {
     int i;
 
@@ -356,9 +402,32 @@ readentry(addr, vlentryp, type)
 }
 
 void
-readSIT(base, addr)
-     int base;
-     int addr;
+writeentry(afs_int32 addr, struct nvlentry *vlentryp)
+{
+    int i;
+
+    if (verbose)
+       printf("Writing back entry at addr %u\n", addr);
+
+    for (i = 0; i < MAXTYPES; i++)
+       vlentryp->volumeId[i] = htonl(vlentryp->volumeId[i]);
+    vlentryp->flags = htonl(vlentryp->flags);
+    vlentryp->LockAfsId = htonl(vlentryp->LockAfsId);
+    vlentryp->LockTimestamp = htonl(vlentryp->LockTimestamp);
+    vlentryp->cloneId = htonl(vlentryp->cloneId);
+    for (i = 0; i < MAXTYPES; i++)
+       vlentryp->nextIdHash[i] = htonl(vlentryp->nextIdHash[i]);
+    vlentryp->nextNameHash = htonl(vlentryp->nextNameHash);
+    for (i = 0; i < NMAXNSERVERS; i++) {
+       vlentryp->serverNumber[i] = htonl(vlentryp->serverNumber[i]);
+       vlentryp->serverPartition[i] = htonl(vlentryp->serverPartition[i]);
+       vlentryp->serverFlags[i] = htonl(vlentryp->serverFlags[i]);
+    }
+    vldbwrite(addr, vlentryp, sizeof(*vlentryp));
+}
+
+void
+readSIT(int base, int addr)
 {
     int i, j, a;
     char sitbuf[VL_ADDREXTBLK_SIZE];
@@ -418,8 +487,7 @@ readSIT(base, addr)
  * Remember what the maximum volume id we found is and check against the header.
  */
 void
-ReadAllEntries(header)
-     struct vlheader *header;
+ReadAllEntries(struct vlheader *header)
 {
     afs_int32 type, rindex, i, j, e;
     int freecount = 0, mhcount = 0, vlcount = 0;
@@ -457,6 +525,10 @@ ReadAllEntries(header)
                        bkcount++;
                    continue;
                }
+               if (!vlentry.serverFlags[j]) {
+                   /*e = 0;*/
+                   continue;
+               }
                if (e) {
                    printf
                        ("VLDB entry '%s' contains an unknown RW/RO index serverFlag\n",
@@ -508,13 +580,63 @@ ReadAllEntries(header)
 }
 
 
+void
+SetHashEnd(long addr, int type, long new)
+{
+    struct nvlentry vlentry;
+    afs_int32 i, rindex, type2, next = -1;
+
+    for (; addr; addr = next) {
+       readentry(addr, &vlentry, &type2);
+       switch(type & 0xf0) {
+       case RWH:
+           next = vlentry.nextIdHash[0];
+           break;
+       case ROH:
+           next = vlentry.nextIdHash[1];
+           break;
+       case BKH:
+           next = vlentry.nextIdHash[2];
+           break;
+       case NH:
+           next = vlentry.nextNameHash;
+           break;
+       default:
+           next = -1;
+       }
+
+       if (next < 1) {
+           switch(type & 0xf0) {
+           case RWH:
+             if (vlentry.nextIdHash[0] != 0) {printf("bwoop\n");}
+               vlentry.nextIdHash[0] = new;
+               break;
+           case ROH:
+             if (vlentry.nextIdHash[1] != 0) {printf("bwoop\n");}
+               vlentry.nextIdHash[1] = new;
+               break;
+           case BKH:
+             if (vlentry.nextIdHash[2] != 0) {printf("bwoop\n");}
+               vlentry.nextIdHash[2] = new;
+               break;
+           case NH:
+             if (vlentry.nextNameHash != 0) {printf("bwoop\n");}
+               vlentry.nextNameHash = new;
+               break;
+           }
+           writeentry(addr, &vlentry);
+           return;
+       }
+    }
+}
+
 /*
  * Follow each Name hash bucket marking it as read in the record array.
  * Record we found it in the name hash within the record array.
  * Check that the name is hashed correctly.
  */
-FollowNameHash(header)
-     struct vlheader *header;
+void
+FollowNameHash(struct vlheader *header)
 {
     int count = 0, longest = 0, shortest = -1, chainlength;
     struct nvlentry vlentry;
@@ -549,6 +671,8 @@ FollowNameHash(header)
            }
            record[rindex].type |= NH;
 
+           record[rindex].type |= REFN;
+
            chainlength++;
            count++;
 
@@ -576,19 +700,20 @@ FollowNameHash(header)
  * Record we found it in the id hash within the record array.
  * Check that the ID is hashed correctly.
  */
-FollowIdHash(header)
-     struct vlheader *header;
+void
+FollowIdHash(struct vlheader *header)
 {
     int count = 0, longest = 0, shortest = -1, chainlength;
     struct nvlentry vlentry;
     afs_uint32 addr;
-    afs_int32 i, j, hash, type, rindex;
+    afs_int32 i, j, hash, type, rindex, ref;
 
     /* Now follow the RW, RO, and BK Hash Tables */
     if (verbose)
        printf("Check RW, RO, and BK id Hashes\n");
     for (i = 0; i < MAXTYPES; i++) {
        hash = ((i == 0) ? RWH : ((i == 1) ? ROH : BKH));
+       ref = ((i == 0) ? REFRW : ((i == 1) ? REFRO : REFBK));
        count = longest = 0;
        shortest = -1;
 
@@ -617,6 +742,7 @@ FollowIdHash(header)
                    break;
                }
                record[rindex].type |= hash;
+               record[rindex].type |= ref;
 
                chainlength++;
                count++;
@@ -648,8 +774,8 @@ FollowIdHash(header)
  * Follow the free chain.
  * Record we found it in the free chain within the record array.
  */
-FollowFreeChain(header)
-     struct vlheader *header;
+void
+FollowFreeChain(struct vlheader *header)
 {
     afs_int32 count = 0;
     struct nvlentry vlentry;
@@ -704,8 +830,8 @@ FollowFreeChain(header)
  * The code does not verify if there are duplicate IP addresses in the 
  * list. The vlserver does this when a fileserver registeres itself.
  */
-CheckIpAddrs(header)
-     struct vlheader *header;
+void
+CheckIpAddrs(struct vlheader *header)
 {
     int mhblocks = 0;
     afs_int32 i, j, m, rindex;
@@ -899,13 +1025,19 @@ CheckIpAddrs(header)
 
 }
 
+void 
+FixBad(afs_uint32 idx, afs_uint32 addr, afs_uint32 type, afs_uint32 tmp, 
+       struct nvlentry *vlentry) {
+    SetHashEnd(addr, type, tmp);
+    printf("linked unlinked chain %u (index %d) to end of chain\n", 
+          tmp, ADDR(tmp));
+}
+
 int
-WorkerBee(as, arock)
-     struct cmd_syndesc *as;
-     char *arock;
+WorkerBee(struct cmd_syndesc *as, char *arock)
 {
     char *dbfile;
-    afs_int32 maxentries, type;
+    afs_int32 maxentries, type, tmp;
     struct vlheader header;
     struct nvlentry vlentry;
     int i, j, help = 0;
@@ -916,9 +1048,10 @@ WorkerBee(as, arock)
     listservers = (as->parms[3].items ? 1 : 0);        /* -servers  */
     listentries = (as->parms[4].items ? 1 : 0);        /* -entries  */
     verbose = (as->parms[5].items ? 1 : 0);    /* -verbose  */
+    fix = (as->parms[6].items ? 1 : 0);        /* -fix  */
 
     /* open the vldb database file */
-    fd = open(dbfile, O_RDONLY, 0);
+    fd = open(dbfile, (fix > 0)?O_RDWR:O_RDONLY, 0);
     if (fd < 0) {
        printf("can't open file '%s'. error = %d\n", dbfile, errno);
        return 0;
@@ -960,6 +1093,12 @@ WorkerBee(as, arock)
     if (verbose)
        printf("Verify each volume entry\n");
     for (i = 0; i < maxentries; i++) {
+       int nextp;
+       int reft;
+       int hash;
+       int *nextpp;
+       char *which;
+
        if (record[i].type == 0)
            continue;
 
@@ -967,27 +1106,76 @@ WorkerBee(as, arock)
         * on the hash chains, and its server numbers are good.
         */
        if (record[i].type & VL) {
+           int foundbad = 0;
+           char volidbuf[256];
+
            readentry(record[i].addr, &vlentry, &type);
 
            if (InvalidVolname(vlentry.name))
                printf("Volume '%s' at addr %u has an invalid name\n",
                       vlentry.name, record[i].addr);
 
-           if (!(record[i].type & NH))
-               printf("Volume '%s' not found in name hash\n", vlentry.name);
+           if (!(record[i].type & NH)) {
+               nextp = ADDR(vlentry.nextNameHash);
+               reft = REFN;
+               hash = NameHash(vlentry.name);
+               nextpp = &vlentry.nextNameHash;
+               which = "name";
+               sprintf(volidbuf, "");
+               foundbad = 1;
+           }
 
-           if (vlentry.volumeId[0] && !(record[i].type & RWH))
-               printf("Volume '%s' id %u not found in RW hash chain\n",
-                      vlentry.name, vlentry.volumeId[0]);
+           if (vlentry.volumeId[0] && !(record[i].type & RWH)) {
+               nextp = ADDR(vlentry.nextIdHash[0]);
+               reft = REFRW;
+               hash = IdHash(vlentry.volumeId[0]);
+               nextpp = &(vlentry.nextIdHash[0]);
+               which = "RW";
+               sprintf(volidbuf, "id %u ", vlentry.volumeId[0]);
+               foundbad = 1;
+           }
 
-           if (vlentry.volumeId[1] && !(record[i].type & ROH))
-               printf("Volume '%s' id %u not found in RO hash chain\n",
-                      vlentry.name, vlentry.volumeId[1]);
+           if (vlentry.volumeId[1] && !(record[i].type & ROH)) {
+               nextp = ADDR(vlentry.nextIdHash[1]);
+               reft = REFRO;
+               hash = IdHash(vlentry.volumeId[1]);
+               nextpp = &(vlentry.nextIdHash[1]);
+               which = "RO";
+               sprintf(volidbuf, "id %u ", vlentry.volumeId[1]);
+               foundbad = 1;
+           }
 
-           if (vlentry.volumeId[2] && !(record[i].type & BKH))
-               printf("Volume '%s' id %u not found in BK hash chain\n",
-                      vlentry.name, vlentry.volumeId[2]);
+           if (vlentry.volumeId[2] && !(record[i].type & BKH)) {
+               nextp = ADDR(vlentry.nextIdHash[2]);
+               reft = REFBK;
+               hash = IdHash(vlentry.volumeId[2]);
+               nextpp = &(vlentry.nextIdHash[2]);
+               which = "BK";
+               sprintf(volidbuf, "id %u ", vlentry.volumeId[2]);
+               foundbad = 1;
+           }
 
+           if (foundbad) {
+               printf("%d: Volume '%s' %snot found in %s hash %d", i, 
+                      vlentry.name, volidbuf, which, hash);
+               if (nextp) {
+                   printf(" (next %d", nextp);
+                   if (!(record[nextp].type & reft)) {
+                       printf(" not in chain ");
+                       record[nextp].type |= reft;
+                   } else if (nextp != 0) {
+                       printf(" next in chain");
+                       if (fix) {
+                           printf(", unchaining");
+                           *nextpp = 0;
+                           writeentry(record[i].addr, &vlentry);
+                       }
+                   }
+                   printf(")");
+               }
+               printf("\n");
+           }
+       
            for (j = 0; j < NMAXNSERVERS; j++) {
                if ((vlentry.serverNumber[j] != 255)
                    && (serveraddrs[vlentry.serverNumber[j]] == 0)) {
@@ -996,45 +1184,96 @@ WorkerBee(as, arock)
                         vlentry.name, j, vlentry.serverNumber[j]);
                }
            }
-
-           if (record[i].type & 0xffffff00)
+       
+           if (record[i].type & 0xffff0f00)
                printf
                    ("Volume '%s' id %u also found on other chains (0x%x)\n",
                     vlentry.name, vlentry.volumeId[0], record[i].type);
-
+           
            /* A free entry */
        } else if (record[i].type & FR) {
            if (!(record[i].type & FRC))
                printf("Free vlentry at %u not on free chain\n",
                       record[i].addr);
-
+           
            if (record[i].type & 0xfffffdf0)
                printf
                    ("Free vlentry at %u also found on other chains (0x%x)\n",
                     record[i].addr, record[i].type);
-
+           
            /* A multihomed entry */
        } else if (record[i].type & MH) {
            if (!(record[i].type & MHC))
                printf("Multihomed block at %u is orphaned\n",
                       record[i].addr);
-
+           
            if (record[i].type & 0xfffffef0)
                printf
                    ("Multihomed block at %u also found on other chains (0x%x)\n",
                     record[i].addr, record[i].type);
-
+           
        } else {
            printf("Unknown entry type at %u (0x%x)\n", record[i].addr,
                   record[i].type);
        }
     }
+
+    /* By the time we get here, unchained entries are really unchained */
+    printf("Scanning %u entries for possible repairs\n", maxentries);
+    for (i = 0; i < maxentries; i++) {
+       if (record[i].type & VL) {
+           readentry(record[i].addr, &vlentry, &type);
+           if (!(record[i].type & REFN) && (strlen(vlentry.name)>0)) {
+               printf("%d: Record %u (type 0x%x) not in a name chain\n", i, 
+                      record[i].addr, record[i].type);
+               if (fix) {
+                   if (header.VolnameHash[NameHash(vlentry.name)] == 0)
+                       header.VolnameHash[NameHash(vlentry.name)] = record[i].addr;
+                   else
+                       FixBad(i, header.VolnameHash[NameHash(vlentry.name)], NH, record[i].addr, &vlentry);
+               }
+           }
+           if (vlentry.volumeId[0] && !(record[i].type & REFRW)) {
+               printf("%d: Record %u (type 0x%x) not in a RW chain\n", i,
+                      record[i].addr, record[i].type);
+               if (fix) {
+                   if (header.VolidHash[0][IdHash(vlentry.volumeId[0])] == 0)
+                       header.VolidHash[0][IdHash(vlentry.volumeId[0])] = record[i].addr;
+                   else
+                       FixBad(i, header.VolidHash[0][IdHash(vlentry.volumeId[0])], RWH, record[i].addr, &vlentry);
+               }
+           }
+           if (vlentry.volumeId[1] && !(record[i].type & REFRO)) {
+               printf("%d: Record %u (type 0x%x) not in a RO chain\n", i, 
+                      record[i].addr, record[i].type);
+               if (fix) {
+                   if (header.VolidHash[1][IdHash(vlentry.volumeId[1])] == 0)
+                       header.VolidHash[1][IdHash(vlentry.volumeId[1])] = record[i].addr;
+                   else
+                       FixBad(i, header.VolidHash[1][IdHash(vlentry.volumeId[1])], ROH, record[i].addr, &vlentry);
+               }
+           }
+           if (vlentry.volumeId[2] && !(record[i].type & REFBK)) {
+               printf("%d: Record %u (type 0x%x) not in a BK chain\n", i, 
+                      record[i].addr, record[i].type);
+               if (fix) {
+                   if (header.VolidHash[2][IdHash(vlentry.volumeId[2])] == 0)
+                       header.VolidHash[2][IdHash(vlentry.volumeId[2])] = record[i].addr;
+                   else
+                       FixBad(i, header.VolidHash[2][IdHash(vlentry.volumeId[2])], BKH, record[i].addr, &vlentry);
+               }
+
+           }
+       }
+    }
+    if (fix) 
+       writeheader(&header);
+
     return 0;
 }
 
-main(argc, argv)
-     int argc;
-     char **argv;
+int
+main(int argc, char **argv)
 {
     struct cmd_syndesc *ts;
 
@@ -1050,6 +1289,7 @@ main(argc, argv)
                "Display server list");
     cmd_AddParm(ts, "-entries", CMD_FLAG, CMD_OPTIONAL, "Display entries");
     cmd_AddParm(ts, "-verbose", CMD_FLAG, CMD_OPTIONAL, "verbose");
+    cmd_AddParm(ts, "-fix", CMD_FLAG, CMD_OPTIONAL, "attempt to patch the database (potentially dangerous)");
 
     return cmd_Dispatch(argc, argv);
 }