From: Martin Mares Date: Tue, 13 Mar 2018 11:22:08 +0000 (+0100) Subject: A digging dd X-Git-Url: http://mj.ucw.cz/gitweb/?a=commitdiff_plain;h=c789bb0a5bcfb02898027f090da9ff94def066cb;p=misc.git A digging dd --- diff --git a/ucw/ddigger.c b/ucw/ddigger.c new file mode 100644 index 0000000..3df6981 --- /dev/null +++ b/ucw/ddigger.c @@ -0,0 +1,179 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static char *dev_name; +static char *out_name; +static char *stat_name; +static uint block_size = 1048576; +static uint error_skip; +static u64 start_pos; +static u64 end_pos = ~(u64)0; + +struct stat_rec { + u64 pos; + u64 len; +}; + +static struct stat_rec *status; + +#define ASORT_PREFIX(x) ss_##x +#define ASORT_KEY_TYPE struct stat_rec +#define ASORT_LT(x,y) x.pos < y.pos +#include + +static void stat_read(int fd) +{ + u64 len = ucw_seek(fd, 0, SEEK_END); + ucw_seek(fd, 0, SEEK_SET); + if (len % sizeof(struct stat_rec)) + die("Malformed status file"); + if (len > ~0U) + die("Status file too long"); + + uns items = len / sizeof(struct stat_rec); + GARY_INIT(status, items); + if (careful_read(fd, status, len) <= 0) + die("Error reading status file"); + + ss_sort(status, items); + +#if 0 + for (uint i=0; i\tTransfer block size (default=1M)"), + OPT_UINT('e', "error-skip", error_skip, OPT_REQUIRED_VALUE, "\tHow far to skip on error (default=block size)"), + OPT_U64(0, "start", start_pos, OPT_REQUIRED_VALUE, "\tStart position (default=beginning of device)"), + OPT_U64(0, "end", end_pos, OPT_REQUIRED_VALUE, "\tEnd position (default=end of device)"), + OPT_END + } +}; + +int main(int argc UNUSED, char **argv) +{ + opt_parse(&options, argv+1); + if (!error_skip) + error_skip = block_size; + + int dev_fd = ucw_open(dev_name, O_RDONLY | O_DIRECT); + if (dev_fd < 0) + die("Cannot open block device %s: %m", dev_name); + + u64 dev_size; + if (ioctl(dev_fd, BLKGETSIZE64, &dev_size) < 0) + die("BLKGETSIZE64: %m"); + start_pos = MIN(dev_size, start_pos); + end_pos = MIN(dev_size, end_pos); + + int out_fd = ucw_open(out_name, O_RDWR | O_CREAT, 0666); + if (out_fd < 0) + die("Cannot open %s: %m", out_name); + + if (ucw_ftruncate(out_fd, dev_size) < 0) + die("Cannot resize %s: %m", out_name); + + int stat_fd = ucw_open(stat_name, O_RDWR | O_CREAT, 0666); + if (stat_fd < 0) + die("Cannot open %s: %m", stat_name); + stat_read(stat_fd); + u64 remains = (end_pos - start_pos) + stat_have(); + + ucw_seek(stat_fd, 0, SEEK_END); + *GARY_PUSH(status) = (struct stat_rec) { .pos = end_pos, .len = 0 }; + + byte *buf = big_alloc(MAX(block_size, 4096)); + u64 pos = start_pos; + u64 have = 0; + uint stat_i = 0; + uint errors = 0; + + while (pos < end_pos) + { + while (status[stat_i].pos + status[stat_i].len < pos) + stat_i++; + if (status[stat_i].pos <= pos) + { + pos = status[stat_i].pos + status[stat_i].len; + stat_i++; + continue; + } + + u64 len = MIN(block_size, status[stat_i].pos - pos); + if (pos % block_size) + len = MIN(len, block_size - pos % block_size); + +#define GAUGE(curr,max) (curr), (max), 100*(double)(curr)/(double)((max)?:1) + fprintf(stderr, "Pos: %zd / %zd (%.2f%%) Rel: %zd / %zd (%.2f%%) Have: %zd / %zd (%.2f%%) Errors: %u\r", + GAUGE(pos, dev_size), + GAUGE(pos-start_pos, end_pos-start_pos), + GAUGE(have, remains), + errors); +#undef GAUGE + + if (ucw_seek(dev_fd, pos, SEEK_SET) < 0) + die("lseek: %m"); + if (ucw_seek(out_fd, pos, SEEK_SET) < 0) + die("lseek: %m"); + + ssize_t done = read(dev_fd, buf, len); + if (done < (ssize_t) len) + { + fprintf(stderr, "\n"); + // msg(L_INFO, "Error: %d bytes read at position %jd", (int) MAX(done, 0), (intmax_t) pos); + errors++; + pos += error_skip; + } + else + { + if (careful_write(out_fd, buf, len) < 0) + die("Error writing %s: %m", out_name); + + struct stat_rec sr = { .pos = pos, .len = len }; + if (careful_write(stat_fd, &sr, sizeof(sr)) < 0) + die("Error writing status file: %m"); + + pos += len; + have += len; + } + } + + return 0; +}