vlserver: use the logging function to log
[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 #include <roken.h>
14
15 #include <lock.h>
16 #include <rx/xdr.h>
17 #include <ubik.h>
18
19 #include "vlserver.h"
20 #include "vlserver_internal.h"
21
22 struct vlheader xheader;
23 extern int maxnservers;
24 extern afs_uint32 rd_HostAddress[MAXSERVERID + 1];
25 extern afs_uint32 wr_HostAddress[MAXSERVERID + 1];
26 struct extentaddr *rd_ex_addr[VL_MAX_ADDREXTBLKS] = { 0, 0, 0, 0 };
27 struct extentaddr *wr_ex_addr[VL_MAX_ADDREXTBLKS] = { 0, 0, 0, 0 };
28 struct vlheader rd_cheader;     /* kept in network byte order */
29 struct vlheader wr_cheader;
30 int vldbversion = 0;
31
32 static int index_OK(struct vl_ctx *ctx, afs_int32 blockindex);
33
34 #define ERROR_EXIT(code) do { \
35     error = (code); \
36     goto error_exit; \
37 } while (0)
38
39 /* Hashing algorithm based on the volume id; HASHSIZE must be prime */
40 afs_int32
41 IDHash(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
49 NameHash(char *volumename)
50 {
51     unsigned int hash;
52     int i;
53
54     hash = 0;
55     for (i = strlen(volumename), volumename += i - 1; i--; volumename--)
56         hash = (hash * 63) + (*((unsigned char *)volumename) - 63);
57     return (hash % HASHSIZE);
58 }
59
60
61 /* package up seek and write into one procedure for ease of use */
62 afs_int32
63 vlwrite(struct ubik_trans *trans, afs_int32 offset, void *buffer,
64         afs_int32 length)
65 {
66     afs_int32 errorcode;
67
68     if ((errorcode = ubik_Seek(trans, 0, offset)))
69         return errorcode;
70     return (ubik_Write(trans, buffer, length));
71 }
72
73
74 /* Package up seek and read into one procedure for ease of use */
75 afs_int32
76 vlread(struct ubik_trans *trans, afs_int32 offset, char *buffer,
77        afs_int32 length)
78 {
79     afs_int32 errorcode;
80
81     if ((errorcode = ubik_Seek(trans, 0, offset)))
82         return errorcode;
83     return (ubik_Read(trans, buffer, length));
84 }
85
86
87 /* take entry and convert to network order and write to disk */
88 afs_int32
89 vlentrywrite(struct ubik_trans *trans, afs_int32 offset, void *buffer,
90              afs_int32 length)
91 {
92     struct vlentry oentry;
93     struct nvlentry nentry, *nep;
94     char *bufp;
95     afs_int32 i;
96
97     if (length != sizeof(oentry))
98         return -1;
99     if (maxnservers == 13) {
100         nep = (struct nvlentry *)buffer;
101         for (i = 0; i < MAXTYPES; i++)
102             nentry.volumeId[i] = htonl(nep->volumeId[i]);
103         nentry.flags = htonl(nep->flags);
104         nentry.LockAfsId = htonl(nep->LockAfsId);
105         nentry.LockTimestamp = htonl(nep->LockTimestamp);
106         nentry.cloneId = htonl(nep->cloneId);
107         for (i = 0; i < MAXTYPES; i++)
108             nentry.nextIdHash[i] = htonl(nep->nextIdHash[i]);
109         nentry.nextNameHash = htonl(nep->nextNameHash);
110         memcpy(nentry.name, nep->name, VL_MAXNAMELEN);
111         memcpy(nentry.serverNumber, nep->serverNumber, NMAXNSERVERS);
112         memcpy(nentry.serverPartition, nep->serverPartition, NMAXNSERVERS);
113         memcpy(nentry.serverFlags, nep->serverFlags, NMAXNSERVERS);
114         bufp = (char *)&nentry;
115     } else {
116         memset(&oentry, 0, sizeof(struct vlentry));
117         nep = (struct nvlentry *)buffer;
118         for (i = 0; i < MAXTYPES; i++)
119             oentry.volumeId[i] = htonl(nep->volumeId[i]);
120         oentry.flags = htonl(nep->flags);
121         oentry.LockAfsId = htonl(nep->LockAfsId);
122         oentry.LockTimestamp = htonl(nep->LockTimestamp);
123         oentry.cloneId = htonl(nep->cloneId);
124         for (i = 0; i < MAXTYPES; i++)
125             oentry.nextIdHash[i] = htonl(nep->nextIdHash[i]);
126         oentry.nextNameHash = htonl(nep->nextNameHash);
127         memcpy(oentry.name, nep->name, VL_MAXNAMELEN);
128         memcpy(oentry.serverNumber, nep->serverNumber, OMAXNSERVERS);
129         memcpy(oentry.serverPartition, nep->serverPartition, OMAXNSERVERS);
130         memcpy(oentry.serverFlags, nep->serverFlags, OMAXNSERVERS);
131         bufp = (char *)&oentry;
132     }
133     return vlwrite(trans, offset, bufp, length);
134 }
135
136 /* read entry and convert to host order and write to disk */
137 afs_int32
138 vlentryread(struct ubik_trans *trans, afs_int32 offset, char *buffer,
139             afs_int32 length)
140 {
141     struct vlentry *oep, tentry;
142     struct nvlentry *nep, *nbufp;
143     char *bufp = (char *)&tentry;
144     afs_int32 i;
145
146     if (length != sizeof(vlentry))
147         return -1;
148     i = vlread(trans, offset, bufp, length);
149     if (i)
150         return i;
151     if (maxnservers == 13) {
152         nep = (struct nvlentry *)bufp;
153         nbufp = (struct nvlentry *)buffer;
154         for (i = 0; i < MAXTYPES; i++)
155             nbufp->volumeId[i] = ntohl(nep->volumeId[i]);
156         nbufp->flags = ntohl(nep->flags);
157         nbufp->LockAfsId = ntohl(nep->LockAfsId);
158         nbufp->LockTimestamp = ntohl(nep->LockTimestamp);
159         nbufp->cloneId = ntohl(nep->cloneId);
160         for (i = 0; i < MAXTYPES; i++)
161             nbufp->nextIdHash[i] = ntohl(nep->nextIdHash[i]);
162         nbufp->nextNameHash = ntohl(nep->nextNameHash);
163         memcpy(nbufp->name, nep->name, VL_MAXNAMELEN);
164         memcpy(nbufp->serverNumber, nep->serverNumber, NMAXNSERVERS);
165         memcpy(nbufp->serverPartition, nep->serverPartition, NMAXNSERVERS);
166         memcpy(nbufp->serverFlags, nep->serverFlags, NMAXNSERVERS);
167     } else {
168         oep = (struct vlentry *)bufp;
169         nbufp = (struct nvlentry *)buffer;
170         memset(nbufp, 0, sizeof(struct nvlentry));
171         for (i = 0; i < MAXTYPES; i++)
172             nbufp->volumeId[i] = ntohl(oep->volumeId[i]);
173         nbufp->flags = ntohl(oep->flags);
174         nbufp->LockAfsId = ntohl(oep->LockAfsId);
175         nbufp->LockTimestamp = ntohl(oep->LockTimestamp);
176         nbufp->cloneId = ntohl(oep->cloneId);
177         for (i = 0; i < MAXTYPES; i++)
178             nbufp->nextIdHash[i] = ntohl(oep->nextIdHash[i]);
179         nbufp->nextNameHash = ntohl(oep->nextNameHash);
180         memcpy(nbufp->name, oep->name, VL_MAXNAMELEN);
181         memcpy(nbufp->serverNumber, oep->serverNumber, NMAXNSERVERS);
182         memcpy(nbufp->serverPartition, oep->serverPartition, NMAXNSERVERS);
183         memcpy(nbufp->serverFlags, oep->serverFlags, NMAXNSERVERS);
184     }
185     return 0;
186 }
187
188 /* Convenient write of small critical vldb header info to the database. */
189 int
190 write_vital_vlheader(struct vl_ctx *ctx)
191 {
192     if (vlwrite
193         (ctx->trans, 0, (char *)&ctx->cheader->vital_header, sizeof(vital_vlheader)))
194         return VL_IO;
195     return 0;
196 }
197
198
199 int extent_mod = 0;
200
201 /* This routine reads in the extent blocks for multi-homed servers.
202  * There used to be an initialization bug that would cause the contaddrs
203  * pointers in the first extent block to be bad. Here we will check the
204  * pointers and zero them in the in-memory copy if we find them bad. We
205  * also try to write the extent blocks back out. If we can't, then we
206  * will wait until the next write transaction to write them out
207  * (extent_mod tells us the on-disk copy is bad).
208  */
209 afs_int32
210 readExtents(struct ubik_trans *trans)
211 {
212     afs_uint32 extentAddr;
213     afs_int32 error = 0, code;
214     int i;
215
216     extent_mod = 0;
217     extentAddr = ntohl(rd_cheader.SIT);
218     if (!extentAddr)
219         return 0;
220
221     /* Read the first extension block */
222     if (!rd_ex_addr[0]) {
223         rd_ex_addr[0] = malloc(VL_ADDREXTBLK_SIZE);
224         if (!rd_ex_addr[0])
225             ERROR_EXIT(VL_NOMEM);
226     }
227     code = vlread(trans, extentAddr, (char *)rd_ex_addr[0], VL_ADDREXTBLK_SIZE);
228     if (code) {
229         free(rd_ex_addr[0]);    /* Not the place to create it */
230         rd_ex_addr[0] = 0;
231         ERROR_EXIT(VL_IO);
232     }
233
234     /* In case more that 64 mh servers are in use they're kept in these
235      * continuation blocks
236      */
237     for (i = 1; i < VL_MAX_ADDREXTBLKS; i++) {
238         if (!rd_ex_addr[0]->ex_contaddrs[i])
239             continue;
240
241         /* Before reading it in, check to see if the address is good */
242         if ((ntohl(rd_ex_addr[0]->ex_contaddrs[i]) <
243              ntohl(rd_ex_addr[0]->ex_contaddrs[i - 1]) + VL_ADDREXTBLK_SIZE)
244             || (ntohl(rd_ex_addr[0]->ex_contaddrs[i]) >
245                 ntohl(rd_cheader.vital_header.eofPtr) - VL_ADDREXTBLK_SIZE)) {
246             extent_mod = 1;
247             rd_ex_addr[0]->ex_contaddrs[i] = 0;
248             continue;
249         }
250
251
252         /* Read the continuation block */
253         if (!rd_ex_addr[i]) {
254             rd_ex_addr[i] = malloc(VL_ADDREXTBLK_SIZE);
255             if (!rd_ex_addr[i])
256                 ERROR_EXIT(VL_NOMEM);
257         }
258         code =
259             vlread(trans, ntohl(rd_ex_addr[0]->ex_contaddrs[i]),
260                    (char *)rd_ex_addr[i], VL_ADDREXTBLK_SIZE);
261         if (code) {
262             free(rd_ex_addr[i]);        /* Not the place to create it */
263             rd_ex_addr[i] = 0;
264             ERROR_EXIT(VL_IO);
265         }
266
267         /* After reading it in, check to see if its a real continuation block */
268         if (ntohl(rd_ex_addr[i]->ex_hdrflags) != VLCONTBLOCK) {
269             extent_mod = 1;
270             rd_ex_addr[0]->ex_contaddrs[i] = 0;
271             free(rd_ex_addr[i]);        /* Not the place to create it */
272             rd_ex_addr[i] = 0;
273             continue;
274         }
275     }
276
277     if (extent_mod) {
278         code = vlwrite(trans, extentAddr, rd_ex_addr[0], VL_ADDREXTBLK_SIZE);
279         if (!code) {
280             VLog(0, ("Multihome server support modification\n"));
281         }
282         /* Keep extent_mod true in-case the transaction aborts */
283         /* Don't return error so we don't abort transaction */
284     }
285
286   error_exit:
287     return error;
288 }
289
290 /* Check that the database has been initialized.  Be careful to fail in a safe
291    manner, to avoid bogusly reinitializing the db.  */
292 /**
293  * reads in db cache from ubik.
294  *
295  * @param[in] ut ubik transaction
296  * @param[in] rock  opaque pointer to an int*; if 1, we should rebuild the db
297  *                  if it appears empty, if 0 we should return an error if the
298  *                  db appears empty
299  *
300  * @return operation status
301  *   @retval 0 success
302  */
303 static afs_int32
304 UpdateCache(struct ubik_trans *trans, void *rock)
305 {
306     int *builddb_rock = rock;
307     int builddb = *builddb_rock;
308     afs_int32 error = 0, i, code, ubcode;
309
310     /* if version changed (or first call), read the header */
311     ubcode = vlread(trans, 0, (char *)&rd_cheader, sizeof(rd_cheader));
312     vldbversion = ntohl(rd_cheader.vital_header.vldbversion);
313
314     if (!ubcode && (vldbversion != 0)) {
315         memcpy(rd_HostAddress, rd_cheader.IpMappedAddr, sizeof(rd_cheader.IpMappedAddr));
316         for (i = 0; i < MAXSERVERID + 1; i++) { /* cvt HostAddress to host order */
317             rd_HostAddress[i] = ntohl(rd_HostAddress[i]);
318         }
319
320         code = readExtents(trans);
321         if (code)
322             ERROR_EXIT(code);
323     }
324
325     /* now, if can't read, or header is wrong, write a new header */
326     if (ubcode || vldbversion == 0) {
327         if (builddb) {
328             VLog(0, ("Can't read VLDB header, re-initialising...\n"));
329
330             /* try to write a good header */
331             /* The read cache will be sync'ed to this new header
332              * when the ubik transaction is ended by vlsynccache(). */
333             memset(&wr_cheader, 0, sizeof(wr_cheader));
334             wr_cheader.vital_header.vldbversion = htonl(VLDBVERSION);
335             wr_cheader.vital_header.headersize = htonl(sizeof(wr_cheader));
336             /* DANGER: Must get this from a master place!! */
337             wr_cheader.vital_header.MaxVolumeId = htonl(0x20000000);
338             wr_cheader.vital_header.eofPtr = htonl(sizeof(wr_cheader));
339             for (i = 0; i < MAXSERVERID + 1; i++) {
340                 wr_cheader.IpMappedAddr[i] = 0;
341                 wr_HostAddress[i] = 0;
342             }
343             code = vlwrite(trans, 0, (char *)&wr_cheader, sizeof(wr_cheader));
344             if (code) {
345                 VLog(0, ("Can't write VLDB header (error = %d)\n", code));
346                 ERROR_EXIT(VL_IO);
347             }
348             vldbversion = ntohl(wr_cheader.vital_header.vldbversion);
349         } else {
350             VLog(1, ("Unable to read VLDB header.\n"));
351             ERROR_EXIT(VL_EMPTY);
352         }
353     }
354
355     if ((vldbversion != VLDBVERSION) && (vldbversion != OVLDBVERSION)
356         && (vldbversion != VLDBVERSION_4)) {
357         VLog(0,
358             ("VLDB version %d doesn't match this software version(%d, %d or %d), quitting!\n",
359              vldbversion, VLDBVERSION_4, VLDBVERSION, OVLDBVERSION));
360         ERROR_EXIT(VL_BADVERSION);
361     }
362
363     maxnservers = ((vldbversion == 3 || vldbversion == 4) ? 13 : 8);
364
365   error_exit:
366     /* all done */
367     return error;
368 }
369
370 afs_int32
371 CheckInit(struct ubik_trans *trans, int builddb)
372 {
373     afs_int32 code;
374
375     code = ubik_CheckCache(trans, UpdateCache, &builddb);
376     if (code) {
377         return code;
378     }
379
380     /* these next two cases shouldn't happen (UpdateCache should either
381      * rebuild the db or return an error if these cases occur), but just to
382      * be on the safe side... */
383     if (vldbversion == 0) {
384         return VL_EMPTY;
385     }
386     if ((vldbversion != VLDBVERSION) && (vldbversion != OVLDBVERSION)
387         && (vldbversion != VLDBVERSION_4)) {
388         return VL_BADVERSION;
389     }
390
391     return 0;
392 }
393
394
395 afs_int32
396 GetExtentBlock(struct vl_ctx *ctx, register afs_int32 base)
397 {
398     afs_int32 blockindex, code, error = 0;
399
400     /* Base 0 must exist before any other can be created */
401     if ((base != 0) && !ctx->ex_addr[0])
402         ERROR_EXIT(VL_CREATEFAIL);      /* internal error */
403
404     if (!ctx->ex_addr[0] || !ctx->ex_addr[0]->ex_contaddrs[base]) {
405         /* Create a new extension block */
406         if (!ctx->ex_addr[base]) {
407             ctx->ex_addr[base] = malloc(VL_ADDREXTBLK_SIZE);
408             if (!ctx->ex_addr[base])
409                 ERROR_EXIT(VL_NOMEM);
410         }
411         memset(ctx->ex_addr[base], 0, VL_ADDREXTBLK_SIZE);
412
413         /* Write the full extension block at end of vldb */
414         ctx->ex_addr[base]->ex_hdrflags = htonl(VLCONTBLOCK);
415         blockindex = ntohl(ctx->cheader->vital_header.eofPtr);
416         code =
417             vlwrite(ctx->trans, blockindex, (char *)ctx->ex_addr[base],
418                     VL_ADDREXTBLK_SIZE);
419         if (code)
420             ERROR_EXIT(VL_IO);
421
422         /* Update the cheader.vitalheader structure on disk */
423         ctx->cheader->vital_header.eofPtr = blockindex + VL_ADDREXTBLK_SIZE;
424         ctx->cheader->vital_header.eofPtr = htonl(ctx->cheader->vital_header.eofPtr);
425         code = write_vital_vlheader(ctx);
426         if (code)
427             ERROR_EXIT(VL_IO);
428
429         /* Write the address of the base extension block in the vldb header */
430         if (base == 0) {
431             ctx->cheader->SIT = htonl(blockindex);
432             code =
433                 vlwrite(ctx->trans, DOFFSET(0, ctx->cheader, &ctx->cheader->SIT),
434                         (char *)&ctx->cheader->SIT, sizeof(ctx->cheader->SIT));
435             if (code)
436                 ERROR_EXIT(VL_IO);
437         }
438
439         /* Write the address of this extension block into the base extension block */
440         ctx->ex_addr[0]->ex_contaddrs[base] = htonl(blockindex);
441         code =
442             vlwrite(ctx->trans, ntohl(ctx->cheader->SIT), ctx->ex_addr[0],
443                     sizeof(struct extentaddr));
444         if (code)
445             ERROR_EXIT(VL_IO);
446     }
447
448   error_exit:
449     return error;
450 }
451
452
453 afs_int32
454 FindExtentBlock(struct vl_ctx *ctx, afsUUID *uuidp,
455                 afs_int32 createit, afs_int32 hostslot,
456                 struct extentaddr **expp, afs_int32 *basep)
457 {
458     afsUUID tuuid;
459     struct extentaddr *exp;
460     afs_int32 i, j, code, base, index, error = 0;
461
462     *expp = NULL;
463     *basep = 0;
464
465     /* Create the first extension block if it does not exist */
466     if (!ctx->cheader->SIT) {
467         code = GetExtentBlock(ctx, 0);
468         if (code)
469             ERROR_EXIT(code);
470     }
471
472     for (i = 0; i < MAXSERVERID + 1; i++) {
473         if ((ctx->hostaddress[i] & 0xff000000) == 0xff000000) {
474             if ((base = (ctx->hostaddress[i] >> 16) & 0xff) > VL_MAX_ADDREXTBLKS) {
475                 ERROR_EXIT(VL_INDEXERANGE);
476             }
477             if ((index = ctx->hostaddress[i] & 0x0000ffff) > VL_MHSRV_PERBLK) {
478                 ERROR_EXIT(VL_INDEXERANGE);
479             }
480             exp = &ctx->ex_addr[base][index];
481             tuuid = exp->ex_hostuuid;
482             afs_ntohuuid(&tuuid);
483             if (afs_uuid_equal(uuidp, &tuuid)) {
484                 *expp = exp;
485                 *basep = base;
486                 ERROR_EXIT(0);
487             }
488         }
489     }
490
491     if (createit) {
492         if (hostslot == -1) {
493             for (i = 0; i < MAXSERVERID + 1; i++) {
494                 if (!ctx->hostaddress[i])
495                     break;
496             }
497             if (i > MAXSERVERID)
498                 ERROR_EXIT(VL_REPSFULL);
499         } else {
500             i = hostslot;
501         }
502
503         for (base = 0; base < VL_MAX_ADDREXTBLKS; base++) {
504             if (!ctx->ex_addr[0]->ex_contaddrs[base]) {
505                 code = GetExtentBlock(ctx, base);
506                 if (code)
507                     ERROR_EXIT(code);
508             }
509             for (j = 1; j < VL_MHSRV_PERBLK; j++) {
510                 exp = &ctx->ex_addr[base][j];
511                 tuuid = exp->ex_hostuuid;
512                 afs_ntohuuid(&tuuid);
513                 if (afs_uuid_is_nil(&tuuid)) {
514                     tuuid = *uuidp;
515                     afs_htonuuid(&tuuid);
516                     exp->ex_hostuuid = tuuid;
517                     code =
518                         vlwrite(ctx->trans,
519                                 DOFFSET(ntohl(ctx->ex_addr[0]->ex_contaddrs[base]),
520                                         (char *)ctx->ex_addr[base], (char *)exp),
521                                 (char *)&tuuid, sizeof(tuuid));
522                     if (code)
523                         ERROR_EXIT(VL_IO);
524                     ctx->hostaddress[i] =
525                         0xff000000 | ((base << 16) & 0xff0000) | (j & 0xffff);
526                     *expp = exp;
527                     *basep = base;
528                     if (vldbversion != VLDBVERSION_4) {
529                         ctx->cheader->vital_header.vldbversion =
530                             htonl(VLDBVERSION_4);
531                         code = write_vital_vlheader(ctx);
532                         if (code)
533                             ERROR_EXIT(VL_IO);
534                     }
535                     ctx->cheader->IpMappedAddr[i] = htonl(ctx->hostaddress[i]);
536                     code =
537                         vlwrite(ctx->trans,
538                                 DOFFSET(0, ctx->cheader,
539                                         &ctx->cheader->IpMappedAddr[i]),
540                                 (char *)&ctx->cheader->IpMappedAddr[i],
541                                 sizeof(afs_int32));
542                     if (code)
543                         ERROR_EXIT(VL_IO);
544                     ERROR_EXIT(0);
545                 }
546             }
547         }
548         ERROR_EXIT(VL_REPSFULL);        /* No reason to utilize a new error code */
549     }
550
551   error_exit:
552     return error;
553 }
554
555 /* Allocate a free block of storage for entry, returning address of a new
556    zeroed entry (or zero if something is wrong).  */
557 afs_int32
558 AllocBlock(struct vl_ctx *ctx, struct nvlentry *tentry)
559 {
560     afs_int32 blockindex;
561
562     if (ctx->cheader->vital_header.freePtr) {
563         /* allocate this dude */
564         blockindex = ntohl(ctx->cheader->vital_header.freePtr);
565         if (vlentryread(ctx->trans, blockindex, (char *)tentry, sizeof(vlentry)))
566             return 0;
567         ctx->cheader->vital_header.freePtr = htonl(tentry->nextIdHash[0]);
568     } else {
569         /* hosed, nothing on free list, grow file */
570         blockindex = ntohl(ctx->cheader->vital_header.eofPtr);  /* remember this guy */
571         ctx->cheader->vital_header.eofPtr = htonl(blockindex + sizeof(vlentry));
572     }
573     ctx->cheader->vital_header.allocs++;
574     if (write_vital_vlheader(ctx))
575         return 0;
576     memset(tentry, 0, sizeof(nvlentry));        /* zero new entry */
577     return blockindex;
578 }
579
580
581 /* Free a block given its index.  It must already have been unthreaded. Returns zero for success or an error code on failure. */
582 int
583 FreeBlock(struct vl_ctx *ctx, afs_int32 blockindex)
584 {
585     struct nvlentry tentry;
586
587     /* check validity of blockindex just to be on the safe side */
588     if (!index_OK(ctx, blockindex))
589         return VL_BADINDEX;
590     memset(&tentry, 0, sizeof(nvlentry));
591     tentry.nextIdHash[0] = ctx->cheader->vital_header.freePtr;  /* already in network order */
592     tentry.flags = htonl(VLFREE);
593     ctx->cheader->vital_header.freePtr = htonl(blockindex);
594     if (vlwrite(ctx->trans, blockindex, (char *)&tentry, sizeof(nvlentry)))
595         return VL_IO;
596     ctx->cheader->vital_header.frees++;
597     if (write_vital_vlheader(ctx))
598         return VL_IO;
599     return 0;
600 }
601
602
603 /* Look for a block by volid and voltype (if not known use -1 which searches
604  * all 3 volid hash lists. Note that the linked lists are read in first from
605  * the database header.  If found read the block's contents into the area
606  * pointed to by tentry and return the block's index.  If not found return 0.
607  */
608 afs_int32
609 FindByID(struct vl_ctx *ctx, afs_uint32 volid, afs_int32 voltype,
610          struct nvlentry *tentry, afs_int32 *error)
611 {
612     afs_int32 typeindex, hashindex, blockindex;
613
614     *error = 0;
615     hashindex = IDHash(volid);
616     if (voltype == -1) {
617 /* Should we have one big hash table for volids as opposed to the three ones? */
618         for (typeindex = 0; typeindex < MAXTYPES; typeindex++) {
619             for (blockindex = ntohl(ctx->cheader->VolidHash[typeindex][hashindex]);
620                  blockindex != NULLO;
621                  blockindex = tentry->nextIdHash[typeindex]) {
622                 if (vlentryread
623                     (ctx->trans, blockindex, (char *)tentry, sizeof(nvlentry))) {
624                     *error = VL_IO;
625                     return 0;
626                 }
627                 if (volid == tentry->volumeId[typeindex])
628                     return blockindex;
629             }
630         }
631     } else {
632         for (blockindex = ntohl(ctx->cheader->VolidHash[voltype][hashindex]);
633              blockindex != NULLO; blockindex = tentry->nextIdHash[voltype]) {
634             if (vlentryread
635                 (ctx->trans, blockindex, (char *)tentry, sizeof(nvlentry))) {
636                 *error = VL_IO;
637                 return 0;
638             }
639             if (volid == tentry->volumeId[voltype])
640                 return blockindex;
641         }
642     }
643     return 0;                   /* no such entry */
644 }
645
646
647 /* Look for a block by volume name. If found read the block's contents into
648  * the area pointed to by tentry and return the block's index.  If not
649  * found return 0.
650  */
651 afs_int32
652 FindByName(struct vl_ctx *ctx, char *volname, struct nvlentry *tentry,
653            afs_int32 *error)
654 {
655     afs_int32 hashindex;
656     afs_int32 blockindex;
657     char tname[VL_MAXNAMELEN];
658
659     /* remove .backup or .readonly extensions for stupid backwards
660      * compatibility
661      */
662     hashindex = strlen(volname);        /* really string length */
663     if (hashindex >= 8 && strcmp(volname + hashindex - 7, ".backup") == 0) {
664         /* this is a backup volume */
665         strcpy(tname, volname);
666         tname[hashindex - 7] = 0;       /* zap extension */
667     } else if (hashindex >= 10
668                && strcmp(volname + hashindex - 9, ".readonly") == 0) {
669         /* this is a readonly volume */
670         strcpy(tname, volname);
671         tname[hashindex - 9] = 0;       /* zap extension */
672     } else
673         strcpy(tname, volname);
674
675     *error = 0;
676     hashindex = NameHash(tname);
677     for (blockindex = ntohl(ctx->cheader->VolnameHash[hashindex]);
678          blockindex != NULLO; blockindex = tentry->nextNameHash) {
679         if (vlentryread(ctx->trans, blockindex, (char *)tentry, sizeof(nvlentry))) {
680             *error = VL_IO;
681             return 0;
682         }
683         if (!strcmp(tname, tentry->name))
684             return blockindex;
685     }
686     return 0;                   /* no such entry */
687 }
688
689 /**
690  * Returns whether or not any of the supplied volume IDs already exist
691  * in the vldb.
692  *
693  * @param ctx      transaction context
694  * @param ids      an array of volume IDs
695  * @param ids_len  the number of elements in the 'ids' array
696  * @param error    filled in with an error code in case of error
697  *
698  * @return whether any of the volume IDs are already used
699  *  @retval 1  at least one of the volume IDs is already used
700  *  @retval 0  none of the volume IDs are used, or an error occurred
701  */
702 int
703 EntryIDExists(struct vl_ctx *ctx, const afs_uint32 *ids,
704               afs_int32 ids_len, afs_int32 *error)
705 {
706     afs_int32 typeindex;
707     struct nvlentry tentry;
708
709     *error = 0;
710
711     for (typeindex = 0; typeindex < ids_len; typeindex++) {
712         if (ids[typeindex]
713             && FindByID(ctx, ids[typeindex], -1, &tentry, error)) {
714
715             return 1;
716         } else if (*error) {
717             return 0;
718         }
719     }
720
721     return 0;
722 }
723
724 /**
725  * Finds the next range of unused volume IDs in the vldb.
726  *
727  * @param ctx       transaction context
728  * @param maxvolid  the current max vol ID, and where to start looking
729  *                  for an unused volume ID range
730  * @param bump      how many volume IDs we need to be unused
731  * @param error     filled in with an error code in case of error
732  *
733  * @return the next volume ID 'volid' such that the range
734  *         [volid, volid+bump) of volume IDs is unused, or 0 if there's
735  *         an error
736  */
737 afs_uint32
738 NextUnusedID(struct vl_ctx *ctx, afs_uint32 maxvolid, afs_uint32 bump,
739              afs_int32 *error)
740 {
741     struct nvlentry tentry;
742     afs_uint32 id;
743     afs_uint32 nfree;
744
745     *error = 0;
746
747      /* we simply start at the given maxvolid, keep a running tally of
748       * how many free volume IDs we've seen in a row, and return when
749       * we've seen 'bump' unused IDs in a row */
750     for (id = maxvolid, nfree = 0; nfree < bump; ++id) {
751         if (FindByID(ctx, id, -1, &tentry, error)) {
752             nfree = 0;
753         } else if (*error) {
754             return 0;
755         } else {
756             ++nfree;
757         }
758     }
759
760     /* 'id' is now at the end of the [maxvolid,maxvolid+bump) range,
761      * but we need to return the first unused id, so subtract the
762      * number of current running free IDs to get the beginning */
763     return id - nfree;
764 }
765
766 int
767 HashNDump(struct vl_ctx *ctx, int hashindex)
768 {
769     int i = 0;
770     int blockindex;
771     struct nvlentry tentry;
772
773     for (blockindex = ntohl(ctx->cheader->VolnameHash[hashindex]);
774          blockindex != NULLO; blockindex = tentry.nextNameHash) {
775         if (vlentryread(ctx->trans, blockindex, (char *)&tentry, sizeof(nvlentry)))
776             return 0;
777         i++;
778         VLog(0,
779              ("[%d]#%d: %10d %d %d (%s)\n", hashindex, i, tentry.volumeId[0],
780               tentry.nextIdHash[0], tentry.nextNameHash, tentry.name));
781     }
782     return 0;
783 }
784
785
786 int
787 HashIdDump(struct vl_ctx *ctx, int hashindex)
788 {
789     int i = 0;
790     int blockindex;
791     struct nvlentry tentry;
792
793     for (blockindex = ntohl(ctx->cheader->VolidHash[0][hashindex]);
794          blockindex != NULLO; blockindex = tentry.nextIdHash[0]) {
795         if (vlentryread(ctx->trans, blockindex, (char *)&tentry, sizeof(nvlentry)))
796             return 0;
797         i++;
798         VLog(0,
799              ("[%d]#%d: %10d %d %d (%s)\n", hashindex, i, tentry.volumeId[0],
800               tentry.nextIdHash[0], tentry.nextNameHash, tentry.name));
801     }
802     return 0;
803 }
804
805
806 /* Add a block to the hash table given a pointer to the block and its index.
807  * The block is threaded onto both hash tables and written to disk.  The
808  * routine returns zero if there were no errors.
809  */
810 int
811 ThreadVLentry(struct vl_ctx *ctx, afs_int32 blockindex,
812               struct nvlentry *tentry)
813 {
814     int errorcode;
815
816     if (!index_OK(ctx, blockindex))
817         return VL_BADINDEX;
818     /* Insert into volid's hash linked list */
819     if ((errorcode = HashVolid(ctx, RWVOL, blockindex, tentry)))
820         return errorcode;
821
822     /* For rw entries we also enter the RO and BACK volume ids (if they
823      * exist) in the hash tables; note all there volids (RW, RO, BACK)
824      * should not be hashed yet! */
825     if (tentry->volumeId[ROVOL]) {
826         if ((errorcode = HashVolid(ctx, ROVOL, blockindex, tentry)))
827             return errorcode;
828     }
829     if (tentry->volumeId[BACKVOL]) {
830         if ((errorcode = HashVolid(ctx, BACKVOL, blockindex, tentry)))
831             return errorcode;
832     }
833
834     /* Insert into volname's hash linked list */
835     HashVolname(ctx, blockindex, tentry);
836
837     /* Update cheader entry */
838     if (write_vital_vlheader(ctx))
839         return VL_IO;
840
841     /* Update hash list pointers in the entry itself */
842     if (vlentrywrite(ctx->trans, blockindex, (char *)tentry, sizeof(nvlentry)))
843         return VL_IO;
844     return 0;
845 }
846
847
848 /* Remove a block from both the hash tables.  If success return 0, else
849  * return an error code. */
850 int
851 UnthreadVLentry(struct vl_ctx *ctx, afs_int32 blockindex,
852                 struct nvlentry *aentry)
853 {
854     afs_int32 errorcode, typeindex;
855
856     if (!index_OK(ctx, blockindex))
857         return VL_BADINDEX;
858     if ((errorcode = UnhashVolid(ctx, RWVOL, blockindex, aentry)))
859         return errorcode;
860
861     /* Take the RO/RW entries of their respective hash linked lists. */
862     for (typeindex = ROVOL; typeindex <= BACKVOL; typeindex++) {
863         if ((errorcode = UnhashVolid(ctx, typeindex, blockindex, aentry)))
864             return errorcode;
865     }
866
867     /* Take it out of the Volname hash list */
868     if ((errorcode = UnhashVolname(ctx, blockindex, aentry)))
869         return errorcode;
870
871     /* Update cheader entry */
872     write_vital_vlheader(ctx);
873
874     return 0;
875 }
876
877 /* cheader must have be read before this routine is called. */
878 int
879 HashVolid(struct vl_ctx *ctx, afs_int32 voltype, afs_int32 blockindex,
880           struct nvlentry *tentry)
881 {
882     afs_int32 hashindex, errorcode;
883     struct nvlentry ventry;
884
885     if (FindByID
886         (ctx, tentry->volumeId[voltype], voltype, &ventry, &errorcode))
887         return VL_IDALREADYHASHED;
888     else if (errorcode)
889         return errorcode;
890     hashindex = IDHash(tentry->volumeId[voltype]);
891     tentry->nextIdHash[voltype] =
892         ntohl(ctx->cheader->VolidHash[voltype][hashindex]);
893     ctx->cheader->VolidHash[voltype][hashindex] = htonl(blockindex);
894     if (vlwrite
895         (ctx->trans, DOFFSET(0, ctx->cheader, &ctx->cheader->VolidHash[voltype][hashindex]),
896          (char *)&ctx->cheader->VolidHash[voltype][hashindex], sizeof(afs_int32)))
897         return VL_IO;
898     return 0;
899 }
900
901
902 /* cheader must have be read before this routine is called. */
903 int
904 UnhashVolid(struct vl_ctx *ctx, afs_int32 voltype, afs_int32 blockindex,
905             struct nvlentry *aentry)
906 {
907     int hashindex, nextblockindex, prevblockindex;
908     struct nvlentry tentry;
909     afs_int32 code;
910     afs_int32 temp;
911
912     if (aentry->volumeId[voltype] == NULLO)     /* Assume no volume id */
913         return 0;
914     /* Take it out of the VolId[voltype] hash list */
915     hashindex = IDHash(aentry->volumeId[voltype]);
916     nextblockindex = ntohl(ctx->cheader->VolidHash[voltype][hashindex]);
917     if (nextblockindex == blockindex) {
918         /* First on the hash list; just adjust pointers */
919         ctx->cheader->VolidHash[voltype][hashindex] =
920             htonl(aentry->nextIdHash[voltype]);
921         code =
922             vlwrite(ctx->trans,
923                     DOFFSET(0, ctx->cheader,
924                             &ctx->cheader->VolidHash[voltype][hashindex]),
925                     (char *)&ctx->cheader->VolidHash[voltype][hashindex],
926                     sizeof(afs_int32));
927         if (code)
928             return VL_IO;
929     } else {
930         while (nextblockindex != blockindex) {
931             prevblockindex = nextblockindex;    /* always done once */
932             if (vlentryread
933                 (ctx->trans, nextblockindex, (char *)&tentry, sizeof(nvlentry)))
934                 return VL_IO;
935             if ((nextblockindex = tentry.nextIdHash[voltype]) == NULLO)
936                 return VL_NOENT;
937         }
938         temp = tentry.nextIdHash[voltype] = aentry->nextIdHash[voltype];
939         temp = htonl(temp);     /* convert to network byte order before writing */
940         if (vlwrite
941             (ctx->trans,
942              DOFFSET(prevblockindex, &tentry, &tentry.nextIdHash[voltype]),
943              (char *)&temp, sizeof(afs_int32)))
944             return VL_IO;
945     }
946     aentry->nextIdHash[voltype] = 0;
947     return 0;
948 }
949
950
951 int
952 HashVolname(struct vl_ctx *ctx, afs_int32 blockindex,
953             struct nvlentry *aentry)
954 {
955     afs_int32 hashindex;
956     afs_int32 code;
957
958     /* Insert into volname's hash linked list */
959     hashindex = NameHash(aentry->name);
960     aentry->nextNameHash = ntohl(ctx->cheader->VolnameHash[hashindex]);
961     ctx->cheader->VolnameHash[hashindex] = htonl(blockindex);
962     code =
963         vlwrite(ctx->trans, DOFFSET(0, ctx->cheader, &ctx->cheader->VolnameHash[hashindex]),
964                 (char *)&ctx->cheader->VolnameHash[hashindex], sizeof(afs_int32));
965     if (code)
966         return VL_IO;
967     return 0;
968 }
969
970
971 int
972 UnhashVolname(struct vl_ctx *ctx, afs_int32 blockindex,
973               struct nvlentry *aentry)
974 {
975     afs_int32 hashindex, nextblockindex, prevblockindex;
976     struct nvlentry tentry;
977     afs_int32 temp;
978
979     /* Take it out of the Volname hash list */
980     hashindex = NameHash(aentry->name);
981     nextblockindex = ntohl(ctx->cheader->VolnameHash[hashindex]);
982     if (nextblockindex == blockindex) {
983         /* First on the hash list; just adjust pointers */
984         ctx->cheader->VolnameHash[hashindex] = htonl(aentry->nextNameHash);
985         if (vlwrite
986             (ctx->trans, DOFFSET(0, ctx->cheader, &ctx->cheader->VolnameHash[hashindex]),
987              (char *)&ctx->cheader->VolnameHash[hashindex], sizeof(afs_int32)))
988             return VL_IO;
989     } else {
990         while (nextblockindex != blockindex) {
991             prevblockindex = nextblockindex;    /* always done at least once */
992             if (vlentryread
993                 (ctx->trans, nextblockindex, (char *)&tentry, sizeof(nvlentry)))
994                 return VL_IO;
995             if ((nextblockindex = tentry.nextNameHash) == NULLO)
996                 return VL_NOENT;
997         }
998         tentry.nextNameHash = aentry->nextNameHash;
999         temp = htonl(tentry.nextNameHash);
1000         if (vlwrite
1001             (ctx->trans, DOFFSET(prevblockindex, &tentry, &tentry.nextNameHash),
1002              (char *)&temp, sizeof(afs_int32)))
1003             return VL_IO;
1004     }
1005     aentry->nextNameHash = 0;
1006     return 0;
1007 }
1008
1009
1010 /* Returns the vldb entry tentry at offset index; remaining is the number of
1011  * entries left; the routine also returns the index of the next sequential
1012  * entry in the vldb
1013  */
1014
1015 afs_int32
1016 NextEntry(struct vl_ctx *ctx, afs_int32 blockindex,
1017           struct nvlentry *tentry, afs_int32 *remaining)
1018 {
1019     afs_int32 lastblockindex;
1020
1021     if (blockindex == 0)        /* get first one */
1022         blockindex = sizeof(*ctx->cheader);
1023     else {
1024         if (!index_OK(ctx, blockindex)) {
1025             *remaining = -1;    /* error */
1026             return 0;
1027         }
1028         blockindex += sizeof(nvlentry);
1029     }
1030     /* now search for the first entry that isn't free */
1031     for (lastblockindex = ntohl(ctx->cheader->vital_header.eofPtr);
1032          blockindex < lastblockindex;) {
1033         if (vlentryread(ctx->trans, blockindex, (char *)tentry, sizeof(nvlentry))) {
1034             *remaining = -1;
1035             return 0;
1036         }
1037         if (tentry->flags == VLCONTBLOCK) {
1038             /*
1039              * This is a special mh extension block just simply skip over it
1040              */
1041             blockindex += VL_ADDREXTBLK_SIZE;
1042         } else {
1043             if (tentry->flags != VLFREE) {
1044                 /* estimate remaining number of entries, not including this one */
1045                 *remaining =
1046                     (lastblockindex - blockindex) / sizeof(nvlentry) - 1;
1047                 return blockindex;
1048             }
1049             blockindex += sizeof(nvlentry);
1050         }
1051     }
1052     *remaining = 0;             /* no more entries */
1053     return 0;
1054 }
1055
1056
1057 /* Routine to verify that index is a legal offset to a vldb entry in the
1058  * table
1059  */
1060 static int
1061 index_OK(struct vl_ctx *ctx, afs_int32 blockindex)
1062 {
1063     if ((blockindex < sizeof(*ctx->cheader))
1064         || (blockindex >= ntohl(ctx->cheader->vital_header.eofPtr)))
1065         return 0;
1066     return 1;
1067 }
1068
1069 /* makes a deep copy of src_ex into dst_ex */
1070 static int
1071 vlexcpy(struct extentaddr **dst_ex, struct extentaddr **src_ex)
1072 {
1073     int i;
1074     for (i = 0; i < VL_MAX_ADDREXTBLKS; i++) {
1075         if (src_ex[i]) {
1076             if (!dst_ex[i]) {
1077                 dst_ex[i] = malloc(VL_ADDREXTBLK_SIZE);
1078             }
1079             if (!dst_ex[i]) {
1080                 return VL_NOMEM;
1081             }
1082             memcpy(dst_ex[i], src_ex[i], VL_ADDREXTBLK_SIZE);
1083
1084         } else if (dst_ex[i]) {
1085             /* we have no src, but we have a dst... meaning, this block
1086              * has gone away */
1087             free(dst_ex[i]);
1088             dst_ex[i] = NULL;
1089         }
1090     }
1091     return 0;
1092 }
1093
1094 int
1095 vlsetcache(struct vl_ctx *ctx, int locktype)
1096 {
1097     if (locktype == LOCKREAD) {
1098         ctx->hostaddress = rd_HostAddress;
1099         ctx->ex_addr = rd_ex_addr;
1100         ctx->cheader = &rd_cheader;
1101         return 0;
1102     } else {
1103         memcpy(wr_HostAddress, rd_HostAddress, sizeof(wr_HostAddress));
1104         memcpy(&wr_cheader, &rd_cheader, sizeof(wr_cheader));
1105
1106         ctx->hostaddress = wr_HostAddress;
1107         ctx->ex_addr = wr_ex_addr;
1108         ctx->cheader = &wr_cheader;
1109
1110         return vlexcpy(wr_ex_addr, rd_ex_addr);
1111     }
1112 }
1113
1114 int
1115 vlsynccache(void)
1116 {
1117     memcpy(rd_HostAddress, wr_HostAddress, sizeof(rd_HostAddress));
1118     memcpy(&rd_cheader, &wr_cheader, sizeof(rd_cheader));
1119     return vlexcpy(rd_ex_addr, wr_ex_addr);
1120 }