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