X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=vbig.cc;h=a243fb7262602703b2eeed2038866778039c9f19;hb=393a419e4e8e6523166dedf482bc714fe9d8c16b;hp=73e6b9f0c940954483c0b8c0f7c869d1c21f64c4;hpb=d12fccff0a321797535e48e057baf0ace2a61e78;p=vbig.git diff --git a/vbig.cc b/vbig.cc index 73e6b9f..a243fb7 100644 --- a/vbig.cc +++ b/vbig.cc @@ -1,6 +1,7 @@ /* * This file is part of vbig. - * Copyright (C) 2011 Richard Kettlewell + * Copyright (C) 2011, 2013 Richard Kettlewell + * Copyright (C) 2013 Ian Jackson * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,14 +25,24 @@ #include #include #include +#include +#include +#include #include "Arcfour.h" +#define DEFAULT_SEED_LENGTH 2048; + // Command line options const struct option opts[] = { { "seed", required_argument, 0, 's' }, + { "seed-file", required_argument, 0, 'S' }, + { "seed-length", required_argument, 0, 'L' }, + { "both", no_argument, 0, 'b' }, { "verify", no_argument, 0, 'v' }, { "create", no_argument, 0, 'c' }, { "flush", no_argument, 0, 'f' }, + { "entire", no_argument, 0, 'e' }, + { "progress", no_argument, 0, 'p' }, { "help", no_argument, 0, 'h' }, { "version", no_argument, 0, 'V' }, { 0, 0, 0, 0 }, @@ -42,27 +53,35 @@ static void help(void) { printf("vbig - create or verify a large but pseudo-random file\n" "\n" "Usage:\n" - " vbig [--seed SEED] --verify|--create PATH SIZE\n" + " vbig [OPTIONS] [--both|--verify|--create] PATH] [SIZE]\n" "\n" "Options:\n" - " --seed, -s Specify random seed\n" - " --verify, -v Verify that PATH contains the expected contents\n" - " --create, -c Create PATH with psuedo-random contents\n" - " --flush, -f Flush cache\n" - " --help, -h Display usage message\n" - " --version, -V Display version string\n"); + " --seed, -s Specify random seed as string\n" + " --seed-file, -S Read random seed from (start of) this file\n" + " --seed-length, -L Set (maximum) seed length to read from file\n" + " --verify, -v Verify that PATH contains the expected contents\n" + " --create, -c Create PATH with psuedo-random contents\n" + " --both, -b Do both create and verify (default)\n" + " --flush, -f Flush cache\n" + " --entire, -e Write until full; read until EOF\n" + " --progress, -p Show progress as we go\n" + " --help, -h Display usage message\n" + " --version, -V Display version string\n"); } // Possible modes of operation enum mode_type { - NONE, VERIFY, - CREATE + CREATE, + BOTH }; +static void clearprogress(); + // Report an error and exit static void fatal(int errno_value, const char *fmt, ...) { va_list ap; + clearprogress(); fprintf(stderr, "ERROR: "); va_start(ap, fmt); vfprintf(stderr, fmt, ap); @@ -102,16 +121,35 @@ static void flushCache(FILE *fp) { #endif } +static long long execute(mode_type mode, bool entire, const char *show); + +static const char default_seed[] = "hexapodia as the key insight"; +static void *seed; +static size_t seedlen; +static const char *seedpath; +static const char *path; +static bool entireopt = false; +static bool flush = false; +static bool progress = false; +static long long size; + int main(int argc, char **argv) { - const char *seed = "hexapodia as the key insight"; - mode_type mode = NONE; - bool flush = false; + mode_type mode = BOTH; int n; - while((n = getopt_long(argc, argv, "+s:vcfhV", opts, 0)) >= 0) { + char *ep; + while((n = getopt_long(argc, argv, "+s:S:L:vcepfhV", opts, 0)) >= 0) { switch(n) { - case 's': seed = optarg; break; + case 's': seed = optarg; seedlen = strlen(optarg); break; + case 'S': seedpath = optarg; break; + case 'L': + seedlen = strtoul(optarg,&ep,0); + if(ep==optarg || *ep) fatal(0, "bad number for -S"); + break; + case 'b': mode = BOTH; break; case 'v': mode = VERIFY; break; case 'c': mode = CREATE; break; + case 'e': entireopt = true; break; + case 'p': progress = true; break; case 'f': flush = true; break; case 'h': help(); exit(0); case 'V': puts(VERSION); exit(0); @@ -121,61 +159,165 @@ int main(int argc, char **argv) { } argc -= optind; argv += optind; - if(mode == NONE) - fatal(0, "must specify one of --verify or --create"); - if(argc != 2) - fatal(0, "must specify a path and size"); - const char *path = argv[0]; - errno = 0; - char *end; - long long size = strtoll(argv[1], &end, 10); - if(errno) - fatal(errno, "invalid size"); - if(end == argv[1]) - fatal(0, "invalid size"); - if(!strcmp(end, "K")) - size *= 1024; - else if(!strcmp(end, "M")) - size *= 1024 * 1024; - else if(!strcmp(end, "G")) - size *= 1024 * 1024 * 1024; - else if(*end) - fatal(0, "invalid size"); - Arcfour rng(seed, strlen(seed)); + if(argc > 2) + fatal(0, "excess arguments"); + if(argc == 1 && mode == BOTH) + entireopt = true; + if(entireopt) { + if(argc != 1) + fatal(0, "with --entire, size should not be specified"); + } else { + if(argc < (mode == VERIFY ? 1 : 2)) + fatal(0, "insufficient arguments"); + } + if(seed && seedpath) + fatal(0, "both --seed and --seed-file specified"); + if(mode == BOTH && !seed && !seedpath) { +#ifdef HAVE_RANDOM_DEVICE + seedpath = RANDOM_DEVICE; +#else + fatal(0, "no --seed or --seed-file specified in --both mode" + " and random device not supported on this system"); +#endif + } + if(seedpath) { + if(!seedlen) + seedlen = DEFAULT_SEED_LENGTH; + FILE *seedfile = fopen(seedpath, "rb"); + if(!seedfile) + fatal(errno, "%s", seedpath); + seed = malloc(seedlen); + if(!seed) + fatal(errno, "allocate seed"); + seedlen = fread(seed, 1, seedlen, seedfile); + if(ferror(seedfile)) + fatal(errno, "read %s", seedpath); + fclose(seedfile); + } + if (!seed) { + seed = (void*)default_seed; + seedlen = sizeof(default_seed)-1; + } + path = argv[0]; + if(argc > 1) { + errno = 0; + char *end; + size = strtoll(argv[1], &end, 10); + if(errno) + fatal(errno, "invalid size"); + if(end == argv[1]) + fatal(0, "invalid size"); + if(!strcmp(end, "K")) + size *= 1024; + else if(!strcmp(end, "M")) + size *= 1024 * 1024; + else if(!strcmp(end, "G")) + size *= 1024 * 1024 * 1024; + else if(*end) + fatal(0, "invalid size"); + } else if(entireopt) { + size = LLONG_MAX; + } else { + struct stat sb; + if(stat(path, &sb) < 0) + fatal(errno, "stat %s", path); + size = sb.st_size; + } + const char *show = entireopt ? (mode == CREATE ? "written" : "verified") : 0; + if(mode == BOTH) { + size = execute(CREATE, entireopt, 0); + execute(VERIFY, false, show); + } else { + execute(mode, entireopt, show); + } + return 0; +} + +static void flushstdout() { + if(ferror(stdout) || fflush(stdout)) + fatal(errno, "flush stdout"); +} + +static void clearprogress() { + if (!progress) return; + printf(" %-10s %*s \r", "", (int)sizeof(long long)*4, ""); + flushstdout(); +} + +static void showprogress(long long amount, const char *show) { + if (!progress) return; + + static int counter; + if (counter++ < 1000) return; + counter = 0; + + int triples = sizeof(amount); + char rawbuf[triples*3 + 1]; + char outbuf[triples*4 + 1]; + snprintf(rawbuf, sizeof(rawbuf), "% *lld", (int)sizeof(rawbuf)-1, amount); + for (int i=0; i 0) { size_t bytesGenerated = (remain > (ssize_t)sizeof generated ? sizeof generated : remain); rng.stream(generated, bytesGenerated); if(mode == CREATE) { - fwrite(generated, 1, bytesGenerated, fp); - if(ferror(fp)) - fatal(errno, "%s", path); + size_t bytesWritten = fwrite(generated, 1, bytesGenerated, fp); + if(ferror(fp)) { + if(!entire || errno != ENOSPC) + fatal(errno, "%s", path); + remain -= bytesWritten; + break; + } + assert(bytesWritten == bytesGenerated); } else { size_t bytesRead = fread(input, 1, bytesGenerated, fp); if(ferror(fp)) fatal(errno, "%s", path); - if(bytesRead < bytesGenerated) - fatal(0, "%s: truncated at %lld/%lld bytes", - path, (size - remain + bytesRead), size); - if(memcmp(generated, input, bytesGenerated)) { - for(size_t n = 0; n < bytesGenerated; ++n) + if(memcmp(generated, input, bytesRead)) { + for(size_t n = 0; n < bytesRead; ++n) if(generated[n] != input[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]); } + if(bytesRead < bytesGenerated) { + if(entire) { + assert(feof(fp)); + remain -= bytesRead; + break; + } + fatal(0, "%s: truncated at %lld/%lld bytes", + path, (size - remain + bytesRead), size); + } } remain -= bytesGenerated; + showprogress(size - remain, mode == VERIFY ? "verifying" : "writing"); } - if(mode == VERIFY && getc(fp) != EOF) + clearprogress(); + if(mode == VERIFY && !entire && getc(fp) != EOF) fatal(0, "%s: extended beyond %lld bytes", path, size); if(mode == CREATE && flush) { @@ -185,5 +327,12 @@ int main(int argc, char **argv) { } if(fclose(fp) < 0) fatal(errno, "%s", path); - return 0; + long long done = size - remain; + if(show) { + printf("%lld bytes (%lldM, %lldG) %s\n", + done, done >> 20, done >> 30, + show); + flushstdout(); + } + return done; }