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