f3brew: work at block level

f3brew were working at sector level because
it was borrowing code from f3write/f3read which work at that level.
This patch writes new functions to fill out, and validate blocks.

This change is important because
f3probe needs to validate blocks as well for its coming features.

A side effect of this patch is that all experimental applications
(i.e. f3probe, f3brew, and f3fix) no longer depend on
code from f3write and f3read.
This commit is contained in:
Michel Machado 2015-11-04 16:23:28 -05:00
parent cc24fdf5b2
commit 246011f053
6 changed files with 142 additions and 145 deletions

View File

@ -28,13 +28,13 @@ f3write: utils.o f3write.o
f3read: utils.o f3read.o
$(CC) -o $@ $^
f3probe: libutils.o libdevs.o libprobe.o utils.o f3probe.o
f3probe: libutils.o libdevs.o libprobe.o f3probe.o
$(CC) -o $@ $^ -lm -ludev
f3brew: libutils.o libdevs.o utils.o f3brew.o
f3brew: libutils.o libdevs.o f3brew.o
$(CC) -o $@ $^ -lm -ludev
f3fix: utils.o f3fix.o
f3fix: libutils.o f3fix.o
$(CC) -o $@ $^ -lparted
-include *.d

189
f3brew.c
View File

@ -10,7 +10,6 @@
#include "version.h"
#include "libutils.h"
#include "libdevs.h"
#include "utils.h"
/* Argp's global variables. */
const char *argp_program_version = "F3 BREW " F3_STR_VERSION;
@ -195,57 +194,43 @@ 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};
/* XXX Avoid code duplication. This function is copied from f3write.c. */
static uint64_t fill_buffer(void *buf, size_t size, uint64_t offset)
{
uint8_t *p, *ptr_next_sector, *ptr_end;
uint64_t rn;
assert(size > 0);
assert(size % SECTOR_SIZE == 0);
assert(SECTOR_SIZE >= sizeof(offset) + sizeof(rn));
assert((SECTOR_SIZE - sizeof(offset)) % sizeof(rn) == 0);
p = buf;
ptr_end = p + size;
while (p < ptr_end) {
rn = offset;
memmove(p, &offset, sizeof(offset));
ptr_next_sector = p + SECTOR_SIZE;
p += sizeof(offset);
for (; p < ptr_next_sector; p += sizeof(rn)) {
rn = random_number(rn);
memmove(p, &rn, sizeof(rn));
}
assert(p == ptr_next_sector);
offset += SECTOR_SIZE;
}
return offset;
}
static void write_blocks(char *stamp_blk, struct device *dev,
uint64_t first_block, uint64_t last_block)
{
const int block_size = dev_get_block_size(dev);
uint64_t sector_offset = first_block << dev_get_block_order(dev);
const int block_order = dev_get_block_order(dev);
uint64_t offset = first_block << block_order;
uint64_t i;
for (i = first_block; i <= last_block; i++) {
sector_offset =
fill_buffer(stamp_blk, block_size, sector_offset);
fill_buffer_with_block(stamp_blk, block_order, offset, 0);
if (dev_write_block(dev, stamp_blk, i))
warn("Failed writing block 0x%" PRIx64, i);
offset += block_size;
}
}
/* XXX Properly handle return errors. */
static void test_write_blocks(struct device *dev,
uint64_t first_block, uint64_t last_block)
{
const int block_order = dev_get_block_order(dev);
const int block_size = dev_get_block_size(dev);
char stack[align_head(block_order) + block_size];
char *blk = align_mem(stack, block_order);
printf("Writing blocks from 0x%" PRIx64 " to 0x%" PRIx64 "...",
first_block, last_block);
fflush(stdout);
write_blocks(blk, dev, first_block, last_block);
printf(" Done\n\n");
}
enum block_state {
bs_unknown,
bs_good,
bs_changed,
bs_bad,
bs_overwritten,
bs_overwritten_changed,
};
struct block_range {
@ -254,59 +239,17 @@ struct block_range {
uint64_t start_sector_offset;
uint64_t end_sector_offset;
/* Only used for states bs_overwritten and bs_overwritten_changed. */
/* Only used by state bs_overwritten. */
uint64_t found_sector_offset;
};
/* XXX Avoid code duplication. Some code of this function is copied from
* f3read.c.
*/
#define TOLERANCE 2
static enum block_state validate_sector(uint64_t expected_sector_offset,
const char *sector, uint64_t *found_sector_offset)
{
uint64_t sector_offset, rn;
const char *p, *ptr_end;
int error_count;
sector_offset = *((__typeof__(sector_offset) *) sector);
rn = sector_offset;
p = sector + sizeof(sector_offset);
ptr_end = sector + SECTOR_SIZE;
error_count = 0;
for (; error_count <= TOLERANCE && p < ptr_end; p += sizeof(rn)) {
rn = random_number(rn);
if (rn != *((__typeof__(rn) *) p))
error_count++;
}
if (sector_offset == expected_sector_offset) {
if (error_count == 0)
return bs_good;
else if (error_count <= TOLERANCE)
return bs_changed;
else
return bs_bad;
} else if (error_count == 0) {
*found_sector_offset = sector_offset;
return bs_overwritten;
} else if (error_count <= TOLERANCE) {
*found_sector_offset = sector_offset;
return bs_overwritten_changed;
} else {
return bs_bad;
}
}
static const char *block_state_to_str(enum block_state state)
{
const char *conv_array[] = {
[bs_unknown] = "Unknown",
[bs_good] = "Good",
[bs_changed] = "Changed",
[bs_bad] = "Bad",
[bs_overwritten] = "Overwritten",
[bs_overwritten_changed] = "Overwritten and changed",
};
return conv_array[state];
}
@ -318,10 +261,8 @@ static int is_block(uint64_t offset, int block_order)
static void print_offset(uint64_t offset, int block_order)
{
if (is_block(offset, block_order))
printf("block 0x%" PRIx64, offset >> block_order);
else
printf("offset 0x%" PRIx64, offset);
assert(is_block(offset, block_order));
printf("block 0x%" PRIx64, offset >> block_order);
}
static void print_block_range(const struct block_range *range)
@ -333,12 +274,10 @@ static void print_block_range(const struct block_range *range)
switch (range->state) {
case bs_good:
case bs_changed:
case bs_bad:
break;
case bs_overwritten:
case bs_overwritten_changed:
printf(", found ");
print_offset(range->found_sector_offset, range->block_order);
break;
@ -351,46 +290,40 @@ static void print_block_range(const struct block_range *range)
}
static void validate_block(uint64_t expected_sector_offset,
const char *probe_blk, int block_size, struct block_range *range)
const char *probe_blk, int block_order, struct block_range *range)
{
const char *sector = probe_blk;
const char *stop_sector = sector + block_size;
enum block_state state;
uint64_t found_sector_offset;
enum block_state state;
bool push_range;
assert(block_size % SECTOR_SIZE == 0);
if (validate_buffer_with_block(probe_blk, block_order,
&found_sector_offset, 0))
state = bs_bad; /* Bad block. */
else if (expected_sector_offset == found_sector_offset)
state = bs_good; /* Good block. */
else
state = bs_overwritten; /* Overwritten block. */
while (sector < stop_sector) {
bool push_range;
state = validate_sector(expected_sector_offset, sector,
&found_sector_offset);
assert(state != bs_unknown);
push_range = (range->state != state) || (
state == bs_overwritten
&& (
(expected_sector_offset
- range->start_sector_offset)
!=
(found_sector_offset
- range->found_sector_offset)
)
);
push_range = (range->state != state) || (
(state == bs_overwritten ||
state == bs_overwritten_changed)
&& (
(expected_sector_offset
- range->start_sector_offset)
!=
(found_sector_offset
- range->found_sector_offset)
)
);
if (push_range) {
if (range->state != bs_unknown)
print_block_range(range);
range->state = state;
range->start_sector_offset = expected_sector_offset;
range->end_sector_offset = expected_sector_offset;
range->found_sector_offset = found_sector_offset;
} else {
range->end_sector_offset = expected_sector_offset;
}
expected_sector_offset += SECTOR_SIZE;
sector += SECTOR_SIZE;
if (push_range) {
if (range->state != bs_unknown)
print_block_range(range);
range->state = state;
range->start_sector_offset = expected_sector_offset;
range->end_sector_offset = expected_sector_offset;
range->found_sector_offset = found_sector_offset;
} else {
range->end_sector_offset = expected_sector_offset;
}
}
@ -412,7 +345,7 @@ static void read_blocks(char *probe_blk, struct device *dev,
for (i = first_block; i <= last_block; i++) {
if (!dev_read_block(dev, probe_blk, i))
validate_block(expected_sector_offset, probe_blk,
block_size, &range);
block_order, &range);
else
warn("Failed reading block 0x%" PRIx64, i);
expected_sector_offset += block_size;
@ -423,22 +356,6 @@ static void read_blocks(char *probe_blk, struct device *dev,
assert(first_block > last_block);
}
/* XXX Properly handle return errors. */
static void test_write_blocks(struct device *dev,
uint64_t first_block, uint64_t last_block)
{
const int block_order = dev_get_block_order(dev);
const int block_size = dev_get_block_size(dev);
char stack[align_head(block_order) + block_size];
char *blk = align_mem(stack, block_order);
printf("Writing blocks from 0x%" PRIx64 " to 0x%" PRIx64 "...",
first_block, last_block);
fflush(stdout);
write_blocks(blk, dev, first_block, last_block);
printf(" Done\n\n");
}
/* XXX Properly handle return errors. */
static void test_read_blocks(struct device *dev,
uint64_t first_block, uint64_t last_block)

View File

@ -4,9 +4,7 @@
#include <parted/parted.h>
#include "version.h"
/* XXX Refactor utils library since f3probe barely uses it. */
#include "utils.h"
#include "libutils.h"
/* Argp's global variables. */
const char *argp_program_version = "F3 Fix " F3_STR_VERSION;

View File

@ -13,9 +13,6 @@
#include "libprobe.h"
#include "libutils.h"
/* XXX Refactor utils library since f3probe barely uses it. */
#include "utils.h"
/* Argp's global variables. */
const char *argp_program_version = "F3 Probe " F3_STR_VERSION;

View File

@ -1,6 +1,10 @@
#include <stdio.h> /* For fprintf(). */
#include <stdlib.h> /* For strtoll(). */
#include <stdbool.h>
#include <assert.h>
#include "libutils.h"
#include "version.h"
/* Count the number of 1 bits. */
static int pop(uint64_t x)
@ -42,6 +46,20 @@ int ceiling_log2(uint64_t x)
return ilog2(clp2(x));
}
const char *adjust_unit(double *ptr_bytes)
{
const char *units[] = { "Byte", "KB", "MB", "GB", "TB", "PB", "EB" };
int i = 0;
double final = *ptr_bytes;
while (i < 7 && final >= 1024) {
final /= 1024;
i++;
}
*ptr_bytes = final;
return units[i];
}
void *align_mem(void *p, int order)
{
uintptr_t ip = (uintptr_t)p;
@ -49,6 +67,15 @@ void *align_mem(void *p, int order)
return (void *)( (ip + head) & ~head );
}
void print_header(FILE *f, const char *name)
{
fprintf(f,
"F3 %s " F3_STR_VERSION "\n"
"Copyright (C) 2010 Digirati Internet LTDA.\n"
"This is free software; see the source for copying conditions.\n"
"\n", name);
}
long long arg_to_ll_bytes(const struct argp_state *state,
const char *arg)
{
@ -94,3 +121,49 @@ long long arg_to_ll_bytes(const struct argp_state *state,
argp_error(state, "`%s' is not an integer", arg);
return ll;
}
static inline uint64_t next_random_number(uint64_t random_number)
{
return random_number * 4294967311ULL + 17;
}
void fill_buffer_with_block(void *buf, int block_order, uint64_t offset,
uint64_t salt)
{
uint64_t *int64_array = buf;
int i, num_int64 = 1 << (block_order - 3);
uint64_t random_number = offset ^ salt;
assert(block_order >= 9);
/* The offset is known by drives,
* so one doesn't have to encrypt it.
* Plese don't add @salt here!
*/
int64_array[0] = offset;
/* Thanks to @salt, a drive has to guess the seeed. */
for (i = 1; i < num_int64; i++)
int64_array[i] = random_number =
next_random_number(random_number);
}
int validate_buffer_with_block(const void *buf, int block_order,
uint64_t *pfound_offset, uint64_t salt)
{
const uint64_t *int64_array = buf;
int i, num_int64 = 1 << (block_order - 3);
uint64_t found_offset = int64_array[0];
uint64_t random_number = found_offset ^ salt;
assert(block_order >= 9);
for (i = 1; i < num_int64; i++) {
random_number = next_random_number(random_number);
if (int64_array[i] != random_number)
return true;
}
*pfound_offset = found_offset;
return false;
}

View File

@ -7,6 +7,8 @@
int ilog2(uint64_t x);
int ceiling_log2(uint64_t x);
const char *adjust_unit(double *ptr_bytes);
static inline int align_head(int order)
{
return (1 << order) - 1;
@ -14,6 +16,16 @@ static inline int align_head(int order)
void *align_mem(void *p, int order);
void print_header(FILE *f, const char *name);
long long arg_to_ll_bytes(const struct argp_state *state, const char *arg);
/* Dependent on the byte order of the processor (i.e. endianness). */
void fill_buffer_with_block(void *buf, int block_order, uint64_t offset,
uint64_t salt);
/* Dependent on the byte order of the processor (i.e. endianness). */
int validate_buffer_with_block(const void *buf, int block_order,
uint64_t *pfound_offset, uint64_t salt);
#endif /* HEADER_LIBUTILS_H */