#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; }