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