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