2 * Copyright 2000, International Business Machines Corporation and others.
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
10 #include <afsconfig.h>
11 #include <afs/param.h>
15 #include <sys/types.h>
19 #include <netinet/in.h>
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[];
44 static int index_OK();
46 #define ERROR_EXIT(code) {error=(code); goto error_exit;}
48 /* Hashing algorithm based on the volume id; HASHSIZE must be prime */
49 afs_int32 IDHash(volumeid)
52 return((abs(volumeid)) % HASHSIZE);
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;
63 for (i=strlen(volumename), volumename += i-1; i--; volumename--)
64 hash = (hash*63) + (*((unsigned char *)volumename) - 63);
65 return(hash % HASHSIZE);
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;
75 { afs_int32 errorcode;
77 if (errorcode = ubik_Seek(trans, 0, offset)) return errorcode;
78 return(ubik_Write(trans, buffer, length));
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;
88 { afs_int32 errorcode;
90 if (errorcode = ubik_Seek(trans, 0, offset)) return errorcode;
91 return(ubik_Read(trans, buffer, length));
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;
101 struct vlentry oentry;
102 struct nvlentry nentry, *nep;
104 register afs_int32 i;
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;
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;
141 return vlwrite(trans, offset, bufp, length);
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;
150 struct vlentry *oep, tentry;
151 struct nvlentry *nep, *nbufp;
152 char *bufp = (char *)&tentry;
153 register afs_int32 i;
155 if (length != sizeof(vlentry)) return -1;
156 i = vlread(trans, offset, bufp, length);
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);
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);
195 /* Convenient write of small critical vldb header info to the database. */
196 int write_vital_vlheader(trans)
197 register struct ubik_trans *trans;
199 if (vlwrite(trans, 0, (char *) &cheader.vital_header, sizeof(vital_vlheader))) return VL_IO;
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).
214 afs_int32 readExtents(trans)
215 struct ubik_trans *trans;
217 afs_uint32 extentAddr;
218 afs_int32 error=0, code;
222 extentAddr = ntohl(cheader.SIT);
226 /* Read the first extension block */
228 ex_addr[0] = (struct extentaddr *)malloc(VL_ADDREXTBLK_SIZE);
230 ERROR_EXIT(VL_NOMEM);
232 code = vlread(trans, extentAddr, (char *)ex_addr[0], VL_ADDREXTBLK_SIZE);
234 free(ex_addr[0]); /* Not the place to create it */
239 /* In case more that 64 mh servers are in use they're kept in these
240 * continuation blocks
242 for (i=1; i < VL_MAX_ADDREXTBLKS; i++) {
243 if (!ex_addr[0]->ex_contaddrs[i])
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) ) {
252 ex_addr[0]->ex_contaddrs[i] = 0;
257 /* Read the continuation block */
259 ex_addr[i] = (struct extentaddr *)malloc(VL_ADDREXTBLK_SIZE);
261 ERROR_EXIT(VL_NOMEM);
263 code = vlread(trans, ntohl(ex_addr[0]->ex_contaddrs[i]),
264 (char *)ex_addr[i], VL_ADDREXTBLK_SIZE);
266 free(ex_addr[i]); /* Not the place to create it */
271 /* After reading it in, check to see if its a real continuation block */
272 if (ntohl(ex_addr[i]->ex_flags) != VLCONTBLOCK) {
274 ex_addr[0]->ex_contaddrs[i] = 0;
275 free(ex_addr[i]); /* Not the place to create it */
282 code = vlwrite(trans, extentAddr, ex_addr[0], VL_ADDREXTBLK_SIZE);
284 VLog(0, ("Multihome server support modification\n"));
286 /* Keep extent_mod true in-case the transaction aborts */
287 /* Don't return error so we don't abort transaction */
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;
300 afs_int32 error=0, i, code, ubcode=0;
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.
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);
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]);
320 code = readExtents(trans);
326 vldbversion = ntohl(cheader.vital_header.vldbversion);
328 /* now, if can't read, or header is wrong, write a new header */
329 if (ubcode || vldbversion == 0) {
331 printf("Can't read VLDB header, re-initialising...\n");
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;
344 code = vlwrite(trans, 0, (char *) &cheader, sizeof(cheader));
346 printf("Can't write VLDB header (error = %d)\n", code);
350 else ERROR_EXIT(VL_EMPTY);
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);
360 maxnservers = ((vldbversion == 3 || vldbversion == 4) ? 13 : 8);
368 afs_int32 GetExtentBlock(trans, base)
369 register struct ubik_trans *trans;
370 register afs_int32 base;
372 afs_int32 blockindex, code, error = 0;
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 */
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);
383 ERROR_EXIT(VL_NOMEM);
385 memset((char *)ex_addr[base], 0, VL_ADDREXTBLK_SIZE);
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);
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);
399 /* Write the address of the base extension block in the vldb header */
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);
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);
418 afs_int32 FindExtentBlock(trans, uuidp, createit, hostslot, expp, basep)
419 register struct ubik_trans *trans;
421 afs_int32 createit, hostslot, *basep;
422 struct extentaddr **expp;
425 struct extentaddr *exp;
426 register afs_int32 i, j, code, base, index, error=0;
431 /* Create the first extension block if it does not exist */
433 code = GetExtentBlock(trans, 0);
434 if (code) ERROR_EXIT(code);
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);
442 if ((index = HostAddress[i] & 0x0000ffff) > VL_MHSRV_PERBLK) {
443 ERROR_EXIT(VL_INDEXERANGE);
445 exp = &ex_addr[base][index];
446 tuuid = exp->ex_hostuuid;
447 afs_ntohuuid(&tuuid);
448 if (afs_uuid_equal(uuidp, &tuuid)) {
457 if (hostslot == -1) {
458 for (i=0; i<MAXSERVERID+1; i++) {
459 if (!HostAddress[i]) break;
461 if (i > MAXSERVERID) ERROR_EXIT(VL_REPSFULL);
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);
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)) {
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);
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);
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);
499 ERROR_EXIT(VL_REPSFULL); /* No reason to utilize a new error code */
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;
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]);
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));
524 cheader.vital_header.allocs++;
525 if (write_vital_vlheader(trans)) return 0;
526 memset(tentry, 0, sizeof(nvlentry)); /* zero new entry */
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;
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)))
545 cheader.vital_header.frees++;
546 if (write_vital_vlheader(trans)) return VL_IO;
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;
556 struct nvlentry *tentry;
558 { register afs_int32 typeindex, hashindex, blockindex;
561 hashindex = IDHash(volid);
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))) {
570 if (volid == tentry->volumeId[typeindex])
575 for (blockindex = ntohl(cheader.VolidHash[voltype][hashindex]); blockindex != NULLO; blockindex = tentry->nextIdHash[voltype]) {
576 if (vlentryread(trans, blockindex, (char *) tentry, sizeof(nvlentry))) {
580 if (volid == tentry->volumeId[voltype])
584 return 0; /* no such entry */
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;
592 struct nvlentry *tentry;
594 { register afs_int32 hashindex;
595 register afs_int32 blockindex;
596 char tname[VL_MAXNAMELEN];
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 */
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 */
610 else strcpy(tname, volname);
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))) {
619 if (!strcmp(tname, tentry->name))
622 return 0; /* no such entry */
625 int HashNDump (trans, hashindex)
626 struct ubik_trans *trans;
630 register int blockindex;
631 struct nvlentry tentry;
633 for (blockindex = ntohl(cheader.VolnameHash[hashindex]); blockindex != NULLO; blockindex = tentry.nextNameHash) {
634 if (vlentryread(trans, blockindex, (char *) &tentry, sizeof(nvlentry))) return 0;
636 VLog(0, ("[%d]#%d: %10d %d %d (%s)\n", hashindex, i, tentry.volumeId[0],
637 tentry.nextIdHash[0], tentry.nextNameHash, tentry.name));
643 int HashIdDump (trans, hashindex)
644 struct ubik_trans *trans;
648 register int blockindex;
649 struct nvlentry tentry;
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;
654 VLog(0, ("[%d]#%d: %10d %d %d (%s)\n", hashindex, i, tentry.volumeId[0],
655 tentry.nextIdHash[0], tentry.nextNameHash, tentry.name));
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;
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))
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))
678 if (tentry->volumeId[BACKVOL]) {
679 if (errorcode = HashVolid(trans, BACKVOL, blockindex, tentry))
683 /* Insert into volname's hash linked list */
684 HashVolname(trans, blockindex, tentry);
686 /* Update cheader entry */
687 if (write_vital_vlheader(trans)) return VL_IO;
689 /* Update hash list pointers in the entry itself */
690 if (vlentrywrite(trans, blockindex, (char *)tentry, sizeof(nvlentry))) return VL_IO;
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;
702 if (!index_OK(trans, blockindex)) return VL_BADINDEX;
703 if (errorcode = UnhashVolid(trans, RWVOL, blockindex, aentry))
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))
712 /* Take it out of the Volname hash list */
713 if (errorcode = UnhashVolname(trans, blockindex, aentry))
716 /* Update cheader entry */
717 write_vital_vlheader(trans);
722 /* cheader must have be read before this routine is called. */
723 int HashVolid(trans, voltype, blockindex, tentry)
724 struct ubik_trans *trans;
726 afs_int32 blockindex;
727 struct nvlentry *tentry;
728 { afs_int32 hashindex, errorcode;
729 struct vlentry ventry;
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;
742 /* cheader must have be read before this routine is called. */
743 int UnhashVolid(trans, voltype, blockindex, aentry)
744 struct ubik_trans *trans;
746 afs_int32 blockindex;
747 struct nvlentry *aentry;
748 { int hashindex, nextblockindex, prevblockindex;
749 struct nvlentry tentry;
753 if (aentry->volumeId[voltype] == NULLO) /* Assume no volume id */
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;
764 while (nextblockindex != blockindex) {
765 prevblockindex = nextblockindex; /* always done once */
766 if (vlentryread(trans, nextblockindex, (char *)&tentry, sizeof(nvlentry)))
768 if ((nextblockindex = tentry.nextIdHash[voltype]) == NULLO)
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)))
776 aentry->nextIdHash[voltype] = 0;
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;
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;
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;
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;
814 while (nextblockindex != blockindex) {
815 prevblockindex = nextblockindex; /* always done at least once */
816 if (vlentryread(trans, nextblockindex, (char *)&tentry, sizeof(nvlentry)))
818 if ((nextblockindex = tentry.nextNameHash) == NULLO)
821 tentry.nextNameHash = aentry->nextNameHash;
822 temp = htonl(tentry.nextNameHash);
823 if (vlwrite(trans, DOFFSET(prevblockindex, &tentry, &tentry.nextNameHash), (char *)&temp, sizeof(afs_int32)))
826 aentry->nextNameHash = 0;
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 */
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;
840 if (blockindex == 0) /* get first one */
841 blockindex = sizeof(cheader);
843 if (!index_OK (trans, blockindex)) {
844 *remaining = -1; /* error */
847 blockindex += sizeof(nvlentry);
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))) {
855 if (tentry->flags == VLCONTBLOCK) {
857 * This is a special mh extension block just simply skip over it
859 blockindex += VL_ADDREXTBLK_SIZE;
861 if (tentry->flags != VLFREE) {
862 /* estimate remaining number of entries, not including this one */
863 *remaining = (lastblockindex - blockindex) / sizeof(nvlentry) - 1;
866 blockindex += sizeof(nvlentry);
869 *remaining = 0; /* no more entries */
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;
879 if ((blockindex < sizeof(cheader)) || (blockindex >= ntohl(cheader.vital_header.eofPtr)))