/* We need F_SETSIG, (signal redirect), so following define */ #define _GNU_SOURCE 1 #include #include #include #include #include #include #include #include #include #include #include #include #include "sg_lib.h" #include "sg_linux_inc.h" /* Test code for the extensions to the Linux OS SCSI generic ("sg") device driver. * Copyright (C) 1999 D. Gilbert and P. Allworth * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. This program is a specialization of the Unix "dd" command in which one or both of the given files is a scsi generic device. A block size ('bs') is assumed to be 512 if not given. This program complains if 'ibs' or 'obs' are given with some other value than 'bs'. If 'if' is not given or 'if=-' then stdin is assumed. If 'of' is not given of 'of=-' then stdout assumed. The multipliers "c, b, k, m" are recognized on numeric arguments. A non-standard argument "bpt" (blocks per transfer) is added to control the maximum number of blocks in each transfer. The default value is 128. For example if "bs=512" and "bpt=32" then a maximum of 32 blocks (16KB in this case) are transferred to or from the sg device in a single SCSI command. BEWARE: If the 'of' file is a 'sg' device (eg a disk) then it _will_ be written to, potentially destroying its previous contents. This version should compile with Linux sg drivers with version numbers >= 30000 . Also this version tries to use real time signals. Version 3.99 20020126 6 byte commands [READ: 0x08, WRITE: 0x0a]: [cmd ][had|lu][midAdd][lowAdd][count ][flags ] 10 byte commands [EREAD: 0x28, EWRITE: 0x2a, READ_CAPACITY 0x25]: [cmd ][ |lu][hiAddr][hmAddr][lmAddr][lowAdd][ ][hiCnt ][lowCnt][flags ] 12 byte commands [LREAD: 0xd8, LWRITE: 0xda]: [cmd ][ |lu][hiAddr][hmAddr][lmAddr][lowAdd][hiCnt ][hmCnt ][lmCnt ][lowCnt] ... [ ][flags ] */ #define DEF_BLOCK_SIZE 512 #define DEF_BLOCKS_PER_TRANSFER 128 /* #define SG_DEBUG */ #define SENSE_BUFF_LEN 32 /* Arbitrary, could be larger */ #define DEF_TIMEOUT 40000 /* 40,000 millisecs == 40 seconds */ #define S_RW_LEN 10 /* Use SCSI READ(10) and WRITE(10) */ #define SGQ_MAX_RD_AHEAD 4 #define SGQ_MAX_WR_AHEAD 4 #define SGQ_NUM_ELEMS (SGQ_MAX_RD_AHEAD+ SGQ_MAX_WR_AHEAD + 1) #define SGQ_FREE 0 #define SGQ_IO_STARTED 1 #define SGQ_IO_FINISHED 2 #define SGQ_IO_ERR 3 #define SGQ_IO_WAIT 4 #define SGQ_CAN_DO_NOTHING 0 /* only temporarily in use */ #define SGQ_CAN_READ 1 #define SGQ_CAN_WRITE 2 #define SGQ_TIMEOUT 4 #define STR_SZ 1024 #define INOUTF_SZ 512 #define EBUFF_SZ 512 typedef struct request_element { struct request_element * nextp; int state; int wr; int blk; int num_blks; unsigned char * buffp; sg_io_hdr_t io_hdr; unsigned char cmd[S_RW_LEN]; unsigned char sb[SENSE_BUFF_LEN]; int result; int stop_after_wr; } Rq_elem; typedef struct request_collection { int infd; int in_is_sg; int in_blk; /* most recent read */ int in_count; /* most recent read */ int in_done_count; /* count of completed in blocks */ int in_partial; int outfd; int out_is_sg; int lowest_seek; int out_blk; /* most recent write */ int out_count; /* most recent write */ int out_done_count; /* count of completed out blocks */ int out_partial; int bs; int bpt; int dio; int dio_incomplete; int sum_of_resids; int debug; sigset_t blocked_sigs; int sigs_waiting; Rq_elem * rd_posp; Rq_elem * wr_posp; Rq_elem elem[SGQ_NUM_ELEMS]; } Rq_coll; void usage() { printf("Usage: " "sgs_dd [if=] [skip=] [of=] [seek=]\n" " [bs=] [bpt=] [count=]" " [dio=] [deb=]\n" " either 'if' or 'of' must be a scsi generic device\n" " 'bpt' is blocks_per_transfer (default is 128)\n" " 'dio' is direct IO, 1->attempt, 0->indirect IO (def)\n" " 'deb' is debug, 1->output some, 0->no debug (def)\n"); } /* Return of 0 -> success, -1 -> failure, 2 -> try again */ int read_capacity(int sg_fd, int * num_sect, int * sect_sz) { int res; unsigned char rcCmdBlk [10] = {0x25, 0, 0, 0, 0, 0, 0, 0, 0, 0}; unsigned char rcBuff[64]; unsigned char sense_b[64]; sg_io_hdr_t io_hdr; memset(&io_hdr, 0, sizeof(sg_io_hdr_t)); io_hdr.interface_id = 'S'; io_hdr.cmd_len = sizeof(rcCmdBlk); io_hdr.mx_sb_len = sizeof(sense_b); io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; io_hdr.dxfer_len = sizeof(rcBuff); io_hdr.dxferp = rcBuff; io_hdr.cmdp = rcCmdBlk; io_hdr.sbp = sense_b; io_hdr.timeout = DEF_TIMEOUT; if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) { perror("read_capacity (SG_IO) error"); return -1; } res = sg_err_category3(&io_hdr); if (SG_LIB_CAT_UNIT_ATTENTION == res) return 2; /* probably have another go ... */ else if (SG_LIB_CAT_CLEAN != res) { sg_chk_n_print3("read capacity", &io_hdr); return -1; } *num_sect = 1 + ((rcBuff[0] << 24) | (rcBuff[1] << 16) | (rcBuff[2] << 8) | rcBuff[3]); *sect_sz = (rcBuff[4] << 24) | (rcBuff[5] << 16) | (rcBuff[6] << 8) | rcBuff[7]; #ifdef SG_DEBUG printf("number of sectors=%d, sector size=%d\n", *num_sect, *sect_sz); #endif return 0; } /* -ve -> unrecoverable error, 0 -> successful, 1 -> recoverable (ENOMEM) */ int sg_start_io(Rq_coll * clp, Rq_elem * rep) { sg_io_hdr_t * hp = &rep->io_hdr; int res; memset(rep->cmd, 0, sizeof(rep->cmd)); rep->cmd[0] = rep->wr ? 0x2a : 0x28; rep->cmd[2] = (unsigned char)((rep->blk >> 24) & 0xFF); rep->cmd[3] = (unsigned char)((rep->blk >> 16) & 0xFF); rep->cmd[4] = (unsigned char)((rep->blk >> 8) & 0xFF); rep->cmd[5] = (unsigned char)(rep->blk & 0xFF); rep->cmd[7] = (unsigned char)((rep->num_blks >> 8) & 0xff); rep->cmd[8] = (unsigned char)(rep->num_blks & 0xff); memset(hp, 0, sizeof(sg_io_hdr_t)); hp->interface_id = 'S'; hp->cmd_len = sizeof(rep->cmd); hp->cmdp = rep->cmd; hp->dxfer_direction = rep->wr ? SG_DXFER_TO_DEV : SG_DXFER_FROM_DEV; hp->dxfer_len = clp->bs * rep->num_blks; hp->dxferp = rep->buffp; hp->mx_sb_len = sizeof(rep->sb); hp->sbp = rep->sb; hp->timeout = DEF_TIMEOUT; hp->usr_ptr = rep; hp->pack_id = rep->blk; if (clp->dio) hp->flags |= SG_FLAG_DIRECT_IO; #ifdef SG_DEBUG printf("sg_start_io: SCSI %s, blk=%d num_blks=%d\n", rep->wr ? "WRITE" : "READ", rep->blk, rep->num_blks); sg_print_command(hp->cmdp); printf("dir=%d, len=%d, dxfrp=%p, cmd_len=%d\n", hp->dxfer_direction, hp->dxfer_len, hp->dxferp, hp->cmd_len); #endif while (((res = write(rep->wr ? clp->outfd : clp->infd, hp, sizeof(sg_io_hdr_t))) < 0) && (EINTR == errno)) ; if (res < 0) { if (ENOMEM == errno) return 1; if ((EDOM == errno) || (EAGAIN == errno)) { rep->state = SGQ_IO_WAIT; /* busy so wait */ return 0; } perror("starting io on sg device, error"); rep->state = SGQ_IO_ERR; return res; } rep->state = SGQ_IO_STARTED; clp->sigs_waiting++; return 0; } /* -1 -> unrecoverable error, 0 -> successful, 1 -> try again */ int sg_finish_io(Rq_coll * clp, int wr, Rq_elem ** repp) { int res; sg_io_hdr_t io_hdr; sg_io_hdr_t * hp; Rq_elem * rep; memset(&io_hdr, 0 , sizeof(sg_io_hdr_t)); while (((res = read(wr ? clp->outfd : clp->infd, &io_hdr, sizeof(sg_io_hdr_t))) < 0) && (EINTR == errno)) ; rep = (Rq_elem *)io_hdr.usr_ptr; if (res < 0) { perror("finishing io on sg device, error"); rep->state = SGQ_IO_ERR; return -1; } if (! (rep && (SGQ_IO_STARTED == rep->state))) { printf("sg_finish_io: bad usr_ptr\n"); rep->state = SGQ_IO_ERR; return -1; } memcpy(&rep->io_hdr, &io_hdr, sizeof(sg_io_hdr_t)); hp = &rep->io_hdr; if (repp) *repp = rep; switch (sg_err_category3(hp)) { case SG_LIB_CAT_CLEAN: break; case SG_LIB_CAT_RECOVERED: printf("Recovered error on block=%d, num=%d\n", rep->blk, rep->num_blks); break; case SG_LIB_CAT_UNIT_ATTENTION: return 1; default: sg_chk_n_print3(rep->wr ? "writing": "reading", hp); rep->state = SGQ_IO_ERR; return -1; } if (clp->dio && ((hp->info & SG_INFO_DIRECT_IO_MASK) != SG_INFO_DIRECT_IO)) ++clp->dio_incomplete; /* count dios done as indirect IO */ clp->sum_of_resids += hp->resid; rep->state = SGQ_IO_FINISHED; #ifdef SG_DEBUG printf("sg_finish_io: %s ", wr ? "writing" : "reading"); printf(" SGQ_IO_FINISHED elem idx=%d\n", rep - clp->elem); #endif return 0; } int sz_reserve(int fd, int bs, int bpt) { int res, t, flags; res = ioctl(fd, SG_GET_VERSION_NUM, &t); if ((res < 0) || (t < 30000)) { printf("sgs_dd: sg driver prior to 3.x.y\n"); return 1; } res = 0; t = bs * bpt; res = ioctl(fd, SG_SET_RESERVED_SIZE, &t); if (res < 0) perror("sgs_dd: SG_SET_RESERVED_SIZE error"); if (-1 == fcntl(fd, F_SETOWN, getpid())) { perror("fcntl(,F_SETOWN,)"); return 1; } flags = fcntl(fd, F_GETFL, 0); if (-1 == fcntl(fd, F_SETFL, flags | O_ASYNC)) { perror("fcntl(,F_SETFL,)"); return 1; } fcntl(fd, F_SETSIG, SIGRTMIN + 1); return 0; } void init_elems(Rq_coll * clp) { Rq_elem * rep; int k; clp->wr_posp = &clp->elem[0]; /* making ring buffer */ clp->rd_posp = clp->wr_posp; for (k = 0; k < SGQ_NUM_ELEMS - 1; ++k) clp->elem[k].nextp = &clp->elem[k + 1]; clp->elem[SGQ_NUM_ELEMS - 1].nextp = &clp->elem[0]; for (k = 0; k < SGQ_NUM_ELEMS; ++k) { rep = &clp->elem[k]; rep->state = SGQ_FREE; if (NULL == (rep->buffp = malloc(clp->bpt * clp->bs))) printf("out of memory creating user buffers\n"); } } int start_read(Rq_coll * clp) { int blocks = (clp->in_count > clp->bpt) ? clp->bpt : clp->in_count; Rq_elem * rep = clp->rd_posp; int buf_sz, res; char ebuff[EBUFF_SZ]; #ifdef SG_DEBUG printf("start_read, elem idx=%d\n", rep - clp->elem); #endif rep->wr = 0; rep->blk = clp->in_blk; rep->num_blks = blocks; clp->in_blk += blocks; clp->in_count -= blocks; if (clp->in_is_sg) { res = sg_start_io(clp, rep); if (1 == res) { /* ENOMEM, find what's available+try that */ if ((res = ioctl(clp->infd, SG_GET_RESERVED_SIZE, &buf_sz)) < 0) { perror("RESERVED_SIZE ioctls failed"); return res; } clp->bpt = (buf_sz + clp->bs - 1) / clp->bs; printf("Reducing blocks per transfer to %d\n", clp->bpt); if (clp->bpt < 1) return -ENOMEM; res = sg_start_io(clp, rep); if (1 == res) res = -ENOMEM; } else if (res < 0) { printf("sgs_dd inputting from sg failed, blk=%d\n", rep->blk); rep->state = SGQ_IO_ERR; return res; } } else { rep->state = SGQ_IO_STARTED; while (((res = read(clp->infd, rep->buffp, blocks * clp->bs)) < 0) && (EINTR == errno)) ; if (res < 0) { snprintf(ebuff, EBUFF_SZ, "sgs_dd: reading, in_blk=%d ", rep->blk); perror(ebuff); rep->state = SGQ_IO_ERR; return res; } if (res < blocks * clp->bs) { int o_blocks = blocks; rep->stop_after_wr = 1; blocks = res / clp->bs; if ((res % clp->bs) > 0) { blocks++; clp->in_partial++; } /* Reverse out + re-apply blocks on clp */ clp->in_blk -= o_blocks; clp->in_count += o_blocks; rep->num_blks = blocks; clp->in_blk += blocks; clp->in_count -= blocks; } clp->in_done_count -= blocks; rep->state = SGQ_IO_FINISHED; } clp->rd_posp = rep->nextp; return blocks; } int start_write(Rq_coll * clp) { Rq_elem * rep = clp->wr_posp; int res, blocks; char ebuff[EBUFF_SZ]; while ((0 != rep->wr) || (SGQ_IO_FINISHED != rep->state)) { rep = rep->nextp; if (rep == clp->rd_posp) return -1; } #ifdef SG_DEBUG printf("start_write, elem idx=%d\n", rep - clp->elem); #endif rep->wr = 1; blocks = rep->num_blks; rep->blk = clp->out_blk; clp->out_blk += blocks; clp->out_count -= blocks; if (clp->out_is_sg) { res = sg_start_io(clp, rep); if (1 == res) /* ENOMEM, give up */ return -ENOMEM; else if (res < 0) { printf("sgs_dd output to sg failed, blk=%d\n", rep->blk); rep->state = SGQ_IO_ERR; return res; } } else { rep->state = SGQ_IO_STARTED; while (((res = write(clp->outfd, rep->buffp, rep->num_blks * clp->bs)) < 0) && (EINTR == errno)) ; if (res < 0) { snprintf(ebuff, EBUFF_SZ, "sgs_dd: output, out_blk=%d ", rep->blk); perror(ebuff); rep->state = SGQ_IO_ERR; return res; } if (res < blocks * clp->bs) { blocks = res / clp->bs; if ((res % clp->bs) > 0) { blocks++; clp->out_partial++; } rep->num_blks = blocks; } rep->state = SGQ_IO_FINISHED; } return blocks; } int do_poll(Rq_coll * clp, int fd) { struct pollfd a_pollfd = {0, POLLIN | POLLOUT, 0}; siginfo_t info; a_pollfd.fd = fd; if (poll(&a_pollfd, 1, 0) < 0) { perror("poll error"); return 0; } /* printf("do_poll: revents=0x%x\n", (int)a_pollfd.revents); */ if (a_pollfd.revents & POLLIN) { if (clp->sigs_waiting) { while (sigwaitinfo(&clp->blocked_sigs, &info) < 0) { if (EINTR != errno) { perror("sigwaitinfo"); /* consume signal */ return -1; } } if ((SIGRTMIN + 1) == info.si_signo) clp->sigs_waiting--; if (SIGIO == info.si_signo) { printf("SIGIO received, continue\n"); clp->sigs_waiting = 0; } else return -1; } return 1; } else return 0; } int can_read_write(Rq_coll * clp) { Rq_elem * rep = NULL; int res = 0; int reading = 0; int writing = 0; int writeable = 0; int rd_waiting = 0; int wr_waiting = 0; int sg_finished = 0; siginfo_t info; /* if write completion pending, then complete it + start read */ if (clp->out_is_sg) { while ((res = do_poll(clp, clp->outfd))) { if (res < 0) return res; res = sg_finish_io(clp, 1, &rep); if (res < 0) return res; else if (1 == res) { res = sg_start_io(clp, rep); if (0 != res) return -1; /* give up if any problems with retry */ } else sg_finished++; } while ((rep = clp->wr_posp) && (SGQ_IO_FINISHED == rep->state) && (1 == rep->wr) && (rep != clp->rd_posp)) { rep->state = SGQ_FREE; clp->out_done_count -= rep->num_blks; clp->wr_posp = rep->nextp; if (rep->stop_after_wr) return -1; } } else if ((rep = clp->wr_posp) && (1 == rep->wr) && (SGQ_IO_FINISHED == rep->state)) { rep->state = SGQ_FREE; clp->out_done_count -= rep->num_blks; clp->wr_posp = rep->nextp; if (rep->stop_after_wr) return -1; } /* if read completion pending, then complete it + start maybe write */ if (clp->in_is_sg) { while ((res = do_poll(clp, clp->infd))) { if (res < 0) return res; res = sg_finish_io(clp, 0, &rep); if (res < 0) return res; if (1 == res) { res = sg_start_io(clp, rep); if (0 != res) return -1; /* give up if any problems with retry */ } else { sg_finished++; clp->in_done_count -= rep->num_blks; } } } for (rep = clp->wr_posp, res = 1; rep != clp->rd_posp; rep = rep->nextp) { if (SGQ_IO_STARTED == rep->state) { if (rep->wr) ++writing; else { res = 0; ++reading; } } else if ((0 == rep->wr) && (SGQ_IO_FINISHED == rep->state)) { if (res) writeable = 1; } else if (SGQ_IO_WAIT == rep->state) { res = 0; if (rep->wr) ++wr_waiting; else ++rd_waiting; } else res = 0; } if (clp->debug) { if ((clp->debug >= 9) || wr_waiting || rd_waiting) printf("%d/%d (nwb/nrb): read=%d/%d (do/wt) " "write=%d/%d (do/wt) writeable=%d sg_fin=%d\n", clp->out_blk, clp->in_blk, reading, rd_waiting, writing, wr_waiting, writeable, sg_finished); fflush(stdout); } if (writeable && (writing < SGQ_MAX_WR_AHEAD) && (clp->out_count > 0)) return SGQ_CAN_WRITE; if ((reading < SGQ_MAX_RD_AHEAD) && (clp->in_count > 0) && (0 == rd_waiting) && (clp->rd_posp->nextp != clp->wr_posp)) return SGQ_CAN_READ; if (clp->out_done_count <= 0) return SGQ_CAN_DO_NOTHING; /* usleep(10000); */ /* hang about for 10 milliseconds */ if (clp->sigs_waiting) { while (sigwaitinfo(&clp->blocked_sigs, &info) < 0) { if (EINTR != errno) { perror("sigwaitinfo"); /* consume signal */ return -1; } } if ((SIGRTMIN + 1) != info.si_signo) return -1; clp->sigs_waiting--; } /* Now check the _whole_ buffer for pending requests */ for (rep = clp->rd_posp->nextp; rep != clp->rd_posp; rep = rep->nextp) { if (SGQ_IO_WAIT == rep->state) { res = sg_start_io(clp, rep); if (res < 0) return res; if (res > 0) return -1; break; } } return SGQ_CAN_DO_NOTHING; } int main(int argc, char * argv[]) { int skip = 0; int seek = 0; int ibs = 0; int obs = 0; int count = -1; char str[STR_SZ]; char * key; char * buf; char inf[INOUTF_SZ]; char outf[INOUTF_SZ]; int res, k; int in_num_sect = 0; int out_num_sect = 0; int in_sect_sz, out_sect_sz, crw; char ebuff[EBUFF_SZ]; Rq_coll rcoll; memset(&rcoll, 0, sizeof(Rq_coll)); rcoll.bpt = DEF_BLOCKS_PER_TRANSFER; inf[0] = '\0'; outf[0] = '\0'; if (argc < 2) { usage(); return 1; } for(k = 1; k < argc; k++) { if (argv[k]) { strncpy(str, argv[k], STR_SZ); str[STR_SZ - 1] = '\0'; } else continue; for(key = str, buf = key; *buf && *buf != '=';) buf++; if (*buf) *buf++ = '\0'; if (strcmp(key,"if") == 0) strncpy(inf, buf, INOUTF_SZ); else if (strcmp(key,"of") == 0) strncpy(outf, buf, INOUTF_SZ); else if (0 == strcmp(key,"ibs")) ibs = sg_get_num(buf); else if (0 == strcmp(key,"obs")) obs = sg_get_num(buf); else if (0 == strcmp(key,"bs")) rcoll.bs = sg_get_num(buf); else if (0 == strcmp(key,"bpt")) rcoll.bpt = sg_get_num(buf); else if (0 == strcmp(key,"skip")) skip = sg_get_num(buf); else if (0 == strcmp(key,"seek")) seek = sg_get_num(buf); else if (0 == strcmp(key,"count")) count = sg_get_num(buf); else if (0 == strcmp(key,"dio")) rcoll.dio = sg_get_num(buf); else if (0 == strcmp(key,"deb")) rcoll.debug = sg_get_num(buf); else { printf("Unrecognized argument '%s'\n", key); usage(); return 1; } } if (rcoll.bs <= 0) { rcoll.bs = DEF_BLOCK_SIZE; printf("Assume default 'bs' (block size) of %d bytes\n", rcoll.bs); } if ((ibs && (ibs != rcoll.bs)) || (obs && (obs != rcoll.bs))) { printf("If 'ibs' or 'obs' given must be same as 'bs'\n"); usage(); return 1; } if ((skip < 0) || (seek < 0)) { printf("skip and seek cannot be negative\n"); return 1; } #ifdef SG_DEBUG printf("sgs_dd: if=%s skip=%d of=%s seek=%d count=%d\n", inf, skip, outf, seek, count); #endif rcoll.infd = STDIN_FILENO; rcoll.outfd = STDOUT_FILENO; if (inf[0] && ('-' != inf[0])) { if ((rcoll.infd = open(inf, O_RDONLY)) < 0) { snprintf(ebuff, EBUFF_SZ, "sgs_dd: could not open %s for reading", inf); perror(ebuff); return 1; } if (ioctl(rcoll.infd, SG_GET_TIMEOUT, 0) < 0) { rcoll.in_is_sg = 0; if (skip > 0) { off_t offset = skip; offset *= rcoll.bs; /* could overflow here! */ if (lseek(rcoll.infd, offset, SEEK_SET) < 0) { snprintf(ebuff, EBUFF_SZ, "sgs_dd: couldn't skip to required position on %s", inf); perror(ebuff); return 1; } } } else { /* looks like sg device so close then re-open it RW */ close(rcoll.infd); if ((rcoll.infd = open(inf, O_RDWR | O_NONBLOCK)) < 0) { printf("If %s is a sg device, need read+write permissions," " even to read it!\n", inf); return 1; } rcoll.in_is_sg = 1; if (sz_reserve(rcoll.infd, rcoll.bs, rcoll.bpt)) return 1; } } if (outf[0] && ('-' != outf[0])) { if ((rcoll.outfd = open(outf, O_RDWR | O_NONBLOCK)) >= 0) { if (ioctl(rcoll.outfd, SG_GET_TIMEOUT, 0) < 0) { /* not a scsi generic device so now try and open RDONLY */ close(rcoll.outfd); } else { rcoll.out_is_sg = 1; if (sz_reserve(rcoll.outfd, rcoll.bs, rcoll.bpt)) return 1; } } if (! rcoll.out_is_sg) { if ((rcoll.outfd = open(outf, O_WRONLY | O_CREAT, 0666)) < 0) { snprintf(ebuff, EBUFF_SZ, "sgs_dd: could not open %s for writing", outf); perror(ebuff); return 1; } else if (seek > 0) { off_t offset = seek; offset *= rcoll.bs; /* could overflow here! */ if (lseek(rcoll.outfd, offset, SEEK_SET) < 0) { snprintf(ebuff, EBUFF_SZ, "sgs_dd: couldn't seek to required position on %s", outf); perror(ebuff); return 1; } } } } if ((STDIN_FILENO == rcoll.infd) && (STDOUT_FILENO == rcoll.outfd)) { printf("Can't have both 'if' as stdin _and_ 'of' as stdout\n"); return 1; } if (! (rcoll.in_is_sg || rcoll.out_is_sg)) { printf("Either 'if' or 'of' must be a scsi generic device\n"); return 1; } if (0 == count) return 0; else if (count < 0) { if (rcoll.in_is_sg) { res = read_capacity(rcoll.infd, &in_num_sect, &in_sect_sz); if (2 == res) { printf("Unit attention, media changed(in), try again\n"); res = read_capacity(rcoll.infd, &in_num_sect, &in_sect_sz); } if (0 != res) { printf("Unable to read capacity on %s\n", inf); in_num_sect = -1; } else { if (in_num_sect > skip) in_num_sect -= skip; } } if (rcoll.out_is_sg) { res = read_capacity(rcoll.outfd, &out_num_sect, &out_sect_sz); if (2 == res) { printf("Unit attention, media changed(out), try again\n"); res = read_capacity(rcoll.outfd, &out_num_sect, &out_sect_sz); } if (0 != res) { printf("Unable to read capacity on %s\n", outf); out_num_sect = -1; } else { if (out_num_sect > seek) out_num_sect -= seek; } } #ifdef SG_DEBUG printf("Start of loop, count=%d, in_num_sect=%d, out_num_sect=%d\n", count, in_num_sect, out_num_sect); #endif if (in_num_sect > 0) { if (out_num_sect > 0) count = (in_num_sect > out_num_sect) ? out_num_sect : in_num_sect; else count = in_num_sect; } else count = out_num_sect; } #ifdef SG_DEBUG printf("Start of loop, count=%d, bpt=%d\n", count, rcoll.bpt); #endif sigemptyset(&rcoll.blocked_sigs); sigaddset(&rcoll.blocked_sigs, SIGRTMIN + 1); sigaddset(&rcoll.blocked_sigs, SIGINT); sigaddset(&rcoll.blocked_sigs, SIGIO); sigprocmask(SIG_BLOCK, &rcoll.blocked_sigs, 0); rcoll.in_count = count; rcoll.in_done_count = count; rcoll.in_blk = skip; rcoll.out_count = count; rcoll.out_done_count = count; rcoll.out_blk = seek; init_elems(&rcoll); /* vvvvvvvvvvvvvvvvv Main Loop vvvvvvvvvvvvvvvvvvvvvvvv */ while (rcoll.out_done_count > 0) { crw = can_read_write(&rcoll); if (crw < 0) break; if (SGQ_CAN_READ & crw) { res = start_read(&rcoll); if (res <= 0) { printf("start_read: res=%d\n", res); break; } } if (SGQ_CAN_WRITE & crw) { res = start_write(&rcoll); if (res <= 0) { printf("start_write: res=%d\n", res); break; } } } if (STDIN_FILENO != rcoll.infd) close(rcoll.infd); if (STDOUT_FILENO != rcoll.outfd) close(rcoll.outfd); if (0 != rcoll.out_count) { printf("Some error occurred, remaining blocks=%d\n", rcoll.out_count); return 1; } printf("%d+%d records in\n", count - rcoll.in_done_count, rcoll.in_partial); printf("%d+%d records out\n", count - rcoll.out_done_count, rcoll.out_partial); if (rcoll.dio_incomplete) printf(">> Direct IO requested but incomplete %d times\n", rcoll.dio_incomplete); if (rcoll.sum_of_resids) printf(">> Non-zero sum of residual counts=%d\n", rcoll.sum_of_resids); return 0; }