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