e62d5f1fc5139c720e8a0776d0318cc73e27ccaf
[openafs.git] / src / vlserver / cnvldb.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 RCSID
14     ("$Header$");
15
16 #include <afs/stds.h>
17 #include <sys/types.h>
18 #include <errno.h>
19 #include <stdio.h>
20 #include <sys/file.h>
21 #include <string.h>
22
23 #include "cnvldb.h"             /* CHANGEME! */
24 #include <netinet/in.h>
25 #include <afs/venus.h>
26 #include <afs/cmd.h>
27 #include <afs/afsutil.h>
28 #include <afs/fileutil.h>
29
30 #include "vlserver.h"
31
32 #define MAXSIZE 2048            /* most I'll get back from PIOCTL */
33 #define BADSERVERID     255     /* XXX */
34
35
36 static char pn[] = "cnvldb";
37 static char tempname[] = "XXnewvldb";
38 static char space[MAXSIZE];
39 static int MaxServers[3] = { 30, 254, 254 };    /* max server # permitted in this version */
40
41 static afs_int32 Conv4to3();
42
43 static int convert_vlentry();
44 static int rewrite_header();
45
46 static char tspace[1024];       /* chdir can't handle anything bigger, anyway */
47 /* return a static pointer to a buffer */
48 static char *
49 Parent(apath)
50      char *apath;
51 {
52     register char *tp;
53     strcpy(tspace, apath);
54     tp = strrchr(tspace, '/');
55     if (tp) {
56         *tp = 0;
57     } else
58         strcpy(tspace, ".");
59     return tspace;
60 }
61
62 int oldpos = 0;
63 int fromvers = 0, tovers = 0, showversion = 0;
64 afs_uint32 mhaddr;
65 afs_int32 dbsize;
66 char *pathname = NULL;
67 const char *dbPath;
68
69 static int
70 handleit(struct cmd_syndesc *as, void *arock)
71 {
72     register struct cmd_item *ti;
73     register afs_int32 code;
74     int w, old, new, rc, dump = 0, fromv = 0;
75     short uvers;
76     char ubik[80];              /* space for some ubik header */
77     union {
78         struct vlheader_1 header1;
79         struct vlheader_2 header2;
80         struct vlheader_3 header3;
81     } oldheader, oldheader1, newheader; /* large enough for either */
82
83     union {
84         struct vlentry_1 entry1;
85         struct vlentry_2 entry2;
86         struct vlentry_3 entry3;
87         char mhinfo_block[VL_ADDREXTBLK_SIZE];
88     } xvlentry;
89
90     pathname = (as->parms[2].items ? as->parms[2].items->data : dbPath);        /* -name */
91     showversion = (as->parms[3].items ? 1 : 0); /* -showversion */
92     dump = (as->parms[4].items ? 1 : 0);        /* -dumpvldb */
93     fromvers = (as->parms[1].items ? atoi(as->parms[1].items->data) : 0);       /* -fromversion */
94     tovers = (as->parms[0].items ? atoi(as->parms[0].items->data) : 0); /* -toversion */
95
96     /* should stat() the old vldb, get its size, and see if there's */
97     /* room for another.  It might be in AFS, so check the quota, too */
98     old = open(pathname, O_RDONLY);
99     if (old < 0) {
100         perror(pn);
101         exit(-1);
102     }
103
104     /* Read the version */
105     lseek(old, 64, L_SET);
106     read(old, &fromv, sizeof(int));
107     fromv = ntohl(fromv);
108     if ((fromv < 1) || (fromv > 4)) {
109         fprintf(stderr, pn);
110         fprintf(stderr, ": Unrecognized VLDB version %d.\n", fromv);
111         exit(-1);
112     }
113
114     /* Sequentially read the database converting the entries as we go */
115     lseek(old, 0, L_SET);
116     read(old, ubik, 64);
117     readheader(old, fromv, &oldheader);
118     if (fromv == 1) {
119         dbsize = ntohl(oldheader.header1.vital_header.eofPtr);
120         fromv = ntohl(oldheader.header1.vital_header.vldbversion);
121         mhaddr = 0;
122     } else if (fromv == 2) {
123         dbsize = ntohl(oldheader.header2.vital_header.eofPtr);
124         fromv = ntohl(oldheader.header2.vital_header.vldbversion);
125         mhaddr = 0;
126     } else {
127         int pos;
128
129         dbsize = ntohl(oldheader.header3.vital_header.eofPtr);
130         fromv = ntohl(oldheader.header3.vital_header.vldbversion);
131         mhaddr = ntohl(oldheader.header3.SIT);
132
133         /* Read the multihomed extent blocks in */
134         pos = oldpos;
135         read_mhentries(mhaddr, old);
136
137         /* Position back to this after header */
138         lseek(old, pos + 64, L_SET);
139         oldpos = pos;
140     }
141
142     if (showversion || dump) {
143         if (showversion)
144             fprintf(stdout, "%s has a version of %d\n", pathname, fromv);
145         if (dump) {
146             while (oldpos < dbsize) {
147                 rc = readentry(old, fromv, &xvlentry);
148                 if ((rc == 0) || (rc == EOF))
149                     break;
150                 printentry(fromv, &xvlentry);
151             }
152         }
153         exit(0);
154     }
155
156     if (!fromvers) {            /* not set */
157         fromvers = fromv;
158     } else if (fromvers != fromv) {
159         fprintf(stdout,
160                 "%s has a version of %d while the -fromversion specified was %d - aborting\n",
161                 pathname, fromv, fromvers);
162         exit(0);
163     }
164
165     if ((fromvers < 1) || (fromvers > 4)) {
166         fprintf(stderr, pn);
167         fprintf(stderr, ": VLDB version %d is not supported.\n", fromvers);
168         fprintf(stderr, pn);
169         fprintf(stderr, ": Only versions 1-4 are currently supported.\n");
170         exit(-1);
171     }
172
173     if (!tovers)
174         tovers = fromvers + 1;
175
176     if (tovers < 1 || tovers > 4) {
177         fprintf(stderr, pn);
178         fprintf(stderr, ": VLDB version %d is not supported.\n", tovers);
179         fprintf(stderr, pn);
180         fprintf(stderr, ": Only versions 1 - 4 are currently supported.\n");
181         exit(-1);
182     }
183
184     if (mhaddr && (tovers < 3)) {
185         fprintf(stderr, pn);
186         fprintf(stderr, ": Cannot convert. VLDB contains multihome info.\n");
187         exit(-1);
188     }
189
190     /* OK! let's get down to business... */
191
192     if (chdir(Parent(pathname))) {
193         perror(pn);
194         exit(-1);
195     }
196
197     new = open(tempname, O_WRONLY | O_CREAT | O_TRUNC, 0600);
198     if (new < 0) {
199         perror(pn);
200         exit(-1);
201     }
202
203     /* Write the UBIK data */
204     w = write(new, ubik, 64);
205     if (w != 64) {
206         printf("Write of ubik header failed %d; error %u\n", w, errno);
207         exit(1);
208     }
209
210     /* Because we know that all the vldb entries are the same size and type we 
211      * can just read them sequentially, fiddle with the fields, and write
212      * them out again.  If we invent a vldb format that has different
213      * types of entries, then we're going to have to invent new logic for
214      * converting the vldb-- we'll probably have to chase down the various
215      * linked lists in turn, doing lseeks and the like.
216      */
217
218     convert_header(old, new, fromvers, tovers, &oldheader, &newheader);
219     while (oldpos < dbsize) {
220         rc = readentry(old, fromvers, &xvlentry);
221         if ((rc == 0) || (rc == EOF))
222             break;
223         convert_vlentry(new, fromvers, tovers, &oldheader, &newheader,
224                         &xvlentry);
225     }
226
227     /* We have now finished sequentially reading and writing the database.
228      * Now randomly offset into database and update multihome entries.
229      */
230     convert_mhentries(old, new, &newheader, fromvers, tovers);
231     rewrite_header(new, tovers, &newheader);
232
233     close(old);
234     if (fsync(new)) {
235         perror(pn);
236         exit(-1);
237     }
238     close(new);
239
240     renamefile(tempname, pathname);
241     sleep(5);
242     exit(0);
243 }
244
245
246 readheader(fd, version, addr)
247      int fd;
248      int version;
249      char *addr;
250 {
251     int hdrsize, size = 0;
252
253     oldpos = 0;
254     if (version == 1)
255         hdrsize = sizeof(struct vlheader_1);
256     else
257         hdrsize = sizeof(struct vlheader_2);
258
259     size = read(fd, addr, hdrsize);
260     if (size > 0)
261         oldpos += size;
262
263     return;
264 }
265
266 readentry(fd, version, addr)
267      int fd;
268      int version;
269      char *addr;
270 {
271     int rc, rc1;
272     struct vlentry_3 *vl3p = (struct vlentry_3 *)addr;
273     int toread;
274
275     toread =
276         ((version ==
277           1) ? sizeof(struct vlentry_1) : sizeof(struct vlentry_2));
278     rc = read(fd, addr, toread);
279     if (rc != toread)
280         printf("Partial read of vlentry at pos %u: %d\n", oldpos, rc);
281     if (rc > 0)
282         oldpos += rc;
283
284     /* Read a mhblock entry if there is one */
285     if ((rc > 0) && (vl3p->flags == VLCONTBLOCK)) {
286         if (!mhaddr)            /* Remember first mh block */
287             mhaddr = oldpos - rc;
288
289         rc1 = read(fd, &addr[rc], VL_ADDREXTBLK_SIZE - rc);
290         if (rc1 != VL_ADDREXTBLK_SIZE - rc)
291             printf("Partial read of mhblock at pos %u: %d\n", oldpos + rc,
292                    rc1);
293         if (rc1 > 0) {
294             oldpos += rc1;
295             rc += rc1;
296         }
297     }
298
299     return rc;
300 }
301
302 printentry(version, addr)
303      int version;
304      char *addr;
305 {
306     struct vlentry_2 *vl2p = (struct vlentry_2 *)addr;
307     struct vlentry_3 *vl3p = (struct vlentry_3 *)addr;
308     int i;
309
310     /* Don't print anything if the entry is a mh info block */
311     if (vl3p->flags == VLCONTBLOCK) {
312         return;
313     }
314
315     if (version == 1 || version == 2) {
316         printf("%s\t%5d [%10d:%10d:%10d]%8X%8d\n", vl2p->name, vl2p->spares3,
317                vl2p->volumeId[0], vl2p->volumeId[1], vl2p->volumeId[2],
318                vl2p->flags, vl2p->LockAfsId);
319         printf("\t%8d%8d%8d [%7d%7d%7d]%7d% [%4d%4d%4d%4d][%4d%4d%4d%4d]\n",
320                vl2p->LockTimestamp, vl2p->cloneId, vl2p->spares0,
321                vl2p->nextIdHash[0], vl2p->nextIdHash[1], vl2p->nextIdHash[2],
322                vl2p->nextNameHash, vl2p->serverNumber[0],
323                vl2p->serverNumber[1], vl2p->serverNumber[2],
324                vl2p->serverNumber[3], vl2p->serverPartition[0],
325                vl2p->serverPartition[1], vl2p->serverPartition[2],
326                vl2p->serverPartition[3]);
327         printf("\t[%4d%4d%4d%4d]\n", vl2p->serverFlags[0],
328                vl2p->serverFlags[1], vl2p->serverFlags[2],
329                vl2p->serverFlags[3]);
330     } else {                    /* if (version >= 3) */
331
332         if (vl3p->flags == VLFREE)
333             return;
334         printf("%s\tPos=%d NextIdHash=[%d:%d:%d] NextNameHash=%d\n",
335                vl3p->name, (oldpos - sizeof(struct vlentry_3)),
336                vl3p->nextIdHash[0], vl3p->nextIdHash[1], vl3p->nextIdHash[2],
337                vl3p->nextNameHash);
338         printf("\tRW=%u RO=%u BK=%u CL=%u flags=0x%X lockBy=%d lockTime=%u\n",
339                vl3p->volumeId[0], vl3p->volumeId[1], vl3p->volumeId[2],
340                vl3p->cloneId, vl3p->flags, vl3p->LockAfsId,
341                vl3p->LockTimestamp);
342         for (i = 0; i < OMAXNSERVERS; i++) {
343             if ((vl3p->serverNumber[i] & 0xff) != 0xff) {
344                 printf("\tServer=%d Partition=%d flags=%X\n",
345                        vl3p->serverNumber[i], vl3p->serverPartition[i],
346                        vl3p->serverFlags[i]);
347             }
348         }
349     }
350     return;
351 }
352
353 int readmhentries = 0;
354 struct extentaddr *base[VL_MAX_ADDREXTBLKS];
355
356 /* Read the multihome extent blocks in. Check if they are good by
357  * verifying their address is not pass the EOF and the flags are good.
358  * If it's not good, then don't read the block in.
359  */
360 void
361 read_mhentries(mh_addr, oldfd)
362      int oldfd;
363      afs_uint32 mh_addr;
364 {
365     afs_uint32 sit, a;
366     afs_int32 code;
367     int j;
368
369     if (readmhentries)
370         return;
371     readmhentries = 1;
372
373     /* Initialize base pointers */
374     for (j = 0; j < VL_MAX_ADDREXTBLKS; j++)
375         base[j] = 0;
376
377     if (!mh_addr)
378         return;
379
380     /* Check if the first extent block is beyond eof. If 
381      * it is, it's not real.
382      */
383     if (mh_addr > dbsize - VL_ADDREXTBLK_SIZE)
384         return;
385
386     /* Now read the first mh extent block */
387     code = lseek(oldfd, mh_addr + 64, L_SET);
388     if (code < 0) {
389         perror("seek MH block");
390         exit(1);
391     }
392     base[0] = (struct extentaddr *)malloc(VL_ADDREXTBLK_SIZE);
393     if (!base[0]) {
394         perror("malloc1");
395         exit(1);
396     }
397     code = read(oldfd, (char *)base[0], VL_ADDREXTBLK_SIZE);
398     if (code != VL_ADDREXTBLK_SIZE) {
399         perror("read MH block");
400         free(base[0]);
401         base[0] = 0;
402         exit(1);
403     }
404
405     /* Verify that this block is the right one */
406     if (ntohl(base[0]->ex_flags) != VLCONTBLOCK) {      /* check if flag is correct */
407         free(base[0]);
408         base[0] = 0;
409         return;
410     }
411
412     /* The first block contains pointers to the other extent blocks.
413      * Check to see if the pointers are good and read them in if they are.
414      */
415     a = mh_addr;
416     for (j = 1; j < VL_MAX_ADDREXTBLKS; j++) {
417         if (!base[0]->ex_contaddrs[j])
418             continue;
419
420         sit = ntohl(base[0]->ex_contaddrs[j]);
421
422         /* Every time we allocate a new extent block, it is allocated after 
423          * the previous ones. But it must be before the EOF.
424          */
425         if ((sit < (a + VL_ADDREXTBLK_SIZE))
426             || (sit > dbsize - VL_ADDREXTBLK_SIZE)) {
427             continue;
428         }
429
430         /* Read the extent block in */
431         sit += 64;
432         code = lseek(oldfd, sit, L_SET);
433         if (code < 0) {
434             perror("seek MH block");
435             exit(1);
436         }
437         base[j] = (struct extentaddr *)malloc(VL_ADDREXTBLK_SIZE);
438         if (!base[j]) {
439             perror("malloc1");
440             exit(1);
441         }
442         code = read(oldfd, (char *)base[j], VL_ADDREXTBLK_SIZE);
443         if (code != VL_ADDREXTBLK_SIZE) {
444             perror("read MH block");
445             exit(1);
446         }
447
448         /* Verify that this block knows its an extent block */
449         if (ntohl(base[j]->ex_flags) != VLCONTBLOCK) {
450             free(base[j]);
451             base[j] = 0;
452             continue;
453         }
454
455         /* The extent block passed our tests */
456         a = ntohl(base[0]->ex_contaddrs[j]);
457     }
458 }
459
460 /* Follow the SIT pointer in the header (mhaddr) to the multihomed
461  * extent blocks and verify that the pointers are good. And fix.
462  * Then convert the multihomed addresses to single address if we
463  * are converting back from version 4.
464  * 
465  * Before this can be called, the routine read_mhentries must be called.
466  */
467 void
468 convert_mhentries(oldfd, newfd, header, fromver, tover)
469      int oldfd, newfd;
470      struct vlheader_2 *header;
471      int fromver, tover;
472 {
473     afs_uint32 sit;
474     afs_int32 code;
475     int i, j, modified = 0, w;
476     afs_uint32 raddr, addr;
477     struct extentaddr *exp;
478     int basei, index;
479
480     /* Check if the header says the extent block exists. If
481      * it does, then read_mhentries should have read it in.
482      */
483     if (mhaddr && !base[0]) {
484         printf("Fix Bad base extent block pointer\n");
485         header->SIT = mhaddr = 0;
486     } else if (mhaddr && base[0]) {
487
488         if ((ntohl(header->SIT) != mhaddr) && (tover == 4)) {
489             printf
490                 ("Fix pointer to first base extent block. Was 0x%x, now 0x%x\n",
491                  ntohl(header->SIT), mhaddr);
492             header->SIT = htonl(mhaddr);
493         }
494
495         /* Check if the first block points to itself. If not, then fix it */
496         if (ntohl(base[0]->ex_contaddrs[0]) != mhaddr) {
497             printf("Fix bad pointer in base extent block: Base 0\n");
498             base[0]->ex_contaddrs[0] = htonl(mhaddr);
499             modified = 1;
500         }
501
502         /* The first block contains pointers to the other extent blocks.
503          * Check to see if the pointers are good.
504          */
505         for (j = 1; j < VL_MAX_ADDREXTBLKS; j++) {
506             /* Check if the base extent block says the extent blocks exist.
507              * If it does, then read_mhentries should have read it in.
508              */
509             if (base[0]->ex_contaddrs[j] && !base[j]) {
510                 printf("Fix bad pointer in base extent block: Base %d\n", j);
511                 base[0]->ex_contaddrs[j] = 0;
512                 modified = 1;
513             }
514         }
515
516         /* Now write out the base extent blocks if it changed */
517         if (modified) {
518             code = lseek(newfd, mhaddr + 64, L_SET);
519             if (code < 0) {
520                 perror("seek MH Block");
521                 exit(1);
522             }
523             w = write(newfd, (char *)base[0], VL_ADDREXTBLK_SIZE);
524             if (w != VL_ADDREXTBLK_SIZE) {
525                 perror("write MH Block");
526                 exit(1);
527             }
528         }
529     }
530
531     /* If we are converting from version 4 to version 3, then 
532      * translate any multihome ptrs in the IpMappedAddr array
533      * to true IP addresses.
534      */
535     if ((fromver == 4) && (tover == 3)) {
536         /* Step through the fileserver addresses in the VLDB header
537          * and convert the pointers back to IP addresses.
538          */
539         for (i = 0; i < 254; i++) {
540             addr = ntohl(header->IpMappedAddr[i]);
541             if (addr && ((addr & 0xff000000) == 0xff000000)) {
542                 basei = (addr >> 16) & 0xff;
543                 index = addr & 0xffff;
544
545                 if ((basei >= VL_ADDREXTBLK_SIZE) || !base[basei]) {
546                     fprintf(stderr,
547                             "Warning: mh entry %d has no IP address; ignored!!\n",
548                             i);
549                     header->IpMappedAddr[i] = 0;
550                     continue;
551                 }
552                 exp = &base[basei][index];
553
554                 /* For now return the first ip address back */
555                 for (j = 0; j < VL_MAXIPADDRS_PERMH; j++) {
556                     if (exp->ex_addrs[j]) {
557                         raddr = ntohl(exp->ex_addrs[j]);
558                         break;
559                     }
560                 }
561                 if (j >= VL_MAXIPADDRS_PERMH) {
562                     fprintf(stderr,
563                             "Warning: mh entry %d has no ip address; ignored!!\n",
564                             i);
565                     raddr = 0;
566                 } else {
567                     printf
568                         ("Multi-homed addr: converting to single ip address %d.%d.%d.%d\n",
569                          (raddr >> 24 & 0xff), (raddr >> 16 & 0xff),
570                          (raddr >> 8 & 0xff), (raddr & 0xff));
571                 }
572                 header->IpMappedAddr[i] = htonl(raddr);
573             }
574         }
575         header->SIT = mhaddr = 0;       /* mhinfo block has been removed */
576
577         /* Now step through the hash tables in header updating them.
578          * Because we removed the mh info blocks and some entries they
579          * point to may have changed position.
580          * The VolnameHash
581          */
582         for (i = 0; i < 8191; i++) {
583             header->VolnameHash[i] = Conv4to3(header->VolnameHash[i]);
584         }
585         /* The VolidHash */
586         for (i = 0; i < 3; i++) {
587             for (j = 0; j < 8191; j++) {
588                 header->VolidHash[i][j] = Conv4to3(header->VolidHash[i][j]);
589             }
590         }
591
592         /* Update eofptr to take into account the removal of the mhinfo blocks */
593         header->vital_header.eofPtr = htonl(Conv4to3(dbsize));
594     }
595 }
596
597
598 convert_header(ofd, fd, fromv, tov, fromaddr, toaddr)
599      int ofd, fd, fromv, tov;
600      char *fromaddr, *toaddr;
601 {
602     struct vlheader_1 *tvp1;
603     struct vlheader_2 *tvp2;
604     int i, j, diff, w;
605
606     if (fromv == 1) {
607         if (tov == 1) {
608             memcpy(toaddr, fromaddr, sizeof(struct vlheader_1));
609             tvp1 = (struct vlheader_1 *)toaddr;
610
611             w = write(fd, tvp1, sizeof(struct vlheader_1));
612             if (w != sizeof(struct vlheader_1)) {
613                 printf("Write of header failed %d; error %u\n", w, errno);
614                 exit(1);
615             }
616
617             /* for garbage-collecting... */
618             for (i = 0; i < 31; i++)
619                 tvp1->IpMappedAddr[i] = 0;
620
621         } else if (tov == 2 || tov == 3) {
622             tvp1 = (struct vlheader_1 *)fromaddr;
623             tvp2 = (struct vlheader_2 *)toaddr;
624             memset(tvp2, 0, sizeof(struct vlheader_2));
625             tvp2->vital_header.vldbversion = htonl(tov);
626             tvp2->vital_header.headersize = htonl(sizeof(struct vlheader_2));
627             diff =
628                 ntohl(tvp2->vital_header.headersize) -
629                 ntohl(tvp1->vital_header.headersize);
630             if (ntohl(tvp1->vital_header.freePtr))
631                 tvp2->vital_header.freePtr =
632                     htonl(ntohl(tvp1->vital_header.freePtr) + diff);
633             if (ntohl(tvp1->vital_header.eofPtr))
634                 tvp2->vital_header.eofPtr =
635                     htonl(ntohl(tvp1->vital_header.eofPtr) + diff);
636             tvp2->vital_header.allocs = tvp1->vital_header.allocs;
637             tvp2->vital_header.frees = tvp1->vital_header.frees;
638             tvp2->vital_header.MaxVolumeId = tvp1->vital_header.MaxVolumeId;
639             for (i = 0; i < 3; i++)
640                 tvp2->vital_header.totalEntries[i] =
641                     tvp1->vital_header.totalEntries[i];
642
643             for (i = 0; i < 31; i++)
644                 tvp2->IpMappedAddr[i] = tvp1->IpMappedAddr[i];
645
646             for (i = 0; i < 8191; i++) {
647                 if (ntohl(tvp1->VolnameHash[i]))
648                     tvp2->VolnameHash[i] =
649                         htonl(ntohl(tvp1->VolnameHash[i]) + diff);
650             }
651
652             for (i = 0; i < 3; i++) {
653                 for (j = 0; j < 8191; j++) {
654                     if (ntohl(tvp1->VolidHash[i][j]))
655                         tvp2->VolidHash[i][j] =
656                             htonl(ntohl(tvp1->VolidHash[i][j]) + diff);
657                 }
658             }
659
660             w = write(fd, tvp2, sizeof(struct vlheader_2));
661             if (w != sizeof(struct vlheader_2)) {
662                 printf("Write of header failed %d; error %u\n", w, errno);
663                 exit(1);
664             }
665
666             /* for garbage-collecting... */
667             for (i = 0; i < 31; i++)
668                 tvp2->IpMappedAddr[i] = 0;
669         } else
670             return EINVAL;
671     } else if (fromv == 2 || fromv == 3 || fromv == 4) {
672         if (tov == 2 || tov == 3 || tov == 4) {
673             memcpy(toaddr, fromaddr, sizeof(struct vlheader_2));
674             tvp2 = (struct vlheader_2 *)toaddr;
675             tvp2->vital_header.vldbversion = htonl(tov);
676             w = write(fd, tvp2, sizeof(struct vlheader_2));
677             if (w != sizeof(struct vlheader_2)) {
678                 printf("Write of header failed %d; error %u\n", w, errno);
679                 exit(1);
680             }
681
682         } else if (tov == 1) {
683             tvp2 = (struct vlheader_2 *)fromaddr;
684             tvp1 = (struct vlheader_1 *)toaddr;
685             memset(tvp1, 0, sizeof(struct vlheader_1));
686             tvp1->vital_header.vldbversion = htonl(1);
687             tvp1->vital_header.headersize = htonl(sizeof(struct vlheader_1));
688             diff =
689                 ntohl(tvp1->vital_header.headersize) -
690                 ntohl(tvp2->vital_header.headersize);
691             if (ntohl(tvp2->vital_header.freePtr))
692                 tvp1->vital_header.freePtr =
693                     htonl(ntohl(tvp2->vital_header.freePtr) + diff);
694             if (ntohl(tvp2->vital_header.eofPtr))
695                 tvp1->vital_header.eofPtr =
696                     htonl(ntohl(tvp2->vital_header.eofPtr) + diff);
697             tvp1->vital_header.allocs = tvp2->vital_header.allocs;
698             tvp1->vital_header.frees = tvp2->vital_header.frees;
699             tvp1->vital_header.MaxVolumeId = tvp2->vital_header.MaxVolumeId;
700             for (i = 0; i < 3; i++)
701                 tvp1->vital_header.totalEntries[i] =
702                     tvp2->vital_header.totalEntries[i];
703
704             for (i = 0; i < 31; i++)
705                 tvp1->IpMappedAddr[i] = tvp2->IpMappedAddr[i];
706
707             for (i = 0; i < 8191; i++) {
708                 if (ntohl(tvp2->VolnameHash[i]))
709                     tvp1->VolnameHash[i] =
710                         htonl(ntohl(tvp2->VolnameHash[i]) + diff);
711             }
712
713             for (i = 0; i < 3; i++) {
714                 for (j = 0; j < 8191; j++) {
715                     if (ntohl(tvp2->VolidHash[i][j]))
716                         tvp1->VolidHash[i][j] =
717                             htonl(ntohl(tvp2->VolidHash[i][j]) + diff);
718                 }
719             }
720
721             w = write(fd, tvp1, sizeof(struct vlheader_1));
722             if (w != sizeof(struct vlheader_2)) {
723                 printf("Write of header failed %d; error %u\n", w, errno);
724                 exit(1);
725             }
726
727             /* for garbage-collecting... */
728             for (i = 0; i < 31; i++)
729                 tvp1->IpMappedAddr[i] = 0;
730         } else
731             return EINVAL;
732     } else
733         return EINVAL;
734     return 0;
735 }
736
737
738 /* Convert an address pointer to a vlentry from version 4 to version 3.
739  * This involves checking if the address is after any of the four
740  * MH block and if it is, subtract the size of the MH block. 
741  *
742  * In going from version 4 to 3, the mh blocks go away and all entries
743  * move up in their place. The adresses then need to be updated.
744  *
745  * Before this can be called, the routine read_mhentries must be called.
746  */
747 static afs_int32
748 Conv4to3(addr)
749      afs_int32 addr;
750 {
751     afs_int32 raddr;
752     int i;
753
754     if (!base[0] || !addr)
755         return (addr);
756
757     raddr = addr;
758     for (i = 0; i < VL_MAX_ADDREXTBLKS; i++) {
759         if (base[i] && base[0]->ex_contaddrs[i]
760             && (addr > base[0]->ex_contaddrs[i]))
761             raddr -= VL_ADDREXTBLK_SIZE;
762     }
763
764     return (raddr);
765 }
766
767 /* this only works because the vlheader struct is essentially the same 
768  * from version 1 to version 2 -- that is, the first bunch of fields
769  * aren't any more or any larger, so they match up pretty well.
770 */
771
772 static int
773 convert_vlentry(new, fromvers, tovers, oldheader, newheader, vlentryp)
774      int new, fromvers, tovers;
775      struct vlheader_1 *oldheader, *newheader;  /* close enough */
776      struct vlentry_1 *vlentryp;        /* 1 and 2 are identical */
777 {
778     int diff, i, s, w;
779     struct vlentry_3 *vl3p = (struct vlentry_3 *)vlentryp;
780
781     /* For mh information blocks,
782      * If going to version 4 or greater, keep the mh info block.
783      * Otherwise, don't keep it (version 3 and earlier don't have them).
784      */
785     if (vl3p->flags == VLCONTBLOCK) {
786         if (tovers >= 4) {
787             w = write(new, vlentryp, VL_ADDREXTBLK_SIZE);
788             if (w != VL_ADDREXTBLK_SIZE) {
789                 printf("Write of mh info block failed %d; error %u\n", w,
790                        errno);
791                 exit(1);
792             }
793         }
794         return 0;
795     }
796
797     if (fromvers == 2 && tovers == 3) {
798         struct vlentry_3 vl;
799
800         vl.volumeId[0] = vlentryp->volumeId[0];
801         vl.volumeId[1] = vlentryp->volumeId[1];
802         vl.volumeId[2] = vlentryp->volumeId[2];
803         vl.flags = vlentryp->flags;
804         vl.LockAfsId = vlentryp->LockAfsId;
805         vl.LockTimestamp = vlentryp->LockTimestamp;
806         vl.cloneId = vlentryp->cloneId;
807         vl.nextIdHash[0] = vlentryp->nextIdHash[0];
808         vl.nextIdHash[1] = vlentryp->nextIdHash[1];
809         vl.nextIdHash[2] = vlentryp->nextIdHash[2];
810         vl.nextNameHash = vlentryp->nextNameHash;
811         memcpy(vl.name, vlentryp->name, 65);
812         for (i = 0; i < 8; i++) {
813             vl.serverNumber[i] = vlentryp->serverNumber[i];
814             vl.serverPartition[i] = vlentryp->serverPartition[i];
815             vl.serverFlags[i] = vlentryp->serverFlags[i];
816         }
817         for (; i < 13; i++)
818             vl.serverNumber[i] = vl.serverPartition[i] = vl.serverFlags[i] =
819                 BADSERVERID;
820         w = write(new, &vl, sizeof(struct vlentry_3));
821         if (w != sizeof(struct vlentry_3)) {
822             printf("Write of entry failed %d; error %u\n", w, errno);
823             exit(1);
824         }
825
826         return 0;
827     } else if (fromvers == 3 && tovers == 2) {
828         struct vlentry_2 vl;
829         struct vlentry_3 *xnvlentry = (struct vlentry_3 *)vlentryp;
830
831         memset((char *)&vl, 0, sizeof(struct vlentry_2));
832         vl.volumeId[0] = xnvlentry->volumeId[0];
833         vl.volumeId[1] = xnvlentry->volumeId[1];
834         vl.volumeId[2] = xnvlentry->volumeId[2];
835         vl.flags = xnvlentry->flags;
836         vl.LockAfsId = xnvlentry->LockAfsId;
837         vl.LockTimestamp = xnvlentry->LockTimestamp;
838         vl.cloneId = xnvlentry->cloneId;
839         for (i = 0; i < 3; i++) {
840             if (ntohl(xnvlentry->nextIdHash[i]))
841                 vl.nextIdHash[i] = xnvlentry->nextIdHash[i];
842         }
843         if (ntohl(xnvlentry->nextNameHash))
844             vl.nextNameHash = xnvlentry->nextNameHash;
845         memcpy(vl.name, xnvlentry->name, 65);
846         for (i = 0; i < 8; i++) {
847             vl.serverNumber[i] = xnvlentry->serverNumber[i];
848             vl.serverPartition[i] = xnvlentry->serverPartition[i];
849             vl.serverFlags[i] = xnvlentry->serverFlags[i];
850         }
851         w = write(new, &vl, sizeof(struct vlentry_2));
852         if (w != sizeof(struct vlentry_2)) {
853             printf("Write of entry failed %d; error %u\n", w, errno);
854             exit(1);
855         }
856         return 0;
857     } else if (fromvers == 3 && tovers == 1) {
858         struct vlentry_1 vl;
859         struct vlentry_3 *xnvlentry = (struct vlentry_3 *)vlentryp;
860
861         diff =
862             (tovers ==
863              1 ? sizeof(struct vlheader_1) : sizeof(struct vlheader_2))
864             - (fromvers ==
865                1 ? sizeof(struct vlheader_1) : sizeof(struct vlheader_2));
866         memset((char *)&vl, 0, sizeof(struct vlentry_1));
867         vl.volumeId[0] = xnvlentry->volumeId[0];
868         vl.volumeId[1] = xnvlentry->volumeId[1];
869         vl.volumeId[2] = xnvlentry->volumeId[2];
870         vl.flags = xnvlentry->flags;
871         vl.LockAfsId = xnvlentry->LockAfsId;
872         vl.LockTimestamp = xnvlentry->LockTimestamp;
873         vl.cloneId = xnvlentry->cloneId;
874         for (i = 0; i < 3; i++) {
875             if (ntohl(xnvlentry->nextIdHash[i]))
876                 vl.nextIdHash[i] =
877                     htonl(ntohl(xnvlentry->nextIdHash[i]) + diff);
878         }
879         if (ntohl(xnvlentry->nextNameHash))
880             vl.nextNameHash = htonl(ntohl(xnvlentry->nextNameHash) + diff);
881
882         memcpy(vl.name, xnvlentry->name, 65);
883         for (i = 0; i < 8; i++) {
884             vl.serverNumber[i] = xnvlentry->serverNumber[i];
885             vl.serverPartition[i] = xnvlentry->serverPartition[i];
886             vl.serverFlags[i] = xnvlentry->serverFlags[i];
887         }
888         for (i = 0; i < 8; i++) {
889             s = xnvlentry->serverNumber[i];
890             if (s != 255) {
891                 if (s > MaxServers[tovers - 1]) {
892                     fprintf(stderr,
893                             "%s: Too Many Servers (%d) for this version!\n",
894                             pn, s + 1);
895                     exit(-1);
896                 } else
897                     newheader->IpMappedAddr[s] = oldheader->IpMappedAddr[s];
898             }
899         }
900         w = write(new, &vl, sizeof(struct vlentry_1));
901         if (w != sizeof(struct vlentry_1)) {
902             printf("Write of entry failed %d; error %u\n", w, errno);
903             exit(1);
904         }
905         return 0;
906     } else if (fromvers == 4 && tovers == 3) {
907         struct vlentry_3 vl;
908         /* We are converting from version 4 to 3. In this conversion, mh info
909          * blocks go away and all vlentries after them move up in the vldb file.
910          * When this happens, the linked list pointers need to be updated.
911          */
912         memcpy(&vl, vlentryp, sizeof(vl));
913         for (i = 0; i < 3; i++) {
914             vl.nextIdHash[i] = Conv4to3(vl.nextIdHash[i]);
915         }
916         vl.nextNameHash = Conv4to3(vl.nextNameHash);
917
918         w = write(new, &vl, sizeof(vl));
919         if (w != sizeof(vl)) {
920             printf("Write of entry failed %d; error %u\n", w, errno);
921             exit(1);
922         }
923         return 0;
924     }
925
926     if (tovers == 1) {
927         w = write(new, vlentryp, sizeof(struct vlentry_1));
928         if (w != sizeof(struct vlentry_1)) {
929             printf("Write of entry failed %d; error %u\n", w, errno);
930             exit(1);
931         }
932     } else if (tovers == 2) {
933         w = write(new, vlentryp, sizeof(struct vlentry_2));
934         if (w != sizeof(struct vlentry_2)) {
935             printf("Write of entry failed %d; error %u\n", w, errno);
936             exit(1);
937         }
938     } else if (tovers == 3 || tovers == 4) {
939         w = write(new, vlentryp, sizeof(struct vlentry_3));
940         if (w != sizeof(struct vlentry_3)) {
941             printf("Write of entry failed %d; error %u\n", w, errno);
942             exit(1);
943         }
944     } else {
945         perror(pn);
946         fprintf(stderr,
947                 "Skipping vlentry write - db corrupted - bad toversion %d\n",
948                 tovers);
949     }
950
951     return;
952 }
953
954 static int
955 rewrite_header(new, tovers, newheader)
956      int new, tovers;
957      char *newheader;
958 {
959     int pos, w, towrite;
960
961     pos = lseek(new, 64, L_SET);        /* leave room for ubik */
962     if (pos == -1) {
963         perror(pn);
964         fprintf(stderr, "%s: no garbage colection\n", pn);
965         return;
966     } else if (pos != 64) {
967         fprintf(stderr, "%s: Can't rewind: no garbage collection\n", pn);
968         return;
969     }
970
971     towrite =
972         ((tovers ==
973           1) ? sizeof(struct vlheader_1) : sizeof(struct vlheader_2));
974     w = write(new, newheader, towrite);
975     if (w != towrite) {
976         printf("Write of entry failed %d; error %u\n", w, errno);
977         exit(1);
978     }
979
980     return;
981 }
982
983
984 #include "AFS_component_version_number.c"
985
986 main(argc, argv)
987      int argc;
988      char **argv;
989 {
990     register struct cmd_syndesc *ts;
991     afs_int32 code;
992
993     ts = cmd_CreateSyntax("initcmd", handleit, NULL, "optional");
994     cmd_AddParm(ts, "-to", CMD_SINGLE, CMD_OPTIONAL, "goal version");
995     cmd_AddParm(ts, "-from", CMD_SINGLE, CMD_OPTIONAL, "current version");
996     cmd_AddParm(ts, "-path", CMD_SINGLE, CMD_OPTIONAL, "pathname");
997     cmd_AddParm(ts, "-showversion", CMD_FLAG, CMD_OPTIONAL,
998                 "Just display version of current vldb");
999     cmd_AddParm(ts, "-dumpvldb", CMD_FLAG, CMD_OPTIONAL,
1000                 "display all vldb entries");
1001
1002 #ifdef DEBUG
1003     cmd_AddParm(ts, "-noGC", CMD_FLAG, CMD_OPTIONAL,
1004                 "Don't do garbage collection");
1005 #endif /* DEBUG */
1006
1007     dbPath = AFSDIR_SERVER_VLDB_FILEPATH;
1008
1009     code = cmd_Dispatch(argc, argv);
1010     exit(code);
1011 }