1 /* 2 * ecp - copy a file fast (in big blocks), cope with errors, optionally verify. 3 * 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. 8 * 9 * Written by Geoff Collyer, originally to run on RSX-11M(!) in 1979. 10 * Later simplified for UNIX and ultimately Plan 9. 11 */ 12 #include <u.h> 13 #include <libc.h> 14 #include <ctype.h> 15 16 /* fundamental constants */ 17 enum { 18 No = 0, 19 Yes, 20 21 Noseek = 0, /* need not seek, may seek on seekable files */ 22 Mustseek, 23 24 Enone = 0, 25 Eio, 26 }; 27 28 /* tunable parameters */ 29 enum { 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 */ 34 }; 35 36 #define TTY "/dev/cons" /* plan 9 */ 37 38 #define badsect(errno) ((errno) != Enone) /* was last transfer in error? */ 39 40 /* disk address (in bytes or sectors), also type of 2nd arg. to seek */ 41 typedef uvlong Daddr; 42 typedef vlong Sdaddr; /* signed disk address */ 43 typedef long Rdwrfn(int, void *, long); /* plan 9 read or write */ 44 45 typedef struct { 46 char *name; 47 int fd; 48 Daddr startsect; 49 int fast; 50 int seekable; 51 52 ulong maxconerrs; /* maximum consecutive errors */ 53 ulong conerrs; /* current consecutive errors */ 54 Daddr congoodblks; 55 56 Daddr harderrs; 57 Daddr lasterr; /* sector #s */ 58 Daddr lastgood; 59 } File; 60 61 /* exports */ 62 char *argv0; 63 64 /* privates */ 65 static int reblock = No, progress = No, swizzle = No; 66 static int reverse = No; 67 static ulong sectsz = Defsectsz; 68 static ulong blocksize = Defblksz; 69 70 static char *buf, *vfybuf; 71 static int blksects; 72 73 /* 74 * warning - print best error message possible and clear errno 75 */ 76 void 77 warning(char *s1, char *s2) 78 { 79 char err[100], msg[256]; 80 char *np, *ep = msg + sizeof msg - 1; 81 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"); 87 88 fprint(2, "%s", msg); 89 } 90 91 int 92 eopen(char *file, int mode) 93 { 94 int fd = open(file, mode); 95 96 if (fd < 0) 97 sysfatal("can't open %s: %r", file); 98 return fd; 99 } 100 101 static int /* boolean */ 102 confirm(File *src, File *dest) 103 { 104 int absent, n, tty = eopen(TTY, 2); 105 char c, junk; 106 Dir *stp; 107 108 if ((stp = dirstat(src->name)) == nil) 109 sysfatal("no input file %s: %r", src->name); 110 free(stp); 111 stp = dirstat(dest->name); 112 absent = (stp == nil); 113 free(stp); 114 fprint(2, "%s: copy %s to %s%s? ", argv0, src->name, dest->name, 115 (absent? " (missing)": "")); 116 n = read(tty, &c, 1); 117 junk = c; 118 if (n < 1) 119 c = 'n'; 120 while (n > 0 && junk != '\n') 121 n = read(tty, &junk, 1); 122 close(tty); 123 if (isascii(c) && isupper(c)) 124 c = tolower(c); 125 return c == 'y'; 126 } 127 128 static char * 129 sectid(File *fp, Daddr sect) 130 { 131 static char sectname[256]; 132 133 if (fp->startsect == 0) 134 snprint(sectname, sizeof sectname, "%s sector %llud", 135 fp->name, sect); 136 else 137 snprint(sectname, sizeof sectname, 138 "%s sector %llud (relative %llud)", 139 fp->name, sect + fp->startsect, sect); 140 return sectname; 141 } 142 143 static void 144 io_expl(File *fp, char *rw, Daddr sect) /* explain an i/o error */ 145 { 146 /* print only first 2 bad sectors in a range, if going forward */ 147 if (reverse || fp->conerrs == 0) { 148 char msg[128]; 149 150 snprint(msg, sizeof msg, "%s %s", rw, sectid(fp, sect)); 151 warning("%s", msg); 152 } else if (fp->conerrs == 1) 153 fprint(2, "%s: ...\n", argv0); 154 } 155 156 static void 157 repos(File *fp, Daddr sect) 158 { 159 if (!fp->seekable) 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); 163 } 164 165 static void 166 rewind(File *fp) 167 { 168 repos(fp, 0); 169 } 170 171 /* 172 * transfer (many) sectors. reblock input as needed. 173 * returns Enone if no failures, others on failure with errstr set. 174 */ 175 static int 176 bio(File *fp, Rdwrfn *rdwr, char *buff, Daddr stsect, int sects, int mustseek) 177 { 178 int xfered; 179 ulong toread, bytes = sects * sectsz; 180 static int reblocked = 0; 181 182 if (mustseek) { 183 if (!fp->seekable) 184 sysfatal("%s: need to seek on unseekable file", 185 fp->name); 186 repos(fp, stsect); 187 } 188 if ((long)blocksize != blocksize || (long)bytes != bytes) 189 sysfatal("i/o count too big: %lud", bytes); 190 191 werrstr(""); 192 xfered = (*rdwr)(fp->fd, buff, bytes); 193 if (xfered == bytes) 194 return Enone; /* did as we asked */ 195 if (xfered < 0) 196 return Eio; /* out-and-out i/o error */ 197 /* 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. 203 */ 204 if (rdwr == write) 205 return Eio; 206 if (!reblock) { 207 memset(buff+xfered, '\0', bytes-xfered); 208 return Eio; /* short read */ 209 } 210 211 /* for pipes that return less than asked */ 212 if (progress && !reblocked) { 213 fprint(2, "%s: reblocking input\n", argv0); 214 reblocked++; 215 } 216 for (toread = bytes - xfered; toread != 0; toread -= xfered) { 217 xfered = (*rdwr)(fp->fd, buff+bytes-toread, toread); 218 if (xfered <= 0) 219 break; 220 } 221 if (xfered < 0) 222 return Eio; /* out-and-out i/o error */ 223 if (toread != 0) /* early EOF? */ 224 memset(buff+bytes-toread, '\0', toread); 225 return Enone; 226 } 227 228 /* called only after a single-sector transfer */ 229 static int 230 toomanyerrs(File *fp, Daddr sect) 231 { 232 if (sect == fp->lasterr+1) 233 fp->conerrs++; 234 else 235 fp->conerrs = 0; 236 fp->lasterr = sect; 237 return fp->maxconerrs != 0 && fp->conerrs >= fp->maxconerrs && 238 fp->lastgood == -1; 239 } 240 241 static void 242 ckendrange(File *fp) 243 { 244 if (!reverse && fp->conerrs > 0) 245 fprint(2, "%s: %lld: ... last bad sector in range\n", 246 argv0, fp->lasterr); 247 } 248 249 static int 250 transfer(File *fp, Rdwrfn *rdwr, char *buff, Daddr stsect, int sects, 251 int mustseek) 252 { 253 int res = bio(fp, rdwr, buff, stsect, sects, mustseek); 254 255 if (badsect(res)) { 256 fp->fast = 0; /* read single sectors for a while */ 257 fp->congoodblks = 0; 258 } else 259 fp->lastgood = stsect + sects - 1; 260 return res; 261 } 262 263 /* 264 * Read or write many sectors at once. 265 * If it fails, retry the individual sectors and report errors. 266 */ 267 static void 268 bigxfer(File *fp, Rdwrfn *rdwr, char *buff, Daddr stsect, int sects, 269 int mustseek) 270 { 271 int i, badsects = 0, wasfast = fp->fast; 272 char *rw = (rdwr == read? "read": "write"); 273 274 if (fp->fast) { 275 if (!badsect(transfer(fp, rdwr, buff, stsect, sects, mustseek))) 276 return; 277 if (progress) 278 fprint(2, "%s: breaking up big transfer on %s error " 279 "`%r' on %s\n", argv0, rw, sectid(fp, stsect)); 280 } 281 282 for (i = 0; i < sects; i++) 283 if (badsect(transfer(fp, rdwr, buff+i*sectsz, stsect+i, 1, 284 Mustseek))) { 285 io_expl(fp, rw, stsect+i); 286 badsects++; 287 fp->harderrs++; 288 if (toomanyerrs(fp, stsect+i)) 289 sysfatal("more than %lud consecutive I/O errors", 290 fp->maxconerrs); 291 } else { 292 ckendrange(fp); 293 fp->conerrs = 0; 294 } 295 if (badsects == 0) { 296 ckendrange(fp); 297 fp->conerrs = 0; 298 if (wasfast) 299 fprint(2, "%s: %s error on big transfer at %s but none " 300 "on retries!\n", argv0, rw, sectid(fp, stsect)); 301 ++fp->congoodblks; 302 if (fp->congoodblks >= Mingoodblks) { 303 fprint(2, "%s: %s: back to big transfers\n", argv0, 304 fp->name); 305 fp->fast = 1; 306 } 307 } else 308 /* 309 * the last sector could have been in error, so the seek pointer 310 * may need to be corrected. 311 */ 312 repos(fp, stsect + sects); 313 } 314 315 static void 316 vrfyfailed(File *src, File *dest, Daddr stsect) 317 { 318 char *srcsect = strdup(sectid(src, stsect)); 319 320 fprint(2, "%s: verify failed at %s (%s)\n", argv0, srcsect, 321 sectid(dest, stsect)); 322 free(srcsect); 323 } 324 325 /* 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). 329 */ 330 int /* error count */ 331 verify(File *src, File *dest, char *buff, char *buft, Daddr stsect, 332 int sectors) 333 { 334 int i, errors = 0; 335 336 for (i = 0; i < sectors; i++) 337 if (memcmp(buff + i*sectsz, buft + i*sectsz, sectsz) != 0) 338 errors++; 339 if (errors == 0) 340 return errors; /* normal case */ 341 342 if (sectors == 1) { 343 vrfyfailed(src, dest, stsect); 344 return errors; 345 } 346 347 /* re-read and verify each sector individually */ 348 errors = 0; 349 for (i = 0; i < sectors; i++) { 350 int thissect = stsect + i; 351 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); 358 ++errors; 359 } 360 } 361 if (errors == 0) { 362 char *srcsect = strdup(sectid(src, stsect)); 363 364 fprint(2, "%s: verification failed on big read at %s (%s) " 365 "but not on retries!\n", argv0, srcsect, 366 sectid(dest, stsect)); 367 free(srcsect); 368 } 369 /* 370 * the last sector of each could have been in error, so the seek 371 * pointers may need to be corrected. 372 */ 373 repos(src, stsect + sectors); 374 repos(dest, stsect + sectors); 375 return errors; 376 } 377 378 /* 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. 382 */ 383 int 384 sectsleft(Daddr start, Daddr nsects, int maxxfr) 385 { 386 /* nsects-start is sectors to the end */ 387 if (start + maxxfr <= nsects - 1) 388 return maxxfr; 389 else 390 return nsects - start; 391 } 392 393 enum { 394 Rotbits = 3, 395 }; 396 397 void 398 swizzlebits(char *buff, int sects) 399 { 400 uchar *bp, *endbp; 401 402 endbp = (uchar *)(buff+sects*sectsz); 403 for (bp = (uchar *)buff; bp < endbp; bp++) 404 *bp = ~(*bp>>Rotbits | *bp<<(8-Rotbits)); 405 } 406 407 /* 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. 411 */ 412 static int 413 copysects(File *src, File *dest, Daddr stsect, Daddr nsects, int mustseek) 414 { 415 int xfrsects = sectsleft(stsect, nsects, blksects); 416 417 if (xfrsects > blksects) { 418 fprint(2, "%s: block size of %d is too big.\n", argv0, xfrsects); 419 exits("block size too big"); 420 } 421 bigxfer(src, read, buf, stsect, xfrsects, mustseek); 422 if (swizzle) 423 swizzlebits(buf, xfrsects); 424 bigxfer(dest, write, buf, stsect, xfrsects, mustseek); 425 /* give a few reassurances at the start, then every 10MB */ 426 if (progress && 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); 430 return 0; 431 } 432 433 /* 434 * verify at most blksects sectors, with error retries. 435 * return error count. 436 */ 437 static int 438 vrfysects(File *src, File *dest, Daddr stsect, Daddr nsects, int mustseek) 439 { 440 int xfrsects = sectsleft(stsect, nsects, blksects); 441 442 if (xfrsects > blksects) { 443 fprint(2, "%s: block size of %d is too big.\n", argv0, xfrsects); 444 exits("block size too big"); 445 } 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); 449 } 450 451 static void 452 setupfile(File *fp, int mode) 453 { 454 fp->fd = open(fp->name, mode); 455 if (fp->fd < 0) 456 sysfatal("can't open %s: %r", fp->name); 457 fp->seekable = (seek(fp->fd, 0, 1) >= 0); 458 if (fp->startsect != 0) 459 rewind(fp); 460 } 461 462 static Daddr 463 copyfile(File *src, File *dest, Daddr nsects, int plsverify) 464 { 465 Sdaddr stsect, vererrs = 0; 466 Dir *stp; 467 468 setupfile(src, OREAD); 469 if ((stp = dirstat(dest->name)) == nil) { 470 int fd = create(dest->name, ORDWR, 0666); 471 472 if (fd >= 0) 473 close(fd); 474 } 475 free(stp); 476 setupfile(dest, ORDWR); 477 478 if (progress) 479 fprint(2, "%s: copying first sectors\n", argv0); 480 if (reverse) 481 for (stsect = (nsects/blksects)*blksects; stsect >= 0; 482 stsect -= blksects) 483 vererrs += copysects(src, dest, stsect, nsects, Mustseek); 484 else { 485 for (stsect = 0; stsect < nsects; stsect += blksects) 486 vererrs += copysects(src, dest, stsect, nsects, Noseek); 487 ckendrange(src); 488 ckendrange(dest); 489 } 490 491 /* 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. 495 */ 496 if (plsverify) { 497 fprint(2, "%s: copy done; verifying...\n", argv0); 498 rewind(src); 499 rewind(dest); 500 for (stsect = 0; stsect < nsects; stsect += blksects) /* forward */ 501 vererrs += vrfysects(src, dest, stsect, nsects, Noseek); 502 if (vererrs <= 0) 503 fprint(2, "%s: no", argv0); 504 else 505 fprint(2, "%s: %llud", argv0, vererrs); 506 fprint(2, " error%s during verification\n", 507 (vererrs != 1? "s": "")); 508 } 509 close(src->fd); 510 close(dest->fd); 511 return vererrs; 512 } 513 514 static void 515 usage(void) 516 { 517 fprint(2, "usage: %s [-bcprvZ][-B blocksz][-e errs][-s sectsz]" 518 "[-i issect][-o ossect] sectors from to\n", argv0); 519 exits("usage"); 520 } 521 522 void 523 initfile(File *fp) 524 { 525 memset(fp, 0, sizeof *fp); 526 fp->fast = 1; 527 fp->lasterr = -1; 528 fp->lastgood = -1; 529 } 530 531 void 532 main(int argc, char **argv) 533 { 534 int errflg = 0, plsconfirm = No, plsverify = No; 535 long lval; 536 File src, dest; 537 Sdaddr sect; 538 539 initfile(&src); 540 initfile(&dest); 541 ARGBEGIN { 542 case 'b': 543 reblock = Yes; 544 break; 545 case 'B': 546 lval = atol(EARGF(usage())); 547 if (lval < 0) 548 usage(); 549 blocksize = lval; 550 break; 551 case 'c': 552 plsconfirm = Yes; 553 break; 554 case 'e': 555 lval = atol(EARGF(usage())); 556 if (lval < 0) 557 usage(); 558 src.maxconerrs = lval; 559 dest.maxconerrs = lval; 560 break; 561 case 'i': 562 sect = atoll(EARGF(usage())); 563 if (sect < 0) 564 usage(); 565 src.startsect = sect; 566 break; 567 case 'o': 568 sect = atoll(EARGF(usage())); 569 if (sect < 0) 570 usage(); 571 dest.startsect = sect; 572 break; 573 case 'p': 574 progress = Yes; 575 break; 576 case 'r': 577 reverse = Yes; 578 break; 579 case 's': 580 sectsz = atol(EARGF(usage())); 581 if (sectsz <= 0 || sectsz % 512 != 0) 582 usage(); 583 break; 584 case 'v': 585 plsverify = Yes; 586 break; 587 case 'Z': 588 swizzle = Yes; 589 break; 590 default: 591 errflg++; 592 break; 593 } ARGEND 594 if (errflg || argc != 3) 595 usage(); 596 if (blocksize <= 0 || blocksize % sectsz != 0) 597 sysfatal("block size not a multiple of sector size"); 598 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"); 602 } 603 src.name = argv[1]; 604 dest.name = argv[2]; 605 606 blksects = blocksize / sectsz; 607 if (blksects < 1) 608 blksects = 1; 609 buf = malloc(blocksize); 610 vfybuf = malloc(blocksize); 611 if (buf == nil || vfybuf == nil) 612 sysfatal("out of memory: %r"); 613 614 if (plsconfirm? confirm(&src, &dest): Yes) 615 copyfile(&src, &dest, atoll(argv[0]), plsverify); 616 exits(src.harderrs || dest.harderrs? "hard errors": 0); 617 } 618