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