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