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