#define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include "bit-rot-object-version.h" /* NOTE: no size discovery */ int brstub_validate_version(char *bpath, unsigned long version) { int ret = 0; int match = 0; size_t xsize = 0; br_version_t *xv = NULL; xsize = sizeof(br_version_t); xv = calloc(1, xsize); if (!xv) { match = -1; goto err; } ret = getxattr(bpath, "trusted.bit-rot.version", xv, xsize); if (ret < 0) { if (errno == ENODATA) match = -2; goto err; } if (xv->ongoingversion != version) { match = -3; fprintf(stderr, "ongoingversion: %lu\n", xv->ongoingversion); } free(xv); err: return match; } int brstub_write_validation(char *filp, char *bpath, unsigned long startversion) { int fd1 = 0; int fd2 = 0; int ret = 0; char *string = "string\n"; /* read only check */ fd1 = open(filp, O_RDONLY); if (fd1 < 0) goto err; close(fd1); ret = brstub_validate_version(bpath, startversion); if (ret != -2) goto err; /* single open (write/) check */ fd1 = open(filp, O_RDWR); if (fd1 < 0) goto err; ret = write(fd1, string, strlen(string)); if (ret <= 0) goto err; /** * Fsync is done so that the write call has properly reached the * disk. For fuse mounts write-behind xlator would have held the * writes with itself and for nfs, client would have held the * write in its cache. So write fop would not have triggered the * versioning as it would have not reached the bit-rot-stub. */ fsync(fd1); ret = brstub_validate_version(bpath, startversion); if (ret != 0) goto err; ret = write(fd1, string, strlen(string)); if (ret <= 0) goto err; fsync(fd1); /* let it reach the disk */ ret = brstub_validate_version(bpath, startversion); if (ret != 0) goto err; close(fd1); /** * Well, this is not a _real_ test per se . For this test to pass * the inode should not get a forget() in the interim. Therefore, * perform this test asap. */ /* multi open (write/) check */ fd1 = open(filp, O_RDWR); if (fd1 < 0) goto err; fd2 = open(filp, O_WRONLY); if (fd1 < 0) goto err; ret = write(fd1, string, strlen(string)); if (ret <= 0) goto err; ret = write(fd2, string, strlen(string)); if (ret <= 0) goto err; /* probably do a syncfs() */ fsync(fd1); fsync(fd2); close(fd1); close(fd2); /** * incremented once per write()/write().../close()/close() sequence */ ret = brstub_validate_version(bpath, startversion); if (ret != 0) goto err; return 0; err: return -1; } int brstub_new_object_validate(char *filp, char *brick) { int ret = 0; char *fname = NULL; char bpath[PATH_MAX] = { 0, }; fname = basename(filp); if (!fname) goto err; (void)snprintf(bpath, PATH_MAX, "%s/%s", brick, fname); printf("Validating initial version..\n"); ret = brstub_validate_version(bpath, 2); if (ret != -2) /* version _should_ be missing */ goto err; printf("Validating version on modifications..\n"); ret = brstub_write_validation(filp, bpath, 2); if (ret < 0) goto err; return 0; err: return -1; } int main(int argc, char **argv) { int ret = 0; char *filp = NULL; char *brick = NULL; if (argc != 3) { printf("Usage: %s \n", argv[0]); goto err; } filp = argv[1]; brick = argv[2]; printf("Validating object version [%s]\n", filp); ret = brstub_new_object_validate(filp, brick); if (ret < 0) goto err; return 0; err: return -1; }