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