2 * ecp - copy a file fast (in big blocks), cope with errors, optionally verify.
4 * Transfers a block at a time. On error, retries one sector at a time,
5 * and reports all errors on the retry.
6 * Unlike dd, ecp ignores EOF, since it is sometimes reported on error.
7 * Also unlike `dd conv=noerror,sync', ecp doesn't get stuck nor give up.
9 * Written by Geoff Collyer, originally to run on RSX-11M(!) in 1979.
10 * Later simplified for UNIX and ultimately Plan 9.
16 /* fundamental constants */
21 Noseek = 0, /* need not seek, may seek on seekable files */
28 /* tunable parameters */
30 Defsectsz = 512, /* default sector size */
31 /* 10K is a good size for HP WORM drives */
32 Defblksz = 16*1024, /* default block (big-transfer) size */
33 Mingoodblks = 3, /* after this many, go back to fast mode */
36 #define TTY "/dev/cons" /* plan 9 */
38 #define badsect(errno) ((errno) != Enone) /* was last transfer in error? */
40 /* disk address (in bytes or sectors), also type of 2nd arg. to seek */
42 typedef vlong Sdaddr; /* signed disk address */
43 typedef long Rdwrfn(int, void *, long); /* plan 9 read or write */
52 ulong maxconerrs; /* maximum consecutive errors */
53 ulong conerrs; /* current consecutive errors */
57 Daddr lasterr; /* sector #s */
65 static int reblock = No, progress = No, swizzle = No;
66 static int reverse = No;
67 static ulong sectsz = Defsectsz;
68 static ulong blocksize = Defblksz;
70 static char *buf, *vfybuf;
74 * warning - print best error message possible and clear errno
77 warning(char *s1, char *s2)
79 char err[100], msg[256];
80 char *np, *ep = msg + sizeof msg - 1;
82 errstr(err, sizeof err); /* save error string */
83 np = seprint(msg, ep, "%s: ", argv0);
84 np = seprint(np, ep, s1, s2);
85 errstr(err, sizeof err); /* restore error string */
86 seprint(np, ep, ": %r\n");
92 eopen(char *file, int mode)
94 int fd = open(file, mode);
97 sysfatal("can't open %s: %r", file);
101 static int /* boolean */
102 confirm(File *src, File *dest)
104 int absent, n, tty = eopen(TTY, 2);
108 if ((stp = dirstat(src->name)) == nil)
109 sysfatal("no input file %s: %r", src->name);
111 stp = dirstat(dest->name);
112 absent = (stp == nil);
114 fprint(2, "%s: copy %s to %s%s? ", argv0, src->name, dest->name,
115 (absent? " (missing)": ""));
116 n = read(tty, &c, 1);
120 while (n > 0 && junk != '\n')
121 n = read(tty, &junk, 1);
123 if (isascii(c) && isupper(c))
129 sectid(File *fp, Daddr sect)
131 static char sectname[256];
133 if (fp->startsect == 0)
134 snprint(sectname, sizeof sectname, "%s sector %llud",
137 snprint(sectname, sizeof sectname,
138 "%s sector %llud (relative %llud)",
139 fp->name, sect + fp->startsect, sect);
144 io_expl(File *fp, char *rw, Daddr sect) /* explain an i/o error */
146 /* print only first 2 bad sectors in a range, if going forward */
147 if (reverse || fp->conerrs == 0) {
150 snprint(msg, sizeof msg, "%s %s", rw, sectid(fp, sect));
152 } else if (fp->conerrs == 1)
153 fprint(2, "%s: ...\n", argv0);
157 repos(File *fp, Daddr sect)
160 sysfatal("%s: trying to seek on unseekable file", fp->name);
161 if (seek(fp->fd, (sect+fp->startsect)*sectsz, 0) == -1)
162 sysfatal("can't seek on %s: %r", fp->name);
172 * transfer (many) sectors. reblock input as needed.
173 * returns Enone if no failures, others on failure with errstr set.
176 bio(File *fp, Rdwrfn *rdwr, char *buff, Daddr stsect, int sects, int mustseek)
179 ulong toread, bytes = sects * sectsz;
180 static int reblocked = 0;
184 sysfatal("%s: need to seek on unseekable file",
188 if ((long)blocksize != blocksize || (long)bytes != bytes)
189 sysfatal("i/o count too big: %lud", bytes);
192 xfered = (*rdwr)(fp->fd, buff, bytes);
194 return Enone; /* did as we asked */
196 return Eio; /* out-and-out i/o error */
198 * Kernel transferred less than asked. Shouldn't happen;
199 * probably indicates disk driver error or trying to
200 * transfer past the end of a disk partition. Treat as an
201 * I/O error that reads zeros past the point of error,
202 * unless reblocking input and this is a read.
207 memset(buff+xfered, '\0', bytes-xfered);
208 return Eio; /* short read */
211 /* for pipes that return less than asked */
212 if (progress && !reblocked) {
213 fprint(2, "%s: reblocking input\n", argv0);
216 for (toread = bytes - xfered; toread != 0; toread -= xfered) {
217 xfered = (*rdwr)(fp->fd, buff+bytes-toread, toread);
222 return Eio; /* out-and-out i/o error */
223 if (toread != 0) /* early EOF? */
224 memset(buff+bytes-toread, '\0', toread);
228 /* called only after a single-sector transfer */
230 toomanyerrs(File *fp, Daddr sect)
232 if (sect == fp->lasterr+1)
237 return fp->maxconerrs != 0 && fp->conerrs >= fp->maxconerrs &&
244 if (!reverse && fp->conerrs > 0)
245 fprint(2, "%s: %lld: ... last bad sector in range\n",
250 transfer(File *fp, Rdwrfn *rdwr, char *buff, Daddr stsect, int sects,
253 int res = bio(fp, rdwr, buff, stsect, sects, mustseek);
256 fp->fast = 0; /* read single sectors for a while */
259 fp->lastgood = stsect + sects - 1;
264 * Read or write many sectors at once.
265 * If it fails, retry the individual sectors and report errors.
268 bigxfer(File *fp, Rdwrfn *rdwr, char *buff, Daddr stsect, int sects,
271 int i, badsects = 0, wasfast = fp->fast;
272 char *rw = (rdwr == read? "read": "write");
275 if (!badsect(transfer(fp, rdwr, buff, stsect, sects, mustseek)))
278 fprint(2, "%s: breaking up big transfer on %s error "
279 "`%r' on %s\n", argv0, rw, sectid(fp, stsect));
282 for (i = 0; i < sects; i++)
283 if (badsect(transfer(fp, rdwr, buff+i*sectsz, stsect+i, 1,
285 io_expl(fp, rw, stsect+i);
288 if (toomanyerrs(fp, stsect+i))
289 sysfatal("more than %lud consecutive I/O errors",
299 fprint(2, "%s: %s error on big transfer at %s but none "
300 "on retries!\n", argv0, rw, sectid(fp, stsect));
302 if (fp->congoodblks >= Mingoodblks) {
303 fprint(2, "%s: %s: back to big transfers\n", argv0,
309 * the last sector could have been in error, so the seek pointer
310 * may need to be corrected.
312 repos(fp, stsect + sects);
316 vrfyfailed(File *src, File *dest, Daddr stsect)
318 char *srcsect = strdup(sectid(src, stsect));
320 fprint(2, "%s: verify failed at %s (%s)\n", argv0, srcsect,
321 sectid(dest, stsect));
326 * I've seen SCSI read errors that the kernel printed but then didn't
327 * report to the program doing the read, so if a big verify fails,
328 * break it up and verify each sector separately to isolate the bad sector(s).
330 int /* error count */
331 verify(File *src, File *dest, char *buff, char *buft, Daddr stsect,
336 for (i = 0; i < sectors; i++)
337 if (memcmp(buff + i*sectsz, buft + i*sectsz, sectsz) != 0)
340 return errors; /* normal case */
343 vrfyfailed(src, dest, stsect);
347 /* re-read and verify each sector individually */
349 for (i = 0; i < sectors; i++) {
350 int thissect = stsect + i;
352 if (badsect(bio(src, read, buff, thissect, 1, Mustseek)))
353 io_expl(src, "read", thissect);
354 if (badsect(bio(dest, read, buft, thissect, 1, Mustseek)))
355 io_expl(dest, "write", thissect);
356 if (memcmp(buff, buft, sectsz) != 0) {
357 vrfyfailed(src, dest, thissect);
362 char *srcsect = strdup(sectid(src, stsect));
364 fprint(2, "%s: verification failed on big read at %s (%s) "
365 "but not on retries!\n", argv0, srcsect,
366 sectid(dest, stsect));
370 * the last sector of each could have been in error, so the seek
371 * pointers may need to be corrected.
373 repos(src, stsect + sectors);
374 repos(dest, stsect + sectors);
379 * start is starting sector of proposed transfer;
380 * nsects is the total number of sectors being copied;
381 * maxxfr is the block size in sectors.
384 sectsleft(Daddr start, Daddr nsects, int maxxfr)
386 /* nsects-start is sectors to the end */
387 if (start + maxxfr <= nsects - 1)
390 return nsects - start;
398 swizzlebits(char *buff, int sects)
402 endbp = (uchar *)(buff+sects*sectsz);
403 for (bp = (uchar *)buff; bp < endbp; bp++)
404 *bp = ~(*bp>>Rotbits | *bp<<(8-Rotbits));
408 * copy at most blksects sectors, with error retries.
409 * stsect is relative to the start of the copy; 0 is the first sector.
410 * to get actual sector numbers, add e.g. dest->startsect.
413 copysects(File *src, File *dest, Daddr stsect, Daddr nsects, int mustseek)
415 int xfrsects = sectsleft(stsect, nsects, blksects);
417 if (xfrsects > blksects) {
418 fprint(2, "%s: block size of %d is too big.\n", argv0, xfrsects);
419 exits("block size too big");
421 bigxfer(src, read, buf, stsect, xfrsects, mustseek);
423 swizzlebits(buf, xfrsects);
424 bigxfer(dest, write, buf, stsect, xfrsects, mustseek);
425 /* give a few reassurances at the start, then every 10MB */
427 (stsect < blksects*10 || stsect%(10*1024*1024/sectsz) == 0))
428 fprint(2, "%s: copied%s to relative sector %llud\n", argv0,
429 (swizzle? " swizzled": ""), stsect + xfrsects - 1);
434 * verify at most blksects sectors, with error retries.
435 * return error count.
438 vrfysects(File *src, File *dest, Daddr stsect, Daddr nsects, int mustseek)
440 int xfrsects = sectsleft(stsect, nsects, blksects);
442 if (xfrsects > blksects) {
443 fprint(2, "%s: block size of %d is too big.\n", argv0, xfrsects);
444 exits("block size too big");
446 bigxfer(src, read, buf, stsect, xfrsects, mustseek);
447 bigxfer(dest, read, vfybuf, stsect, xfrsects, mustseek);
448 return verify(src, dest, buf, vfybuf, stsect, xfrsects);
452 setupfile(File *fp, int mode)
454 fp->fd = open(fp->name, mode);
456 sysfatal("can't open %s: %r", fp->name);
457 fp->seekable = (seek(fp->fd, 0, 1) >= 0);
458 if (fp->startsect != 0)
463 copyfile(File *src, File *dest, Daddr nsects, int plsverify)
465 Sdaddr stsect, vererrs = 0;
468 setupfile(src, OREAD);
469 if ((stp = dirstat(dest->name)) == nil) {
470 int fd = create(dest->name, ORDWR, 0666);
476 setupfile(dest, ORDWR);
479 fprint(2, "%s: copying first sectors\n", argv0);
481 for (stsect = (nsects/blksects)*blksects; stsect >= 0;
483 vererrs += copysects(src, dest, stsect, nsects, Mustseek);
485 for (stsect = 0; stsect < nsects; stsect += blksects)
486 vererrs += copysects(src, dest, stsect, nsects, Noseek);
492 * verification is done as a separate pass rather than immediately after
493 * writing, in part to defeat caching in clever disk controllers.
494 * we really want to see the bits that hit the disk.
497 fprint(2, "%s: copy done; verifying...\n", argv0);
500 for (stsect = 0; stsect < nsects; stsect += blksects) /* forward */
501 vererrs += vrfysects(src, dest, stsect, nsects, Noseek);
503 fprint(2, "%s: no", argv0);
505 fprint(2, "%s: %llud", argv0, vererrs);
506 fprint(2, " error%s during verification\n",
507 (vererrs != 1? "s": ""));
517 fprint(2, "usage: %s [-bcprvZ][-B blocksz][-e errs][-s sectsz]"
518 "[-i issect][-o ossect] sectors from to\n", argv0);
525 memset(fp, 0, sizeof *fp);
532 main(int argc, char **argv)
534 int errflg = 0, plsconfirm = No, plsverify = No;
546 lval = atol(EARGF(usage()));
555 lval = atol(EARGF(usage()));
558 src.maxconerrs = lval;
559 dest.maxconerrs = lval;
562 sect = atoll(EARGF(usage()));
565 src.startsect = sect;
568 sect = atoll(EARGF(usage()));
571 dest.startsect = sect;
580 sectsz = atol(EARGF(usage()));
581 if (sectsz <= 0 || sectsz % 512 != 0)
594 if (errflg || argc != 3)
596 if (blocksize <= 0 || blocksize % sectsz != 0)
597 sysfatal("block size not a multiple of sector size");
599 if (!isascii(argv[0][0]) || !isdigit(argv[0][0])) {
600 fprint(2, "%s: %s is not numeric\n", argv0, argv[0]);
601 exits("non-numeric sector count");
606 blksects = blocksize / sectsz;
609 buf = malloc(blocksize);
610 vfybuf = malloc(blocksize);
611 if (buf == nil || vfybuf == nil)
612 sysfatal("out of memory: %r");
614 if (plsconfirm? confirm(&src, &dest): Yes)
615 copyfile(&src, &dest, atoll(argv[0]), plsverify);
616 exits(src.harderrs || dest.harderrs? "hard errors": 0);