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