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