a99d0be23d7520f35d2f5d60dc86d782a04079e8
[openafs.git] / src / butc / read_tape.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 #include <afsconfig.h>
11 #include <afs/param.h>
12
13 RCSID
14     ("$Header$");
15
16 #include <afs/cmd.h>
17 #include <lock.h>
18 #include <afs/tcdata.h>
19
20 #include <sys/types.h>
21 #include <netinet/in.h>
22 #include <fcntl.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include <errno.h>
26
27 #include <afs/usd.h>
28
29 usd_handle_t fd;
30 usd_handle_t ofd;
31 int ofdIsOpen = 0;
32 afs_int32 nrestore, nskip;
33 int ask, printlabels, printheaders, verbose;
34 char filename[100];
35 char *outfile;
36
37 #define TAPE_MAGIC  1100000009  /* Defined in file_tm.c */
38 #define BLOCK_MAGIC 1100000005
39 #define FILE_MAGIC  1000000007
40 #define FILE_BEGIN     0
41 #define FILE_END       1
42 #define FILE_EOD      -1
43
44 struct tapeLabel {              /* also in file_tm.c */
45     afs_int32 magic;
46     struct butm_tapeLabel label;
47 };
48 struct fileMark {               /* also in file_tm.c */
49     afs_int32 magic;
50     afs_uint32 nBytes;
51 };
52
53 /* Read a tape block of size 16K */
54 afs_int32
55 readblock(buffer)
56      char *buffer;
57 {
58     static rerror = 0;
59     u_int nread, total = 0;
60     int rc, fmcount = 0;
61
62     while (total < BUTM_BLOCKSIZE) {
63         rc = USD_READ(fd, buffer + total, BUTM_BLOCKSIZE - total, &nread);
64         if (rc != 0) {
65             return (rc);
66         } else if ((nread == 0) && (total == 0)) {
67             if (verbose)
68                 fprintf(stderr, "Hardware file mark\n");
69             if (++fmcount > 3) {
70                 if (verbose)
71                     fprintf(stderr,
72                             "Greater than 3 hardware file marks in a row - done\n");
73                 return -1;
74             }
75         } else if (nread == 0) {
76             fprintf(stderr, "Reached unexpected end of dump file\n");
77             return -1;
78         } else {
79             total += nread;
80         }
81     }
82     return 0;
83 }
84
85 printLabel(tapeLabelPtr)
86      struct tapeLabel *tapeLabelPtr;
87 {
88     tapeLabelPtr->label.dumpid = ntohl(tapeLabelPtr->label.dumpid);
89     tapeLabelPtr->label.creationTime =
90         ntohl(tapeLabelPtr->label.creationTime);
91     tapeLabelPtr->label.expirationDate =
92         ntohl(tapeLabelPtr->label.expirationDate);
93     tapeLabelPtr->label.structVersion =
94         ntohl(tapeLabelPtr->label.structVersion);
95     tapeLabelPtr->label.useCount = ntohl(tapeLabelPtr->label.useCount);
96     tapeLabelPtr->label.size = ntohl(tapeLabelPtr->label.size);
97
98     fprintf(stderr, "\nDUMP       %u\n", tapeLabelPtr->label.dumpid);
99     if (printlabels) {
100         fprintf(stderr, "   AFS Tape  Name   : %s\n",
101                 tapeLabelPtr->label.AFSName);
102         fprintf(stderr, "   Permanent Name   : %s\n",
103                 tapeLabelPtr->label.pName);
104         fprintf(stderr, "   Dump Id          : %u\n",
105                 tapeLabelPtr->label.dumpid);
106         fprintf(stderr, "   Dump Id Time     : %.24s\n",
107                 ctime(&(tapeLabelPtr->label.dumpid)));
108         fprintf(stderr, "   Date Created     : %.24s\n",
109                 ctime(&(tapeLabelPtr->label.creationTime)));
110         fprintf(stderr, "   Date Expires     : %.24s\n",
111                 ctime(&(tapeLabelPtr->label.expirationDate)));
112         fprintf(stderr, "   Version Number   : %d\n",
113                 tapeLabelPtr->label.structVersion);
114         fprintf(stderr, "   Tape Use Count   : %d\n",
115                 tapeLabelPtr->label.useCount);
116         fprintf(stderr, "   Tape Size        : %u\n",
117                 tapeLabelPtr->label.size);
118         fprintf(stderr, "   Comment          : %s\n",
119                 tapeLabelPtr->label.comment);
120         fprintf(stderr, "   Dump Path        : %s\n",
121                 tapeLabelPtr->label.dumpPath);
122         fprintf(stderr, "   Cell Name        : %s\n",
123                 tapeLabelPtr->label.cell);
124         fprintf(stderr, "   Creator Name     : %s\n",
125                 tapeLabelPtr->label.creator.name);
126         fprintf(stderr, "   Creator Instance : %s\n",
127                 tapeLabelPtr->label.creator.instance);
128         fprintf(stderr, "   Creator Cell     : %s\n",
129                 tapeLabelPtr->label.creator.cell);
130     }
131 }
132
133 printHeader(headerPtr, isvolheader)
134      struct volumeHeader *headerPtr;
135      afs_int32 *isvolheader;
136 {
137     static volcount = 0;
138
139     *isvolheader = 0;
140     headerPtr->volumeID = ntohl(headerPtr->volumeID);
141     headerPtr->server = ntohl(headerPtr->server);
142     headerPtr->part = ntohl(headerPtr->part);
143     headerPtr->from = ntohl(headerPtr->from);
144     headerPtr->frag = ntohl(headerPtr->frag);
145     headerPtr->magic = ntohl(headerPtr->magic);
146     headerPtr->contd = ntohl(headerPtr->contd);
147     headerPtr->dumpID = ntohl(headerPtr->dumpID);
148     headerPtr->level = ntohl(headerPtr->level);
149     headerPtr->parentID = ntohl(headerPtr->parentID);
150     headerPtr->endTime = ntohl(headerPtr->endTime);
151     headerPtr->versionflags = ntohl(headerPtr->versionflags);
152     headerPtr->cloneDate = ntohl(headerPtr->cloneDate);
153
154     if (headerPtr->magic == TC_VOLBEGINMAGIC) {
155         *isvolheader = 1;
156         if (verbose)
157             fprintf(stderr, "Volume header\n");
158         fprintf(stderr,
159                 "VOLUME %3d %s (%u) - %s dump from %.24s till %.24s\n",
160                 ++volcount, headerPtr->volumeName, headerPtr->volumeID,
161                 (headerPtr->level ? "Incr" : "Full"),
162                 ((headerPtr->from) ? (char *)ctime(&headerPtr->from) : "0"),
163                 ctime(&(headerPtr->cloneDate)));
164         if (printheaders) {
165             fprintf(stderr, "   Volume Name    = %s\n",
166                     headerPtr->volumeName);
167             fprintf(stderr, "   Volume ID      = %u\n", headerPtr->volumeID);
168             fprintf(stderr, "   Clone Date     = %.24s\n",
169                     ctime(&headerPtr->cloneDate));
170             fprintf(stderr, "   Vol Fragment   = %d\n", headerPtr->frag);
171             fprintf(stderr, "   Vol Continued  = 0x%x\n", headerPtr->contd);
172             fprintf(stderr, "   DumpSet Name   = %s\n",
173                     headerPtr->dumpSetName);
174             fprintf(stderr, "   Dump ID        = %u\n", headerPtr->dumpID);
175             fprintf(stderr, "   Dump Level     = %d\n", headerPtr->level);
176             fprintf(stderr, "   Dump Since     = %.24s\n",
177                     ctime(&headerPtr->from));
178             fprintf(stderr, "   parent Dump ID = %u\n", headerPtr->parentID);
179         }
180     } else if (headerPtr->magic == TC_VOLENDMAGIC) {
181         if (verbose)
182             fprintf(stderr, "Volume Trailer\n");
183     } else {
184         fprintf(stderr, "Unrecognized Volume Header/Trailer\n");
185     }
186 }
187
188 int
189 openOutFile(headerPtr)
190      struct volumeHeader *headerPtr;
191 {
192     afs_int32 len;
193     int ch;
194     int rc;
195     int oflag;
196     int skip, first;
197     char rest[100];
198     afs_hyper_t size;
199
200     /* If we were asked to skip this volume, then skip it */
201     if (nskip) {
202         nskip--;
203         return 0;
204     }
205     /* Skip if we are not to restore any */
206     if (!nrestore)
207         return 0;
208
209     /* Get the volume name and strip off the BK or RO extension */
210     if (outfile) {
211         strcpy(filename, outfile);
212     } else {
213         strcpy(filename, headerPtr->volumeName);
214         len = strlen(filename);
215         if ((len > 7) && (strcmp(".backup", filename + len - 7) == 0)) {
216             filename[len - 7] = 0;
217         } else if ((len > 9)
218                    && (strcmp(".readonly", filename + len - 9) == 0)) {
219             filename[len - 9] = 0;
220         }
221     }
222
223     if (ask) {
224         first = 1;
225         skip = 0;
226         printf("Press return to retreive volume %s (%u) to file %s; " "s"
227                " to skip\n", headerPtr->volumeName, headerPtr->volumeID,
228                filename);
229         do {
230             ch = getchar();
231             if ((first == 1) && (ch == 's'))
232                 skip = 1;
233             if ((first == 1) && (ch == 'q'))
234                 exit(0);
235             first = 0;
236         } while (ch != '\n');
237         if (skip) {
238             printf("Will not restore volume %s\n", headerPtr->volumeName,
239                    headerPtr->volumeID);
240             return 0;
241         }
242     } else {
243         printf("Retreive volume %s (%u) to file %s\n", headerPtr->volumeName,
244                headerPtr->volumeID, filename);
245     }
246
247     /* Should I append the date onto the end of the name? */
248
249     /* Open the file to write to */
250     if (headerPtr->contd == TC_VOLCONTD) {
251         /* Continuation of dump */
252         oflag = USD_OPEN_RDWR;
253     } else {
254         /* An all new dump */
255         oflag = USD_OPEN_RDWR | USD_OPEN_CREATE;
256     }
257     rc = usd_Open(filename, oflag, 0664, &ofd);
258     if (rc != 0) {
259         fprintf(stderr, "Unable to open file %s. Skipping. Code = %d\n",
260                 filename, rc);
261         nrestore--;
262         return 0;
263     }
264     if (headerPtr->contd != TC_VOLCONTD) {
265         hzero(size);
266         rc = USD_IOCTL(ofd, USD_IOCTL_SETSIZE, &size);
267         if (rc != 0) {
268             fprintf(stderr, "Unable to open file %s. Skipping. Code = %d\n",
269                     filename, rc);
270             USD_CLOSE(ofd);
271             nrestore--;
272             return 0;
273         }
274     }
275     ofdIsOpen = 1;
276     return 0;
277 }
278
279 int
280 writeLastBlocks(char *lastblock, char *lastblock2)
281 {
282     int rc;
283     struct volumeHeader vh;
284     char trailer[12];
285     struct blockMark *bmark, *bmark2;
286     char *data, *data2;
287     int count, count2;
288     int tlen, skip, pos;
289
290     if (!ofdIsOpen)
291         return 0;
292
293     bmark = (struct blockMark *)lastblock;
294     data = &lastblock[sizeof(struct blockMark)];
295     count = ntohl(bmark->count);
296     if (lastblock2) {
297         bmark2 = (struct blockMark *)lastblock2;
298         data2 = &lastblock2[sizeof(struct blockMark)];
299         count2 = ntohl(bmark2->count);
300     } else {
301         count2 = 0;
302     }
303
304     /*
305      * Strip off all but the last twelve bytes of the volume trailer
306      */
307     skip = sizeof(struct volumeHeader) - 12;
308     if (count >= skip) {
309         count = count - skip;
310     } else if (count + count2 >= skip) {
311         count2 = count2 - (skip - count);
312         count = 0;
313     } else {
314         fprintf(stderr, "Failed to strip off volume trailer (1).\n");
315         return 0;
316     }
317
318     /* volume trailer is somewhere in the last 12 bytes of the tape file.
319      * The volume trailer may span tape blocks. */
320     if (count >= 12) {
321         tlen = 0;
322         memcpy(trailer, data + (count - 12), 12);
323     } else {
324         tlen = 12 - count;
325         memcpy(trailer, data2 + (count2 - tlen), tlen);
326         if (count != 0) {
327             memcpy(trailer + tlen, data, count);
328         }
329     }
330
331     for (pos = 0; pos <= 2; pos++) {
332         if (strncmp(&trailer[pos], "H++NAME#", 8) == 0) {
333             break;
334         }
335         if (tlen > 0) {
336             tlen--;
337         }
338     }
339
340     if (pos == 3) {
341         fprintf(stderr, "Failed to strip off volume trailer (2).\n");
342     } else {
343         if (count2 - tlen > 0) {
344             rc = writeData(data2, count2 - tlen);
345         }
346         if ((tlen == 0) && (count > 12 - pos)) {
347             rc = writeData(data, count - (12 - pos));
348         }
349     }
350     return 0;
351 }
352
353 int
354 closeOutFile()
355 {
356     if (!ofdIsOpen)
357         return 0;
358
359     USD_CLOSE(ofd);
360     ofdIsOpen = 0;
361
362     /* Decrement the number of volumes to restore */
363     nrestore--;
364     return 0;
365 }
366
367 int
368 writeData(data, size)
369      char *data;
370      afs_int32 size;
371 {
372     int rc;
373     u_int nwritten;
374
375     if (!ofdIsOpen)
376         return 0;
377     rc = USD_WRITE(ofd, data, (u_int) size, &nwritten);
378     if (rc != 0) {
379         fprintf(stderr, "Unable to write volume data to file. Code = %d\n",
380                 rc);
381     }
382     return 0;
383 }
384
385 WorkerBee(as, arock)
386      struct cmd_syndesc *as;
387      char *arock;
388 {
389     char *tapedev;
390     struct tapeLabel *label;
391     struct fileMark *fmark;
392     afs_int32 fmtype;
393     struct blockMark *bmark;
394     afs_int32 isheader, isdatablock;
395     char *data;
396     char *tblock;
397     afs_int32 code;
398     struct volumeHeader *volheaderPtr;
399     int eod = 1;
400     int rc;
401     char *nextblock;            /* We cycle through three tape blocks so we  */
402     char *lastblock;            /* can trim off the volume trailer from the  */
403     char *lastblock2;           /* end of each volume without having to back */
404     char *tapeblock1;           /* up the output stream.                     */
405     char *tapeblock2;
406     char *tapeblock3;
407
408     tapedev = as->parms[0].items->data; /* -tape     */
409     nrestore =
410         (as->parms[1].items ? atol(as->parms[1].items->data) : 0x7fffffff);
411     nskip = (as->parms[2].items ? atol(as->parms[2].items->data) : 0);
412     if (as->parms[4].items)
413         nskip = 0x7fffffff;     /* -scan     */
414     outfile = (as->parms[3].items ? as->parms[3].items->data : 0);
415     ask = (as->parms[5].items ? 0 : 1); /* -noask    */
416     printlabels = (as->parms[6].items ? 1 : 0); /* -label    */
417     printheaders = (as->parms[7].items ? 1 : 0);        /* -vheaders */
418     verbose = (as->parms[8].items ? 1 : 0);     /* -verbose  */
419
420     /* Open the tape device */
421     rc = usd_Open(tapedev, USD_OPEN_RDONLY, 0, &fd);
422     if (rc != 0) {
423         printf("Failed to open tape device %s. Code = %d\n", tapedev, rc);
424         exit(1);
425     }
426
427     /*
428      * Initialize the tape block buffers
429      */
430     tapeblock1 = (char *)malloc(3 * 16384);
431     if (tapeblock1 == NULL) {
432         printf("Failed to allocate I/O buffers.\n");
433         exit(1);
434     }
435     tapeblock2 = tapeblock1 + 16384;
436     tapeblock3 = tapeblock2 + 16384;
437
438     nextblock = tapeblock1;
439     lastblock = NULL;
440     lastblock2 = NULL;
441
442     /* Read each tape block deciding what to do with it */
443     do {                        /* while ((nskip!=0) && (nrestore!=0)) */
444         code = readblock(nextblock);
445         if (code) {
446             if (!eod)
447                 fprintf(stderr, "Tape device read error: %d\n", code);
448             break;
449         }
450         isdatablock = 0;
451
452         /* A data block can be either a volume header, volume trailer,
453          * or actual data from a dump.
454          */
455         bmark = (struct blockMark *)nextblock;
456         label = (struct tapeLabel *)nextblock;
457         fmark = (struct fileMark *)nextblock;
458         if (ntohl(bmark->magic) == BLOCK_MAGIC) {
459             if (verbose)
460                 printf("Data block\n");
461             isdatablock = 1;
462             isheader = 0;
463             data = &nextblock[sizeof(struct blockMark)];
464             if (strncmp(data, "H++NAME#", 8) == 0) {
465                 volheaderPtr = (struct volumeHeader *)data;
466                 printHeader(volheaderPtr, &isheader);
467             }
468             if (isheader) {
469                 code = openOutFile(volheaderPtr);
470                 nextblock = tapeblock1;
471                 lastblock = NULL;
472                 lastblock2 = NULL;
473             } else {
474                 if (lastblock2 != NULL) {
475                     data = &lastblock2[sizeof(struct blockMark)];
476                     bmark = (struct blockMark *)lastblock2;
477                     code = writeData(data, ntohl(bmark->count));
478                     tblock = lastblock2;
479                 } else if (lastblock != NULL) {
480                     tblock = tapeblock2;
481                 } else {
482                     tblock = tapeblock3;
483                 }
484                 lastblock2 = lastblock;
485                 lastblock = nextblock;
486                 nextblock = tblock;
487             }
488         }
489
490         /* Filemarks come in 3 forms: BEGIN, END, and EOD.
491          * There is no information stored in filemarks.
492          */
493         else if (ntohl(fmark->magic) == FILE_MAGIC) {
494             fmtype = ntohl(fmark->nBytes);
495             if (fmtype == FILE_BEGIN) {
496                 if (verbose)
497                     fprintf(stderr, "File mark volume begin\n");
498             } else if (fmtype == FILE_END) {
499                 if (verbose)
500                     fprintf(stderr, "File mark volume end\n");
501             } else if (fmtype == FILE_EOD) {
502                 if (verbose)
503                     fprintf(stderr, "File mark end-of-dump\n");
504                 eod = 1;
505             }
506         }
507
508         /* A dump label */
509         else if (ntohl(label->magic) == TAPE_MAGIC) {
510             if (verbose)
511                 fprintf(stderr, "Dump label\n");
512             printLabel(label);
513             eod = 0;
514         }
515
516         else {
517             if (verbose) {
518                 fprintf(stderr, "Unrecognized tape block - end\n");
519             }
520         }
521
522         /* Anything other than a data block closes the out file.
523          * At this point nextblock contains the end of tape file mark,
524          * lastblock contains the last data block for the current volume,
525          * and lastblock2 contains the second to last block for the current
526          * volume. If the volume fits in a single block, lastblock2 will
527          * be NULL. Call writeLastBlocks to strip off the dump trailer before
528          * writing the last of the volume data to the dump file. The dump
529          * trailer may span block boundaries.
530          */
531         if (!isdatablock && lastblock) {
532             writeLastBlocks(lastblock, lastblock2);
533             closeOutFile();
534             nextblock = tapeblock1;
535             lastblock = NULL;
536             lastblock2 = NULL;
537         }
538     } while ((nskip != 0) || (nrestore != 0));
539
540     free(tapeblock1);
541
542     return 0;
543 }
544
545 main(argc, argv)
546      int argc;
547      char **argv;
548 {
549     struct cmd_syndesc *ts;
550     struct cmd_item *ti;
551
552     setlinebuf(stdout);
553
554     ts = cmd_CreateSyntax(NULL, WorkerBee, NULL,
555                           "Restore volumes from backup tape");
556     cmd_AddParm(ts, "-tape", CMD_SINGLE, CMD_REQUIRED, "tape device");
557     cmd_AddParm(ts, "-restore", CMD_SINGLE, CMD_OPTIONAL,
558                 "# volumes to restore");
559     cmd_AddParm(ts, "-skip", CMD_SINGLE, CMD_OPTIONAL, "# volumes to skip");
560     cmd_AddParm(ts, "-file", CMD_SINGLE, CMD_OPTIONAL, "filename");
561     cmd_AddParm(ts, "-scan", CMD_FLAG, CMD_OPTIONAL, "Scan the tape");
562     cmd_AddParm(ts, "-noask", CMD_FLAG, CMD_OPTIONAL,
563                 "Prompt for each volume");
564     cmd_AddParm(ts, "-label", CMD_FLAG, CMD_OPTIONAL, "Display dump label");
565     cmd_AddParm(ts, "-vheaders", CMD_FLAG, CMD_OPTIONAL,
566                 "Display volume headers");
567     cmd_AddParm(ts, "-verbose", CMD_FLAG, CMD_OPTIONAL, "verbose");
568
569     return cmd_Dispatch(argc, argv);
570 }