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