mirror of
https://github.com/eledio-devices/thirdparty-littlefs.git
synced 2025-10-30 16:15:40 +01:00
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.
This commit is contained in:
184
lfs.c
184
lfs.c
@@ -821,8 +821,12 @@ static int lfs_dir_traverse(lfs_t *lfs,
|
||||
return err;
|
||||
}
|
||||
} else if (lfs_tag_type3(tag) == LFS_FROM_USERATTRS) {
|
||||
const struct lfs_attr *a = buffer;
|
||||
for (unsigned i = 0; i < lfs_tag_size(tag); i++) {
|
||||
const struct lfs_attr *a = buffer;
|
||||
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) {
|
||||
@@ -1180,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
|
||||
@@ -2020,6 +2029,7 @@ compact:
|
||||
#ifndef LFS_READONLY
|
||||
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;
|
||||
@@ -2457,14 +2467,14 @@ static int lfs_file_rawopencfg(lfs_t *lfs, lfs_file_t *file,
|
||||
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
|
||||
@@ -2475,7 +2485,7 @@ static int lfs_file_rawopencfg(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;
|
||||
@@ -2483,19 +2493,22 @@ static int lfs_file_rawopencfg(lfs_t *lfs, lfs_file_t *file,
|
||||
}
|
||||
|
||||
// get id, add to list of mdirs to catch update changes
|
||||
file->type = LFS_TYPE_REG;
|
||||
lfs_mlist_append(lfs, (struct lfs_mlist *)file);
|
||||
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;
|
||||
#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 (tag == LFS_ERR_NOENT) {
|
||||
if (!(flags & LFS_O_CREAT)) {
|
||||
err = LFS_ERR_NOENT;
|
||||
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);
|
||||
@@ -2512,60 +2525,53 @@ static int lfs_file_rawopencfg(lfs_t *lfs, lfs_file_t *file,
|
||||
{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
|
||||
// always mark dirty in case we have custom attributes
|
||||
tag = LFS_MKTAG(LFS_TYPE_INLINESTRUCT, file->id, 0);
|
||||
file->flags |= LFS_F_DIRTY;
|
||||
#endif
|
||||
} else {
|
||||
// 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);
|
||||
if (tag < 0) {
|
||||
err = tag;
|
||||
goto cleanup;
|
||||
}
|
||||
lfs_ctz_fromle32(&file->ctz);
|
||||
}
|
||||
|
||||
// fetch attrs
|
||||
for (lfs_size_t i = 0; i < file->cfg->attr_count; i++) {
|
||||
// if opened for read / read-write operations
|
||||
if ((file->flags & LFS_O_RDONLY) == LFS_O_RDONLY) {
|
||||
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,
|
||||
file->id, file->cfg->attrs[i].size),
|
||||
file->cfg->attrs[i].buffer);
|
||||
if (res < 0 && res != LFS_ERR_NOENT) {
|
||||
err = res;
|
||||
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;
|
||||
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);
|
||||
if (tag < 0) {
|
||||
err = tag;
|
||||
goto cleanup;
|
||||
}
|
||||
lfs_ctz_fromle32(&file->ctz);
|
||||
}
|
||||
|
||||
// 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,
|
||||
file->id, file->cfg->attrs[i].size),
|
||||
file->cfg->attrs[i].buffer);
|
||||
if (res < 0 && res != LFS_ERR_NOENT) {
|
||||
err = res;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// allocate buffer if needed
|
||||
@@ -2610,7 +2616,7 @@ static int lfs_file_rawopencfg(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_rawclose(lfs, file);
|
||||
return err;
|
||||
@@ -2624,10 +2630,9 @@ static int lfs_file_rawopen(lfs_t *lfs, lfs_file_t *file,
|
||||
}
|
||||
|
||||
static int lfs_file_rawclose(lfs_t *lfs, lfs_file_t *file) {
|
||||
#ifndef LFS_READONLY
|
||||
int err = lfs_file_rawsync(lfs, file);
|
||||
#else
|
||||
int err = 0;
|
||||
#ifndef LFS_READONLY
|
||||
err = lfs_file_rawsync(lfs, file);
|
||||
#endif
|
||||
|
||||
// remove from list of mdirs
|
||||
@@ -2809,17 +2814,22 @@ relocate:
|
||||
|
||||
#ifndef LFS_READONLY
|
||||
static int lfs_file_rawsync(lfs_t *lfs, lfs_file_t *file) {
|
||||
if (file->flags & LFS_F_ERRED) {
|
||||
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)) {
|
||||
// update dir entry
|
||||
@@ -2848,7 +2858,7 @@ static int lfs_file_rawsync(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;
|
||||
}
|
||||
|
||||
@@ -2860,7 +2870,7 @@ static int lfs_file_rawsync(lfs_t *lfs, lfs_file_t *file) {
|
||||
f->id == file->id &&
|
||||
// only readable handles because wronly files
|
||||
// may reference attributes in ROM
|
||||
(f->flags & LFS_O_RDONLY) == LFS_O_RDONLY) {
|
||||
(f->flags & LFS_O_RDONLY)) {
|
||||
// sync disk structure
|
||||
f->ctz = file->ctz;
|
||||
// copying the cache is required for inline files
|
||||
@@ -2892,7 +2902,7 @@ static int lfs_file_rawsync(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;
|
||||
@@ -2966,7 +2976,7 @@ static lfs_ssize_t lfs_file_rawread(lfs_t *lfs, lfs_file_t *file,
|
||||
#ifndef LFS_READONLY
|
||||
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;
|
||||
@@ -3008,7 +3018,7 @@ static lfs_ssize_t lfs_file_rawwrite(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;
|
||||
}
|
||||
}
|
||||
@@ -3024,7 +3034,7 @@ static lfs_ssize_t lfs_file_rawwrite(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;
|
||||
}
|
||||
|
||||
@@ -3038,7 +3048,7 @@ static lfs_ssize_t lfs_file_rawwrite(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 {
|
||||
@@ -3058,7 +3068,7 @@ static lfs_ssize_t lfs_file_rawwrite(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;
|
||||
}
|
||||
|
||||
@@ -3066,7 +3076,7 @@ static lfs_ssize_t lfs_file_rawwrite(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;
|
||||
}
|
||||
}
|
||||
@@ -3079,7 +3089,7 @@ relocate:
|
||||
lfs_alloc_ack(lfs);
|
||||
}
|
||||
|
||||
file->flags &= ~LFS_F_ERRED;
|
||||
file->flags &= ~LFS_F_ZOMBIE;
|
||||
return size;
|
||||
}
|
||||
#endif
|
||||
@@ -3116,7 +3126,7 @@ static lfs_soff_t lfs_file_rawseek(lfs_t *lfs, lfs_file_t *file,
|
||||
|
||||
#ifndef LFS_READONLY
|
||||
static int lfs_file_rawtruncate(lfs_t *lfs, lfs_file_t *file, lfs_off_t size) {
|
||||
LFS_ASSERT((file->flags & LFS_O_WRONLY) == LFS_O_WRONLY);
|
||||
LFS_ASSERT(file->flags & LFS_O_WRONLY);
|
||||
|
||||
if (size > LFS_FILE_MAX) {
|
||||
return LFS_ERR_INVAL;
|
||||
@@ -3477,7 +3487,7 @@ static int lfs_commitattr(lfs_t *lfs, const char *path,
|
||||
f->id == id &&
|
||||
// only readable handles because wronly files
|
||||
// may reference attributes in ROM
|
||||
(f->flags & LFS_O_RDONLY) == LFS_O_RDONLY) {
|
||||
(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) {
|
||||
@@ -3893,7 +3903,7 @@ int lfs_fs_rawtraverse(lfs_t *lfs,
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
} else if (includeorphans &&
|
||||
} else if (includeorphans &&
|
||||
lfs_tag_type3(tag) == LFS_TYPE_DIRSTRUCT) {
|
||||
for (int i = 0; i < 2; i++) {
|
||||
err = cb(data, (&ctz.head)[i]);
|
||||
@@ -5148,7 +5158,8 @@ 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_rawclose(lfs, file);
|
||||
|
||||
@@ -5164,7 +5175,8 @@ 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_rawsync(lfs, file);
|
||||
|
||||
@@ -5182,7 +5194,8 @@ 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_rawread(lfs, file, buffer, size);
|
||||
|
||||
@@ -5200,7 +5213,8 @@ 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_rawwrite(lfs, file, buffer, size);
|
||||
|
||||
@@ -5218,7 +5232,8 @@ 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_rawseek(lfs, file, off, whence);
|
||||
|
||||
@@ -5235,7 +5250,8 @@ 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_rawtruncate(lfs, file, size);
|
||||
|
||||
@@ -5251,7 +5267,8 @@ 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_rawtell(lfs, file);
|
||||
|
||||
@@ -5280,7 +5297,8 @@ 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_rawsize(lfs, file);
|
||||
|
||||
|
||||
28
lfs.h
28
lfs.h
@@ -123,26 +123,30 @@ enum lfs_type {
|
||||
// File open flags
|
||||
enum lfs_open_flags {
|
||||
// open flags
|
||||
LFS_O_RDONLY = 1, // Open a file as read only
|
||||
LFS_O_RDONLY = 1, // Open a file as read only
|
||||
#ifndef LFS_READONLY
|
||||
LFS_O_WRONLY = 2, // Open a file as write only
|
||||
LFS_O_RDWR = 3, // Open a file as read and write
|
||||
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_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
|
||||
LFS_F_DIRTY = 0x010000, // File does not match storage
|
||||
#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
|
||||
LFS_F_INLINE = 0x100000, // Currently inlined in directory entry
|
||||
};
|
||||
|
||||
// File seek flags
|
||||
|
||||
@@ -219,8 +219,8 @@ code = '''
|
||||
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},
|
||||
|
||||
@@ -504,3 +504,433 @@ code = '''
|
||||
}
|
||||
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;
|
||||
'''
|
||||
|
||||
Reference in New Issue
Block a user