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