mirror of
https://github.com/AltraMayor/f3.git
synced 2025-08-03 10:35:57 -04:00
f3probe: improve probe_device()
- It works with drives of any size. - It precisely identifies the real size of drives. - It identify the geometry of the drive.
This commit is contained in:
parent
ceb79aae4e
commit
d940ecd19a
46
f3probe.c
46
f3probe.c
@ -4,6 +4,7 @@
|
||||
#include <argp.h>
|
||||
#include <stdbool.h>
|
||||
#include <assert.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "version.h"
|
||||
#include "libprobe.h"
|
||||
@ -152,14 +153,17 @@ static int unit_test(const char *filename)
|
||||
int i, success = 0;
|
||||
for (i = 0; i < UNIT_TEST_N_CASES; i++) {
|
||||
enum fake_type fake_type;
|
||||
int real_size_gb, good = 0;
|
||||
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);
|
||||
assert(dev);
|
||||
fake_type = probe_device(dev, &real_size_gb);
|
||||
probe_device(dev, &real_size_byte, &announced_size_byte, &wrap);
|
||||
free_device(dev);
|
||||
fake_type = dev_param_to_type(real_size_byte,
|
||||
announced_size_byte, wrap);
|
||||
|
||||
/* Report */
|
||||
printf("Test %i (type %s, file-size=%iGB, fake-size=%iGB): ",
|
||||
@ -167,21 +171,26 @@ static int unit_test(const char *filename)
|
||||
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_gb == ftype_to_params[i].file_size_gb) {
|
||||
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_gb == ftype_to_params[i].file_size_gb) {
|
||||
} 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 and real size %iGB\n",
|
||||
fake_type_to_name(fake_type), real_size_gb);
|
||||
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");
|
||||
}
|
||||
|
||||
@ -198,26 +207,31 @@ static int test_device(struct args *args)
|
||||
{
|
||||
struct device *dev;
|
||||
enum fake_type fake_type;
|
||||
int real_size_gb;
|
||||
uint64_t real_size_byte, announced_size_byte;
|
||||
int wrap;
|
||||
|
||||
dev = args->debug
|
||||
? create_file_device(args->filename, args->file_size_gb,
|
||||
args->fake_size_gb, args->fake_type)
|
||||
: create_block_device(args->filename);
|
||||
assert(dev);
|
||||
probe_device(dev, &real_size_byte, &announced_size_byte, &wrap);
|
||||
free_device(dev);
|
||||
|
||||
fake_type = probe_device(dev, &real_size_gb);
|
||||
fake_type = dev_param_to_type(real_size_byte, announced_size_byte,
|
||||
wrap);
|
||||
switch (fake_type) {
|
||||
case FKTY_GOOD:
|
||||
printf("Nice! The device `%s' is the real thing, and its size is %iGB\n",
|
||||
args->filename, real_size_gb);
|
||||
printf("Good news: The device `%s' is the real thing\n",
|
||||
args->filename);
|
||||
break;
|
||||
|
||||
case FKTY_BAD:
|
||||
case FKTY_LIMBO:
|
||||
case FKTY_WRAPAROUND:
|
||||
printf("Bad news: The device `%s' is a counterfeit of type %s, and its *real* size is %iGB\n",
|
||||
args->filename, fake_type_to_name(fake_type),
|
||||
real_size_gb);
|
||||
case FKTY_CHAIN:
|
||||
printf("Bad news: The device `%s' is a counterfeit of type %s\n",
|
||||
args->filename, fake_type_to_name(fake_type));
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -225,7 +239,11 @@ static int test_device(struct args *args)
|
||||
break;
|
||||
}
|
||||
|
||||
free_device(dev);
|
||||
/* XXX Add user friendly values, and sectors. */
|
||||
printf("\nDevice geometry:\n");
|
||||
printf("\t *Real* size: %" PRIu64 " Bytes\n", real_size_byte);
|
||||
printf("\tAnnounced size: %" PRIu64 " Bytes\n", announced_size_byte);
|
||||
printf("\t Wrap: 2^%i\n\n", wrap);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
349
libprobe.c
349
libprobe.c
@ -2,6 +2,7 @@
|
||||
#define _POSIX_C_SOURCE 200809L
|
||||
#define _FILE_OFFSET_BITS 64
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
@ -25,6 +26,7 @@ static const char const *ftype_to_name[FKTY_MAX] = {
|
||||
[FKTY_BAD] = "bad",
|
||||
[FKTY_LIMBO] = "limbo",
|
||||
[FKTY_WRAPAROUND] = "wraparound",
|
||||
[FKTY_CHAIN] = "chain",
|
||||
};
|
||||
|
||||
const char *fake_type_to_name(enum fake_type fake_type)
|
||||
@ -33,11 +35,50 @@ const char *fake_type_to_name(enum fake_type fake_type)
|
||||
return ftype_to_name[fake_type];
|
||||
}
|
||||
|
||||
int dev_param_valid(uint64_t real_size_byte,
|
||||
uint64_t announced_size_byte, int wrap)
|
||||
{
|
||||
/* Check general ranges. */
|
||||
if (real_size_byte > announced_size_byte || wrap < 0 || wrap >= 64)
|
||||
return false;
|
||||
|
||||
/* If good, @wrap must make sense. */
|
||||
if (real_size_byte == announced_size_byte) {
|
||||
uint64_t two_wrap = ((uint64_t)1) << wrap;
|
||||
return announced_size_byte <= two_wrap;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
enum fake_type dev_param_to_type(uint64_t real_size_byte,
|
||||
uint64_t announced_size_byte, int wrap)
|
||||
{
|
||||
uint64_t two_wrap;
|
||||
|
||||
assert(dev_param_valid(real_size_byte, announced_size_byte, wrap));
|
||||
|
||||
if (real_size_byte == announced_size_byte)
|
||||
return FKTY_GOOD;
|
||||
|
||||
if (real_size_byte == 0)
|
||||
return FKTY_BAD;
|
||||
|
||||
/* real_size_byte < announced_size_byte */
|
||||
|
||||
two_wrap = ((uint64_t)1) << wrap;
|
||||
if (two_wrap <= real_size_byte)
|
||||
return FKTY_WRAPAROUND;
|
||||
if (two_wrap < announced_size_byte)
|
||||
return FKTY_CHAIN;
|
||||
return FKTY_LIMBO;
|
||||
}
|
||||
|
||||
struct device {
|
||||
int (*read_block)(struct device *dev, char *buf, uint64_t block);
|
||||
int (*write_block)(struct device *dev, char *buf, uint64_t block);
|
||||
int (*write_block)(struct device *dev, const char *buf, uint64_t block);
|
||||
int (*reset)(struct device *dev);
|
||||
int (*get_size_gb)(struct device *dev);
|
||||
uint64_t (*get_size_byte)(struct device *dev);
|
||||
void (*free)(struct device *dev);
|
||||
};
|
||||
|
||||
@ -45,6 +86,9 @@ 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;
|
||||
@ -110,7 +154,7 @@ static int fdev_read_block(struct device *dev, char *buf, uint64_t block)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int write_all(int fd, char *buf, int count)
|
||||
static int write_all(int fd, const char *buf, int count)
|
||||
{
|
||||
int done = 0;
|
||||
do {
|
||||
@ -121,7 +165,7 @@ static int write_all(int fd, char *buf, int count)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fdev_write_block(struct device *dev, 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);
|
||||
off_t offset = block * BLOCK_SIZE;
|
||||
@ -150,16 +194,15 @@ static int fdev_write_block(struct device *dev, char *buf, uint64_t block)
|
||||
return write_all(fdev->fd, buf, BLOCK_SIZE);
|
||||
}
|
||||
|
||||
static int fdev_get_size_gb(struct device *dev)
|
||||
static uint64_t fdev_get_size_byte(struct device *dev)
|
||||
{
|
||||
return dev_fdev(dev)->fake_size_gb;
|
||||
return (uint64_t)dev_fdev(dev)->fake_size_gb * GIGABYTE;
|
||||
}
|
||||
|
||||
static void fdev_free(struct device *dev)
|
||||
{
|
||||
struct file_device *fdev = dev_fdev(dev);
|
||||
assert(!close(fdev->fd));
|
||||
assert(!unlink(fdev->filename));
|
||||
free((void *)fdev->filename);
|
||||
}
|
||||
|
||||
@ -183,6 +226,10 @@ struct device *create_file_device(const char *filename,
|
||||
err(errno, "Can't create file `%s'", filename);
|
||||
goto filename;
|
||||
}
|
||||
/* 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;
|
||||
@ -191,7 +238,7 @@ struct device *create_file_device(const char *filename,
|
||||
fdev->dev.read_block = fdev_read_block;
|
||||
fdev->dev.write_block = fdev_write_block;
|
||||
fdev->dev.reset = NULL;
|
||||
fdev->dev.get_size_gb = fdev_get_size_gb;
|
||||
fdev->dev.get_size_byte = fdev_get_size_byte;
|
||||
fdev->dev.free = fdev_free;
|
||||
|
||||
return &fdev->dev;
|
||||
@ -238,7 +285,7 @@ static int bdev_read_block(struct device *dev, char *buf, uint64_t block)
|
||||
return read_all(bdev->fd, buf, BLOCK_SIZE);
|
||||
}
|
||||
|
||||
static int bdev_write_block(struct device *dev, char *buf, uint64_t block)
|
||||
static int bdev_write_block(struct device *dev, const char *buf, uint64_t block)
|
||||
{
|
||||
struct block_device *bdev = dev_bdev(dev);
|
||||
off_t offset = block * BLOCK_SIZE;
|
||||
@ -262,12 +309,11 @@ static int bdev_reset(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bdev_get_size_gb(struct device *dev)
|
||||
static uint64_t bdev_get_size_byte(struct device *dev)
|
||||
{
|
||||
uint64_t size_bytes;
|
||||
assert(!ioctl(dev_bdev(dev)->fd, BLKGETSIZE64, &size_bytes));
|
||||
/* XXX Support everything. Specially devices smaller than 1GB! */
|
||||
return size_bytes >> 30;
|
||||
uint64_t size_byte;
|
||||
assert(!ioctl(dev_bdev(dev)->fd, BLKGETSIZE64, &size_byte));
|
||||
return size_byte;
|
||||
}
|
||||
|
||||
static void bdev_free(struct device *dev)
|
||||
@ -405,7 +451,7 @@ struct device *create_block_device(const char *filename)
|
||||
bdev->dev.read_block = bdev_read_block;
|
||||
bdev->dev.write_block = bdev_write_block;
|
||||
bdev->dev.reset = bdev_reset;
|
||||
bdev->dev.get_size_gb = bdev_get_size_gb;
|
||||
bdev->dev.get_size_byte = bdev_get_size_byte;
|
||||
bdev->dev.free = bdev_free;
|
||||
|
||||
free((void *)usb_filename);
|
||||
@ -434,32 +480,27 @@ static inline int dev_read_block(struct device *dev, char *buf, uint64_t block)
|
||||
return dev->read_block(dev, buf, block);
|
||||
}
|
||||
|
||||
static inline int dev_write_block(struct device *dev, char *buf, uint64_t block)
|
||||
static inline int dev_write_block(struct device *dev, const char *buf,
|
||||
uint64_t block)
|
||||
{
|
||||
int rc = dev->write_block(dev, buf, block);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* XXX probe_device() should be smart enough to know when dev->reset()
|
||||
* must be called.
|
||||
*/
|
||||
if (dev->reset)
|
||||
return dev->reset(dev);
|
||||
return 0;
|
||||
return dev->write_block(dev, buf, block);
|
||||
}
|
||||
|
||||
static inline int dev_get_size_gb(struct device *dev)
|
||||
static inline int dev_reset(struct device *dev)
|
||||
{
|
||||
return dev->get_size_gb(dev);
|
||||
return dev->reset ? dev->reset(dev) : 0;
|
||||
}
|
||||
|
||||
/* XXX Write random data for testing.
|
||||
* There would be a random seed, and all the other blocks would be
|
||||
* this seed XOR'd with the number of the test.
|
||||
*/
|
||||
static void fill_buffer(char *buf, int len, int signature)
|
||||
static inline int dev_write_and_reset(struct device *dev, const char *buf,
|
||||
uint64_t block)
|
||||
{
|
||||
memset(buf, signature, len);
|
||||
int rc = dev_write_block(dev, buf, block);
|
||||
return rc ? rc : dev_reset(dev);
|
||||
}
|
||||
|
||||
static inline uint64_t dev_get_size_byte(struct device *dev)
|
||||
{
|
||||
return dev->get_size_byte(dev);
|
||||
}
|
||||
|
||||
static inline int equal_blk(const char *b1, const char *b2)
|
||||
@ -473,95 +514,183 @@ static inline void *align_512(void *p)
|
||||
return (void *)( (ip + 511) & ~511 );
|
||||
}
|
||||
|
||||
/* XXX Don't write at the very beginning of the card to avoid
|
||||
* losing the partition table.
|
||||
* But write at a random locations to make harder for fake chips
|
||||
* to become "smarter".
|
||||
/* Minimum size of the memory chunk used to build flash drives.
|
||||
* It must be a power of two.
|
||||
*/
|
||||
/* XXX Finish testing the last block, and the next one that should fail.
|
||||
* Then report the last block, so user can create the largest partition.
|
||||
*/
|
||||
/* XXX Properly handle read and write errors. */
|
||||
enum fake_type probe_device(struct device *dev, int *preal_size_gb)
|
||||
{
|
||||
int device_size_gb = dev_get_size_gb(dev);
|
||||
char stack[511 + 3 * BLOCK_SIZE];
|
||||
char *first_blk, *stamp_blk, *probe_blk;
|
||||
const int step = GIGABYTE / BLOCK_SIZE;
|
||||
uint64_t first_pos = 10;
|
||||
uint64_t pos = first_pos + step;
|
||||
int i;
|
||||
#define INITAIL_HIGH_BIT (1 << 20)
|
||||
|
||||
assert(device_size_gb > 0);
|
||||
/* Caller must guarantee that the left bock is good, and written. */
|
||||
static int search_wrap(struct device *dev,
|
||||
uint64_t left_pos, uint64_t *pright_pos,
|
||||
const char *stamp_blk, char *probe_blk)
|
||||
{
|
||||
uint64_t high_bit = INITAIL_HIGH_BIT;
|
||||
uint64_t pos = high_bit + left_pos;
|
||||
|
||||
/* The left block must be in the first memory chunk. */
|
||||
assert(left_pos < high_bit);
|
||||
|
||||
/* Check that the drive has at least one memory chunk. */
|
||||
assert((high_bit - 1) <= *pright_pos);
|
||||
|
||||
while (pos < *pright_pos) {
|
||||
if (dev_read_block(dev, probe_blk, pos) &&
|
||||
dev_read_block(dev, probe_blk, pos))
|
||||
return true;
|
||||
/* XXX Deal with flipped bit on reception. */
|
||||
if (equal_blk(stamp_blk, probe_blk)) {
|
||||
/* XXX Test wraparound hypothesis. */
|
||||
*pright_pos = high_bit - 1;
|
||||
return false;
|
||||
}
|
||||
high_bit <<= 1;
|
||||
pos = high_bit + left_pos;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* XXX Should test if block if fully damaged, if so, avoid retrying. */
|
||||
/* Return true if the block @pos is damaged. */
|
||||
static int test_block(struct device *dev,
|
||||
const char *stamp_blk, char *probe_blk, uint64_t pos)
|
||||
{
|
||||
/* Write block. */
|
||||
if (dev_write_block(dev, stamp_blk, pos) &&
|
||||
dev_write_block(dev, stamp_blk, pos))
|
||||
return true;
|
||||
|
||||
/* Reset. */
|
||||
if (dev_reset(dev) && dev_reset(dev))
|
||||
return true;
|
||||
|
||||
/* Test block. */
|
||||
if (dev_read_block(dev, probe_blk, pos) &&
|
||||
dev_read_block(dev, probe_blk, pos))
|
||||
return true;
|
||||
if (!equal_blk(stamp_blk, probe_blk)) {
|
||||
/* The probe block seems to be damaged.
|
||||
* Trying a second time...
|
||||
*/
|
||||
return dev_write_and_reset(dev, stamp_blk, pos) ||
|
||||
dev_read_block(dev, probe_blk, pos) ||
|
||||
!equal_blk(stamp_blk, probe_blk);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Caller must guarantee that the left bock is good, and written. */
|
||||
static int search_edge(struct device *dev,
|
||||
uint64_t *pleft_pos, uint64_t right_pos,
|
||||
const char *stamp_blk, char *probe_blk)
|
||||
{
|
||||
uint64_t pos = right_pos;
|
||||
do {
|
||||
if (test_block(dev, stamp_blk, probe_blk, pos))
|
||||
right_pos = pos;
|
||||
else
|
||||
*pleft_pos = pos;
|
||||
pos = (*pleft_pos + right_pos) / 2;
|
||||
} while (right_pos - *pleft_pos >= 2);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* XXX Write random data to make it harder for fake chips to become "smarter".
|
||||
* There would be a random seed.
|
||||
* Buffer cannot be all 0x00 or all 0xFF.
|
||||
*/
|
||||
static void fill_buffer(char *buf, int len)
|
||||
{
|
||||
memset(buf, 0xAA, len);
|
||||
}
|
||||
|
||||
/* Count the number of 1 bits. */
|
||||
static int pop(uint64_t x)
|
||||
{
|
||||
int n = 0;
|
||||
while (x) {
|
||||
n++;
|
||||
x = x & (x - 1);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
static int ilog2(uint64_t x)
|
||||
{
|
||||
x = x | (x >> 1);
|
||||
x = x | (x >> 2);
|
||||
x = x | (x >> 4);
|
||||
x = x | (x >> 8);
|
||||
x = x | (x >> 16);
|
||||
x = x | (x >> 32);
|
||||
return pop(x) - 1;
|
||||
}
|
||||
|
||||
/* Least power of 2 greater than or equal to x. */
|
||||
static uint64_t clp2(uint64_t x)
|
||||
{
|
||||
x = x - 1;
|
||||
x = x | (x >> 1);
|
||||
x = x | (x >> 2);
|
||||
x = x | (x >> 4);
|
||||
x = x | (x >> 8);
|
||||
x = x | (x >> 16);
|
||||
x = x | (x >> 32);
|
||||
return x + 1;
|
||||
}
|
||||
|
||||
static int ceiling_log2(uint64_t x)
|
||||
{
|
||||
return ilog2(clp2(x));
|
||||
}
|
||||
|
||||
/* XXX Properly handle read and write errors. */
|
||||
void probe_device(struct device *dev, uint64_t *preal_size_byte,
|
||||
uint64_t *pannounced_size_byte, int *pwrap)
|
||||
{
|
||||
uint64_t dev_size_byte = dev_get_size_byte(dev);
|
||||
int dev_size_block = dev_size_byte >> BLOCK_SIZE_BITS;
|
||||
char stack[511 + 2 * BLOCK_SIZE];
|
||||
char *stamp_blk, *probe_blk;
|
||||
/* XXX Don't write at the very beginning of the card to avoid
|
||||
* losing the partition table.
|
||||
* But write at a random locations to make harder for fake chips
|
||||
* to become "smarter".
|
||||
* And try a couple of blocks if they keep failing.
|
||||
*/
|
||||
uint64_t left_pos = 10, right_pos = dev_size_block - 1;
|
||||
|
||||
assert(dev_size_byte % BLOCK_SIZE == 0);
|
||||
assert(left_pos < right_pos);
|
||||
|
||||
/* Aligning these pointers is necessary to directly read and write
|
||||
* the block device.
|
||||
* For the file device, this is superfluous.
|
||||
*/
|
||||
first_blk = align_512(stack);
|
||||
stamp_blk = first_blk + BLOCK_SIZE;
|
||||
stamp_blk = align_512(stack);
|
||||
probe_blk = stamp_blk + BLOCK_SIZE;
|
||||
|
||||
/* Base case. */
|
||||
fill_buffer(first_blk, BLOCK_SIZE, 1);
|
||||
dev_write_block(dev, first_blk, first_pos);
|
||||
dev_read_block(dev, probe_blk, first_pos);
|
||||
if (!equal_blk(first_blk, probe_blk)) {
|
||||
/* There is a block before the first 1GB that seems to
|
||||
* be damaged. Trying a second time...
|
||||
*/
|
||||
dev_write_block(dev, first_blk, first_pos);
|
||||
dev_read_block(dev, probe_blk, first_pos);
|
||||
if (!equal_blk(first_blk, probe_blk)) {
|
||||
/* Okay, this device is damaged. */
|
||||
goto bad;
|
||||
}
|
||||
}
|
||||
fill_buffer(stamp_blk, BLOCK_SIZE);
|
||||
|
||||
/* Inductive step. */
|
||||
fill_buffer(stamp_blk, BLOCK_SIZE, 2);
|
||||
for (i = 1; i < device_size_gb; i++) {
|
||||
dev_write_block(dev, stamp_blk, pos);
|
||||
/* Make sure that there is at least a good block at the beginning
|
||||
* of the drive.
|
||||
*/
|
||||
if (test_block(dev, stamp_blk, probe_blk, left_pos))
|
||||
goto bad;
|
||||
|
||||
dev_read_block(dev, probe_blk, first_pos);
|
||||
if (!equal_blk(first_blk, probe_blk)) {
|
||||
/* Wrapping around? */
|
||||
if (equal_blk(stamp_blk, probe_blk)) {
|
||||
/* yes. */
|
||||
*preal_size_gb = i;
|
||||
return FKTY_WRAPAROUND;
|
||||
}
|
||||
if (search_wrap(dev, left_pos, &right_pos, stamp_blk, probe_blk))
|
||||
goto bad;
|
||||
|
||||
/* The block at @first_pos changed to a value
|
||||
* different from the one written.
|
||||
* Trying a second time...
|
||||
*/
|
||||
dev_write_block(dev, first_blk, first_pos);
|
||||
dev_write_block(dev, stamp_blk, pos);
|
||||
dev_read_block(dev, probe_blk, first_pos);
|
||||
if (!equal_blk(first_blk, probe_blk)) {
|
||||
if (equal_blk(stamp_blk, probe_blk)) {
|
||||
*preal_size_gb = i;
|
||||
return FKTY_WRAPAROUND;
|
||||
}
|
||||
/* Okay, this device is damaged. */
|
||||
goto bad;
|
||||
}
|
||||
}
|
||||
if (search_edge(dev, &left_pos, right_pos, stamp_blk, probe_blk))
|
||||
goto bad;
|
||||
|
||||
dev_read_block(dev, probe_blk, pos);
|
||||
if (!equal_blk(stamp_blk, probe_blk)) {
|
||||
*preal_size_gb = i;
|
||||
return FKTY_LIMBO;
|
||||
}
|
||||
|
||||
pos += step;
|
||||
}
|
||||
|
||||
*preal_size_gb = device_size_gb;
|
||||
return FKTY_GOOD;
|
||||
*preal_size_byte = (left_pos + 1) << BLOCK_SIZE_BITS;
|
||||
*pannounced_size_byte = dev_size_byte;
|
||||
*pwrap = ceiling_log2(right_pos * BLOCK_SIZE);
|
||||
return;
|
||||
|
||||
bad:
|
||||
*preal_size_gb = 0;
|
||||
return FKTY_BAD;
|
||||
*preal_size_byte = 0;
|
||||
*pannounced_size_byte = dev_size_byte;
|
||||
*pwrap = ceiling_log2(dev_size_byte);
|
||||
}
|
||||
|
30
libprobe.h
30
libprobe.h
@ -1,16 +1,35 @@
|
||||
#ifndef HEADER_LIBPROBE_H
|
||||
#define HEADER_LIBPROBE_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
enum fake_type {
|
||||
FKTY_GOOD, /* Device is good. */
|
||||
FKTY_BAD, /* Device is at least partially damaged. */
|
||||
FKTY_LIMBO, /* Device discards data after a given limit. */
|
||||
FKTY_WRAPAROUND,/* Device overwrites data after a given limit. */
|
||||
/* Device is good. */
|
||||
FKTY_GOOD,
|
||||
|
||||
/* Device is at least partially damaged. */
|
||||
FKTY_BAD,
|
||||
|
||||
/* Device discards data after a given limit. */
|
||||
FKTY_LIMBO,
|
||||
|
||||
/* Device overwrites data after a given limit. */
|
||||
FKTY_WRAPAROUND,
|
||||
|
||||
/* Device is a sequence of wraparound and limbo regions. */
|
||||
FKTY_CHAIN,
|
||||
|
||||
FKTY_MAX,
|
||||
};
|
||||
|
||||
const char *fake_type_to_name(enum fake_type fake_type);
|
||||
|
||||
int dev_param_valid(uint64_t real_size_byte,
|
||||
uint64_t announced_size_byte, int wrap);
|
||||
|
||||
enum fake_type dev_param_to_type(uint64_t real_size_byte,
|
||||
uint64_t announced_size_byte, int wrap);
|
||||
|
||||
struct device;
|
||||
|
||||
struct device *create_file_device(const char *filename,
|
||||
@ -20,6 +39,7 @@ struct device *create_block_device(const char *filename);
|
||||
|
||||
void free_device(struct device *dev);
|
||||
|
||||
enum fake_type probe_device(struct device *dev, int *real_size_gb);
|
||||
void probe_device(struct device *dev, uint64_t *preal_size_byte,
|
||||
uint64_t *pannounced_size_byte, int *pwrap);
|
||||
|
||||
#endif /* HEADER_LIBPROBE_H */
|
||||
|
Loading…
x
Reference in New Issue
Block a user