mirror of
https://github.com/AltraMayor/f3.git
synced 2025-09-10 07:39:31 -04:00
Verify that "all" F3 files are present
If an F3 file (i.e. *.fff) but the last one is missing, f3read will report the missing file with a warning similar to this one: Missing file 0001.fff AND the following warning will be presented at the end of the report: WARNING: Not all F3 files are available Kenneth Arnold suggested this feature in an e-mail: "I also noticed that if one of the files is completely missing, f3read will still report no errors. The chance that this will happen on its own without messing something else up is too small to worry about, except possibly in the case that the device was actually read-only and no files actually got created. In that case, f3read succeeds with 0 sectors OK and 0 sectors lost." The last F3 file, if missing, is not reported because it can be confusing. The following example gives an idea of the problem: a memory card wasn't empty when f3write was called and the last F3 file created was 0010.fff with exactly 1GB; here none F3 file is missing. Then, some non-F3 files were removed, and f3read is called. f3read would report a missing file even when non-F3 files were removed! The problem gets a little bit uglier if 0010.fff in the example is less that 1GB, or the file system doesn't let f3write to fill it up for a few bytes (this should be very rare, but there's no guarantee that it cannot happen). Finally, the implementation of this feature has a positive side effect: f3read processes F3 files in ascending order, that is, 0001.fff, 0002.fff, ..., NNNN.fff. Before the order was file system dependent. Magic numbers related to the number of digits in the F3 files' names were eliminated.
This commit is contained in:
parent
9a70cbbbf1
commit
d350745fc2
151
f3read.c
151
f3read.c
@ -13,20 +13,6 @@
|
|||||||
|
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
static uint64_t offset_from_filename(const char *filename)
|
|
||||||
{
|
|
||||||
char str[5];
|
|
||||||
uint64_t number;
|
|
||||||
|
|
||||||
/* Obtain number. */
|
|
||||||
assert(is_my_file(filename));
|
|
||||||
strncpy(str, filename, 4);
|
|
||||||
str[4] = '\0';
|
|
||||||
number = (uint64_t) strtol(str, NULL, 10) - 1;
|
|
||||||
|
|
||||||
return number * GIGABYTES;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void update_dt(struct timeval *dt, const struct timeval *t1,
|
static inline void update_dt(struct timeval *dt, const struct timeval *t1,
|
||||||
const struct timeval *t2)
|
const struct timeval *t2)
|
||||||
{
|
{
|
||||||
@ -48,11 +34,13 @@ static inline void update_dt(struct timeval *dt, const struct timeval *t1,
|
|||||||
#define CLEAR ("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b" \
|
#define CLEAR ("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b" \
|
||||||
"\b\b\b\b\b\b\b\b\b\b\b\b\b")
|
"\b\b\b\b\b\b\b\b\b\b\b\b\b")
|
||||||
|
|
||||||
static void validate_file(const char *path, const char *filename,
|
static void validate_file(const char *path, int number,
|
||||||
uint64_t *ptr_ok, uint64_t *ptr_corrupted, uint64_t *ptr_changed,
|
uint64_t *ptr_ok, uint64_t *ptr_corrupted, uint64_t *ptr_changed,
|
||||||
uint64_t *ptr_overwritten, uint64_t *ptr_size, int *read_all,
|
uint64_t *ptr_overwritten, uint64_t *ptr_size, int *read_all,
|
||||||
struct timeval *ptr_dt, int progress)
|
struct timeval *ptr_dt, int progress)
|
||||||
{
|
{
|
||||||
|
char full_fn[PATH_MAX];
|
||||||
|
const char *filename;
|
||||||
uint8_t sector[SECTOR_SIZE], *p, *ptr_end;
|
uint8_t sector[SECTOR_SIZE], *p, *ptr_end;
|
||||||
FILE *f;
|
FILE *f;
|
||||||
int fd;
|
int fd;
|
||||||
@ -61,7 +49,6 @@ static void validate_file(const char *path, const char *filename,
|
|||||||
uint64_t offset, expected_offset;
|
uint64_t offset, expected_offset;
|
||||||
struct drand48_data state;
|
struct drand48_data state;
|
||||||
long int rand_int;
|
long int rand_int;
|
||||||
char full_fn[PATH_MAX];
|
|
||||||
char *tail_msg = "";
|
char *tail_msg = "";
|
||||||
struct timeval t1, t2;
|
struct timeval t1, t2;
|
||||||
/* Progress time. */
|
/* Progress time. */
|
||||||
@ -69,9 +56,9 @@ static void validate_file(const char *path, const char *filename,
|
|||||||
|
|
||||||
*ptr_ok = *ptr_corrupted = *ptr_changed = *ptr_overwritten = 0;
|
*ptr_ok = *ptr_corrupted = *ptr_changed = *ptr_overwritten = 0;
|
||||||
|
|
||||||
|
full_fn_from_number(full_fn, &filename, path, number);
|
||||||
printf("Validating file %s ... %s", filename, progress ? BLANK : "");
|
printf("Validating file %s ... %s", filename, progress ? BLANK : "");
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
get_full_fn(full_fn, sizeof(full_fn), path, filename);
|
|
||||||
f = fopen(full_fn, "rb");
|
f = fopen(full_fn, "rb");
|
||||||
if (!f)
|
if (!f)
|
||||||
err(errno, "Can't open file %s", full_fn);
|
err(errno, "Can't open file %s", full_fn);
|
||||||
@ -92,7 +79,7 @@ static void validate_file(const char *path, const char *filename,
|
|||||||
|
|
||||||
ptr_end = sector + SECTOR_SIZE;
|
ptr_end = sector + SECTOR_SIZE;
|
||||||
sectors_read = fread(sector, SECTOR_SIZE, 1, f);
|
sectors_read = fread(sector, SECTOR_SIZE, 1, f);
|
||||||
expected_offset = offset_from_filename(filename);
|
expected_offset = (uint64_t)number * GIGABYTES;
|
||||||
while (sectors_read > 0) {
|
while (sectors_read > 0) {
|
||||||
assert(sectors_read == 1);
|
assert(sectors_read == 1);
|
||||||
offset = *((uint64_t *) sector);
|
offset = *((uint64_t *) sector);
|
||||||
@ -162,44 +149,45 @@ static inline double dt_to_s(struct timeval *dt)
|
|||||||
return ret > 0 ? ret : 1;
|
return ret > 0 ? ret : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int iterate_path(const char *path, int progress)
|
static void iterate_files(const char *path, const int *files, int progress)
|
||||||
{
|
{
|
||||||
DIR *ptr_dir;
|
|
||||||
struct dirent *entry;
|
|
||||||
const char *filename, *unit;
|
|
||||||
uint64_t tot_ok, tot_corrupted, tot_changed, tot_overwritten, tot_size;
|
uint64_t tot_ok, tot_corrupted, tot_changed, tot_overwritten, tot_size;
|
||||||
struct timeval tot_dt = { .tv_sec = 0, .tv_usec = 0 };
|
struct timeval tot_dt = { .tv_sec = 0, .tv_usec = 0 };
|
||||||
double read_speed;
|
double read_speed;
|
||||||
int and_read_all;
|
const char *unit;
|
||||||
|
int and_read_all = 1;
|
||||||
|
int or_missing_file = 0;
|
||||||
|
int number = 0;
|
||||||
|
|
||||||
ptr_dir = opendir(path);
|
|
||||||
if (!ptr_dir)
|
|
||||||
err(errno, "Can't open path %s", path);
|
|
||||||
|
|
||||||
entry = readdir(ptr_dir);
|
|
||||||
tot_ok = tot_corrupted = tot_changed = tot_overwritten = tot_size = 0;
|
tot_ok = tot_corrupted = tot_changed = tot_overwritten = tot_size = 0;
|
||||||
and_read_all = 1;
|
|
||||||
printf(" SECTORS "
|
printf(" SECTORS "
|
||||||
" ok/corrupted/changed/overwritten\n");
|
" ok/corrupted/changed/overwritten\n");
|
||||||
while (entry) {
|
|
||||||
filename = entry->d_name;
|
while (*files >= 0) {
|
||||||
if (is_my_file(filename)) {
|
uint64_t sec_ok, sec_corrupted, sec_changed,
|
||||||
uint64_t sec_ok, sec_corrupted, sec_changed,
|
sec_overwritten, file_size;
|
||||||
sec_overwritten, file_size;
|
int read_all;
|
||||||
int read_all;
|
|
||||||
validate_file(path, filename, &sec_ok, &sec_corrupted,
|
or_missing_file = or_missing_file || (*files != number);
|
||||||
&sec_changed, &sec_overwritten,
|
for (; number < *files; number++) {
|
||||||
&file_size, &read_all, &tot_dt, progress);
|
char full_fn[PATH_MAX];
|
||||||
tot_ok += sec_ok;
|
const char *filename;
|
||||||
tot_corrupted += sec_corrupted;
|
full_fn_from_number(full_fn, &filename, "", number);
|
||||||
tot_changed += sec_changed;
|
printf("Missing file %s\n", filename);
|
||||||
tot_overwritten += sec_overwritten;
|
|
||||||
tot_size += file_size;
|
|
||||||
and_read_all = and_read_all && read_all;
|
|
||||||
}
|
}
|
||||||
entry = readdir(ptr_dir);
|
number++;
|
||||||
|
|
||||||
|
validate_file(path, *files, &sec_ok, &sec_corrupted,
|
||||||
|
&sec_changed, &sec_overwritten,
|
||||||
|
&file_size, &read_all, &tot_dt, progress);
|
||||||
|
tot_ok += sec_ok;
|
||||||
|
tot_corrupted += sec_corrupted;
|
||||||
|
tot_changed += sec_changed;
|
||||||
|
tot_overwritten += sec_overwritten;
|
||||||
|
tot_size += file_size;
|
||||||
|
and_read_all = and_read_all && read_all;
|
||||||
|
files++;
|
||||||
}
|
}
|
||||||
closedir(ptr_dir);
|
|
||||||
assert(tot_size / SECTOR_SIZE ==
|
assert(tot_size / SECTOR_SIZE ==
|
||||||
(tot_ok + tot_corrupted + tot_changed + tot_overwritten));
|
(tot_ok + tot_corrupted + tot_changed + tot_overwritten));
|
||||||
|
|
||||||
@ -208,6 +196,8 @@ static int iterate_path(const char *path, int progress)
|
|||||||
report("\t Corrupted:", tot_corrupted);
|
report("\t Corrupted:", tot_corrupted);
|
||||||
report("\tSlightly changed:", tot_changed);
|
report("\tSlightly changed:", tot_changed);
|
||||||
report("\t Overwritten:", tot_overwritten);
|
report("\t Overwritten:", tot_overwritten);
|
||||||
|
if (or_missing_file)
|
||||||
|
printf("WARNING: Not all F3 files are available\n");
|
||||||
if (!and_read_all)
|
if (!and_read_all)
|
||||||
printf("WARNING: Not all data was read due to I/O error(s)\n");
|
printf("WARNING: Not all data was read due to I/O error(s)\n");
|
||||||
|
|
||||||
@ -215,14 +205,79 @@ static int iterate_path(const char *path, int progress)
|
|||||||
read_speed = (double)tot_size / dt_to_s(&tot_dt);
|
read_speed = (double)tot_size / dt_to_s(&tot_dt);
|
||||||
unit = adjust_unit(&read_speed);
|
unit = adjust_unit(&read_speed);
|
||||||
printf("Average reading speed: %.2f %s/s\n", read_speed, unit);
|
printf("Average reading speed: %.2f %s/s\n", read_speed, unit);
|
||||||
return 0;
|
}
|
||||||
|
|
||||||
|
static int number_from_filename(const char *filename)
|
||||||
|
{
|
||||||
|
char str[FILENAME_NUM_DIGITS + 1];
|
||||||
|
assert(is_my_file(filename));
|
||||||
|
strncpy(str, filename, FILENAME_NUM_DIGITS);
|
||||||
|
str[FILENAME_NUM_DIGITS] = '\0';
|
||||||
|
return strtol(str, NULL, 10) - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Don't call this function directly, use ls_my_files instead. */
|
||||||
|
static int *__ls_my_files(DIR *dir, int *pcount, int *pindex)
|
||||||
|
{
|
||||||
|
struct dirent *entry;
|
||||||
|
const char *filename;
|
||||||
|
|
||||||
|
entry = readdir(dir);
|
||||||
|
if (!entry) {
|
||||||
|
int *ret = malloc(sizeof(const int) * (*pcount + 1));
|
||||||
|
*pindex = *pcount - 1;
|
||||||
|
ret[*pcount] = -1;
|
||||||
|
closedir(dir);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
filename = entry->d_name;
|
||||||
|
if (is_my_file(filename)) {
|
||||||
|
int my_index;
|
||||||
|
int *ret;
|
||||||
|
(*pcount)++;
|
||||||
|
ret = __ls_my_files(dir, pcount, &my_index);
|
||||||
|
ret[my_index] = number_from_filename(filename);
|
||||||
|
*pindex = my_index - 1;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return __ls_my_files(dir, pcount, pindex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* To be used with qsort(3). */
|
||||||
|
static int cmpintp(const void *p1, const void *p2)
|
||||||
|
{
|
||||||
|
return *(const int *)p1 - *(const int *)p2;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const int *ls_my_files(const char *path)
|
||||||
|
{
|
||||||
|
DIR *dir = opendir(path);
|
||||||
|
int my_count;
|
||||||
|
int my_index;
|
||||||
|
int *ret;
|
||||||
|
|
||||||
|
if (!dir)
|
||||||
|
err(errno, "Can't open path %s", path);
|
||||||
|
|
||||||
|
my_count = 0;
|
||||||
|
ret = __ls_my_files(dir, &my_count, &my_index);
|
||||||
|
assert(my_index == -1);
|
||||||
|
qsort(ret, my_count, sizeof(*ret), cmpintp);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
if (argc == 2) {
|
if (argc == 2) {
|
||||||
|
const char *path = argv[1];
|
||||||
|
const int *files = ls_my_files(path);
|
||||||
/* If stdout isn't a terminal, supress progress. */
|
/* If stdout isn't a terminal, supress progress. */
|
||||||
return iterate_path(argv[1], isatty(STDOUT_FILENO));
|
int progress = isatty(STDOUT_FILENO);
|
||||||
|
iterate_files(path, files, progress);
|
||||||
|
free((void *)files);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf(stderr, "Usage: f3read <PATH>\n");
|
fprintf(stderr, "Usage: f3read <PATH>\n");
|
||||||
|
18
f3write.c
18
f3write.c
@ -269,7 +269,8 @@ static inline void end_measurement(int fd, struct flow *fw)
|
|||||||
static int create_and_fill_file(const char *path, int number, size_t size,
|
static int create_and_fill_file(const char *path, int number, size_t size,
|
||||||
struct flow *fw)
|
struct flow *fw)
|
||||||
{
|
{
|
||||||
char filename[PATH_MAX];
|
char full_fn[PATH_MAX];
|
||||||
|
const char *filename;
|
||||||
int fd, fine;
|
int fd, fine;
|
||||||
void *buf;
|
void *buf;
|
||||||
size_t remaining;
|
size_t remaining;
|
||||||
@ -280,17 +281,17 @@ static int create_and_fill_file(const char *path, int number, size_t size,
|
|||||||
assert(size % fw->block_size == 0);
|
assert(size % fw->block_size == 0);
|
||||||
|
|
||||||
/* Create the file. */
|
/* Create the file. */
|
||||||
assert(snprintf(filename, sizeof(filename), "%s/%04i.fff",
|
|
||||||
path, number + 1) < sizeof(filename));
|
full_fn_from_number(full_fn, &filename, path, number);
|
||||||
printf("Creating file %04i.fff ... ", number + 1);
|
printf("Creating file %s ... ", filename);
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR);
|
fd = open(full_fn, O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR);
|
||||||
if (fd < 0) {
|
if (fd < 0) {
|
||||||
if (errno == ENOSPC) {
|
if (errno == ENOSPC) {
|
||||||
printf("No space left.\n");
|
printf("No space left.\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
err(errno, "Can't create file %s", filename);
|
err(errno, "Can't create file %s", full_fn);
|
||||||
}
|
}
|
||||||
assert(fd >= 0);
|
assert(fd >= 0);
|
||||||
|
|
||||||
@ -311,7 +312,7 @@ static int create_and_fill_file(const char *path, int number, size_t size,
|
|||||||
fine = 0;
|
fine = 0;
|
||||||
break;
|
break;
|
||||||
} else
|
} else
|
||||||
err(errno, "Write to file %s failed", filename);
|
err(errno, "Write to file %s failed", full_fn);
|
||||||
}
|
}
|
||||||
assert(written == fw->block_size);
|
assert(written == fw->block_size);
|
||||||
remaining -= written;
|
remaining -= written;
|
||||||
@ -397,7 +398,8 @@ static void unlink_old_files(const char *path)
|
|||||||
filename = entry->d_name;
|
filename = entry->d_name;
|
||||||
if (is_my_file(filename)) {
|
if (is_my_file(filename)) {
|
||||||
char full_fn[PATH_MAX];
|
char full_fn[PATH_MAX];
|
||||||
get_full_fn(full_fn, sizeof(full_fn), path, filename);
|
assert(snprintf(full_fn, sizeof(full_fn), "%s/%s",
|
||||||
|
path, filename) < sizeof(full_fn));
|
||||||
printf("Removing old file %s ...\n", filename);
|
printf("Removing old file %s ...\n", filename);
|
||||||
if (unlink(full_fn))
|
if (unlink(full_fn))
|
||||||
err(errno, "Can't remove file %s", full_fn);
|
err(errno, "Can't remove file %s", full_fn);
|
||||||
|
13
utils.c
13
utils.c
@ -14,6 +14,19 @@ const char *adjust_unit(double *ptr_bytes)
|
|||||||
return units[i];
|
return units[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void full_fn_from_number(char *full_fn, const char **filename,
|
||||||
|
const char *path, int num)
|
||||||
|
{
|
||||||
|
static char format[32] = "";
|
||||||
|
if (!format[0]) {
|
||||||
|
assert(FILENAME_NUM_DIGITS > 0);
|
||||||
|
assert(FILENAME_NUM_DIGITS < 10);
|
||||||
|
sprintf(format, "%%s/%%%02ii.fff", FILENAME_NUM_DIGITS);
|
||||||
|
}
|
||||||
|
assert(snprintf(full_fn, PATH_MAX, format, path, num + 1) < PATH_MAX);
|
||||||
|
*filename = full_fn + strlen(path) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef APPLE_MAC
|
#ifdef APPLE_MAC
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
21
utils.h
21
utils.h
@ -8,6 +8,9 @@
|
|||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
|
||||||
|
#define FILENAME_NUM_DIGITS 4
|
||||||
|
#define FILENAME_SIZE (FILENAME_NUM_DIGITS + 4)
|
||||||
|
|
||||||
#define SECTOR_SIZE (512)
|
#define SECTOR_SIZE (512)
|
||||||
#define GIGABYTES (1024 * 1024 * 1024)
|
#define GIGABYTES (1024 * 1024 * 1024)
|
||||||
|
|
||||||
@ -15,18 +18,16 @@ const char *adjust_unit(double *ptr_bytes);
|
|||||||
|
|
||||||
static inline int is_my_file(const char *filename)
|
static inline int is_my_file(const char *filename)
|
||||||
{
|
{
|
||||||
return (strlen(filename) == 8) && isdigit(filename[0]) &&
|
return (strlen(filename) == FILENAME_SIZE) &&
|
||||||
isdigit(filename[1]) && isdigit(filename[2]) &&
|
isdigit(filename[0]) && isdigit(filename[1]) &&
|
||||||
isdigit(filename[3]) && (filename[4] == '.') &&
|
isdigit(filename[2]) && isdigit(filename[3]) &&
|
||||||
(filename[5] == 'f') && (filename[6] == 'f') &&
|
(filename[4] == '.') && (filename[5] == 'f') &&
|
||||||
(filename[7] == 'f');
|
(filename[6] == 'f') && (filename[7] == 'f');
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void get_full_fn(char *full_fn, int len,
|
/* @filename should be PATH_MAX long. */
|
||||||
const char *path, const char *filename)
|
void full_fn_from_number(char *full_fn, const char **filename,
|
||||||
{
|
const char *path, int num);
|
||||||
assert(snprintf(full_fn, len, "%s/%s", path, filename) < len);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline long delay_ms(const struct timeval *t1, const struct timeval *t2)
|
static inline long delay_ms(const struct timeval *t1, const struct timeval *t2)
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user