diff --git a/lfs.c b/lfs.c index d7439fe3..f582902e 100644 --- a/lfs.c +++ b/lfs.c @@ -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, @@ -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) { + 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) { @@ -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 @@ -1821,7 +1837,7 @@ static int lfs_dir_compact(lfs_t *lfs, #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 && @@ -2013,6 +2029,7 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir, #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; @@ -2450,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 @@ -2468,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; @@ -2476,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); @@ -2501,63 +2521,57 @@ static int lfs_file_rawopencfg(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 { - // 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 (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) { - 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; } - - file->flags |= LFS_F_DIRTY; + 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 @@ -2602,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; @@ -2616,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 @@ -2801,17 +2814,21 @@ static int lfs_file_flush(lfs_t *lfs, lfs_file_t *file) { #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)) { @@ -2841,10 +2858,41 @@ 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; } + // 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; } @@ -2854,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; @@ -2928,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; @@ -2970,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; } } @@ -2986,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; } @@ -3000,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 { @@ -3020,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; } @@ -3028,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; } } @@ -3041,7 +3089,7 @@ static lfs_ssize_t lfs_file_rawwrite(lfs_t *lfs, lfs_file_t *file, lfs_alloc_ack(lfs); } - file->flags &= ~LFS_F_ERRED; + file->flags &= ~LFS_F_ZOMBIE; return size; } #endif @@ -3078,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; @@ -3424,8 +3472,34 @@ 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 @@ -3829,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]); @@ -5084,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); @@ -5100,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); @@ -5118,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); @@ -5136,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); @@ -5154,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); @@ -5171,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); @@ -5187,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); @@ -5216,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); diff --git a/lfs.h b/lfs.h index 3b02b6a7..8a8fa416 100644 --- a/lfs.h +++ b/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 @@ -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 diff --git a/tests/test_attrs.toml b/tests/test_attrs.toml index db8d0c7e..dad40606 100644 --- a/tests/test_attrs.toml +++ b/tests/test_attrs.toml @@ -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; diff --git a/tests/test_interspersed.toml b/tests/test_interspersed.toml index 87a05780..9d35dfbc 100644 --- a/tests/test_interspersed.toml +++ b/tests/test_interspersed.toml @@ -1,7 +1,7 @@ [[case]] # interspersed file test define.SIZE = [10, 100] -define.FILES = [4, 10, 26] +define.FILES = [4, 10, 26] code = ''' lfs_file_t files[FILES]; const char alphas[] = "abcdefghijklmnopqrstuvwxyz"; @@ -55,7 +55,7 @@ code = ''' for (int j = 0; j < FILES; j++) { lfs_file_close(&lfs, &files[j]); } - + lfs_unmount(&lfs) => 0; ''' @@ -108,7 +108,7 @@ code = ''' assert(buffer[0] == '~'); } lfs_file_close(&lfs, &file); - + lfs_unmount(&lfs) => 0; ''' @@ -168,13 +168,13 @@ code = ''' } lfs_file_close(&lfs, &files[0]); lfs_file_close(&lfs, &files[1]); - + lfs_unmount(&lfs) => 0; ''' [[case]] # reentrant interspersed file test define.SIZE = [10, 100] -define.FILES = [4, 10, 26] +define.FILES = [4, 10, 26] reentrant = true code = ''' lfs_file_t files[FILES]; @@ -239,6 +239,698 @@ code = ''' for (int j = 0; j < FILES; j++) { lfs_file_close(&lfs, &files[j]); } - + + 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; ''' diff --git a/tests/test_paths.toml b/tests/test_paths.toml index a7474c0b..f1ab65ae 100644 --- a/tests/test_paths.toml +++ b/tests/test_paths.toml @@ -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; '''