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