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