diff --git a/libprobe.c b/libprobe.c index 4741be1..8af3b71 100644 --- a/libprobe.c +++ b/libprobe.c @@ -17,15 +17,16 @@ #include "libprobe.h" -static const char const *ftype_to_name[] = { +static const char const *ftype_to_name[FKTY_MAX] = { [FKTY_GOOD] = "good", + [FKTY_BAD] = "bad", [FKTY_LIMBO] = "limbo", [FKTY_WRAPAROUND] = "wraparound", - [FKTY_MAX] = "max", }; const char *fake_type_to_name(enum fake_type fake_type) { + assert(fake_type < FKTY_MAX); return ftype_to_name[fake_type]; } @@ -67,9 +68,6 @@ static int fdev_read_block(struct device *dev, char *buf, uint64_t block) switch (fdev->fake_type) { case FKTY_LIMBO: if (offset >= GIGABYTE * fdev->file_size_gb) { - /* XXX Support different types of LIMBO. - * For example: all zeros, all ones, and random. - */ memset(buf, 0, BLOCK_SIZE); return 0; } @@ -160,7 +158,6 @@ static void fdev_free(struct device *dev) assert(!close(fdev->fd)); assert(!unlink(fdev->filename)); free((void *)fdev->filename); - fdev->filename = NULL; } /* XXX Validate parameters. @@ -197,7 +194,6 @@ struct device *create_file_device(const char *filename, filename: free((void *)fdev->filename); - fdev->filename = NULL; fdev: free(fdev); error: @@ -313,26 +309,115 @@ static inline int dev_get_size_gb(struct device *dev) return dev->get_size_gb(dev); } +/* 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) +{ + memset(buf, signature, len); +} + +static inline int equal_blk(const char *b1, const char *b2) +{ + return !memcmp(b1, b2, BLOCK_SIZE); +} + +static inline void *align_512(void *p) +{ + uintptr_t ip = (uintptr_t)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". */ -/* 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. - */ /* 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) { - char buf[BLOCK_SIZE]; + 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; - /* TODO */ - dev_read_block(dev, buf, 10); - dev_write_block(dev, buf, 10); + assert(device_size_gb > 0); - *preal_size_gb = dev_get_size_gb(dev); + /* 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; + 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; + } + } + + /* Inductive step. */ + fill_buffer(stamp_blk, BLOCK_SIZE, 2); + for (i = 1; i < device_size_gb; i++) { + dev_write_block(dev, stamp_blk, pos); + + 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; + } + + /* 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; + } + } + + 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; + +bad: + *preal_size_gb = 0; + return FKTY_BAD; } diff --git a/libprobe.h b/libprobe.h index 760a7ac..f4c403e 100644 --- a/libprobe.h +++ b/libprobe.h @@ -2,9 +2,10 @@ #define HEADER_LIBPROBE_H enum fake_type { - FKTY_GOOD, - FKTY_LIMBO, - FKTY_WRAPAROUND, + 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. */ FKTY_MAX, };