]> mj.ucw.cz Git - misc.git/commitdiff
verbose-copy
authorMartin Mares <mj@ucw.cz>
Sat, 29 Jan 2022 20:13:03 +0000 (21:13 +0100)
committerMartin Mares <mj@ucw.cz>
Sat, 29 Jan 2022 20:13:03 +0000 (21:13 +0100)
verbose-copy.c [new file with mode: 0644]

diff --git a/verbose-copy.c b/verbose-copy.c
new file mode 100644 (file)
index 0000000..6b7c136
--- /dev/null
@@ -0,0 +1,96 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <linux/fs.h>
+
+// XXX: Assuming block size == 512
+
+int main(int argc, char **argv)
+{
+       if (argc != 3) {
+               fprintf(stderr, "Usage: %s <from> <to>\n", argv[0]);
+               return 1;
+       }
+
+       int fi = open(argv[1], O_RDONLY);
+       if (fi < 0) {
+               fprintf(stderr, "Cannot open source %s: %m\n", argv[1]);
+               return 1;
+       }
+
+       int fo = open(argv[2], O_WRONLY);
+       if (fo < 0) {
+               fprintf(stderr, "Cannot open destination %s: %m\n", argv[1]);
+               return 1;
+       }
+
+       struct stat sti, sto;
+       if (fstat(fi, &sti) < 0 || fstat(fo, &sto) < 0) {
+               fprintf(stderr, "Stat failed: %m\n");
+               return 1;
+       }
+       if (!S_ISBLK(sti.st_mode)) {
+               fprintf(stderr, "Input is not a block device\n");
+               return 1;
+       }
+       if (!S_ISBLK(sto.st_mode)) {
+               fprintf(stderr, "Output is not a block device\n");
+               return 1;
+       }
+
+       unsigned long leni, leno;
+       if (ioctl(fi, BLKGETSIZE, &leni) < 0 ||
+           ioctl(fo, BLKGETSIZE, &leno) < 0) {
+               fprintf(stderr, "Cannot get device size: %m\n");
+               return 1;
+       }
+       if (leni > leno) {
+               fprintf(stderr, "Will not fit: %ld > %ld\n", leni, leno);
+               return 1;
+       }
+
+#define BUFSIZE 65536
+       char *buf = mmap(NULL, BUFSIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+       if (buf == MAP_FAILED) {
+               fprintf(stderr, "Cannot mmap buffer: %m\n");
+               return 1;
+       }
+
+       unsigned int remi = leni;
+       int verb = 1;
+       while (remi) {
+               int s = (remi < BUFSIZE/512) ? remi : BUFSIZE/512;
+               int l = read(fi, buf, s*512);
+               if (l < 0) {
+                       fprintf(stderr, "Read error: %m\n");
+                       return 1;
+               }
+               if (l != s*512) {
+                       fprintf(stderr, "Short read: %d of %d. Recovering.\n", l, s*512);
+               }
+               int w = write(fo, buf, l);
+               if (w < 0) {
+                       fprintf(stderr, "Write error: %m\n");
+                       return 1;
+               }
+               if (w != l) {
+                       fprintf(stderr, "Short write: %d of %d\n", w, l);
+                       return 1;
+               }
+               remi -= s;
+               if (!--verb) {
+                       printf("\rCopied %d of %d MB (%d%%)...", (int)((leni-remi)/2048), (int)((leni+2047)/2048),
+                               (int)((double)(leni-remi) / leni * 100));
+                       verb = 64;
+               }
+       }
+
+       printf("Copied %d MB                                       \n", (int)((leni+2047)/2048));
+       close(fo);
+       close(fi);
+       return 0;
+}