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