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