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