210471eb64cca3f56f94aeaf375216d862f71846
[openafs.git] / src / bucoord / restore.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 /*
11  * ALL RIGHTS RESERVED
12  */
13
14 #include <afsconfig.h>
15 #include <afs/param.h>
16
17
18 #include <afs/stds.h>
19 #include <sys/types.h>
20 #include <afs/cmd.h>
21 #ifdef AFS_NT40_ENV
22 #include <winsock2.h>
23 #else
24 #include <sys/socket.h>
25 #include <netinet/in.h>
26 #include <netdb.h>
27 #include <sys/time.h>
28 #endif
29 #include <lwp.h>
30 #include <afs/bubasics.h>
31 #include "bc.h"
32 #include <afs/com_err.h>
33 #include <afs/butc.h>
34 #include <afs/budb.h>
35 #include <afs/vlserver.h>
36 #include "error_macros.h"
37 #include "bucoord_internal.h"
38 #include "bucoord_prototypes.h"
39
40 extern struct bc_dumpTask bc_dumpTasks[BC_MAXSIMDUMPS];
41 extern char *whoami;
42
43 #define MAXTAPESATONCE      10
44
45 #define HOSTADDR(sockaddr) (sockaddr)->sin_addr.s_addr
46
47 /* local structure to keep track of volumes and the dumps from which
48  * they should be restored 
49  */
50 struct dumpinfo {
51     struct dumpinfo *next;
52     struct volinfo *volinfolist;
53     struct volinfo *lastinlist;
54     afs_int32 DumpId;
55     afs_int32 initialDumpId;
56     afs_int32 parentDumpId;
57     afs_int32 level;
58 };
59
60 struct volinfo {
61     struct volinfo *next;
62     char *volname;
63     afs_int32 server;
64     afs_int32 partition;
65 };
66
67 /* local structure used to keep track of a tape, including a list of
68  * volumes to restore from that tape (bc_tapeItem)
69  */
70 struct bc_tapeList {
71     struct bc_tapeList *next;   /* next guy in list */
72     char *tapeName;             /* name of the tape */
73     afs_int32 dumpID;           /* dump located on this tape */
74     afs_int32 initialDumpID;    /* initial dump located on this tape */
75     afs_int32 tapeNumber;       /* which tape in the dump */
76     struct bc_tapeItem *restoreList;    /* volumes to restore from this tape */
77 };
78
79 /* each tape has a list of volumes to restore; hangs off of a struct
80    bc_tapeList.  Kept sorted so that we can read the tape once to do
81    everything we need to do.  */
82 struct bc_tapeItem {
83     struct bc_tapeItem *next;
84     char *volumeName;           /* volume to restore */
85     afs_int32 position;         /* file slot on this tape */
86     afs_int32 oid;              /* id of the volume */
87     afs_int32 dumplevel;        /* level # of the containing dump (0 == top level) */
88     afs_int32 vollevel;         /* level # of the containing volume (0 == full dump) */
89     afs_uint32 server;
90     afs_int32 partition;
91     int lastdump;               /* The last incremental to restore */
92 };
93
94 /* strip .backup from the end of a name */
95 static int
96 StripBackup(char *aname)
97 {
98     int j;
99
100     if ((j = BackupName(aname))) {
101         aname[j] = 0;
102         return 0;
103     }
104     return -1;
105 }
106
107 int
108 BackupName(char *aname)
109 {
110     int j;
111
112     j = strlen(aname);
113     if ((j > 7) && (strcmp(".backup", aname + j - 7) == 0))
114         return (j - 7);
115     if ((j > 9) && (strcmp(".readonly", aname + j - 9) == 0))
116         return (j - 9);
117     return 0;
118 }
119
120 int
121 extractTapeSeq(char *tapename)
122 {
123     char *sptr;
124
125     sptr = strrchr(tapename, '.');
126     if (!sptr)
127         return (-1);
128     sptr++;
129     return (atol(sptr));
130 }
131
132 void
133 viceName(long value)
134 {
135     char *alph;
136     int r;
137
138     alph = "abcdefjhijklmnopqrstuvwxyz";
139
140     if (value > 25)
141         viceName(value / 26 - 1);
142
143     r = value % 26;
144     printf("%c", alph[r]);
145 }
146
147 /* bc_DoRestore
148  * entry:
149  *      aindex - index into bc_dumpTasks that describes this dump.
150  */
151 int
152 bc_Restorer(afs_int32 aindex)
153 {
154     struct bc_dumpTask *dumpTaskPtr;
155
156     char vname[BU_MAXNAMELEN];
157     struct budb_dumpEntry *dumpDescr, dumpDescr1, dumpDescr2;
158     struct budb_volumeEntry *volumeEntries;
159     struct bc_volumeDump *tvol;
160     afs_int32 code = 0, tcode;
161     afs_int32 tapedumpid, parent;
162
163     afs_int32 nentries = 0;
164     afs_int32 last = 0;
165     afs_int32 next, ve, vecount;
166     struct bc_tapeItem *ti, *pti, *nti;
167     struct bc_tapeList *tapeList = (struct bc_tapeList *)0;
168     struct bc_tapeList *tle, *ptle, *ntle;
169     afs_int32 tlid;
170     afs_int32 tapeid, tid;
171
172     struct tc_restoreArray rpcArray;    /* the rpc structure we use */
173     struct tc_restoreDesc *tcarray = (struct tc_restoreDesc *)0;
174
175     afs_int32 i, startentry, todump;
176     afs_int32 port, nextport;
177     afs_int32 level, tapeseq;
178     afs_int32 foundvolume, voldumplevel;
179     struct rx_connection *aconn = (struct rx_connection *)0;
180     statusP statusPtr, newStatusPtr;
181
182     struct dumpinfo *dumpinfolist = NULL;
183     struct dumpinfo *pdi, *ndi, *di, *dlevels;
184     struct volinfo *nvi, *vi;
185     afs_int32 lvl, lv;
186     int num_dlevels = 20;
187
188     afs_int32 serverAll;        /* The server to which all volumes are to be restore to */
189     afs_int32 partitionAll;     /* Likewise for partition */
190     struct hostent *hostPtr;
191     long haddr;
192     time_t did;
193     int foundtape, c;
194
195     dlevels = (struct dumpinfo *) malloc(num_dlevels * sizeof(*dlevels));
196
197     dumpTaskPtr = &bc_dumpTasks[aindex];
198     serverAll = HOSTADDR(&dumpTaskPtr->destServer);
199     partitionAll = dumpTaskPtr->destPartition;
200
201     volumeEntries = (struct budb_volumeEntry *)
202         malloc(MAXTAPESATONCE * sizeof(struct budb_volumeEntry));
203     if (!volumeEntries) {
204         afs_com_err(whoami, BC_NOMEM, NULL);
205         ERROR(BC_NOMEM);
206     }
207
208     /* For each volume to restore, find which dump it's most recent full or 
209      * incremental is on and thread onto our dump list (from oldest to newest
210      * dump). Also hang the volume off of the dump (no particular order).
211      */
212     for (tvol = dumpTaskPtr->volumes; tvol; tvol = tvol->next) {        /*tvol */
213         strcpy(vname, tvol->name);
214         dumpDescr = &dumpDescr1;
215         if (dumpTaskPtr->parentDumpID > 0) /* Told which dump to try */
216           {
217             /* Right now, this assumes that all volumes listed will be
218              * from the given dumpID.  FIXME
219              */
220             code = bcdb_FindDumpByID(dumpTaskPtr->parentDumpID, dumpDescr);
221             if (code)
222               {
223                 afs_com_err(whoami, code, "Couldn't look up info for dump %d\n",
224                         dumpTaskPtr->parentDumpID);
225                 continue;
226               }
227             code = bcdb_FindVolumes(dumpTaskPtr->parentDumpID, vname, volumeEntries,
228                                     last, &next, MAXTAPESATONCE, &vecount);
229             if (code)
230               {
231                 if (!BackupName(vname))
232                   {
233                     strcat(vname, ".backup");
234                     code = bcdb_FindVolumes(dumpTaskPtr->parentDumpID, vname, volumeEntries,
235                                             last, &next, MAXTAPESATONCE, &vecount);
236                   }
237               }
238           }
239         else
240           {
241             code = bcdb_FindDump(vname, dumpTaskPtr->fromDate, dumpDescr);
242             if (!BackupName(vname)) {   /* See if backup volume is there */
243               strcat(vname, ".backup");
244               dumpDescr = &dumpDescr2;
245               tcode = code;
246               code = bcdb_FindDump(vname, dumpTaskPtr->fromDate, dumpDescr);
247
248               if (code) {       /* Can't find backup, go with first results */
249                 strcpy(vname, tvol->name);
250                 dumpDescr = &dumpDescr1;
251                 code = tcode;
252               } else if (!tcode) {      /* Both found an entry, go with latest result */
253                 if (dumpDescr1.created > dumpDescr2.created) {
254                   strcpy(vname, tvol->name);
255                   dumpDescr = &dumpDescr1;
256                   code = tcode;
257                 }
258               }
259             }
260           }
261
262         if (code) {             /* If FindDump took an error */
263             afs_com_err(whoami, code, "; Can't find any dump for volume %s",
264                     tvol->name);
265             continue;
266         }
267
268         /* look to see if this dump has already been found */
269         for (pdi = 0, di = dumpinfolist; di; pdi = di, di = di->next) {
270             if (di->DumpId < dumpDescr->id) {
271                 di = 0;
272                 break;
273             } else if (di->DumpId == dumpDescr->id) {
274                 break;
275             }
276         }
277
278         /* If didn't find it, create one and thread into list */
279         if (!di) {
280             di = (struct dumpinfo *)malloc(sizeof(struct dumpinfo));
281             if (!di) {
282                 afs_com_err(whoami, BC_NOMEM, NULL);
283                 ERROR(BC_NOMEM);
284             }
285             memset(di, 0, sizeof(struct dumpinfo));
286
287             di->DumpId = dumpDescr->id;
288             di->initialDumpId = dumpDescr->initialDumpID;
289             di->parentDumpId = dumpDescr->parent;
290             di->level = dumpDescr->level;
291
292             if (!pdi) {
293                 di->next = dumpinfolist;
294                 dumpinfolist = di;
295             } else {
296                 di->next = pdi->next;
297                 pdi->next = di;
298             }
299         }
300
301         /* Create one and thread into list */
302         vi = (struct volinfo *)malloc(sizeof(struct volinfo));
303         if (!vi) {
304             afs_com_err(whoami, BC_NOMEM, NULL);
305             ERROR(BC_NOMEM);
306         }
307         memset(vi, 0, sizeof(struct volinfo));
308
309         vi->volname = (char *)malloc(strlen(vname) + 1);
310         if (!vi->volname) {
311             free(vi);
312             afs_com_err(whoami, BC_NOMEM, NULL);
313             ERROR(BC_NOMEM);
314         }
315
316         strcpy(vi->volname, vname);
317         if (serverAll) {
318             vi->server = serverAll;
319             vi->partition = partitionAll;
320         } else {
321             vi->server = HOSTADDR(&tvol->server);
322             vi->partition = tvol->partition;
323         }
324
325         /* thread onto end of list */
326         if (!di->lastinlist)
327             di->volinfolist = vi;
328         else
329             di->lastinlist->next = vi;
330         di->lastinlist = vi;
331     }                           /*tvol */
332
333     /* For each of the above dumps we found (they could be increments), find 
334      * the dump's lineage (up to the full dump). 
335      */
336     for (di = dumpinfolist; di; di = di->next) {
337         /* Find each of the parent dumps */
338         memcpy(&dlevels[0], di, sizeof(struct dumpinfo));
339         for (lvl = 1, parent = dlevels[0].parentDumpId; parent;
340              parent = dlevels[lvl].parentDumpId, lvl++) {
341             if (lvl >= num_dlevels) {           /* running out of dump levels */
342                 struct dumpinfo *tdl = dlevels;
343
344                 num_dlevels += num_dlevels;     /* double */
345                 dlevels = (struct dumpinfo *) malloc(num_dlevels * sizeof(*dlevels));
346                 memcpy(dlevels, tdl, (num_dlevels/2) * sizeof(*dlevels));
347                 free(tdl);
348             }
349             code = bcdb_FindDumpByID(parent, &dumpDescr1);
350             if (code) {
351                 for (vi = di->volinfolist; vi; vi = vi->next) {
352                     afs_com_err(whoami, code,
353                             "; Can't find parent DumpID %u for volume %s",
354                             parent, vi->volname);
355                 }
356                 break;
357             }
358
359             dlevels[lvl].DumpId = dumpDescr1.id;
360             dlevels[lvl].initialDumpId = dumpDescr1.initialDumpID;
361             dlevels[lvl].parentDumpId = dumpDescr1.parent;
362             dlevels[lvl].level = dumpDescr1.level;
363         }
364
365         /* For each of the volumes that has a dump in this lineage (vi),
366          * find where it is in each dump level (lv) starting at level 0 and
367          * going to higest. Each dump level could contain one or more
368          * fragments (vecount) of the volume (volume fragments span tapes). 
369          * Each volume fragment is sorted by tapeid, tape sequence, and tape 
370          * position.
371          */
372         for (vi = di->volinfolist; vi; vi = vi->next) {
373             tle = tapeList;     /* Use for searching the tapelist */
374             ptle = 0;           /* The previous tape list entry */
375             tlid = 0;           /* tapeid of first entry */
376             /* Volume's dump-level. May not be same as dump's dump-level. This
377              * value gets incremented everytime the volume is found on a dump.
378              */
379             voldumplevel = 0;
380
381             /* Do from level 0 dump to highest level dump */
382             for (lv = (lvl - 1); lv >= 0; lv--) {
383                 foundvolume = 0;        /* If found volume on this dump */
384                 tapeid =
385                     (dlevels[lv].initialDumpId ? dlevels[lv].
386                      initialDumpId : dlevels[lv].DumpId);
387
388                 /* Cycle through the volume fragments for this volume on the tape */
389                 for (last = 0, c = 0; last >= 0; last = next, c++) {
390                     code =
391                         bcdb_FindVolumes(dlevels[lv].DumpId, vi->volname,
392                                          volumeEntries, last, &next,
393                                          MAXTAPESATONCE, &vecount);
394                     if (code) {
395                         /* Volume wasn't found on the tape - so continue.
396                          * This can happen when volume doesn't exist during level 0 dump
397                          * but exists for incremental dump.
398                          */
399                         if (code == BUDB_ENDOFLIST) {
400                             code = 0;
401                             break;
402                         }
403
404                         afs_com_err(whoami, code,
405                                 "; Can't find volume %s in DumpID %u",
406                                 vi->volname, dlevels[lv].DumpId);
407                         ERROR(code);
408                     }
409
410                     /* If we have made the Findvolumes call more than once for the same
411                      * volume, then begin the search at the top. This sort assumes that
412                      * we are collecting information from lowest level dump (full dump)
413                      * to the highest level dump (highest incremental)
414                      * and from first fragment to last fragment at each level. If
415                      * there is more than one call to FindVolumes, this is not true
416                      * anymore.
417                      */
418                     if (c) {
419                         tle = tapeList;
420                         ptle = 0;
421                         tlid = 0;
422                     }
423
424                     /* For each of the volume fragments that we read, sort them into
425                      * the list of tapes they are on. Sort by tapeid, then tape sequence.
426                      * Do from first fragment (where volume begins) to last fragment.
427                      */
428                     for (ve = (vecount - 1); ve >= 0; ve--) {
429                         foundvolume = 1;        /* Found the volume on this dump */
430                         foundtape = 0;
431                         tapeseq = volumeEntries[ve].tapeSeq;
432
433                         /* Determine where in the list of tapes this should go */
434                         for (; tle; ptle = tle, tle = tle->next) {
435                             tid =
436                                 (tle->initialDumpID ? tle->
437                                  initialDumpID : tle->dumpID);
438
439                             /* Sort by tapeids. BUT, we don't want add an entry in the middle 
440                              * of a dumpset (might split a volume fragmented across tapes). 
441                              * So make sure we step to next dumpset. tlid is the tapeid of 
442                              * the last tape we added a volume to. This can happen when an
443                              * incremental was appended to a tape created prior its parent-
444                              * dump's tape, and needs to be restored after it.
445                              */
446                             if (tapeid < tid) {
447                                 if (tlid != tid) {
448                                     tle = 0;
449                                     break;
450                                 }       /* Allocate and insert a tape entry */
451                             }
452
453                             /* Found the tapeid (the dumpset). Check if its the correct 
454                              * tape sequence
455                              */
456                             else if (tapeid == tid) {
457                                 if (tapeseq < tle->tapeNumber) {
458                                     tle = 0;
459                                     break;
460                                 }
461                                 /* Allocate and insert a tape entry */
462                                 if (tapeseq == tle->tapeNumber) {
463                                     break;
464                                 }
465                                 /* Don't allocate tape entry */
466                                 foundtape = 1;  /* Found dumpset but not the tape */
467                             }
468
469                             /* Prevously found the tapeid (the dumpset) but this tape 
470                              * sequence not included (the tapeid has changed). So add 
471                              * this tape entry to the end of its dumpset.
472                              */
473                             else if (foundtape) {
474                                 tle = 0;
475                                 break;
476                             }
477                             /* Allocate and insert a tape entry */
478                         }
479
480                         /* Allocate a new tapelist entry if one not found */
481                         if (!tle) {
482                             tle = (struct bc_tapeList *)
483                                 malloc(sizeof(struct bc_tapeList));
484                             if (!tle) {
485                                 afs_com_err(whoami, BC_NOMEM, NULL);
486                                 return (BC_NOMEM);
487                             }
488                             memset(tle, 0, sizeof(struct bc_tapeList));
489
490                             tle->tapeName =
491                                 (char *)malloc(strlen(volumeEntries[ve].tape)
492                                                + 1);
493                             if (!tle->tapeName) {
494                                 free(tle);
495                                 afs_com_err(whoami, BC_NOMEM, NULL);
496                                 return (BC_NOMEM);
497                             }
498
499                             strcpy(tle->tapeName, volumeEntries[ve].tape);
500                             tle->dumpID = dlevels[lv].DumpId;
501                             tle->initialDumpID = dlevels[lv].initialDumpId;
502                             tle->tapeNumber = tapeseq;
503
504                             /* Now thread the tape onto the list */
505                             if (!ptle) {
506                                 tle->next = tapeList;
507                                 tapeList = tle;
508                             } else {
509                                 tle->next = ptle->next;
510                                 ptle->next = tle;
511                             }
512                         }
513                         tlid =
514                             (tle->initialDumpID ? tle->initialDumpID : tle->
515                              dumpID);
516
517                         /* Now place the volume fragment into the correct position on 
518                          * this tapelist entry. Duplicate entries are ignored.
519                          */
520                         for (pti = 0, ti = tle->restoreList; ti;
521                              pti = ti, ti = ti->next) {
522                             if (volumeEntries[ve].position < ti->position) {
523                                 ti = 0;
524                                 break;
525                             }
526                             /* Allocate an entry */
527                             else if (volumeEntries[ve].position ==
528                                      ti->position) {
529                                 break;
530                             }   /* Duplicate entry */
531                         }
532
533                         /* If didn't find one, allocate and thread onto list.
534                          * Remember the server and partition.
535                          */
536                         if (!ti) {
537                             ti = (struct bc_tapeItem *)
538                                 malloc(sizeof(struct bc_tapeItem));
539                             if (!ti) {
540                                 afs_com_err(whoami, BC_NOMEM, NULL);
541                                 return (BC_NOMEM);
542                             }
543                             memset(ti, 0, sizeof(struct bc_tapeItem));
544
545                             ti->volumeName =
546                                 (char *)malloc(strlen(volumeEntries[ve].name)
547                                                + 1);
548                             if (!ti->volumeName) {
549                                 free(ti);
550                                 afs_com_err(whoami, BC_NOMEM, NULL);
551                                 return (BC_NOMEM);
552                             }
553
554                             strcpy(ti->volumeName, volumeEntries[ve].name);
555                             ti->server = vi->server;
556                             ti->partition = vi->partition;
557                             ti->oid = volumeEntries[ve].id;
558                             ti->position = volumeEntries[ve].position;
559                             ti->vollevel = voldumplevel;
560                             ti->dumplevel = dlevels[lv].level;
561                             ti->lastdump = ((lv == 0) ? 1 : 0);
562
563                             if (!pti) {
564                                 ti->next = tle->restoreList;
565                                 tle->restoreList = ti;
566                             } else {
567                                 ti->next = pti->next;
568                                 pti->next = ti;
569                             }
570
571                             nentries++;
572                         }       /* !ti */
573                     }           /* ve: for each returned volume fragment by bcdb_FindVolumes() */
574                 }               /* last: Multiple calls to bcdb_FindVolumes() */
575
576                 if (foundvolume)
577                     voldumplevel++;
578             }                   /* lv: For each dump level */
579         }                       /* vi: For each volume to search for in the dump hierarchy */
580     }                           /* di: For each dump */
581
582     if (!nentries) {
583         afs_com_err(whoami, 0, "No volumes to restore");
584         ERROR(0);
585     }
586
587     if (dumpTaskPtr->dontExecute) {
588         for (tle = tapeList; tle; tle = tle->next) {
589             for (ti = tle->restoreList; ti; ti = ti->next) {
590                 tapedumpid =
591                     (tle->initialDumpID ? tle->initialDumpID : tle->dumpID);
592                 strcpy(vname, ti->volumeName);
593                 StripBackup(vname);
594                 if (dumpTaskPtr->newExt)
595                     strcat(vname, dumpTaskPtr->newExt);
596
597                 /* print volume to restore and the tape and position its on */
598                 if (serverAll) {
599                     printf
600                         ("Restore volume %s (ID %d) from tape %s (%u), position %d",
601                          ti->volumeName, ti->oid, tle->tapeName, tapedumpid,
602                          ti->position);
603
604                     if (strcmp(ti->volumeName, vname) != 0)
605                         printf(" as %s", vname);
606
607                     printf(".\n");
608                 }
609
610                 /* print in format recognized by volsetrestore */
611                 else {
612                     haddr = ti->server;
613                     hostPtr =
614                         gethostbyaddr((char *)&haddr, sizeof(haddr), AF_INET);
615                     if (hostPtr)
616                         printf("%s", hostPtr->h_name);
617                     else
618                         printf("%u", ti->server);
619
620                     printf(" /vicep");
621                     viceName(ti->partition);
622
623                     printf(" %s # as %s; %s (%u); pos %d;", ti->volumeName,
624                            vname, tle->tapeName, tapedumpid, ti->position);
625
626                     did = tle->dumpID;
627                     printf(" %s", ctime(&did));
628                 }
629             }
630         }
631
632         ERROR(0);
633     }
634
635     /* Allocate a list of volumes to restore */
636     tcarray =
637         (struct tc_restoreDesc *)malloc(nentries *
638                                         sizeof(struct tc_restoreDesc));
639     if (!tcarray) {
640         afs_com_err(whoami, BC_NOMEM, NULL);
641         ERROR(BC_NOMEM);
642     }
643     memset(tcarray, 0, nentries * sizeof(struct tc_restoreDesc));
644
645     /* Fill in the array with the list above */
646     i = 0;
647     for (tle = tapeList; tle; tle = tle->next) {
648         for (ti = tle->restoreList; ti; ti = ti->next) {
649             tcarray[i].origVid = ti->oid;       /* means unknown */
650             tcarray[i].vid = 0;
651             tcarray[i].flags = 0;
652             if (ti->vollevel == 0)
653                 tcarray[i].flags |= RDFLAG_FIRSTDUMP;
654             if (ti->lastdump)
655                 tcarray[i].flags |= RDFLAG_LASTDUMP;
656             tcarray[i].position = ti->position;
657             tcarray[i].dbDumpId = tle->dumpID;
658             tcarray[i].initialDumpId = tle->initialDumpID;
659             tcarray[i].hostAddr = ti->server;   /* just the internet address */
660             tcarray[i].partition = ti->partition;
661
662             strcpy(tcarray[i].tapeName, tle->tapeName);
663             strcpy(tcarray[i].oldName, ti->volumeName);
664             strcpy(tcarray[i].newName, ti->volumeName);
665             StripBackup(tcarray[i].newName);
666             if (dumpTaskPtr->newExt)
667                 strcat(tcarray[i].newName, dumpTaskPtr->newExt);
668
669             tcarray[i].dumpLevel = ti->dumplevel;
670             i++;
671         }
672     }
673
674     printf("Starting restore\n");
675
676     /* Loop until all of the tape entries are used */
677     for (startentry = 0; startentry < nentries; startentry += todump) {
678         /* Get all the next tape entries with the same port offset */
679         if (dumpTaskPtr->portCount == 0) {
680             port = 0;
681             todump = nentries - startentry;
682         } else if (dumpTaskPtr->portCount == 1) {
683             port = dumpTaskPtr->portOffset[0];
684             todump = nentries - startentry;
685         } else {
686             level = tcarray[startentry].dumpLevel;
687             port =
688                 dumpTaskPtr->
689                 portOffset[(level <=
690                             dumpTaskPtr->portCount -
691                             1 ? level : dumpTaskPtr->portCount - 1)];
692
693             for (todump = 1; ((startentry + todump) < nentries); todump++) {
694                 level = tcarray[startentry + todump].dumpLevel;
695                 nextport =
696                     dumpTaskPtr->
697                     portOffset[(level <=
698                                 dumpTaskPtr->portCount -
699                                 1 ? level : dumpTaskPtr->portCount - 1)];
700                 if (port != nextport)
701                     break;      /* break if we change ports */
702             }
703         }
704
705         rpcArray.tc_restoreArray_len = todump;
706         rpcArray.tc_restoreArray_val = &tcarray[startentry];
707
708         code = ConnectButc(dumpTaskPtr->config, port, &aconn);
709         if (code)
710             return (code);
711
712         if (tcarray[startentry].dumpLevel == 0)
713             printf("\nFull restore being processed on port %d\n", port);
714         else
715             printf("\nIncremental restore being processed on port %d\n",
716                    port);
717
718         code =
719             TC_PerformRestore(aconn, "DumpSetName", &rpcArray,
720                               &dumpTaskPtr->dumpID);
721         if (code) {
722             afs_com_err(whoami, code, "; Failed to start restore");
723             break;
724         }
725
726         /* setup status monitor node */
727         statusPtr = createStatusNode();
728         lock_Status();
729         statusPtr->port = port;
730         statusPtr->taskId = dumpTaskPtr->dumpID;
731         statusPtr->jobNumber = bc_jobNumber();
732         statusPtr->flags &= ~STARTING;  /* clearStatus to examine */
733         if (tcarray[startentry].dumpLevel == 0)
734             sprintf(statusPtr->taskName, "Full Restore");
735         else
736             sprintf(statusPtr->taskName, "Incremental Restore");
737         unlock_Status();
738
739         /* Wait until this restore pass is complete before starting the next
740          * pass. Query the statusWatcher for the status
741          */
742         while (1) {
743             lock_Status();
744             newStatusPtr = findStatus(dumpTaskPtr->dumpID);
745             if (!newStatusPtr
746                 || (newStatusPtr->
747                     flags & (ABORT_REQUEST | ABORT_SENT | ABORT_DONE |
748                              TASK_ERROR))) {
749                 unlock_Status();
750                 ERROR(0);
751             } else if (newStatusPtr->flags & (TASK_DONE)) {
752                 unlock_Status();
753                 break;
754             }
755
756             unlock_Status();
757             IOMGR_Sleep(2);
758         }
759
760         if (aconn)
761             rx_DestroyConnection(aconn);
762         aconn = (struct rx_connection *)0;
763     }                           /* while */
764
765     /* free up everything */
766   error_exit:
767     if (aconn)
768         rx_DestroyConnection(aconn);
769
770     if (tcarray)
771         free(tcarray);
772
773     /* Free the dumpinfo list and its volumes */
774     for (di = dumpinfolist; di; di = ndi) {
775         for (vi = di->volinfolist; vi; vi = nvi) {
776             if (vi->volname)
777                 free(vi->volname);
778             nvi = vi->next;
779             free(vi);
780         }
781         ndi = di->next;
782         free(di);
783     }
784
785     /* Free the tape lists and items */
786     for (tle = tapeList; tle; tle = ntle) {
787         for (ti = tle->restoreList; ti; ti = nti) {
788             nti = ti->next;
789             if (ti->volumeName)
790                 free(ti->volumeName);
791             free(ti);
792         }
793         ntle = tle->next;
794         if (tle->tapeName)
795             free(tle->tapeName);
796         free(tle);
797     }
798
799     /* free local-like things we alloacted to save stack space */
800     if (volumeEntries)
801         free(volumeEntries);
802
803     free(dlevels);
804
805     return code;
806 }