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