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.
This commit is contained in:
Michel Machado 2014-08-21 17:44:26 -04:00
parent 185145f5a1
commit ea938f2ff2
3 changed files with 104 additions and 135 deletions

149
f3probe.c
View File

@ -1,3 +1,5 @@
#define _POSIX_C_SOURCE 200112L
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -40,12 +42,15 @@ static char doc[] = "F3 Probe -- probe a block device for "
*/ */
static struct argp_option options[] = { static struct argp_option options[] = {
{"debug-file-size", 'd', "SIZE_GB", OPTION_HIDDEN, {"debug", 'd', NULL, OPTION_HIDDEN,
"Enable debuging with a regular file", 1}, "Enable debugging; only needed if none --debug-* option used",
{"debug-fake-size", 'f', "SIZE_GB", OPTION_HIDDEN, 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}, "Fake size of the emulated flash", 0},
{"debug-type", 't', "N", OPTION_HIDDEN, {"debug-wrap", 'w', "N", OPTION_HIDDEN,
"Set the type of the fake flash", 0}, "Wrap parameter of the emulated flash", 0},
{"debug-unit-test", 'u', NULL, OPTION_HIDDEN, {"debug-unit-test", 'u', NULL, OPTION_HIDDEN,
"Run a unit test; it ignores all other debug options", 0}, "Run a unit test; it ignores all other debug options", 0},
{ 0 } { 0 }
@ -57,54 +62,61 @@ struct args {
/* Debugging options. */ /* Debugging options. */
bool unit_test; bool unit_test;
bool debug; bool debug;
enum fake_type fake_type; /* 2 bytes free. */
/* 1 bytes free. */ uint64_t real_size_byte;
int file_size_gb; uint64_t fake_size_byte;
int fake_size_gb; 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; char *end;
long l = strtol(arg, &end, 0); long long ll = strtoll(arg, &end, 0);
if (!arg) if (!arg)
argp_error(state, "An integer must be provided"); argp_error(state, "An integer must be provided");
if (!*arg || *end) if (!*arg || *end)
argp_error(state, "`%s' is not an integer", arg); 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) static error_t parse_opt(int key, char *arg, struct argp_state *state)
{ {
struct args *args = state->input; struct args *args = state->input;
long long ll;
switch (key) { switch (key) {
case 'd': case 'd':
args->file_size_gb = arg_to_long(state, arg); args->debug = true;
if (args->file_size_gb < 1) break;
case 'r':
ll = arg_to_long_long(state, arg);
if (ll < 0)
argp_error(state, 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; args->debug = true;
break; break;
case 'f': case 'f':
args->fake_size_gb = arg_to_long(state, arg); ll = arg_to_long_long(state, arg);
if (args->fake_size_gb < 1) if (ll < 0)
argp_error(state, 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; args->debug = true;
break; break;
case 't': { case 'w':
long l = arg_to_long(state, arg); ll = arg_to_long_long(state, arg);
if (l < FKTY_GOOD || l >= FKTY_MAX) if (ll < 0 || ll >= 64)
argp_error(state, argp_error(state,
"Fake type must be a number in the interval [%i, %i]", "Wrap must be in the interval [0, 63]");
FKTY_GOOD, FKTY_MAX); args->wrap = ll;
args->fake_type = l;
args->debug = true; args->debug = true;
break; break;
}
case 'u': case 'u':
args->unit_test = true; args->unit_test = true;
@ -125,6 +137,11 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state)
if (!args->filename) if (!args->filename)
argp_error(state, argp_error(state,
"The block device was not specified"); "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; break;
default: 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}; static struct argp argp = {options, parse_opt, adoc, doc, NULL, NULL, NULL};
struct unit_test_item { struct unit_test_item {
enum fake_type fake_type; uint64_t real_size_byte;
int file_size_gb; uint64_t fake_size_byte;
int fake_size_gb; int wrap;
}; };
static const struct unit_test_item ftype_to_params[] = { static const struct unit_test_item ftype_to_params[] = {
{FKTY_GOOD, 1, 1}, {1ULL << 30, 1ULL << 30, 30},
{FKTY_BAD, 0, 1}, {0, 1ULL << 30, 30},
{FKTY_LIMBO, 1, 2}, {1ULL << 30, 1ULL << 34, 34},
{FKTY_WRAPAROUND, 1, 2}, {1ULL << 31, 1ULL << 34, 31},
{FKTY_WRAPAROUND, 2, 3}, {1ULL << 31, 1ULL << 34, 32},
}; };
#define UNIT_TEST_N_CASES \ #define UNIT_TEST_N_CASES \
@ -156,13 +173,21 @@ static int unit_test(const char *filename)
{ {
int i, success = 0; int i, success = 0;
for (i = 0; i < UNIT_TEST_N_CASES; i++) { 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; enum fake_type fake_type;
uint64_t real_size_byte, announced_size_byte; uint64_t real_size_byte, announced_size_byte;
int wrap, good = 0; int wrap;
struct device *dev = create_file_device(filename, struct device *dev;
ftype_to_params[i].file_size_gb,
ftype_to_params[i].fake_size_gb, dev = create_file_device(filename, item->real_size_byte,
ftype_to_params[i].fake_type); item->fake_size_byte, item->wrap);
assert(dev); assert(dev);
probe_device(dev, &real_size_byte, &announced_size_byte, &wrap); probe_device(dev, &real_size_byte, &announced_size_byte, &wrap);
free_device(dev); free_device(dev);
@ -170,32 +195,24 @@ static int unit_test(const char *filename)
announced_size_byte, wrap); announced_size_byte, wrap);
/* Report */ /* Report */
printf("Test %i (type %s, file-size=%iGB, fake-size=%iGB): ", printf("Test %i (type %s, real-size=%.2f %s, fake-size=%.2f %s, module=2^%i)\n",
i + 1, fake_type_to_name(ftype_to_params[i].fake_type), i + 1, fake_type_to_name(origin_type),
ftype_to_params[i].file_size_gb, f_real, unit_real, f_fake, unit_fake, item->wrap);
ftype_to_params[i].fake_size_gb); if (real_size_byte == item->real_size_byte &&
if (fake_type == ftype_to_params[i].fake_type) { announced_size_byte == item->fake_size_byte &&
if (real_size_byte == wrap == item->wrap) {
(uint64_t)ftype_to_params[i].file_size_gb <<
30) {
good = 1;
success++; success++;
printf("Perfect!\n"); printf("\tPerfect!\n\n");
} else { } else {
printf("Correct type, wrong size\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);
} }
} else if (real_size_byte ==
(uint64_t)ftype_to_params[i].file_size_gb <<
30) {
printf("Wrong type, correct size\n");
} else {
printf("Got it all wrong\n");
}
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: "); printf("SUMMARY: ");
@ -226,8 +243,8 @@ static int test_device(struct args *args)
int wrap; int wrap;
dev = args->debug dev = args->debug
? create_file_device(args->filename, args->file_size_gb, ? create_file_device(args->filename, args->real_size_byte,
args->fake_size_gb, args->fake_type) args->fake_size_byte, args->wrap)
: create_block_device(args->filename); : create_block_device(args->filename);
assert(dev); assert(dev);
assert(!gettimeofday(&t1, NULL)); assert(!gettimeofday(&t1, NULL));
@ -271,9 +288,9 @@ int main(int argc, char **argv)
/* Defaults. */ /* Defaults. */
.unit_test = false, .unit_test = false,
.debug = false, .debug = false,
.fake_type = FKTY_LIMBO, .real_size_byte = 1ULL << 31,
.file_size_gb = 1, .fake_size_byte = 1ULL << 34,
.fake_size_gb = 2, .wrap = 31,
}; };
/* Read parameters. */ /* Read parameters. */

View File

@ -86,15 +86,10 @@ struct file_device {
/* This must be the first field. See dev_fdev() for details. */ /* This must be the first field. See dev_fdev() for details. */
struct device dev; 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 fd;
int file_size_gb; uint64_t real_size_byte;
int fake_size_gb; uint64_t fake_size_byte;
enum fake_type fake_type; uint64_t address_mask;
/* 3 free bytes. */
}; };
static inline struct file_device *dev_fdev(struct device *dev) 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) static int fdev_read_block(struct device *dev, char *buf, uint64_t block)
{ {
struct file_device *fdev = dev_fdev(dev); struct file_device *fdev = dev_fdev(dev);
off_t offset = block * BLOCK_SIZE; off_t offset = (block * BLOCK_SIZE) & fdev->address_mask;
int done; int done;
switch (fdev->fake_type) { if ((uint64_t)offset >= fdev->real_size_byte) {
case FKTY_BAD:
memset(buf, 0, BLOCK_SIZE); memset(buf, 0, BLOCK_SIZE);
return 0; 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); 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) static int fdev_write_block(struct device *dev, const char *buf, uint64_t block)
{ {
struct file_device *fdev = dev_fdev(dev); struct file_device *fdev = dev_fdev(dev);
off_t offset = block * BLOCK_SIZE; off_t offset = (block * BLOCK_SIZE) & fdev->address_mask;
if ((uint64_t)offset >= fdev->real_size_byte)
switch (fdev->fake_type) {
case FKTY_BAD:
return 0; 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); assert(lseek(fdev->fd, offset, SEEK_SET) == offset);
return write_all(fdev->fd, buf, BLOCK_SIZE); return write_all(fdev->fd, buf, BLOCK_SIZE);
} }
static uint64_t fdev_get_size_byte(struct device *dev) 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) static void fdev_free(struct device *dev)
{ {
struct file_device *fdev = dev_fdev(dev); struct file_device *fdev = dev_fdev(dev);
assert(!close(fdev->fd)); 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, 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)); struct file_device *fdev;
if (!fdev)
if (!dev_param_valid(real_size_byte, fake_size_byte, wrap))
goto error; goto error;
fdev->filename = strdup(filename); fdev = malloc(sizeof(*fdev));
if (!fdev->filename) if (!fdev)
goto fdev; goto error;
fdev->fd = open(filename, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); fdev->fd = open(filename, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
if (fdev->fd < 0) { if (fdev->fd < 0) {
err(errno, "Can't create file `%s'", filename); err(errno, "Can't create file `%s'", filename);
goto filename; goto fdev;
} }
/* Unlinking the file now guarantees that it won't exist if /* Unlinking the file now guarantees that it won't exist if
* there is a crash. * there is a crash.
*/ */
assert(!unlink(filename)); assert(!unlink(filename));
fdev->file_size_gb = file_size_gb; fdev->real_size_byte = real_size_byte;
fdev->fake_size_gb = fake_size_gb; fdev->fake_size_byte = fake_size_byte;
fdev->fake_type = fake_type; fdev->address_mask = (((uint64_t)1) << wrap) - 1;
fdev->dev.read_block = fdev_read_block; fdev->dev.read_block = fdev_read_block;
fdev->dev.write_block = fdev_write_block; fdev->dev.write_block = fdev_write_block;
@ -243,8 +197,6 @@ struct device *create_file_device(const char *filename,
return &fdev->dev; return &fdev->dev;
filename:
free((void *)fdev->filename);
fdev: fdev:
free(fdev); free(fdev);
error: error:

View File

@ -33,7 +33,7 @@ enum fake_type dev_param_to_type(uint64_t real_size_byte,
struct device; struct device;
struct device *create_file_device(const char *filename, 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); struct device *create_block_device(const char *filename);