Compare commits

...

7 Commits

Author SHA1 Message Date
Christopher Haster
c7820e653e Introduced the LFS_O_SNAPSHOT flag
LFS_O_SNAPSHOT brings back some of littlefs's idiosyncratic behavior
removed in the changes to open file syncing in a form that may be more
useful for users.

LFS_O_SNAPSHOT allows you to open a "snapshot" of a file. This is a cheap,
local copy of a file who's changes are not reflected on disk.

Internally, snapshot files use the same mechanism as pending writes. A
separate, copy-on-write CTZ skip-list is created, with read-only
references to the existing data blocks until a write occurs. The
difference is that snapshot files are not enrolled in the mlist, meaning
they won't get updates from open file syncs, and during close their
contents are simply discarded.

As an extra benefit, LFS_O_CREAT | LFS_O_SNAPSHOT is equivalent to
Linux's O_TMPFILE, making it easy to create temporary, unnamed files.

This may be useful for embedded development, where unnamed flash-backed
buffers may provide a slower, but larger, alternative to RAM-backed
buffers.
2020-12-24 23:10:24 -06:00
Christopher Haster
deeaa17317 Added tests, cleanup, and fixed a small issue with updating file flags
Related to changes to custom attribute and open file syncing
2020-12-24 23:10:20 -06:00
Christopher Haster
026833214a Removing zeroing of trailing space in attribute buffers
This was provided as a courtesy to hopefully make custom attributes more
easy to use, however the zeroing turned out to be a bit complicated when
syncing custom attributes across multiple open files.

Implicitly zeroing trailing buffer space is also inconsistent with the
other APIs in the filesystem, such as lfs_file_read, so this commit
removes the behavior.

If you need to handle differently sized custom attributes, you can
either pre-zero the custom attribute buffers, or use lfs_getattr to find
the on-disk size of custom attributes explicitly.
2020-12-24 23:10:11 -06:00
Christopher Haster
b19a51c044 Reduced when custom attributse are written to strictly when files are dirty
This is a bit of a complicated area for the custom-attribute API without
much precedent. littlefs allows users to provide custom attributes in
the lfs_file_config struct, which get written along with other file
metadata.

Sounds great on paper, but the devil is in the details. When does the
metadata actually get written?

What about this case?

    lfs_file_opencfg(lfs, file, "path", LFS_O_WRONLY, cfg_with_attrs);
    lfs_file_close(lfs, file); // does not write metadata

This normally doesn't write out metadata! We've opened the file for
writing, but made no changes, so normally littlefs doesn't bother to
commit anything to disk.

Before, as a courtesy, littlefs marked the file as dirty if it noticed
the file was opened for writing with custom attributes, but this is
inaccurate could to leave to problems after a file is synced:

    lfs_file_opencfg(lfs, file, "path", LFS_O_WRONLY, cfg_with_attrs);
    lfs_file_sync(lfs, file);
    change_attrs();
    lfs_file_close(lfs, file); // does not write metadata

Unfortunately, it isn't easy to know when metadata needs to be written.
Custom attributes are provided as read-only pointers to buffers which
may be updated without additional filesystem calls, this means we don't
know if custom attributes have actually changed on the device side. If
they haven't changed, writing out metadata on every sync would be
wasteful.

Another solution would be to compare our device-side attributes with
the disk-side attributes every sync, but that would be even more
expensive.

---

So for now, the simpliest and most efficient solution wins. Custom
attributes attached to open files, are not written unless the file data
itself changes.

Note that explicit calls to lfs_setattr always update on-disk
attributes, and opening a file with LFS_O_CREATE | LFS_O_TRUNC will also
always update the on-disk attributes (though not with just LFS_O_CREAT!).

There are a few ways we could provide an API that manually forces a write
of custom attributes, such as lfs_file_setattr, though without dynamic
memory, providing these APIs gets a bit complicated. So for now we will
see if users run into issues with the current scheme.
2020-12-24 23:10:08 -06:00
Christopher Haster
d97d66adf5 Added device-side syncing of open files
Compared to other filesystems, littlefs's handling of open files may
come across as a bit odd, especially when you open the same file with
multiple file handles.

This commit addresses this by forcing all open readable file handles to
be consistent if any open writable file handle is synced though either
lfs_file_sync or lfs_file_close. This means open readable file handles
always mirror the state of the filesystem on disk.

To do this we again rely on the internal linked-list of open file
handles, marking files as clean, copying over the written file
cache, and synchronizing any custom attributes attached to the file
handles.

Note, this still needs cleanup and tests

---

Why was the previous behavior?

One of the nifty mechanism in littlefs is the ability to have multiple
device-side copies of a file that share copy-on-write blocks of data.
This is very useful for staging any amount of changes, which may live either
in RAM caches or allocated-but-not-committed blocks on disk, that can be
atomically updated in a single commit. After this change, littlefs still uses
this update mechanism to track open files, meaning if you lose power, the
entire file will revert to what was written at the last lfs_file_sync.

Because this mechanism already exists, it was easy enough to rely on
this to handle multiple open file handles gracefully. Each file handle
gets its own copy-on-write copy of the contents at time of open, and and
writes are managed independently of other open files.

This behavior was idiosyncratic, but consistent, though after some time
enough users raised feedback that this behavior needed to be reassessed.

Now multiple open files should conform to what's found in other
filesystem APIs, at a small code cost to manage syncing open files.
2020-12-24 23:09:59 -06:00
Christopher Haster
1a59954ec6 Merge pull request #495 from littlefs-project/devel
Minor release: v2.3
2020-12-07 20:50:31 -06:00
Christopher Haster
6a7012774d Renamed internal lfs_*raw -> lfs_raw* functions
- Prefixing with raw is slightly more readable, follows
  common-prefix rule
- Matches existing raw prefixes in testbd
2020-12-06 00:26:24 -06:00
5 changed files with 1047 additions and 272 deletions

390
lfs.c
View File

@@ -24,6 +24,14 @@ static inline void lfs_cache_zero(lfs_t *lfs, lfs_cache_t *pcache) {
pcache->block = LFS_BLOCK_NULL;
}
static inline void lfs_cache_copy(lfs_t *lfs,
lfs_cache_t *dcache, const lfs_cache_t *scache) {
memcpy(dcache->buffer, scache->buffer, lfs->cfg->cache_size);
dcache->block = scache->block;
dcache->off = scache->off;
dcache->size = scache->size;
}
static int lfs_bd_read(lfs_t *lfs,
const lfs_cache_t *pcache, lfs_cache_t *rcache, lfs_size_t hint,
lfs_block_t block, lfs_off_t off,
@@ -459,9 +467,9 @@ static int lfs_dir_compact(lfs_t *lfs,
lfs_mdir_t *dir, const struct lfs_mattr *attrs, int attrcount,
lfs_mdir_t *source, uint16_t begin, uint16_t end);
static lfs_ssize_t lfs_file_writeraw(lfs_t *lfs, lfs_file_t *file,
static lfs_ssize_t lfs_file_rawwrite(lfs_t *lfs, lfs_file_t *file,
const void *buffer, lfs_size_t size);
static int lfs_file_syncraw(lfs_t *lfs, lfs_file_t *file);
static int lfs_file_rawsync(lfs_t *lfs, lfs_file_t *file);
static int lfs_file_outline(lfs_t *lfs, lfs_file_t *file);
static int lfs_file_flush(lfs_t *lfs, lfs_file_t *file);
@@ -482,20 +490,20 @@ static int lfs1_traverse(lfs_t *lfs,
int (*cb)(void*, lfs_block_t), void *data);
#endif
static int lfs_dir_rewindraw(lfs_t *lfs, lfs_dir_t *dir);
static int lfs_dir_rawrewind(lfs_t *lfs, lfs_dir_t *dir);
static lfs_ssize_t lfs_file_readraw(lfs_t *lfs, lfs_file_t *file,
static lfs_ssize_t lfs_file_rawread(lfs_t *lfs, lfs_file_t *file,
void *buffer, lfs_size_t size);
static int lfs_file_closeraw(lfs_t *lfs, lfs_file_t *file);
static lfs_soff_t lfs_file_sizeraw(lfs_t *lfs, lfs_file_t *file);
static int lfs_file_rawclose(lfs_t *lfs, lfs_file_t *file);
static lfs_soff_t lfs_file_rawsize(lfs_t *lfs, lfs_file_t *file);
static lfs_ssize_t lfs_fs_sizeraw(lfs_t *lfs);
static int lfs_fs_traverseraw(lfs_t *lfs,
static lfs_ssize_t lfs_fs_rawsize(lfs_t *lfs);
static int lfs_fs_rawtraverse(lfs_t *lfs,
int (*cb)(void *data, lfs_block_t block), void *data,
bool includeorphans);
static int lfs_deinit(lfs_t *lfs);
static int lfs_unmountraw(lfs_t *lfs);
static int lfs_rawunmount(lfs_t *lfs);
/// Block allocator ///
@@ -567,7 +575,7 @@ static int lfs_alloc(lfs_t *lfs, lfs_block_t *block) {
// find mask of free blocks from tree
memset(lfs->free.buffer, 0, lfs->cfg->lookahead_size);
int err = lfs_fs_traverseraw(lfs, lfs_alloc_lookahead, lfs, true);
int err = lfs_fs_rawtraverse(lfs, lfs_alloc_lookahead, lfs, true);
if (err) {
lfs_alloc_drop(lfs);
return err;
@@ -630,8 +638,6 @@ static lfs_stag_t lfs_dir_getslice(lfs_t *lfs, const lfs_mdir_t *dir,
return err;
}
memset((uint8_t*)gbuffer + diff, 0, gsize - diff);
return tag + gdiff;
}
}
@@ -815,8 +821,12 @@ static int lfs_dir_traverse(lfs_t *lfs,
return err;
}
} else if (lfs_tag_type3(tag) == LFS_FROM_USERATTRS) {
for (unsigned i = 0; i < lfs_tag_size(tag); i++) {
const struct lfs_attr *a = buffer;
for (unsigned i = 0; i < lfs_tag_size(tag); i++) {
if (a[i].size > lfs->attr_max) {
return LFS_ERR_NOSPC;
}
int err = cb(data, LFS_MKTAG(LFS_TYPE_USERATTR + a[i].type,
lfs_tag_id(tag) + diff, a[i].size), a[i].buffer);
if (err) {
@@ -1080,7 +1090,7 @@ static int lfs_dir_fetch(lfs_t *lfs,
static int lfs_dir_getgstate(lfs_t *lfs, const lfs_mdir_t *dir,
lfs_gstate_t *gstate) {
lfs_gstate_t temp;
lfs_gstate_t temp = {0};
lfs_stag_t res = lfs_dir_get(lfs, dir, LFS_MKTAG(0x7ff, 0, 0),
LFS_MKTAG(LFS_TYPE_MOVESTATE, 0, sizeof(temp)), &temp);
if (res < 0 && res != LFS_ERR_NOENT) {
@@ -1106,10 +1116,11 @@ static int lfs_dir_getinfo(lfs_t *lfs, lfs_mdir_t *dir,
}
lfs_stag_t tag = lfs_dir_get(lfs, dir, LFS_MKTAG(0x780, 0x3ff, 0),
LFS_MKTAG(LFS_TYPE_NAME, id, lfs->name_max+1), info->name);
LFS_MKTAG(LFS_TYPE_NAME, id, lfs->name_max), info->name);
if (tag < 0) {
return (int)tag;
}
info->name[lfs_tag_size(tag)] = '\0';
info->type = lfs_tag_type3(tag);
@@ -1173,6 +1184,11 @@ static lfs_stag_t lfs_dir_find(lfs_t *lfs, lfs_mdir_t *dir,
dir->tail[0] = lfs->root[0];
dir->tail[1] = lfs->root[1];
// NULL path == root
if (!name) {
return tag;
}
while (true) {
nextname:
// skip slashes
@@ -1626,7 +1642,7 @@ static int lfs_dir_compact(lfs_t *lfs,
if (lfs_pair_cmp(dir->pair, (const lfs_block_t[2]){0, 1}) == 0) {
// oh no! we're writing too much to the superblock,
// should we expand?
lfs_ssize_t res = lfs_fs_sizeraw(lfs);
lfs_ssize_t res = lfs_fs_rawsize(lfs);
if (res < 0) {
return res;
}
@@ -1821,7 +1837,7 @@ relocate:
#ifndef LFS_READONLY
static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir,
const struct lfs_mattr *attrs, int attrcount) {
// check for any inline files that aren't RAM backed and
// check for any open inline files that aren't RAM backed and
// forcefully evict them, needed for filesystem consistency
for (lfs_file_t *f = (lfs_file_t*)lfs->mlist; f; f = f->next) {
if (dir != &f->m && lfs_pair_cmp(f->m.pair, dir->pair) == 0 &&
@@ -2011,8 +2027,9 @@ compact:
/// Top level directory operations ///
#ifndef LFS_READONLY
static int lfs_mkdirraw(lfs_t *lfs, const char *path) {
static int lfs_rawmkdir(lfs_t *lfs, const char *path) {
// deorphan if we haven't yet, needed at most once after poweron
LFS_ASSERT(path);
int err = lfs_fs_forceconsistency(lfs);
if (err) {
return err;
@@ -2101,7 +2118,7 @@ static int lfs_mkdirraw(lfs_t *lfs, const char *path) {
}
#endif
static int lfs_dir_openraw(lfs_t *lfs, lfs_dir_t *dir, const char *path) {
static int lfs_dir_rawopen(lfs_t *lfs, lfs_dir_t *dir, const char *path) {
lfs_stag_t tag = lfs_dir_find(lfs, &dir->m, &path, NULL);
if (tag < 0) {
return tag;
@@ -2145,14 +2162,14 @@ static int lfs_dir_openraw(lfs_t *lfs, lfs_dir_t *dir, const char *path) {
return 0;
}
static int lfs_dir_closeraw(lfs_t *lfs, lfs_dir_t *dir) {
static int lfs_dir_rawclose(lfs_t *lfs, lfs_dir_t *dir) {
// remove from list of mdirs
lfs_mlist_remove(lfs, (struct lfs_mlist *)dir);
return 0;
}
static int lfs_dir_readraw(lfs_t *lfs, lfs_dir_t *dir, struct lfs_info *info) {
static int lfs_dir_rawread(lfs_t *lfs, lfs_dir_t *dir, struct lfs_info *info) {
memset(info, 0, sizeof(*info));
// special offset for '.' and '..'
@@ -2197,9 +2214,9 @@ static int lfs_dir_readraw(lfs_t *lfs, lfs_dir_t *dir, struct lfs_info *info) {
return true;
}
static int lfs_dir_seekraw(lfs_t *lfs, lfs_dir_t *dir, lfs_off_t off) {
static int lfs_dir_rawseek(lfs_t *lfs, lfs_dir_t *dir, lfs_off_t off) {
// simply walk from head dir
int err = lfs_dir_rewindraw(lfs, dir);
int err = lfs_dir_rawrewind(lfs, dir);
if (err) {
return err;
}
@@ -2234,12 +2251,12 @@ static int lfs_dir_seekraw(lfs_t *lfs, lfs_dir_t *dir, lfs_off_t off) {
return 0;
}
static lfs_soff_t lfs_dir_tellraw(lfs_t *lfs, lfs_dir_t *dir) {
static lfs_soff_t lfs_dir_rawtell(lfs_t *lfs, lfs_dir_t *dir) {
(void)lfs;
return dir->pos;
}
static int lfs_dir_rewindraw(lfs_t *lfs, lfs_dir_t *dir) {
static int lfs_dir_rawrewind(lfs_t *lfs, lfs_dir_t *dir) {
// reload the head dir
int err = lfs_dir_fetch(lfs, &dir->m, dir->head);
if (err) {
@@ -2445,19 +2462,19 @@ static int lfs_ctz_traverse(lfs_t *lfs,
/// Top level file operations ///
static int lfs_file_opencfgraw(lfs_t *lfs, lfs_file_t *file,
static int lfs_file_rawopencfg(lfs_t *lfs, lfs_file_t *file,
const char *path, int flags,
const struct lfs_file_config *cfg) {
#ifndef LFS_READONLY
// deorphan if we haven't yet, needed at most once after poweron
if ((flags & LFS_O_WRONLY) == LFS_O_WRONLY) {
if (flags & LFS_O_WRONLY) {
int err = lfs_fs_forceconsistency(lfs);
if (err) {
return err;
}
}
#else
LFS_ASSERT((flags & LFS_O_RDONLY) == LFS_O_RDONLY);
LFS_ASSERT(flags & LFS_O_RDONLY);
#endif
// setup simple file details
@@ -2468,7 +2485,7 @@ static int lfs_file_opencfgraw(lfs_t *lfs, lfs_file_t *file,
file->off = 0;
file->cache.buffer = NULL;
// allocate entry for file if it doesn't exist
// find path
lfs_stag_t tag = lfs_dir_find(lfs, &file->m, &path, &file->id);
if (tag < 0 && !(tag == LFS_ERR_NOENT && file->id != 0x3ff)) {
err = tag;
@@ -2476,20 +2493,23 @@ static int lfs_file_opencfgraw(lfs_t *lfs, lfs_file_t *file,
}
// get id, add to list of mdirs to catch update changes
if (!(flags & LFS_O_SNAPSHOT)) {
file->type = LFS_TYPE_REG;
lfs_mlist_append(lfs, (struct lfs_mlist *)file);
#ifdef LFS_READONLY
if (tag == LFS_ERR_NOENT) {
err = LFS_ERR_NOENT;
goto cleanup;
#else
if (tag == LFS_ERR_NOENT) {
if (!(flags & LFS_O_CREAT)) {
err = LFS_ERR_NOENT;
goto cleanup;
}
#ifndef LFS_READONLY
if ((flags & LFS_O_CREAT) && (flags & LFS_O_SNAPSHOT) &&
(tag == LFS_ERR_NOENT || lfs_tag_type3(tag) != LFS_TYPE_REG)) {
// special case for "temporary" files
tag = LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, 0);
} else if ((flags & LFS_O_EXCL) && tag != LFS_ERR_NOENT) {
err = LFS_ERR_EXIST;
goto cleanup;
} else if ((flags & LFS_O_CREAT) && tag == LFS_ERR_NOENT) {
// allocate entry for file if it doesn't exist
LFS_ASSERT(path);
// check that name fits
lfs_size_t nlen = strlen(path);
if (nlen > lfs->name_max) {
@@ -2501,27 +2521,33 @@ static int lfs_file_opencfgraw(lfs_t *lfs, lfs_file_t *file,
err = lfs_dir_commit(lfs, &file->m, LFS_MKATTRS(
{LFS_MKTAG(LFS_TYPE_CREATE, file->id, 0), NULL},
{LFS_MKTAG(LFS_TYPE_REG, file->id, nlen), path},
{LFS_MKTAG(LFS_TYPE_INLINESTRUCT, file->id, 0), NULL}));
{LFS_MKTAG(LFS_TYPE_INLINESTRUCT, file->id, 0), NULL},
{LFS_MKTAG(LFS_FROM_USERATTRS, file->id,
file->cfg->attr_count), file->cfg->attrs}));
if (err) {
err = LFS_ERR_NAMETOOLONG;
goto cleanup;
}
tag = LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, 0);
} else if (flags & LFS_O_EXCL) {
err = LFS_ERR_EXIST;
} else /**/
#endif /**/
/*********/
/**/ if (tag == LFS_ERR_NOENT) {
err = LFS_ERR_NOENT;
goto cleanup;
#endif
} else if (lfs_tag_type3(tag) != LFS_TYPE_REG) {
err = LFS_ERR_ISDIR;
goto cleanup;
#ifndef LFS_READONLY
} else if (flags & LFS_O_TRUNC) {
// truncate if requested
tag = LFS_MKTAG(LFS_TYPE_INLINESTRUCT, file->id, 0);
file->flags |= LFS_F_DIRTY;
#endif
} else {
#ifndef LFS_READONLY
if (flags & LFS_O_TRUNC) {
// truncate if requested
// always mark dirty in case we have custom attributes
tag = LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, 0);
file->flags |= LFS_F_DIRTY;
} else /**/
#endif /*********/
/**/ {
// try to load what's on disk, if it's inlined we'll fix it later
tag = lfs_dir_get(lfs, &file->m, LFS_MKTAG(0x700, 0x3ff, 0),
LFS_MKTAG(LFS_TYPE_STRUCT, file->id, 8), &file->ctz);
@@ -2532,10 +2558,9 @@ static int lfs_file_opencfgraw(lfs_t *lfs, lfs_file_t *file,
lfs_ctz_fromle32(&file->ctz);
}
// fetch attrs
for (unsigned i = 0; i < file->cfg->attr_count; i++) {
// if opened for read / read-write operations
if ((file->flags & LFS_O_RDONLY) == LFS_O_RDONLY) {
// fetch attrs if opened for read / read-write operations
if (flags & LFS_O_RDONLY) {
for (lfs_size_t i = 0; i < file->cfg->attr_count; i++) {
lfs_stag_t res = lfs_dir_get(lfs, &file->m,
LFS_MKTAG(0x7ff, 0x3ff, 0),
LFS_MKTAG(LFS_TYPE_USERATTR + file->cfg->attrs[i].type,
@@ -2546,18 +2571,7 @@ static int lfs_file_opencfgraw(lfs_t *lfs, lfs_file_t *file,
goto cleanup;
}
}
#ifndef LFS_READONLY
// if opened for write / read-write operations
if ((file->flags & LFS_O_WRONLY) == LFS_O_WRONLY) {
if (file->cfg->attrs[i].size > lfs->attr_max) {
err = LFS_ERR_NOSPC;
goto cleanup;
}
file->flags |= LFS_F_DIRTY;
}
#endif
}
// allocate buffer if needed
@@ -2602,24 +2616,23 @@ static int lfs_file_opencfgraw(lfs_t *lfs, lfs_file_t *file,
cleanup:
// clean up lingering resources
#ifndef LFS_READONLY
file->flags |= LFS_F_ERRED;
file->flags |= LFS_F_ZOMBIE;
#endif
lfs_file_closeraw(lfs, file);
lfs_file_rawclose(lfs, file);
return err;
}
static int lfs_file_openraw(lfs_t *lfs, lfs_file_t *file,
static int lfs_file_rawopen(lfs_t *lfs, lfs_file_t *file,
const char *path, int flags) {
static const struct lfs_file_config defaults = {0};
int err = lfs_file_opencfgraw(lfs, file, path, flags, &defaults);
int err = lfs_file_rawopencfg(lfs, file, path, flags, &defaults);
return err;
}
static int lfs_file_closeraw(lfs_t *lfs, lfs_file_t *file) {
#ifndef LFS_READONLY
int err = lfs_file_syncraw(lfs, file);
#else
static int lfs_file_rawclose(lfs_t *lfs, lfs_file_t *file) {
int err = 0;
#ifndef LFS_READONLY
err = lfs_file_rawsync(lfs, file);
#endif
// remove from list of mdirs
@@ -2746,12 +2759,12 @@ static int lfs_file_flush(lfs_t *lfs, lfs_file_t *file) {
// copy over a byte at a time, leave it up to caching
// to make this efficient
uint8_t data;
lfs_ssize_t res = lfs_file_readraw(lfs, &orig, &data, 1);
lfs_ssize_t res = lfs_file_rawread(lfs, &orig, &data, 1);
if (res < 0) {
return res;
}
res = lfs_file_writeraw(lfs, file, &data, 1);
res = lfs_file_rawwrite(lfs, file, &data, 1);
if (res < 0) {
return res;
}
@@ -2800,18 +2813,22 @@ relocate:
#endif
#ifndef LFS_READONLY
static int lfs_file_syncraw(lfs_t *lfs, lfs_file_t *file) {
if (file->flags & LFS_F_ERRED) {
static int lfs_file_rawsync(lfs_t *lfs, lfs_file_t *file) {
if (file->flags & LFS_F_ZOMBIE) {
// it's not safe to do anything if our file errored
return 0;
}
int err = lfs_file_flush(lfs, file);
if (err) {
file->flags |= LFS_F_ERRED;
file->flags |= LFS_F_ZOMBIE;
return err;
}
if (file->flags & LFS_O_SNAPSHOT) {
// we do flush snapshot files, but we don't commit, so stop here
return 0;
}
if ((file->flags & LFS_F_DIRTY) &&
!lfs_pair_isnull(file->m.pair)) {
@@ -2841,10 +2858,41 @@ static int lfs_file_syncraw(lfs_t *lfs, lfs_file_t *file) {
{LFS_MKTAG(LFS_FROM_USERATTRS, file->id,
file->cfg->attr_count), file->cfg->attrs}));
if (err) {
file->flags |= LFS_F_ERRED;
file->flags |= LFS_F_ZOMBIE;
return err;
}
// update readable handles referencing this file device-side
for (lfs_file_t *f = (lfs_file_t*)lfs->mlist; f; f = f->next) {
if (f != file &&
f->type == LFS_TYPE_REG &&
lfs_pair_cmp(f->m.pair, file->m.pair) == 0 &&
f->id == file->id &&
// only readable handles because wronly files
// may reference attributes in ROM
(f->flags & LFS_O_RDONLY)) {
// sync disk structure
f->ctz = file->ctz;
// copying the cache is required for inline files
lfs_cache_copy(lfs, &f->cache, &file->cache);
// sync attrs
for (lfs_size_t i = 0; i < f->cfg->attr_count; i++) {
for (lfs_size_t j = 0; j < file->cfg->attr_count; j++) {
if (f->cfg->attrs[i].type == file->cfg->attrs[i].type) {
memcpy(f->cfg->attrs[i].buffer,
file->cfg->attrs[i].buffer,
lfs_min(
f->cfg->attrs[i].size,
file->cfg->attrs[i].size));
}
}
}
f->flags &= ~(LFS_F_DIRTY | LFS_F_WRITING | LFS_F_READING);
}
}
file->flags &= ~LFS_F_DIRTY;
}
@@ -2852,9 +2900,9 @@ static int lfs_file_syncraw(lfs_t *lfs, lfs_file_t *file) {
}
#endif
static lfs_ssize_t lfs_file_readraw(lfs_t *lfs, lfs_file_t *file,
static lfs_ssize_t lfs_file_rawread(lfs_t *lfs, lfs_file_t *file,
void *buffer, lfs_size_t size) {
LFS_ASSERT((file->flags & LFS_O_RDONLY) == LFS_O_RDONLY);
LFS_ASSERT(file->flags & LFS_O_RDONLY);
uint8_t *data = buffer;
lfs_size_t nsize = size;
@@ -2926,9 +2974,9 @@ static lfs_ssize_t lfs_file_readraw(lfs_t *lfs, lfs_file_t *file,
}
#ifndef LFS_READONLY
static lfs_ssize_t lfs_file_writeraw(lfs_t *lfs, lfs_file_t *file,
static lfs_ssize_t lfs_file_rawwrite(lfs_t *lfs, lfs_file_t *file,
const void *buffer, lfs_size_t size) {
LFS_ASSERT((file->flags & LFS_O_WRONLY) == LFS_O_WRONLY);
LFS_ASSERT(file->flags & LFS_O_WRONLY);
const uint8_t *data = buffer;
lfs_size_t nsize = size;
@@ -2956,7 +3004,7 @@ static lfs_ssize_t lfs_file_writeraw(lfs_t *lfs, lfs_file_t *file,
file->pos = file->ctz.size;
while (file->pos < pos) {
lfs_ssize_t res = lfs_file_writeraw(lfs, file, &(uint8_t){0}, 1);
lfs_ssize_t res = lfs_file_rawwrite(lfs, file, &(uint8_t){0}, 1);
if (res < 0) {
return res;
}
@@ -2970,7 +3018,7 @@ static lfs_ssize_t lfs_file_writeraw(lfs_t *lfs, lfs_file_t *file,
// inline file doesn't fit anymore
int err = lfs_file_outline(lfs, file);
if (err) {
file->flags |= LFS_F_ERRED;
file->flags |= LFS_F_ZOMBIE;
return err;
}
}
@@ -2986,7 +3034,7 @@ static lfs_ssize_t lfs_file_writeraw(lfs_t *lfs, lfs_file_t *file,
file->ctz.head, file->ctz.size,
file->pos-1, &file->block, &file->off);
if (err) {
file->flags |= LFS_F_ERRED;
file->flags |= LFS_F_ZOMBIE;
return err;
}
@@ -3000,7 +3048,7 @@ static lfs_ssize_t lfs_file_writeraw(lfs_t *lfs, lfs_file_t *file,
file->block, file->pos,
&file->block, &file->off);
if (err) {
file->flags |= LFS_F_ERRED;
file->flags |= LFS_F_ZOMBIE;
return err;
}
} else {
@@ -3020,7 +3068,7 @@ static lfs_ssize_t lfs_file_writeraw(lfs_t *lfs, lfs_file_t *file,
if (err == LFS_ERR_CORRUPT) {
goto relocate;
}
file->flags |= LFS_F_ERRED;
file->flags |= LFS_F_ZOMBIE;
return err;
}
@@ -3028,7 +3076,7 @@ static lfs_ssize_t lfs_file_writeraw(lfs_t *lfs, lfs_file_t *file,
relocate:
err = lfs_file_relocate(lfs, file);
if (err) {
file->flags |= LFS_F_ERRED;
file->flags |= LFS_F_ZOMBIE;
return err;
}
}
@@ -3041,12 +3089,12 @@ relocate:
lfs_alloc_ack(lfs);
}
file->flags &= ~LFS_F_ERRED;
file->flags &= ~LFS_F_ZOMBIE;
return size;
}
#endif
static lfs_soff_t lfs_file_seekraw(lfs_t *lfs, lfs_file_t *file,
static lfs_soff_t lfs_file_rawseek(lfs_t *lfs, lfs_file_t *file,
lfs_soff_t off, int whence) {
#ifndef LFS_READONLY
// write out everything beforehand, may be noop if rdonly
@@ -3077,15 +3125,15 @@ static lfs_soff_t lfs_file_seekraw(lfs_t *lfs, lfs_file_t *file,
}
#ifndef LFS_READONLY
static int lfs_file_truncateraw(lfs_t *lfs, lfs_file_t *file, lfs_off_t size) {
LFS_ASSERT((file->flags & LFS_O_WRONLY) == LFS_O_WRONLY);
static int lfs_file_rawtruncate(lfs_t *lfs, lfs_file_t *file, lfs_off_t size) {
LFS_ASSERT(file->flags & LFS_O_WRONLY);
if (size > LFS_FILE_MAX) {
return LFS_ERR_INVAL;
}
lfs_off_t pos = file->pos;
lfs_off_t oldsize = lfs_file_sizeraw(lfs, file);
lfs_off_t oldsize = lfs_file_rawsize(lfs, file);
if (size < oldsize) {
// need to flush since directly changing metadata
int err = lfs_file_flush(lfs, file);
@@ -3107,7 +3155,7 @@ static int lfs_file_truncateraw(lfs_t *lfs, lfs_file_t *file, lfs_off_t size) {
} else if (size > oldsize) {
// flush+seek if not already at end
if (file->pos != oldsize) {
lfs_soff_t res = lfs_file_seekraw(lfs, file, 0, LFS_SEEK_END);
lfs_soff_t res = lfs_file_rawseek(lfs, file, 0, LFS_SEEK_END);
if (res < 0) {
return (int)res;
}
@@ -3115,7 +3163,7 @@ static int lfs_file_truncateraw(lfs_t *lfs, lfs_file_t *file, lfs_off_t size) {
// fill with zeros
while (file->pos < size) {
lfs_ssize_t res = lfs_file_writeraw(lfs, file, &(uint8_t){0}, 1);
lfs_ssize_t res = lfs_file_rawwrite(lfs, file, &(uint8_t){0}, 1);
if (res < 0) {
return (int)res;
}
@@ -3123,7 +3171,7 @@ static int lfs_file_truncateraw(lfs_t *lfs, lfs_file_t *file, lfs_off_t size) {
}
// restore pos
lfs_soff_t res = lfs_file_seekraw(lfs, file, pos, LFS_SEEK_SET);
lfs_soff_t res = lfs_file_rawseek(lfs, file, pos, LFS_SEEK_SET);
if (res < 0) {
return (int)res;
}
@@ -3132,13 +3180,13 @@ static int lfs_file_truncateraw(lfs_t *lfs, lfs_file_t *file, lfs_off_t size) {
}
#endif
static lfs_soff_t lfs_file_tellraw(lfs_t *lfs, lfs_file_t *file) {
static lfs_soff_t lfs_file_rawtell(lfs_t *lfs, lfs_file_t *file) {
(void)lfs;
return file->pos;
}
static int lfs_file_rewindraw(lfs_t *lfs, lfs_file_t *file) {
lfs_soff_t res = lfs_file_seekraw(lfs, file, 0, LFS_SEEK_SET);
static int lfs_file_rawrewind(lfs_t *lfs, lfs_file_t *file) {
lfs_soff_t res = lfs_file_rawseek(lfs, file, 0, LFS_SEEK_SET);
if (res < 0) {
return (int)res;
}
@@ -3146,7 +3194,7 @@ static int lfs_file_rewindraw(lfs_t *lfs, lfs_file_t *file) {
return 0;
}
static lfs_soff_t lfs_file_sizeraw(lfs_t *lfs, lfs_file_t *file) {
static lfs_soff_t lfs_file_rawsize(lfs_t *lfs, lfs_file_t *file) {
(void)lfs;
#ifndef LFS_READONLY
@@ -3160,7 +3208,7 @@ static lfs_soff_t lfs_file_sizeraw(lfs_t *lfs, lfs_file_t *file) {
/// General fs operations ///
static int lfs_statraw(lfs_t *lfs, const char *path, struct lfs_info *info) {
static int lfs_rawstat(lfs_t *lfs, const char *path, struct lfs_info *info) {
lfs_mdir_t cwd;
lfs_stag_t tag = lfs_dir_find(lfs, &cwd, &path, NULL);
if (tag < 0) {
@@ -3171,7 +3219,7 @@ static int lfs_statraw(lfs_t *lfs, const char *path, struct lfs_info *info) {
}
#ifndef LFS_READONLY
static int lfs_removeraw(lfs_t *lfs, const char *path) {
static int lfs_rawremove(lfs_t *lfs, const char *path) {
// deorphan if we haven't yet, needed at most once after poweron
int err = lfs_fs_forceconsistency(lfs);
if (err) {
@@ -3244,7 +3292,7 @@ static int lfs_removeraw(lfs_t *lfs, const char *path) {
#endif
#ifndef LFS_READONLY
static int lfs_renameraw(lfs_t *lfs, const char *oldpath, const char *newpath) {
static int lfs_rawrename(lfs_t *lfs, const char *oldpath, const char *newpath) {
// deorphan if we haven't yet, needed at most once after poweron
int err = lfs_fs_forceconsistency(lfs);
if (err) {
@@ -3372,7 +3420,7 @@ static int lfs_renameraw(lfs_t *lfs, const char *oldpath, const char *newpath) {
}
#endif
static lfs_ssize_t lfs_getattrraw(lfs_t *lfs, const char *path,
static lfs_ssize_t lfs_rawgetattr(lfs_t *lfs, const char *path,
uint8_t type, void *buffer, lfs_size_t size) {
lfs_mdir_t cwd;
lfs_stag_t tag = lfs_dir_find(lfs, &cwd, &path, NULL);
@@ -3424,13 +3472,39 @@ static int lfs_commitattr(lfs_t *lfs, const char *path,
}
}
return lfs_dir_commit(lfs, &cwd, LFS_MKATTRS(
int err = lfs_dir_commit(lfs, &cwd, LFS_MKATTRS(
{LFS_MKTAG(LFS_TYPE_USERATTR + type, id, size), buffer}));
if (err) {
return err;
}
if (lfs_tag_type3(tag) == LFS_TYPE_REG && size != 0x3ff) {
// sync attrs with any files open for reading, this follows
// the behavior of lfs_file_sync with attributes
for (lfs_file_t *f = (lfs_file_t*)lfs->mlist; f; f = f->next) {
if (f->type == LFS_TYPE_REG &&
lfs_pair_cmp(f->m.pair, cwd.pair) == 0 &&
f->id == id &&
// only readable handles because wronly files
// may reference attributes in ROM
(f->flags & LFS_O_RDONLY)) {
// sync attrs
for (lfs_size_t i = 0; i < f->cfg->attr_count; i++) {
if (f->cfg->attrs[i].type == type) {
memcpy(f->cfg->attrs[i].buffer, buffer,
lfs_min(f->cfg->attrs[i].size, size));
}
}
}
}
}
return 0;
}
#endif
#ifndef LFS_READONLY
static int lfs_setattrraw(lfs_t *lfs, const char *path,
static int lfs_rawsetattr(lfs_t *lfs, const char *path,
uint8_t type, const void *buffer, lfs_size_t size) {
if (size > lfs->attr_max) {
return LFS_ERR_NOSPC;
@@ -3441,7 +3515,7 @@ static int lfs_setattrraw(lfs_t *lfs, const char *path,
#endif
#ifndef LFS_READONLY
static int lfs_removeattrraw(lfs_t *lfs, const char *path, uint8_t type) {
static int lfs_rawremoveattr(lfs_t *lfs, const char *path, uint8_t type) {
return lfs_commitattr(lfs, path, type, NULL, 0x3ff);
}
#endif
@@ -3573,7 +3647,7 @@ static int lfs_deinit(lfs_t *lfs) {
}
#ifndef LFS_READONLY
static int lfs_formatraw(lfs_t *lfs, const struct lfs_config *cfg) {
static int lfs_rawformat(lfs_t *lfs, const struct lfs_config *cfg) {
int err = 0;
{
err = lfs_init(lfs, cfg);
@@ -3638,7 +3712,7 @@ cleanup:
}
#endif
static int lfs_mountraw(lfs_t *lfs, const struct lfs_config *cfg) {
static int lfs_rawmount(lfs_t *lfs, const struct lfs_config *cfg) {
int err = lfs_init(lfs, cfg);
if (err) {
return err;
@@ -3761,17 +3835,17 @@ static int lfs_mountraw(lfs_t *lfs, const struct lfs_config *cfg) {
return 0;
cleanup:
lfs_unmountraw(lfs);
lfs_rawunmount(lfs);
return err;
}
static int lfs_unmountraw(lfs_t *lfs) {
static int lfs_rawunmount(lfs_t *lfs) {
return lfs_deinit(lfs);
}
/// Filesystem filesystem operations ///
int lfs_fs_traverseraw(lfs_t *lfs,
int lfs_fs_rawtraverse(lfs_t *lfs,
int (*cb)(void *data, lfs_block_t block), void *data,
bool includeorphans) {
// iterate over metadata pairs
@@ -4201,9 +4275,9 @@ static int lfs_fs_size_count(void *p, lfs_block_t block) {
return 0;
}
static lfs_ssize_t lfs_fs_sizeraw(lfs_t *lfs) {
static lfs_ssize_t lfs_fs_rawsize(lfs_t *lfs) {
lfs_size_t size = 0;
int err = lfs_fs_traverseraw(lfs, lfs_fs_size_count, &size, false);
int err = lfs_fs_rawtraverse(lfs, lfs_fs_size_count, &size, false);
if (err) {
return err;
}
@@ -4632,7 +4706,7 @@ static int lfs1_unmount(lfs_t *lfs) {
}
/// v1 migration ///
static int lfs_migrateraw(lfs_t *lfs, const struct lfs_config *cfg) {
static int lfs_rawmigrate(lfs_t *lfs, const struct lfs_config *cfg) {
struct lfs1 lfs1;
int err = lfs1_mount(lfs, &lfs1, cfg);
if (err) {
@@ -4895,7 +4969,7 @@ int lfs_format(lfs_t *lfs, const struct lfs_config *cfg) {
cfg->read_buffer, cfg->prog_buffer, cfg->lookahead_buffer,
cfg->name_max, cfg->file_max, cfg->attr_max);
err = lfs_formatraw(lfs, cfg);
err = lfs_rawformat(lfs, cfg);
LFS_TRACE("lfs_format -> %d", err);
LFS_UNLOCK(cfg);
@@ -4925,7 +4999,7 @@ int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) {
cfg->read_buffer, cfg->prog_buffer, cfg->lookahead_buffer,
cfg->name_max, cfg->file_max, cfg->attr_max);
err = lfs_mountraw(lfs, cfg);
err = lfs_rawmount(lfs, cfg);
LFS_TRACE("lfs_mount -> %d", err);
LFS_UNLOCK(cfg);
@@ -4939,7 +5013,7 @@ int lfs_unmount(lfs_t *lfs) {
}
LFS_TRACE("lfs_unmount(%p)", (void*)lfs);
err = lfs_unmountraw(lfs);
err = lfs_rawunmount(lfs);
LFS_TRACE("lfs_unmount -> %d", err);
LFS_UNLOCK(lfs->cfg);
@@ -4954,7 +5028,7 @@ int lfs_remove(lfs_t *lfs, const char *path) {
}
LFS_TRACE("lfs_remove(%p, \"%s\")", (void*)lfs, path);
err = lfs_removeraw(lfs, path);
err = lfs_rawremove(lfs, path);
LFS_TRACE("lfs_remove -> %d", err);
LFS_UNLOCK(lfs->cfg);
@@ -4970,7 +5044,7 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) {
}
LFS_TRACE("lfs_rename(%p, \"%s\", \"%s\")", (void*)lfs, oldpath, newpath);
err = lfs_renameraw(lfs, oldpath, newpath);
err = lfs_rawrename(lfs, oldpath, newpath);
LFS_TRACE("lfs_rename -> %d", err);
LFS_UNLOCK(lfs->cfg);
@@ -4985,7 +5059,7 @@ int lfs_stat(lfs_t *lfs, const char *path, struct lfs_info *info) {
}
LFS_TRACE("lfs_stat(%p, \"%s\", %p)", (void*)lfs, path, (void*)info);
err = lfs_statraw(lfs, path, info);
err = lfs_rawstat(lfs, path, info);
LFS_TRACE("lfs_stat -> %d", err);
LFS_UNLOCK(lfs->cfg);
@@ -5001,7 +5075,7 @@ lfs_ssize_t lfs_getattr(lfs_t *lfs, const char *path,
LFS_TRACE("lfs_getattr(%p, \"%s\", %"PRIu8", %p, %"PRIu32")",
(void*)lfs, path, type, buffer, size);
lfs_ssize_t res = lfs_getattrraw(lfs, path, type, buffer, size);
lfs_ssize_t res = lfs_rawgetattr(lfs, path, type, buffer, size);
LFS_TRACE("lfs_getattr -> %"PRId32, res);
LFS_UNLOCK(lfs->cfg);
@@ -5018,7 +5092,7 @@ int lfs_setattr(lfs_t *lfs, const char *path,
LFS_TRACE("lfs_setattr(%p, \"%s\", %"PRIu8", %p, %"PRIu32")",
(void*)lfs, path, type, buffer, size);
err = lfs_setattrraw(lfs, path, type, buffer, size);
err = lfs_rawsetattr(lfs, path, type, buffer, size);
LFS_TRACE("lfs_setattr -> %d", err);
LFS_UNLOCK(lfs->cfg);
@@ -5034,7 +5108,7 @@ int lfs_removeattr(lfs_t *lfs, const char *path, uint8_t type) {
}
LFS_TRACE("lfs_removeattr(%p, \"%s\", %"PRIu8")", (void*)lfs, path, type);
err = lfs_removeattrraw(lfs, path, type);
err = lfs_rawremoveattr(lfs, path, type);
LFS_TRACE("lfs_removeattr -> %d", err);
LFS_UNLOCK(lfs->cfg);
@@ -5051,7 +5125,7 @@ int lfs_file_open(lfs_t *lfs, lfs_file_t *file, const char *path, int flags) {
(void*)lfs, (void*)file, path, flags);
LFS_ASSERT(!lfs_mlist_isopen(lfs->mlist, (struct lfs_mlist*)file));
err = lfs_file_openraw(lfs, file, path, flags);
err = lfs_file_rawopen(lfs, file, path, flags);
LFS_TRACE("lfs_file_open -> %d", err);
LFS_UNLOCK(lfs->cfg);
@@ -5071,7 +5145,7 @@ int lfs_file_opencfg(lfs_t *lfs, lfs_file_t *file,
(void*)cfg, cfg->buffer, (void*)cfg->attrs, cfg->attr_count);
LFS_ASSERT(!lfs_mlist_isopen(lfs->mlist, (struct lfs_mlist*)file));
err = lfs_file_opencfgraw(lfs, file, path, flags, cfg);
err = lfs_file_rawopencfg(lfs, file, path, flags, cfg);
LFS_TRACE("lfs_file_opencfg -> %d", err);
LFS_UNLOCK(lfs->cfg);
@@ -5084,9 +5158,10 @@ int lfs_file_close(lfs_t *lfs, lfs_file_t *file) {
return err;
}
LFS_TRACE("lfs_file_close(%p, %p)", (void*)lfs, (void*)file);
LFS_ASSERT(lfs_mlist_isopen(lfs->mlist, (struct lfs_mlist*)file));
LFS_ASSERT((file->flags & LFS_O_SNAPSHOT) ||
lfs_mlist_isopen(lfs->mlist, (struct lfs_mlist*)file));
err = lfs_file_closeraw(lfs, file);
err = lfs_file_rawclose(lfs, file);
LFS_TRACE("lfs_file_close -> %d", err);
LFS_UNLOCK(lfs->cfg);
@@ -5100,9 +5175,10 @@ int lfs_file_sync(lfs_t *lfs, lfs_file_t *file) {
return err;
}
LFS_TRACE("lfs_file_sync(%p, %p)", (void*)lfs, (void*)file);
LFS_ASSERT(lfs_mlist_isopen(lfs->mlist, (struct lfs_mlist*)file));
LFS_ASSERT((file->flags & LFS_O_SNAPSHOT) ||
lfs_mlist_isopen(lfs->mlist, (struct lfs_mlist*)file));
err = lfs_file_syncraw(lfs, file);
err = lfs_file_rawsync(lfs, file);
LFS_TRACE("lfs_file_sync -> %d", err);
LFS_UNLOCK(lfs->cfg);
@@ -5118,9 +5194,10 @@ lfs_ssize_t lfs_file_read(lfs_t *lfs, lfs_file_t *file,
}
LFS_TRACE("lfs_file_read(%p, %p, %p, %"PRIu32")",
(void*)lfs, (void*)file, buffer, size);
LFS_ASSERT(lfs_mlist_isopen(lfs->mlist, (struct lfs_mlist*)file));
LFS_ASSERT((file->flags & LFS_O_SNAPSHOT) ||
lfs_mlist_isopen(lfs->mlist, (struct lfs_mlist*)file));
lfs_ssize_t res = lfs_file_readraw(lfs, file, buffer, size);
lfs_ssize_t res = lfs_file_rawread(lfs, file, buffer, size);
LFS_TRACE("lfs_file_read -> %"PRId32, res);
LFS_UNLOCK(lfs->cfg);
@@ -5136,9 +5213,10 @@ lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file,
}
LFS_TRACE("lfs_file_write(%p, %p, %p, %"PRIu32")",
(void*)lfs, (void*)file, buffer, size);
LFS_ASSERT(lfs_mlist_isopen(lfs->mlist, (struct lfs_mlist*)file));
LFS_ASSERT((file->flags & LFS_O_SNAPSHOT) ||
lfs_mlist_isopen(lfs->mlist, (struct lfs_mlist*)file));
lfs_ssize_t res = lfs_file_writeraw(lfs, file, buffer, size);
lfs_ssize_t res = lfs_file_rawwrite(lfs, file, buffer, size);
LFS_TRACE("lfs_file_write -> %"PRId32, res);
LFS_UNLOCK(lfs->cfg);
@@ -5154,9 +5232,10 @@ lfs_soff_t lfs_file_seek(lfs_t *lfs, lfs_file_t *file,
}
LFS_TRACE("lfs_file_seek(%p, %p, %"PRId32", %d)",
(void*)lfs, (void*)file, off, whence);
LFS_ASSERT(lfs_mlist_isopen(lfs->mlist, (struct lfs_mlist*)file));
LFS_ASSERT((file->flags & LFS_O_SNAPSHOT) ||
lfs_mlist_isopen(lfs->mlist, (struct lfs_mlist*)file));
lfs_soff_t res = lfs_file_seekraw(lfs, file, off, whence);
lfs_soff_t res = lfs_file_rawseek(lfs, file, off, whence);
LFS_TRACE("lfs_file_seek -> %"PRId32, res);
LFS_UNLOCK(lfs->cfg);
@@ -5171,9 +5250,10 @@ int lfs_file_truncate(lfs_t *lfs, lfs_file_t *file, lfs_off_t size) {
}
LFS_TRACE("lfs_file_truncate(%p, %p, %"PRIu32")",
(void*)lfs, (void*)file, size);
LFS_ASSERT(lfs_mlist_isopen(lfs->mlist, (struct lfs_mlist*)file));
LFS_ASSERT((file->flags & LFS_O_SNAPSHOT) ||
lfs_mlist_isopen(lfs->mlist, (struct lfs_mlist*)file));
err = lfs_file_truncateraw(lfs, file, size);
err = lfs_file_rawtruncate(lfs, file, size);
LFS_TRACE("lfs_file_truncate -> %d", err);
LFS_UNLOCK(lfs->cfg);
@@ -5187,9 +5267,10 @@ lfs_soff_t lfs_file_tell(lfs_t *lfs, lfs_file_t *file) {
return err;
}
LFS_TRACE("lfs_file_tell(%p, %p)", (void*)lfs, (void*)file);
LFS_ASSERT(lfs_mlist_isopen(lfs->mlist, (struct lfs_mlist*)file));
LFS_ASSERT((file->flags & LFS_O_SNAPSHOT) ||
lfs_mlist_isopen(lfs->mlist, (struct lfs_mlist*)file));
lfs_soff_t res = lfs_file_tellraw(lfs, file);
lfs_soff_t res = lfs_file_rawtell(lfs, file);
LFS_TRACE("lfs_file_tell -> %"PRId32, res);
LFS_UNLOCK(lfs->cfg);
@@ -5203,7 +5284,7 @@ int lfs_file_rewind(lfs_t *lfs, lfs_file_t *file) {
}
LFS_TRACE("lfs_file_rewind(%p, %p)", (void*)lfs, (void*)file);
err = lfs_file_rewindraw(lfs, file);
err = lfs_file_rawrewind(lfs, file);
LFS_TRACE("lfs_file_rewind -> %d", err);
LFS_UNLOCK(lfs->cfg);
@@ -5216,9 +5297,10 @@ lfs_soff_t lfs_file_size(lfs_t *lfs, lfs_file_t *file) {
return err;
}
LFS_TRACE("lfs_file_size(%p, %p)", (void*)lfs, (void*)file);
LFS_ASSERT(lfs_mlist_isopen(lfs->mlist, (struct lfs_mlist*)file));
LFS_ASSERT((file->flags & LFS_O_SNAPSHOT) ||
lfs_mlist_isopen(lfs->mlist, (struct lfs_mlist*)file));
lfs_soff_t res = lfs_file_sizeraw(lfs, file);
lfs_soff_t res = lfs_file_rawsize(lfs, file);
LFS_TRACE("lfs_file_size -> %"PRId32, res);
LFS_UNLOCK(lfs->cfg);
@@ -5233,7 +5315,7 @@ int lfs_mkdir(lfs_t *lfs, const char *path) {
}
LFS_TRACE("lfs_mkdir(%p, \"%s\")", (void*)lfs, path);
err = lfs_mkdirraw(lfs, path);
err = lfs_rawmkdir(lfs, path);
LFS_TRACE("lfs_mkdir -> %d", err);
LFS_UNLOCK(lfs->cfg);
@@ -5249,7 +5331,7 @@ int lfs_dir_open(lfs_t *lfs, lfs_dir_t *dir, const char *path) {
LFS_TRACE("lfs_dir_open(%p, %p, \"%s\")", (void*)lfs, (void*)dir, path);
LFS_ASSERT(!lfs_mlist_isopen(lfs->mlist, (struct lfs_mlist*)dir));
err = lfs_dir_openraw(lfs, dir, path);
err = lfs_dir_rawopen(lfs, dir, path);
LFS_TRACE("lfs_dir_open -> %d", err);
LFS_UNLOCK(lfs->cfg);
@@ -5263,7 +5345,7 @@ int lfs_dir_close(lfs_t *lfs, lfs_dir_t *dir) {
}
LFS_TRACE("lfs_dir_close(%p, %p)", (void*)lfs, (void*)dir);
err = lfs_dir_closeraw(lfs, dir);
err = lfs_dir_rawclose(lfs, dir);
LFS_TRACE("lfs_dir_close -> %d", err);
LFS_UNLOCK(lfs->cfg);
@@ -5278,7 +5360,7 @@ int lfs_dir_read(lfs_t *lfs, lfs_dir_t *dir, struct lfs_info *info) {
LFS_TRACE("lfs_dir_read(%p, %p, %p)",
(void*)lfs, (void*)dir, (void*)info);
err = lfs_dir_readraw(lfs, dir, info);
err = lfs_dir_rawread(lfs, dir, info);
LFS_TRACE("lfs_dir_read -> %d", err);
LFS_UNLOCK(lfs->cfg);
@@ -5293,7 +5375,7 @@ int lfs_dir_seek(lfs_t *lfs, lfs_dir_t *dir, lfs_off_t off) {
LFS_TRACE("lfs_dir_seek(%p, %p, %"PRIu32")",
(void*)lfs, (void*)dir, off);
err = lfs_dir_seekraw(lfs, dir, off);
err = lfs_dir_rawseek(lfs, dir, off);
LFS_TRACE("lfs_dir_seek -> %d", err);
LFS_UNLOCK(lfs->cfg);
@@ -5307,7 +5389,7 @@ lfs_soff_t lfs_dir_tell(lfs_t *lfs, lfs_dir_t *dir) {
}
LFS_TRACE("lfs_dir_tell(%p, %p)", (void*)lfs, (void*)dir);
lfs_soff_t res = lfs_dir_tellraw(lfs, dir);
lfs_soff_t res = lfs_dir_rawtell(lfs, dir);
LFS_TRACE("lfs_dir_tell -> %"PRId32, res);
LFS_UNLOCK(lfs->cfg);
@@ -5321,7 +5403,7 @@ int lfs_dir_rewind(lfs_t *lfs, lfs_dir_t *dir) {
}
LFS_TRACE("lfs_dir_rewind(%p, %p)", (void*)lfs, (void*)dir);
err = lfs_dir_rewindraw(lfs, dir);
err = lfs_dir_rawrewind(lfs, dir);
LFS_TRACE("lfs_dir_rewind -> %d", err);
LFS_UNLOCK(lfs->cfg);
@@ -5335,7 +5417,7 @@ lfs_ssize_t lfs_fs_size(lfs_t *lfs) {
}
LFS_TRACE("lfs_fs_size(%p)", (void*)lfs);
lfs_ssize_t res = lfs_fs_sizeraw(lfs);
lfs_ssize_t res = lfs_fs_rawsize(lfs);
LFS_TRACE("lfs_fs_size -> %"PRId32, res);
LFS_UNLOCK(lfs->cfg);
@@ -5350,7 +5432,7 @@ int lfs_fs_traverse(lfs_t *lfs, int (*cb)(void *, lfs_block_t), void *data) {
LFS_TRACE("lfs_fs_traverse(%p, %p, %p)",
(void*)lfs, (void*)(uintptr_t)cb, data);
err = lfs_fs_traverseraw(lfs, cb, data, true);
err = lfs_fs_rawtraverse(lfs, cb, data, true);
LFS_TRACE("lfs_fs_traverse -> %d", err);
LFS_UNLOCK(lfs->cfg);
@@ -5380,7 +5462,7 @@ int lfs_migrate(lfs_t *lfs, const struct lfs_config *cfg) {
cfg->read_buffer, cfg->prog_buffer, cfg->lookahead_buffer,
cfg->name_max, cfg->file_max, cfg->attr_max);
err = lfs_migrateraw(lfs, cfg);
err = lfs_rawmigrate(lfs, cfg);
LFS_TRACE("lfs_migrate -> %d", err);
LFS_UNLOCK(cfg);

32
lfs.h
View File

@@ -127,20 +127,24 @@ enum lfs_open_flags {
#ifndef LFS_READONLY
LFS_O_WRONLY = 2, // Open a file as write only
LFS_O_RDWR = 3, // Open a file as read and write
#endif
#ifndef LFS_READONLY
LFS_O_CREAT = 0x0100, // Create a file if it does not exist
LFS_O_EXCL = 0x0200, // Fail if a file already exists
LFS_O_TRUNC = 0x0400, // Truncate the existing file to zero size
LFS_O_APPEND = 0x0800, // Move to end of file on every write
LFS_O_SNAPSHOT = 0x1000, // Open a temporary snapshot, ignore changes
#endif
// internally used flags
#ifndef LFS_READONLY
LFS_F_DIRTY = 0x010000, // File does not match storage
LFS_F_WRITING = 0x020000, // File has been written since last flush
#endif
LFS_F_READING = 0x040000, // File has been read since last flush
LFS_F_READING = 0x020000, // File has been read since last flush
#ifndef LFS_READONLY
LFS_F_ERRED = 0x080000, // An error occurred during write
LFS_F_WRITING = 0x040000, // File has been written since last flush
LFS_F_ZOMBIE = 0x080000, // An error occurred during write
#endif
LFS_F_INLINE = 0x100000, // Currently inlined in directory entry
};
@@ -294,16 +298,15 @@ struct lfs_file_config {
void *buffer;
// Optional list of custom attributes related to the file. If the file
// is opened with read access, these attributes will be read from disk
// during the open call. If the file is opened with write access, the
// attributes will be written to disk every file sync or close. This
// write occurs atomically with update to the file's contents.
// is opened for reading, these attributes will be read from disk during
// open. If the file is open for writing, these attribute will be atomically
// written to disk when the file is written to disk. Note that these
// attributes are not written unless the file is modified.
//
// Custom attributes are uniquely identified by an 8-bit type and limited
// to LFS_ATTR_MAX bytes. When read, if the stored attribute is smaller
// than the buffer, it will be padded with zeros. If the stored attribute
// is larger, then it will be silently truncated. If the attribute is not
// found, it will be created implicitly.
// to LFS_ATTR_MAX bytes. If the stored attribute is larger than the
// provided buffer, it will be silently truncated. If no attribute is
// found, and the file is open for writing, it will be created implicitly.
struct lfs_attr *attrs;
// Number of custom attributes in the list
@@ -471,10 +474,9 @@ int lfs_stat(lfs_t *lfs, const char *path, struct lfs_info *info);
// Get a custom attribute
//
// Custom attributes are uniquely identified by an 8-bit type and limited
// to LFS_ATTR_MAX bytes. When read, if the stored attribute is smaller than
// the buffer, it will be padded with zeros. If the stored attribute is larger,
// then it will be silently truncated. If no attribute is found, the error
// LFS_ERR_NOATTR is returned and the buffer is filled with zeros.
// to LFS_ATTR_MAX bytes. If the stored attribute is larger than the
// provided buffer, it will be silently truncated. If no attribute is found,
// the error LFS_ERR_NOATTR is returned and the buffer is filled with zeros.
//
// Returns the size of the attribute, or a negative error code on failure.
// Note, the returned size is the size of the attribute on disk, irrespective

View File

@@ -16,41 +16,39 @@ code = '''
lfs_getattr(&lfs, "hello", 'A', buffer, 4) => 4;
lfs_getattr(&lfs, "hello", 'B', buffer+4, 6) => 6;
lfs_getattr(&lfs, "hello", 'C', buffer+10, 5) => 5;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "bbbbbb", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
assert(memcmp(buffer, "aaaa", 4) == 0);
assert(memcmp(buffer+4, "bbbbbb", 6) == 0);
assert(memcmp(buffer+10, "ccccc", 5) == 0);
lfs_setattr(&lfs, "hello", 'B', "", 0) => 0;
lfs_getattr(&lfs, "hello", 'A', buffer, 4) => 4;
lfs_getattr(&lfs, "hello", 'B', buffer+4, 6) => 0;
lfs_getattr(&lfs, "hello", 'C', buffer+10, 5) => 5;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "\0\0\0\0\0\0", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
assert(memcmp(buffer, "aaaa", 4) == 0);
assert(memcmp(buffer+10, "ccccc", 5) == 0);
lfs_removeattr(&lfs, "hello", 'B') => 0;
lfs_getattr(&lfs, "hello", 'A', buffer, 4) => 4;
lfs_getattr(&lfs, "hello", 'B', buffer+4, 6) => LFS_ERR_NOATTR;
lfs_getattr(&lfs, "hello", 'C', buffer+10, 5) => 5;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "\0\0\0\0\0\0", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
assert(memcmp(buffer, "aaaa", 4) == 0);
assert(memcmp(buffer+10, "ccccc", 5) == 0);
lfs_setattr(&lfs, "hello", 'B', "dddddd", 6) => 0;
lfs_getattr(&lfs, "hello", 'A', buffer, 4) => 4;
lfs_getattr(&lfs, "hello", 'B', buffer+4, 6) => 6;
lfs_getattr(&lfs, "hello", 'C', buffer+10, 5) => 5;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "dddddd", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
assert(memcmp(buffer, "aaaa", 4) == 0);
assert(memcmp(buffer+4, "dddddd", 6) == 0);
assert(memcmp(buffer+10, "ccccc", 5) == 0);
lfs_setattr(&lfs, "hello", 'B', "eee", 3) => 0;
lfs_getattr(&lfs, "hello", 'A', buffer, 4) => 4;
lfs_getattr(&lfs, "hello", 'B', buffer+4, 6) => 3;
lfs_getattr(&lfs, "hello", 'C', buffer+10, 5) => 5;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "eee\0\0\0", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
assert(memcmp(buffer, "aaaa", 4) == 0);
assert(memcmp(buffer+4, "eee", 3) == 0);
assert(memcmp(buffer+10, "ccccc", 5) == 0);
lfs_setattr(&lfs, "hello", 'A', buffer, LFS_ATTR_MAX+1) => LFS_ERR_NOSPC;
lfs_setattr(&lfs, "hello", 'B', "fffffffff", 9) => 0;
@@ -65,13 +63,13 @@ code = '''
lfs_getattr(&lfs, "hello", 'A', buffer, 4) => 4;
lfs_getattr(&lfs, "hello", 'B', buffer+4, 9) => 9;
lfs_getattr(&lfs, "hello", 'C', buffer+13, 5) => 5;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "fffffffff", 9) => 0;
memcmp(buffer+13, "ccccc", 5) => 0;
assert(memcmp(buffer, "aaaa", 4) == 0);
assert(memcmp(buffer+4, "fffffffff", 9) == 0);
assert(memcmp(buffer+13, "ccccc", 5) == 0);
lfs_file_open(&lfs, &file, "hello/hello", LFS_O_RDONLY) => 0;
lfs_file_read(&lfs, &file, buffer, sizeof(buffer)) => strlen("hello");
memcmp(buffer, "hello", strlen("hello")) => 0;
assert(memcmp(buffer, "hello", strlen("hello")) == 0);
lfs_file_close(&lfs, &file);
lfs_unmount(&lfs) => 0;
'''
@@ -94,41 +92,39 @@ code = '''
lfs_getattr(&lfs, "/", 'A', buffer, 4) => 4;
lfs_getattr(&lfs, "/", 'B', buffer+4, 6) => 6;
lfs_getattr(&lfs, "/", 'C', buffer+10, 5) => 5;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "bbbbbb", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
assert(memcmp(buffer, "aaaa", 4) == 0);
assert(memcmp(buffer+4, "bbbbbb", 6) == 0);
assert(memcmp(buffer+10, "ccccc", 5) == 0);
lfs_setattr(&lfs, "/", 'B', "", 0) => 0;
lfs_getattr(&lfs, "/", 'A', buffer, 4) => 4;
lfs_getattr(&lfs, "/", 'B', buffer+4, 6) => 0;
lfs_getattr(&lfs, "/", 'C', buffer+10, 5) => 5;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "\0\0\0\0\0\0", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
assert(memcmp(buffer, "aaaa", 4) == 0);
assert(memcmp(buffer+10, "ccccc", 5) == 0);
lfs_removeattr(&lfs, "/", 'B') => 0;
lfs_getattr(&lfs, "/", 'A', buffer, 4) => 4;
lfs_getattr(&lfs, "/", 'B', buffer+4, 6) => LFS_ERR_NOATTR;
lfs_getattr(&lfs, "/", 'C', buffer+10, 5) => 5;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "\0\0\0\0\0\0", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
assert(memcmp(buffer, "aaaa", 4) == 0);
assert(memcmp(buffer+10, "ccccc", 5) == 0);
lfs_setattr(&lfs, "/", 'B', "dddddd", 6) => 0;
lfs_getattr(&lfs, "/", 'A', buffer, 4) => 4;
lfs_getattr(&lfs, "/", 'B', buffer+4, 6) => 6;
lfs_getattr(&lfs, "/", 'C', buffer+10, 5) => 5;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "dddddd", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
assert(memcmp(buffer, "aaaa", 4) == 0);
assert(memcmp(buffer+4, "dddddd", 6) == 0);
assert(memcmp(buffer+10, "ccccc", 5) == 0);
lfs_setattr(&lfs, "/", 'B', "eee", 3) => 0;
lfs_getattr(&lfs, "/", 'A', buffer, 4) => 4;
lfs_getattr(&lfs, "/", 'B', buffer+4, 6) => 3;
lfs_getattr(&lfs, "/", 'C', buffer+10, 5) => 5;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "eee\0\0\0", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
assert(memcmp(buffer, "aaaa", 4) == 0);
assert(memcmp(buffer+4, "eee", 3) == 0);
assert(memcmp(buffer+10, "ccccc", 5) == 0);
lfs_setattr(&lfs, "/", 'A', buffer, LFS_ATTR_MAX+1) => LFS_ERR_NOSPC;
lfs_setattr(&lfs, "/", 'B', "fffffffff", 9) => 0;
@@ -142,13 +138,13 @@ code = '''
lfs_getattr(&lfs, "/", 'A', buffer, 4) => 4;
lfs_getattr(&lfs, "/", 'B', buffer+4, 9) => 9;
lfs_getattr(&lfs, "/", 'C', buffer+13, 5) => 5;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "fffffffff", 9) => 0;
memcmp(buffer+13, "ccccc", 5) => 0;
assert(memcmp(buffer, "aaaa", 4) == 0);
assert(memcmp(buffer+4, "fffffffff", 9) == 0);
assert(memcmp(buffer+13, "ccccc", 5) == 0);
lfs_file_open(&lfs, &file, "hello/hello", LFS_O_RDONLY) => 0;
lfs_file_read(&lfs, &file, buffer, sizeof(buffer)) => strlen("hello");
memcmp(buffer, "hello", strlen("hello")) => 0;
assert(memcmp(buffer, "hello", strlen("hello")) == 0);
lfs_file_close(&lfs, &file);
lfs_unmount(&lfs) => 0;
'''
@@ -176,52 +172,55 @@ code = '''
memcpy(buffer, "aaaa", 4);
memcpy(buffer+4, "bbbbbb", 6);
memcpy(buffer+10, "ccccc", 5);
lfs_file_write(&lfs, &file, "hi", 2) => 2;
lfs_file_close(&lfs, &file) => 0;
memset(buffer, 0, 15);
lfs_file_opencfg(&lfs, &file, "hello/hello", LFS_O_RDONLY, &cfg1) => 0;
lfs_file_close(&lfs, &file) => 0;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "bbbbbb", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
assert(memcmp(buffer, "aaaa", 4) == 0);
assert(memcmp(buffer+4, "bbbbbb", 6) == 0);
assert(memcmp(buffer+10, "ccccc", 5) == 0);
attrs1[1].size = 0;
lfs_file_opencfg(&lfs, &file, "hello/hello", LFS_O_WRONLY, &cfg1) => 0;
lfs_file_write(&lfs, &file, "hi", 2) => 2;
lfs_file_close(&lfs, &file) => 0;
memset(buffer, 0, 15);
attrs1[1].size = 6;
lfs_file_opencfg(&lfs, &file, "hello/hello", LFS_O_RDONLY, &cfg1) => 0;
lfs_file_close(&lfs, &file) => 0;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "\0\0\0\0\0\0", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
assert(memcmp(buffer, "aaaa", 4) == 0);
assert(memcmp(buffer+10, "ccccc", 5) == 0);
attrs1[1].size = 6;
lfs_file_opencfg(&lfs, &file, "hello/hello", LFS_O_WRONLY, &cfg1) => 0;
memcpy(buffer+4, "dddddd", 6);
lfs_file_write(&lfs, &file, "hi", 2) => 2;
lfs_file_close(&lfs, &file) => 0;
memset(buffer, 0, 15);
attrs1[1].size = 6;
lfs_file_opencfg(&lfs, &file, "hello/hello", LFS_O_RDONLY, &cfg1) => 0;
lfs_file_close(&lfs, &file) => 0;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "dddddd", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
assert(memcmp(buffer, "aaaa", 4) == 0);
assert(memcmp(buffer+4, "dddddd", 6) == 0);
assert(memcmp(buffer+10, "ccccc", 5) == 0);
attrs1[1].size = 3;
lfs_file_opencfg(&lfs, &file, "hello/hello", LFS_O_WRONLY, &cfg1) => 0;
memcpy(buffer+4, "eee", 3);
lfs_file_write(&lfs, &file, "hi", 2) => 2;
lfs_file_close(&lfs, &file) => 0;
memset(buffer, 0, 15);
attrs1[1].size = 6;
lfs_file_opencfg(&lfs, &file, "hello/hello", LFS_O_RDONLY, &cfg1) => 0;
lfs_file_close(&lfs, &file) => 0;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "eee\0\0\0", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
assert(memcmp(buffer, "aaaa", 4) == 0);
assert(memcmp(buffer+4, "eee", 3) == 0);
assert(memcmp(buffer+10, "ccccc", 5) == 0);
attrs1[0].size = LFS_ATTR_MAX+1;
lfs_file_opencfg(&lfs, &file, "hello/hello", LFS_O_WRONLY, &cfg1)
=> LFS_ERR_NOSPC;
lfs_file_opencfg(&lfs, &file, "hello/hello2",
LFS_O_WRONLY | LFS_O_CREAT, &cfg1) => LFS_ERR_NOSPC;
struct lfs_attr attrs2[] = {
{'A', buffer, 4},
@@ -231,6 +230,7 @@ code = '''
struct lfs_file_config cfg2 = {.attrs=attrs2, .attr_count=3};
lfs_file_opencfg(&lfs, &file, "hello/hello", LFS_O_RDWR, &cfg2) => 0;
memcpy(buffer+4, "fffffffff", 9);
lfs_file_write(&lfs, &file, "hi", 2) => 2;
lfs_file_close(&lfs, &file) => 0;
attrs1[0].size = 4;
lfs_file_opencfg(&lfs, &file, "hello/hello", LFS_O_RDONLY, &cfg1) => 0;
@@ -249,13 +249,13 @@ code = '''
lfs_file_opencfg(&lfs, &file, "hello/hello", LFS_O_RDONLY, &cfg3) => 0;
lfs_file_close(&lfs, &file) => 0;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "fffffffff", 9) => 0;
memcmp(buffer+13, "ccccc", 5) => 0;
assert(memcmp(buffer, "aaaa", 4) == 0);
assert(memcmp(buffer+4, "fffffffff", 9) == 0);
assert(memcmp(buffer+13, "ccccc", 5) == 0);
lfs_file_open(&lfs, &file, "hello/hello", LFS_O_RDONLY) => 0;
lfs_file_read(&lfs, &file, buffer, sizeof(buffer)) => strlen("hello");
memcmp(buffer, "hello", strlen("hello")) => 0;
assert(memcmp(buffer, "hillo", strlen("hello")) == 0);
lfs_file_close(&lfs, &file);
lfs_unmount(&lfs) => 0;
'''
@@ -287,17 +287,16 @@ code = '''
lfs_getattr(&lfs, "hello/hello", 'B', buffer, 9) => 9;
lfs_getattr(&lfs, "hello/hello", 'C', buffer+9, 9) => 5;
lfs_getattr(&lfs, "hello/hello", 'D', buffer+18, 9) => LFS_ERR_NOATTR;
memcmp(buffer, "fffffffff", 9) => 0;
memcmp(buffer+9, "ccccc\0\0\0\0", 9) => 0;
memcmp(buffer+18, "\0\0\0\0\0\0\0\0\0", 9) => 0;
assert(memcmp(buffer, "fffffffff", 9) == 0);
assert(memcmp(buffer+9, "ccccc", 5) == 0);
lfs_file_write(&lfs, &file, "hi", 2) => 2;
lfs_file_sync(&lfs, &file) => 0;
lfs_getattr(&lfs, "hello/hello", 'B', buffer, 9) => 4;
lfs_getattr(&lfs, "hello/hello", 'C', buffer+9, 9) => 0;
lfs_getattr(&lfs, "hello/hello", 'D', buffer+18, 9) => 4;
memcmp(buffer, "gggg\0\0\0\0\0", 9) => 0;
memcmp(buffer+9, "\0\0\0\0\0\0\0\0\0", 9) => 0;
memcmp(buffer+18, "hhhh\0\0\0\0\0", 9) => 0;
assert(memcmp(buffer, "gggg", 4) == 0);
assert(memcmp(buffer+18, "hhhh", 4) == 0);
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;

View File

@@ -242,3 +242,695 @@ code = '''
lfs_unmount(&lfs) => 0;
'''
[[case]] # open same file reading from separate file handles
define.READERS = 3
define.SIZE = [10, 100, 1000, 10000]
define.RDMODE = ['LFS_O_RDONLY', 'LFS_O_RDWR']
code = '''
const char alphas[] = "abcdefghijklmnopqrstuvwxyz";
lfs_format(&lfs, &cfg) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file, "shared", LFS_O_CREAT | LFS_O_WRONLY) => 0;
for (int j = 0; j < SIZE; j++) {
lfs_file_write(&lfs, &file, &alphas[j % 26], 1) => 1;
}
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
// open all files
lfs_mount(&lfs, &cfg) => 0;
lfs_file_t readers[READERS];
for (int i = 0; i < READERS; i++) {
lfs_file_open(&lfs, &readers[i], "shared", RDMODE) => 0;
}
// perform operations while all readers are open
for (int i = 0; i < READERS; i++) {
for (int j = 0; j < SIZE; j++) {
lfs_file_read(&lfs, &readers[i], buffer, 1) => 1;
assert(buffer[0] == alphas[j % 26]);
}
}
for (int i = 0; i < READERS; i++) {
lfs_file_close(&lfs, &readers[i]) => 0;
}
lfs_unmount(&lfs) => 0;
'''
[[case]] # open same file reading and writing from separate file handles
define.READERS = 3
define.SIZE = [10, 100, 1000, 10000]
define.RDMODE = ['LFS_O_RDONLY', 'LFS_O_RDWR']
define.WRMODE = ['LFS_O_WRONLY', 'LFS_O_RDWR']
code = '''
const char alphas[] = "abcdefghijklmnopqrstuvwxyz";
const char nums[] = "0123456789";
lfs_format(&lfs, &cfg) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file, "shared", LFS_O_CREAT | LFS_O_WRONLY) => 0;
for (int j = 0; j < SIZE; j++) {
lfs_file_write(&lfs, &file, &alphas[j % 26], 1) => 1;
}
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
// open all files
lfs_mount(&lfs, &cfg) => 0;
lfs_file_t writer;
lfs_file_t readers[READERS];
lfs_file_open(&lfs, &writer, "shared", WRMODE) => 0;
for (int i = 0; i < READERS; i++) {
lfs_file_open(&lfs, &readers[i], "shared", RDMODE) => 0;
}
// perform operations while all readers are open
for (int j = 0; j < SIZE; j++) {
lfs_file_write(&lfs, &writer, &nums[j % 10], 1) => 1;
}
for (int i = 0; i < READERS; i++) {
for (int j = 0; j < SIZE/2; j++) {
lfs_file_read(&lfs, &readers[i], buffer, 1) => 1;
assert(buffer[0] == alphas[j % 26]);
}
}
// sync, now write should reflect in all open files
lfs_file_sync(&lfs, &writer) => 0;
for (int i = 0; i < READERS; i++) {
for (int j = SIZE/2; j < SIZE; j++) {
lfs_file_read(&lfs, &readers[i], buffer, 1) => 1;
assert(buffer[0] == nums[j % 10]);
}
}
// double check our writer reflects its own changes
if (WRMODE == LFS_O_RDWR) {
lfs_file_rewind(&lfs, &writer) => 0;
for (int j = 0; j < SIZE; j++) {
lfs_file_read(&lfs, &writer, buffer, 1) => 1;
assert(buffer[0] == nums[j % 10]);
}
}
for (int i = 0; i < READERS; i++) {
lfs_file_close(&lfs, &readers[i]) => 0;
}
lfs_unmount(&lfs) => 0;
'''
[[case]] # check that attributes are updated in open files
define.READERS = 3
define.SIZE = 10
define.RDMODE = ['LFS_O_RDONLY', 'LFS_O_RDWR']
define.WRMODE = ['LFS_O_WRONLY', 'LFS_O_RDWR']
code = '''
const char alphas[] = "abcdefghijklmnopqrstuvwxyz";
const char nums[] = "0123456789";
lfs_format(&lfs, &cfg) => 0;
lfs_mount(&lfs, &cfg) => 0;
const struct lfs_file_config filecfg = {
.attr_count = 3,
.attrs = (struct lfs_attr[]){
{'A', "a", 1},
{'B', "bb", 2},
{'C', "ccc", 3},
},
};
lfs_file_opencfg(&lfs, &file, "shared",
LFS_O_CREAT | LFS_O_WRONLY, &filecfg) => 0;
for (int j = 0; j < SIZE; j++) {
lfs_file_write(&lfs, &file, &alphas[j % 26], 1) => 1;
}
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
// open all files
lfs_mount(&lfs, &cfg) => 0;
lfs_file_t writer;
const struct lfs_file_config writercfg = {
.attr_count = 3,
.attrs = (struct lfs_attr[]){
{'A', &(uint8_t[1]){0}, 1},
{'B', &(uint8_t[2]){0}, 2},
{'C', &(uint8_t[3]){0}, 3}}};
lfs_file_t readers[READERS];
const struct lfs_file_config readercfgs[READERS] = {
{ .attr_count = 3,
.attrs = (struct lfs_attr[]){
{'A', &(uint8_t[1]){0}, 1},
{'B', &(uint8_t[2]){0}, 2},
{'C', &(uint8_t[3]){0}, 3}}},
{ .attr_count = 3,
.attrs = (struct lfs_attr[]){
{'A', &(uint8_t[1]){0}, 1},
{'B', &(uint8_t[2]){0}, 2},
{'C', &(uint8_t[3]){0}, 3}}},
{ .attr_count = 3,
.attrs = (struct lfs_attr[]){
{'A', &(uint8_t[1]){0}, 1},
{'B', &(uint8_t[2]){0}, 2},
{'C', &(uint8_t[3]){0}, 3}}}};
lfs_file_opencfg(&lfs, &writer, "shared",
WRMODE, &writercfg) => 0;
for (int i = 0; i < READERS; i++) {
lfs_file_opencfg(&lfs, &readers[i], "shared",
RDMODE, &readercfgs[i]) => 0;
}
// perform operations while all readers are open
writercfg.attrs[0].size = 1;
memcpy(writercfg.attrs[0].buffer, "0", 1);
writercfg.attrs[1].size = 2;
memcpy(writercfg.attrs[1].buffer, "11", 2);
writercfg.attrs[2].size = 3;
memcpy(writercfg.attrs[2].buffer, "222", 3);
for (int j = 0; j < SIZE; j++) {
lfs_file_write(&lfs, &writer, &nums[j % 10], 1) => 1;
}
for (int i = 0; i < READERS; i++) {
assert(readercfgs[i].attrs[0].size == 1);
assert(memcmp(readercfgs[i].attrs[0].buffer, "a", 1) == 0);
assert(readercfgs[i].attrs[1].size == 2);
assert(memcmp(readercfgs[i].attrs[1].buffer, "bb", 2) == 0);
assert(readercfgs[i].attrs[2].size == 3);
assert(memcmp(readercfgs[i].attrs[2].buffer, "ccc", 3) == 0);
for (int j = 0; j < SIZE; j++) {
lfs_file_read(&lfs, &readers[i], buffer, 1) => 1;
assert(buffer[0] == alphas[j % 26]);
}
}
// sync, now write should reflect in all open files
lfs_file_sync(&lfs, &writer) => 0;
for (int i = 0; i < READERS; i++) {
assert(readercfgs[i].attrs[0].size == 1);
assert(memcmp(readercfgs[i].attrs[0].buffer, "0", 1) == 0);
assert(readercfgs[i].attrs[1].size == 2);
assert(memcmp(readercfgs[i].attrs[1].buffer, "11", 2) == 0);
assert(readercfgs[i].attrs[2].size == 3);
assert(memcmp(readercfgs[i].attrs[2].buffer, "222", 3) == 0);
lfs_file_rewind(&lfs, &readers[i]) => 0;
for (int j = 0; j < SIZE; j++) {
lfs_file_read(&lfs, &readers[i], buffer, 1) => 1;
assert(buffer[0] == nums[j % 10]);
}
}
// double check our writer reflects its own changes
if (WRMODE == LFS_O_RDWR) {
assert(writercfg.attrs[0].size == 1);
assert(memcmp(writercfg.attrs[0].buffer, "0", 1) == 0);
assert(writercfg.attrs[1].size == 2);
assert(memcmp(writercfg.attrs[1].buffer, "11", 2) == 0);
assert(writercfg.attrs[2].size == 3);
assert(memcmp(writercfg.attrs[2].buffer, "222", 3) == 0);
lfs_file_rewind(&lfs, &writer) => 0;
for (int j = 0; j < SIZE; j++) {
lfs_file_read(&lfs, &writer, buffer, 1) => 1;
assert(buffer[0] == nums[j % 10]);
}
}
// now try explicit lfs_setattr calls, this should still update open files
lfs_setattr(&lfs, "shared", 'A', "A", 1) => 0;
lfs_setattr(&lfs, "shared", 'B', "BB", 2) => 0;
lfs_setattr(&lfs, "shared", 'C', "CCC", 3) => 0;
for (int i = 0; i < READERS; i++) {
assert(readercfgs[i].attrs[0].size == 1);
assert(memcmp(readercfgs[i].attrs[0].buffer, "A", 1) == 0);
assert(readercfgs[i].attrs[1].size == 2);
assert(memcmp(readercfgs[i].attrs[1].buffer, "BB", 2) == 0);
assert(readercfgs[i].attrs[2].size == 3);
assert(memcmp(readercfgs[i].attrs[2].buffer, "CCC", 3) == 0);
lfs_file_rewind(&lfs, &readers[i]) => 0;
for (int j = 0; j < SIZE; j++) {
lfs_file_read(&lfs, &readers[i], buffer, 1) => 1;
assert(buffer[0] == nums[j % 10]);
}
}
if (WRMODE == LFS_O_RDWR) {
assert(writercfg.attrs[0].size == 1);
assert(memcmp(writercfg.attrs[0].buffer, "A", 1) == 0);
assert(writercfg.attrs[1].size == 2);
assert(memcmp(writercfg.attrs[1].buffer, "BB", 2) == 0);
assert(writercfg.attrs[2].size == 3);
assert(memcmp(writercfg.attrs[2].buffer, "CCC", 3) == 0);
lfs_file_rewind(&lfs, &writer) => 0;
for (int j = 0; j < SIZE; j++) {
lfs_file_read(&lfs, &writer, buffer, 1) => 1;
assert(buffer[0] == nums[j % 10]);
}
} else if (WRMODE == LFS_O_WRONLY) {
// this should NOT update wronly attributes, these may be
// stored in read-only memory
assert(writercfg.attrs[0].size == 1);
assert(memcmp(writercfg.attrs[0].buffer, "0", 1) == 0);
assert(writercfg.attrs[1].size == 2);
assert(memcmp(writercfg.attrs[1].buffer, "11", 2) == 0);
assert(writercfg.attrs[2].size == 3);
assert(memcmp(writercfg.attrs[2].buffer, "222", 3) == 0);
}
for (int i = 0; i < READERS; i++) {
lfs_file_close(&lfs, &readers[i]) => 0;
}
lfs_unmount(&lfs) => 0;
'''
[[case]] # simple snapshot for reading
define.SIZE = [10, 100, 1000, 10000]
code = '''
const char alphas[] = "abcdefghijklmnopqrstuvwxyz";
const char nums[] = "0123456789";
lfs_format(&lfs, &cfg) => 0;
lfs_mount(&lfs, &cfg) => 0;
const struct lfs_file_config filecfg = {
.attr_count = 1,
.attrs = (struct lfs_attr[]){
{'A', "abcd", 4},
},
};
lfs_file_opencfg(&lfs, &file, "open_me",
LFS_O_CREAT | LFS_O_WRONLY, &filecfg) => 0;
for (int j = 0; j < SIZE; j++) {
lfs_file_write(&lfs, &file, &alphas[j % 26], 1) => 1;
}
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
// open reader/writer/snapshot
lfs_mount(&lfs, &cfg) => 0;
lfs_file_t reader;
const struct lfs_file_config readercfg = {
.attr_count = 1,
.attrs = (struct lfs_attr[]){
{'A', &(uint8_t[4]){0}, 4}
},
};
lfs_file_t writer;
const struct lfs_file_config writercfg = {
.attr_count = 1,
.attrs = (struct lfs_attr[]){
{'A', &(uint8_t[4]){0}, 4}
},
};
lfs_file_t snapshot;
const struct lfs_file_config snapshotcfg = {
.attr_count = 1,
.attrs = (struct lfs_attr[]){
{'A', &(uint8_t[4]){0}, 4}
},
};
lfs_file_opencfg(&lfs, &reader, "open_me",
LFS_O_RDONLY, &readercfg) => 0;
lfs_file_opencfg(&lfs, &writer, "open_me",
LFS_O_WRONLY, &writercfg) => 0;
lfs_file_opencfg(&lfs, &snapshot, "open_me",
LFS_O_RDONLY | LFS_O_SNAPSHOT, &snapshotcfg) => 0;
assert(memcmp(readercfg.attrs[0].buffer, "abcd", 4) == 0);
for (int j = 0; j < SIZE/2; j++) {
lfs_file_read(&lfs, &reader, buffer, 1) => 1;
assert(buffer[0] == alphas[j % 26]);
}
assert(memcmp(snapshotcfg.attrs[0].buffer, "abcd", 4) == 0);
for (int j = 0; j < SIZE/2; j++) {
lfs_file_read(&lfs, &snapshot, buffer, 1) => 1;
assert(buffer[0] == alphas[j % 26]);
}
// write file
for (int j = 0; j < SIZE; j++) {
lfs_file_write(&lfs, &writer, &nums[j % 10], 1) => 1;
}
memcpy(writercfg.attrs[0].buffer, "0123", 4);
lfs_file_sync(&lfs, &writer) => 0;
// reader should change
assert(memcmp(readercfg.attrs[0].buffer, "0123", 4) == 0);
for (int j = SIZE/2; j < SIZE; j++) {
lfs_file_read(&lfs, &reader, buffer, 1) => 1;
assert(buffer[0] == nums[j % 10]);
}
// snapshot should remain unchanged
assert(memcmp(snapshotcfg.attrs[0].buffer, "abcd", 4) == 0);
for (int j = SIZE/2; j < SIZE; j++) {
lfs_file_read(&lfs, &snapshot, buffer, 1) => 1;
assert(buffer[0] == alphas[j % 26]);
}
lfs_file_close(&lfs, &reader) => 0;
lfs_file_close(&lfs, &writer) => 0;
lfs_file_close(&lfs, &snapshot) => 0;
// disk should change
lfs_file_opencfg(&lfs, &reader, "open_me",
LFS_O_RDONLY, &readercfg) => 0;
assert(memcmp(readercfg.attrs[0].buffer, "0123", 4) == 0);
for (int j = 0; j < SIZE; j++) {
lfs_file_read(&lfs, &reader, buffer, 1) => 1;
assert(buffer[0] == nums[j % 10]);
}
lfs_file_close(&lfs, &reader) => 0;
lfs_unmount(&lfs) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_file_opencfg(&lfs, &reader, "open_me",
LFS_O_RDONLY, &readercfg) => 0;
assert(memcmp(readercfg.attrs[0].buffer, "0123", 4) == 0);
for (int j = 0; j < SIZE; j++) {
lfs_file_read(&lfs, &reader, buffer, 1) => 1;
assert(buffer[0] == nums[j % 10]);
}
lfs_file_close(&lfs, &reader) => 0;
lfs_unmount(&lfs) => 0;
'''
[[case]] # simple snapshot for writing
define.SIZE = [10, 100, 1000, 10000]
code = '''
const char alphas[] = "abcdefghijklmnopqrstuvwxyz";
const char nums[] = "0123456789";
lfs_format(&lfs, &cfg) => 0;
lfs_mount(&lfs, &cfg) => 0;
const struct lfs_file_config filecfg = {
.attr_count = 1,
.attrs = (struct lfs_attr[]){
{'A', "abcd", 4},
},
};
lfs_file_opencfg(&lfs, &file, "open_me",
LFS_O_CREAT | LFS_O_WRONLY, &filecfg) => 0;
for (int j = 0; j < SIZE; j++) {
lfs_file_write(&lfs, &file, &alphas[j % 26], 1) => 1;
}
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
// open reader/snapshot
lfs_mount(&lfs, &cfg) => 0;
lfs_file_t reader;
const struct lfs_file_config readercfg = {
.attr_count = 1,
.attrs = (struct lfs_attr[]){
{'A', &(uint8_t[4]){0}, 4}
},
};
lfs_file_t snapshot;
const struct lfs_file_config snapshotcfg = {
.attr_count = 1,
.attrs = (struct lfs_attr[]){
{'A', &(uint8_t[4]){0}, 4}
},
};
lfs_file_opencfg(&lfs, &reader, "open_me",
LFS_O_RDONLY, &readercfg) => 0;
lfs_file_opencfg(&lfs, &snapshot, "open_me",
LFS_O_RDWR | LFS_O_SNAPSHOT, &snapshotcfg) => 0;
assert(memcmp(readercfg.attrs[0].buffer, "abcd", 4) == 0);
for (int j = 0; j < SIZE/2; j++) {
lfs_file_read(&lfs, &reader, buffer, 1) => 1;
assert(buffer[0] == alphas[j % 26]);
}
assert(memcmp(snapshotcfg.attrs[0].buffer, "abcd", 4) == 0);
for (int j = 0; j < SIZE; j++) {
lfs_file_read(&lfs, &snapshot, buffer, 1) => 1;
assert(buffer[0] == alphas[j % 26]);
}
// modify snapshot
lfs_file_rewind(&lfs, &snapshot) => 0;
for (int j = 0; j < SIZE; j++) {
lfs_file_write(&lfs, &snapshot, &nums[j % 10], 1) => 1;
}
memcpy(snapshotcfg.attrs[0].buffer, "0123", 4);
lfs_file_rewind(&lfs, &snapshot) => 0;
for (int j = 0; j < SIZE; j++) {
lfs_file_read(&lfs, &snapshot, buffer, 1) => 1;
assert(buffer[0] == nums[j % 10]);
}
lfs_file_sync(&lfs, &snapshot) => 0;
// reader should not change
assert(memcmp(readercfg.attrs[0].buffer, "abcd", 4) == 0);
for (int j = SIZE/2; j < SIZE; j++) {
lfs_file_read(&lfs, &reader, buffer, 1) => 1;
assert(buffer[0] == alphas[j % 26]);
}
// snapshot should changed
assert(memcmp(snapshotcfg.attrs[0].buffer, "0123", 4) == 0);
lfs_file_rewind(&lfs, &snapshot) => 0;
for (int j = 0; j < SIZE; j++) {
lfs_file_read(&lfs, &snapshot, buffer, 1) => 1;
assert(buffer[0] == nums[j % 10]);
}
lfs_file_close(&lfs, &reader) => 0;
lfs_file_close(&lfs, &snapshot) => 0;
// disk should not change
lfs_file_opencfg(&lfs, &reader, "open_me",
LFS_O_RDONLY, &readercfg) => 0;
assert(memcmp(readercfg.attrs[0].buffer, "abcd", 4) == 0);
for (int j = 0; j < SIZE; j++) {
lfs_file_read(&lfs, &reader, buffer, 1) => 1;
assert(buffer[0] == alphas[j % 26]);
}
lfs_file_close(&lfs, &reader) => 0;
lfs_unmount(&lfs) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_file_opencfg(&lfs, &reader, "open_me",
LFS_O_RDONLY, &readercfg) => 0;
assert(memcmp(readercfg.attrs[0].buffer, "abcd", 4) == 0);
for (int j = 0; j < SIZE; j++) {
lfs_file_read(&lfs, &reader, buffer, 1) => 1;
assert(buffer[0] == alphas[j % 26]);
}
lfs_file_close(&lfs, &reader) => 0;
lfs_unmount(&lfs) => 0;
'''
[[case]] # temporary files
define.SIZE = [10, 100, 1000, 10000]
define.TMP_PATH = 'range(4)'
code = '''
const char alphas[] = "abcdefghijklmnopqrstuvwxyz";
const char nums[] = "0123456789";
const char caps[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
lfs_format(&lfs, &cfg) => 0;
lfs_mount(&lfs, &cfg) => 0;
const struct lfs_file_config filecfg = {
.attr_count = 1,
.attrs = (struct lfs_attr[]){
{'A', "abcd", 4},
},
};
lfs_file_opencfg(&lfs, &file, "open_me",
LFS_O_CREAT | LFS_O_WRONLY, &filecfg) => 0;
for (int j = 0; j < SIZE; j++) {
lfs_file_write(&lfs, &file, &alphas[j % 26], 1) => 1;
}
lfs_file_close(&lfs, &file) => 0;
lfs_file_opencfg(&lfs, &file, "dont_open_me",
LFS_O_CREAT | LFS_O_WRONLY, &filecfg) => 0;
for (int j = 0; j < SIZE; j++) {
lfs_file_write(&lfs, &file, &alphas[j % 26], 1) => 1;
}
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
// open reader/writer/temp
lfs_mount(&lfs, &cfg) => 0;
lfs_file_t reader;
const struct lfs_file_config readercfg = {
.attr_count = 1,
.attrs = (struct lfs_attr[]){
{'A', &(uint8_t[4]){0}, 4}
},
};
lfs_file_t writer;
const struct lfs_file_config writercfg = {
.attr_count = 1,
.attrs = (struct lfs_attr[]){
{'A', &(uint8_t[4]){0}, 4}
},
};
lfs_file_t tmp;
const struct lfs_file_config tmpcfg = {
.attr_count = 1,
.attrs = (struct lfs_attr[]){
{'A', &(uint8_t[4]){0}, 4}
},
};
lfs_file_opencfg(&lfs, &reader, "open_me",
LFS_O_RDONLY, &readercfg) => 0;
lfs_file_opencfg(&lfs, &writer, "open_me",
LFS_O_WRONLY, &writercfg) => 0;
const char *tmp_paths[] = {NULL, "/", "/tmp", "/open_me.tmp"};
lfs_file_opencfg(&lfs, &tmp, tmp_paths[TMP_PATH],
LFS_O_RDWR | LFS_O_CREAT | LFS_O_SNAPSHOT, &tmpcfg) => 0;
assert(memcmp(readercfg.attrs[0].buffer, "abcd", 4) == 0);
for (int j = 0; j < SIZE/3; j++) {
lfs_file_read(&lfs, &reader, buffer, 1) => 1;
assert(buffer[0] == alphas[j % 26]);
}
assert(memcmp(tmpcfg.attrs[0].buffer, "\0\0\0\0", 4) == 0);
assert(lfs_file_size(&lfs, &tmp) == 0);
// write to tmp
for (int j = 0; j < SIZE; j++) {
lfs_file_write(&lfs, &tmp, &nums[j % 10], 1) => 1;
}
memcpy(tmpcfg.attrs[0].buffer, "0123", 4);
lfs_file_rewind(&lfs, &tmp) => 0;
for (int j = 0; j < SIZE; j++) {
lfs_file_read(&lfs, &tmp, buffer, 1) => 1;
assert(buffer[0] == nums[j % 10]);
}
lfs_file_sync(&lfs, &tmp) => 0;
// reader should not change
assert(memcmp(readercfg.attrs[0].buffer, "abcd", 4) == 0);
for (int j = SIZE/3; j < 2*SIZE/3; j++) {
lfs_file_read(&lfs, &reader, buffer, 1) => 1;
assert(buffer[0] == alphas[j % 26]);
}
// tmp should change
assert(memcmp(tmpcfg.attrs[0].buffer, "0123", 4) == 0);
lfs_file_rewind(&lfs, &tmp) => 0;
for (int j = 0; j < SIZE/2; j++) {
lfs_file_read(&lfs, &tmp, buffer, 1) => 1;
assert(buffer[0] == nums[j % 10]);
}
// write to file
for (int j = 0; j < SIZE; j++) {
lfs_file_write(&lfs, &writer, &caps[j % 26], 1) => 1;
}
memcpy(writercfg.attrs[0].buffer, "ABCD", 4);
lfs_file_sync(&lfs, &writer) => 0;
// reader should change
assert(memcmp(readercfg.attrs[0].buffer, "ABCD", 4) == 0);
for (int j = 2*SIZE/3; j < SIZE; j++) {
lfs_file_read(&lfs, &reader, buffer, 1) => 1;
assert(buffer[0] == caps[j % 26]);
}
// tmp should not change
assert(memcmp(tmpcfg.attrs[0].buffer, "0123", 4) == 0);
for (int j = SIZE/2; j < SIZE; j++) {
lfs_file_read(&lfs, &tmp, buffer, 1) => 1;
assert(buffer[0] == nums[j % 10]);
}
lfs_file_close(&lfs, &reader) => 0;
lfs_file_close(&lfs, &writer) => 0;
lfs_file_close(&lfs, &tmp) => 0;
// tmp should not appear on disk
lfs_dir_open(&lfs, &dir, "/") => 0;
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(info.type == LFS_TYPE_DIR);
assert(strcmp(info.name, ".") == 0);
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(info.type == LFS_TYPE_DIR);
assert(strcmp(info.name, "..") == 0);
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(info.type == LFS_TYPE_REG);
assert(strcmp(info.name, "dont_open_me") == 0);
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(info.type == LFS_TYPE_REG);
assert(strcmp(info.name, "open_me") == 0);
lfs_dir_read(&lfs, &dir, &info) => 0;
lfs_dir_close(&lfs, &dir) => 0;
lfs_file_opencfg(&lfs, &reader, "open_me",
LFS_O_RDONLY, &readercfg) => 0;
assert(memcmp(readercfg.attrs[0].buffer, "ABCD", 4) == 0);
for (int j = 0; j < SIZE; j++) {
lfs_file_read(&lfs, &reader, buffer, 1) => 1;
assert(buffer[0] == caps[j % 26]);
}
lfs_file_close(&lfs, &reader) => 0;
lfs_file_opencfg(&lfs, &reader, "dont_open_me",
LFS_O_RDONLY, &readercfg) => 0;
assert(memcmp(readercfg.attrs[0].buffer, "abcd", 4) == 0);
for (int j = 0; j < SIZE; j++) {
lfs_file_read(&lfs, &reader, buffer, 1) => 1;
assert(buffer[0] == alphas[j % 26]);
}
lfs_file_close(&lfs, &reader) => 0;
lfs_unmount(&lfs) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_dir_open(&lfs, &dir, "/") => 0;
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(info.type == LFS_TYPE_DIR);
assert(strcmp(info.name, ".") == 0);
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(info.type == LFS_TYPE_DIR);
assert(strcmp(info.name, "..") == 0);
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(info.type == LFS_TYPE_REG);
assert(strcmp(info.name, "dont_open_me") == 0);
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(info.type == LFS_TYPE_REG);
assert(strcmp(info.name, "open_me") == 0);
lfs_dir_read(&lfs, &dir, &info) => 0;
lfs_dir_close(&lfs, &dir) => 0;
lfs_file_opencfg(&lfs, &reader, "open_me",
LFS_O_RDONLY, &readercfg) => 0;
assert(memcmp(readercfg.attrs[0].buffer, "ABCD", 4) == 0);
for (int j = 0; j < SIZE; j++) {
lfs_file_read(&lfs, &reader, buffer, 1) => 1;
assert(buffer[0] == caps[j % 26]);
}
lfs_file_close(&lfs, &reader) => 0;
lfs_file_opencfg(&lfs, &reader, "dont_open_me",
LFS_O_RDONLY, &readercfg) => 0;
assert(memcmp(readercfg.attrs[0].buffer, "abcd", 4) == 0);
for (int j = 0; j < SIZE; j++) {
lfs_file_read(&lfs, &reader, buffer, 1) => 1;
assert(buffer[0] == alphas[j % 26]);
}
lfs_file_close(&lfs, &reader) => 0;
lfs_unmount(&lfs) => 0;
'''
[[case]] # test snapshot open errors
code = '''
lfs_format(&lfs, &cfg) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file, NULL,
LFS_O_RDWR | LFS_O_SNAPSHOT) => LFS_ERR_ISDIR;
lfs_file_open(&lfs, &file, "/",
LFS_O_RDWR | LFS_O_SNAPSHOT) => LFS_ERR_ISDIR;
lfs_file_open(&lfs, &file, "/tmp",
LFS_O_RDWR | LFS_O_SNAPSHOT) => LFS_ERR_NOENT;
lfs_file_open(&lfs, &file, "/tmp/",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_SNAPSHOT) => LFS_ERR_NOENT;
lfs_file_open(&lfs, &file, "/tmp/tmp",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_SNAPSHOT) => LFS_ERR_NOENT;
lfs_unmount(&lfs) => 0;
'''

View File

@@ -95,9 +95,9 @@ code = '''
lfs_mkdir(&lfs, "coffee/../milk") => 0;
lfs_stat(&lfs, "coffee/../milk", &info) => 0;
strcmp(info.name, "milk") => 0;
assert(strcmp(info.name, "milk") == 0);
lfs_stat(&lfs, "milk", &info) => 0;
strcmp(info.name, "milk") => 0;
assert(strcmp(info.name, "milk") == 0);
lfs_unmount(&lfs) => 0;
'''
@@ -129,9 +129,9 @@ code = '''
lfs_mount(&lfs, &cfg) => 0;
lfs_mkdir(&lfs, ".milk") => 0;
lfs_stat(&lfs, ".milk", &info) => 0;
strcmp(info.name, ".milk") => 0;
assert(strcmp(info.name, ".milk") == 0);
lfs_stat(&lfs, "tea/.././.milk", &info) => 0;
strcmp(info.name, ".milk") => 0;
assert(strcmp(info.name, ".milk") == 0);
lfs_unmount(&lfs) => 0;
'''
@@ -149,13 +149,13 @@ code = '''
lfs_mkdir(&lfs, "coffee/coldcoffee") => 0;
lfs_stat(&lfs, "coffee/../../../../../../tea/hottea", &info) => 0;
strcmp(info.name, "hottea") => 0;
assert(strcmp(info.name, "hottea") == 0);
lfs_mkdir(&lfs, "coffee/../../../../../../milk") => 0;
lfs_stat(&lfs, "coffee/../../../../../../milk", &info) => 0;
strcmp(info.name, "milk") => 0;
assert(strcmp(info.name, "milk") == 0);
lfs_stat(&lfs, "milk", &info) => 0;
strcmp(info.name, "milk") => 0;
assert(strcmp(info.name, "milk") == 0);
lfs_unmount(&lfs) => 0;
'''