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

151
f3probe.c
View File

@ -1,3 +1,5 @@
#define _POSIX_C_SOURCE 200112L
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -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. */

View File

@ -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:

View File

@ -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);