#define _GNU_SOURCE #include "snapdiff.h" #include #include #include #include #include #include #define SNAPDIFF_BUF_SIZE 65536 int snapdiff_paths(const char* path1, const char* path2, FILE* out, snapdiff_report_t* rep) { if (!path1 || !path2) return -EINVAL; snapdiff_report_t local; memset(&local, 0, sizeof(local)); local.diff_offset = -1; int fd1 = open(path1, O_RDONLY); if (fd1 < 0) { int saved = errno; if (out) fprintf(out, "snapdiff: %s: %s\n", path1, strerror(saved)); return -saved; } int fd2 = open(path2, O_RDONLY); if (fd2 < 0) { int saved = errno; close(fd1); if (out) fprintf(out, "snapdiff: %s: %s\n", path2, strerror(saved)); return -saved; } struct stat st1, st2; if (fstat(fd1, &st1) != 0 || fstat(fd2, &st2) != 0) { int saved = errno; close(fd1); close(fd2); if (out) fprintf(out, "snapdiff: fstat failed: %s\n", strerror(saved)); return -saved; } local.size1 = st1.st_size; local.size2 = st2.st_size; if (st1.st_size != st2.st_size) { local.size_mismatch = 1; close(fd1); close(fd2); if (rep) *rep = local; if (out) fprintf(out, "snapdiff: %s (%jd bytes) vs %s (%jd bytes): SIZE MISMATCH\n", path1, (intmax_t)local.size1, path2, (intmax_t)local.size2); return 1; } uint8_t buf1[SNAPDIFF_BUF_SIZE]; uint8_t buf2[SNAPDIFF_BUF_SIZE]; off_t off = 0; int found_diff = 0; for (;;) { ssize_t n1 = read(fd1, buf1, sizeof(buf1)); ssize_t n2 = read(fd2, buf2, sizeof(buf2)); if (n1 <= 0 || n2 <= 0) break; ssize_t n = (n1 < n2) ? n1 : n2; for (ssize_t i = 0; i < n; i++) { if (buf1[i] != buf2[i]) { local.diff_offset = off + (off_t)i; found_diff = 1; break; } } if (found_diff) break; off += (off_t)n; } close(fd1); close(fd2); if (!found_diff) { local.identical = 1; if (rep) *rep = local; if (out) fprintf(out, "snapdiff: %s vs %s: IDENTICAL (%jd bytes)\n", path1, path2, (intmax_t)local.size1); return 0; } local.chunk_index = local.diff_offset / SNAPDIFF_CHUNK_SIZE; local.at_chunk_boundary = (local.diff_offset % SNAPDIFF_CHUNK_SIZE == 0); if (rep) *rep = local; if (out) fprintf(out, "snapdiff: %s vs %s: DIFFER at offset %jd (chunk %jd%s)\n", path1, path2, (intmax_t)local.diff_offset, (intmax_t)local.chunk_index, local.at_chunk_boundary ? " -- exactly at a chunk boundary, first byte of chunk" : " -- NOT at a chunk boundary, mid-chunk"); return 1; }