Created
March 3, 2020 01:12
-
-
Save whitslack/f32ecc241ee1ac1e319104616f9a2df5 to your computer and use it in GitHub Desktop.
bsync: a write-minimizing block device synchronizing utility
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdbool.h> | |
#include <stdint.h> | |
#include <stdio.h> | |
#include <string.h> | |
#include <errno.h> | |
#include <error.h> | |
#include <fcntl.h> | |
#include <sysexits.h> | |
#include <unistd.h> | |
#include <sys/mman.h> | |
static bool all_balls(const void *buf, size_t size) { | |
for (const char *p = buf, *q = p + size; p < q; ++p) { | |
if (*p) { | |
return false; | |
} | |
} | |
return true; | |
} | |
int main(int argc, char *argv[]) { | |
if (argc != 3) { | |
fprintf(stderr, "usage: %s <input> <output>\n", argv[0]); | |
return -1; | |
} | |
close(STDIN_FILENO); | |
close(STDOUT_FILENO); | |
int in_fd = open(argv[1], O_RDONLY); | |
if (in_fd < 0) { | |
error(EX_NOINPUT, errno, "%s: open", argv[1]); | |
} | |
int out_fd = open(argv[2], O_RDWR); | |
if (out_fd < 0) { | |
error(EX_NOINPUT, errno, "%s: open", argv[2]); | |
} | |
off_t size = lseek(in_fd, 0, SEEK_END); | |
if (size < 0) { | |
error(EX_OSERR, errno, "%s: lseek", argv[1]); | |
} | |
off_t out_size = lseek(out_fd, 0, SEEK_END); | |
if (out_size < 0) { | |
error(EX_OSERR, errno, "%s: lseek", argv[2]); | |
} | |
if (size != out_size) { | |
fputs("input and output sizes must match\n", stderr); | |
return EX_DATAERR; | |
} | |
long page_size = sysconf(_SC_PAGESIZE); | |
if (page_size < 0) { | |
perror("sysconf"); | |
return EX_OSERR; | |
} | |
if (size % page_size != 0) { | |
fputs("size must be a multiple of page size\n", stderr); | |
return EX_DATAERR; | |
} | |
const void *in = mmap(NULL, size, PROT_READ, MAP_SHARED, in_fd, 0); | |
if (!~(uintptr_t) in) { | |
error(EX_OSERR, errno, "%s: mmap", argv[1]); | |
} | |
void *out = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, out_fd, 0); | |
if (!~(uintptr_t) out) { | |
error(EX_OSERR, errno, "%s: mmap", argv[2]); | |
} | |
madvise((void *) in, size, MADV_SEQUENTIAL); | |
madvise(out, size, MADV_SEQUENTIAL); | |
const void *in_tail = in; | |
void *out_tail = out; | |
bool try_discard = true; | |
void *discard_begin = out, *discard_end = out; | |
for (off_t rem = size; rem > 0;) { | |
if (memcmp(out, in, page_size) != 0) { | |
if (try_discard && all_balls(in, page_size)) { | |
if (discard_end != out) { | |
size_t discard_size = discard_end - discard_begin; | |
if (discard_size > 0 && madvise(discard_begin, discard_size, MADV_REMOVE) != 0) { | |
memset(discard_begin, 0, discard_size); | |
try_discard = false; | |
} | |
discard_begin = out; | |
} | |
discard_end = (char *) out + page_size; | |
} | |
else { | |
memcpy(out, in, page_size); | |
} | |
} | |
in = (const char *) in + page_size; | |
out = (char *) out + page_size; | |
rem -= page_size; | |
if ((rem & (1 << 25) - 1) == 0) { | |
madvise((void *) in_tail, in - in_tail, MADV_DONTNEED), in_tail = in; | |
madvise(out_tail, out - out_tail, MADV_DONTNEED), out_tail = out; | |
size_t advice_len = rem < 2 << 25 ? (size_t) rem : 2 << 25; | |
madvise((void *) in, advice_len, MADV_WILLNEED); | |
madvise(out, advice_len, MADV_WILLNEED); | |
fprintf(stderr, "\r%.1f%%", (double) (size - rem) / (double) size * 100); | |
fflush(stderr); | |
} | |
} | |
size_t discard_size = discard_end - discard_begin; | |
if (discard_size > 0 && madvise(discard_begin, discard_size, MADV_REMOVE) != 0) { | |
memset(discard_begin, 0, discard_size); | |
} | |
putc('\n', stderr); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment