convert-from-bsd-to-posix-string-and-memory-functions-20010807
[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, *oep;
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, *obufp, 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                         vldbversion != VLDBVERSION_4;
479                         cheader.vital_header.vldbversion = htonl(VLDBVERSION_4);
480                         code = write_vital_vlheader(trans);
481                         if (code) ERROR_EXIT(VL_IO);
482                     }
483                     cheader.IpMappedAddr[i] = htonl(HostAddress[i]);
484                     code = vlwrite(trans, DOFFSET(0, &cheader, &cheader.IpMappedAddr[i]),
485                                    (char *) &cheader.IpMappedAddr[i], sizeof(afs_int32));
486                     if (code) ERROR_EXIT(VL_IO);
487                     ERROR_EXIT(0);
488                 }
489             }
490         }
491         ERROR_EXIT(VL_REPSFULL);        /* No reason to utilize a new error code */
492     }
493
494   error_exit:
495     return error;
496 }
497
498 /* Allocate a free block of storage for entry, returning address of a new
499    zeroed entry (or zero if something is wrong).  */
500 afs_int32 AllocBlock (trans, tentry)
501 register struct ubik_trans  *trans;
502 struct nvlentry             *tentry;
503 {   register afs_int32    blockindex;
504
505     if (cheader.vital_header.freePtr) {
506         /* allocate this dude */
507         blockindex = ntohl(cheader.vital_header.freePtr);
508         if (vlentryread(trans, blockindex, (char *) tentry, sizeof(vlentry))) return 0;
509         cheader.vital_header.freePtr = htonl(tentry->nextIdHash[0]);
510     }
511     else {
512         /* hosed, nothing on free list, grow file */
513         blockindex = ntohl(cheader.vital_header.eofPtr);        /* remember this guy */
514         cheader.vital_header.eofPtr = htonl(blockindex + sizeof(vlentry));
515     }
516     cheader.vital_header.allocs++;
517     if (write_vital_vlheader(trans))   return 0;
518     memset(tentry, 0, sizeof(nvlentry));        /* zero new entry */
519     return blockindex;
520 }
521
522
523 /* Free a block given its index.  It must already have been unthreaded. Returns zero for success or an error code on failure. */
524 int FreeBlock (trans, blockindex)
525 struct ubik_trans       *trans;
526 afs_int32                       blockindex;
527 {   struct nvlentry     tentry;
528
529     /* check validity of blockindex just to be on the safe side */
530     if (!index_OK (trans, blockindex)) return VL_BADINDEX;
531     memset(&tentry, 0, sizeof(nvlentry));
532     tentry.nextIdHash[0] = cheader.vital_header.freePtr; /* already in network order */
533     tentry.flags = htonl(VLFREE);
534     cheader.vital_header.freePtr = htonl(blockindex);
535     if (vlwrite (trans, blockindex, (char *)&tentry, sizeof(nvlentry))) 
536         return VL_IO;
537     cheader.vital_header.frees++;
538     if (write_vital_vlheader(trans))   return VL_IO;
539     return 0;
540 }
541
542
543 /* 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. */
544 afs_int32 FindByID (trans, volid, voltype, tentry, error)
545 struct ubik_trans   *trans;
546 afs_int32                   volid;
547 afs_int32                   voltype;
548 struct nvlentry     *tentry;
549 afs_int32                   *error;
550 {   register afs_int32   typeindex, hashindex, blockindex;
551
552     *error = 0;
553     hashindex = IDHash(volid);
554     if (voltype == -1) {
555 /* Should we have one big hash table for volids as opposed to the three ones? */
556         for (typeindex = 0; typeindex < MAXTYPES; typeindex++) {
557             for (blockindex = ntohl(cheader.VolidHash[typeindex][hashindex]); blockindex != NULLO; blockindex = tentry->nextIdHash[typeindex]) {
558                 if (vlentryread(trans, blockindex, (char *) tentry, sizeof(nvlentry))) {
559                     *error = VL_IO;
560                     return 0;
561                 }
562                 if (volid == tentry->volumeId[typeindex])
563                     return blockindex;
564             }
565         }
566     } else {
567         for (blockindex = ntohl(cheader.VolidHash[voltype][hashindex]); blockindex != NULLO; blockindex = tentry->nextIdHash[voltype]) {
568             if (vlentryread(trans, blockindex, (char *) tentry, sizeof(nvlentry))) {
569                 *error = VL_IO;
570                 return 0;
571             }
572             if (volid == tentry->volumeId[voltype])
573                 return blockindex;
574         }
575     }
576     return 0;                           /* no such entry */
577 }
578
579
580 /* 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. */
581 afs_int32 FindByName (trans, volname, tentry, error)
582 struct ubik_trans   *trans;
583 char                *volname;
584 struct nvlentry     *tentry;
585 afs_int32                   *error;
586 {   register afs_int32   hashindex;
587     register afs_int32   blockindex;
588     char tname[VL_MAXNAMELEN];
589
590     /* remove .backup or .readonly extensions for stupid backwards compatibility */
591     hashindex = strlen(volname);    /* really string length */
592     if (hashindex >= 8 && strcmp(volname+hashindex-7, ".backup")==0) {
593         /* this is a backup volume */
594         strcpy(tname, volname);
595         tname[hashindex-7] = 0; /* zap extension */
596     }
597     else if (hashindex >= 10 && strcmp(volname+hashindex-9, ".readonly")==0) {
598         /* this is a readonly volume */
599         strcpy(tname, volname);
600         tname[hashindex-9] = 0; /* zap extension */
601     }
602     else strcpy(tname, volname);
603
604     *error = 0;
605     hashindex = NameHash(tname);
606     for (blockindex = ntohl(cheader.VolnameHash[hashindex]); blockindex != NULLO; blockindex = tentry->nextNameHash) {
607         if (vlentryread(trans, blockindex, (char *) tentry, sizeof(nvlentry))) {
608             *error = VL_IO;
609             return 0;
610         }
611         if (!strcmp(tname, tentry->name))
612             return blockindex;
613     }
614     return 0;                           /* no such entry */
615 }
616
617 int HashNDump (trans, hashindex)
618 struct ubik_trans   *trans;
619 int                 hashindex;
620
621 {   register int   i=0;
622     register int   blockindex;
623     struct nvlentry  tentry;
624
625     for (blockindex = ntohl(cheader.VolnameHash[hashindex]); blockindex != NULLO; blockindex = tentry.nextNameHash) {
626         if (vlentryread(trans, blockindex, (char *) &tentry, sizeof(nvlentry))) return 0;
627         i++;
628         VLog(0, ("[%d]#%d: %10d %d %d (%s)\n", hashindex, i, tentry.volumeId[0],
629               tentry.nextIdHash[0], tentry.nextNameHash, tentry.name));
630     }
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 }
649
650
651 /* 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. */
652 int ThreadVLentry (trans, blockindex, tentry)
653 struct ubik_trans   *trans;
654 afs_int32                   blockindex;
655 struct nvlentry     *tentry;
656 {   int             errorcode;
657
658     if (!index_OK(trans, blockindex)) return VL_BADINDEX;
659     /* Insert into volid's hash linked list */
660     if (errorcode = HashVolid(trans, RWVOL, blockindex, tentry))
661         return errorcode;
662
663     /* 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! */
664     if (tentry->volumeId[ROVOL]) {
665         if (errorcode = HashVolid(trans, ROVOL, blockindex, tentry))
666             return errorcode;
667     }   
668     if (tentry->volumeId[BACKVOL]) {
669         if (errorcode = HashVolid(trans, BACKVOL, blockindex, tentry))
670             return errorcode;
671     }  
672     
673     /* Insert into volname's hash linked list */
674     HashVolname(trans, blockindex, tentry);
675
676     /* Update cheader entry */
677     if (write_vital_vlheader(trans)) return VL_IO;
678
679     /* Update hash list pointers in the entry itself */
680     if (vlentrywrite(trans, blockindex, (char *)tentry, sizeof(nvlentry))) return VL_IO;
681     return 0;
682 }
683
684
685 /* Remove a block from both the hash tables.  If success return 0, else return an error code. */
686 int UnthreadVLentry (trans, blockindex, aentry)
687 struct ubik_trans   *trans;
688 afs_int32                   blockindex;
689 struct nvlentry     *aentry;
690 {   register afs_int32   errorcode, typeindex;
691
692     if (!index_OK(trans, blockindex)) return VL_BADINDEX;
693     if (errorcode = UnhashVolid(trans, RWVOL, blockindex, aentry))
694         return errorcode;
695
696     /* Take the RO/RW entries of their respective hash linked lists. */
697     for (typeindex = ROVOL; typeindex <= BACKVOL; typeindex++) {
698         if (errorcode = UnhashVolid(trans, typeindex, blockindex, aentry))
699             return errorcode;
700     }
701
702     /* Take it out of the Volname hash list */
703     if (errorcode = UnhashVolname(trans, blockindex, aentry))
704         return errorcode;
705
706     /* Update cheader entry */
707     write_vital_vlheader(trans);
708
709     return 0;
710 }
711
712 /* cheader must have be read before this routine is called. */
713 int HashVolid(trans, voltype, blockindex, tentry)
714 struct ubik_trans   *trans;
715 afs_int32                   voltype;
716 afs_int32                   blockindex;
717 struct nvlentry     *tentry;
718 {   afs_int32 hashindex, errorcode;
719     struct vlentry ventry;
720
721     if (FindByID(trans, tentry->volumeId[voltype], voltype, &ventry, &errorcode))
722         return VL_IDALREADYHASHED;
723     else if (errorcode) return errorcode;
724     hashindex = IDHash(tentry->volumeId[voltype]);
725     tentry->nextIdHash[voltype] = ntohl(cheader.VolidHash[voltype][hashindex]);
726     cheader.VolidHash[voltype][hashindex] = htonl(blockindex);
727     if (vlwrite(trans, DOFFSET(0, &cheader, &cheader.VolidHash[voltype][hashindex]), (char *) &cheader.VolidHash[voltype][hashindex], sizeof(afs_int32))) return VL_IO;
728     return 0;
729 }
730
731
732 /* cheader must have be read before this routine is called. */
733 int UnhashVolid(trans, voltype, blockindex, aentry)
734 struct ubik_trans   *trans;
735 afs_int32                   voltype;
736 afs_int32                   blockindex;
737 struct nvlentry     *aentry;
738 {   int hashindex, nextblockindex, prevblockindex;
739     struct nvlentry tentry;
740     afs_int32 code;
741     afs_int32 temp;
742
743     if (aentry->volumeId[voltype] == NULLO)     /* Assume no volume id */
744         return 0;
745     /* Take it out of the VolId[voltype] hash list */
746     hashindex = IDHash(aentry->volumeId[voltype]);
747     nextblockindex = ntohl(cheader.VolidHash[voltype][hashindex]);
748     if (nextblockindex == blockindex) {
749         /* First on the hash list; just adjust pointers */
750         cheader.VolidHash[voltype][hashindex] = htonl(aentry->nextIdHash[voltype]);
751         code = vlwrite(trans, DOFFSET(0, &cheader, &cheader.VolidHash[voltype][hashindex]), (char *) &cheader.VolidHash[voltype][hashindex], sizeof(afs_int32));
752         if (code) return VL_IO;
753     } else {
754         while (nextblockindex != blockindex) {
755             prevblockindex = nextblockindex;    /* always done once */
756             if (vlentryread(trans, nextblockindex, (char *)&tentry, sizeof(nvlentry))) 
757                 return VL_IO;
758             if ((nextblockindex = tentry.nextIdHash[voltype]) == NULLO)
759                 return VL_NOENT;
760         }
761         temp = tentry.nextIdHash[voltype] = aentry->nextIdHash[voltype];
762         temp = htonl(temp);     /* convert to network byte order before writing */
763         if (vlwrite(trans, DOFFSET(prevblockindex, &tentry, &tentry.nextIdHash[voltype]), (char *)&temp, sizeof(afs_int32)))
764             return VL_IO;
765     }
766     aentry->nextIdHash[voltype] = 0;
767     return 0;
768 }
769
770
771 int HashVolname(trans, blockindex, aentry)
772 struct ubik_trans   *trans;
773 afs_int32                   blockindex;
774 struct nvlentry     *aentry;
775 {   register afs_int32   hashindex;
776     register afs_int32 code;
777
778     /* Insert into volname's hash linked list */
779     hashindex = NameHash(aentry->name);
780     aentry->nextNameHash = ntohl(cheader.VolnameHash[hashindex]);
781     cheader.VolnameHash[hashindex] = htonl(blockindex);
782     code = vlwrite(trans, DOFFSET(0, &cheader, &cheader.VolnameHash[hashindex]), (char *) &cheader.VolnameHash[hashindex], sizeof(afs_int32));
783     if (code) return VL_IO;
784     return 0;
785 }
786
787
788 int UnhashVolname(trans, blockindex, aentry)
789 struct ubik_trans   *trans;
790 afs_int32                   blockindex;
791 struct nvlentry     *aentry;
792 {   register afs_int32   hashindex, nextblockindex, prevblockindex;
793     struct nvlentry  tentry;
794     afs_int32 temp;
795
796     /* Take it out of the Volname hash list */
797     hashindex = NameHash(aentry->name);
798     nextblockindex = ntohl(cheader.VolnameHash[hashindex]);
799     if (nextblockindex == blockindex) {
800         /* First on the hash list; just adjust pointers */
801         cheader.VolnameHash[hashindex] = htonl(aentry->nextNameHash);
802         if (vlwrite(trans, DOFFSET(0, &cheader, &cheader.VolnameHash[hashindex]), (char *) &cheader.VolnameHash[hashindex], sizeof(afs_int32))) return VL_IO;
803     } else {
804         while (nextblockindex != blockindex) {
805             prevblockindex = nextblockindex;    /* always done at least once */
806             if (vlentryread(trans, nextblockindex, (char *)&tentry, sizeof(nvlentry)))
807                 return VL_IO;
808             if ((nextblockindex = tentry.nextNameHash) == NULLO)
809                 return VL_NOENT;
810         }
811         tentry.nextNameHash = aentry->nextNameHash;
812         temp = htonl(tentry.nextNameHash);
813         if (vlwrite(trans, DOFFSET(prevblockindex, &tentry, &tentry.nextNameHash), (char *)&temp, sizeof(afs_int32)))
814             return VL_IO;
815     }
816     aentry->nextNameHash = 0;
817     return 0;
818 }
819
820
821 /* 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 */
822
823 afs_int32 NextEntry (trans, blockindex, tentry, remaining)
824 struct ubik_trans   *trans;
825 afs_int32                   blockindex;
826 struct nvlentry     *tentry;
827 afs_int32                   *remaining;
828 {   register afs_int32    lastblockindex;
829
830     if (blockindex == 0)                        /* get first one */
831         blockindex = sizeof(cheader);
832     else {
833         if (!index_OK (trans, blockindex)) {
834             *remaining = -1;            /* error */
835             return 0;
836         }
837         blockindex += sizeof(nvlentry);
838     }
839     /* now search for the first entry that isn't free */
840     for (lastblockindex = ntohl(cheader.vital_header.eofPtr); blockindex < lastblockindex;) {
841         if (vlentryread(trans, blockindex, (char *)tentry, sizeof(nvlentry))) {
842             *remaining = -1;
843             return 0;
844         }
845         if (tentry->flags == VLCONTBLOCK) {
846             /*
847              * This is a special mh extension block just simply skip over it
848              */
849             blockindex += VL_ADDREXTBLK_SIZE;
850         } else {
851             if (tentry->flags != VLFREE) {
852                 /* estimate remaining number of entries, not including this one */
853                 *remaining = (lastblockindex - blockindex) / sizeof(nvlentry) - 1;
854                 return blockindex;
855             }
856             blockindex += sizeof(nvlentry);
857         }
858     }
859     *remaining = 0;                     /* no more entries */
860     return 0;
861 }
862
863
864 /* Routine to verify that index is a legal offset to a vldb entry in the table */
865 static int index_OK (trans, blockindex)
866 struct ubik_trans   *trans;
867 afs_int32                   blockindex;
868 {
869     if ((blockindex < sizeof(cheader)) || (blockindex >= ntohl(cheader.vital_header.eofPtr))) 
870         return 0;
871     return 1;
872 }
873
874