// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2000-2003 Silicon Graphics, Inc. * All Rights Reserved. */ /* * fill2: * Derived from fill.c, adds command line options, block boundary marking * and allows me to tweak to suit fill2fs without breaking other qa tests. * This version marks block boundaries (512, 1k, 4k and 16k) within the file * using characters guaranteed not to appear anywhere else, this may allow * simple checks in future which can inspect these offsets and ensure one of * of the four characters is present. Also fixes off-by-one error in fill.c * and is more careful about checking when write operations fail - this is * needed by fill2fs to ensure that the number of bytes written is accurately * determined. */ #include "global.h" #define constpp char * const * #define N(x) (sizeof(x)/sizeof(x[0])) /* function prototypes */ static void illegal(char *, char *); static void reqval(char, char * [], int); static void respec(char, char * [], int); static void unknown(char, char *); static void usage(void); char *progname; char *dopts[] = { "nbytes", "linelength", "seed", "file", NULL }; enum { D_NBYTES, D_LINELENGTH, D_SEED, D_FILE, D_ISSET, D_NUM }; int dflags[D_NUM] = { 0 }; char *bopts[] = { "512", "1k", "4k", "16k" }; enum { B_512, B_1K, B_4K, B_16K, B_ISSET, B_NUM }; int bflags[B_NUM] = { 0 }; /* * block boundaries * */ /* block boundaries which should be flagged in the output file */ /* flag is the character that should be used to indicate each type */ /* of block boundary */ struct s_block_type { int mark; int size; char flag; }; static struct s_block_type btypes[] = { { 0, 512, '!' }, /* 512 */ { 0, 1024, '"' }, /* 1k */ { 0, 4096, '#' }, /* 4k */ { 0, 16384, '$' }, /* 16k */ }; /* * main * */ int main(int argc, char **argv) { int status = 0; /* return status */ int c; /* current option character */ char *p; /* for getsubopt calls */ long nbytes; /* total number of bytes to write */ int dlen; /* length of normal output data line */ const char *dseed = NULL; /* input string for seeding rand */ unsigned int seed; /* seed for output data */ char *dfile = NULL; /* where to write output */ FILE *f; /* output file */ char *dbuf; /* output line buffer */ char bbuf[50]; /* block boundary string */ char *active = NULL; /* active buffer to copy out of */ size_t hlen; /* header length (bytes+key) in output */ /* lines */ char *hptr; /* pointer to end of header */ char *ptr; /* current position to copy from */ int blktype = 0; /* current block boundary type */ int boundary; /* set if current output char lies on */ /* block boundary */ long i; int j; int l = 0; /* defaults */ progname = basename(argv[0]); for (p = progname; *p; p++) { if (*p == '/') { progname = p + 1; } } nbytes = 1024 * 1024; dlen = 73; /* includes the trailing newline */ while ((c = getopt(argc, argv, "d:b:")) != EOF) { switch (c) { case 'd': if (dflags[D_ISSET]) respec('d', NULL, 0); dflags[D_ISSET] = 1; p = optarg; while (*p != '\0') { char *value; switch (getsubopt(&p, (constpp)dopts, &value)) { case D_NBYTES: if (! value) reqval('d', dopts, D_NBYTES); if (dflags[D_NBYTES]) respec('d', dopts, D_NBYTES); dflags[D_NBYTES] = 1; nbytes = strtol(value, &ptr, 10); if (ptr == value) illegal(value, "d nbytes"); break; case D_LINELENGTH: if (! value) reqval('d', dopts, D_LINELENGTH); if (dflags[D_LINELENGTH]) respec('d', dopts, D_LINELENGTH); dflags[D_LINELENGTH] = 1; dlen = (int) strtol(value, &ptr, 10) + 1; if (ptr == value) illegal(value, "d linelength"); break; case D_SEED: if (! value) reqval('d', dopts, D_SEED); if (dflags[D_SEED]) respec('D', dopts, D_SEED); dflags[D_SEED] = 1; dseed = value; break; case D_FILE: if (! value) reqval('d', dopts, D_FILE); if (dflags[D_FILE]) respec('d', dopts, D_FILE); dflags[D_FILE] = 1; dfile = value; break; default: unknown('d', value); } } break; case 'b': if (bflags[B_ISSET]) respec('b', NULL, 0); bflags[B_ISSET] = 1; p = optarg; while (*p != '\0') { char *value; switch (getsubopt(&p, (constpp)bopts, &value)) { case B_512: if (value) illegal(value, "b 512"); if (bflags[B_512]) respec('b', bopts, B_512); bflags[B_512] = 1; btypes[0].mark = 1; break; case B_1K: if (value) illegal(value, "b 1k"); if (bflags[B_1K]) respec('b', bopts, B_1K); bflags[B_1K] = 1; btypes[1].mark = 1; break; case B_4K: if (value) illegal(value, "b 4k"); if (bflags[B_4K]) respec('b', bopts, B_4K); bflags[B_4K] = 1; btypes[2].mark = 1; break; case B_16K: if (value) illegal(value, "b 16k"); if (bflags[B_16K]) respec('b', bopts, B_16K); bflags[B_16K] = 1; btypes[3].mark = 1; break; default: unknown('b', value); break; } } break; case '?': usage(); } } if (dflags[D_FILE] && optind != argc) usage(); if (! dflags[D_FILE] && argc - optind != 1) usage(); if (! dflags[D_FILE]) dfile = argv[optind]; if ((f = fopen(dfile, "w")) == NULL) { fprintf(stderr, "fill2: cannot create \"%s\": %s\n", dfile, strerror(errno)); status = 1; goto cleanup; } if (! dflags[D_SEED]) dseed = dfile; /* seed is an ascii string */ seed = 0; i = 0; while (dseed[i]) { seed <<= 8; seed |= dseed[i]; i++; } srand(seed); /* normal data line format: XXXXXXXXXXXX CCCC...CCC CCCCCCCCCCCC...CCC */ /* block boundary format: CXXXXXXX */ dbuf = (char *) malloc(dlen + 1); hlen = 12+1+strlen(dseed)+1; assert(hlen < dlen); hptr = dbuf + hlen; for (i = 0; i < nbytes; i++) { /* is this a block boundary? check largest to smallest */ boundary = 0; for (j = N(btypes) - 1; j >= 0; j--) { if (btypes[j].mark) { /* first time through or just before a block boundary? */ if (i == 0 || i % btypes[j].size == btypes[j].size - 1) { boundary = 1; blktype = j; break; } } } /* are there block boundaries to check? */ /* is this the first time through? */ /* block boundaries take priority over other output */ if (bflags[B_ISSET] && (i == 0 || boundary)) { sprintf(bbuf, "%s%c%07ld\n", i ? "\n" : "", btypes[blktype].flag, /* remember i is one less than block boundary */ i ? (i+1) / btypes[blktype].size : 0); active = bbuf; ptr = bbuf; } /* are we at the start of a new line? */ else if (i == 0 || (active == bbuf && *ptr == '\0') || (active == dbuf && l == dlen)) { sprintf(dbuf, "%012ld %s ", i, dseed); assert(*hptr == '\0'); for (ptr = hptr; ptr != dbuf + dlen - 1; ptr++) { /* characters upto 126 '~' are used */ /* do not use !"#$ - leave free for use as block markers */ *ptr = 37 + (rand() % (127 - 37)); } *ptr++ = '\n'; *ptr = '\0'; assert(ptr == dbuf + dlen); active = dbuf; ptr = dbuf; l = 0; } else { /* continue copying from the active buffer */ } /* output one new character from current buffer */ if (fputc(*ptr++, f) == EOF) { fprintf(stderr, "fill2: could not write character to \"%s\": %s\n", dfile, strerror(errno)); status = 1; goto cleanup; } if (active == dbuf) l++; } cleanup: /* close file and flush buffers - check if this fails */ if (fclose(f) != 0) { fprintf(stderr, "fill2: fclose() on \"%s\" failed: %s\n", dfile, strerror(errno)); status = 1; } return status; } /* * suboption checking functions * */ static void illegal(char *value, char *opt) { fprintf(stderr, "%s: Error: Illegal value \"%s\" for -%s option\n", progname, value, opt); usage(); } static void reqval(char opt, char *tab[], int idx) { fprintf(stderr, "%s: Error: -%c %s option requires a value\n", progname, opt, tab[idx]); usage(); } static void respec(char opt, char *tab[], int idx) { fprintf(stderr, "%s: Error: ", progname); fprintf(stderr, "-%c ", opt); if (tab) fprintf(stderr, "%s ", tab[idx]); fprintf(stderr, "option respecified\n"); usage(); } static void unknown(char opt, char *s) { fprintf(stderr, "%s: Error: Unknown option -%c %s\n", progname, opt, s); usage(); } static void usage(void) { fprintf(stderr, "Usage: %s [options] filename\n" "Options:\n" " -d [nbytes=num,linelength=num, output data properties\n" " seed=string,file=name]\n" " -b [512,1k,4k,16k] where to mark block boundaries\n", progname); exit(2); }