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