From 5f485019260c62239217a05f4451252caf947915 Mon Sep 17 00:00:00 2001 From: Richard Kettlewell Date: Wed, 15 Jun 2011 22:19:33 +0100 Subject: [PATCH] Add --flush option to evict the subject file from RAM, i.e. so you are really testing the storage device rather than the OS's cache. As implemented will only work on Linux, and requires superuser privilege. --- vbig.1 | 4 +++ vbig.cc | 76 +++++++++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 65 insertions(+), 15 deletions(-) diff --git a/vbig.1 b/vbig.1 index 61db4b8..45b491b 100644 --- a/vbig.1 +++ b/vbig.1 @@ -30,6 +30,10 @@ Selects verify mode. Checks that \fIPATH\fR has exactly the contents that would be produced by the equivalent \fB--create\fR call. .TP +.B --flush +Flush cached data after creating the file or before verifying it. +Only root can use this option. +.TP .B --help Displays a usage message. .TP diff --git a/vbig.cc b/vbig.cc index eeea02a..3862e5b 100644 --- a/vbig.cc +++ b/vbig.cc @@ -3,18 +3,27 @@ #include #include #include +#include #include +#include +#include #include "Arcfour.h" +// Path to magic file to drop filesystem caches +static const char dropCaches[] = "/proc/sys/vm/drop_caches"; + +// Command line options const struct option opts[] = { { "seed", required_argument, 0, 's' }, { "verify", no_argument, 0, 'v' }, { "create", no_argument, 0, 'c' }, + { "flush", no_argument, 0, 'f' }, { "help", no_argument, 0, 'h' }, { "version", no_argument, 0, 'V' }, { 0, 0, 0, 0 }, }; +// Display help message static void help(void) { printf("vbig - create or verify a large but pseudo-random file\n" "\n" @@ -25,38 +34,68 @@ static void help(void) { " --seed Specify random seed\n" " --verify Verify that PATH contains the expected contents\n" " --create Create PATH with psuedo-random contents\n" + " --flush Flush cache\n" " --help Display usage message\n" " --version Display version string\n"); } +// Possible modes of operation enum mode_type { NONE, VERIFY, CREATE }; +// Report an error and exit +static void fatal(int errno_value, const char *fmt, ...) { + va_list ap; + fprintf(stderr, "ERROR: "); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + if(errno_value) + fprintf(stderr, ": %s", strerror(errno_value)); + fputc('\n', stderr); + exit(1); +} + +// Evict whatever FP points to from RAM +static void flushCache(FILE *fp) { + // drop_caches only evicts clean pages, so first the target file is + // synced. + if(fsync(fileno(fp)) < 0) + fatal(errno, "fsync"); + int fd; + if((fd = open(dropCaches, O_WRONLY, 0)) < 0) + fatal(errno, "%s", dropCaches); + if(write(fd, "3\n", 2) < 0) + fatal(errno, "%s", dropCaches); + close(fd); +} + int main(int argc, char **argv) { const char *seed = "hexapodia as the key insight"; mode_type mode = NONE; + bool flush = false; int n; - while((n = getopt_long(argc, argv, "+s:vchV", opts, 0)) >= 0) { + while((n = getopt_long(argc, argv, "+s:vcfhV", opts, 0)) >= 0) { switch(n) { case 's': seed = optarg; break; case 'v': mode = VERIFY; break; case 'c': mode = CREATE; break; + case 'f': flush = true; break; case 'h': help(); exit(0); case 'V': puts(VERSION); exit(0); default: - fprintf(stderr, "ERROR: unknown option\n"); - exit(1); + fatal(0, "unknown option"); } } if(mode == NONE) { - fprintf(stderr, "ERROR: must specify one of --verify or --create\n"); + fatal(0, "must specify one of --verify or --create"); exit(1); } if(optind + 2 != argc) { - fprintf(stderr, "ERROR: must specify a path and size\n"); + fatal(0, "must specify a path and size"); exit(1); } const char *path = argv[optind]; @@ -64,11 +103,11 @@ int main(int argc, char **argv) { char *end; long long size = strtoll(argv[optind + 1], &end, 10); if(errno) { - fprintf(stderr, "ERROR: invalid size: %s\n", strerror(errno)); + fatal(errno, "invalid size"); exit(1); } if(end == argv[optind + 1]) { - fprintf(stderr, "ERROR: invalid size\n"); + fatal(0, "invalid size"); exit(1); } if(!strcmp(end, "K")) @@ -78,15 +117,17 @@ int main(int argc, char **argv) { else if(!strcmp(end, "G")) size *= 1024 * 1024 * 1024; else if(*end) { - fprintf(stderr, "ERROR: invalid size\n"); + fatal(0, "invalid size"); exit(1); } Arcfour rng(seed, strlen(seed)); FILE *fp = fopen(path, mode == VERIFY ? "rb" : "wb"); if(!fp) { - fprintf(stderr, "ERROR: %s: %s\n", path, strerror(errno)); + fatal(errno, "%s", path); exit(1); } + if(mode == VERIFY && flush) + flushCache(fp); char generated[4096], input[4096]; size_t remain = size; while(remain > 0) { @@ -97,24 +138,24 @@ int main(int argc, char **argv) { if(mode == CREATE) { fwrite(generated, 1, bytesGenerated, fp); if(ferror(fp)) { - fprintf(stderr, "ERROR: %s: %s\n", path, strerror(errno)); + fatal(errno, "%s", path); exit(1); } } else { size_t bytesRead = fread(input, 1, bytesGenerated, fp); if(ferror(fp)) { - fprintf(stderr, "ERROR: %s: %s\n", path, strerror(errno)); + fatal(errno, "%s", path); exit(1); } if(bytesRead < bytesGenerated) { - fprintf(stderr, "ERROR: %s: truncated at %lld/%lld bytes\n", + fatal(0, "%s: truncated at %lld/%lld bytes", path, (size - remain + bytesRead), size); exit(1); } if(memcmp(generated, input, bytesGenerated)) { for(size_t n = 0; n < bytesGenerated; ++n) if(generated[n] != input[n]){ - fprintf(stderr, "ERROR: %s corrupted at %lld/%lld bytes (expected %d got %d)\n", + fatal(0, "%s corrupted at %lld/%lld bytes (expected %d got %d)", path, size - remain + n, size, (unsigned char)generated[n], (unsigned char)input[n]); exit(1); @@ -124,12 +165,17 @@ int main(int argc, char **argv) { remain -= bytesGenerated; } if(mode == VERIFY && getc(fp) != EOF) { - fprintf(stderr, "ERROR: %s: extended beyond %lld bytes\n", + fatal(0, "%s: extended beyond %lld bytes", path, size); exit(1); } + if(mode == CREATE && flush) { + if(fflush(fp) < 0) + fatal(errno, "%s", path); + flushCache(fp); + } if(fclose(fp) < 0) { - fprintf(stderr, "ERROR: %s: %s\n", path, strerror(errno)); + fatal(errno, "%s", path); exit(1); } return 0; -- 2.30.2