/* dd_rescue.c */ /* * dd_rescue copies your data from one file to another * files might as well be block devices, such as hd partitions * unlike dd, it does not necessarily abort on errors, but * continues to copy the disk, possibly leaving holes behind. * Also, it does NOT truncate the output file, so you can copy * more and more pieces of your data, as time goes by. * So, this tool is suitable for rescueing data of crashed disk, * and that's the reason, it has been written by me. * * (c) Kurt Garloff , 11/97, 10/99 * Copyright: GNU GPL * * Improvements from LAB Valentin, see * http://www.tharbad.ath.cx/~vaab/kalysto/Utilities/dd_rhelp/dd_rhelp_en.html */ /* * TODO: * - Use termcap to fetch cursor up/down codes * - Better handling of write errors: also try sub blocks */ #ifndef VERSION # define VERSION "(unknown)" #endif #define ID "$Id: dd_rescue.c,v 1.50 2005/02/14 00:39:44 garloff Exp $" #ifndef SOFTBLOCKSIZE # define SOFTBLOCKSIZE 65536 #endif #ifndef HARDBLOCKSIZE # define HARDBLOCKSIZE 512 #endif #define _GNU_SOURCE #define _LARGEFILE_SOURCE #define _FILE_OFFSET_BITS 64 #include #include #include #include #include #include #include #include #include #include #include #include #include #include int softbs, hardbs; int maxerr, nrerr, reverse, dotrunc, abwrerr, sparse, nosparse; int verbose, quiet, interact, force; void* buf; char *lname, *iname, *oname; off_t ipos, opos, xfer, lxfer, sxfer, fxfer, maxxfer; int ides, odes, identical, pres; int o_dir_in, o_dir_out; char i_chr, o_chr; FILE *logfd; struct timeval starttime, lasttime, currenttime; struct timezone tz; clock_t startclock; const char* up = "\x1b[A"; //] const char* down = "\n"; const char* right = "\x1b[C"; //] inline float difftimetv(const struct timeval* const t2, const struct timeval* const t1) { return (float) (t2->tv_sec - t1->tv_sec ) + (float) (t2->tv_usec - t1->tv_usec) * 1e-6; } /* Write to file and simultaneously log to logfdile, if exsiting */ int fplog(FILE* const file, const char * const fmt, ...) { int ret = 0; va_list vl; va_start(vl, fmt); if (file) ret = vfprintf(file, fmt, vl); va_end(vl); if (logfd) { va_start(vl, fmt); ret = vfprintf(logfd, fmt, vl); va_end(vl); } return ret; } int check_identical(const char* const in, const char* const on) { int err = 0; struct stat istat, ostat; errno = 0; if (strcmp(in, on) == 0) return 1; err -= stat(in, &istat); if (err) return 0; err -= stat(on, &ostat); errno = 0; if (!err && istat.st_ino == ostat.st_ino && istat.st_dev == ostat.st_dev) return 1; return 0; } int openfile(const char* const fname, const int flags) { int fdes; if (!strcmp(fname, "-")) { if (flags & O_WRONLY) fdes = 1; /* stdout */ else fdes = 0; /* stdin */ } else fdes = open(fname, flags, 0640); if (fdes == -1) { char nbuf[128]; snprintf(nbuf, 128, "dd_rescue: (fatal): open \"%s\" failed", fname); perror(nbuf); exit(17); } return fdes; } /* Checks whether files are seekable */ void check_seekable(const int id, const int od) { errno = 0; if (lseek(id, (off_t)0, SEEK_SET) != 0) { fplog(stderr, "dd_rescue: (warning): input file is not seekable!\n"); fplog(stderr, "dd_rescue: (warning): %s\n", strerror(errno)); i_chr = 1; } //else // lseek(id, (off_t)0, SEEK_SET); errno = 0; if (lseek(od, (off_t)0, SEEK_SET) != 0) { fplog(stderr, "dd_rescue: (warning): output file is not seekable!\n"); fplog(stderr, "dd_rescue: (warning): %s\n", strerror(errno)); o_chr = 1; } //else // lseek(od, (off_t)0, SEEK_SET); errno = 0; } void doprint(FILE* const file, const int bs, const clock_t cl, const float t1, const float t2, const int sync) { fprintf(file, "dd_rescue: (info): ipos:%12.1fk, opos:%12.1fk, xferd:%12.1fk\n", (float)ipos/1024, (float)opos/1024, (float)xfer/1024); fprintf(file, " %s %s errs:%7i, errxfer:%12.1fk, succxfer:%12.1fk\n", (reverse? "-": " "), (bs==hardbs? "*": " "), nrerr, (float)fxfer/1024, (float)sxfer/1024); if (sync || (file != stdin && file != stdout) ) fprintf(file, " +curr.rate:%9.0fkB/s, avg.rate:%9.0fkB/s, avg.load:%5.1f%%\n", (float)(xfer-lxfer)/(t2*1024), (float)xfer/(t1*1024), 100.0*(cl-startclock)/(CLOCKS_PER_SEC*t1)); else fprintf(file, " -curr.rate:%s%s%s%s%s%s%s%s%skB/s, avg.rate:%9.0fkB/s, avg.load:%5.1f%%\n", right, right, right, right, right, right, right, right, right, (float)xfer/(t1*1024), 100.0*(cl-startclock)/(CLOCKS_PER_SEC*t1)); } void printstatus(FILE* const file1, FILE* const file2, const int bs, const int sync) { float t1, t2; clock_t cl; int err = 0; if (sync) { err = fsync(odes); if (err) fplog(stderr, "dd_rescue: (warning): %s (%.1fk): %s!\n", oname, (float)ipos/1024, strerror(errno)); } gettimeofday(¤ttime, NULL); t1 = difftimetv(¤ttime, &starttime); t2 = difftimetv(¤ttime, &lasttime); cl = clock(); if (file1 == stderr || file1 == stdout) fprintf(file1, "%s%s%s", up, up, up); if (file2 == stderr || file2 == stdout) fprintf(file2, "%s%s%s", up, up, up); if (file1) doprint(file1, bs, cl, t1, t2, sync); if (file2) doprint(file2, bs, cl, t1, t2, sync); if (sync) { memcpy(&lasttime, ¤ttime, sizeof(lasttime)); lxfer = xfer; } } void printreport() { /* report */ FILE *report = (!quiet || nrerr)? stderr: 0; fplog(report, "Summary for %s -> %s:\n", iname, oname); if (report) fprintf(stderr, "%s%s%s", down, down, down); printstatus(report, logfd, 0, 1); } int cleanup() { int rc, errs = 0; if (odes != -1) { /* Make sure, the output file is expanded to the last (first) position */ pwrite(odes, buf, 0, opos); rc = fsync(odes); if (rc) { fplog(stderr, "dd_rescue: (warning): %s (%.1fk): %s!\n", oname, (float)opos/1024, strerror(errno)); ++errs; } rc = close(odes); if (rc) { fplog(stderr, "dd_rescue: (warning): %s (%.1fk): %s!\n", oname, (float)opos/1024, strerror(errno)); ++errs; } } if (ides != -1) { rc = close(ides); if (rc) { fplog(stderr, "dd_rescue: (warning): %s (%.1fk): %s!\n", iname, (float)ipos/1024, strerror(errno)); ++errs; } } if (logfd) fclose(logfd); if (buf) free(buf); return errs; } /* is the block zero ? */ int blockiszero(const char* blk, const int ln) { unsigned long* ptr = (unsigned long*)blk; while ((ptr-(unsigned long*)blk) < ln/sizeof(unsigned long)) if (*(ptr++)) return 0; return 1; } inline ssize_t mypread(int fd, void* bf, size_t sz, off_t off) { if (i_chr) return read(fd, bf, sz); else return pread(fd, bf, sz, off); } inline ssize_t mypwrite(int fd, void* bf, size_t sz, off_t off) { if (o_chr) return write(fd, bf, sz); else return pwrite(fd, bf, sz, off); } ssize_t readblock(const int toread) { ssize_t err, rd = 0; //errno = 0; /* should not be necessary */ do { rd += (err = mypread(ides, buf+rd, toread-rd, ipos+rd-reverse*toread)); if (err == -1) rd++; } while ((err == -1 && (errno == EINTR || errno == EAGAIN)) || (rd < toread && err > 0 && errno == 0)); //if (rd < toread) memset (buf+rd, 0, toread-rd); return (/*err == -1? err:*/ rd); } ssize_t writeblock(const int towrite) { ssize_t err, wr = 0; //errno = 0; /* should not be necessary */ do { wr += (err = mypwrite(odes, buf+wr, towrite-wr, opos+wr-reverse*towrite)); if (err == -1) wr++; } while ((err == -1 && (errno == EINTR || errno == EAGAIN)) || (wr < towrite && err > 0 && errno == 0)); if (wr < towrite && err != 0) { /* Write error: handle ? .. */ fplog(stderr, "dd_rescue: (%s): %s (%.1fk): %s\n", (abwrerr? "fatal": "warning"), oname, (float)opos/1024, strerror(errno)); if (abwrerr) { cleanup(); exit(21); } nrerr++; } return (/*err == -1? err:*/ wr); } /* can be invoked in two ways: bs==hardbs or bs==softbs */ int copyfile(const off_t max, const int bs) { int errs = 0; errno = 0; #if 0 fprintf(stderr, "%s%s%s copyfile (ipos=%.1fk, xfer=%.1fk, max=%.1fk, bs=%i) ##\n%s%s%s", up, up, up, (float)ipos/1024, (float)xfer/1024, (float)max/1024, bs, down, down, down); #endif /* expand file to the right length */ if (!o_chr) pwrite(odes, buf, 0, opos); while ( (!max || (max-xfer > 0)) && ((!reverse) || (ipos > 0 && opos > 0)) ) { int err; ssize_t rd = 0; ssize_t toread = ((max && max-xfer < bs)? (max-xfer): bs); if (reverse) { if (toread > ipos) toread = ipos; if (toread > opos) toread = opos; } if (nosparse && bs == hardbs) memset(buf, 0, bs); rd = readblock(toread); /* EOF */ if (rd == 0 && !errno) { if (!errs) fplog(stderr, "dd_rescue: (info): %s (%.1fk): EOF\n", iname, (float)ipos/1024); return errs; } /* READ ERROR */ if (rd < toread/* && errno*/) { /* Read error occurred: Print warning */ printstatus(stderr, logfd, bs, 1); errs++; /* Some errnos are fatal */ if (errno == ESPIPE || errno == EPERM || errno == ENXIO || errno == ENODEV) { fplog(stderr, "dd_rescue: (warning): %s (%.1fk): %s!\n", iname, (float)ipos/1024, strerror(errno)); fplog(stderr, "dd_rescue: Last error fatal! Exiting ...\n"); cleanup(); exit(20); } /* Non fatal error */ if (bs == hardbs) { /* Real error: Don't retry */ nrerr++; fplog(stderr, "dd_rescue: (warning): %s (%.1fk): %s!\n", iname, (float)ipos/1024, strerror(errno)); /* exit if too many errs */ if (maxerr && nrerr >= maxerr) { fplog(stderr, "dd_rescue: (fatal): maxerr reached!\n"); printreport(); cleanup(); exit(32); } fprintf(stderr, "%s%s%s", down, down, down); /* advance */ errno = 0; if (nosparse) { ssize_t wr = 0; errs += ((wr = writeblock(rd)) < rd ? 1: 0); if (wr < 0 && (errno == ENOSPC || (errno == EFBIG && !reverse))) return errs; if (rd != wr) { fplog(stderr, "dd_rescue: (warning): assumption rd(%i) == wr(%i) failed! \n", rd, wr); /* fplog(stderr, "dd_rescue: (warning): %s (%.1fk): %s!\n", oname, (float)opos/1024, strerror(errno)); fprintf(stderr, "%s%s%s", down, down, down); */ } } fxfer += toread; xfer += toread; if (reverse) { ipos -= toread; opos -= toread; } else { ipos += toread; opos += toread; } } else { off_t new_max = xfer + toread; off_t old_xfer; /* Error with large blocks: Try small ones ... */ if (verbose) fprintf(stderr, "dd_rescue: (info): problems at ipos %.1fk: %s \n fall back to smaller blocksize \n%s%s%s", (float)ipos/1024, strerror(errno), down, down, down); /* But first: write available data and advance (optimization) */ if (rd > 0) { ssize_t wr = 0; errno = 0; if (!sparse || !blockiszero(buf, bs)) errs += ((wr = writeblock(rd)) < rd ? 1: 0); if (!reverse) { ipos += rd; opos += rd; sxfer += wr; xfer += rd; } /* else { new_max -= rd; } */ if (wr < 0 && (errno == ENOSPC || (errno == EFBIG && !reverse))) return errs; if (rd != wr && !sparse) { fplog(stderr, "dd_rescue: (warning): assumption rd(%i) == wr(%i) failed! \n", rd, wr); /* fplog(stderr, "dd_rescue: (warning): %s (%.1fk): %s!\n", oname, (float)opos/1024, strerror(errno)); fprintf(stderr, "%s%s%s", down, down, down); */ } } /* rd > 0 */ old_xfer = xfer; errs += (err = copyfile(new_max, hardbs)); /* EOF */ if (!err && old_xfer == xfer) return errs; /* if (reverse && rd) { ipos -= rd; opos -= rd; xfer += rd; sxfer += wr; } */ /* Stay with small blocks, until we could read two whole large ones without errors */ new_max = xfer; while (err && (!max || (max-xfer > 0)) && ((!reverse) || (ipos > 0 && opos > 0))) { new_max += 2*softbs; old_xfer = xfer; if (max && new_max > max) new_max = max; errs += (err = copyfile(new_max, hardbs)); } errno = 0; /* EOF ? */ if (!err && xfer == old_xfer) return errs; if (verbose) fprintf(stderr, "dd_rescue: (info): ipos %.1fk promote to large bs again! \n%s%s%s", (float)ipos/1024, down, down, down); } /* bs == hardbs */ } else { /* errno == 0: We can write to disk */ if (rd > 0) { ssize_t wr = 0; if (!sparse || !blockiszero(buf, bs)) errs += ((wr = writeblock(rd)) < rd ? 1: 0); sxfer += wr; xfer += rd; if (reverse) { ipos -= rd; opos -= rd; } else { ipos += rd; opos += rd; } if (wr < 0 && (errno == ENOSPC || (errno == EFBIG && !reverse))) return errs; if (rd != wr && !sparse) { fplog(stderr, "dd_rescue: (warning): assumption rd(%i) == wr(%i) failed! \n", rd, wr); fplog(stderr, "dd_rescue: (warning): %s (%.1fk): %s!\n", oname, (float)opos/1024, strerror(errno)); fprintf(stderr, "%s%s", down, down); errno = 0; } } /* rd > 0 */ } /* errno */ if (!quiet && !(xfer % (16*softbs)) && (xfer % (512*softbs))) printstatus(stderr, 0, bs, 0); if (!quiet && !(xfer % (512*softbs))) printstatus(stderr, 0, bs, 1); } /* remain */ return errs; } int copyperm(int ifd, int ofd) { int err; mode_t fmode; struct stat stbuf; err = fstat(ifd, &stbuf); if (err) return err; fmode = stbuf.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID | S_ISGID | S_ISVTX); err = fchown(ofd, stbuf.st_uid, stbuf.st_gid); if (err) fmode &= ~(S_ISUID | S_ISGID); err += fchmod(ofd, fmode); return err; } /* File time copy */ int copytimes(const char* inm, const char* onm) { int err; struct stat stbuf; struct utimbuf utbuf; err = stat(inm, &stbuf); if (err) return err; utbuf.actime = stbuf.st_atime; utbuf.modtime = stbuf.st_mtime; err = utime(onm, &utbuf); return err; } off_t readint(const char* const ptr) { char *es; double res; res = strtod(ptr, &es); switch (*es) { case 'b': res *= 512; break; case 'k': res *= 1024; break; case 'M': res *= 1024*1024; break; case 'G': res *= 1024*1024*1024; break; case ' ': case '\0': break; default: fplog(stderr, "dd_rescue: (warning): suffix %c ignored!\n", *es); } return (off_t)res; } void printversion() { fprintf(stderr, "\ndd_rescue Version %s, garloff@suse.de, GNU GPL\n", VERSION); fprintf(stderr, " (%s)\n", ID); } void printhelp() { printversion(); fprintf(stderr, "dd_rescue copies data from one file (or block device) to another\n"); fprintf(stderr, "USAGE: dd_rescue [options] infile outfile\n"); fprintf(stderr, "Options: -s ipos start position in input file (default=0),\n"); fprintf(stderr, " -S opos start position in output file (def=ipos);\n"); fprintf(stderr, " -b softbs block size for copy operation (def=%i),\n", SOFTBLOCKSIZE ); fprintf(stderr, " -B hardbs fallback block size in case of errs (def=%i);\n", HARDBLOCKSIZE ); fprintf(stderr, " -e maxerr exit after maxerr errors (def=0=infinite);\n"); fprintf(stderr, " -m maxxfer maximum amount of data to be transfered (def=0=inf);\n"); fprintf(stderr, " -l logfdile name of a file to log errors and summary to (def=\"\");\n"); fprintf(stderr, " -r reverse direction copy (def=forward);\n"); fprintf(stderr, " -t truncate output file (def=no);\n"); #ifdef O_DIRECT fprintf(stderr, " -d/D use O_DIRECT for input/output (def=no);\n"); #endif fprintf(stderr, " -w abort on Write errors (def=no);\n"); fprintf(stderr, " -a spArse file writing (def=no),\n"); fprintf(stderr, " -A Always write blocks, zeroed if err (def=no);\n"); fprintf(stderr, " -i interactive: ask before overwriting data (def=no);\n"); fprintf(stderr, " -f force: skip some sanity checks (def=no);\n"); fprintf(stderr, " -p preserve: preserve ownership / perms (def=no)\n"); fprintf(stderr, " -q quiet operation,\n"); fprintf(stderr, " -v verbose operation;\n"); fprintf(stderr, " -V display version and exit;\n"); fprintf(stderr, " -h display this help and exit.\n"); fprintf(stderr, "Note: Sizes may be given in units b(=512), k(=1024), M(=1024^2) or G(1024^3) bytes\n"); fprintf(stderr, "This program is useful to rescue data in case of I/O errors, because\n"); fprintf(stderr, " it does not necessarily abort or truncate the output.\n"); } #define YESNO(flag) (flag? "yes": "no ") void printinfo(FILE* const file) { fplog(file, "dd_rescue: (info): about to transfer %.1f kBytes from %s to %s\n", (double)maxxfer/1024, iname, oname); fplog(file, "dd_rescue: (info): blocksizes: soft %i, hard %i\n", softbs, hardbs); fplog(file, "dd_rescue: (info): starting positions: in %.1fk, out %.1fk\n", (double)ipos/1024, (double)opos/1024); fplog(file, "dd_rescue: (info): Logfile: %s, Maxerr: %li\n", (lname? lname: "(none)"), maxerr); fplog(file, "dd_rescue: (info): Reverse: %s, Trunc: %s, interactive: %s\n", YESNO(reverse), YESNO(dotrunc), YESNO(interact)); fplog(file, "dd_rescue: (info): abort on Write errs: %s, spArse write: %s\n", YESNO(abwrerr), (sparse? "yes": (nosparse? "never": "if err"))); /* fplog(file, "dd_rescue: (info): verbose: %s, quiet: %s\n", YESNO(verbose), YESNO(quiet)); */ } void breakhandler(int sig) { fplog(stderr, "dd_rescue: (fatal): Caught signal %i \"%s\". Exiting!\n", sig, strsignal(sig)); printreport(); cleanup(); signal(sig, SIG_DFL); raise(sig); } int main(int argc, char* argv[]) { int c; void **mp = (void **) &buf; /* defaults */ softbs = SOFTBLOCKSIZE; hardbs = HARDBLOCKSIZE; maxerr = 0; ipos = (off_t)-1; opos = (off_t)-1; maxxfer = 0; reverse = 0; dotrunc = 0; abwrerr = 0; sparse = 0; nosparse = 0; verbose = 0; quiet = 0; interact = 0; force = 0; pres = 0; lname = 0; iname = 0; oname = 0; o_dir_in = 0; o_dir_out = 0; /* Initialization */ sxfer = 0; fxfer = 0; lxfer = 0; xfer = 0; ides = -1; odes = -1; logfd = 0; nrerr = 0; buf = 0; i_chr = 0; o_chr = 0; while ((c = getopt(argc, argv, ":rtfihqvVwaAdDpb:B:m:e:s:S:l:")) != -1) { switch (c) { case 'r': reverse = 1; break; case 't': dotrunc = O_TRUNC; break; case 'i': interact = 1; force = 0; break; case 'f': interact = 0; force = 1; break; #ifdef O_DIRECT case 'd': o_dir_in = O_DIRECT; break; case 'D': o_dir_out = O_DIRECT; break; #endif case 'p': pres = 1; break; case 'a': sparse = 1; nosparse = 0; break; case 'A': nosparse = 1; sparse = 0; break; case 'w': abwrerr = 1; break; case 'h': printhelp(); exit(0); break; case 'V': printversion(); exit(0); break; case 'v': quiet = 0; verbose = 1; break; case 'q': verbose = 0; quiet = 1; break; case 'b': softbs = (int)readint(optarg); break; case 'B': hardbs = (int)readint(optarg); break; case 'm': maxxfer = readint(optarg); break; case 'e': maxerr = (int)readint(optarg); break; case 's': ipos = readint(optarg); break; case 'S': opos = readint(optarg); break; case 'l': lname = optarg; break; case ':': fplog (stderr, "dd_rescue: (fatal): option %c requires an argument!\n", optopt); printhelp(); exit(11); break; case '?': fplog(stderr, "dd_rescue: (fatal): unknown option %c!\n", optopt, argv[0]); printhelp(); exit(11); break; default: fplog(stderr, "dd_rescue: (fatal): your getopt() is buggy!\n"); exit(255); } } if (optind < argc) iname = argv[optind++]; if (optind < argc) oname = argv[optind++]; if (optind < argc) { fplog(stderr, "dd_rescue: (fatal): spurious options: %s ...\n", argv[optind]); printhelp(); exit(12); } if (!iname || !oname) { fplog(stderr, "dd_rescue: (fatal): both input and output have to be specified!\n"); printhelp(); exit(12); } if (lname) { c = openfile(lname, O_WRONLY | O_CREAT /*| O_EXCL*/); logfd = fdopen(c, "a"); } /* sanity checks */ #ifdef O_DIRECT if ((o_dir_in || o_dir_out) && hardbs < sysconf(_SC_PAGESIZE)) { hardbs = sysconf(_SC_PAGESIZE); fplog(stderr, "dd_rescue: (warning): O_DIRECT requires hardbs of at least %i!\n", hardbs); } if (o_dir_in || o_dir_out) fplog(stderr, "dd_rescue: (warning): We don't handle misalignment of last block w/ O_DIRECT!\n"); #endif if (softbs < hardbs) { fplog(stderr, "dd_rescue: (warning): setting hardbs from %i to softbs %i!\n", hardbs, softbs); hardbs = softbs; } if (hardbs <= 0) { fplog(stderr, "dd_rescue: (fatal): you're crazy to set block size to %i!\n", hardbs); cleanup(); exit(15); } /* Have those been set by cmdline params? */ if (ipos == (off_t)-1) ipos = 0; #ifdef O_DIRECT if (posix_memalign(mp, sysconf(_SC_PAGESIZE), softbs)) { fplog(stderr, "dd_rescue: (fatal): allocation of aligned buffer failed!\n"); cleanup(); exit(18); } #else buf = malloc(softbs); if (!buf) { fplog(stderr, "dd_rescue: (fatal): allocation of buffer failed!\n"); cleanup(); exit(18); } #endif memset(buf, 0, softbs); identical = check_identical(iname, oname); if (identical && dotrunc && !force) { fplog(stderr, "dd_rescue: (fatal): infile and outfile are identical and trunc turned on!\n"); cleanup(); exit(19); } /* Open input and output files */ ides = openfile(iname, O_RDONLY | o_dir_in); if (ides < 0) { fplog(stderr, "dd_rescue: (fatal): %s: %s\n", iname, strerror(errno)); cleanup(); exit(22); }; /* Overwrite? */ /* Special case '-': stdout */ if (strcmp(oname, "-")) odes = open(oname, O_WRONLY | o_dir_out, 0640); else odes = 0; if (odes > 0) close(odes); if (odes > 0 && interact) { int a; do { fprintf(stderr, "dd_rescue: (question): %s existing %s [y/n] ?", (dotrunc? "Overwrite": "Write into"), oname); a = toupper(fgetc (stdin)); //fprintf(stderr, "\n"); } while (a != 'Y' && a != 'N'); if (a == 'N') { fplog(stderr, "dd_rescue: (fatal): exit on user request!\n"); cleanup(); exit(23); } } odes = openfile(oname, O_WRONLY | O_CREAT | o_dir_out /*| O_EXCL*/ | dotrunc); if (odes < 0) { fplog(stderr, "dd_rescue: (fatal): %s: %s\n", oname, strerror(errno)); cleanup(); exit(24); } if (pres) copyperm(ides, odes); check_seekable(ides, odes); if (0 && i_chr && o_chr) { fprintf(stderr, "dd_rescue: (fatal): Sorry, there is no support yet for non-seekable\n"); fprintf(stderr, " input and output. This will hopefully change soon ... \n"); exit(19); } /* special case: reverse with ipos == 0 means ipos = end_of_file */ if (reverse && ipos == 0) { ipos = lseek(ides, ipos, SEEK_END); if (ipos == -1) { fprintf(stderr, "dd_rescue: (fatal): could not seek to end of file %s!\n", iname); perror("dd_rescue"); cleanup(); exit(19); } if (verbose) fprintf(stderr, "dd_rescue: (info): ipos set to the end: %.1fk\n", (float)ipos/1024); /* if opos not set, assume same position */ if (opos == (off_t)-1) opos = ipos; /* if explicitly set to zero, assume end of _existing_ file */ if (opos == 0) { opos = lseek(odes, opos, SEEK_END); if (opos == (off_t)-1) { fprintf(stderr, "dd_rescue: (fatal): could not seek to end of file %s!\n", oname); perror(""); cleanup(); exit(19); } /* if existing empty, assume same position */ if (opos == 0) opos = ipos; if (verbose) fprintf(stderr, "dd_rescue: (info): opos set to: %.1fk\n", (float)opos/1024); } } /* if opos not set, assume same position */ if (opos == (off_t)-1) opos = ipos; if (identical) { fplog(stderr, "dd_rescue: (warning): infile and outfile are identical!\n"); if (opos > ipos && !reverse && !force) { fplog(stderr, "dd_rescue: (warning): turned on reverse, as ipos < opos!\n"); reverse = 1; } if (opos < ipos && reverse && !force) { fplog(stderr, "dd_rescue: (warning): turned off reverse, as opos < ipos!\n"); reverse = 0; } } if (o_chr && opos != 0) { fplog(stderr, "dd_rescue: (fatal): outfile not seekable, but opos !=0 requested!\n"); cleanup(); exit(19); } if (i_chr && ipos != 0) { fplog(stderr, "dd_rescue: (fatal): infile not seekable, but ipos !=0 requested!\n"); cleanup(); exit(19); } if (verbose) { printinfo(stderr); if (logfd) printinfo(logfd); } /* Install signal handler */ signal(SIGHUP , breakhandler); signal(SIGINT , breakhandler); signal(SIGTERM, breakhandler); signal(SIGQUIT, breakhandler); /* Save time and start to work */ startclock = clock(); gettimeofday(&starttime, NULL); memcpy(&lasttime, &starttime, sizeof(lasttime)); if (!quiet) { fprintf(stderr, "%s%s%s", down, down, down); printstatus(stderr, 0, softbs, 0); } c = copyfile(maxxfer, softbs); gettimeofday(¤ttime, NULL); printreport(); cleanup(); if (pres) copytimes(iname, oname); return c; }