static inline int dd_infopen(const char *name, struct dd_s *dd) { FAR struct nsh_vtbl_s *vtbl = dd->vtbl; int ret; int type; /* Get the type of the input file */ type = dd_filetype(name); if (type < 0) { nsh_output(vtbl, g_fmtcmdfailed, g_dd, "stat", NSH_ERRNO_OF(-type)); return type; } /* Open the input file */ if (!type) { DD_INFD = open(name, O_RDONLY); if (DD_INFD < 0) { nsh_output(vtbl, g_fmtcmdfailed, g_dd, "open", NSH_ERRNO); return ERROR; } dd->infread = dd_readch; /* Character oriented read */ dd->infclose = dd_infclosech; } else { ret = bchlib_setup(name, true, &DD_INHANDLE); if (ret < 0) { return ERROR; } dd->infread = dd_readblk; dd->infclose = dd_infcloseblk; } return OK; }
static inline int dd_outfopen(const char *name, struct dd_s *dd) { int type; int ret = OK; /* Get the type of the output file */ type = dd_filetype(name); /* Open the block driver for input */ if (type == true) { ret = bchlib_setup(name, true, &DD_OUTHANDLE); if (ret < 0) { return ERROR; } dd->outfwrite = dd_writeblk; /* Block oriented write */ dd->outfclose = dd_outfcloseblk; } /* Otherwise, the file is character oriented or does not exist */ else { DD_OUTFD = open(name, O_WRONLY|O_CREAT|O_TRUNC, 0644); if (DD_OUTFD < 0) { FAR struct nsh_vtbl_s *vtbl = dd->vtbl; nsh_output(vtbl, g_fmtcmdfailed, g_dd, "open", NSH_ERRNO); return ERROR; } dd->outfwrite = dd_writech; /* Character oriented write */ dd->outfclose = dd_outfclosech; } return OK; }
int main(int argc, char * argv[]) { int64_t skip = 0; int64_t seek = 0; int ibs = 0; int obs = 0; int bpt = DEF_BLOCKS_PER_TRANSFER; int bpt_given = 0; char str[STR_SZ]; char * key; char * buf; char inf[INOUTF_SZ]; int in_type = FT_OTHER; char outf[INOUTF_SZ]; int out_type = FT_OTHER; int res, k, t; int infd, outfd, blocks; unsigned char * wrkPos; unsigned char * wrkBuff = NULL; unsigned char * wrkMmap = NULL; int64_t in_num_sect = -1; int in_res_sz = 0; int64_t out_num_sect = -1; int out_res_sz = 0; int scsi_cdbsz_in = DEF_SCSI_CDBSZ; int scsi_cdbsz_out = DEF_SCSI_CDBSZ; int cdbsz_given = 0; int do_coe = 0; /* dummy, just accept + ignore */ int do_sync = 0; int num_dio_not_done = 0; int in_sect_sz, out_sect_sz; int n, flags; char ebuff[EBUFF_SZ]; char b[80]; int blocks_per; size_t psz; struct flags_t in_flags; struct flags_t out_flags; int ret = 0; #if defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE) psz = sysconf(_SC_PAGESIZE); /* POSIX.1 (was getpagesize()) */ #else psz = 4096; /* give up, pick likely figure */ #endif inf[0] = '\0'; outf[0] = '\0'; memset(&in_flags, 0, sizeof(in_flags)); memset(&out_flags, 0, sizeof(out_flags)); for (k = 1; k < argc; k++) { if (argv[k]) strncpy(str, argv[k], STR_SZ); else continue; for (key = str, buf = key; *buf && *buf != '=';) buf++; if (*buf) *buf++ = '\0'; if (0 == strcmp(key,"bpt")) { bpt = sg_get_num(buf); if (-1 == bpt) { pr2serr(ME "bad argument to 'bpt'\n"); return SG_LIB_SYNTAX_ERROR; } bpt_given = 1; } else if (0 == strcmp(key,"bs")) { blk_sz = sg_get_num(buf); if (-1 == blk_sz) { pr2serr(ME "bad argument to 'bs'\n"); return SG_LIB_SYNTAX_ERROR; } } else if (0 == strcmp(key,"cdbsz")) { scsi_cdbsz_in = sg_get_num(buf); scsi_cdbsz_out = scsi_cdbsz_in; cdbsz_given = 1; } else if (0 == strcmp(key,"coe")) { do_coe = sg_get_num(buf); /* dummy, just accept + ignore */ if (do_coe) { ; /* unused, dummy to suppress warning */ } } else if (0 == strcmp(key,"count")) { if (0 != strcmp("-1", buf)) { dd_count = sg_get_llnum(buf); if (-1LL == dd_count) { pr2serr(ME "bad argument to 'count'\n"); return SG_LIB_SYNTAX_ERROR; } } /* treat 'count=-1' as calculate count (same as not given) */ } else if (0 == strcmp(key,"dio")) out_flags.dio = sg_get_num(buf); else if (0 == strcmp(key,"fua")) { n = sg_get_num(buf); if (n & 1) out_flags.fua = 1; if (n & 2) in_flags.fua = 1; } else if (0 == strcmp(key,"ibs")) { ibs = sg_get_num(buf); if (-1 == ibs) { pr2serr(ME "bad argument to 'ibs'\n"); return SG_LIB_SYNTAX_ERROR; } } else if (strcmp(key,"if") == 0) { if ('\0' != inf[0]) { pr2serr("Second 'if=' argument??\n"); return SG_LIB_SYNTAX_ERROR; } else strncpy(inf, buf, INOUTF_SZ); } else if (0 == strcmp(key, "iflag")) { if (process_flags(buf, &in_flags)) { pr2serr(ME "bad argument to 'iflag'\n"); return SG_LIB_SYNTAX_ERROR; } } else if (strcmp(key,"of") == 0) { if ('\0' != outf[0]) { pr2serr("Second 'of=' argument??\n"); return SG_LIB_SYNTAX_ERROR; } else strncpy(outf, buf, INOUTF_SZ); } else if (0 == strcmp(key, "oflag")) { if (process_flags(buf, &out_flags)) { pr2serr(ME "bad argument to 'oflag'\n"); return SG_LIB_SYNTAX_ERROR; } } else if (0 == strcmp(key,"obs")) { obs = sg_get_num(buf); if (-1 == obs) { pr2serr(ME "bad argument to 'obs'\n"); return SG_LIB_SYNTAX_ERROR; } } else if (0 == strcmp(key,"seek")) { seek = sg_get_llnum(buf); if (-1LL == seek) { pr2serr(ME "bad argument to 'seek'\n"); return SG_LIB_SYNTAX_ERROR; } } else if (0 == strcmp(key,"skip")) { skip = sg_get_llnum(buf); if (-1LL == skip) { pr2serr(ME "bad argument to 'skip'\n"); return SG_LIB_SYNTAX_ERROR; } } else if (0 == strcmp(key,"sync")) do_sync = sg_get_num(buf); else if (0 == strcmp(key,"time")) do_time = sg_get_num(buf); else if (0 == strncmp(key, "verb", 4)) verbose = sg_get_num(buf); else if ((0 == strncmp(key, "--help", 7)) || (0 == strcmp(key, "-h")) || (0 == strcmp(key, "-?"))) { usage(); return 0; } else if ((0 == strncmp(key, "--vers", 6)) || (0 == strcmp(key, "-V"))) { pr2serr(ME ": %s\n", version_str); return 0; } else { pr2serr("Unrecognized option '%s'\n", key); pr2serr("For more information use '--help'\n"); return SG_LIB_SYNTAX_ERROR; } } if (blk_sz <= 0) { blk_sz = DEF_BLOCK_SIZE; pr2serr("Assume default 'bs' (block size) of %d bytes\n", blk_sz); } if ((ibs && (ibs != blk_sz)) || (obs && (obs != blk_sz))) { pr2serr("If 'ibs' or 'obs' given must be same as 'bs'\n"); usage(); return SG_LIB_SYNTAX_ERROR; } if ((skip < 0) || (seek < 0)) { pr2serr("skip and seek cannot be negative\n"); return SG_LIB_SYNTAX_ERROR; } if ((out_flags.append > 0) && (seek > 0)) { pr2serr("Can't use both append and seek switches\n"); return SG_LIB_SYNTAX_ERROR; } if (bpt < 1) { pr2serr("bpt must be greater than 0\n"); return SG_LIB_SYNTAX_ERROR; } /* defaulting transfer size to 128*2048 for CD/DVDs is too large for the block layer in lk 2.6 and results in an EIO on the SG_IO ioctl. So reduce it in that case. */ if ((blk_sz >= 2048) && (0 == bpt_given)) bpt = DEF_BLOCKS_PER_2048TRANSFER; #ifdef SG_DEBUG pr2serr(ME "if=%s skip=%" PRId64 " of=%s seek=%" PRId64 " count=%" PRId64 "\n", inf, skip, outf, seek, dd_count); #endif install_handler (SIGINT, interrupt_handler); install_handler (SIGQUIT, interrupt_handler); install_handler (SIGPIPE, interrupt_handler); install_handler (SIGUSR1, siginfo_handler); infd = STDIN_FILENO; outfd = STDOUT_FILENO; if (inf[0] && ('-' != inf[0])) { in_type = dd_filetype(inf); if (verbose) pr2serr(" >> Input file type: %s\n", dd_filetype_str(in_type, ebuff)); if (FT_ERROR == in_type) { pr2serr(ME "unable to access %s\n", inf); return SG_LIB_FILE_ERROR; } else if (FT_ST == in_type) { pr2serr(ME "unable to use scsi tape device %s\n", inf); return SG_LIB_FILE_ERROR; } else if (FT_SG == in_type) { flags = O_RDWR | O_NONBLOCK; if (in_flags.direct) flags |= O_DIRECT; if (in_flags.excl) flags |= O_EXCL; if (in_flags.dsync) flags |= O_SYNC; if ((infd = open(inf, flags)) < 0) { snprintf(ebuff, EBUFF_SZ, ME "could not open %s for sg reading", inf); perror(ebuff); return SG_LIB_FILE_ERROR; } res = ioctl(infd, SG_GET_VERSION_NUM, &t); if ((res < 0) || (t < 30122)) { pr2serr(ME "sg driver prior to 3.1.22\n"); return SG_LIB_FILE_ERROR; } in_res_sz = blk_sz * bpt; if (0 != (in_res_sz % psz)) /* round up to next page */ in_res_sz = ((in_res_sz / psz) + 1) * psz; if (ioctl(infd, SG_GET_RESERVED_SIZE, &t) < 0) { perror(ME "SG_GET_RESERVED_SIZE error"); return SG_LIB_FILE_ERROR; } if (t < MIN_RESERVED_SIZE) t = MIN_RESERVED_SIZE; if (in_res_sz > t) { if (ioctl(infd, SG_SET_RESERVED_SIZE, &in_res_sz) < 0) { perror(ME "SG_SET_RESERVED_SIZE error"); return SG_LIB_FILE_ERROR; } } wrkMmap = (unsigned char *)mmap(NULL, in_res_sz, PROT_READ | PROT_WRITE, MAP_SHARED, infd, 0); if (MAP_FAILED == wrkMmap) { snprintf(ebuff, EBUFF_SZ, ME "error using mmap() on file: %s", inf); perror(ebuff); return SG_LIB_FILE_ERROR; } } else { flags = O_RDONLY; if (in_flags.direct) flags |= O_DIRECT; if (in_flags.excl) flags |= O_EXCL; if (in_flags.dsync) flags |= O_SYNC; if ((infd = open(inf, flags)) < 0) { snprintf(ebuff, EBUFF_SZ, ME "could not open %s for reading", inf); perror(ebuff); return SG_LIB_FILE_ERROR; } else if (skip > 0) { off64_t offset = skip; offset *= blk_sz; /* could exceed 32 bits here! */ if (lseek64(infd, offset, SEEK_SET) < 0) { snprintf(ebuff, EBUFF_SZ, ME "couldn't skip to " "required position on %s", inf); perror(ebuff); return SG_LIB_FILE_ERROR; } if (verbose) pr2serr(" >> skip: lseek64 SEEK_SET, byte offset=0x%" PRIx64 "\n", (uint64_t)offset); } } } if (outf[0] && ('-' != outf[0])) { out_type = dd_filetype(outf); if (verbose) pr2serr(" >> Output file type: %s\n", dd_filetype_str(out_type, ebuff)); if (FT_ST == out_type) { pr2serr(ME "unable to use scsi tape device %s\n", outf); return SG_LIB_FILE_ERROR; } else if (FT_SG == out_type) { flags = O_RDWR | O_NONBLOCK; if (out_flags.direct) flags |= O_DIRECT; if (out_flags.excl) flags |= O_EXCL; if (out_flags.dsync) flags |= O_SYNC; if ((outfd = open(outf, flags)) < 0) { snprintf(ebuff, EBUFF_SZ, ME "could not open %s for " "sg writing", outf); perror(ebuff); return SG_LIB_FILE_ERROR; } res = ioctl(outfd, SG_GET_VERSION_NUM, &t); if ((res < 0) || (t < 30122)) { pr2serr(ME "sg driver prior to 3.1.22\n"); return SG_LIB_FILE_ERROR; } if (ioctl(outfd, SG_GET_RESERVED_SIZE, &t) < 0) { perror(ME "SG_GET_RESERVED_SIZE error"); return SG_LIB_FILE_ERROR; } if (t < MIN_RESERVED_SIZE) t = MIN_RESERVED_SIZE; out_res_sz = blk_sz * bpt; if (out_res_sz > t) { if (ioctl(outfd, SG_SET_RESERVED_SIZE, &out_res_sz) < 0) { perror(ME "SG_SET_RESERVED_SIZE error"); return SG_LIB_FILE_ERROR; } } if (NULL == wrkMmap) { wrkMmap = (unsigned char *)mmap(NULL, out_res_sz, PROT_READ | PROT_WRITE, MAP_SHARED, outfd, 0); if (MAP_FAILED == wrkMmap) { snprintf(ebuff, EBUFF_SZ, ME "error using mmap() on file: %s", outf); perror(ebuff); return SG_LIB_FILE_ERROR; } } } else if (FT_DEV_NULL == out_type) outfd = -1; /* don't bother opening */ else { if (FT_RAW != out_type) { flags = O_WRONLY | O_CREAT; if (out_flags.direct) flags |= O_DIRECT; if (out_flags.excl) flags |= O_EXCL; if (out_flags.dsync) flags |= O_SYNC; if (out_flags.append) flags |= O_APPEND; if ((outfd = open(outf, flags, 0666)) < 0) { snprintf(ebuff, EBUFF_SZ, ME "could not open %s for writing", outf); perror(ebuff); return SG_LIB_FILE_ERROR; } } else { if ((outfd = open(outf, O_WRONLY)) < 0) { snprintf(ebuff, EBUFF_SZ, ME "could not open %s " "for raw writing", outf); perror(ebuff); return SG_LIB_FILE_ERROR; } } if (seek > 0) { off64_t offset = seek; offset *= blk_sz; /* could exceed 32 bits here! */ if (lseek64(outfd, offset, SEEK_SET) < 0) { snprintf(ebuff, EBUFF_SZ, ME "couldn't seek to " "required position on %s", outf); perror(ebuff); return SG_LIB_FILE_ERROR; } if (verbose) pr2serr(" >> seek: lseek64 SEEK_SET, byte offset=0x%" PRIx64 "\n", (uint64_t)offset); } } } if ((STDIN_FILENO == infd) && (STDOUT_FILENO == outfd)) { pr2serr("Won't default both IFILE to stdin _and_ OFILE to as " "stdout\n"); pr2serr("For more information use '--help'\n"); return SG_LIB_SYNTAX_ERROR; } if (dd_count < 0) { in_num_sect = -1; if (FT_SG == in_type) { res = scsi_read_capacity(infd, &in_num_sect, &in_sect_sz); if (SG_LIB_CAT_UNIT_ATTENTION == res) { pr2serr("Unit attention(in), continuing\n"); res = scsi_read_capacity(infd, &in_num_sect, &in_sect_sz); } else if (SG_LIB_CAT_ABORTED_COMMAND == res) { pr2serr("Aborted command(in), continuing\n"); res = scsi_read_capacity(infd, &in_num_sect, &in_sect_sz); } if (0 != res) { sg_get_category_sense_str(res, sizeof(b), b, verbose); pr2serr("Read capacity (if=%s): %s\n", inf, b); in_num_sect = -1; } } else if (FT_BLOCK == in_type) { if (0 != read_blkdev_capacity(infd, &in_num_sect, &in_sect_sz)) { pr2serr("Unable to read block capacity on %s\n", inf); in_num_sect = -1; } if (blk_sz != in_sect_sz) { pr2serr("block size on %s confusion; bs=%d, from device=%d\n", inf, blk_sz, in_sect_sz); in_num_sect = -1; } } if (in_num_sect > skip) in_num_sect -= skip; out_num_sect = -1; if (FT_SG == out_type) { res = scsi_read_capacity(outfd, &out_num_sect, &out_sect_sz); if (SG_LIB_CAT_UNIT_ATTENTION == res) { pr2serr("Unit attention(out), continuing\n"); res = scsi_read_capacity(outfd, &out_num_sect, &out_sect_sz); } else if (SG_LIB_CAT_ABORTED_COMMAND == res) { pr2serr("Aborted command(out), continuing\n"); res = scsi_read_capacity(outfd, &out_num_sect, &out_sect_sz); } if (0 != res) { sg_get_category_sense_str(res, sizeof(b), b, verbose); pr2serr("Read capacity (of=%s): %s\n", inf, b); out_num_sect = -1; } } else if (FT_BLOCK == out_type) { if (0 != read_blkdev_capacity(outfd, &out_num_sect, &out_sect_sz)) { pr2serr("Unable to read block capacity on %s\n", outf); out_num_sect = -1; } if (blk_sz != out_sect_sz) { pr2serr("block size on %s confusion: bs=%d, from device=%d\n", outf, blk_sz, out_sect_sz); out_num_sect = -1; } } if (out_num_sect > seek) out_num_sect -= seek; #ifdef SG_DEBUG pr2serr("Start of loop, count=%" PRId64 ", in_num_sect=%" PRId64 ", " "out_num_sect=%" PRId64 "\n", dd_count, in_num_sect, out_num_sect); #endif if (in_num_sect > 0) { if (out_num_sect > 0) dd_count = (in_num_sect > out_num_sect) ? out_num_sect : in_num_sect; else dd_count = in_num_sect; } else dd_count = out_num_sect; } if (dd_count < 0) { pr2serr("Couldn't calculate count, please give one\n"); return SG_LIB_SYNTAX_ERROR; } if (! cdbsz_given) { if ((FT_SG == in_type) && (MAX_SCSI_CDBSZ != scsi_cdbsz_in) && (((dd_count + skip) > UINT_MAX) || (bpt > USHRT_MAX))) { pr2serr("Note: SCSI command size increased to 16 bytes (for " "'if')\n"); scsi_cdbsz_in = MAX_SCSI_CDBSZ; } if ((FT_SG == out_type) && (MAX_SCSI_CDBSZ != scsi_cdbsz_out) && (((dd_count + seek) > UINT_MAX) || (bpt > USHRT_MAX))) { pr2serr("Note: SCSI command size increased to 16 bytes (for " "'of')\n"); scsi_cdbsz_out = MAX_SCSI_CDBSZ; } } if (out_flags.dio && (FT_SG != in_type)) { out_flags.dio = 0; pr2serr(">>> dio only performed on 'of' side when 'if' is an sg " "device\n"); } if (out_flags.dio) { int fd; char c; if ((fd = open(proc_allow_dio, O_RDONLY)) >= 0) { if (1 == read(fd, &c, 1)) { if ('0' == c) pr2serr(">>> %s set to '0' but should be set to '1' for " "direct IO\n", proc_allow_dio); } close(fd); } } if (wrkMmap) { wrkPos = wrkMmap; } else { if ((FT_RAW == in_type) || (FT_RAW == out_type)) { wrkBuff = (unsigned char *)malloc(blk_sz * bpt + psz); if (0 == wrkBuff) { pr2serr("Not enough user memory for raw\n"); return SG_LIB_FILE_ERROR; } /* perhaps use posix_memalign() instead */ wrkPos = (unsigned char *)(((uintptr_t)wrkBuff + psz - 1) & (~(psz - 1))); } else { wrkBuff = (unsigned char *)malloc(blk_sz * bpt); if (0 == wrkBuff) { pr2serr("Not enough user memory\n"); return SG_LIB_FILE_ERROR; } wrkPos = wrkBuff; } } blocks_per = bpt; #ifdef SG_DEBUG pr2serr("Start of loop, count=%" PRId64 ", blocks_per=%d\n", dd_count, blocks_per); #endif if (do_time) { start_tm.tv_sec = 0; start_tm.tv_usec = 0; gettimeofday(&start_tm, NULL); start_tm_valid = 1; } req_count = dd_count; if (verbose && (dd_count > 0) && (0 == out_flags.dio) && (FT_SG == in_type) && (FT_SG == out_type)) pr2serr("Since both 'if' and 'of' are sg devices, only do mmap-ed " "transfers on 'if'\n"); while (dd_count > 0) { blocks = (dd_count > blocks_per) ? blocks_per : dd_count; if (FT_SG == in_type) { ret = sg_read(infd, wrkPos, blocks, skip, blk_sz, scsi_cdbsz_in, in_flags.fua, in_flags.dpo, 1); if ((SG_LIB_CAT_UNIT_ATTENTION == ret) || (SG_LIB_CAT_ABORTED_COMMAND == ret)) { pr2serr("Unit attention or aborted command, continuing " "(r)\n"); ret = sg_read(infd, wrkPos, blocks, skip, blk_sz, scsi_cdbsz_in, in_flags.fua, in_flags.dpo, 1); } if (0 != ret) { pr2serr("sg_read failed, skip=%" PRId64 "\n", skip); break; } else in_full += blocks; } else { while (((res = read(infd, wrkPos, blocks * blk_sz)) < 0) && ((EINTR == errno) || (EAGAIN == errno))) ; if (verbose > 2) pr2serr("read(unix): count=%d, res=%d\n", blocks * blk_sz, res); if (ret < 0) { snprintf(ebuff, EBUFF_SZ, ME "reading, skip=%" PRId64 " ", skip); perror(ebuff); ret = -1; break; } else if (res < blocks * blk_sz) { dd_count = 0; blocks = res / blk_sz; if ((res % blk_sz) > 0) { blocks++; in_partial++; } } in_full += blocks; } if (0 == blocks) break; /* read nothing so leave loop */ if (FT_SG == out_type) { int do_mmap = (FT_SG == in_type) ? 0 : 1; int dio_res = out_flags.dio; ret = sg_write(outfd, wrkPos, blocks, seek, blk_sz, scsi_cdbsz_out, out_flags.fua, out_flags.dpo, do_mmap, &dio_res); if ((SG_LIB_CAT_UNIT_ATTENTION == ret) || (SG_LIB_CAT_ABORTED_COMMAND == ret)) { pr2serr("Unit attention or aborted command, continuing (w)\n"); dio_res = out_flags.dio; ret = sg_write(outfd, wrkPos, blocks, seek, blk_sz, scsi_cdbsz_out, out_flags.fua, out_flags.dpo, do_mmap, &dio_res); } if (0 != ret) { pr2serr("sg_write failed, seek=%" PRId64 "\n", seek); break; } else { out_full += blocks; if (out_flags.dio && (0 == dio_res)) num_dio_not_done++; } } else if (FT_DEV_NULL == out_type) out_full += blocks; /* act as if written out without error */ else { while (((res = write(outfd, wrkPos, blocks * blk_sz)) < 0) && ((EINTR == errno) || (EAGAIN == errno))) ; if (verbose > 2) pr2serr("write(unix): count=%d, res=%d\n", blocks * blk_sz, res); if (res < 0) { snprintf(ebuff, EBUFF_SZ, ME "writing, seek=%" PRId64 " ", seek); perror(ebuff); break; } else if (res < blocks * blk_sz) { pr2serr("output file probably full, seek=%" PRId64 " ", seek); blocks = res / blk_sz; out_full += blocks; if ((res % blk_sz) > 0) out_partial++; break; } else out_full += blocks; } if (dd_count > 0) dd_count -= blocks; skip += blocks; seek += blocks; } if (do_time) calc_duration_throughput(0); if (do_sync) { if (FT_SG == out_type) { pr2serr(">> Synchronizing cache on %s\n", outf); res = sg_ll_sync_cache_10(outfd, 0, 0, 0, 0, 0, 0, 0); if (SG_LIB_CAT_UNIT_ATTENTION == res) { pr2serr("Unit attention(out), continuing\n"); res = sg_ll_sync_cache_10(outfd, 0, 0, 0, 0, 0, 0, 0); } if (0 != res) { sg_get_category_sense_str(res, sizeof(b), b, verbose); pr2serr("Synchronize cache(out): %s\n", b); } } } if (wrkBuff) free(wrkBuff); if (STDIN_FILENO != infd) close(infd); if ((STDOUT_FILENO != outfd) && (FT_DEV_NULL != out_type)) close(outfd); if (0 != dd_count) { pr2serr("Some error occurred,"); if (0 == ret) ret = SG_LIB_CAT_OTHER; } print_stats(); if (sum_of_resids) pr2serr(">> Non-zero sum of residual counts=%d\n", sum_of_resids); if (num_dio_not_done) pr2serr(">> dio requested but _not_ done %d times\n", num_dio_not_done); return (ret >= 0) ? ret : SG_LIB_CAT_OTHER; }
int main(int argc, char * argv[]) { int skip = 0; int seek = 0; int ibs = 0; int obs = 0; 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 num_threads = DEF_NUM_THREADS; int gen = 0; int do_time = 0; int in_sect_sz, out_sect_sz, first_xfer, qstate, req_index, seek_skip; int blocks, stop_after_write, terminate; char ebuff[EBUFF_SZ]; Rq_elem * rep; struct timeval start_tm, end_tm; memset(&rcoll, 0, sizeof(Rq_coll)); rcoll.bpt = DEF_BLOCKS_PER_TRANSFER; rcoll.in_type = FT_OTHER; rcoll.out_type = FT_OTHER; 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); 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")) dd_count = sg_get_num(buf); else if (0 == strcmp(key,"dio")) rcoll.dio = sg_get_num(buf); else if (0 == strcmp(key,"thr")) num_threads = sg_get_num(buf); else if (0 == strcmp(key,"coe")) rcoll.coe = sg_get_num(buf); else if (0 == strcmp(key,"gen")) gen = sg_get_num(buf); else if (0 == strncmp(key,"deb", 3)) rcoll.debug = sg_get_num(buf); else if (0 == strcmp(key,"time")) do_time = sg_get_num(buf); else if (0 == strncmp(key, "--vers", 6)) { fprintf(stderr, "sgq_dd for sg version 3 driver: %s\n", version_str); return 0; } else { fprintf(stderr, "Unrecognized argument '%s'\n", key); usage(); return 1; } } if (rcoll.bs <= 0) { rcoll.bs = DEF_BLOCK_SIZE; fprintf(stderr, "Assume default 'bs' (block size) of %d bytes\n", rcoll.bs); } if ((ibs && (ibs != rcoll.bs)) || (obs && (obs != rcoll.bs))) { fprintf(stderr, "If 'ibs' or 'obs' given must be same as 'bs'\n"); usage(); return 1; } if ((skip < 0) || (seek < 0)) { fprintf(stderr, "skip and seek cannot be negative\n"); return 1; } if ((num_threads < 1) || (num_threads > MAX_NUM_THREADS)) { fprintf(stderr, "too few or too many threads requested\n"); usage(); return 1; } if (rcoll.debug) fprintf(stderr, "sgq_dd: if=%s skip=%d of=%s seek=%d count=%d\n", inf, skip, outf, seek, dd_count); install_handler (SIGINT, interrupt_handler); install_handler (SIGQUIT, interrupt_handler); install_handler (SIGPIPE, interrupt_handler); install_handler (SIGUSR1, siginfo_handler); rcoll.infd = STDIN_FILENO; rcoll.outfd = STDOUT_FILENO; if (inf[0] && ('-' != inf[0])) { rcoll.in_type = dd_filetype(inf); if (FT_SG == rcoll.in_type) { if ((rcoll.infd = open(inf, O_RDWR)) < 0) { snprintf(ebuff, EBUFF_SZ, "sgq_dd: could not open %s for sg reading", inf); perror(ebuff); return 1; } } if (FT_SG != rcoll.in_type) { if ((rcoll.infd = open(inf, O_RDONLY)) < 0) { snprintf(ebuff, EBUFF_SZ, "sgq_dd: could not open %s for reading", inf); perror(ebuff); return 1; } else if (skip > 0) { loff_t offset = skip; offset *= rcoll.bs; /* could exceed 32 here! */ if (lseek(rcoll.infd, offset, SEEK_SET) < 0) { snprintf(ebuff, EBUFF_SZ, "sgq_dd: couldn't skip to required position on %s", inf); perror(ebuff); return 1; } } } } if (outf[0] && ('-' != outf[0])) { rcoll.out_type = dd_filetype(outf); if (FT_SG == rcoll.out_type) { if ((rcoll.outfd = open(outf, O_RDWR)) < 0) { snprintf(ebuff, EBUFF_SZ, "sgq_dd: could not open %s for sg writing", outf); perror(ebuff); return 1; } } else { if (FT_OTHER == rcoll.out_type) { if ((rcoll.outfd = open(outf, O_WRONLY | O_CREAT, 0666)) < 0) { snprintf(ebuff, EBUFF_SZ, "sgq_dd: could not open %s for writing", outf); perror(ebuff); return 1; } } else { if ((rcoll.outfd = open(outf, O_WRONLY)) < 0) { snprintf(ebuff, EBUFF_SZ, "sgq_dd: could not open %s for raw writing", outf); perror(ebuff); return 1; } } if (seek > 0) { loff_t offset = seek; offset *= rcoll.bs; /* could exceed 32 bits here! */ if (lseek(rcoll.outfd, offset, SEEK_SET) < 0) { snprintf(ebuff, EBUFF_SZ, "sgq_dd: couldn't seek to required position on %s", outf); perror(ebuff); return 1; } } } } if ((STDIN_FILENO == rcoll.infd) && (STDOUT_FILENO == rcoll.outfd)) { fprintf(stderr, "Disallow both if and of to be stdin and stdout"); return 1; } if ((FT_OTHER == rcoll.in_type) && (FT_OTHER == rcoll.out_type) && !gen) { fprintf(stderr, "Either 'if' or 'of' must be a sg or raw device\n"); return 1; } if (0 == dd_count) return 0; else if (dd_count < 0) { if (FT_SG == rcoll.in_type) { res = read_capacity(rcoll.infd, &in_num_sect, &in_sect_sz); if (2 == res) { fprintf(stderr, "Unit attention, media changed(in), repeat\n"); res = read_capacity(rcoll.infd, &in_num_sect, &in_sect_sz); } if (0 != res) { fprintf(stderr, "Unable to read capacity on %s\n", inf); in_num_sect = -1; } else { if (in_num_sect > skip) in_num_sect -= skip; } } if (FT_SG == rcoll.out_type) { res = read_capacity(rcoll.outfd, &out_num_sect, &out_sect_sz); if (2 == res) { fprintf(stderr, "Unit attention, media changed(out), " "repeat\n"); res = read_capacity(rcoll.outfd, &out_num_sect, &out_sect_sz); } if (0 != res) { fprintf(stderr, "Unable to read capacity on %s\n", outf); out_num_sect = -1; } else { if (out_num_sect > seek) out_num_sect -= seek; } } if (in_num_sect > 0) { if (out_num_sect > 0) dd_count = (in_num_sect > out_num_sect) ? out_num_sect : in_num_sect; else dd_count = in_num_sect; } else dd_count = out_num_sect; } if (rcoll.debug > 1) fprintf(stderr, "Start of loop, count=%d, in_num_sect=%d, " "out_num_sect=%d\n", dd_count, in_num_sect, out_num_sect); if (dd_count <= 0) { fprintf(stderr, "Couldn't calculate count, please give one\n"); return 1; } rcoll.in_count = dd_count; rcoll.in_done_count = dd_count; rcoll.skip = skip; rcoll.in_blk = skip; rcoll.out_count = dd_count; rcoll.out_done_count = dd_count; rcoll.seek = seek; rcoll.out_blk = seek; if ((FT_SG == rcoll.in_type) || (FT_SG == rcoll.out_type)) rcoll.num_rq_elems = num_threads; else rcoll.num_rq_elems = 1; if (prepare_rq_elems(&rcoll, inf, outf)) { fprintf(stderr, "Setup failure, perhaps no memory\n"); return 1; } first_xfer = 1; stop_after_write = 0; terminate = 0; seek_skip = rcoll.seek - rcoll.skip; if (do_time) { start_tm.tv_sec = 0; start_tm.tv_usec = 0; gettimeofday(&start_tm, NULL); } while (rcoll.out_done_count > 0) { /* >>>>>>>>> main loop */ req_index = -1; qstate = decider(&rcoll, first_xfer, &req_index); rep = (req_index < 0) ? NULL : (rcoll.req_arr + req_index); switch (qstate) { case QS_IDLE: if ((NULL == rep) || (rcoll.in_count <= 0)) { /* usleep(1000); */ /* do_poll(&rcoll, 10, NULL); */ /* do_poll(&rcoll, 0, NULL); */ break; } if (rcoll.debug > 8) fprintf(stderr, " sgq_dd: non-sleeping QS_IDLE state, " "req_index=%d\n", req_index); if (first_xfer >= 2) first_xfer = 0; else if (1 == first_xfer) ++first_xfer; if (stop_after_write) { terminate = 1; break; } blocks = (rcoll.in_count > rcoll.bpt) ? rcoll.bpt : rcoll.in_count; rep->wr = 0; rep->blk = rcoll.in_blk; rep->num_blks = blocks; rcoll.in_blk += blocks; rcoll.in_count -= blocks; if (FT_SG == rcoll.in_type) { res = sg_start_io(rep); if (0 != res) { if (1 == res) fprintf(stderr, "Out of memory starting sg io\n"); terminate = 1; } } else { res = normal_in_operation(&rcoll, rep, blocks); if (res < 0) terminate = 1; else if (res > 0) stop_after_write = 1; } break; case QS_IN_FINISHED: if (rcoll.debug > 8) fprintf(stderr, " sgq_dd: state is QS_IN_FINISHED, " "req_index=%d\n", req_index); if ((rep->blk + seek_skip) != rcoll.out_blk) { /* if write would be out of sequence then wait */ if (rcoll.debug > 4) fprintf(stderr, " sgq_dd: QS_IN_FINISHED, " "out of sequence\n"); usleep(200); break; } rep->wr = 1; rep->blk = rcoll.out_blk; blocks = rep->num_blks; rcoll.out_blk += blocks; rcoll.out_count -= blocks; if (FT_SG == rcoll.out_type) { res = sg_start_io(rep); if (0 != res) { if (1 == res) fprintf(stderr, "Out of memory starting sg io\n"); terminate = 1; } } else { if (normal_out_operation(&rcoll, rep, blocks) < 0) terminate = 1; } break; case QS_IN_POLL: if (rcoll.debug > 8) fprintf(stderr, " sgq_dd: state is QS_IN_POLL, " "req_index=%d\n", req_index); res = sg_fin_in_operation(&rcoll, rep); if (res < 0) terminate = 1; else if (res > 1) { if (first_xfer) { /* only retry on first xfer */ if (0 != sg_start_io(rep)) terminate = 1; } else terminate = 1; } break; case QS_OUT_POLL: if (rcoll.debug > 8) fprintf(stderr, " sgq_dd: state is QS_OUT_POLL, " "req_index=%d\n", req_index); res = sg_fin_out_operation(&rcoll, rep); if (res < 0) terminate = 1; else if (res > 1) { if (first_xfer) { /* only retry on first xfer */ if (0 != sg_start_io(rep)) terminate = 1; } else terminate = 1; } break; default: if (rcoll.debug > 8) fprintf(stderr, " sgq_dd: state is ?????\n"); terminate = 1; break; } if (terminate) break; } /* >>>>>>>>>>>>> end of main loop */ if ((do_time) && (start_tm.tv_sec || start_tm.tv_usec)) { struct timeval res_tm; double a, b; gettimeofday(&end_tm, NULL); res_tm.tv_sec = end_tm.tv_sec - start_tm.tv_sec; res_tm.tv_usec = end_tm.tv_usec - start_tm.tv_usec; if (res_tm.tv_usec < 0) { --res_tm.tv_sec; res_tm.tv_usec += 1000000; } a = res_tm.tv_sec; a += (0.000001 * res_tm.tv_usec); b = (double)rcoll.bs * (dd_count - rcoll.out_done_count); printf("time to transfer data was %d.%06d secs", (int)res_tm.tv_sec, (int)res_tm.tv_usec); if ((a > 0.00001) && (b > 511)) printf(", %.2f MB/sec\n", b / (a * 1000000.0)); else printf("\n"); } if (STDIN_FILENO != rcoll.infd) close(rcoll.infd); if (STDOUT_FILENO != rcoll.outfd) close(rcoll.outfd); res = 0; if (0 != rcoll.out_count) { fprintf(stderr, ">>>> Some error occurred,\n"); res = 2; } print_stats(); if (rcoll.dio_incomplete) { int fd; char c; fprintf(stderr, ">> Direct IO requested but incomplete %d times\n", rcoll.dio_incomplete); if ((fd = open(proc_allow_dio, O_RDONLY)) >= 0) { if (1 == read(fd, &c, 1)) { if ('0' == c) fprintf(stderr, ">>> %s set to '0' but should be set " "to '1' for direct IO\n", proc_allow_dio); } close(fd); } } if (rcoll.sum_of_resids) fprintf(stderr, ">> Non-zero sum of residual counts=%d\n", rcoll.sum_of_resids); return res; }