/* test_pack_overlay.c — overlay backend: copy-up, whiteout deletion of a * lower-layer entry (Section 4.2), compaction (Section 4.1/5.3), * cross-restart durability via journal replay (Section 4.4), and pack * integrity validation rejecting a corrupted file (Section 7). */ #include #include #include #include #include #include "packfs.h" #include "test_harness.h" static char *tmp_pack_path(void) { static char path[512]; snprintf(path, sizeof(path), "/tmp/packfs_test_pack_%d.img", (int)getpid()); return path; } int main(void) { char *pack_path = tmp_pack_path(); unlink(pack_path); char jpath[600]; snprintf(jpath, sizeof(jpath), "%s.jnl", pack_path); unlink(jpath); /* --- phase 1: build an initial pack directly (as if shipped) --- */ Vfs *v = vfs_new(); Backend *mem = backend_mem_new(); int oerr = 0; Backend *ov = backend_overlay_new(pack_path, mem, &oerr); CHECK(ov != NULL); CHECK_EQ_INT(vfs_mount(v, "/", ov), VFS_OK); int err = 0; VfsFile *f = vfs_open(v, "/a.txt", VFS_O_WRONLY | VFS_O_CREAT, &err); CHECK(f != NULL); CHECK_EQ_INT(vfs_write(f, "AAAA", 4), 4); CHECK_EQ_INT(vfs_close(f), VFS_OK); f = vfs_open(v, "/dup.txt", VFS_O_WRONLY | VFS_O_CREAT, &err); CHECK(f != NULL); CHECK_EQ_INT(vfs_write(f, "AAAA", 4), 4); /* same content as a.txt: exercises dedup (Section 9.2) */ CHECK_EQ_INT(vfs_close(f), VFS_OK); f = vfs_open(v, "/b.txt", VFS_O_WRONLY | VFS_O_CREAT, &err); CHECK(f != NULL); CHECK_EQ_INT(vfs_write(f, "BBBBBB", 6), 6); CHECK_EQ_INT(vfs_close(f), VFS_OK); CHECK_EQ_INT(vfs_sync(v, "/"), VFS_OK); /* compaction */ /* read-only open straight from the freshly-compacted pack */ f = vfs_open(v, "/a.txt", VFS_O_RDONLY, &err); CHECK(f != NULL); char buf[16] = {0}; CHECK_EQ_INT(vfs_read(f, buf, sizeof(buf)), 4); CHECK_STR_EQ(buf, "AAAA"); CHECK_EQ_INT(vfs_close(f), VFS_OK); /* --- copy-up on write intent, whiteout on delete (Section 4.2) --- */ f = vfs_open(v, "/a.txt", VFS_O_RDWR, &err); /* triggers copy-up */ CHECK(f != NULL); CHECK_EQ_INT(vfs_write(f, "ZZZZ", 4), 4); CHECK_EQ_INT(vfs_close(f), VFS_OK); f = vfs_open(v, "/a.txt", VFS_O_RDONLY, &err); memset(buf, 0, sizeof(buf)); CHECK_EQ_INT(vfs_read(f, buf, sizeof(buf)), 4); CHECK_STR_EQ(buf, "ZZZZ"); CHECK_EQ_INT(vfs_close(f), VFS_OK); CHECK_EQ_INT(vfs_unlink(v, "/b.txt"), VFS_OK); /* whiteout: b.txt only existed in the pack */ VfsStat st; CHECK_EQ_INT(vfs_stat(v, "/b.txt", &st), VFS_ERR_NOENT); /* --- durability: a fresh Vfs replays the journal without compacting --- */ vfs_unmount(v, "/"); backend_free(ov); backend_free(mem); vfs_free(v); Vfs *v2 = vfs_new(); Backend *mem2 = backend_mem_new(); Backend *ov2 = backend_overlay_new(pack_path, mem2, &oerr); CHECK(ov2 != NULL); CHECK_EQ_INT(vfs_mount(v2, "/", ov2), VFS_OK); CHECK_EQ_INT(vfs_stat(v2, "/b.txt", &st), VFS_ERR_NOENT); /* whiteout replayed */ f = vfs_open(v2, "/a.txt", VFS_O_RDONLY, &err); CHECK(f != NULL); memset(buf, 0, sizeof(buf)); CHECK_EQ_INT(vfs_read(f, buf, sizeof(buf)), 4); CHECK_STR_EQ(buf, "ZZZZ"); /* the post-compaction write survived via the journal */ CHECK_EQ_INT(vfs_close(f), VFS_OK); CHECK_EQ_INT(vfs_sync(v2, "/"), VFS_OK); /* compact again to fold the journal in */ vfs_unmount(v2, "/"); backend_free(ov2); backend_free(mem2); vfs_free(v2); /* --- pack integrity validation (Section 7): a corrupted pack is rejected --- */ int fd = open(pack_path, O_RDWR); CHECK(fd >= 0); unsigned char one; CHECK_EQ_INT(pread(fd, &one, 1, 40), 1); /* somewhere inside the header/offsets region */ one = (unsigned char)(one ^ 0xFF); CHECK_EQ_INT(pwrite(fd, &one, 1, 40), 1); close(fd); Vfs *v3 = vfs_new(); Backend *mem3 = backend_mem_new(); int oerr3 = 0; Backend *ov3 = backend_overlay_new(pack_path, mem3, &oerr3); CHECK(ov3 == NULL); CHECK_EQ_INT(oerr3, VFS_ERR_CORRUPT); backend_free(mem3); vfs_free(v3); unlink(pack_path); unlink(jpath); TEST_MAIN_END(); }