1 /* TAPECOPY.C   (c) Copyright Roger Bowler, 1999-2010                */
2 /*              Convert SCSI tape into AWSTAPE format                */
3 
4 /*              Read from AWSTAPE and write to SCSI tape mods        */
5 /*              Copyright 2005-2009 James R. Maynard III             */
6 
7 /*-------------------------------------------------------------------*/
8 /* This program reads a SCSI tape and produces a disk file with      */
9 /* each block of the tape prefixed by an AWSTAPE block header.       */
10 /* If no disk file name is supplied, then the program simply         */
11 /* prints a summary of the tape files and blocksizes.                */
12 /*-------------------------------------------------------------------*/
13 
14 #include "hstdinc.h"
15 
16 #include "hercules.h"
17 #include "tapedev.h"
18 #include "scsitape.h"
19 
20 /*-------------------------------------------------------------------*/
21 /* (if no SCSI tape support generated, do nothing)                   */
22 /*-------------------------------------------------------------------*/
23 #if !defined(OPTION_SCSI_TAPE)
24 // SYSBLK sysblk;
main(int argc,char * argv[])25 int main (int argc, char *argv[])
26 {
27     UNREFERENCED(argc);
28     UNREFERENCED(argv);
29     printf( _("HHCTC017E SCSI tape not supported with this build\n") );
30     return 0;
31 }
32 #else
33 
34 /*-------------------------------------------------------------------*/
35 /* External GUI flag...                                              */
36 /*-------------------------------------------------------------------*/
37 #if defined(EXTERNALGUI)
38 time_t curr_progress_time = 0;
39 time_t prev_progress_time = 0;
40 #define PROGRESS_INTERVAL_SECS  (   3   )     /* (just what it says) */
41 #endif /*defined(EXTERNALGUI)*/
42 
43 
44 /*-------------------------------------------------------------------*/
45 /* Return Codes...                                                   */
46 /*-------------------------------------------------------------------*/
47 #define  RC_SUCCESS                                ( 0)
48 #define  RC_ERROR_BAD_ARGUMENTS                    ( 1)
49 #define  RC_ERROR_OPENING_SCSI_DEVICE              ( 3)
50 #define  RC_ERROR_OPENING_AWS_FILE                 ( 4)
51 #define  RC_ERROR_SETTING_SCSI_VARBLK_PROCESSING   ( 5)
52 #define  RC_ERROR_REWINDING_SCSI                   ( 6)
53 #define  RC_ERROR_OBTAINING_SCSI_STATUS            ( 7)
54 #define  RC_ERROR_READING_AWS_HEADER               ( 8)
55 #define  RC_ERROR_READING_DATA                     ( 9)
56 #define  RC_ERROR_AWSTAPE_BLOCK_TOO_LARGE          (10)
57 #define  RC_ERROR_WRITING_TAPEMARK                 (11)
58 #define  RC_ERROR_WRITING_OUTPUT_AWS_HEADER_BLOCK  (12)
59 #define  RC_ERROR_WRITING_DATA                     (13)
60 
61 /*-------------------------------------------------------------------*/
62 /* Static data areas                                                 */
63 /*-------------------------------------------------------------------*/
64 static BYTE vollbl[] = "\xE5\xD6\xD3";  /* EBCDIC characters "VOL"   */
65 static BYTE hdrlbl[] = "\xC8\xC4\xD9";  /* EBCDIC characters "HDR"   */
66 static BYTE eoflbl[] = "\xC5\xD6\xC6";  /* EBCDIC characters "EOF"   */
67 static BYTE eovlbl[] = "\xC5\xD6\xE5";  /* EBCDIC characters "EOV"   */
68 
69 static struct mt_tape_info tapeinfo[] = MT_TAPE_INFO;
70 
71 static struct mt_tape_info densinfo[] =
72 {
73     {0x01, "NRZI (800 bpi)"              },
74     {0x02, "PE (1600 bpi)"               },
75     {0x03, "GCR (6250 bpi)"              },
76     {0x05, "QIC-45/60 (GCR, 8000 bpi)"   },
77     {0x06, "PE (3200 bpi)"               },
78     {0x07, "IMFM (6400 bpi)"             },
79     {0x08, "GCR (8000 bpi)"              },
80     {0x09, "GCR (37871 bpi)"             },
81     {0x0A, "MFM (6667 bpi)"              },
82     {0x0B, "PE (1600 bpi)"               },
83     {0x0C, "GCR (12960 bpi)"             },
84     {0x0D, "GCR (25380 bpi)"             },
85     {0x0F, "QIC-120 (GCR 10000 bpi)"     },
86     {0x10, "QIC-150/250 (GCR 10000 bpi)" },
87     {0x11, "QIC-320/525 (GCR 16000 bpi)" },
88     {0x12, "QIC-1350 (RLL 51667 bpi)"    },
89     {0x13, "DDS (61000 bpi)"             },
90     {0x14, "EXB-8200 (RLL 43245 bpi)"    },
91     {0x15, "EXB-8500 (RLL 45434 bpi)"    },
92     {0x16, "MFM 10000 bpi"               },
93     {0x17, "MFM 42500 bpi"               },
94     {0x24, "DDS-2"                       },
95     {0x8C, "EXB-8505 compressed"         },
96     {0x90, "EXB-8205 compressed"         },
97     {0,     NULL                         },
98 };
99 
100 /*-------------------------------------------------------------------*/
101 /* Maximum blocksized SCSI tape I/O buffer...                        */
102 /*-------------------------------------------------------------------*/
103 static BYTE buf[ 65535 ];
104 
105 /*-------------------------------------------------------------------*/
106 /* Global variables used by main and the read/write functions        */
107 /*-------------------------------------------------------------------*/
108 int             len;                    /* Block length              */
109 int             prevlen;                /* Previous block length     */
110 int64_t         bytes_written;          /* Bytes written to o/p file */
111 char           *devnamein;              /* -> Input tape device name */
112 char           *devnameout;             /* -> Output tape device name*/
113 char           *filenamein;             /* -> Input AWS file name    */
114 char           *filenameout;            /* -> Output AWS file name   */
115 
116 /*-------------------------------------------------------------------*/
117 /* Custom exit function...                                           */
118 /*-------------------------------------------------------------------*/
delayed_exit(int exit_code)119 void delayed_exit (int exit_code)
120 {
121     if (RC_SUCCESS != exit_code)
122         printf( _( "HHCTC000I Abnormal termination\n" ) );
123 
124     /* Delay exiting is to give the system
125      * time to display the error message. */
126     usleep(100000);
127     exit(exit_code);
128 }
129 #define  EXIT(rc)   delayed_exit(rc)   /* (use this macro to exit)   */
130 
131 /*-------------------------------------------------------------------*/
132 /* Subroutine to print tape status                                   */
133 /*-------------------------------------------------------------------*/
print_status(char * devname,long stat)134 static void print_status (char *devname, long stat)
135 {
136     printf (_("HHCTC015I %s status: %8.8lX"), devname, stat);
137 
138     if (GMT_EOF    ( stat )) printf (" EOF"    );
139     if (GMT_BOT    ( stat )) printf (" BOT"    );
140     if (GMT_EOT    ( stat )) printf (" EOT"    );
141     if (GMT_SM     ( stat )) printf (" SETMARK");
142     if (GMT_EOD    ( stat )) printf (" EOD"    );
143     if (GMT_WR_PROT( stat )) printf (" WRPROT" );
144     if (GMT_ONLINE ( stat )) printf (" ONLINE" );
145     if (GMT_D_6250 ( stat )) printf (" 6250"   );
146     if (GMT_D_1600 ( stat )) printf (" 1600"   );
147     if (GMT_D_800  ( stat )) printf (" 800"    );
148     if (GMT_DR_OPEN( stat )) printf (" NOTAPE" );
149 
150     printf ("\n");
151 
152 } /* end function print_status */
153 
154 /*-------------------------------------------------------------------*/
155 /* Subroutine to print usage message                                 */
156 /*-------------------------------------------------------------------*/
print_usage(void)157 static void print_usage (void)
158 {
159     printf
160     ( _(
161         "\n"
162 //       1...5...10...15...20...25...30...35...40...45...50...55...60...65...70...75...80
163         "Copies a SCSI tape to or from an AWSTAPE disk file.\n\n"
164 
165         "Tapecopy reads a SCSI tape and outputs an AWSTAPE file representation\n"
166         "of the tape, or else reads an AWSTAPE file and creates an identical copy\n"
167         "of its contents on a tape mounted on a SCSI tape drive.\n\n"
168 
169         "Usage:\n\n"
170 
171         "   tapecopy  [tapedrive] [awsfile] or\n"
172         "   tapecopy  [awsfile] [tapedrive]\n\n"
173 
174         "Where:\n\n"
175 
176         "   tapedrive    specifies the device filename of the SCSI tape drive.\n"
177         "                Must begin with /dev%s to be recognized.\n"
178         "   awsfile      specifies the filename of the AWSTAPE disk file.\n\n"
179 
180         "The first filename is the input; the second is the output.\n\n"
181 
182         "If the input file is a SCSI tape, it is read and processed until physical EOD\n"
183         "(end-of-data) is reached (i.e. it does not stop whenever multiple tapemarks or\n"
184         "filemarks are read; it continues processing until the SCSI tape drive says\n"
185         "there is no more data on the tape). The resulting AWSTAPE output disk file,\n"
186         "when specified for the filename on a Hercules tape device configuration\n"
187         "statement, can then be used instead in order for the Hercules guest O/S to\n"
188         "read the exact same data without having to have a SCSI tape drive physically\n"
189         "attached to the host system. This allows you to easily transfer SCSI tape data\n"
190         "to other systems that may not have SCSI tape drives attached to them.\n\n"
191 
192         "The possible return codes and their meaning are:\n\n"
193 
194         "   %2d           Successful completion.\n"
195         "   %2d           Invalid arguments or no arguments given.\n"
196         "   %2d           Unable to open SCSI tape drive device file.\n"
197         "   %2d           Unable to open AWSTAPE disk file.\n"
198         "   %2d           Unrecoverable I/O error setting variable length block\n"
199         "                processing for SCSI tape device.\n"
200         "   %2d           Unrecoverable I/O error rewinding SCSI tape device.\n"
201         "   %2d           Unrecoverable I/O error obtaining status of SCSI device.\n"
202         "   %2d           Unrecoverable I/O error reading block header\n"
203         "                from AWSTAPE disk file.\n"
204         "   %2d           Unrecoverable I/O error reading data block.\n"
205         "   %2d           AWSTAPE block size too large.\n"
206         "   %2d           Unrecoverable I/O error writing tapemark.\n"
207         "   %2d           Unrecoverable I/O error writing block header\n"
208         "                to AWSTAPE disk file.\n"
209         "   %2d           Unrecoverable I/O error writing data block.\n"
210         "\n"
211         )
212 
213 #if defined(_MSVC_)
214         ," or \\\\.\\Tape"
215 #else
216         ,""
217 #endif
218         ,RC_SUCCESS
219         ,RC_ERROR_BAD_ARGUMENTS
220         ,RC_ERROR_OPENING_SCSI_DEVICE
221         ,RC_ERROR_OPENING_AWS_FILE
222         ,RC_ERROR_SETTING_SCSI_VARBLK_PROCESSING
223 
224         ,RC_ERROR_REWINDING_SCSI
225         ,RC_ERROR_OBTAINING_SCSI_STATUS
226         ,RC_ERROR_READING_AWS_HEADER
227         ,RC_ERROR_READING_DATA
228         ,RC_ERROR_AWSTAPE_BLOCK_TOO_LARGE
229         ,RC_ERROR_WRITING_TAPEMARK
230 
231         ,RC_ERROR_WRITING_OUTPUT_AWS_HEADER_BLOCK
232 
233         ,RC_ERROR_WRITING_DATA
234     );
235 
236 } /* end function print_usage */
237 
238 /*-------------------------------------------------------------------*/
239 /* Subroutine to obtain SCSI tape status...                          */
240 /*                                                                   */
241 /* Return value:     0  ==  normal                                   */
242 /*                  +1  ==  end-of-tape                              */
243 /*                  -1  ==  error                                    */
244 /*-------------------------------------------------------------------*/
obtain_status(char * devname,int devfd,struct mtget * mtget)245 static int obtain_status (char *devname, int devfd, struct mtget* mtget)
246 {
247 int rc;                                 /* Return code               */
248 
249     rc = ioctl_tape (devfd, MTIOCGET, (char*)mtget);
250     if (rc < 0)
251     {
252         if (1
253             && EIO == errno
254             && (0
255                 || GMT_EOD( mtget->mt_gstat )
256                 || GMT_EOT( mtget->mt_gstat )
257             )
258         )
259             return +1;
260 
261         printf (_("HHCTC016E Error reading status of %s: rc=%d, errno=%d: %s\n"),
262                 devname, rc, errno, strerror(errno));
263         return -1;
264     }
265 
266     if (GMT_EOD( mtget->mt_gstat ) ||
267         GMT_EOT( mtget->mt_gstat ))
268         return +1;
269 
270     return 0;
271 } /* end function obtain_status */
272 
273 /*-------------------------------------------------------------------*/
274 /* Read a block from SCSI tape                                       */
275 /*-------------------------------------------------------------------*/
read_scsi_tape(int devfd,void * buf,size_t bufsize,struct mtget * mtget)276 int read_scsi_tape (int devfd, void *buf, size_t bufsize, struct mtget* mtget)
277 {
278     int rc;
279     int save_errno;
280 
281     len = read_tape (devfd, buf, bufsize);
282     if (len < 0)
283     {
284         /* Determine whether end-of-tape has been read */
285         save_errno = errno;
286         ASSERT( devnamein );
287         rc = obtain_status (devnamein, devfd, mtget);
288         if (rc == +1)
289         {
290             printf (_("HHCTC011I End of tape.\n"));
291             errno = save_errno;
292             return(-1);
293         }
294         printf (_("HHCTC008E Error reading %s: errno=%d: %s\n"),
295             devnamein, errno, strerror(errno));
296         EXIT( RC_ERROR_READING_DATA );
297     }
298 
299     return(len);
300 } /* end function read_scsi_tape */
301 
302 /*-------------------------------------------------------------------*/
303 /* Read a block from AWSTAPE disk file                               */
304 /*-------------------------------------------------------------------*/
read_aws_disk(int diskfd,void * buf,size_t bufsize)305 int read_aws_disk (int diskfd, void *buf, size_t bufsize)
306 {
307     AWSTAPE_BLKHDR  awshdr;                 /* AWSTAPE block header      */
308     int             rc;
309     unsigned int    count_read = 0;
310     unsigned int    blksize;
311     int             end_block;
312     BYTE           *bufptr = buf;
313 
314     while (1)
315     {
316         /* Read block header */
317         rc = read (diskfd, &awshdr, sizeof(AWSTAPE_BLKHDR));
318         if (rc == 0)
319         {
320             printf (_("HHCTC018I End of AWSTAPE input file.\n"));
321             return (-1);
322         }
323         if (rc < (int)sizeof(AWSTAPE_BLKHDR))
324         {
325             printf (_("HHCTC019E Error reading AWSTAPE header from %s: rc=%d, errno=%d: %s\n"),
326                    filenamein, rc, errno, strerror(errno));
327             EXIT( RC_ERROR_READING_AWS_HEADER );
328         } /* end if(rc) */
329 
330         /* Interpret the block header */
331         blksize = ((int)awshdr.curblkl[1] << 8) + awshdr.curblkl[0];
332         end_block = (awshdr.flags1 & AWSTAPE_FLAG1_ENDREC) != 0;
333 
334         /* If this is a tapemark, return immediately */
335         if (blksize == 0)
336             return (0);
337 
338         /* Check maximum block length */
339         if ((count_read + blksize) > bufsize)
340         {
341             printf (_("HHCTC020E AWSTAPE block too large on %s: block size=%d, maximum=%d\n"),
342                    filenamein, count_read+blksize, (int)bufsize);
343             EXIT( RC_ERROR_AWSTAPE_BLOCK_TOO_LARGE );
344         } /* end if(count) */
345 
346         /* Read data block */
347         rc = read (diskfd, bufptr, blksize);
348         if (rc < (int)blksize)
349         {
350             printf (_("HHCTC021E Error reading data block from %s: rc=%d, errno=%d: %s\n"),
351                    filenamein, rc, errno, strerror(errno));
352             EXIT( RC_ERROR_READING_DATA );
353         } /* end if(rc) */
354 
355         bufptr += blksize;
356         count_read += blksize;
357         if (end_block)
358             break;
359     }
360 
361     return(count_read);
362 
363 } /* end function read_aws_disk */
364 
365 /*-------------------------------------------------------------------*/
366 /* Write a block to SCSI tape                                        */
367 /*-------------------------------------------------------------------*/
write_scsi_tape(int devfd,void * buf,size_t len)368 int write_scsi_tape (int devfd, void *buf, size_t len)
369 {
370     int                 rc;
371 
372     rc = write_tape (devfd, buf, len);
373     if (rc < (int)len)
374     {
375         printf (_("HHCTC022E Error writing data block to %s: rc=%d, errno=%d: %s\n"),
376                devnameout, rc, errno, strerror(errno));
377         EXIT( RC_ERROR_WRITING_DATA );
378     } /* end if(rc) */
379 
380     bytes_written += rc;
381 
382     return(rc);
383 
384 } /* end function write_scsi_tape */
385 
386 /*-------------------------------------------------------------------*/
387 /* Write a block to AWSTAPE disk file                                */
388 /*-------------------------------------------------------------------*/
write_aws_disk(int diskfd,void * buf,size_t len)389 int write_aws_disk (int diskfd, void *buf, size_t len)
390 {
391     AWSTAPE_BLKHDR  awshdr;                 /* AWSTAPE block header      */
392     int             rc;
393 
394     /* Build the block header */
395     awshdr.curblkl[0] =   len            & 0xFF;
396     awshdr.curblkl[1] = ( len     >> 8 ) & 0xFF;
397     awshdr.prvblkl[0] =   prevlen        & 0xFF;
398     awshdr.prvblkl[1] = ( prevlen >> 8 ) & 0xFF;
399     awshdr.flags1     = 0
400                         | AWSTAPE_FLAG1_NEWREC
401                         | AWSTAPE_FLAG1_ENDREC
402                         ;
403     awshdr.flags2     = 0;
404 
405     /* Write block header to output file */
406     rc = write (diskfd, &awshdr, sizeof(AWSTAPE_BLKHDR));
407     if (rc < (int)sizeof(AWSTAPE_BLKHDR))
408     {
409         printf (_("HHCTC013E Error writing AWSTAPE header on %s: rc=%d, errno=%d: %s\n"),
410                filenameout, rc, errno, strerror(errno));
411         EXIT( RC_ERROR_WRITING_OUTPUT_AWS_HEADER_BLOCK );
412     } /* end if(rc) */
413 
414     bytes_written += rc;
415 
416     /* Write data block to output file */
417     rc = write (diskfd, buf, len);
418     if (rc < (int)len)
419     {
420         printf (_("HHCTC014E Error writing data block to %s: rc=%d, errno=%d: %s\n"),
421                filenameout, rc, errno, strerror(errno));
422         EXIT( RC_ERROR_WRITING_DATA );
423     } /* end if(rc) */
424 
425     bytes_written += rc;
426 
427     return(rc);
428 } /* end function write_aws_disk */
429 
430 /*-------------------------------------------------------------------*/
431 /* Write a tapemark to SCSI tape                                     */
432 /*-------------------------------------------------------------------*/
write_tapemark_scsi_tape(int devfd)433 int write_tapemark_scsi_tape (int devfd)
434 {
435     struct mtop     opblk;                  /* Area for MTIOCTOP ioctl   */
436     int             rc;
437 
438     opblk.mt_op = MTWEOF;
439     opblk.mt_count = 1;
440     rc = ioctl_tape (devfd, MTIOCTOP, (char*)&opblk);
441     if (rc < 0)
442     {
443         printf (_("HHCTC023E Error writing tapemark on %s: rc=%d, errno=%d: %s\n"),
444                 devnameout, rc, errno, strerror(errno));
445         EXIT( RC_ERROR_WRITING_TAPEMARK );
446     }
447     return(rc);
448 
449 } /* end function write_tapemark_scsi_tape */
450 
451 /*-------------------------------------------------------------------*/
452 /* Write a tapemark to AWSTAPE disk file                             */
453 /*-------------------------------------------------------------------*/
write_tapemark_aws_disk(int diskfd)454 int write_tapemark_aws_disk (int diskfd)
455 {
456     AWSTAPE_BLKHDR  awshdr;                 /* AWSTAPE block header      */
457     int             rc;
458 
459     /* Build block header for tape mark */
460     awshdr.curblkl[0] = 0;
461     awshdr.curblkl[1] = 0;
462     awshdr.prvblkl[0] =   prevlen        & 0xFF;
463     awshdr.prvblkl[1] = ( prevlen >> 8 ) & 0xFF;
464     awshdr.flags1     = AWSTAPE_FLAG1_TAPEMARK;
465     awshdr.flags2     = 0;
466 
467     /* Write block header to output file */
468     rc = write (diskfd, &awshdr, sizeof(AWSTAPE_BLKHDR));
469     if (rc < (int)sizeof(AWSTAPE_BLKHDR))
470     {
471         printf (_("HHCTC010E Error writing tapemark on %s: rc=%d, errno=%d, %s\n"),
472                   filenameout, rc, errno, strerror(errno));
473        EXIT( RC_ERROR_WRITING_TAPEMARK );
474     } /* end if(rc) */
475 
476     bytes_written += rc;
477     return(rc);
478 } /* end function write_tapemark_aws_disk */
479 
480 /*-------------------------------------------------------------------*/
481 /* TAPECOPY main entry point                                         */
482 /*-------------------------------------------------------------------*/
main(int argc,char * argv[])483 int main (int argc, char *argv[])
484 {
485 int             rc;                     /* Return code               */
486 int             i;                      /* Array subscript           */
487 int             devfd;                  /* Tape file descriptor      */
488 int             diskfd = -1;            /* Disk file descriptor      */
489 int             fileno;                 /* Tape file number          */
490 int             blkcount;               /* Block count               */
491 int             totalblks = 0;          /* Block count               */
492 int             minblksz;               /* Minimum block size        */
493 int             maxblksz;               /* Maximum block size        */
494 struct mtop     opblk;                  /* Area for MTIOCTOP ioctl   */
495 long            density;                /* Tape density code         */
496 BYTE            labelrec[81];           /* Standard label (ASCIIZ)   */
497 int64_t         bytes_read;             /* Bytes read from i/p file  */
498 int64_t         file_bytes;             /* Byte count for curr file  */
499 char            pathname[MAX_PATH];     /* file name in host format  */
500 struct mtget    mtget;                  /* Area for MTIOCGET ioctl   */
501 #if defined(EXTERNALGUI)
502 struct mtpos    mtpos;                  /* Area for MTIOCPOS ioctl   */
503 int             is3590 = 0;             /* 1 == 3590, 0 == 3480/3490 */
504 #endif /*defined(EXTERNALGUI)*/
505 
506     INITIALIZE_UTILITY("tapecopy");
507 
508    /* Display the program identification message */
509     display_version (stderr, "Hercules tape copy program ", FALSE);
510 
511     /* The first argument is the input file name
512        (either AWS disk file or SCSI tape device)
513     */
514     if ((argc < 2) || (argv[1] == NULL))
515     {
516         print_usage();
517         EXIT( RC_ERROR_BAD_ARGUMENTS );
518         return(0); /* Make gcc -Wall happy */
519     }
520 
521     if (0
522         || ( strlen( argv[1] ) > 5 && strnfilenamecmp( argv[1], "/dev/",   5 ) == 0 )
523         || ( strlen( argv[1] ) > 4 && strnfilenamecmp( argv[1], "\\\\.\\", 4 ) == 0 )
524     )
525     {
526         devnamein = argv[1];
527         filenamein = NULL;
528     }
529     else
530     {
531         filenamein = argv[1];
532         devnamein = NULL;
533     }
534 
535     /* The second argument is the output file name
536        (either AWS disk file or SCSI tape device)
537     */
538     if (argc > 2 && argv[2] )
539     {
540         if (0
541             || ( strlen( argv[2] ) > 5 && strnfilenamecmp( argv[2], "/dev/",   5 ) == 0 )
542             || ( strlen( argv[2] ) > 4 && strnfilenamecmp( argv[2], "\\\\.\\", 4 ) == 0 )
543         )
544         {
545             devnameout = argv[2];
546             filenameout = NULL;
547         }
548         else
549         {
550             filenameout = argv[2];
551             devnameout = NULL;
552         }
553     }
554     else
555     {
556         print_usage();
557         EXIT( RC_ERROR_BAD_ARGUMENTS );
558     }
559 
560     /* Check input arguments and disallow tape-to-tape or disk-to-disk copy */
561     if ((!devnamein && !devnameout) || (!filenamein && !filenameout))
562     {
563         print_usage();
564         EXIT( RC_ERROR_BAD_ARGUMENTS );
565     }
566 
567     /* Open the SCSI tape device */
568     if (devnamein)
569     {
570         hostpath( pathname, devnamein, sizeof(pathname) );
571         devfd = open_tape (pathname, O_RDONLY|O_BINARY);
572     }
573     else // (devnameout)
574     {
575         hostpath( pathname, devnameout, sizeof(pathname) );
576         devfd = open_tape (pathname, O_RDWR|O_BINARY);
577     }
578     if (devfd < 0)
579     {
580         printf (_("HHCTC001E Error opening %s: errno=%d: %s\n"),
581                 (devnamein ? devnamein : devnameout), errno, strerror(errno));
582         EXIT( RC_ERROR_OPENING_SCSI_DEVICE );
583     }
584 
585     usleep(50000);
586 
587     /* Set the tape device to process variable length blocks */
588     opblk.mt_op = MTSETBLK;
589     opblk.mt_count = 0;
590     rc = ioctl_tape (devfd, MTIOCTOP, (char*)&opblk);
591     if (rc < 0)
592     {
593         printf (_("HHCTC005E Error setting attributes for %s: rc=%d, errno=%d: %s\n"),
594                 (devnamein ? devnamein : devnameout), rc, errno, strerror(errno));
595         EXIT( RC_ERROR_SETTING_SCSI_VARBLK_PROCESSING );
596     }
597 
598     usleep(50000);
599 
600     /* Rewind the tape to the beginning */
601     opblk.mt_op = MTREW;
602     opblk.mt_count = 1;
603     rc = ioctl_tape (devfd, MTIOCTOP, (char*)&opblk);
604     if (rc < 0)
605     {
606         printf (_("HHCTC006E Error rewinding %s: rc=%d, errno=%d: %s\n"),
607                 (devnamein ? devnamein : devnameout), rc, errno, strerror(errno));
608         EXIT( RC_ERROR_REWINDING_SCSI );
609     }
610 
611     usleep(50000);
612 
613     /* Obtain the tape status */
614     rc = obtain_status ((devnamein ? devnamein : devnameout), devfd, &mtget);
615     if (rc < 0)
616         EXIT( RC_ERROR_OBTAINING_SCSI_STATUS );
617 
618     /* Display tape status information */
619     for (i = 0; tapeinfo[i].t_type != 0
620                 && tapeinfo[i].t_type != mtget.mt_type; i++);
621 
622     if (tapeinfo[i].t_name)
623         printf (_("HHCTC003I %s device type: %s\n"),
624             (devnamein ? devnamein : devnameout), tapeinfo[i].t_name);
625     else
626         printf (_("HHCTC003I %s device type: 0x%lX\n"),
627             (devnamein ? devnamein : devnameout), mtget.mt_type);
628 
629     density = (mtget.mt_dsreg & MT_ST_DENSITY_MASK)
630                 >> MT_ST_DENSITY_SHIFT;
631 
632     for (i = 0; densinfo[i].t_type != 0
633                 && densinfo[i].t_type != density; i++);
634 
635     if (densinfo[i].t_name)
636         printf (_("HHCTC004I %s tape density: %s\n"),
637                 (devnamein ? devnamein : devnameout), densinfo[i].t_name);
638     else
639         printf (_("HHCTC004I %s tape density code: 0x%lX\n"),
640             (devnamein ? devnamein : devnameout), density);
641 
642     if (mtget.mt_gstat != 0)
643         print_status ((devnamein ? devnamein : devnameout), mtget.mt_gstat);
644 
645     /* Open the disk file */
646     if (filenamein)
647     {
648         hostpath( pathname, filenamein, sizeof(pathname) );
649         diskfd = hopen(pathname, O_RDONLY | O_BINARY);
650     }
651     else
652     {
653         hostpath( pathname, filenameout, sizeof(pathname) );
654         diskfd = hopen(pathname, O_WRONLY | O_CREAT | O_BINARY,
655                         S_IRUSR | S_IWUSR | S_IRGRP);
656     }
657     if (diskfd < 0)
658     {
659         printf (_("HHCTC007E Error opening %s: errno=%d: %s\n"),
660                 (filenamein ? filenamein : filenameout),
661                 errno, strerror(errno));
662         EXIT( RC_ERROR_OPENING_AWS_FILE );
663     }
664 
665     /* Copy blocks from input to output */
666     fileno = 1;
667     blkcount = 0;
668     totalblks = 0;
669     minblksz = 0;
670     maxblksz = 0;
671     len = 0;
672     bytes_read = 0;
673     bytes_written = 0;
674     file_bytes = 0;
675 
676 #if defined(EXTERNALGUI)
677     // Notify the GUI of the high-end of the copy-progress range...
678     if ( extgui )
679     {
680         // Retrieve BOT block-id...
681         VERIFY( 0 == ioctl_tape( devfd, MTIOCPOS, (char*)&mtpos ) );
682 
683         is3590 = ((mtpos.mt_blkno & 0x7F000000) != 0x01000000) ? 1 : 0;
684 
685         if (!is3590)
686         {
687             // The seg# portion the SCSI tape physical
688             // block-id number values ranges from 1 to 95...
689             fprintf( stderr, "BLKS=%d\n", 95 );
690         }
691         else
692         {
693             // FIXME: 3590s (e.g. Magstar) use 32-bit block addressing,
694             // and thus its block-id does not contain a seg# value, so
695             // we must use some other technique. For now, we'll simply
696             // presume the last block on the tape is block# 0x003FFFFF
697             // (just to keep things simple).
698 
699             fprintf( stderr, "BLKS=%d\n", 0x003FFFFF );
700         }
701 
702         // Init time of last issued progress message
703         prev_progress_time = time( NULL );
704     }
705 #endif /*defined(EXTERNALGUI)*/
706 
707     /* Perform the copy... */
708 
709     while (1)
710     {
711 #if defined(EXTERNALGUI)
712         /* Issue a progress message every few seconds... */
713         if ( extgui )
714         {
715             if ( ( curr_progress_time = time( NULL ) ) >=
716                 ( prev_progress_time + PROGRESS_INTERVAL_SECS ) )
717             {
718                 prev_progress_time = curr_progress_time;
719                 if ( ioctl_tape( devfd, MTIOCPOS, (char*)&mtpos ) == 0 )
720                 {
721                     if (!is3590)
722                         fprintf( stderr, "BLK=%ld\n", (mtpos.mt_blkno >> 24) & 0x0000007F );
723                     else
724                         fprintf( stderr, "BLK=%ld\n", mtpos.mt_blkno );
725                 }
726             }
727         }
728 #endif /*defined(EXTERNALGUI)*/
729 
730         /* Save previous block length */
731         prevlen = len;
732 
733         /* Read a block */
734         if (devnamein)
735             len = read_scsi_tape(devfd, buf, sizeof(buf), &mtget);
736         else
737             len = read_aws_disk(diskfd, buf, sizeof(buf));
738 
739         /* If returned with -1, end of tape; errors are handled by the
740             read functions themselves */
741         if (len < 0)
742             break;
743 
744         /* Check for tape mark */
745         if (len == 0)
746         {
747             /* Write tape mark to output file */
748             if (filenameout)
749                 write_tapemark_aws_disk(diskfd);
750             else
751                 write_tapemark_scsi_tape(devfd);
752 
753             /* Print summary of current file */
754             if (blkcount)
755             {
756                 ASSERT( file_bytes ); // (sanity check)
757 
758                 printf (_("HHCTC009I File %u: Blocks=%u, Bytes=%"I64_FMT"d, Block size min=%u, "
759                         "max=%u, avg=%u\n"),
760                         fileno, blkcount, file_bytes, minblksz, maxblksz,
761                         (int)file_bytes/blkcount);
762             }
763             else
764             {
765                 ASSERT( !file_bytes ); // (sanity check)
766             }
767 
768             /* Show the 'tapemark' AFTER the above file summary since
769                that's the actual physical sequence of events; i.e. the
770                file data came first THEN it was followed by a tapemark */
771             printf(_("          (tapemark)\n"));  // (align past HHCmsg#)
772 
773             /* Reset counters for next file */
774             if (blkcount)
775                 fileno++;
776             minblksz = 0;
777             maxblksz = 0;
778             blkcount = 0;
779             file_bytes = 0;
780             continue;
781         }
782 
783         /* Count blocks and block sizes */
784         blkcount++;
785         totalblks++;
786         bytes_read += len;
787         file_bytes += len;
788         if (len > maxblksz) maxblksz = len;
789         if (minblksz == 0 || len < minblksz) minblksz = len;
790 
791         /* Print standard labels */
792         if (1
793             && blkcount < 4
794             && len == 80
795             && (0
796                 || memcmp( buf, vollbl, 3 ) == 0
797                 || memcmp( buf, hdrlbl, 3 ) == 0
798                 || memcmp( buf, eoflbl, 3 ) == 0
799                 || memcmp( buf, eovlbl, 3 ) == 0
800                )
801         )
802         {
803             for (i=0; i < 80; i++)
804                 labelrec[i] = guest_to_host(buf[i]);
805             labelrec[i] = '\0';
806             printf (_("HHCTC012I %s\n"), labelrec);
807         }
808         else
809         {
810             ASSERT(blkcount);
811 
812 #if defined(EXTERNALGUI)
813             if ( !extgui )
814 #endif
815                 printf( _("File %u: Block %u\r"),
816                     fileno, blkcount );
817         }
818 
819         /* Write block to output file */
820         if (filenameout)
821             write_aws_disk(diskfd, buf, len);
822         else
823             write_scsi_tape(devfd, buf, len);
824 
825     } /* end while */
826 
827     /* Print run totals, close files, and exit... */
828 
829 #define  ONE_MEGABYTE    ( 1024 * 1024 )
830 #define  HALF_MEGABYTE   ( ONE_MEGABYTE / 2 )
831 
832     printf
833     (
834         _(
835             "HHCTC000I Successful completion;\n"
836             "          Bytes read: %"I64_FMT"d (%3.1f MB), Blocks=%u, avg=%u\n"
837          )
838         ,           bytes_read
839         ,(double) ( bytes_read    + HALF_MEGABYTE ) / (double) ONE_MEGABYTE
840         ,totalblks
841         ,totalblks ? (int)bytes_read/totalblks : -1
842     );
843 
844     printf
845     (
846         _(
847             "          Bytes written: %"I64_FMT"d (%3.1f MB)\n"
848          )
849         ,           bytes_written
850         ,(double) ( bytes_written + HALF_MEGABYTE ) / (double) ONE_MEGABYTE
851     );
852     close (diskfd);
853 
854     /* Rewind the tape back to the beginning again before exiting */
855 
856     opblk.mt_op = MTREW;
857     opblk.mt_count = 1;
858 
859     rc = ioctl_tape (devfd, MTIOCTOP, (char*)&opblk);
860 
861     if (rc < 0)
862     {
863         printf (_("HHCTC006E Error rewinding %s: rc=%d, errno=%d: %s\n"),
864                 (devnamein ? devnamein : devnameout), rc, errno, strerror(errno));
865         EXIT( RC_ERROR_REWINDING_SCSI );
866     }
867 
868     close_tape (devfd);
869 
870     EXIT( RC_SUCCESS );
871     return(0);  /* Make -Wall happy */
872 
873 } /* end function main */
874 
875 #endif /* defined(OPTION_SCSI_TAPE) */
876