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