Added support for the basic file operation

Missing seek, but these are the core filesystem operations
provided by this filesystem:
- Read a file
- Append to a file

Additional work is needed around freeing the previous file, so
right now it's limited to appending to existing files, a real
append only filesystem. Unfortunately the overhead of the free
list with multiple open files is becoming tricky.
This commit is contained in:
Christopher Haster
2017-03-19 22:00:56 -05:00
parent 53674cb3bc
commit ed674e8414
3 changed files with 281 additions and 15 deletions

251
lfs.c
View File

@@ -67,9 +67,19 @@ static lfs_off_t lfs_inext(lfs_t *lfs, lfs_off_t ioff) {
return ioff;
}
static lfs_off_t lfs_toindex(lfs_t *lfs, lfs_off_t off) {
lfs_off_t i = 0;
while (off > 512) {
i = lfs_inext(lfs, i);
off -= 512;
}
return i;
}
// Find index in index chain given its index offset
static lfs_error_t lfs_ifind(lfs_t *lfs, lfs_ino_t head,
lfs_size_t icount, lfs_off_t ioff, lfs_ino_t *ino) {
static lfs_error_t lfs_ifind_block(lfs_t *lfs, lfs_ino_t head,
lfs_size_t icount, lfs_off_t ioff, lfs_ino_t *block) {
lfs_size_t wcount = lfs->info.erase_size/4;
lfs_off_t iitarget = ioff / wcount;
lfs_off_t iicurrent = (icount-1) / wcount;
@@ -88,6 +98,18 @@ static lfs_error_t lfs_ifind(lfs_t *lfs, lfs_ino_t head,
iicurrent -= 1 << skip;
}
*block = head;
return 0;
}
static lfs_error_t lfs_ifind(lfs_t *lfs, lfs_ino_t head,
lfs_size_t icount, lfs_off_t ioff, lfs_ino_t *ino) {
lfs_size_t wcount = lfs->info.erase_size/4;
int err = lfs_ifind_block(lfs, head, icount, ioff, &head);
if (err) {
return err;
}
return lfs->ops->read(lfs->bd, (void*)ino, head, 4*(ioff % wcount), 4);
}
@@ -107,7 +129,7 @@ static lfs_error_t lfs_iappend(lfs_t *lfs, lfs_ino_t *headp,
return err;
}
lfs_off_t skips = lfs_min(lfs_ctz(ioff/wcount + 1), wcount-1) + 1;
lfs_off_t skips = lfs_min(lfs_ctz(ioff/wcount + 1), wcount-2) + 1;
for (lfs_off_t i = 0; i < skips; i++) {
err = lfs->ops->write(lfs->bd, (void*)&head, nhead, 4*i, 4);
if (err) {
@@ -141,20 +163,44 @@ static lfs_error_t lfs_iappend(lfs_t *lfs, lfs_ino_t *headp,
static lfs_error_t lfs_alloc(lfs_t *lfs, lfs_ino_t *ino) {
// TODO save slot for freeing?
lfs_error_t err = lfs_ifind(lfs, lfs->free.d.head,
lfs->free.d.icount, lfs->free.d.ioff, ino);
lfs->free.end, lfs->free.off, ino);
if (err) {
return err;
}
lfs->free.d.ioff = lfs_inext(lfs, lfs->free.d.ioff);
lfs->free.off = lfs_inext(lfs, lfs->free.off);
return lfs->ops->erase(lfs->bd, *ino, 0, lfs->info.erase_size);
}
static lfs_error_t lfs_free(lfs_t *lfs, lfs_ino_t ino) {
return lfs_iappend(lfs, &lfs->free.d.head, &lfs->free.d.icount, ino);
return lfs_iappend(lfs, &lfs->free.d.head, &lfs->free.end, ino);
}
static lfs_error_t lfs_free_flush(lfs_t *lfs) {
lfs_size_t wcount = lfs->info.erase_size/4;
for (lfs_word_t i = lfs->free.begin / wcount;
i + wcount < lfs->free.off; i += wcount) {
lfs_ino_t block;
int err = lfs_ifind_block(lfs, lfs->free.d.head,
lfs->free.end, i, &block);
if (err) {
return err;
}
lfs_free(lfs, block);
}
if (lfs->free.off != lfs->free.d.off || lfs->free.end != lfs->free.d.end) {
// TODO handle overflow?
lfs->free.d.rev += 1;
}
lfs->free.d.off = lfs->free.off;
lfs->free.d.end = lfs->free.end;
return 0;
}
lfs_error_t lfs_check(lfs_t *lfs, lfs_ino_t block) {
uint32_t crc = 0xffffffff;
@@ -303,8 +349,7 @@ lfs_error_t lfs_dir_make(lfs_t *lfs, lfs_dir_t *dir, lfs_ino_t parent[2]) {
dir->d.size = sizeof(struct lfs_disk_dir);
dir->d.tail[0] = 0;
dir->d.tail[1] = 0;
// TODO sort this out
lfs_free_flush(lfs);
dir->d.free = lfs->free.d;
if (parent) {
@@ -469,6 +514,8 @@ lfs_error_t lfs_mkdir(lfs_t *lfs, const char *path) {
cwd.d.rev += 1;
cwd.d.size += entry.d.len;
lfs_free_flush(lfs);
cwd.d.free = lfs->free.d;
return lfs_pair_commit(lfs, entry.dir,
3, (struct lfs_commit_region[3]) {
@@ -478,6 +525,184 @@ lfs_error_t lfs_mkdir(lfs_t *lfs, const char *path) {
});
}
lfs_error_t lfs_file_open(lfs_t *lfs, lfs_file_t *file,
const char *path, int flags) {
// Allocate entry for file if it doesn't exist
// TODO check open files
lfs_dir_t cwd;
int err = lfs_dir_fetch(lfs, &cwd, lfs->cwd);
if (err) {
return err;
}
if (flags & LFS_O_CREAT) {
err = lfs_dir_alloc(lfs, &cwd, path,
&file->entry, sizeof(file->entry.d)+strlen(path));
if (err && err != LFS_ERROR_EXISTS) {
return err;
}
} else {
err = lfs_dir_find(lfs, &cwd, path, &file->entry);
if (err) {
return err;
}
}
if ((flags & LFS_O_CREAT) && err != LFS_ERROR_EXISTS) {
// Store file
file->head = 0;
file->size = 0;
file->wblock = 0;
file->windex = 0;
file->rblock = 0;
file->rindex = 0;
file->roff = 0;
file->entry.d.type = 1;
file->entry.d.len = sizeof(file->entry.d) + strlen(path);
file->entry.d.u.file.head = file->head;
file->entry.d.u.file.size = file->size;
cwd.d.rev += 1;
cwd.d.size += file->entry.d.len;
lfs_free_flush(lfs);
cwd.d.free = lfs->free.d;
return lfs_pair_commit(lfs, file->entry.dir,
3, (struct lfs_commit_region[3]) {
{0, sizeof(cwd.d), &cwd.d},
{file->entry.off, sizeof(file->entry.d),
&file->entry.d},
{file->entry.off+sizeof(file->entry.d),
file->entry.d.len-sizeof(file->entry.d), path}
});
} else {
file->head = file->entry.d.u.file.head;
file->size = file->entry.d.u.file.size;
file->windex = lfs_toindex(lfs, file->size);
file->rblock = 0;
file->rindex = 0;
file->roff = 0;
// TODO do this lazily in write?
// TODO cow the head i/d block
if (file->size < lfs->info.erase_size) {
file->wblock = file->head;
} else {
int err = lfs_ifind(lfs, file->head, file->windex,
file->windex, &file->wblock);
if (err) {
return err;
}
}
return 0;
}
}
lfs_error_t lfs_file_close(lfs_t *lfs, lfs_file_t *file) {
// Store file
lfs_dir_t cwd;
int err = lfs_dir_fetch(lfs, &cwd, file->entry.dir);
if (err) {
return err;
}
file->entry.d.u.file.head = file->head;
file->entry.d.u.file.size = file->size;
cwd.d.rev += 1;
lfs_free_flush(lfs);
cwd.d.free = lfs->free.d;
return lfs_pair_commit(lfs, file->entry.dir,
3, (struct lfs_commit_region[3]) {
{0, sizeof(cwd.d), &cwd.d},
{file->entry.off, sizeof(file->entry.d), &file->entry.d},
});
}
lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file,
const void *buffer, lfs_size_t size) {
const uint8_t *data = buffer;
lfs_size_t nsize = size;
while (nsize > 0) {
lfs_off_t woff = file->size % lfs->info.erase_size;
if (file->size == 0) {
int err = lfs_alloc(lfs, &file->wblock);
if (err) {
return err;
}
file->head = file->wblock;
file->windex = 0;
} else if (woff == 0) {
// TODO check that 2 blocks are available
// TODO check for available blocks for backing up scratch files?
int err = lfs_alloc(lfs, &file->wblock);
if (err) {
return err;
}
err = lfs_iappend(lfs, &file->head, &file->windex, file->wblock);
if (err) {
return err;
}
}
lfs_size_t diff = lfs_min(nsize, lfs->info.erase_size - woff);
int err = lfs->ops->write(lfs->bd, data, file->wblock, woff, diff);
if (err) {
return err;
}
file->size += diff;
data += diff;
nsize -= diff;
}
return size;
}
lfs_ssize_t lfs_file_read(lfs_t *lfs, lfs_file_t *file,
void *buffer, lfs_size_t size) {
uint8_t *data = buffer;
lfs_size_t nsize = size;
while (nsize > 0 && file->roff < file->size) {
lfs_off_t roff = file->roff % lfs->info.erase_size;
// TODO cache index blocks
if (file->size < lfs->info.erase_size) {
file->rblock = file->head;
} else if (roff == 0) {
int err = lfs_ifind(lfs, file->head, file->windex,
file->rindex, &file->rblock);
if (err) {
return err;
}
file->rindex = lfs_inext(lfs, file->rindex);
}
lfs_size_t diff = lfs_min(
lfs_min(nsize, file->size-file->roff),
lfs->info.erase_size - roff);
int err = lfs->ops->read(lfs->bd, data, file->rblock, roff, diff);
if (err) {
return err;
}
file->roff += diff;
data += diff;
nsize -= diff;
}
return size - nsize;
}
// Little filesystem operations
@@ -509,10 +734,14 @@ lfs_error_t lfs_format(lfs_t *lfs) {
{ // Create free list
lfs->free = (lfs_free_t){
.d.head = 2,
.d.ioff = 1,
.d.icount = 1,
.begin = 1,
.off = 1,
.end = 1,
.d.rev = 1,
.d.head = 2,
.d.off = 1,
.d.end = 1,
};
lfs_size_t block_count = lfs->info.total_size / lfs->info.erase_size;

42
lfs.h
View File

@@ -26,12 +26,27 @@ enum lfs_type {
LFS_TYPE_DIR = 2,
};
enum lfs_open_flags {
LFS_O_RDONLY = 0,
LFS_O_WRONLY = 1,
LFS_O_RDWR = 2,
LFS_O_CREAT = 0x0040,
LFS_O_EXCL = 0x0080,
LFS_O_TRUNC = 0x0200,
LFS_O_APPEND = 0x0400,
LFS_O_SYNC = 0x1000,
};
typedef struct lfs_free {
lfs_word_t begin;
lfs_word_t off;
lfs_word_t end;
lfs_disk_struct lfs_disk_free {
lfs_ino_t head;
lfs_word_t ioff;
lfs_word_t icount;
lfs_word_t rev;
lfs_ino_t head;
lfs_word_t off;
lfs_word_t end;
} d;
} lfs_free_t;
@@ -65,6 +80,20 @@ typedef struct lfs_entry {
} d;
} lfs_entry_t;
typedef struct lfs_file {
lfs_ino_t head;
lfs_size_t size;
lfs_ino_t wblock;
lfs_word_t windex;
lfs_ino_t rblock;
lfs_word_t rindex;
lfs_off_t roff;
struct lfs_entry entry;
} lfs_file_t;
typedef struct lfs_superblock {
lfs_ino_t pair[2];
lfs_disk_struct lfs_disk_superblock {
@@ -94,5 +123,12 @@ lfs_error_t lfs_mount(lfs_t *lfs);
lfs_error_t lfs_mkdir(lfs_t *lfs, const char *path);
lfs_error_t lfs_file_open(lfs_t *lfs, lfs_file_t *file,
const char *path, int flags);
lfs_error_t lfs_file_close(lfs_t *lfs, lfs_file_t *file);
lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file,
const void *buffer, lfs_size_t size);
lfs_ssize_t lfs_file_read(lfs_t *lfs, lfs_file_t *file,
void *buffer, lfs_size_t size);
#endif

View File

@@ -15,6 +15,7 @@ typedef uint32_t lfs_word_t;
typedef uint16_t lfs_hword_t;
typedef lfs_word_t lfs_size_t;
typedef int32_t lfs_ssize_t;
typedef lfs_word_t lfs_off_t;
typedef int lfs_error_t;