8a7b2ca85f33475b6f599f93047e3503472ed819
[debian/mtx] / scsitape.c
1 /* Copyright 2001 Enhanced Software Technologies Inc.
2  *   Released under terms of the GNU General Public License as
3  * required by the license on 'mtxl.c'.
4  * $Date: 2007-03-24 18:14:01 -0700 (Sat, 24 Mar 2007) $
5  * $Revision: 166 $
6  */
7
8 /* This is a generic SCSI tape control program. It operates by
9  * directly sending commands to the tape drive. If you are going
10  * through your operating system's SCSI tape driver, do *NOT* use 
11  * this program! If, on the other hand, you are using raw READ and WRITE
12  * commands through your operating system's generic SCSI interface (or
13  * through our built-in 'read' and 'write'), this is the place for you. 
14  */
15
16 /*#define DEBUG_PARTITION */
17 /*#define DEBUG 1 */
18
19 /* 
20         Commands:
21                 setblk <n> -- set the block size to <n>
22                 fsf <n> -- go forward by <n> filemarks
23                 bsf <n> -- go backward by <n> filemarks
24                 eod  -- go to end of data
25                 rewind -- rewind back to start of data
26                 eject  -- rewind, then eject the tape. 
27                 erase  -- (short) erase the tape (we have no long erase)
28                 mark <n> -- write <n> filemarks.
29                 seek <n> -- seek to position <n>.
30
31                 write <blksize> <-- write blocks from stdin to the tape 
32                 read  [<blksize>] [<#blocks/#bytes>] -- read blocks from tape, write to stdout. 
33
34         See the 'tapeinfo' program for status info about the tape drive.
35
36  */
37
38 #include <stdio.h>
39 #include <string.h>
40
41 #include "mtx.h"
42 #include "mtxl.h"
43
44 #if HAVE_UNISTD_H
45 #include <unistd.h>
46 #endif
47
48 #if HAVE_SYS_TYPES_H
49 #include <sys/types.h>
50 #endif
51
52 #if HAVE_SYS_IOCTL_H
53 #include <sys/ioctl.h>
54 #endif
55
56 #if HAVE_SYS_MTIO_H
57 #include <sys/mtio.h> /* will try issuing some ioctls for Solaris, sigh. */
58 #endif
59
60 #ifdef _MSC_VER
61 #include <io.h>
62 #endif
63
64 void Usage(void) {
65   FatalError("Usage: scsitape -f <generic-device> <command> where <command> is:\n setblk <n> | fsf <n> | bsf <n> | eod | rewind | eject | mark <n> |\n  seek <n> | read [<blksize> [<numblocks]] | write [<blocksize>] \n");
66 }
67
68 #define arg1 (arg[0])  /* for backward compatibility, sigh */
69 static int arg[4];  /* the argument for the command, sigh. */
70
71 /* the device handle we're operating upon, sigh. */
72 static char *device;  /* the text of the device thingy. */
73 static DEVICE_TYPE MediumChangerFD = (DEVICE_TYPE) -1;
74
75
76
77 static int S_setblk(void);
78 static int S_fsf(void);
79 static int S_bsf(void);
80 static int S_eod(void);
81 static int S_rewind(void);
82 static int S_eject(void);
83 static int S_mark(void);
84 static int S_seek(void);
85 static int S_reten(void);
86 static int S_erase(void);
87
88 static int S_read(void);
89 static int S_write(void);
90
91
92 struct command_table_struct {
93         int num_args;
94         char *name;
95         int (*command)(void);
96 } command_table[] = {
97         { 1, "setblk", S_setblk },
98         { 1, "fsf", S_fsf },
99         { 1, "bsf", S_bsf },
100         { 0, "eod", S_eod },
101         { 0, "rewind", S_rewind },
102         { 0, "eject", S_eject },
103         { 0, "reten", S_reten },
104         { 0, "erase", S_erase },
105         { 1, "mark", S_mark },
106         { 1, "seek", S_seek },
107         { 2, "read", S_read },
108         { 2, "write",S_write },
109         { 0, NULL, NULL } /* terminate list */
110 };
111
112 char *argv0;
113
114
115 /* open_device() -- set the 'fh' variable.... */
116 void open_device(void)
117 {
118         if (MediumChangerFD != -1)
119         {
120                 SCSI_CloseDevice("Unknown",MediumChangerFD);  /* close it, sigh...  new device now! */
121         }
122
123         MediumChangerFD = SCSI_OpenDevice(device);
124 }
125
126 static int get_arg(char *arg)
127 {
128         int retval=-1;
129
130         if (*arg < '0' || *arg > '9')
131         {
132                 return -1;  /* sorry! */
133         }
134
135         retval=atoi(arg);
136         return retval;
137 }
138
139
140 /* we see if we've got a file open. If not, we open one :-(. Then
141  * we execute the actual command. Or not :-(. 
142  */ 
143 int execute_command(struct command_table_struct *command)
144 {
145         /* if the device is not already open, then open it from the 
146         * environment.
147         */
148         if (!MediumChangerFD == -1)
149         {
150                 /* try to get it from STAPE or TAPE environment variable... */
151                 device = getenv("STAPE");
152                 if (device == NULL)
153                 {
154                         device = getenv("TAPE");
155                         if (device == NULL)
156                         {
157                                 Usage();
158                         }
159                 }
160                 open_device();
161         }
162
163         /* okay, now to execute the command... */
164         return command->command();
165 }
166
167
168 /* parse_args():
169  *   Basically, we are parsing argv/argc. We can have multiple commands
170  * on a line now, such as "unload 3 0 load 4 0" to unload one tape and
171  * load in another tape into drive 0, and we execute these commands one
172  * at a time as we come to them. If we don't have a -f at the start, we
173  * barf. If we leave out a drive #, we default to drive 0 (the first drive
174  * in the cabinet). 
175  */ 
176
177 int parse_args(int argc, char **argv)
178 {
179         int i, cmd_tbl_idx,retval,arg_idx;
180         struct command_table_struct *command;
181
182         i=1;
183         arg_idx = 0;
184         while (i < argc)
185         {
186                 if (strcmp(argv[i],"-f") == 0)
187                 {
188                         i++;
189                         if (i >= argc)
190                         {
191                                 Usage();
192                         }
193                         device = argv[i++];
194                         open_device(); /* open the device and do a status scan on it... */
195                 }
196                 else
197                 {
198                         cmd_tbl_idx=0;
199                         command = &command_table[0]; /* default to the first command... */
200                         command = &command_table[cmd_tbl_idx];
201                         while (command->name)
202                         {
203                                 if (strcmp(command->name,argv[i]) == 0)
204                                 {
205                                         /* we have a match... */
206                                         break;
207                                 }
208                                 /* otherwise we don't have a match... */
209                                 cmd_tbl_idx++;
210                                 command = &command_table[cmd_tbl_idx];
211                         }
212                         /* if it's not a command, exit.... */
213                         if (command->name == NULL)
214                         {
215                                 Usage();
216                         }
217                         i++;  /* go to the next argument, if possible... */
218                         /* see if we need to gather arguments, though! */
219                         arg1 = -1; /* default it to something */
220                         for (arg_idx=0;arg_idx < command->num_args ; arg_idx++)
221                         {
222                                 if (i < argc)
223                                 {
224                                         arg[arg_idx] = get_arg(argv[i]);
225                                         if (arg[arg_idx] !=  -1)
226                                         {
227                                                 i++; /* increment i over the next cmd. */
228                                         }
229                                 }
230                                 else
231                                 {
232                                         arg[arg_idx] = 0; /* default to 0 setmarks or whatever */
233                                 }
234                         }
235                         retval=execute_command(command);  /* execute_command handles 'stuff' */
236                         exit(retval);
237                 }
238         }
239         return 0; /* should never get here */
240 }
241
242 /* For Linux, this allows us to do a short erase on a tape (sigh!).
243  * Note that you'll need to do a 'mt status' on the tape afterwards in
244  * order to get the tape driver in sync with the tape drive again. Also
245  * note that on other OS's, this might do other evil things to the tape
246  * driver. Note that to do an erase, you must first rewind!
247
248  */
249 static int S_erase(void)
250 {
251         int retval;
252         RequestSense_T *RequestSense;
253
254         retval=S_rewind();
255         if (retval)
256         {
257                 return retval;  /* we have an exit status :-(. */
258         } 
259
260         RequestSense=Erase(MediumChangerFD);
261         if (RequestSense)
262         {
263                 PrintRequestSense(RequestSense);
264                 exit(1);  /* exit with an error status. */
265         }
266         return 0;
267 }
268
269 /* This should eject a tape or magazine, depending upon the device sent
270  * to.
271  */
272 static int S_eject(void)
273 {
274         int i;
275         i = LoadUnload(MediumChangerFD, 0);
276         if ( i < 0)
277         {
278                 fprintf(stderr,"scsitape:eject failed\n");
279                 fflush(stderr);
280         }
281         return i;
282 }
283
284
285
286 /* We write a filemarks of 0 before going to grab position, in order
287  * to insure that data in the buffer is not a problem. 
288  */
289
290 static int S_mark(void)
291 {
292         RequestSense_T RequestSense; /* for result of ReadElementStatus */
293         CDB_T CDB;
294         unsigned char buffer[6];
295         int count = arg1; /* voila! */
296
297         CDB[0] = 0x10;  /* SET_MARK */
298         CDB[1] = 0;
299         CDB[2] = (unsigned char)(count >> 16);
300         CDB[3] = (unsigned char)(count >> 8);
301         CDB[4] = (unsigned char)count;
302         CDB[5] = 0; 
303
304         /* we really don't care if this command works or not, sigh.  */
305         slow_bzero((char *)&RequestSense, sizeof(RequestSense_T));
306
307         if (SCSI_ExecuteCommand(MediumChangerFD, Input, &CDB, 6, buffer, 0, &RequestSense)!= 0)
308         {
309                 PrintRequestSense(&RequestSense);
310                 return 1;
311         }
312         return 0;
313 }
314 /* let's rewind to bod! 
315  */
316
317 static int S_rewind(void)
318 {
319         RequestSense_T sense;
320         CDB_T CDB;
321         unsigned char buffer[6];
322
323         CDB[0] = 0x01;  /* REWIND */
324         CDB[1] = 0;
325         CDB[2] = 0;
326         CDB[3] = 0;
327         CDB[4] = 0;
328         CDB[5] = 0; 
329
330         /* we really don't care if this command works or not, sigh.  */
331         slow_bzero((char *)&sense,sizeof(RequestSense_T));
332         if (SCSI_ExecuteCommand(MediumChangerFD,Input,&CDB,6,buffer,0,&sense)!=0)
333         {
334                 PrintRequestSense(&sense);
335                 return 1;
336         }
337         return 0;
338 }
339
340
341
342
343 /* This is used for fsf and bsf. */
344 static int Space(int count, char code)
345 {
346         RequestSense_T sense;
347         CDB_T CDB;
348         unsigned char buffer[6];
349
350         CDB[0] = 0x11;  /* SET_MARK */
351         CDB[1] = code;
352         CDB[2] = (unsigned char)(count >> 16);
353         CDB[3] = (unsigned char)(count >> 8);
354         CDB[4] = (unsigned char)count;
355         CDB[5] = 0; 
356
357         /* we really don't care if this command works or not, sigh.  */
358         slow_bzero((char *)&sense,sizeof(RequestSense_T));
359         if (SCSI_ExecuteCommand(MediumChangerFD, Input, &CDB, 6, buffer, 0, &sense) != 0)
360         {
361                 PrintRequestSense(&sense);
362                 return 1;
363         }
364         return 0;
365 }
366
367
368 /* Let's try a fsf: */
369 /* We write a filemarks of 0 before going to grab position, in order
370  * to insure that data in the buffer is not a problem. 
371  */
372
373 static int S_fsf(void)
374 {
375         return Space(arg1,1); /* go forward! */
376 }
377
378 static int S_bsf(void)
379 {
380         return Space(-arg1,1); /* go backward! */
381 }
382
383 static int S_eod(void)
384 {
385         return Space(0,3); /* go to eod! */
386 }
387
388 /* sigh, abuse of the LOAD command...
389
390  */
391 static int S_reten(void)
392 {
393         RequestSense_T sense;
394         CDB_T CDB;
395         unsigned char buffer[6];
396
397         CDB[0] = 0x1B;  /* START_STOP */
398         CDB[1] = 0; /* wait */
399         CDB[2] = 0;
400         CDB[3] = 0;
401         CDB[4] = 3; /* reten. */ 
402         CDB[5] = 0; 
403
404         /* we really don't care if this command works or not, sigh.  */
405         slow_bzero((char *)&sense, sizeof(RequestSense_T));
406         if (SCSI_ExecuteCommand(MediumChangerFD, Input, &CDB, 6, buffer, 0, &sense) != 0)
407         {
408                 PrintRequestSense(&sense);
409                 return 1;
410         }
411         return 0;
412 }
413
414 /* seek a position on the tape (sigh!) */
415 static int S_seek(void)
416 {
417         RequestSense_T sense;
418         CDB_T CDB;
419         unsigned char buffer[6];
420         int count =  arg1;
421
422         /* printf("count=%d\n",arg1); */
423
424         CDB[0] = 0x2B;  /* LOCATE */
425         CDB[1] = 0;  /* Logical */
426         CDB[2] = 0; /* padding */
427         CDB[3] = (unsigned char)(count >> 24);
428         CDB[4] = (unsigned char)(count >> 16);
429         CDB[5] = (unsigned char)(count >> 8);
430         CDB[6] = (unsigned char)count;
431         CDB[7] = 0; 
432         CDB[8] = 0; 
433         CDB[9] = 0; 
434
435         /* we really don't care if this command works or not, sigh.  */
436         slow_bzero((char *)&sense,sizeof(RequestSense_T));
437         if (SCSI_ExecuteCommand(MediumChangerFD, Input, &CDB, 10, buffer, 0, &sense) != 0)
438         {
439                 PrintRequestSense(&sense);
440                 return 1;
441         }
442         return 0;
443 }
444
445 #ifdef MTSRSZ
446 static int Solaris_setblk(int fh,int count)
447 {
448         /* we get here only if we have a MTSRSZ, which means Solaris. */
449         struct mtop mt_com;  /* the struct used for the MTIOCTOP ioctl */
450         int result;
451
452         /* okay, we have fh and count.... */
453
454         /* Now to try the ioctl: */
455         mt_com.mt_op=MTSRSZ;
456         mt_com.mt_count=count;
457
458         /* surround the actual ioctl to enable threading, since fsf/etc. can be
459          * big time consumers and we want other threads to be able to run too. 
460          */
461
462         result=ioctl(fh, MTIOCTOP, (char *)&mt_com);
463
464         if (result < 0)
465         {
466                 return errno;
467         }
468
469         /* okay, we did okay. Return a value of None... */
470         return 0;
471 }
472 #endif
473
474
475 /* okay, this is a write: we need to set the block size to something: */
476 static int S_setblk(void)
477 {
478         RequestSense_T sense;
479         CDB_T CDB;
480         char buffer[12];
481         unsigned int count = (unsigned int) arg1;
482
483         CDB[0] = 0x15;  /* MODE SELECT */
484         CDB[1] = 0x10;  /* scsi2 */
485         CDB[2] = 0; 
486         CDB[3] = 0;
487         CDB[4] = 12; /* length of data */
488         CDB[5] = 0;
489
490         slow_bzero((char *)&sense, sizeof(RequestSense_T));
491         slow_bzero(buffer, 12);
492
493         /* Now to set the mode page header: */
494         buffer[0] = 0;
495         buffer[1] = 0;
496         buffer[2] = 0x10; /* we are in buffered mode now, people! */
497         buffer[3] = 8; /* block descriptor length. */ 
498         buffer[4] = 0; /* reset to default density, sigh. */ /* 0 */
499         buffer[5] = 0; /* 1 */
500         buffer[6] = 0; /* 2 */
501         buffer[7] = 0; /* 3 */
502         buffer[8] = 0; /* 4 */
503         buffer[9] = (unsigned char)(count >> 16); /* 5 */
504         buffer[10] = (unsigned char)(count >> 8); /* 6 */
505         buffer[11] = (unsigned char)count;  /* 7 */
506
507         if (SCSI_ExecuteCommand(MediumChangerFD,Output,&CDB,6,buffer,12,&sense)!=0)
508         {
509                 PrintRequestSense(&sense);
510                 return 1;
511         }
512 #ifdef MTSRSZ
513         /*   Solaris_setblk(MediumChangerFD,count);   */
514 #endif
515
516         return 0;
517 }
518
519 /*************************************************************************/
520 /* SCSI read/write calls. These are mostly pulled out of BRU 16.1, 
521  * modified to work within the mtxl.h framework rather than the
522  * scsi_lowlevel.h framework. 
523  *************************************************************************/ 
524
525 #define MAX_READ_SIZE 128*1024  /* max size of a variable-block read */
526
527 #define READ_OK 0
528 #define READ_FILEMARK 1
529 #define READ_EOD 2
530 #define READ_EOP 3
531 #define READ_SHORT 5
532 #define READ_ERROR 255
533
534 #define WRITE_OK 0
535 #define WRITE_ERROR 1
536 #define WRITE_EOM 2
537 #define WRITE_EOV 3
538
539
540 /* These are copied out of BRU 16.1, with all the boolean masks changed
541  * to our bitmasks.
542 */
543 #define S_NO_SENSE(s) ((s).SenseKey == 0x0)
544 #define S_RECOVERED_ERROR(s) ((s).SenseKey == 0x1)
545
546 #define S_NOT_READY(s) ((s).SenseKey == 0x2)
547 #define S_MEDIUM_ERROR(s) ((s).SenseKey == 0x3)
548 #define S_HARDWARE_ERROR(s) ((s).SenseKey == 0x4)
549 #define S_UNIT_ATTENTION(s) ((s).SenseKey == 0x6)
550 #define S_BLANK_CHECK(s) ((s).SenseKey == 0x8)
551 #define S_VOLUME_OVERFLOW(s) ((s).SenseKey == 0xd)
552
553 #define DEFAULT_TIMEOUT 3*60  /* 3 minutes here */
554
555 #define HIT_FILEMARK(s) (S_NO_SENSE((s)) && (s).Filemark && (s).Valid)
556 /* Sigh, the T-10 SSC spec says all of the following is needed to
557  * detect a short read while in variable block mode. We'll see.
558  */
559 #define SHORT_READ(s) (S_NO_SENSE((s)) && (s).ILI && (s).Valid &&  (s).AdditionalSenseCode==0  && (s).AdditionalSenseCodeQualifier==0)
560
561 #define HIT_EOD(s) (S_BLANK_CHECK((s)) && (s).Valid)
562 #define HIT_EOP(s) (S_MEDIUM_ERROR((s)) && (s).EOM && (s).Valid)
563 #define HIT_EOM(s) ((s).EOM && (s).Valid)
564 #define BECOMING_READY(s) (S_UNIT_ATTENTION((s)) && (s).AdditionalSenseCode == 0x28 && (s).AdditionalSenseCodeQualifier == 0)
565
566 /* Reading is a problem. We can hit a filemark, hit an EOD, or hit an
567  * EOP. Our caller may do something about that. Note that we assume that
568  * our caller has already put us into fixed block mode. If he has not, then
569  * we are in trouble anyhow. 
570  */
571 int SCSI_readt(DEVICE_TYPE fd, char * buf, unsigned int bufsize, unsigned int *len, unsigned int timeout) {
572         int rtnval;
573         CDB_T cmd;
574
575         int blockCount;
576         int info;
577
578         RequestSense_T RequestSense;
579
580         if (bufsize==0)
581         {
582                 /* we are in variable block mode */
583                 blockCount=MAX_READ_SIZE; /* variable block size. */
584         }
585         else
586         {
587                 blockCount= *len / bufsize;
588                 if ((*len % bufsize) != 0)
589                 {
590                         fprintf(stderr,"Error: Data (%d bytes) not even multiple of block size (%d bytes).\n",*len,bufsize);
591                         exit(1); /* we're finished, sigh. */
592                 }
593         }
594
595         if (timeout == 0)
596         {
597                 timeout = 1 * 60; /* 1 minutes */
598         }
599
600         memset(&cmd, 0, sizeof(CDB_T));
601         cmd[0] = 0x08; /* READ */
602         cmd[1] = (bufsize) ? 1 : 0; /* fixed length or var length blocks */
603         cmd[2] = (unsigned char)(blockCount >> 16); /* MSB */
604         cmd[3] = (unsigned char)(blockCount >> 8);
605         cmd[4] = (unsigned char)blockCount; /* LSB */
606
607         /* okay, let's read, look @ the result code: */
608         rtnval=READ_OK;
609         if (SCSI_ExecuteCommand(fd,Input,&cmd,6,buf,(bufsize) ? *len : MAX_READ_SIZE,&RequestSense))
610         {
611                 rtnval=READ_ERROR;
612                 if (HIT_EOP(RequestSense))
613                 {
614                         cmd[0]=0x08;
615                         rtnval=READ_EOP;
616                 }
617
618                 if (HIT_FILEMARK(RequestSense))
619                 {
620                         rtnval=READ_FILEMARK;
621                 }
622
623                 if (HIT_EOD(RequestSense))
624                 {
625                         rtnval=READ_EOD;
626                 }
627
628                 if ( (bufsize==0) && SHORT_READ(RequestSense))
629                 {
630                         rtnval=READ_SHORT; /* we only do short reads for variable block mode */
631                 }
632
633                 if (rtnval != READ_ERROR)
634                 {
635                         /* info contains number of blocks or bytes *not* read. May be 
636                         negative if the block we were trying to read was too big. So
637                         we will have to account for that and set it to zero if so, so that
638                         we return the proper # of blocks read. 
639                         */
640                         info = ((RequestSense.Information[0] << 24) +
641                                         (RequestSense.Information[1] << 16) +
642                                         (RequestSense.Information[2] << 8) +
643                                          RequestSense.Information[3]);
644
645                         /* on 64-bit platforms, we may need to turn 'info' into a negative # */
646                         if (info > 0x7fffffff)
647                                 info = 0;
648
649                         if (info < 0)
650                                 info=0;  /* make sure  we don't return too big len read. */
651
652                         /* Now set *len to # of bytes read. */
653                         *len= bufsize ? (blockCount-info) * bufsize : MAX_READ_SIZE-info ;
654                 }
655                 else
656                 {
657                         PrintRequestSense(&RequestSense);
658                         exit(1);  /* foo. */
659                 }
660         }
661
662         return rtnval;
663 }
664
665 /* Low level SCSI write. Modified from BRU 16.1,  with much BRU smarts
666  * taken out and with the various types changed to mtx types rather than
667  * BRU types.
668  */ 
669 int SCSI_write(DEVICE_TYPE fd, char * buf, unsigned int blocksize,
670                                 unsigned int *len)
671 {
672         CDB_T cmd;
673
674         int blockCount;
675         int rtnval=0;
676         RequestSense_T RequestSense;
677
678         if (blocksize == 0)
679         {
680                 /* we are in variable block mode */
681                 blockCount = *len; /* variable block size. */
682         }
683         else
684         {
685                 blockCount= *len / blocksize ;
686                 if ((*len % blocksize) != 0)
687                 {
688                         fprintf(stderr,"Error: Data (%d bytes) not even multiple of block size (%d bytes).\n",*len,blocksize);
689                         exit(1); /* we're finished, sigh. */
690                 }
691         }
692
693         fprintf(stderr,"Writing %d blocks\n",blockCount);
694
695         memset(&cmd, 0, sizeof(CDB_T));
696         cmd[0] = 0x0a; /* WRITE */
697         cmd[1] = (blocksize) ? 1 : 0; /* fixed length or var length blocks */
698         cmd[2] = (unsigned char)(blockCount >> 16); /* MSB */
699         cmd[3] = (unsigned char)(blockCount >> 8);
700         cmd[4] = (unsigned char)blockCount; /* LSB */
701
702
703         if (SCSI_ExecuteCommand(fd,Output,&cmd,6,buf, *len, &RequestSense))
704         {
705                 if (HIT_EOM(RequestSense))
706                 {
707                         /* we hit end of media. Return -1. */
708                         if (S_VOLUME_OVERFLOW(RequestSense))
709                         {
710                                 exit(WRITE_EOV);
711                         }
712                         exit(WRITE_EOM); /* end of media! */
713                 }
714                 else
715                 {
716                         /* it was plain old write error: */
717                         PrintRequestSense(&RequestSense);
718                         exit(WRITE_ERROR);
719                 }
720         }
721         else
722         {
723                 rtnval = *len; /* worked! */
724         }
725         return rtnval;
726 }
727
728 /* S_write is not implemented yet! */
729 static int S_write(void)
730 {
731         char *buffer; /* the buffer we're gonna read/write out of. */
732         int buffersize;
733         int len; /* the length of the data in the buffer */
734         int blocksize = arg[0];
735         int numblocks = arg[1];
736         int varsize=0; /* variable size block flag */
737         int result;
738         int eof_input;
739         int infile=fileno(stdin); /* sigh */
740
741         if (blocksize == 0)
742         {
743                 varsize = 1;
744                 buffersize = MAX_READ_SIZE;
745                 len = MAX_READ_SIZE;
746         }
747         else
748         {
749                 varsize = 0; /* fixed block mode */
750                 buffersize = blocksize;
751                 len = blocksize;
752         }
753
754         /* sigh, make it oversized just to have some */  
755         buffer = malloc(buffersize+8); 
756
757         eof_input = 0;
758         while (!eof_input)
759         {
760                 /* size_t could be 64 bit on a 32 bit platform, so do casts. */
761                 len=0;
762                 /* If it is a pipe, we could read 4096 bytes rather than the full
763                 * 128K bytes or whatever, so we must gather multiple reads into
764                 * the buffer.
765                 */
766                 while (len < buffersize)
767                 {
768                         result=(int)read(infile, buffer + len, (size_t)(buffersize - len));
769                         if (!result)
770                         {
771                                 eof_input = 1;
772                                 if (!len)
773                                 {
774                                         /* if we have no deata in our buffer, exit */
775                                         return 0; /* we're at end of file! */
776                                 }
777                                 break;          /* otherwise, break and write the data */
778                         }
779                         len += result;  /* add the result input to our length. */
780                 }
781
782                 result = SCSI_write(MediumChangerFD, buffer, blocksize, (unsigned int *)&len);
783                 if (!result)
784                 {
785                         return 1; /* at end of tape! */
786                 }
787
788                 /* Now see if we have numbytes or numblocks. If so, we may wish to exit
789                 this loop.
790                 */
791                 if (arg[1])
792                 {
793                         if (varsize)
794                         {
795                                 /***BUG***/
796                                 return 0; /* we will only write one block in variable size mode :-( */
797                         }
798                         else
799                         {
800                                 if (numblocks)
801                                 {
802                                         numblocks--;
803                                 }
804                                 else
805                                 {
806                                         return 0; /* we're done. */
807                                 }
808                         }
809                 }
810         }
811         /* and done! */
812         return 0;
813 }
814
815 /* Okay, the read thingy: */
816
817 /* We have a device opened (we hope!) by the parser. 
818  * we will have arg[0] and arg[1] being the blocksize and # of blocks
819  * (respectively).
820  */ 
821
822
823 static int S_read(void)
824 {
825         char *buffer; /* the buffer we're going to be reading out of */
826         int buffersize;
827         unsigned int len; /* the length of the data in the buffer */
828         int blocksize = arg[0];
829         int numblocks = arg[1];
830         int varsize = 0; /* variable size block flag. */
831
832         int result;
833
834         int outfile=fileno(stdout); /* sigh. */
835
836         if (blocksize == 0)
837         {
838                 varsize=1;
839                 buffersize=MAX_READ_SIZE;
840                 len=MAX_READ_SIZE;
841         }
842         else
843         {
844                 varsize=0; /* fixed block mode */
845                 buffersize=blocksize;
846                 len=blocksize;
847         }
848
849         /* sigh, make it oversized just to have some */  
850         buffer = malloc(buffersize + 8); 
851
852         for ( ; ; )
853         {
854                 if (varsize)
855                 {
856                         /* it could have gotten reset by prior short read... */
857                         len=MAX_READ_SIZE; 
858                 }
859                 result=SCSI_readt(MediumChangerFD,buffer,blocksize, &len, DEFAULT_TIMEOUT);
860
861                 if (result==READ_FILEMARK || result==READ_EOD || result==READ_EOP)
862                 {
863                         /* okay, normal end of file? */
864                         if (len > 0)
865                         {
866                                 write(outfile,buffer,len);
867                         }
868
869 #ifdef NEED_TO_GO_PAST_FILEMARK
870                         /* Now, let's try to go past the filemark if that's what we hit: */
871                         if (result==READ_FILEMARK)
872                         {
873                                 arg1 = 1;       /* arg for S_fsf. */
874                                 S_fsf();        /* and go forward 1 filemark, we hope! */
875                         }
876 #endif
877                         return 0; /* hit normal end of file. */
878                 }
879                 else if (result == READ_SHORT)
880                 {
881                         /* short reads are only valid in variable block mode. */
882                         if (varsize)
883                         {
884                                 if (len > 0)
885                                 {
886                                         write(outfile,buffer,len);
887                                 }
888                         }
889                         else
890                         {
891                                 fprintf(stderr,"scsitape:Short Read encountered on input. Aborting.\n");
892                                 fflush(stderr);
893                                 exit(1); /* error exit! */
894                         }
895                 }
896                 else if (result == READ_OK)
897                 {
898                         write(outfile,buffer,len);
899                 }
900                 else
901                 {
902                         fprintf(stderr,"scsitape:Read Error\n");
903                         fflush(stderr);
904                         exit(1);
905                 }
906
907                 /* Now see if we have numbytes or numblocks: if so, we may wish to
908                  * exit this loop.
909                  */
910                 if (arg[1])
911                 {
912                         if (varsize)
913                         {
914                                 /****BUG****/ 
915                                 return 0; /* we're only reading one block in var size mode! */
916                         }
917                         else if (numblocks)
918                         {
919                                 numblocks--;
920                         }
921                         else
922                         {
923                                 return 0; /* we're done. */
924                         }
925                 }
926         }
927 }
928
929
930 /* See parse_args for the scoop. parse_args does all. */
931 int main(int argc, char **argv)
932 {
933         argv0 = argv[0];
934         parse_args(argc, argv);
935
936         if (device)
937                 SCSI_CloseDevice(device,MediumChangerFD);
938
939         exit(0);
940 }