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