mirror of
https://github.com/AltraMayor/f3.git
synced 2025-08-03 18:46:00 -04:00
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:
parent
185145f5a1
commit
ea938f2ff2
151
f3probe.c
151
f3probe.c
@ -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 <<
|
success++;
|
||||||
30) {
|
printf("\tPerfect!\n\n");
|
||||||
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");
|
|
||||||
} else {
|
} 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: ");
|
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. */
|
||||||
|
86
libprobe.c
86
libprobe.c
@ -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:
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user