Simplified config

Before, the lfs had multiple paths to determine config options:
- lfs_config struct passed during initialization
- lfs_bd_info struct passed during block device initialization
- compile time options

This allowed different developers to provide their own needs
to the filesystem, such as the block device capabilities and
the higher level user's own tweaks.

However, this comes with additional complexity and action required
when the configurations are incompatible.

For now, this has been reduced to all information (including block
device function pointers) being passed through the lfs_config struct.
We just defer more complicated handling of configuration options to
the top level user.

This simplifies configuration handling and gives the top level user
the responsibility to handle configuration, which they probably would
have wanted to do anyways.
This commit is contained in:
Christopher Haster
2017-04-22 11:42:05 -05:00
parent 3b9d6630c8
commit 789286a257
13 changed files with 254 additions and 416 deletions

View File

@@ -19,9 +19,12 @@
// Block device emulated on existing filesystem // Block device emulated on existing filesystem
int lfs_emubd_create(lfs_emubd_t *emu, const char *path) { int lfs_emubd_create(const struct lfs_config *cfg, const char *path) {
memset(&emu->info, 0, sizeof(emu->info)); lfs_emubd_t *emu = cfg->context;
memset(&emu->stats, 0, sizeof(emu->stats)); emu->cfg.read_size = cfg->read_size;
emu->cfg.prog_size = cfg->prog_size;
emu->cfg.block_size = cfg->block_size;
emu->cfg.block_count = cfg->block_count;
// Allocate buffer for creating children files // Allocate buffer for creating children files
size_t pathlen = strlen(path); size_t pathlen = strlen(path);
@@ -41,12 +44,6 @@ int lfs_emubd_create(lfs_emubd_t *emu, const char *path) {
return -errno; return -errno;
} }
// Setup info based on configuration
emu->info.read_size = LFS_EMUBD_READ_SIZE;
emu->info.prog_size = LFS_EMUBD_PROG_SIZE;
emu->info.erase_size = LFS_EMUBD_ERASE_SIZE;
emu->info.total_size = LFS_EMUBD_TOTAL_SIZE;
// Load stats to continue incrementing // Load stats to continue incrementing
snprintf(emu->child, LFS_NAME_MAX, "stats"); snprintf(emu->child, LFS_NAME_MAX, "stats");
FILE *f = fopen(emu->path, "r"); FILE *f = fopen(emu->path, "r");
@@ -67,92 +64,42 @@ int lfs_emubd_create(lfs_emubd_t *emu, const char *path) {
return 0; return 0;
} }
void lfs_emubd_destroy(lfs_emubd_t *emu) { void lfs_emubd_destroy(const struct lfs_config *cfg) {
lfs_emubd_sync(emu); lfs_emubd_sync(cfg);
lfs_emubd_t *emu = cfg->context;
free(emu->path); free(emu->path);
} }
int lfs_emubd_read(lfs_emubd_t *emu, lfs_block_t block, int lfs_emubd_read(const struct lfs_config *cfg, lfs_block_t block,
lfs_off_t off, lfs_size_t size, void *buffer) { lfs_off_t off, lfs_size_t size, void *buffer) {
lfs_emubd_t *emu = cfg->context;
uint8_t *data = buffer; uint8_t *data = buffer;
// Check if read is valid // Check if read is valid
assert(off % emu->info.read_size == 0); assert(off % cfg->read_size == 0);
assert(size % emu->info.read_size == 0); assert(size % cfg->read_size == 0);
assert((uint64_t)block*emu->info.erase_size + off + size assert(block < cfg->block_count);
< emu->info.total_size);
// Zero out buffer for debugging // Zero out buffer for debugging
memset(data, 0, size); memset(data, 0, size);
// Iterate over blocks until enough data is read // Read data
while (size > 0) { snprintf(emu->child, LFS_NAME_MAX, "%x", block);
snprintf(emu->child, LFS_NAME_MAX, "%x", block);
size_t count = lfs_min(emu->info.erase_size - off, size);
FILE *f = fopen(emu->path, "rb"); FILE *f = fopen(emu->path, "rb");
if (!f && errno != ENOENT) { if (!f && errno != ENOENT) {
return -errno; return -errno;
}
if (f) {
int err = fseek(f, off, SEEK_SET);
if (err) {
return -errno;
}
size_t res = fread(data, 1, count, f);
if (res < count && !feof(f)) {
return -errno;
}
err = fclose(f);
if (err) {
return -errno;
}
}
size -= count;
data += count;
block += 1;
off = 0;
} }
emu->stats.read_count += 1; if (f) {
return 0;
}
int lfs_emubd_prog(lfs_emubd_t *emu, lfs_block_t block,
lfs_off_t off, lfs_size_t size, const void *buffer) {
const uint8_t *data = buffer;
// Check if write is valid
assert(off % emu->info.prog_size == 0);
assert(size % emu->info.prog_size == 0);
assert((uint64_t)block*emu->info.erase_size + off + size
< emu->info.total_size);
// Iterate over blocks until enough data is read
while (size > 0) {
snprintf(emu->child, LFS_NAME_MAX, "%x", block);
size_t count = lfs_min(emu->info.erase_size - off, size);
FILE *f = fopen(emu->path, "r+b");
if (!f && errno == ENOENT) {
f = fopen(emu->path, "w+b");
if (!f) {
return -errno;
}
}
int err = fseek(f, off, SEEK_SET); int err = fseek(f, off, SEEK_SET);
if (err) { if (err) {
return -errno; return -errno;
} }
size_t res = fwrite(data, 1, count, f); size_t res = fread(data, 1, size, f);
if (res < count) { if (res < size && !feof(f)) {
return -errno; return -errno;
} }
@@ -160,60 +107,88 @@ int lfs_emubd_prog(lfs_emubd_t *emu, lfs_block_t block,
if (err) { if (err) {
return -errno; return -errno;
} }
}
size -= count; emu->stats.read_count += 1;
data += count; return 0;
block += 1; }
off = 0;
int lfs_emubd_prog(const struct lfs_config *cfg, lfs_block_t block,
lfs_off_t off, lfs_size_t size, const void *buffer) {
lfs_emubd_t *emu = cfg->context;
const uint8_t *data = buffer;
// Check if write is valid
assert(off % cfg->prog_size == 0);
assert(size % cfg->prog_size == 0);
assert(block < cfg->block_count);
// Program data
snprintf(emu->child, LFS_NAME_MAX, "%x", block);
FILE *f = fopen(emu->path, "r+b");
if (!f && errno == ENOENT) {
f = fopen(emu->path, "w+b");
if (!f) {
return -errno;
}
}
int err = fseek(f, off, SEEK_SET);
if (err) {
return -errno;
}
size_t res = fwrite(data, 1, size, f);
if (res < size) {
return -errno;
}
err = fclose(f);
if (err) {
return -errno;
} }
emu->stats.prog_count += 1; emu->stats.prog_count += 1;
return 0; return 0;
} }
int lfs_emubd_erase(lfs_emubd_t *emu, lfs_block_t block, int lfs_emubd_erase(const struct lfs_config *cfg, lfs_block_t block) {
lfs_off_t off, lfs_size_t size) { lfs_emubd_t *emu = cfg->context;
// Check if erase is valid // Check if erase is valid
assert(off % emu->info.erase_size == 0); assert(block < cfg->block_count);
assert(size % emu->info.erase_size == 0);
assert((uint64_t)block*emu->info.erase_size + off + size
< emu->info.total_size);
// Iterate and erase blocks // Erase the block
while (size > 0) { snprintf(emu->child, LFS_NAME_MAX, "%x", block);
snprintf(emu->child, LFS_NAME_MAX, "%x", block); struct stat st;
struct stat st; int err = stat(emu->path, &st);
int err = stat(emu->path, &st); if (err && errno != ENOENT) {
if (err && errno != ENOENT) { return -errno;
}
if (!err && S_ISREG(st.st_mode)) {
int err = unlink(emu->path);
if (err) {
return -errno; return -errno;
} }
if (!err && S_ISREG(st.st_mode)) {
int err = unlink(emu->path);
if (err) {
return -errno;
}
}
size -= emu->info.erase_size;
block += 1;
off = 0;
} }
emu->stats.erase_count += 1; emu->stats.erase_count += 1;
return 0; return 0;
} }
int lfs_emubd_sync(lfs_emubd_t *emu) { int lfs_emubd_sync(const struct lfs_config *cfg) {
lfs_emubd_t *emu = cfg->context;
// Just write out info/stats for later lookup // Just write out info/stats for later lookup
snprintf(emu->child, LFS_NAME_MAX, "info"); snprintf(emu->child, LFS_NAME_MAX, "config");
FILE *f = fopen(emu->path, "w"); FILE *f = fopen(emu->path, "w");
if (!f) { if (!f) {
return -errno; return -errno;
} }
size_t res = fwrite(&emu->info, sizeof(emu->info), 1, f); size_t res = fwrite(&emu->cfg, sizeof(emu->cfg), 1, f);
if (res < 1) { if (res < 1) {
return -errno; return -errno;
} }
@@ -242,46 +217,3 @@ int lfs_emubd_sync(lfs_emubd_t *emu) {
return 0; return 0;
} }
int lfs_emubd_info(lfs_emubd_t *emu, struct lfs_bd_info *info) {
*info = emu->info;
return 0;
}
int lfs_emubd_stats(lfs_emubd_t *emu, struct lfs_bd_stats *stats) {
*stats = emu->stats;
return 0;
}
// Wrappers for void*s
static int lfs_emubd_bd_read(void *bd, lfs_block_t block,
lfs_off_t off, lfs_size_t size, void *buffer) {
return lfs_emubd_read((lfs_emubd_t*)bd, block, off, size, buffer);
}
static int lfs_emubd_bd_prog(void *bd, lfs_block_t block,
lfs_off_t off, lfs_size_t size, const void *buffer) {
return lfs_emubd_prog((lfs_emubd_t*)bd, block, off, size, buffer);
}
static int lfs_emubd_bd_erase(void *bd, lfs_block_t block,
lfs_off_t off, lfs_size_t size) {
return lfs_emubd_erase((lfs_emubd_t*)bd, block, off, size);
}
static int lfs_emubd_bd_sync(void *bd) {
return lfs_emubd_sync((lfs_emubd_t*)bd);
}
static int lfs_emubd_bd_info(void *bd, struct lfs_bd_info *info) {
return lfs_emubd_info((lfs_emubd_t*)bd, info);
}
const struct lfs_bd_ops lfs_emubd_ops = {
.read = lfs_emubd_bd_read,
.prog = lfs_emubd_bd_prog,
.erase = lfs_emubd_bd_erase,
.sync = lfs_emubd_bd_sync,
.info = lfs_emubd_bd_info,
};

View File

@@ -7,9 +7,8 @@
#ifndef LFS_EMUBD_H #ifndef LFS_EMUBD_H
#define LFS_EMUBD_H #define LFS_EMUBD_H
#include "lfs_config.h" #include "lfs.h"
#include "lfs_util.h" #include "lfs_util.h"
#include "lfs_bd.h"
// Config options // Config options
@@ -30,60 +29,50 @@
#endif #endif
// Stats for debugging and optimization
struct lfs_bd_stats {
uint64_t read_count;
uint64_t prog_count;
uint64_t erase_count;
};
// The emu bd state // The emu bd state
typedef struct lfs_emubd { typedef struct lfs_emubd {
char *path; char *path;
char *child; char *child;
struct lfs_bd_info info;
struct lfs_bd_stats stats; struct {
uint64_t read_count;
uint64_t prog_count;
uint64_t erase_count;
} stats;
struct {
uint32_t read_size;
uint32_t prog_size;
uint32_t block_size;
uint32_t block_count;
} cfg;
} lfs_emubd_t; } lfs_emubd_t;
// Create a block device using path for the directory to store blocks // Create a block device using path for the directory to store blocks
int lfs_emubd_create(lfs_emubd_t *emu, const char *path); int lfs_emubd_create(const struct lfs_config *cfg, const char *path);
// Clean up memory associated with emu block device // Clean up memory associated with emu block device
void lfs_emubd_destroy(lfs_emubd_t *emu); void lfs_emubd_destroy(const struct lfs_config *cfg);
// Read a block // Read a block
int lfs_emubd_read(lfs_emubd_t *bd, lfs_block_t block, int lfs_emubd_read(const struct lfs_config *cfg, lfs_block_t block,
lfs_off_t off, lfs_size_t size, void *buffer); lfs_off_t off, lfs_size_t size, void *buffer);
// Program a block // Program a block
// //
// The block must have previously been erased. // The block must have previously been erased.
int lfs_emubd_prog(lfs_emubd_t *bd, lfs_block_t block, int lfs_emubd_prog(const struct lfs_config *cfg, lfs_block_t block,
lfs_off_t off, lfs_size_t size, const void *buffer); lfs_off_t off, lfs_size_t size, const void *buffer);
// Erase a block // Erase a block
// //
// A block must be erased before being programmed. The // A block must be erased before being programmed. The
// state of an erased block is undefined. // state of an erased block is undefined.
int lfs_emubd_erase(lfs_emubd_t *bd, lfs_block_t block, int lfs_emubd_erase(const struct lfs_config *cfg, lfs_block_t block);
lfs_off_t off, lfs_size_t size);
// Sync the block device // Sync the block device
int lfs_emubd_sync(lfs_emubd_t *bd); int lfs_emubd_sync(const struct lfs_config *cfg);
// Get a description of the block device
//
// Any unknown information may be left unmodified
int lfs_emubd_info(lfs_emubd_t *bd, struct lfs_bd_info *info);
// Get stats of operations on the block device
//
// Used for debugging and optimizations
int lfs_emubd_stats(lfs_emubd_t *bd, struct lfs_bd_stats *stats);
// Block device operations
extern const struct lfs_bd_ops lfs_emubd_ops;
#endif #endif

141
lfs.c
View File

@@ -12,27 +12,22 @@
/// Block device operations /// /// Block device operations ///
static int lfs_bd_info(lfs_t *lfs, struct lfs_bd_info *info) {
return lfs->bd_ops->info(lfs->bd, info);
}
static int lfs_bd_read(lfs_t *lfs, lfs_block_t block, static int lfs_bd_read(lfs_t *lfs, lfs_block_t block,
lfs_off_t off, lfs_size_t size, void *buffer) { lfs_off_t off, lfs_size_t size, void *buffer) {
return lfs->bd_ops->read(lfs->bd, block, off, size, buffer); return lfs->cfg->read(lfs->cfg, block, off, size, buffer);
} }
static int lfs_bd_prog(lfs_t *lfs, lfs_block_t block, static int lfs_bd_prog(lfs_t *lfs, lfs_block_t block,
lfs_off_t off, lfs_size_t size, const void *buffer) { lfs_off_t off, lfs_size_t size, const void *buffer) {
return lfs->bd_ops->prog(lfs->bd, block, off, size, buffer); return lfs->cfg->prog(lfs->cfg, block, off, size, buffer);
} }
static int lfs_bd_erase(lfs_t *lfs, lfs_block_t block, static int lfs_bd_erase(lfs_t *lfs, lfs_block_t block) {
lfs_off_t off, lfs_size_t size) { return lfs->cfg->erase(lfs->cfg, block);
return lfs->bd_ops->erase(lfs->bd, block, off, size);
} }
static int lfs_bd_sync(lfs_t *lfs) { static int lfs_bd_sync(lfs_t *lfs) {
return lfs->bd_ops->sync(lfs->bd); return lfs->cfg->sync(lfs->cfg);
} }
static int lfs_bd_cmp(lfs_t *lfs, lfs_block_t block, static int lfs_bd_cmp(lfs_t *lfs, lfs_block_t block,
@@ -101,7 +96,7 @@ static int lfs_alloc_scan(lfs_t *lfs) {
// found free block, now find stride of free blocks // found free block, now find stride of free blocks
// since this is relatively cheap (stress on relatively) // since this is relatively cheap (stress on relatively)
lfs->free.begin += off; lfs->free.begin += off;
lfs->free.end = lfs->block_count; // before superblock lfs->free.end = lfs->cfg->block_count; // before superblock
// find maximum stride in tree // find maximum stride in tree
return lfs_traverse(lfs, lfs_alloc_stride, lfs); return lfs_traverse(lfs, lfs_alloc_stride, lfs);
@@ -157,7 +152,7 @@ static int lfs_alloc_erased(lfs_t *lfs, lfs_block_t *block) {
return err; return err;
} }
return lfs_bd_erase(lfs, *block, 0, lfs->block_size); return lfs_bd_erase(lfs, *block);
} }
@@ -175,9 +170,9 @@ static lfs_off_t lfs_indexnext(lfs_t *lfs, lfs_off_t ioff) {
static lfs_off_t lfs_indexfrom(lfs_t *lfs, lfs_off_t off) { static lfs_off_t lfs_indexfrom(lfs_t *lfs, lfs_off_t off) {
lfs_off_t i = 0; lfs_off_t i = 0;
while (off > lfs->block_size) { while (off > lfs->cfg->block_size) {
i = lfs_indexnext(lfs, i); i = lfs_indexnext(lfs, i);
off -= lfs->block_size; off -= lfs->cfg->block_size;
} }
return i; return i;
@@ -370,7 +365,7 @@ static int lfs_dir_fetch(lfs_t *lfs,
uint32_t crc = 0xffffffff; uint32_t crc = 0xffffffff;
crc = lfs_crc(crc, sizeof(test), &test); crc = lfs_crc(crc, sizeof(test), &test);
for (lfs_off_t j = sizeof(test); j < lfs->block_size; j += 4) { for (lfs_off_t j = sizeof(test); j < lfs->cfg->block_size; j += 4) {
uint32_t word; uint32_t word;
int err = lfs_bd_read(lfs, tpair[i], j, 4, &word); int err = lfs_bd_read(lfs, tpair[i], j, 4, &word);
if (err) { if (err) {
@@ -406,7 +401,7 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_dir_t *dir,
dir->d.rev += 1; dir->d.rev += 1;
lfs_pairswap(dir->pair); lfs_pairswap(dir->pair);
int err = lfs_bd_erase(lfs, dir->pair[0], 0, lfs->block_size); int err = lfs_bd_erase(lfs, dir->pair[0]);
if (err) { if (err) {
return err; return err;
} }
@@ -456,7 +451,7 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_dir_t *dir,
} }
} }
while (off < lfs->block_size-4) { while (off < lfs->cfg->block_size-4) {
uint8_t data; uint8_t data;
int err = lfs_bd_read(lfs, dir->pair[0], off, 1, &data); int err = lfs_bd_read(lfs, dir->pair[0], off, 1, &data);
if (err) { if (err) {
@@ -467,7 +462,7 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_dir_t *dir,
off += 1; off += 1;
} }
err = lfs_bd_prog(lfs, dir->pair[0], lfs->block_size-4, 4, &crc); err = lfs_bd_prog(lfs, dir->pair[0], lfs->cfg->block_size-4, 4, &crc);
if (err) { if (err) {
return err; return err;
} }
@@ -480,7 +475,7 @@ static int lfs_dir_shift(lfs_t *lfs, lfs_dir_t *dir, lfs_entry_t *entry) {
dir->d.size -= entry->d.len; dir->d.size -= entry->d.len;
lfs_pairswap(dir->pair); lfs_pairswap(dir->pair);
int err = lfs_bd_erase(lfs, dir->pair[0], 0, lfs->block_size); int err = lfs_bd_erase(lfs, dir->pair[0]);
if (err) { if (err) {
return err; return err;
} }
@@ -516,7 +511,7 @@ static int lfs_dir_shift(lfs_t *lfs, lfs_dir_t *dir, lfs_entry_t *entry) {
} }
} }
while (woff < lfs->block_size-4) { while (woff < lfs->cfg->block_size-4) {
uint8_t data; uint8_t data;
int err = lfs_bd_read(lfs, dir->pair[0], woff, 1, &data); int err = lfs_bd_read(lfs, dir->pair[0], woff, 1, &data);
if (err) { if (err) {
@@ -527,7 +522,7 @@ static int lfs_dir_shift(lfs_t *lfs, lfs_dir_t *dir, lfs_entry_t *entry) {
woff += 1; woff += 1;
} }
err = lfs_bd_prog(lfs, dir->pair[0], lfs->block_size-4, 4, &crc); err = lfs_bd_prog(lfs, dir->pair[0], lfs->cfg->block_size-4, 4, &crc);
if (err) { if (err) {
return err; return err;
} }
@@ -539,7 +534,7 @@ static int lfs_dir_append(lfs_t *lfs, lfs_dir_t *dir,
lfs_entry_t *entry, const void *data) { lfs_entry_t *entry, const void *data) {
// check if we fit, if top bit is set we do not and move on // check if we fit, if top bit is set we do not and move on
while (true) { while (true) {
if (dir->d.size + entry->d.len <= lfs->block_size - 4) { if (dir->d.size + entry->d.len <= lfs->cfg->block_size - 4) {
entry->pair[0] = dir->pair[0]; entry->pair[0] = dir->pair[0];
entry->pair[1] = dir->pair[1]; entry->pair[1] = dir->pair[1];
entry->off = dir->d.size; entry->off = dir->d.size;
@@ -882,7 +877,7 @@ int lfs_file_open(lfs_t *lfs, lfs_file_t *file,
// TODO do this lazily in write? // TODO do this lazily in write?
// TODO cow the head i/d block // TODO cow the head i/d block
if (file->size < lfs->block_size) { if (file->size < lfs->cfg->block_size) {
file->wblock = file->head; file->wblock = file->head;
} else { } else {
int err = lfs_index_find(lfs, file->head, file->windex, int err = lfs_index_find(lfs, file->head, file->windex,
@@ -915,7 +910,7 @@ lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file,
lfs_size_t nsize = size; lfs_size_t nsize = size;
while (nsize > 0) { while (nsize > 0) {
lfs_off_t woff = file->size % lfs->block_size; lfs_off_t woff = file->size % lfs->cfg->block_size;
if (file->size == 0) { if (file->size == 0) {
int err = lfs_alloc_erased(lfs, &file->wblock); int err = lfs_alloc_erased(lfs, &file->wblock);
@@ -938,7 +933,7 @@ lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file,
} }
} }
lfs_size_t diff = lfs_min(nsize, lfs->block_size - woff); lfs_size_t diff = lfs_min(nsize, lfs->cfg->block_size - woff);
int err = lfs_bd_prog(lfs, file->wblock, woff, diff, data); int err = lfs_bd_prog(lfs, file->wblock, woff, diff, data);
if (err) { if (err) {
return err; return err;
@@ -958,10 +953,10 @@ lfs_ssize_t lfs_file_read(lfs_t *lfs, lfs_file_t *file,
lfs_size_t nsize = size; lfs_size_t nsize = size;
while (nsize > 0 && file->roff < file->size) { while (nsize > 0 && file->roff < file->size) {
lfs_off_t roff = file->roff % lfs->block_size; lfs_off_t roff = file->roff % lfs->cfg->block_size;
// TODO cache index blocks // TODO cache index blocks
if (file->size < lfs->block_size) { if (file->size < lfs->cfg->block_size) {
file->rblock = file->head; file->rblock = file->head;
} else if (roff == 0) { } else if (roff == 0) {
int err = lfs_index_find(lfs, file->head, file->windex, int err = lfs_index_find(lfs, file->head, file->windex,
@@ -975,7 +970,7 @@ lfs_ssize_t lfs_file_read(lfs_t *lfs, lfs_file_t *file,
lfs_size_t diff = lfs_min( lfs_size_t diff = lfs_min(
lfs_min(nsize, file->size-file->roff), lfs_min(nsize, file->size-file->roff),
lfs->block_size - roff); lfs->cfg->block_size - roff);
int err = lfs_bd_read(lfs, file->rblock, roff, diff, data); int err = lfs_bd_read(lfs, file->rblock, roff, diff, data);
if (err) { if (err) {
return err; return err;
@@ -991,85 +986,17 @@ lfs_ssize_t lfs_file_read(lfs_t *lfs, lfs_file_t *file,
/// Generic filesystem operations /// /// Generic filesystem operations ///
static int lfs_configure(lfs_t *lfs, const struct lfs_config *config) {
lfs->bd = config->bd;
lfs->bd_ops = config->bd_ops;
struct lfs_bd_info info;
int err = lfs_bd_info(lfs, &info);
if (err) {
return err;
}
if (config->read_size) {
if (config->read_size < info.read_size ||
config->read_size % info.read_size != 0) {
LFS_ERROR("Invalid read size %u, device has %u\n",
config->read_size, info.read_size);
return LFS_ERROR_INVALID;
}
lfs->read_size = config->read_size;
} else {
lfs->read_size = info.read_size;
}
if (config->prog_size) {
if (config->prog_size < info.prog_size ||
config->prog_size % info.prog_size != 0) {
LFS_ERROR("Invalid prog size %u, device has %u\n",
config->prog_size, info.prog_size);
return LFS_ERROR_INVALID;
}
lfs->prog_size = config->prog_size;
} else {
lfs->prog_size = info.prog_size;
}
if (config->block_size) {
if (config->block_size < info.erase_size ||
config->block_size % info.erase_size != 0) {
LFS_ERROR("Invalid block size %u, device has %u\n",
config->prog_size, info.prog_size);
return LFS_ERROR_INVALID;
}
lfs->block_size = config->block_size;
} else {
lfs->block_size = lfs_min(512, info.erase_size);
}
if (config->block_count) {
if (config->block_count > info.total_size/info.erase_size) {
LFS_ERROR("Invalid block size %u, device has %u\n",
config->block_size,
(uint32_t)(info.total_size/info.erase_size));
return LFS_ERROR_INVALID;
}
lfs->block_count = config->block_count;
} else {
lfs->block_count = info.total_size / info.erase_size;
}
lfs->words = lfs->block_size / sizeof(uint32_t);
return 0;
}
int lfs_format(lfs_t *lfs, const struct lfs_config *config) { int lfs_format(lfs_t *lfs, const struct lfs_config *config) {
int err = lfs_configure(lfs, config); lfs->cfg = config;
if (err) { lfs->words = lfs->cfg->block_size / sizeof(uint32_t);
return err;
}
// Create free list // Create free list
lfs->free.begin = 0; lfs->free.begin = 0;
lfs->free.end = lfs->block_count-1; lfs->free.end = lfs->cfg->block_count-1;
// Create superblock dir // Create superblock dir
lfs_dir_t superdir; lfs_dir_t superdir;
err = lfs_dir_alloc(lfs, &superdir); int err = lfs_dir_alloc(lfs, &superdir);
if (err) { if (err) {
return err; return err;
} }
@@ -1096,8 +1023,8 @@ int lfs_format(lfs_t *lfs, const struct lfs_config *config) {
.d.len = sizeof(superblock.d), .d.len = sizeof(superblock.d),
.d.version = 0x00000001, .d.version = 0x00000001,
.d.magic = {"littlefs"}, .d.magic = {"littlefs"},
.d.block_size = lfs->block_size, .d.block_size = lfs->cfg->block_size,
.d.block_count = lfs->block_count, .d.block_count = lfs->cfg->block_count,
.d.root = {lfs->root[0], lfs->root[1]}, .d.root = {lfs->root[0], lfs->root[1]},
}; };
superdir.d.tail[0] = root.pair[0]; superdir.d.tail[0] = root.pair[0];
@@ -1121,14 +1048,12 @@ int lfs_format(lfs_t *lfs, const struct lfs_config *config) {
} }
int lfs_mount(lfs_t *lfs, const struct lfs_config *config) { int lfs_mount(lfs_t *lfs, const struct lfs_config *config) {
int err = lfs_configure(lfs, config); lfs->cfg = config;
if (err) { lfs->words = lfs->cfg->block_size / sizeof(uint32_t);
return err;
}
lfs_dir_t dir; lfs_dir_t dir;
lfs_superblock_t superblock; lfs_superblock_t superblock;
err = lfs_dir_fetch(lfs, &dir, (const lfs_block_t[2]){0, 1}); int err = lfs_dir_fetch(lfs, &dir, (const lfs_block_t[2]){0, 1});
if (!err) { if (!err) {
err = lfs_bd_read(lfs, dir.pair[0], err = lfs_bd_read(lfs, dir.pair[0],
sizeof(dir.d), sizeof(superblock.d), &superblock.d); sizeof(dir.d), sizeof(superblock.d), &superblock.d);
@@ -1186,7 +1111,7 @@ int lfs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data) {
dir.off += file.entry.d.len; dir.off += file.entry.d.len;
if ((0xf & file.entry.d.type) == LFS_TYPE_REG) { if ((0xf & file.entry.d.type) == LFS_TYPE_REG) {
if (file.entry.d.u.file.size < lfs->block_size) { if (file.entry.d.u.file.size < lfs->cfg->block_size) {
int err = cb(data, file.entry.d.u.file.head); int err = cb(data, file.entry.d.u.file.head);
if (err) { if (err) {
return err; return err;

51
lfs.h
View File

@@ -8,10 +8,9 @@
#define LFS_H #define LFS_H
#include "lfs_config.h" #include "lfs_config.h"
#include "lfs_bd.h"
// Data structures // The littefs constants
enum lfs_error { enum lfs_error {
LFS_ERROR_OK = 0, LFS_ERROR_OK = 0,
LFS_ERROR_CORRUPT = -3, LFS_ERROR_CORRUPT = -3,
@@ -41,23 +40,56 @@ enum lfs_open_flags {
}; };
// Configuration provided during initialization of the littlefs
struct lfs_config { struct lfs_config {
lfs_bd_t *bd; // Opaque user provided context
const struct lfs_bd_ops *bd_ops; void *context;
// Read a region in a block
int (*read)(const struct lfs_config *c, lfs_block_t block,
lfs_off_t off, lfs_size_t size, void *buffer);
// Program a region in a block. The block must have previously
// been erased.
int (*prog)(const struct lfs_config *c, lfs_block_t block,
lfs_off_t off, lfs_size_t size, const void *buffer);
// Erase a block. A block must be erased before being programmed.
// The state of an erased block is undefined.
int (*erase)(const struct lfs_config *c, lfs_block_t block);
// Sync the state of the underlying block device
int (*sync)(const struct lfs_config *c);
// Minimum size of a read. This may be larger than the physical
// read size to cache reads from the block device.
lfs_size_t read_size; lfs_size_t read_size;
// Minimum size of a program. This may be larger than the physical
// program size to cache programs to the block device.
lfs_size_t prog_size; lfs_size_t prog_size;
// Size of an erasable block.
lfs_size_t block_size; lfs_size_t block_size;
// Number of erasable blocks on the device.
lfs_size_t block_count; lfs_size_t block_count;
}; };
// File info structure
struct lfs_info { struct lfs_info {
// Type of the file, either REG or DIR
uint8_t type; uint8_t type;
// Size of the file, only valid for REG files
lfs_size_t size; lfs_size_t size;
// Name of the file stored as a null-terminated string
char name[LFS_NAME_MAX+1]; char name[LFS_NAME_MAX+1];
}; };
// Internal data structures
typedef struct lfs_entry { typedef struct lfs_entry {
lfs_block_t pair[2]; lfs_block_t pair[2];
lfs_off_t off; lfs_off_t off;
@@ -115,17 +147,12 @@ typedef struct lfs_superblock {
} d; } d;
} lfs_superblock_t; } lfs_superblock_t;
// Little filesystem type // Little filesystem type
typedef struct lfs { typedef struct lfs {
lfs_size_t read_size; // size of read const struct lfs_config *cfg;
lfs_size_t prog_size; // size of program
lfs_size_t block_size; // size of erase (block size)
lfs_size_t block_count; // number of erasable blocks
lfs_size_t words; // number of 32-bit words that can fit in a block lfs_size_t words; // number of 32-bit words that can fit in a block
lfs_bd_t *bd;
const struct lfs_bd_ops *bd_ops;
lfs_block_t root[2]; lfs_block_t root[2];
struct { struct {
lfs_block_t begin; lfs_block_t begin;
@@ -135,6 +162,7 @@ typedef struct lfs {
uint32_t lookahead[LFS_CFG_LOOKAHEAD/32]; uint32_t lookahead[LFS_CFG_LOOKAHEAD/32];
} lfs_t; } lfs_t;
// Functions // Functions
int lfs_format(lfs_t *lfs, const struct lfs_config *config); int lfs_format(lfs_t *lfs, const struct lfs_config *config);
int lfs_mount(lfs_t *lfs, const struct lfs_config *config); int lfs_mount(lfs_t *lfs, const struct lfs_config *config);
@@ -160,4 +188,5 @@ lfs_ssize_t lfs_file_read(lfs_t *lfs, lfs_file_t *file,
int lfs_deorphan(lfs_t *lfs); int lfs_deorphan(lfs_t *lfs);
int lfs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data); int lfs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data);
#endif #endif

View File

@@ -1,59 +0,0 @@
/*
* Block device interface
*
* Copyright (c) 2017 Christopher Haster
* Distributed under the MIT license
*/
#ifndef LFS_BD_H
#define LFS_BD_H
#include "lfs_config.h"
// Opaque type for block devices
typedef void lfs_bd_t;
// Description of block devices
struct lfs_bd_info {
lfs_size_t read_size; // Size of readable block
lfs_size_t prog_size; // Size of programmable block
lfs_size_t erase_size; // Size of erase block
uint64_t total_size; // Total size of the device
};
// Block device operations
//
// The little file system takes in a pointer to an opaque type
// and this struct, all operations are passed the opaque pointer
// which can be used to reference any state associated with the
// block device
struct lfs_bd_ops {
// Read a block
int (*read)(lfs_bd_t *bd, lfs_block_t block,
lfs_off_t off, lfs_size_t size, void *buffer);
// Program a block
//
// The block must have previously been erased.
int (*prog)(lfs_bd_t *bd, lfs_block_t block,
lfs_off_t off, lfs_size_t size, const void *buffer);
// Erase a block
//
// A block must be erased before being programmed. The
// state of an erased block is undefined.
int (*erase)(lfs_bd_t *bd, lfs_block_t block,
lfs_off_t off, lfs_size_t size);
// Sync the block device
int (*sync)(lfs_bd_t *bd);
// Get a description of the block device
//
// Any unknown information may be left as zero
int (*info)(lfs_bd_t *bd, struct lfs_bd_info *info);
};
#endif

View File

@@ -7,12 +7,12 @@ import os
import re import re
def main(): def main():
with open('blocks/info') as file: with open('blocks/config') as file:
s = struct.unpack('<LLL4xQ', file.read()) s = struct.unpack('<LLLL', file.read())
print 'read_size: %d' % s[0] print 'read_size: %d' % s[0]
print 'prog_size: %d' % s[1] print 'prog_size: %d' % s[1]
print 'erase_size: %d' % s[2] print 'block_size: %d' % s[2]
print 'total_size: %d' % s[3] print 'block_size: %d' % s[3]
print 'real_size: %d' % sum( print 'real_size: %d' % sum(
os.path.getsize(os.path.join('blocks', f)) os.path.getsize(os.path.join('blocks', f))

View File

@@ -43,8 +43,6 @@ lfs_t lfs;
lfs_emubd_t bd; lfs_emubd_t bd;
lfs_file_t file[4]; lfs_file_t file[4];
lfs_dir_t dir[4]; lfs_dir_t dir[4];
struct lfs_bd_info bd_info;
struct lfs_bd_stats bd_stats;
struct lfs_info info; struct lfs_info info;
uint8_t buffer[1024]; uint8_t buffer[1024];
@@ -56,17 +54,41 @@ lfs_size_t rsize;
uintmax_t res; uintmax_t res;
const struct lfs_config config = {{ #ifndef LFS_READ_SIZE
.bd = &bd, #define LFS_READ_SIZE 1
.bd_ops = &lfs_emubd_ops, #endif
#ifndef LFS_PROG_SIZE
#define LFS_PROG_SIZE 1
#endif
#ifndef LFS_BLOCK_SIZE
#define LFS_BLOCK_SIZE 512
#endif
#ifndef LFS_BLOCK_COUNT
#define LFS_BLOCK_COUNT 1024
#endif
const struct lfs_config cfg = {{
.context = &bd,
.read = &lfs_emubd_read,
.prog = &lfs_emubd_prog,
.erase = &lfs_emubd_erase,
.sync = &lfs_emubd_sync,
.read_size = LFS_READ_SIZE,
.prog_size = LFS_PROG_SIZE,
.block_size = LFS_BLOCK_SIZE,
.block_count = LFS_BLOCK_COUNT,
}}; }};
// Entry point // Entry point
int main() {{ int main() {{
lfs_emubd_create(&bd, "blocks"); lfs_emubd_create(&cfg, "blocks");
{tests} {tests}
lfs_emubd_destroy(&bd); lfs_emubd_destroy(&cfg);
}} }}

View File

@@ -4,14 +4,14 @@ set -eu
echo "=== Allocator tests ===" echo "=== Allocator tests ==="
rm -rf blocks rm -rf blocks
tests/test.py << TEST tests/test.py << TEST
lfs_format(&lfs, &config) => 0; lfs_format(&lfs, &cfg) => 0;
TEST TEST
SIZE=15000 SIZE=15000
lfs_mkdir() { lfs_mkdir() {
tests/test.py << TEST tests/test.py << TEST
lfs_mount(&lfs, &config) => 0; lfs_mount(&lfs, &cfg) => 0;
lfs_mkdir(&lfs, "$1") => 0; lfs_mkdir(&lfs, "$1") => 0;
lfs_unmount(&lfs) => 0; lfs_unmount(&lfs) => 0;
TEST TEST
@@ -19,7 +19,7 @@ TEST
lfs_remove() { lfs_remove() {
tests/test.py << TEST tests/test.py << TEST
lfs_mount(&lfs, &config) => 0; lfs_mount(&lfs, &cfg) => 0;
lfs_remove(&lfs, "$1/eggs") => 0; lfs_remove(&lfs, "$1/eggs") => 0;
lfs_remove(&lfs, "$1/bacon") => 0; lfs_remove(&lfs, "$1/bacon") => 0;
lfs_remove(&lfs, "$1/pancakes") => 0; lfs_remove(&lfs, "$1/pancakes") => 0;
@@ -31,7 +31,7 @@ TEST
lfs_alloc_singleproc() { lfs_alloc_singleproc() {
tests/test.py << TEST tests/test.py << TEST
const char *names[] = {"bacon", "eggs", "pancakes"}; const char *names[] = {"bacon", "eggs", "pancakes"};
lfs_mount(&lfs, &config) => 0; lfs_mount(&lfs, &cfg) => 0;
for (int n = 0; n < sizeof(names)/sizeof(names[0]); n++) { for (int n = 0; n < sizeof(names)/sizeof(names[0]); n++) {
sprintf((char*)buffer, "$1/%s", names[n]); sprintf((char*)buffer, "$1/%s", names[n]);
lfs_file_open(&lfs, &file[n], (char*)buffer, lfs_file_open(&lfs, &file[n], (char*)buffer,
@@ -54,7 +54,7 @@ lfs_alloc_multiproc() {
for name in bacon eggs pancakes for name in bacon eggs pancakes
do do
tests/test.py << TEST tests/test.py << TEST
lfs_mount(&lfs, &config) => 0; lfs_mount(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file[0], "$1/$name", lfs_file_open(&lfs, &file[0], "$1/$name",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0; LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0;
size = strlen("$name"); size = strlen("$name");
@@ -72,7 +72,7 @@ lfs_verify() {
for name in bacon eggs pancakes for name in bacon eggs pancakes
do do
tests/test.py << TEST tests/test.py << TEST
lfs_mount(&lfs, &config) => 0; lfs_mount(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file[0], "$1/$name", LFS_O_RDONLY) => 0; lfs_file_open(&lfs, &file[0], "$1/$name", LFS_O_RDONLY) => 0;
size = strlen("$name"); size = strlen("$name");
for (int i = 0; i < $SIZE; i++) { for (int i = 0; i < $SIZE; i++) {

View File

@@ -6,12 +6,12 @@ LARGESIZE=128
echo "=== Directory tests ===" echo "=== Directory tests ==="
rm -rf blocks rm -rf blocks
tests/test.py << TEST tests/test.py << TEST
lfs_format(&lfs, &config) => 0; lfs_format(&lfs, &cfg) => 0;
TEST TEST
echo "--- Root directory ---" echo "--- Root directory ---"
tests/test.py << TEST tests/test.py << TEST
lfs_mount(&lfs, &config) => 0; lfs_mount(&lfs, &cfg) => 0;
lfs_dir_open(&lfs, &dir[0], "/") => 0; lfs_dir_open(&lfs, &dir[0], "/") => 0;
lfs_dir_close(&lfs, &dir[0]) => 0; lfs_dir_close(&lfs, &dir[0]) => 0;
lfs_unmount(&lfs) => 0; lfs_unmount(&lfs) => 0;
@@ -19,14 +19,14 @@ TEST
echo "--- Directory creation ---" echo "--- Directory creation ---"
tests/test.py << TEST tests/test.py << TEST
lfs_mount(&lfs, &config) => 0; lfs_mount(&lfs, &cfg) => 0;
lfs_mkdir(&lfs, "potato") => 0; lfs_mkdir(&lfs, "potato") => 0;
lfs_unmount(&lfs) => 0; lfs_unmount(&lfs) => 0;
TEST TEST
echo "--- File creation ---" echo "--- File creation ---"
tests/test.py << TEST tests/test.py << TEST
lfs_mount(&lfs, &config) => 0; lfs_mount(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file[0], "burito", LFS_O_CREAT | LFS_O_WRONLY) => 0; lfs_file_open(&lfs, &file[0], "burito", LFS_O_CREAT | LFS_O_WRONLY) => 0;
lfs_file_close(&lfs, &file[0]) => 0; lfs_file_close(&lfs, &file[0]) => 0;
lfs_unmount(&lfs) => 0; lfs_unmount(&lfs) => 0;
@@ -34,7 +34,7 @@ TEST
echo "--- Directory iteration ---" echo "--- Directory iteration ---"
tests/test.py << TEST tests/test.py << TEST
lfs_mount(&lfs, &config) => 0; lfs_mount(&lfs, &cfg) => 0;
lfs_dir_open(&lfs, &dir[0], "/") => 0; lfs_dir_open(&lfs, &dir[0], "/") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1; lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, ".") => 0; strcmp(info.name, ".") => 0;
@@ -55,7 +55,7 @@ TEST
echo "--- Directory failures ---" echo "--- Directory failures ---"
tests/test.py << TEST tests/test.py << TEST
lfs_mount(&lfs, &config) => 0; lfs_mount(&lfs, &cfg) => 0;
lfs_mkdir(&lfs, "potato") => LFS_ERROR_EXISTS; lfs_mkdir(&lfs, "potato") => LFS_ERROR_EXISTS;
lfs_dir_open(&lfs, &dir[0], "tomato") => LFS_ERROR_NO_ENTRY; lfs_dir_open(&lfs, &dir[0], "tomato") => LFS_ERROR_NO_ENTRY;
lfs_dir_open(&lfs, &dir[0], "burito") => LFS_ERROR_NOT_DIR; lfs_dir_open(&lfs, &dir[0], "burito") => LFS_ERROR_NOT_DIR;
@@ -66,14 +66,14 @@ TEST
echo "--- Nested directories ---" echo "--- Nested directories ---"
tests/test.py << TEST tests/test.py << TEST
lfs_mount(&lfs, &config) => 0; lfs_mount(&lfs, &cfg) => 0;
lfs_mkdir(&lfs, "potato/baked") => 0; lfs_mkdir(&lfs, "potato/baked") => 0;
lfs_mkdir(&lfs, "potato/sweet") => 0; lfs_mkdir(&lfs, "potato/sweet") => 0;
lfs_mkdir(&lfs, "potato/fried") => 0; lfs_mkdir(&lfs, "potato/fried") => 0;
lfs_unmount(&lfs) => 0; lfs_unmount(&lfs) => 0;
TEST TEST
tests/test.py << TEST tests/test.py << TEST
lfs_mount(&lfs, &config) => 0; lfs_mount(&lfs, &cfg) => 0;
lfs_dir_open(&lfs, &dir[0], "potato") => 0; lfs_dir_open(&lfs, &dir[0], "potato") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1; lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, ".") => 0; strcmp(info.name, ".") => 0;
@@ -97,7 +97,7 @@ TEST
echo "--- Multi-block directory ---" echo "--- Multi-block directory ---"
tests/test.py << TEST tests/test.py << TEST
lfs_mount(&lfs, &config) => 0; lfs_mount(&lfs, &cfg) => 0;
lfs_mkdir(&lfs, "cactus") => 0; lfs_mkdir(&lfs, "cactus") => 0;
for (int i = 0; i < $LARGESIZE; i++) { for (int i = 0; i < $LARGESIZE; i++) {
sprintf((char*)buffer, "cactus/test%d", i); sprintf((char*)buffer, "cactus/test%d", i);
@@ -106,7 +106,7 @@ tests/test.py << TEST
lfs_unmount(&lfs) => 0; lfs_unmount(&lfs) => 0;
TEST TEST
tests/test.py << TEST tests/test.py << TEST
lfs_mount(&lfs, &config) => 0; lfs_mount(&lfs, &cfg) => 0;
lfs_dir_open(&lfs, &dir[0], "cactus") => 0; lfs_dir_open(&lfs, &dir[0], "cactus") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1; lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, ".") => 0; strcmp(info.name, ".") => 0;
@@ -125,7 +125,7 @@ TEST
echo "--- Directory remove ---" echo "--- Directory remove ---"
tests/test.py << TEST tests/test.py << TEST
lfs_mount(&lfs, &config) => 0; lfs_mount(&lfs, &cfg) => 0;
lfs_remove(&lfs, "potato") => LFS_ERROR_INVALID; lfs_remove(&lfs, "potato") => LFS_ERROR_INVALID;
lfs_remove(&lfs, "potato/sweet") => 0; lfs_remove(&lfs, "potato/sweet") => 0;
lfs_remove(&lfs, "potato/baked") => 0; lfs_remove(&lfs, "potato/baked") => 0;
@@ -161,7 +161,7 @@ tests/test.py << TEST
lfs_unmount(&lfs) => 0; lfs_unmount(&lfs) => 0;
TEST TEST
tests/test.py << TEST tests/test.py << TEST
lfs_mount(&lfs, &config) => 0; lfs_mount(&lfs, &cfg) => 0;
lfs_dir_open(&lfs, &dir[0], "/") => 0; lfs_dir_open(&lfs, &dir[0], "/") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1; lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, ".") => 0; strcmp(info.name, ".") => 0;
@@ -182,7 +182,7 @@ TEST
echo "--- Directory rename ---" echo "--- Directory rename ---"
tests/test.py << TEST tests/test.py << TEST
lfs_mount(&lfs, &config) => 0; lfs_mount(&lfs, &cfg) => 0;
lfs_mkdir(&lfs, "coldpotato") => 0; lfs_mkdir(&lfs, "coldpotato") => 0;
lfs_mkdir(&lfs, "coldpotato/baked") => 0; lfs_mkdir(&lfs, "coldpotato/baked") => 0;
lfs_mkdir(&lfs, "coldpotato/sweet") => 0; lfs_mkdir(&lfs, "coldpotato/sweet") => 0;
@@ -190,12 +190,12 @@ tests/test.py << TEST
lfs_unmount(&lfs) => 0; lfs_unmount(&lfs) => 0;
TEST TEST
tests/test.py << TEST tests/test.py << TEST
lfs_mount(&lfs, &config) => 0; lfs_mount(&lfs, &cfg) => 0;
lfs_rename(&lfs, "coldpotato", "hotpotato") => 0; lfs_rename(&lfs, "coldpotato", "hotpotato") => 0;
lfs_unmount(&lfs) => 0; lfs_unmount(&lfs) => 0;
TEST TEST
tests/test.py << TEST tests/test.py << TEST
lfs_mount(&lfs, &config) => 0; lfs_mount(&lfs, &cfg) => 0;
lfs_dir_open(&lfs, &dir[0], "hotpotato") => 0; lfs_dir_open(&lfs, &dir[0], "hotpotato") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1; lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, ".") => 0; strcmp(info.name, ".") => 0;
@@ -217,7 +217,7 @@ tests/test.py << TEST
lfs_unmount(&lfs) => 0; lfs_unmount(&lfs) => 0;
TEST TEST
tests/test.py << TEST tests/test.py << TEST
lfs_mount(&lfs, &config) => 0; lfs_mount(&lfs, &cfg) => 0;
lfs_mkdir(&lfs, "warmpotato") => 0; lfs_mkdir(&lfs, "warmpotato") => 0;
lfs_mkdir(&lfs, "warmpotato/mushy") => 0; lfs_mkdir(&lfs, "warmpotato/mushy") => 0;
lfs_rename(&lfs, "hotpotato", "warmpotato") => LFS_ERROR_INVALID; lfs_rename(&lfs, "hotpotato", "warmpotato") => LFS_ERROR_INVALID;
@@ -228,7 +228,7 @@ tests/test.py << TEST
lfs_unmount(&lfs) => 0; lfs_unmount(&lfs) => 0;
TEST TEST
tests/test.py << TEST tests/test.py << TEST
lfs_mount(&lfs, &config) => 0; lfs_mount(&lfs, &cfg) => 0;
lfs_dir_open(&lfs, &dir[0], "warmpotato") => 0; lfs_dir_open(&lfs, &dir[0], "warmpotato") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1; lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, ".") => 0; strcmp(info.name, ".") => 0;
@@ -250,7 +250,7 @@ tests/test.py << TEST
lfs_unmount(&lfs) => 0; lfs_unmount(&lfs) => 0;
TEST TEST
tests/test.py << TEST tests/test.py << TEST
lfs_mount(&lfs, &config) => 0; lfs_mount(&lfs, &cfg) => 0;
lfs_mkdir(&lfs, "coldpotato") => 0; lfs_mkdir(&lfs, "coldpotato") => 0;
lfs_rename(&lfs, "warmpotato/baked", "coldpotato/baked") => 0; lfs_rename(&lfs, "warmpotato/baked", "coldpotato/baked") => 0;
lfs_rename(&lfs, "warmpotato/sweet", "coldpotato/sweet") => 0; lfs_rename(&lfs, "warmpotato/sweet", "coldpotato/sweet") => 0;
@@ -260,7 +260,7 @@ tests/test.py << TEST
lfs_unmount(&lfs) => 0; lfs_unmount(&lfs) => 0;
TEST TEST
tests/test.py << TEST tests/test.py << TEST
lfs_mount(&lfs, &config) => 0; lfs_mount(&lfs, &cfg) => 0;
lfs_dir_open(&lfs, &dir[0], "coldpotato") => 0; lfs_dir_open(&lfs, &dir[0], "coldpotato") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1; lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, ".") => 0; strcmp(info.name, ".") => 0;

View File

@@ -8,12 +8,12 @@ LARGESIZE=262144
echo "=== File tests ===" echo "=== File tests ==="
rm -rf blocks rm -rf blocks
tests/test.py << TEST tests/test.py << TEST
lfs_format(&lfs, &config) => 0; lfs_format(&lfs, &cfg) => 0;
TEST TEST
echo "--- Simple file test ---" echo "--- Simple file test ---"
tests/test.py << TEST tests/test.py << TEST
lfs_mount(&lfs, &config) => 0; lfs_mount(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file[0], "hello", LFS_O_RDWR | LFS_O_CREAT | LFS_O_APPEND) => 0; lfs_file_open(&lfs, &file[0], "hello", LFS_O_RDWR | LFS_O_CREAT | LFS_O_APPEND) => 0;
size = strlen("Hello World!\n"); size = strlen("Hello World!\n");
memcpy(wbuffer, "Hello World!\n", size); memcpy(wbuffer, "Hello World!\n", size);
@@ -29,7 +29,7 @@ tests/test.py << TEST
lfs_size_t size = $1; lfs_size_t size = $1;
lfs_size_t chunk = 31; lfs_size_t chunk = 31;
srand(0); srand(0);
lfs_mount(&lfs, &config) => 0; lfs_mount(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file[0], "$2", LFS_O_WRONLY | LFS_O_CREAT) => 0; lfs_file_open(&lfs, &file[0], "$2", LFS_O_WRONLY | LFS_O_CREAT) => 0;
for (lfs_size_t i = 0; i < size; i += chunk) { for (lfs_size_t i = 0; i < size; i += chunk) {
chunk = (chunk < size - i) ? chunk : size - i; chunk = (chunk < size - i) ? chunk : size - i;
@@ -48,7 +48,7 @@ tests/test.py << TEST
lfs_size_t size = $1; lfs_size_t size = $1;
lfs_size_t chunk = 29; lfs_size_t chunk = 29;
srand(0); srand(0);
lfs_mount(&lfs, &config) => 0; lfs_mount(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file[0], "$2", LFS_O_RDONLY) => 0; lfs_file_open(&lfs, &file[0], "$2", LFS_O_RDONLY) => 0;
for (lfs_size_t i = 0; i < size; i += chunk) { for (lfs_size_t i = 0; i < size; i += chunk) {
chunk = (chunk < size - i) ? chunk : size - i; chunk = (chunk < size - i) ? chunk : size - i;
@@ -81,7 +81,7 @@ r_test $LARGESIZE largeavacado
echo "--- Dir check ---" echo "--- Dir check ---"
tests/test.py << TEST tests/test.py << TEST
lfs_mount(&lfs, &config) => 0; lfs_mount(&lfs, &cfg) => 0;
lfs_dir_open(&lfs, &dir[0], "/") => 0; lfs_dir_open(&lfs, &dir[0], "/") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1; lfs_dir_read(&lfs, &dir[0], &info) => 1;
lfs_dir_read(&lfs, &dir[0], &info) => 1; lfs_dir_read(&lfs, &dir[0], &info) => 1;

View File

@@ -6,42 +6,42 @@ rm -rf blocks
echo "--- Basic formatting ---" echo "--- Basic formatting ---"
tests/test.py << TEST tests/test.py << TEST
lfs_format(&lfs, &config) => 0; lfs_format(&lfs, &cfg) => 0;
TEST TEST
echo "--- Invalid superblocks ---" echo "--- Invalid superblocks ---"
ln -f -s /dev/null blocks/0 ln -f -s /dev/null blocks/0
ln -f -s /dev/null blocks/1 ln -f -s /dev/null blocks/1
tests/test.py << TEST tests/test.py << TEST
lfs_format(&lfs, &config) => LFS_ERROR_CORRUPT; lfs_format(&lfs, &cfg) => LFS_ERROR_CORRUPT;
TEST TEST
rm blocks/0 blocks/1 rm blocks/0 blocks/1
echo "--- Basic mounting ---" echo "--- Basic mounting ---"
tests/test.py << TEST tests/test.py << TEST
lfs_format(&lfs, &config) => 0; lfs_format(&lfs, &cfg) => 0;
TEST TEST
tests/test.py << TEST tests/test.py << TEST
lfs_mount(&lfs, &config) => 0; lfs_mount(&lfs, &cfg) => 0;
lfs_unmount(&lfs) => 0; lfs_unmount(&lfs) => 0;
TEST TEST
echo "--- Invalid mount ---" echo "--- Invalid mount ---"
tests/test.py << TEST tests/test.py << TEST
lfs_format(&lfs, &config) => 0; lfs_format(&lfs, &cfg) => 0;
TEST TEST
rm blocks/0 blocks/1 rm blocks/0 blocks/1
tests/test.py << TEST tests/test.py << TEST
lfs_mount(&lfs, &config) => LFS_ERROR_CORRUPT; lfs_mount(&lfs, &cfg) => LFS_ERROR_CORRUPT;
TEST TEST
echo "--- Valid corrupt mount ---" echo "--- Valid corrupt mount ---"
tests/test.py << TEST tests/test.py << TEST
lfs_format(&lfs, &config) => 0; lfs_format(&lfs, &cfg) => 0;
TEST TEST
rm blocks/0 rm blocks/0
tests/test.py << TEST tests/test.py << TEST
lfs_mount(&lfs, &config) => 0; lfs_mount(&lfs, &cfg) => 0;
lfs_unmount(&lfs) => 0; lfs_unmount(&lfs) => 0;
TEST TEST

View File

@@ -4,12 +4,12 @@ set -eu
echo "=== Orphan tests ===" echo "=== Orphan tests ==="
rm -rf blocks rm -rf blocks
tests/test.py << TEST tests/test.py << TEST
lfs_format(&lfs, &config) => 0; lfs_format(&lfs, &cfg) => 0;
TEST TEST
echo "--- Orphan test ---" echo "--- Orphan test ---"
tests/test.py << TEST tests/test.py << TEST
lfs_mount(&lfs, &config) => 0; lfs_mount(&lfs, &cfg) => 0;
lfs_mkdir(&lfs, "parent") => 0; lfs_mkdir(&lfs, "parent") => 0;
lfs_mkdir(&lfs, "parent/orphan") => 0; lfs_mkdir(&lfs, "parent/orphan") => 0;
lfs_mkdir(&lfs, "parent/child") => 0; lfs_mkdir(&lfs, "parent/child") => 0;
@@ -19,7 +19,7 @@ TEST
# linked-list entry and should orphan the child # linked-list entry and should orphan the child
rm -v blocks/8 rm -v blocks/8
tests/test.py << TEST tests/test.py << TEST
lfs_mount(&lfs, &config) => 0; lfs_mount(&lfs, &cfg) => 0;
lfs_stat(&lfs, "parent/orphan", &info) => LFS_ERROR_NO_ENTRY; lfs_stat(&lfs, "parent/orphan", &info) => LFS_ERROR_NO_ENTRY;
unsigned before = 0; unsigned before = 0;
lfs_traverse(&lfs, test_count, &before) => 0; lfs_traverse(&lfs, test_count, &before) => 0;

View File

@@ -4,11 +4,11 @@ set -eu
echo "=== Path tests ===" echo "=== Path tests ==="
rm -rf blocks rm -rf blocks
tests/test.py << TEST tests/test.py << TEST
lfs_format(&lfs, &config) => 0; lfs_format(&lfs, &cfg) => 0;
TEST TEST
tests/test.py << TEST tests/test.py << TEST
lfs_mount(&lfs, &config) => 0; lfs_mount(&lfs, &cfg) => 0;
lfs_mkdir(&lfs, "tea") => 0; lfs_mkdir(&lfs, "tea") => 0;
lfs_mkdir(&lfs, "coffee") => 0; lfs_mkdir(&lfs, "coffee") => 0;
lfs_mkdir(&lfs, "soda") => 0; lfs_mkdir(&lfs, "soda") => 0;
@@ -26,7 +26,7 @@ TEST
echo "--- Root path tests ---" echo "--- Root path tests ---"
tests/test.py << TEST tests/test.py << TEST
lfs_mount(&lfs, &config) => 0; lfs_mount(&lfs, &cfg) => 0;
lfs_stat(&lfs, "tea/hottea", &info) => 0; lfs_stat(&lfs, "tea/hottea", &info) => 0;
strcmp(info.name, "hottea") => 0; strcmp(info.name, "hottea") => 0;
lfs_stat(&lfs, "/tea/hottea", &info) => 0; lfs_stat(&lfs, "/tea/hottea", &info) => 0;
@@ -36,7 +36,7 @@ TEST
echo "--- Redundant slash path tests ---" echo "--- Redundant slash path tests ---"
tests/test.py << TEST tests/test.py << TEST
lfs_mount(&lfs, &config) => 0; lfs_mount(&lfs, &cfg) => 0;
lfs_stat(&lfs, "/tea/hottea", &info) => 0; lfs_stat(&lfs, "/tea/hottea", &info) => 0;
strcmp(info.name, "hottea") => 0; strcmp(info.name, "hottea") => 0;
lfs_stat(&lfs, "//tea//hottea", &info) => 0; lfs_stat(&lfs, "//tea//hottea", &info) => 0;
@@ -48,7 +48,7 @@ TEST
echo "--- Dot path tests ---" echo "--- Dot path tests ---"
tests/test.py << TEST tests/test.py << TEST
lfs_mount(&lfs, &config) => 0; lfs_mount(&lfs, &cfg) => 0;
lfs_stat(&lfs, "./tea/hottea", &info) => 0; lfs_stat(&lfs, "./tea/hottea", &info) => 0;
strcmp(info.name, "hottea") => 0; strcmp(info.name, "hottea") => 0;
lfs_stat(&lfs, "/./tea/hottea", &info) => 0; lfs_stat(&lfs, "/./tea/hottea", &info) => 0;
@@ -62,7 +62,7 @@ TEST
echo "--- Dot dot path tests ---" echo "--- Dot dot path tests ---"
tests/test.py << TEST tests/test.py << TEST
lfs_mount(&lfs, &config) => 0; lfs_mount(&lfs, &cfg) => 0;
lfs_stat(&lfs, "coffee/../tea/hottea", &info) => 0; lfs_stat(&lfs, "coffee/../tea/hottea", &info) => 0;
strcmp(info.name, "hottea") => 0; strcmp(info.name, "hottea") => 0;
lfs_stat(&lfs, "tea/coldtea/../hottea", &info) => 0; lfs_stat(&lfs, "tea/coldtea/../hottea", &info) => 0;
@@ -76,7 +76,7 @@ TEST
echo "--- Root dot dot path tests ---" echo "--- Root dot dot path tests ---"
tests/test.py << TEST tests/test.py << TEST
lfs_mount(&lfs, &config) => 0; lfs_mount(&lfs, &cfg) => 0;
lfs_stat(&lfs, "coffee/../../../../../../tea/hottea", &info) => 0; lfs_stat(&lfs, "coffee/../../../../../../tea/hottea", &info) => 0;
strcmp(info.name, "hottea") => 0; strcmp(info.name, "hottea") => 0;
lfs_unmount(&lfs) => 0; lfs_unmount(&lfs) => 0;