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>
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[];
35 static int index_OK();
37 #define ERROR_EXIT(code) {error=(code); goto error_exit;}
39 /* Hashing algorithm based on the volume id; HASHSIZE must be prime */
40 afs_int32 IDHash(volumeid)
43 return((abs(volumeid)) % HASHSIZE);
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;
54 for (i=strlen(volumename), volumename += i-1; i--; volumename--)
55 hash = (hash*63) + (*((unsigned char *)volumename) - 63);
56 return(hash % HASHSIZE);
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;
66 { afs_int32 errorcode;
68 if (errorcode = ubik_Seek(trans, 0, offset)) return errorcode;
69 return(ubik_Write(trans, buffer, length));
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;
79 { afs_int32 errorcode;
81 if (errorcode = ubik_Seek(trans, 0, offset)) return errorcode;
82 return(ubik_Read(trans, buffer, length));
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;
92 struct vlentry oentry;
93 struct nvlentry nentry, *nep;
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;
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;
132 return vlwrite(trans, offset, bufp, length);
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;
141 struct vlentry *oep, tentry;
142 struct nvlentry *nep, *nbufp;
143 char *bufp = (char *)&tentry;
144 register afs_int32 i;
146 if (length != sizeof(vlentry)) return -1;
147 i = vlread(trans, offset, bufp, length);
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);
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);
186 /* Convenient write of small critical vldb header info to the database. */
187 int write_vital_vlheader(trans)
188 register struct ubik_trans *trans;
190 if (vlwrite(trans, 0, (char *) &cheader.vital_header, sizeof(vital_vlheader))) return VL_IO;
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).
205 afs_int32 readExtents(trans)
206 struct ubik_trans *trans;
208 afs_uint32 extentAddr;
209 afs_int32 error=0, code;
213 extentAddr = ntohl(cheader.SIT);
217 /* Read the first extension block */
219 ex_addr[0] = (struct extentaddr *)malloc(VL_ADDREXTBLK_SIZE);
221 ERROR_EXIT(VL_NOMEM);
223 code = vlread(trans, extentAddr, (char *)ex_addr[0], VL_ADDREXTBLK_SIZE);
225 free(ex_addr[0]); /* Not the place to create it */
230 /* In case more that 64 mh servers are in use they're kept in these
231 * continuation blocks
233 for (i=1; i < VL_MAX_ADDREXTBLKS; i++) {
234 if (!ex_addr[0]->ex_contaddrs[i])
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) ) {
243 ex_addr[0]->ex_contaddrs[i] = 0;
248 /* Read the continuation block */
250 ex_addr[i] = (struct extentaddr *)malloc(VL_ADDREXTBLK_SIZE);
252 ERROR_EXIT(VL_NOMEM);
254 code = vlread(trans, ntohl(ex_addr[0]->ex_contaddrs[i]),
255 (char *)ex_addr[i], VL_ADDREXTBLK_SIZE);
257 free(ex_addr[i]); /* Not the place to create it */
262 /* After reading it in, check to see if its a real continuation block */
263 if (ntohl(ex_addr[i]->ex_flags) != VLCONTBLOCK) {
265 ex_addr[0]->ex_contaddrs[i] = 0;
266 free(ex_addr[i]); /* Not the place to create it */
273 code = vlwrite(trans, extentAddr, ex_addr[0], VL_ADDREXTBLK_SIZE);
275 VLog(0, ("Multihome server support modification\n"));
277 /* Keep extent_mod true in-case the transaction aborts */
278 /* Don't return error so we don't abort transaction */
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;
291 afs_int32 error=0, i, code, ubcode=0;
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.
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);
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]);
311 code = readExtents(trans);
317 vldbversion = ntohl(cheader.vital_header.vldbversion);
319 /* now, if can't read, or header is wrong, write a new header */
320 if (ubcode || vldbversion == 0) {
322 printf("Can't read VLDB header, re-initialising...\n");
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;
335 code = vlwrite(trans, 0, (char *) &cheader, sizeof(cheader));
337 printf("Can't write VLDB header (error = %d)\n", code);
341 else ERROR_EXIT(VL_EMPTY);
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);
351 maxnservers = ((vldbversion == 3 || vldbversion == 4) ? 13 : 8);
359 afs_int32 GetExtentBlock(trans, base)
360 register struct ubik_trans *trans;
361 register afs_int32 base;
363 afs_int32 blockindex, code, error = 0;
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 */
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);
374 ERROR_EXIT(VL_NOMEM);
376 memset((char *)ex_addr[base], 0, VL_ADDREXTBLK_SIZE);
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);
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);
390 /* Write the address of the base extension block in the vldb header */
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);
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);
409 afs_int32 FindExtentBlock(trans, uuidp, createit, hostslot, expp, basep)
410 register struct ubik_trans *trans;
412 afs_int32 createit, hostslot, *basep;
413 struct extentaddr **expp;
416 struct extentaddr *exp;
417 register afs_int32 i, j, code, base, index, error=0;
419 *expp = (struct extentaddr *)0;
422 /* Create the first extension block if it does not exist */
424 code = GetExtentBlock(trans, 0);
425 if (code) ERROR_EXIT(code);
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);
433 if ((index = HostAddress[i] & 0x0000ffff) > VL_MHSRV_PERBLK) {
434 ERROR_EXIT(VL_INDEXERANGE);
436 exp = &ex_addr[base][index];
437 tuuid = exp->ex_hostuuid;
438 afs_ntohuuid(&tuuid);
439 if (afs_uuid_equal(uuidp, &tuuid)) {
448 if (hostslot == -1) {
449 for (i=0; i<MAXSERVERID+1; i++) {
450 if (!HostAddress[i]) break;
452 if (i > MAXSERVERID) ERROR_EXIT(VL_REPSFULL);
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);
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)) {
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);
477 if (vldbversion != VLDBVERSION_4) {
478 cheader.vital_header.vldbversion = htonl(VLDBVERSION_4);
479 code = write_vital_vlheader(trans);
480 if (code) ERROR_EXIT(VL_IO);
482 cheader.IpMappedAddr[i] = htonl(HostAddress[i]);
483 code = vlwrite(trans, DOFFSET(0, &cheader, &cheader.IpMappedAddr[i]),
484 (char *) &cheader.IpMappedAddr[i], sizeof(afs_int32));
485 if (code) ERROR_EXIT(VL_IO);
490 ERROR_EXIT(VL_REPSFULL); /* No reason to utilize a new error code */
497 /* Allocate a free block of storage for entry, returning address of a new
498 zeroed entry (or zero if something is wrong). */
499 afs_int32 AllocBlock (trans, tentry)
500 register struct ubik_trans *trans;
501 struct nvlentry *tentry;
502 { register afs_int32 blockindex;
504 if (cheader.vital_header.freePtr) {
505 /* allocate this dude */
506 blockindex = ntohl(cheader.vital_header.freePtr);
507 if (vlentryread(trans, blockindex, (char *) tentry, sizeof(vlentry))) return 0;
508 cheader.vital_header.freePtr = htonl(tentry->nextIdHash[0]);
511 /* hosed, nothing on free list, grow file */
512 blockindex = ntohl(cheader.vital_header.eofPtr); /* remember this guy */
513 cheader.vital_header.eofPtr = htonl(blockindex + sizeof(vlentry));
515 cheader.vital_header.allocs++;
516 if (write_vital_vlheader(trans)) return 0;
517 memset(tentry, 0, sizeof(nvlentry)); /* zero new entry */
522 /* Free a block given its index. It must already have been unthreaded. Returns zero for success or an error code on failure. */
523 int FreeBlock (trans, blockindex)
524 struct ubik_trans *trans;
525 afs_int32 blockindex;
526 { struct nvlentry tentry;
528 /* check validity of blockindex just to be on the safe side */
529 if (!index_OK (trans, blockindex)) return VL_BADINDEX;
530 memset(&tentry, 0, sizeof(nvlentry));
531 tentry.nextIdHash[0] = cheader.vital_header.freePtr; /* already in network order */
532 tentry.flags = htonl(VLFREE);
533 cheader.vital_header.freePtr = htonl(blockindex);
534 if (vlwrite (trans, blockindex, (char *)&tentry, sizeof(nvlentry)))
536 cheader.vital_header.frees++;
537 if (write_vital_vlheader(trans)) return VL_IO;
542 /* Look for a block by volid and voltype (if not known use -1 which searches all 3 volid hash lists. Note that the linked lists are read in first from the database header. If found read the block's contents into the area pointed to by tentry and return the block's index. If not found return 0. */
543 afs_int32 FindByID (trans, volid, voltype, tentry, error)
544 struct ubik_trans *trans;
547 struct nvlentry *tentry;
549 { register afs_int32 typeindex, hashindex, blockindex;
552 hashindex = IDHash(volid);
554 /* Should we have one big hash table for volids as opposed to the three ones? */
555 for (typeindex = 0; typeindex < MAXTYPES; typeindex++) {
556 for (blockindex = ntohl(cheader.VolidHash[typeindex][hashindex]); blockindex != NULLO; blockindex = tentry->nextIdHash[typeindex]) {
557 if (vlentryread(trans, blockindex, (char *) tentry, sizeof(nvlentry))) {
561 if (volid == tentry->volumeId[typeindex])
566 for (blockindex = ntohl(cheader.VolidHash[voltype][hashindex]); blockindex != NULLO; blockindex = tentry->nextIdHash[voltype]) {
567 if (vlentryread(trans, blockindex, (char *) tentry, sizeof(nvlentry))) {
571 if (volid == tentry->volumeId[voltype])
575 return 0; /* no such entry */
579 /* Look for a block by volume name. If found read the block's contents into the area pointed to by tentry and return the block's index. If not found return 0. */
580 afs_int32 FindByName (trans, volname, tentry, error)
581 struct ubik_trans *trans;
583 struct nvlentry *tentry;
585 { register afs_int32 hashindex;
586 register afs_int32 blockindex;
587 char tname[VL_MAXNAMELEN];
589 /* remove .backup or .readonly extensions for stupid backwards compatibility */
590 hashindex = strlen(volname); /* really string length */
591 if (hashindex >= 8 && strcmp(volname+hashindex-7, ".backup")==0) {
592 /* this is a backup volume */
593 strcpy(tname, volname);
594 tname[hashindex-7] = 0; /* zap extension */
596 else if (hashindex >= 10 && strcmp(volname+hashindex-9, ".readonly")==0) {
597 /* this is a readonly volume */
598 strcpy(tname, volname);
599 tname[hashindex-9] = 0; /* zap extension */
601 else strcpy(tname, volname);
604 hashindex = NameHash(tname);
605 for (blockindex = ntohl(cheader.VolnameHash[hashindex]); blockindex != NULLO; blockindex = tentry->nextNameHash) {
606 if (vlentryread(trans, blockindex, (char *) tentry, sizeof(nvlentry))) {
610 if (!strcmp(tname, tentry->name))
613 return 0; /* no such entry */
616 int HashNDump (trans, hashindex)
617 struct ubik_trans *trans;
621 register int blockindex;
622 struct nvlentry tentry;
624 for (blockindex = ntohl(cheader.VolnameHash[hashindex]); blockindex != NULLO; blockindex = tentry.nextNameHash) {
625 if (vlentryread(trans, blockindex, (char *) &tentry, sizeof(nvlentry))) return 0;
627 VLog(0, ("[%d]#%d: %10d %d %d (%s)\n", hashindex, i, tentry.volumeId[0],
628 tentry.nextIdHash[0], tentry.nextNameHash, tentry.name));
634 int HashIdDump (trans, hashindex)
635 struct ubik_trans *trans;
639 register int blockindex;
640 struct nvlentry tentry;
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;
645 VLog(0, ("[%d]#%d: %10d %d %d (%s)\n", hashindex, i, tentry.volumeId[0],
646 tentry.nextIdHash[0], tentry.nextNameHash, tentry.name));
652 /* Add a block to the hash table given a pointer to the block and its index. The block is threaded onto both hash tables and written to disk. The routine returns zero if there were no errors. */
653 int ThreadVLentry (trans, blockindex, tentry)
654 struct ubik_trans *trans;
655 afs_int32 blockindex;
656 struct nvlentry *tentry;
659 if (!index_OK(trans, blockindex)) return VL_BADINDEX;
660 /* Insert into volid's hash linked list */
661 if (errorcode = HashVolid(trans, RWVOL, blockindex, tentry))
664 /* For rw entries we also enter the RO and BACK volume ids (if they exist) in the hash tables; note all there volids (RW, RO, BACK) should not be hashed yet! */
665 if (tentry->volumeId[ROVOL]) {
666 if (errorcode = HashVolid(trans, ROVOL, blockindex, tentry))
669 if (tentry->volumeId[BACKVOL]) {
670 if (errorcode = HashVolid(trans, BACKVOL, blockindex, tentry))
674 /* Insert into volname's hash linked list */
675 HashVolname(trans, blockindex, tentry);
677 /* Update cheader entry */
678 if (write_vital_vlheader(trans)) return VL_IO;
680 /* Update hash list pointers in the entry itself */
681 if (vlentrywrite(trans, blockindex, (char *)tentry, sizeof(nvlentry))) return VL_IO;
686 /* Remove a block from both the hash tables. If success return 0, else return an error code. */
687 int UnthreadVLentry (trans, blockindex, aentry)
688 struct ubik_trans *trans;
689 afs_int32 blockindex;
690 struct nvlentry *aentry;
691 { register afs_int32 errorcode, typeindex;
693 if (!index_OK(trans, blockindex)) return VL_BADINDEX;
694 if (errorcode = UnhashVolid(trans, RWVOL, blockindex, aentry))
697 /* Take the RO/RW entries of their respective hash linked lists. */
698 for (typeindex = ROVOL; typeindex <= BACKVOL; typeindex++) {
699 if (errorcode = UnhashVolid(trans, typeindex, blockindex, aentry))
703 /* Take it out of the Volname hash list */
704 if (errorcode = UnhashVolname(trans, blockindex, aentry))
707 /* Update cheader entry */
708 write_vital_vlheader(trans);
713 /* cheader must have be read before this routine is called. */
714 int HashVolid(trans, voltype, blockindex, tentry)
715 struct ubik_trans *trans;
717 afs_int32 blockindex;
718 struct nvlentry *tentry;
719 { afs_int32 hashindex, errorcode;
720 struct vlentry ventry;
722 if (FindByID(trans, tentry->volumeId[voltype], voltype, &ventry, &errorcode))
723 return VL_IDALREADYHASHED;
724 else if (errorcode) return errorcode;
725 hashindex = IDHash(tentry->volumeId[voltype]);
726 tentry->nextIdHash[voltype] = ntohl(cheader.VolidHash[voltype][hashindex]);
727 cheader.VolidHash[voltype][hashindex] = htonl(blockindex);
728 if (vlwrite(trans, DOFFSET(0, &cheader, &cheader.VolidHash[voltype][hashindex]), (char *) &cheader.VolidHash[voltype][hashindex], sizeof(afs_int32))) return VL_IO;
733 /* cheader must have be read before this routine is called. */
734 int UnhashVolid(trans, voltype, blockindex, aentry)
735 struct ubik_trans *trans;
737 afs_int32 blockindex;
738 struct nvlentry *aentry;
739 { int hashindex, nextblockindex, prevblockindex;
740 struct nvlentry tentry;
744 if (aentry->volumeId[voltype] == NULLO) /* Assume no volume id */
746 /* Take it out of the VolId[voltype] hash list */
747 hashindex = IDHash(aentry->volumeId[voltype]);
748 nextblockindex = ntohl(cheader.VolidHash[voltype][hashindex]);
749 if (nextblockindex == blockindex) {
750 /* First on the hash list; just adjust pointers */
751 cheader.VolidHash[voltype][hashindex] = htonl(aentry->nextIdHash[voltype]);
752 code = vlwrite(trans, DOFFSET(0, &cheader, &cheader.VolidHash[voltype][hashindex]), (char *) &cheader.VolidHash[voltype][hashindex], sizeof(afs_int32));
753 if (code) return VL_IO;
755 while (nextblockindex != blockindex) {
756 prevblockindex = nextblockindex; /* always done once */
757 if (vlentryread(trans, nextblockindex, (char *)&tentry, sizeof(nvlentry)))
759 if ((nextblockindex = tentry.nextIdHash[voltype]) == NULLO)
762 temp = tentry.nextIdHash[voltype] = aentry->nextIdHash[voltype];
763 temp = htonl(temp); /* convert to network byte order before writing */
764 if (vlwrite(trans, DOFFSET(prevblockindex, &tentry, &tentry.nextIdHash[voltype]), (char *)&temp, sizeof(afs_int32)))
767 aentry->nextIdHash[voltype] = 0;
772 int HashVolname(trans, blockindex, aentry)
773 struct ubik_trans *trans;
774 afs_int32 blockindex;
775 struct nvlentry *aentry;
776 { register afs_int32 hashindex;
777 register afs_int32 code;
779 /* Insert into volname's hash linked list */
780 hashindex = NameHash(aentry->name);
781 aentry->nextNameHash = ntohl(cheader.VolnameHash[hashindex]);
782 cheader.VolnameHash[hashindex] = htonl(blockindex);
783 code = vlwrite(trans, DOFFSET(0, &cheader, &cheader.VolnameHash[hashindex]), (char *) &cheader.VolnameHash[hashindex], sizeof(afs_int32));
784 if (code) return VL_IO;
789 int UnhashVolname(trans, blockindex, aentry)
790 struct ubik_trans *trans;
791 afs_int32 blockindex;
792 struct nvlentry *aentry;
793 { register afs_int32 hashindex, nextblockindex, prevblockindex;
794 struct nvlentry tentry;
797 /* Take it out of the Volname hash list */
798 hashindex = NameHash(aentry->name);
799 nextblockindex = ntohl(cheader.VolnameHash[hashindex]);
800 if (nextblockindex == blockindex) {
801 /* First on the hash list; just adjust pointers */
802 cheader.VolnameHash[hashindex] = htonl(aentry->nextNameHash);
803 if (vlwrite(trans, DOFFSET(0, &cheader, &cheader.VolnameHash[hashindex]), (char *) &cheader.VolnameHash[hashindex], sizeof(afs_int32))) return VL_IO;
805 while (nextblockindex != blockindex) {
806 prevblockindex = nextblockindex; /* always done at least once */
807 if (vlentryread(trans, nextblockindex, (char *)&tentry, sizeof(nvlentry)))
809 if ((nextblockindex = tentry.nextNameHash) == NULLO)
812 tentry.nextNameHash = aentry->nextNameHash;
813 temp = htonl(tentry.nextNameHash);
814 if (vlwrite(trans, DOFFSET(prevblockindex, &tentry, &tentry.nextNameHash), (char *)&temp, sizeof(afs_int32)))
817 aentry->nextNameHash = 0;
822 /* Returns the vldb entry tentry at offset index; remaining is the number of entries left; the routine also returns the index of the next sequential entry in the vldb */
824 afs_int32 NextEntry (trans, blockindex, tentry, remaining)
825 struct ubik_trans *trans;
826 afs_int32 blockindex;
827 struct nvlentry *tentry;
828 afs_int32 *remaining;
829 { register afs_int32 lastblockindex;
831 if (blockindex == 0) /* get first one */
832 blockindex = sizeof(cheader);
834 if (!index_OK (trans, blockindex)) {
835 *remaining = -1; /* error */
838 blockindex += sizeof(nvlentry);
840 /* now search for the first entry that isn't free */
841 for (lastblockindex = ntohl(cheader.vital_header.eofPtr); blockindex < lastblockindex;) {
842 if (vlentryread(trans, blockindex, (char *)tentry, sizeof(nvlentry))) {
846 if (tentry->flags == VLCONTBLOCK) {
848 * This is a special mh extension block just simply skip over it
850 blockindex += VL_ADDREXTBLK_SIZE;
852 if (tentry->flags != VLFREE) {
853 /* estimate remaining number of entries, not including this one */
854 *remaining = (lastblockindex - blockindex) / sizeof(nvlentry) - 1;
857 blockindex += sizeof(nvlentry);
860 *remaining = 0; /* no more entries */
865 /* Routine to verify that index is a legal offset to a vldb entry in the table */
866 static int index_OK (trans, blockindex)
867 struct ubik_trans *trans;
868 afs_int32 blockindex;
870 if ((blockindex < sizeof(cheader)) || (blockindex >= ntohl(cheader.vital_header.eofPtr)))