From ea938f2ff28985274ca4d6c7d85ca85f06c3afd5 Mon Sep 17 00:00:00 2001 From: Michel Machado Date: Thu, 21 Aug 2014 17:44:26 -0400 Subject: [PATCH] f3probe: rewrite file device This patch makes file devices use the same geometry model that probe_device() probes for. This change helps one to test whatever probe_device() finds in the field. --- f3probe.c | 151 +++++++++++++++++++++++++++++------------------------ libprobe.c | 86 +++++++----------------------- libprobe.h | 2 +- 3 files changed, 104 insertions(+), 135 deletions(-) diff --git a/f3probe.c b/f3probe.c index dba6751..1723d4c 100644 --- a/f3probe.c +++ b/f3probe.c @@ -1,3 +1,5 @@ +#define _POSIX_C_SOURCE 200112L + #include #include #include @@ -40,12 +42,15 @@ static char doc[] = "F3 Probe -- probe a block device for " */ static struct argp_option options[] = { - {"debug-file-size", 'd', "SIZE_GB", OPTION_HIDDEN, - "Enable debuging with a regular file", 1}, - {"debug-fake-size", 'f', "SIZE_GB", OPTION_HIDDEN, + {"debug", 'd', NULL, OPTION_HIDDEN, + "Enable debugging; only needed if none --debug-* option used", + 1}, + {"debug-real-size", 'r', "SIZE_BYTE", OPTION_HIDDEN, + "Real size of the emulated flash", 0}, + {"debug-fake-size", 'f', "SIZE_BYTE", OPTION_HIDDEN, "Fake size of the emulated flash", 0}, - {"debug-type", 't', "N", OPTION_HIDDEN, - "Set the type of the fake flash", 0}, + {"debug-wrap", 'w', "N", OPTION_HIDDEN, + "Wrap parameter of the emulated flash", 0}, {"debug-unit-test", 'u', NULL, OPTION_HIDDEN, "Run a unit test; it ignores all other debug options", 0}, { 0 } @@ -57,54 +62,61 @@ struct args { /* Debugging options. */ bool unit_test; bool debug; - enum fake_type fake_type; - /* 1 bytes free. */ - int file_size_gb; - int fake_size_gb; + /* 2 bytes free. */ + uint64_t real_size_byte; + uint64_t fake_size_byte; + int wrap; }; -static long arg_to_long(const struct argp_state *state, const char *arg) +static long long arg_to_long_long(const struct argp_state *state, + const char *arg) { char *end; - long l = strtol(arg, &end, 0); + long long ll = strtoll(arg, &end, 0); if (!arg) argp_error(state, "An integer must be provided"); if (!*arg || *end) argp_error(state, "`%s' is not an integer", arg); - return l; + return ll; } +/* XXX Add a friendly way to enter real and fake sizes: 1, 1k, 1m, 1g... */ static error_t parse_opt(int key, char *arg, struct argp_state *state) { struct args *args = state->input; + long long ll; switch (key) { case 'd': - args->file_size_gb = arg_to_long(state, arg); - if (args->file_size_gb < 1) + args->debug = true; + break; + + case 'r': + ll = arg_to_long_long(state, arg); + if (ll < 0) argp_error(state, - "Size of the regular file to be used for debugging must be at least 1GB"); + "Real size must be greater or equal to zero"); + args->real_size_byte = ll; args->debug = true; break; case 'f': - args->fake_size_gb = arg_to_long(state, arg); - if (args->fake_size_gb < 1) + ll = arg_to_long_long(state, arg); + if (ll < 0) argp_error(state, - "Fake size of the emulated flash must be at least 1GB"); + "Fake size must be greater or equal to zero"); + args->fake_size_byte = ll; args->debug = true; break; - case 't': { - long l = arg_to_long(state, arg); - if (l < FKTY_GOOD || l >= FKTY_MAX) + case 'w': + ll = arg_to_long_long(state, arg); + if (ll < 0 || ll >= 64) argp_error(state, - "Fake type must be a number in the interval [%i, %i]", - FKTY_GOOD, FKTY_MAX); - args->fake_type = l; + "Wrap must be in the interval [0, 63]"); + args->wrap = ll; args->debug = true; break; - } case 'u': args->unit_test = true; @@ -125,6 +137,11 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state) if (!args->filename) argp_error(state, "The block device was not specified"); + if (args->debug && + !dev_param_valid(args->real_size_byte, + args->fake_size_byte, args->wrap)) + argp_error(state, + "The debugging parameters are not valid"); break; default: @@ -136,17 +153,17 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state) static struct argp argp = {options, parse_opt, adoc, doc, NULL, NULL, NULL}; struct unit_test_item { - enum fake_type fake_type; - int file_size_gb; - int fake_size_gb; + uint64_t real_size_byte; + uint64_t fake_size_byte; + int wrap; }; static const struct unit_test_item ftype_to_params[] = { - {FKTY_GOOD, 1, 1}, - {FKTY_BAD, 0, 1}, - {FKTY_LIMBO, 1, 2}, - {FKTY_WRAPAROUND, 1, 2}, - {FKTY_WRAPAROUND, 2, 3}, + {1ULL << 30, 1ULL << 30, 30}, + {0, 1ULL << 30, 30}, + {1ULL << 30, 1ULL << 34, 34}, + {1ULL << 31, 1ULL << 34, 31}, + {1ULL << 31, 1ULL << 34, 32}, }; #define UNIT_TEST_N_CASES \ @@ -156,13 +173,21 @@ static int unit_test(const char *filename) { int i, success = 0; for (i = 0; i < UNIT_TEST_N_CASES; i++) { + const struct unit_test_item *item = &ftype_to_params[i]; + enum fake_type origin_type = dev_param_to_type( + item->real_size_byte, item->fake_size_byte, item->wrap); + double f_real = item->real_size_byte; + double f_fake = item->fake_size_byte; + const char *unit_real = adjust_unit(&f_real); + const char *unit_fake = adjust_unit(&f_fake); + enum fake_type fake_type; uint64_t real_size_byte, announced_size_byte; - int wrap, good = 0; - struct device *dev = create_file_device(filename, - ftype_to_params[i].file_size_gb, - ftype_to_params[i].fake_size_gb, - ftype_to_params[i].fake_type); + int wrap; + struct device *dev; + + dev = create_file_device(filename, item->real_size_byte, + item->fake_size_byte, item->wrap); assert(dev); probe_device(dev, &real_size_byte, &announced_size_byte, &wrap); free_device(dev); @@ -170,32 +195,24 @@ static int unit_test(const char *filename) announced_size_byte, wrap); /* Report */ - printf("Test %i (type %s, file-size=%iGB, fake-size=%iGB): ", - i + 1, fake_type_to_name(ftype_to_params[i].fake_type), - ftype_to_params[i].file_size_gb, - ftype_to_params[i].fake_size_gb); - if (fake_type == ftype_to_params[i].fake_type) { - if (real_size_byte == - (uint64_t)ftype_to_params[i].file_size_gb << - 30) { - good = 1; - success++; - printf("Perfect!\n"); - } else { - printf("Correct type, wrong size\n"); - } - } else if (real_size_byte == - (uint64_t)ftype_to_params[i].file_size_gb << - 30) { - printf("Wrong type, correct size\n"); + printf("Test %i (type %s, real-size=%.2f %s, fake-size=%.2f %s, module=2^%i)\n", + i + 1, fake_type_to_name(origin_type), + f_real, unit_real, f_fake, unit_fake, item->wrap); + if (real_size_byte == item->real_size_byte && + announced_size_byte == item->fake_size_byte && + wrap == item->wrap) { + success++; + printf("\tPerfect!\n\n"); } else { - printf("Got it all wrong\n"); + double ret_f_real = real_size_byte; + double ret_f_fake = announced_size_byte; + const char *ret_unit_real = adjust_unit(&ret_f_real); + const char *ret_unit_fake = adjust_unit(&ret_f_fake); + printf("\tFound type %s, real size %.2f %s, fake size %.2f %s, and module 2^%i\n\n", + fake_type_to_name(fake_type), + ret_f_real, ret_unit_real, + ret_f_fake, ret_unit_fake, wrap); } - if (!good) - printf("\tFound type %s, real size %" PRIu64 " Bytes, fake size %" PRIu64 " Bytes, and wrap 2^%i\n", - fake_type_to_name(fake_type), real_size_byte, - announced_size_byte, wrap); - printf("\n"); } printf("SUMMARY: "); @@ -226,8 +243,8 @@ static int test_device(struct args *args) int wrap; dev = args->debug - ? create_file_device(args->filename, args->file_size_gb, - args->fake_size_gb, args->fake_type) + ? create_file_device(args->filename, args->real_size_byte, + args->fake_size_byte, args->wrap) : create_block_device(args->filename); assert(dev); assert(!gettimeofday(&t1, NULL)); @@ -271,9 +288,9 @@ int main(int argc, char **argv) /* Defaults. */ .unit_test = false, .debug = false, - .fake_type = FKTY_LIMBO, - .file_size_gb = 1, - .fake_size_gb = 2, + .real_size_byte = 1ULL << 31, + .fake_size_byte = 1ULL << 34, + .wrap = 31, }; /* Read parameters. */ diff --git a/libprobe.c b/libprobe.c index b8f3837..32880dc 100644 --- a/libprobe.c +++ b/libprobe.c @@ -86,15 +86,10 @@ struct file_device { /* This must be the first field. See dev_fdev() for details. */ struct device dev; - /* XXX Now the the file is unliked right after being created, - * wouldn't it be a good idea to drop field @filename? - */ - const char *filename; int fd; - int file_size_gb; - int fake_size_gb; - enum fake_type fake_type; - /* 3 free bytes. */ + uint64_t real_size_byte; + uint64_t fake_size_byte; + uint64_t address_mask; }; static inline struct file_device *dev_fdev(struct device *dev) @@ -110,30 +105,12 @@ static inline struct file_device *dev_fdev(struct device *dev) static int fdev_read_block(struct device *dev, char *buf, uint64_t block) { struct file_device *fdev = dev_fdev(dev); - off_t offset = block * BLOCK_SIZE; + off_t offset = (block * BLOCK_SIZE) & fdev->address_mask; int done; - switch (fdev->fake_type) { - case FKTY_BAD: + if ((uint64_t)offset >= fdev->real_size_byte) { memset(buf, 0, BLOCK_SIZE); return 0; - - case FKTY_LIMBO: - if (offset >= GIGABYTE * fdev->file_size_gb) { - memset(buf, 0, BLOCK_SIZE); - return 0; - } - break; - - case FKTY_WRAPAROUND: - offset %= GIGABYTE * fdev->file_size_gb; - /* Fall through. */ - - case FKTY_GOOD: - break; - - default: - assert(0); } assert(lseek(fdev->fd, offset, SEEK_SET) == offset); @@ -168,72 +145,49 @@ static int write_all(int fd, const char *buf, int count) static int fdev_write_block(struct device *dev, const char *buf, uint64_t block) { struct file_device *fdev = dev_fdev(dev); - off_t offset = block * BLOCK_SIZE; - - switch (fdev->fake_type) { - case FKTY_BAD: + off_t offset = (block * BLOCK_SIZE) & fdev->address_mask; + if ((uint64_t)offset >= fdev->real_size_byte) return 0; - - case FKTY_LIMBO: - if (offset >= GIGABYTE * fdev->file_size_gb) - return 0; - break; - - case FKTY_WRAPAROUND: - offset %= GIGABYTE * fdev->file_size_gb; - /* Fall through. */ - - case FKTY_GOOD: - break; - - default: - assert(0); - } - assert(lseek(fdev->fd, offset, SEEK_SET) == offset); return write_all(fdev->fd, buf, BLOCK_SIZE); } static uint64_t fdev_get_size_byte(struct device *dev) { - return (uint64_t)dev_fdev(dev)->fake_size_gb * GIGABYTE; + return dev_fdev(dev)->fake_size_byte; } static void fdev_free(struct device *dev) { struct file_device *fdev = dev_fdev(dev); assert(!close(fdev->fd)); - free((void *)fdev->filename); } -/* XXX Validate parameters. - * For example, if @fake_type == FKTY_GOOD, then @fake_size_gb and - * fake_size_gb must be equal. - */ struct device *create_file_device(const char *filename, - int file_size_gb, int fake_size_gb, enum fake_type fake_type) + uint64_t real_size_byte, uint64_t fake_size_byte, int wrap) { - struct file_device *fdev = malloc(sizeof(*fdev)); - if (!fdev) + struct file_device *fdev; + + if (!dev_param_valid(real_size_byte, fake_size_byte, wrap)) goto error; - fdev->filename = strdup(filename); - if (!fdev->filename) - goto fdev; + fdev = malloc(sizeof(*fdev)); + if (!fdev) + goto error; fdev->fd = open(filename, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); if (fdev->fd < 0) { err(errno, "Can't create file `%s'", filename); - goto filename; + goto fdev; } /* Unlinking the file now guarantees that it won't exist if * there is a crash. */ assert(!unlink(filename)); - fdev->file_size_gb = file_size_gb; - fdev->fake_size_gb = fake_size_gb; - fdev->fake_type = fake_type; + fdev->real_size_byte = real_size_byte; + fdev->fake_size_byte = fake_size_byte; + fdev->address_mask = (((uint64_t)1) << wrap) - 1; fdev->dev.read_block = fdev_read_block; fdev->dev.write_block = fdev_write_block; @@ -243,8 +197,6 @@ struct device *create_file_device(const char *filename, return &fdev->dev; -filename: - free((void *)fdev->filename); fdev: free(fdev); error: diff --git a/libprobe.h b/libprobe.h index 620cd29..f982afb 100644 --- a/libprobe.h +++ b/libprobe.h @@ -33,7 +33,7 @@ enum fake_type dev_param_to_type(uint64_t real_size_byte, struct device; struct device *create_file_device(const char *filename, - int file_size_gb, int fake_size_gb, enum fake_type fake_type); + uint64_t real_size_byte, uint64_t fake_size_byte, int wrap); struct device *create_block_device(const char *filename);