misc-build-cleanup-20010917
[openafs.git] / src / vlserver / vlutils.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 #include <afsconfig.h>
11 #include <afs/param.h>
12
13 RCSID("$Header$");
14
15 #include <sys/types.h>
16 #ifdef AFS_NT40_ENV
17 #include <winsock2.h>
18 #else
19 #include <netinet/in.h>
20 #endif
21 #include <lock.h>
22 #include <rx/xdr.h>
23 #include <ubik.h>
24 #include "vlserver.h"
25
26 extern struct vlheader    cheader;
27 struct vlheader xheader;
28 extern struct ubik_dbase *VL_dbase;
29 extern afs_uint32 HostAddress[];
30 extern int maxnservers;
31 struct extentaddr extentaddr;
32 extern struct extentaddr *ex_addr[];
33 int vldbversion=0;
34
35 static int index_OK();
36
37 #define ERROR_EXIT(code) {error=(code); goto error_exit;}
38
39 /* Hashing algorithm based on the volume id; HASHSIZE must be prime */
40 afs_int32 IDHash(volumeid)
41 afs_int32       volumeid;
42 {
43     return((abs(volumeid)) % HASHSIZE);
44 }
45
46
47 /* Hashing algorithm based on the volume name; name's size is implicit (64 chars) and if changed it should be reflected here. */
48 afs_int32 NameHash (volumename)
49 register char   *volumename;
50 {   register unsigned int   hash;
51     register int            i;
52
53     hash = 0;
54     for (i=strlen(volumename), volumename += i-1; i--; volumename--)
55         hash = (hash*63) + (*((unsigned char *)volumename) - 63);
56     return(hash % HASHSIZE);
57 }
58
59
60 /* package up seek and write into one procedure for ease of use */
61 afs_int32 vlwrite (trans, offset, buffer, length)
62 struct ubik_trans   *trans;
63 afs_int32                   offset;
64 char                *buffer;
65 afs_int32                   length;
66 {   afs_int32   errorcode;
67
68     if (errorcode = ubik_Seek(trans, 0, offset)) return errorcode;
69     return(ubik_Write(trans, buffer, length));
70 }
71
72
73 /* Package up seek and read into one procedure for ease of use */
74 afs_int32 vlread (trans, offset, buffer, length)
75 struct ubik_trans   *trans;
76 afs_int32                   offset;
77 char                *buffer;
78 afs_int32                   length;
79 {   afs_int32   errorcode;
80
81     if (errorcode = ubik_Seek(trans, 0, offset)) return errorcode;
82     return(ubik_Read(trans, buffer, length));
83 }
84
85
86 /* take entry and convert to network order and write to disk */
87 afs_int32 vlentrywrite(trans, offset, buffer, length)
88 struct ubik_trans   *trans;
89 afs_int32                   offset;
90 char                *buffer;
91 afs_int32                   length; {
92     struct vlentry oentry;
93     struct nvlentry nentry, *nep;
94     char *bufp;
95     register afs_int32 i;
96
97     if (length != sizeof(oentry)) return -1;
98     if (maxnservers == 13) {
99         nep = (struct nvlentry *)buffer;
100         for (i=0;i<MAXTYPES;i++)
101             nentry.volumeId[i] = htonl(nep->volumeId[i]);
102         nentry.flags = htonl(nep->flags);
103         nentry.LockAfsId = htonl(nep->LockAfsId);
104         nentry.LockTimestamp = htonl(nep->LockTimestamp);
105         nentry.cloneId = htonl(nep->cloneId);
106         for (i=0;i<MAXTYPES;i++)
107             nentry.nextIdHash[i] = htonl(nep->nextIdHash[i]);
108         nentry.nextNameHash = htonl(nep->nextNameHash);
109         memcpy(nentry.name, nep->name, VL_MAXNAMELEN);
110         memcpy(nentry.serverNumber, nep->serverNumber, NMAXNSERVERS);
111         memcpy(nentry.serverPartition, nep->serverPartition, NMAXNSERVERS);
112         memcpy(nentry.serverFlags, nep->serverFlags, NMAXNSERVERS);
113         bufp = (char *)&nentry;
114     } else {
115         memset(&oentry, 0, sizeof(struct vlentry));
116         nep = (struct nvlentry *)buffer;
117         for (i=0;i<MAXTYPES;i++)
118             oentry.volumeId[i] = htonl(nep->volumeId[i]);
119         oentry.flags = htonl(nep->flags);
120         oentry.LockAfsId = htonl(nep->LockAfsId);
121         oentry.LockTimestamp = htonl(nep->LockTimestamp);
122         oentry.cloneId = htonl(nep->cloneId);
123         for (i=0;i<MAXTYPES;i++)
124             oentry.nextIdHash[i] = htonl(nep->nextIdHash[i]);
125         oentry.nextNameHash = htonl(nep->nextNameHash);
126         memcpy(oentry.name, nep->name, VL_MAXNAMELEN);
127         memcpy(oentry.serverNumber, nep->serverNumber, OMAXNSERVERS);
128         memcpy(oentry.serverPartition, nep->serverPartition, OMAXNSERVERS);
129         memcpy(oentry.serverFlags, nep->serverFlags, OMAXNSERVERS);
130         bufp = (char *)&oentry; 
131     }
132     return vlwrite(trans, offset, bufp, length);
133 }
134
135 /* read entry and convert to host order and write to disk */
136 afs_int32 vlentryread(trans, offset, buffer, length)
137 struct ubik_trans   *trans;
138 afs_int32                   offset;
139 char                *buffer;
140 afs_int32                   length; {
141     struct vlentry *oep, tentry;
142     struct nvlentry *nep, *nbufp;
143     char *bufp = (char *)&tentry;
144     register afs_int32 i;
145
146     if (length != sizeof(vlentry)) return -1;
147     i = vlread(trans, offset, bufp, length);
148     if (i) return i;
149     if (maxnservers == 13) {
150         nep = (struct nvlentry *)bufp;
151         nbufp = (struct nvlentry *)buffer;
152         for(i=0;i<MAXTYPES;i++)
153             nbufp->volumeId[i] = ntohl(nep->volumeId[i]);
154         nbufp->flags = ntohl(nep->flags);
155         nbufp->LockAfsId = ntohl(nep->LockAfsId);
156         nbufp->LockTimestamp = ntohl(nep->LockTimestamp);
157         nbufp->cloneId = ntohl(nep->cloneId);
158         for(i=0;i<MAXTYPES;i++)
159             nbufp->nextIdHash[i] = ntohl(nep->nextIdHash[i]);
160         nbufp->nextNameHash = ntohl(nep->nextNameHash);
161         memcpy(nbufp->name, nep->name, VL_MAXNAMELEN);
162         memcpy(nbufp->serverNumber, nep->serverNumber, NMAXNSERVERS);
163         memcpy(nbufp->serverPartition, nep->serverPartition, NMAXNSERVERS);
164         memcpy(nbufp->serverFlags, nep->serverFlags, NMAXNSERVERS);
165     } else {
166         oep = (struct vlentry *)bufp;
167         nbufp = (struct nvlentry *)buffer;
168         memset(nbufp, 0, sizeof (struct nvlentry));
169         for(i=0;i<MAXTYPES;i++)
170             nbufp->volumeId[i] = ntohl(oep->volumeId[i]);
171         nbufp->flags = ntohl(oep->flags);
172         nbufp->LockAfsId = ntohl(oep->LockAfsId);
173         nbufp->LockTimestamp = ntohl(oep->LockTimestamp);
174         nbufp->cloneId = ntohl(oep->cloneId);
175         for(i=0;i<MAXTYPES;i++)
176             nbufp->nextIdHash[i] = ntohl(oep->nextIdHash[i]);
177         nbufp->nextNameHash = ntohl(oep->nextNameHash);
178         memcpy(nbufp->name, oep->name, VL_MAXNAMELEN);
179         memcpy(nbufp->serverNumber, oep->serverNumber, NMAXNSERVERS);
180         memcpy(nbufp->serverPartition, oep->serverPartition, NMAXNSERVERS);
181         memcpy(nbufp->serverFlags, oep->serverFlags, NMAXNSERVERS);
182     }
183     return 0;
184 }
185
186 /* Convenient write of small critical vldb header info to the database. */
187 int write_vital_vlheader(trans)
188 register struct ubik_trans  *trans;
189 {
190     if (vlwrite(trans, 0, (char *) &cheader.vital_header, sizeof(vital_vlheader))) return VL_IO;
191     return 0;
192 }
193
194
195 int extent_mod = 0;
196
197 /* This routine reads in the extent blocks for multi-homed servers.
198  * There used to be an initialization bug that would cause the contaddrs
199  * pointers in the first extent block to be bad. Here we will check the
200  * pointers and zero them in the in-memory copy if we find them bad. We
201  * also try to write the extent blocks back out. If we can't, then we
202  * will wait until the next write transaction to write them out
203  * (extent_mod tells us the on-disk copy is bad).
204  */
205 afs_int32 readExtents(trans)
206   struct ubik_trans *trans;
207 {
208   afs_uint32 extentAddr;
209   afs_int32 error=0, code;
210   int i;
211
212   extent_mod = 0;
213   extentAddr = ntohl(cheader.SIT);
214   if (!extentAddr)
215      return 0;
216             
217   /* Read the first extension block */
218   if (!ex_addr[0]) {
219      ex_addr[0] = (struct extentaddr *)malloc(VL_ADDREXTBLK_SIZE);
220      if (!ex_addr[0])
221         ERROR_EXIT(VL_NOMEM);
222   }
223   code = vlread(trans, extentAddr, (char *)ex_addr[0], VL_ADDREXTBLK_SIZE);
224   if (code) {
225      free(ex_addr[0]);    /* Not the place to create it */
226      ex_addr[0] = 0;
227      ERROR_EXIT(VL_IO);
228   }
229
230   /* In case more that 64 mh servers are in use they're kept in these
231    * continuation blocks
232    */
233   for (i=1; i < VL_MAX_ADDREXTBLKS; i++) {
234      if (!ex_addr[0]->ex_contaddrs[i])
235         continue;
236
237      /* Before reading it in, check to see if the address is good */
238      if ( (ntohl(ex_addr[0]->ex_contaddrs[i]) < 
239            ntohl(ex_addr[0]->ex_contaddrs[i-1])+VL_ADDREXTBLK_SIZE) ||
240           (ntohl(ex_addr[0]->ex_contaddrs[i]) > 
241            ntohl(cheader.vital_header.eofPtr)-VL_ADDREXTBLK_SIZE) ) {
242         extent_mod = 1;
243         ex_addr[0]->ex_contaddrs[i] = 0;
244         continue;
245      }
246
247
248      /* Read the continuation block */
249      if (!ex_addr[i]) {
250         ex_addr[i] = (struct extentaddr *)malloc(VL_ADDREXTBLK_SIZE);
251         if (!ex_addr[i])
252            ERROR_EXIT(VL_NOMEM);
253      }
254      code = vlread(trans, ntohl(ex_addr[0]->ex_contaddrs[i]),
255                    (char *)ex_addr[i], VL_ADDREXTBLK_SIZE);
256      if (code) {
257         free(ex_addr[i]);    /* Not the place to create it */
258         ex_addr[i] = 0;
259         ERROR_EXIT(VL_IO);
260      }
261
262      /* After reading it in, check to see if its a real continuation block */
263      if (ntohl(ex_addr[i]->ex_flags) != VLCONTBLOCK) {
264         extent_mod = 1;
265         ex_addr[0]->ex_contaddrs[i] = 0;
266         free(ex_addr[i]);    /* Not the place to create it */
267         ex_addr[i] = 0;
268         continue;
269      }
270   }
271
272   if (extent_mod) {
273      code = vlwrite(trans, extentAddr, ex_addr[0], VL_ADDREXTBLK_SIZE);
274      if (!code) {
275         VLog(0, ("Multihome server support modification\n"));
276      }
277      /* Keep extent_mod true in-case the transaction aborts */
278      /* Don't return error so we don't abort transaction */
279   }
280
281  error_exit:
282   return error;
283 }
284
285 /* Check that the database has been initialized.  Be careful to fail in a safe
286    manner, to avoid bogusly reinitializing the db.  */
287 afs_int32 CheckInit (trans, builddb)
288     struct ubik_trans   *trans;
289     int builddb;
290 {
291     afs_int32   error=0, i, code, ubcode=0;
292
293     /* ubik_CacheUpdate must be called on every transaction.  It returns 0 if the
294      * previous transaction would have left the cache fine, and non-zero otherwise.
295      * Thus, a local abort or a remote commit will cause this to return non-zero
296      * and force a header re-read.  Necessary for a local abort because we may
297      * have damaged cheader during the operation.  Necessary for a remote commit
298      * since it may have changed cheader. 
299      */
300     if (ubik_CacheUpdate(trans) != 0) {
301         /* if version changed (or first call), read the header */
302         ubcode = vlread (trans, 0, (char *) &cheader, sizeof(cheader));
303         vldbversion = ntohl(cheader.vital_header.vldbversion);
304
305         if (!ubcode && (vldbversion != 0)) {
306             memcpy(HostAddress, cheader.IpMappedAddr, sizeof(cheader.IpMappedAddr));
307             for (i=0; i<MAXSERVERID+1; i++) {  /* cvt HostAddress to host order */
308                 HostAddress[i] = ntohl(HostAddress[i]);
309             }
310
311             code = readExtents(trans);
312             if (code)
313                ERROR_EXIT(code);
314         }
315     }
316
317     vldbversion = ntohl(cheader.vital_header.vldbversion);
318
319     /* now, if can't read, or header is wrong, write a new header */
320     if (ubcode || vldbversion == 0) {
321         if (builddb) {
322             printf("Can't read VLDB header, re-initialising...\n");
323
324             /* try to write a good header */
325             memset(&cheader, 0, sizeof(cheader));
326             cheader.vital_header.vldbversion = htonl(VLDBVERSION);
327             cheader.vital_header.headersize = htonl(sizeof(cheader));
328             /* DANGER: Must get this from a master place!! */
329             cheader.vital_header.MaxVolumeId = htonl(0x20000000);
330             cheader.vital_header.eofPtr = htonl(sizeof(cheader));
331             for (i=0; i<MAXSERVERID+1; i++) {
332                 cheader.IpMappedAddr[i] = 0;
333                 HostAddress[i] = 0;
334             }
335             code = vlwrite(trans, 0, (char *) &cheader, sizeof(cheader));
336             if (code) {
337                 printf("Can't write VLDB header (error = %d)\n", code);
338                 ERROR_EXIT(VL_IO);
339             }
340         }
341         else ERROR_EXIT(VL_EMPTY);
342     }
343     else if ((vldbversion != VLDBVERSION)  &&
344              (vldbversion != OVLDBVERSION) &&
345              (vldbversion != VLDBVERSION_4)) {
346         printf("VLDB version %d doesn't match this software version(%d, %d or %d), quitting!\n", 
347                vldbversion, VLDBVERSION_4, VLDBVERSION, OVLDBVERSION);
348         ERROR_EXIT(VL_BADVERSION);
349     }
350
351     maxnservers = ((vldbversion == 3 || vldbversion == 4) ? 13 : 8);
352       
353   error_exit:
354     /* all done */
355     return error;
356 }
357
358
359 afs_int32 GetExtentBlock(trans, base) 
360     register struct ubik_trans  *trans;
361     register afs_int32 base;
362 {
363     afs_int32  blockindex, code, error = 0;
364     
365     /* Base 0 must exist before any other can be created */
366     if ((base != 0) && !ex_addr[0])
367        ERROR_EXIT(VL_CREATEFAIL);  /* internal error */
368
369     if (!ex_addr[0] || !ex_addr[0]->ex_contaddrs[base]) {
370         /* Create a new extension block */
371         if (!ex_addr[base]) {
372             ex_addr[base] = (struct extentaddr *)malloc(VL_ADDREXTBLK_SIZE); 
373             if (!ex_addr[base])
374                ERROR_EXIT(VL_NOMEM);
375         }
376         memset((char *)ex_addr[base], 0, VL_ADDREXTBLK_SIZE);
377
378         /* Write the full extension block at end of vldb */
379         ex_addr[base]->ex_flags = htonl(VLCONTBLOCK);
380         blockindex = ntohl(cheader.vital_header.eofPtr);
381         code = vlwrite(trans, blockindex, (char *)ex_addr[base], VL_ADDREXTBLK_SIZE);
382         if (code) ERROR_EXIT(VL_IO);
383
384         /* Update the cheader.vitalheader structure on disk */
385         cheader.vital_header.eofPtr = blockindex + VL_ADDREXTBLK_SIZE;
386         cheader.vital_header.eofPtr = htonl(cheader.vital_header.eofPtr);
387         code = write_vital_vlheader(trans);
388         if (code) ERROR_EXIT(VL_IO);
389         
390         /* Write the address of the base extension block in the vldb header */
391         if (base == 0) {
392             cheader.SIT = htonl(blockindex);
393             code = vlwrite(trans, DOFFSET(0,&cheader,&cheader.SIT), 
394                            (char *)&cheader.SIT, sizeof(cheader.SIT));
395             if (code) ERROR_EXIT(VL_IO);
396         }
397
398         /* Write the address of this extension block into the base extension block */
399         ex_addr[0]->ex_contaddrs[base] = htonl(blockindex);
400         code = vlwrite(trans, ntohl(cheader.SIT), ex_addr[0], sizeof(struct extentaddr));
401         if (code) ERROR_EXIT(VL_IO);
402     }
403
404   error_exit:
405     return error;
406 }
407
408
409 afs_int32 FindExtentBlock(trans, uuidp, createit, hostslot, expp, basep)
410     register struct ubik_trans  *trans;
411     afsUUID *uuidp;
412     afs_int32 createit, hostslot, *basep;
413     struct extentaddr **expp;
414 {
415     afsUUID tuuid;
416     struct extentaddr *exp;    
417     register afs_int32 i, j, code, base, index, error=0;        
418
419     *expp = (struct extentaddr *)0;
420     *basep = 0;
421
422     /* Create the first extension block if it does not exist */
423     if (!cheader.SIT) {
424         code = GetExtentBlock(trans, 0);
425         if (code) ERROR_EXIT(code);
426     }
427
428     for (i = 0; i < MAXSERVERID+1; i++) {
429         if ((HostAddress[i] & 0xff000000) == 0xff000000) {
430             if ((base = (HostAddress[i] >> 16) & 0xff) > VL_MAX_ADDREXTBLKS) {
431                 ERROR_EXIT(VL_INDEXERANGE);
432             }
433             if ((index = HostAddress[i] & 0x0000ffff) > VL_MHSRV_PERBLK) {
434                 ERROR_EXIT(VL_INDEXERANGE);
435             }
436             exp = &ex_addr[base][index];
437             tuuid = exp->ex_hostuuid;
438             afs_ntohuuid(&tuuid);
439             if (afs_uuid_equal(uuidp, &tuuid)) {
440                 *expp = exp;
441                 *basep = base;
442                 ERROR_EXIT(0);
443             }
444         }
445     }
446
447     if (createit) {
448         if (hostslot == -1) {
449             for (i=0; i<MAXSERVERID+1; i++) {
450                 if (!HostAddress[i]) break;
451             }
452             if (i > MAXSERVERID) ERROR_EXIT(VL_REPSFULL);
453         } else {
454             i = hostslot;
455         }
456
457         for (base=0; base<VL_MAX_ADDREXTBLKS; base++) {
458             if (!ex_addr[0]->ex_contaddrs[base]) {
459                 code = GetExtentBlock(trans, base);
460                 if (code) ERROR_EXIT(code);
461             }
462             for (j=1; j<VL_MHSRV_PERBLK; j++) {
463                 exp = &ex_addr[base][j];
464                 tuuid = exp->ex_hostuuid;
465                 afs_ntohuuid(&tuuid);
466                 if (afs_uuid_is_nil(&tuuid)) {
467                     tuuid = *uuidp;
468                     afs_htonuuid(&tuuid);
469                     exp->ex_hostuuid = tuuid;
470                     code = vlwrite(trans, DOFFSET(ntohl(ex_addr[0]->ex_contaddrs[base]), 
471                                                   (char *)ex_addr[base], (char *)exp),
472                                    (char *) &tuuid, sizeof(tuuid));
473                     if (code) ERROR_EXIT(VL_IO);
474                     HostAddress[i] = 0xff000000 | ((base << 16) & 0xff0000) | (j & 0xffff);
475                     *expp = exp;
476                     *basep = base;
477                     if (vldbversion != VLDBVERSION_4) {
478                         cheader.vital_header.vldbversion = htonl(VLDBVERSION_4);
479                         code = write_vital_vlheader(trans);
480                         if (code) ERROR_EXIT(VL_IO);
481                     }
482                     cheader.IpMappedAddr[i] = htonl(HostAddress[i]);
483                     code = vlwrite(trans, DOFFSET(0, &cheader, &cheader.IpMappedAddr[i]),
484                                    (char *) &cheader.IpMappedAddr[i], sizeof(afs_int32));
485                     if (code) ERROR_EXIT(VL_IO);
486                     ERROR_EXIT(0);
487                 }
488             }
489         }
490         ERROR_EXIT(VL_REPSFULL);        /* No reason to utilize a new error code */
491     }
492
493   error_exit:
494     return error;
495 }
496
497 /* Allocate a free block of storage for entry, returning address of a new
498    zeroed entry (or zero if something is wrong).  */
499 afs_int32 AllocBlock (trans, tentry)
500 register struct ubik_trans  *trans;
501 struct nvlentry             *tentry;
502 {   register afs_int32    blockindex;
503
504     if (cheader.vital_header.freePtr) {
505         /* allocate this dude */
506         blockindex = ntohl(cheader.vital_header.freePtr);
507         if (vlentryread(trans, blockindex, (char *) tentry, sizeof(vlentry))) return 0;
508         cheader.vital_header.freePtr = htonl(tentry->nextIdHash[0]);
509     }
510     else {
511         /* hosed, nothing on free list, grow file */
512         blockindex = ntohl(cheader.vital_header.eofPtr);        /* remember this guy */
513         cheader.vital_header.eofPtr = htonl(blockindex + sizeof(vlentry));
514     }
515     cheader.vital_header.allocs++;
516     if (write_vital_vlheader(trans))   return 0;
517     memset(tentry, 0, sizeof(nvlentry));        /* zero new entry */
518     return blockindex;
519 }
520
521
522 /* Free a block given its index.  It must already have been unthreaded. Returns zero for success or an error code on failure. */
523 int FreeBlock (trans, blockindex)
524 struct ubik_trans       *trans;
525 afs_int32                       blockindex;
526 {   struct nvlentry     tentry;
527
528     /* check validity of blockindex just to be on the safe side */
529     if (!index_OK (trans, blockindex)) return VL_BADINDEX;
530     memset(&tentry, 0, sizeof(nvlentry));
531     tentry.nextIdHash[0] = cheader.vital_header.freePtr; /* already in network order */
532     tentry.flags = htonl(VLFREE);
533     cheader.vital_header.freePtr = htonl(blockindex);
534     if (vlwrite (trans, blockindex, (char *)&tentry, sizeof(nvlentry))) 
535         return VL_IO;
536     cheader.vital_header.frees++;
537     if (write_vital_vlheader(trans))   return VL_IO;
538     return 0;
539 }
540
541
542 /* Look for a block by volid and voltype (if not known use -1 which searches all 3 volid hash lists. Note that the linked lists are read in first from the database header.  If found read the block's contents into the area pointed to by tentry and return the block's index.  If not found return 0. */
543 afs_int32 FindByID (trans, volid, voltype, tentry, error)
544 struct ubik_trans   *trans;
545 afs_int32                   volid;
546 afs_int32                   voltype;
547 struct nvlentry     *tentry;
548 afs_int32                   *error;
549 {   register afs_int32   typeindex, hashindex, blockindex;
550
551     *error = 0;
552     hashindex = IDHash(volid);
553     if (voltype == -1) {
554 /* Should we have one big hash table for volids as opposed to the three ones? */
555         for (typeindex = 0; typeindex < MAXTYPES; typeindex++) {
556             for (blockindex = ntohl(cheader.VolidHash[typeindex][hashindex]); blockindex != NULLO; blockindex = tentry->nextIdHash[typeindex]) {
557                 if (vlentryread(trans, blockindex, (char *) tentry, sizeof(nvlentry))) {
558                     *error = VL_IO;
559                     return 0;
560                 }
561                 if (volid == tentry->volumeId[typeindex])
562                     return blockindex;
563             }
564         }
565     } else {
566         for (blockindex = ntohl(cheader.VolidHash[voltype][hashindex]); blockindex != NULLO; blockindex = tentry->nextIdHash[voltype]) {
567             if (vlentryread(trans, blockindex, (char *) tentry, sizeof(nvlentry))) {
568                 *error = VL_IO;
569                 return 0;
570             }
571             if (volid == tentry->volumeId[voltype])
572                 return blockindex;
573         }
574     }
575     return 0;                           /* no such entry */
576 }
577
578
579 /* Look for a block by volume name. If found read the block's contents into the area pointed to by tentry and return the block's index.  If not found return 0. */
580 afs_int32 FindByName (trans, volname, tentry, error)
581 struct ubik_trans   *trans;
582 char                *volname;
583 struct nvlentry     *tentry;
584 afs_int32                   *error;
585 {   register afs_int32   hashindex;
586     register afs_int32   blockindex;
587     char tname[VL_MAXNAMELEN];
588
589     /* remove .backup or .readonly extensions for stupid backwards compatibility */
590     hashindex = strlen(volname);    /* really string length */
591     if (hashindex >= 8 && strcmp(volname+hashindex-7, ".backup")==0) {
592         /* this is a backup volume */
593         strcpy(tname, volname);
594         tname[hashindex-7] = 0; /* zap extension */
595     }
596     else if (hashindex >= 10 && strcmp(volname+hashindex-9, ".readonly")==0) {
597         /* this is a readonly volume */
598         strcpy(tname, volname);
599         tname[hashindex-9] = 0; /* zap extension */
600     }
601     else strcpy(tname, volname);
602
603     *error = 0;
604     hashindex = NameHash(tname);
605     for (blockindex = ntohl(cheader.VolnameHash[hashindex]); blockindex != NULLO; blockindex = tentry->nextNameHash) {
606         if (vlentryread(trans, blockindex, (char *) tentry, sizeof(nvlentry))) {
607             *error = VL_IO;
608             return 0;
609         }
610         if (!strcmp(tname, tentry->name))
611             return blockindex;
612     }
613     return 0;                           /* no such entry */
614 }
615
616 int HashNDump (trans, hashindex)
617 struct ubik_trans   *trans;
618 int                 hashindex;
619
620 {   register int   i=0;
621     register int   blockindex;
622     struct nvlentry  tentry;
623
624     for (blockindex = ntohl(cheader.VolnameHash[hashindex]); blockindex != NULLO; blockindex = tentry.nextNameHash) {
625         if (vlentryread(trans, blockindex, (char *) &tentry, sizeof(nvlentry))) return 0;
626         i++;
627         VLog(0, ("[%d]#%d: %10d %d %d (%s)\n", hashindex, i, tentry.volumeId[0],
628               tentry.nextIdHash[0], tentry.nextNameHash, tentry.name));
629     }
630     return 0;
631 }
632
633
634 int HashIdDump (trans, hashindex)
635 struct ubik_trans   *trans;
636 int                 hashindex;
637
638 {   register int   i=0;
639     register int   blockindex;
640     struct nvlentry  tentry;
641
642     for (blockindex = ntohl(cheader.VolidHash[0][hashindex]); blockindex != NULLO; blockindex = tentry.nextIdHash[0]) {
643         if (vlentryread(trans, blockindex, (char *) &tentry, sizeof(nvlentry))) return 0;
644         i++;
645         VLog(0, ("[%d]#%d: %10d %d %d (%s)\n", hashindex, i, tentry.volumeId[0],
646               tentry.nextIdHash[0],  tentry.nextNameHash, tentry.name));
647     }
648     return 0;
649 }
650
651
652 /* Add a block to the hash table given a pointer to the block and its index. The block is threaded onto both hash tables and written to disk.  The routine returns zero if there were no errors. */
653 int ThreadVLentry (trans, blockindex, tentry)
654 struct ubik_trans   *trans;
655 afs_int32                   blockindex;
656 struct nvlentry     *tentry;
657 {   int             errorcode;
658
659     if (!index_OK(trans, blockindex)) return VL_BADINDEX;
660     /* Insert into volid's hash linked list */
661     if (errorcode = HashVolid(trans, RWVOL, blockindex, tentry))
662         return errorcode;
663
664     /* For rw entries we also enter the RO and BACK volume ids (if they exist) in the hash tables; note all there volids (RW, RO, BACK) should not be hashed yet! */
665     if (tentry->volumeId[ROVOL]) {
666         if (errorcode = HashVolid(trans, ROVOL, blockindex, tentry))
667             return errorcode;
668     }   
669     if (tentry->volumeId[BACKVOL]) {
670         if (errorcode = HashVolid(trans, BACKVOL, blockindex, tentry))
671             return errorcode;
672     }  
673     
674     /* Insert into volname's hash linked list */
675     HashVolname(trans, blockindex, tentry);
676
677     /* Update cheader entry */
678     if (write_vital_vlheader(trans)) return VL_IO;
679
680     /* Update hash list pointers in the entry itself */
681     if (vlentrywrite(trans, blockindex, (char *)tentry, sizeof(nvlentry))) return VL_IO;
682     return 0;
683 }
684
685
686 /* Remove a block from both the hash tables.  If success return 0, else return an error code. */
687 int UnthreadVLentry (trans, blockindex, aentry)
688 struct ubik_trans   *trans;
689 afs_int32                   blockindex;
690 struct nvlentry     *aentry;
691 {   register afs_int32   errorcode, typeindex;
692
693     if (!index_OK(trans, blockindex)) return VL_BADINDEX;
694     if (errorcode = UnhashVolid(trans, RWVOL, blockindex, aentry))
695         return errorcode;
696
697     /* Take the RO/RW entries of their respective hash linked lists. */
698     for (typeindex = ROVOL; typeindex <= BACKVOL; typeindex++) {
699         if (errorcode = UnhashVolid(trans, typeindex, blockindex, aentry))
700             return errorcode;
701     }
702
703     /* Take it out of the Volname hash list */
704     if (errorcode = UnhashVolname(trans, blockindex, aentry))
705         return errorcode;
706
707     /* Update cheader entry */
708     write_vital_vlheader(trans);
709
710     return 0;
711 }
712
713 /* cheader must have be read before this routine is called. */
714 int HashVolid(trans, voltype, blockindex, tentry)
715 struct ubik_trans   *trans;
716 afs_int32                   voltype;
717 afs_int32                   blockindex;
718 struct nvlentry     *tentry;
719 {   afs_int32 hashindex, errorcode;
720     struct vlentry ventry;
721
722     if (FindByID(trans, tentry->volumeId[voltype], voltype, &ventry, &errorcode))
723         return VL_IDALREADYHASHED;
724     else if (errorcode) return errorcode;
725     hashindex = IDHash(tentry->volumeId[voltype]);
726     tentry->nextIdHash[voltype] = ntohl(cheader.VolidHash[voltype][hashindex]);
727     cheader.VolidHash[voltype][hashindex] = htonl(blockindex);
728     if (vlwrite(trans, DOFFSET(0, &cheader, &cheader.VolidHash[voltype][hashindex]), (char *) &cheader.VolidHash[voltype][hashindex], sizeof(afs_int32))) return VL_IO;
729     return 0;
730 }
731
732
733 /* cheader must have be read before this routine is called. */
734 int UnhashVolid(trans, voltype, blockindex, aentry)
735 struct ubik_trans   *trans;
736 afs_int32                   voltype;
737 afs_int32                   blockindex;
738 struct nvlentry     *aentry;
739 {   int hashindex, nextblockindex, prevblockindex;
740     struct nvlentry tentry;
741     afs_int32 code;
742     afs_int32 temp;
743
744     if (aentry->volumeId[voltype] == NULLO)     /* Assume no volume id */
745         return 0;
746     /* Take it out of the VolId[voltype] hash list */
747     hashindex = IDHash(aentry->volumeId[voltype]);
748     nextblockindex = ntohl(cheader.VolidHash[voltype][hashindex]);
749     if (nextblockindex == blockindex) {
750         /* First on the hash list; just adjust pointers */
751         cheader.VolidHash[voltype][hashindex] = htonl(aentry->nextIdHash[voltype]);
752         code = vlwrite(trans, DOFFSET(0, &cheader, &cheader.VolidHash[voltype][hashindex]), (char *) &cheader.VolidHash[voltype][hashindex], sizeof(afs_int32));
753         if (code) return VL_IO;
754     } else {
755         while (nextblockindex != blockindex) {
756             prevblockindex = nextblockindex;    /* always done once */
757             if (vlentryread(trans, nextblockindex, (char *)&tentry, sizeof(nvlentry))) 
758                 return VL_IO;
759             if ((nextblockindex = tentry.nextIdHash[voltype]) == NULLO)
760                 return VL_NOENT;
761         }
762         temp = tentry.nextIdHash[voltype] = aentry->nextIdHash[voltype];
763         temp = htonl(temp);     /* convert to network byte order before writing */
764         if (vlwrite(trans, DOFFSET(prevblockindex, &tentry, &tentry.nextIdHash[voltype]), (char *)&temp, sizeof(afs_int32)))
765             return VL_IO;
766     }
767     aentry->nextIdHash[voltype] = 0;
768     return 0;
769 }
770
771
772 int HashVolname(trans, blockindex, aentry)
773 struct ubik_trans   *trans;
774 afs_int32                   blockindex;
775 struct nvlentry     *aentry;
776 {   register afs_int32   hashindex;
777     register afs_int32 code;
778
779     /* Insert into volname's hash linked list */
780     hashindex = NameHash(aentry->name);
781     aentry->nextNameHash = ntohl(cheader.VolnameHash[hashindex]);
782     cheader.VolnameHash[hashindex] = htonl(blockindex);
783     code = vlwrite(trans, DOFFSET(0, &cheader, &cheader.VolnameHash[hashindex]), (char *) &cheader.VolnameHash[hashindex], sizeof(afs_int32));
784     if (code) return VL_IO;
785     return 0;
786 }
787
788
789 int UnhashVolname(trans, blockindex, aentry)
790 struct ubik_trans   *trans;
791 afs_int32                   blockindex;
792 struct nvlentry     *aentry;
793 {   register afs_int32   hashindex, nextblockindex, prevblockindex;
794     struct nvlentry  tentry;
795     afs_int32 temp;
796
797     /* Take it out of the Volname hash list */
798     hashindex = NameHash(aentry->name);
799     nextblockindex = ntohl(cheader.VolnameHash[hashindex]);
800     if (nextblockindex == blockindex) {
801         /* First on the hash list; just adjust pointers */
802         cheader.VolnameHash[hashindex] = htonl(aentry->nextNameHash);
803         if (vlwrite(trans, DOFFSET(0, &cheader, &cheader.VolnameHash[hashindex]), (char *) &cheader.VolnameHash[hashindex], sizeof(afs_int32))) return VL_IO;
804     } else {
805         while (nextblockindex != blockindex) {
806             prevblockindex = nextblockindex;    /* always done at least once */
807             if (vlentryread(trans, nextblockindex, (char *)&tentry, sizeof(nvlentry)))
808                 return VL_IO;
809             if ((nextblockindex = tentry.nextNameHash) == NULLO)
810                 return VL_NOENT;
811         }
812         tentry.nextNameHash = aentry->nextNameHash;
813         temp = htonl(tentry.nextNameHash);
814         if (vlwrite(trans, DOFFSET(prevblockindex, &tentry, &tentry.nextNameHash), (char *)&temp, sizeof(afs_int32)))
815             return VL_IO;
816     }
817     aentry->nextNameHash = 0;
818     return 0;
819 }
820
821
822 /* Returns the vldb entry tentry at offset index; remaining is the number of entries left; the routine also returns the index of the next sequential entry in the vldb */
823
824 afs_int32 NextEntry (trans, blockindex, tentry, remaining)
825 struct ubik_trans   *trans;
826 afs_int32                   blockindex;
827 struct nvlentry     *tentry;
828 afs_int32                   *remaining;
829 {   register afs_int32    lastblockindex;
830
831     if (blockindex == 0)                        /* get first one */
832         blockindex = sizeof(cheader);
833     else {
834         if (!index_OK (trans, blockindex)) {
835             *remaining = -1;            /* error */
836             return 0;
837         }
838         blockindex += sizeof(nvlentry);
839     }
840     /* now search for the first entry that isn't free */
841     for (lastblockindex = ntohl(cheader.vital_header.eofPtr); blockindex < lastblockindex;) {
842         if (vlentryread(trans, blockindex, (char *)tentry, sizeof(nvlentry))) {
843             *remaining = -1;
844             return 0;
845         }
846         if (tentry->flags == VLCONTBLOCK) {
847             /*
848              * This is a special mh extension block just simply skip over it
849              */
850             blockindex += VL_ADDREXTBLK_SIZE;
851         } else {
852             if (tentry->flags != VLFREE) {
853                 /* estimate remaining number of entries, not including this one */
854                 *remaining = (lastblockindex - blockindex) / sizeof(nvlentry) - 1;
855                 return blockindex;
856             }
857             blockindex += sizeof(nvlentry);
858         }
859     }
860     *remaining = 0;                     /* no more entries */
861     return 0;
862 }
863
864
865 /* Routine to verify that index is a legal offset to a vldb entry in the table */
866 static int index_OK (trans, blockindex)
867 struct ubik_trans   *trans;
868 afs_int32                   blockindex;
869 {
870     if ((blockindex < sizeof(cheader)) || (blockindex >= ntohl(cheader.vital_header.eofPtr))) 
871         return 0;
872     return 1;
873 }
874
875