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:
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -46,7 +61,7 @@ typedef struct lfs_dir {
|
||||
|
||||
struct lfs_disk_free free;
|
||||
} d;
|
||||
} lfs_dir_t;
|
||||
} lfs_dir_t;
|
||||
|
||||
typedef struct lfs_entry {
|
||||
lfs_ino_t dir[2];
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user