Compare commits

...

9 Commits

Author SHA1 Message Date
Christopher Haster
494dd6673d Merge pull request #263 from rojer/wundef
Fix build with -Wundef
2019-08-08 18:50:40 -05:00
Christopher Haster
fce2569005 Merge pull request #257 from pabigot/pr/20190803a
fix seek position corruption in truncate function
2019-08-08 18:50:28 -05:00
Christopher Haster
9d1f1211a9 Merge pull request #253 from pabigot/pr/20190730a
lfs: correct alignment restriction on lookahead buffer
2019-08-08 18:50:15 -05:00
Christopher Haster
151104c790 Changed CI to create release note for patches
This is a result of feedback that the current release notes made it too
difficult to see what changes happened on patch releases. From my
experience as well it became difficult to chase down which release a
commit landed on.

The risk is that this creates additional noise, both for the release
page and for user notifications. I am open to feedback if this causes a
problem.

Other tweaks on the CI side, these came from iteration with the same
scheme for coru and equeue:

- Changed version branch updates to be atomic (vN and vN-prefix). This
  makes it a bit easier to fix if one of the pushes fails due to a rogue
  branch with the same name.

- Added GEKY_BOT_DRAFT as a CI macro that can optionally switch between
  only creating drafts or immediately posting a release. The default is
  what I will be trying with littlefs which is to draft minor/major
  releases, but automatically create patch release.

  The real benefit of automatic releases is to use on tiny repos that
  don't really have an active maintainer. Though this is definitely no
  longer the case with littlefs, and I'm happy it has gained this much
  attention.
2019-08-08 18:50:00 -05:00
Deomid "rojer" Ryabkov
303ffb2da4 Fix build with -Wundef
Part of https://github.com/mongoose-os-libs/vfs-fs-lfs/issues/2
2019-08-08 16:54:34 +01:00
Peter A. Bigot
5bf71fa43e lfs: do not reposition seek pointer on truncate
When using lfs_file_truncate() to make a file shorter the file block and
off were incorrectly positioned at the new end, resulting in invalid
data accessed when reading.  Lift the seek pointer restoration to apply
to both increasing and reducing truncates.

Signed-off-by: Peter A. Bigot <pab@pabigot.com>
2019-08-03 17:17:49 -05:00
Peter A. Bigot
55fb1416c7 lfs: initialize file offs field
The uninitialized value creates confusion when diagnosing anomalies.

Signed-off-by: Peter A. Bigot <pab@pabigot.com>
2019-08-03 09:59:27 -05:00
Peter A. Bigot
dc031ce1d9 lfs: use meaningful names for magic block identifiers
The difference between 0xffffffff and 0xfffffffe is too subtle.  Use
names that reflect what the value represents.

Signed-off-by: Peter A. Bigot <pab@pabigot.com>
2019-08-03 09:59:07 -05:00
Peter A. Bigot
f85ff1d2f8 lfs: correct alignment restriction on lookahead buffer
The buffer need only be 32-bit aligned.

Signed-off-by: Peter A. Bigot <pab@pabigot.com>
2019-07-30 20:02:42 -05:00
4 changed files with 118 additions and 69 deletions

View File

@@ -248,34 +248,30 @@ jobs:
-m "Generated v$LFS_VERSION_MAJOR prefixes")
git reset --hard
# Update major version branches (vN and vN-prefix)
git push https://$GEKY_BOT_RELEASES@github.com/$TRAVIS_REPO_SLUG.git \
git push --atomic https://$GEKY_BOT_RELEASES@github.com/$TRAVIS_REPO_SLUG.git \
v$LFS_VERSION_MAJOR \
v$LFS_VERSION_MAJOR-prefix
# Create patch version tag (vN.N.N)
curl -f -u "$GEKY_BOT_RELEASES" -X POST \
https://api.github.com/repos/$TRAVIS_REPO_SLUG/git/refs \
-d "{
\"ref\": \"refs/tags/$LFS_VERSION\",
\"sha\": \"$TRAVIS_COMMIT\"
}"
# Create minor release?
[[ "$LFS_VERSION" == *.0 ]] || exit 0
# Build release notes
PREV=$(git tag --sort=-v:refname -l "v*.0" | head -1)
PREV=$(git tag --sort=-v:refname -l "v*" | head -1)
if [ ! -z "$PREV" ]
then
echo "PREV $PREV"
CHANGES=$'### Changes\n\n'$( \
git log --oneline $PREV.. --grep='^Merge' --invert-grep)
CHANGES=$(git log --oneline $PREV.. --grep='^Merge' --invert-grep)
printf "CHANGES\n%s\n\n" "$CHANGES"
fi
# Create the release
case ${GEKY_BOT_DRAFT:-minor} in
true) DRAFT=true ;;
minor) DRAFT=$(jq -R 'endswith(".0")' <<< "$LFS_VERSION") ;;
false) DRAFT=false ;;
esac
# Create the release and patch version tag (vN.N.N)
curl -f -u "$GEKY_BOT_RELEASES" -X POST \
https://api.github.com/repos/$TRAVIS_REPO_SLUG/releases \
-d "{
\"tag_name\": \"$LFS_VERSION\",
\"name\": \"${LFS_VERSION%.0}\",
\"draft\": true,
\"target_commitish\": \"$TRAVIS_COMMIT\",
\"draft\": $DRAFT,
\"body\": $(jq -sR '.' <<< "$CHANGES")
}" #"
SCRIPT

View File

@@ -29,7 +29,7 @@ override CFLAGS += -DLFS_YES_TRACE
endif
override CFLAGS += -I.
override CFLAGS += -std=c99 -Wall -pedantic
override CFLAGS += -Wextra -Wshadow -Wjump-misses-init
override CFLAGS += -Wextra -Wshadow -Wjump-misses-init -Wundef
# Remove missing-field-initializers because of GCC bug
override CFLAGS += -Wno-missing-field-initializers

98
lfs.c
View File

@@ -7,19 +7,21 @@
#include "lfs.h"
#include "lfs_util.h"
#define LFS_BLOCK_NULL ((lfs_block_t)-1)
#define LFS_BLOCK_INLINE ((lfs_block_t)-2)
/// Caching block device operations ///
static inline void lfs_cache_drop(lfs_t *lfs, lfs_cache_t *rcache) {
// do not zero, cheaper if cache is readonly or only going to be
// written with identical data (during relocates)
(void)lfs;
rcache->block = 0xffffffff;
rcache->block = LFS_BLOCK_NULL;
}
static inline void lfs_cache_zero(lfs_t *lfs, lfs_cache_t *pcache) {
// zero to avoid information leak
memset(pcache->buffer, 0xff, lfs->cfg->cache_size);
pcache->block = 0xffffffff;
pcache->block = LFS_BLOCK_NULL;
}
static int lfs_bd_read(lfs_t *lfs,
@@ -27,7 +29,7 @@ static int lfs_bd_read(lfs_t *lfs,
lfs_block_t block, lfs_off_t off,
void *buffer, lfs_size_t size) {
uint8_t *data = buffer;
LFS_ASSERT(block != 0xffffffff);
LFS_ASSERT(block != LFS_BLOCK_NULL);
if (off+size > lfs->cfg->block_size) {
return LFS_ERR_CORRUPT;
}
@@ -121,7 +123,7 @@ static int lfs_bd_cmp(lfs_t *lfs,
static int lfs_bd_flush(lfs_t *lfs,
lfs_cache_t *pcache, lfs_cache_t *rcache, bool validate) {
if (pcache->block != 0xffffffff && pcache->block != 0xfffffffe) {
if (pcache->block != LFS_BLOCK_NULL && pcache->block != LFS_BLOCK_INLINE) {
LFS_ASSERT(pcache->block < lfs->cfg->block_count);
lfs_size_t diff = lfs_alignup(pcache->size, lfs->cfg->prog_size);
int err = lfs->cfg->prog(lfs->cfg, pcache->block,
@@ -171,7 +173,7 @@ static int lfs_bd_prog(lfs_t *lfs,
lfs_block_t block, lfs_off_t off,
const void *buffer, lfs_size_t size) {
const uint8_t *data = buffer;
LFS_ASSERT(block != 0xffffffff);
LFS_ASSERT(block != LFS_BLOCK_NULL);
LFS_ASSERT(off + size <= lfs->cfg->block_size);
while (size > 0) {
@@ -201,7 +203,7 @@ static int lfs_bd_prog(lfs_t *lfs,
// pcache must have been flushed, either by programming and
// entire block or manually flushing the pcache
LFS_ASSERT(pcache->block == 0xffffffff);
LFS_ASSERT(pcache->block == LFS_BLOCK_NULL);
// prepare pcache, first condition can no longer fail
pcache->block = block;
@@ -229,7 +231,7 @@ static inline void lfs_pair_swap(lfs_block_t pair[2]) {
}
static inline bool lfs_pair_isnull(const lfs_block_t pair[2]) {
return pair[0] == 0xffffffff || pair[1] == 0xffffffff;
return pair[0] == LFS_BLOCK_NULL || pair[1] == LFS_BLOCK_NULL;
}
static inline int lfs_pair_cmp(
@@ -571,7 +573,7 @@ static int lfs_dir_getread(lfs_t *lfs, const lfs_mdir_t *dir,
while (size > 0) {
lfs_size_t diff = size;
if (pcache && pcache->block == 0xfffffffe &&
if (pcache && pcache->block == LFS_BLOCK_INLINE &&
off < pcache->off + pcache->size) {
if (off >= pcache->off) {
// is already in pcache?
@@ -588,7 +590,7 @@ static int lfs_dir_getread(lfs_t *lfs, const lfs_mdir_t *dir,
diff = lfs_min(diff, pcache->off-off);
}
if (rcache->block == 0xfffffffe &&
if (rcache->block == LFS_BLOCK_INLINE &&
off < rcache->off + rcache->size) {
if (off >= rcache->off) {
// is already in rcache?
@@ -606,7 +608,7 @@ static int lfs_dir_getread(lfs_t *lfs, const lfs_mdir_t *dir,
}
// load to cache, first condition can no longer fail
rcache->block = 0xfffffffe;
rcache->block = LFS_BLOCK_INLINE;
rcache->off = lfs_aligndown(off, lfs->cfg->read_size);
rcache->size = lfs_min(lfs_alignup(off+hint, lfs->cfg->read_size),
lfs->cfg->cache_size);
@@ -723,7 +725,7 @@ static int lfs_dir_traverse(lfs_t *lfs,
uint16_t fromid = lfs_tag_size(tag);
uint16_t toid = lfs_tag_id(tag);
int err = lfs_dir_traverse(lfs,
buffer, 0, 0xffffffff, NULL, 0, true,
buffer, 0, LFS_BLOCK_NULL, NULL, 0, true,
LFS_MKTAG(0x600, 0x3ff, 0),
LFS_MKTAG(LFS_TYPE_STRUCT, 0, 0),
fromid, fromid+1, toid-fromid+diff,
@@ -783,15 +785,15 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs,
// now scan tags to fetch the actual dir and find possible match
for (int i = 0; i < 2; i++) {
lfs_off_t off = 0;
lfs_tag_t ptag = 0xffffffff;
lfs_tag_t ptag = LFS_BLOCK_NULL;
uint16_t tempcount = 0;
lfs_block_t temptail[2] = {0xffffffff, 0xffffffff};
lfs_block_t temptail[2] = {LFS_BLOCK_NULL, LFS_BLOCK_NULL};
bool tempsplit = false;
lfs_stag_t tempbesttag = besttag;
dir->rev = lfs_tole32(dir->rev);
uint32_t crc = lfs_crc(0xffffffff, &dir->rev, sizeof(dir->rev));
uint32_t crc = lfs_crc(LFS_BLOCK_NULL, &dir->rev, sizeof(dir->rev));
dir->rev = lfs_fromle32(dir->rev);
while (true) {
@@ -860,7 +862,7 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs,
dir->split = tempsplit;
// reset crc
crc = 0xffffffff;
crc = LFS_BLOCK_NULL;
continue;
}
@@ -1248,7 +1250,7 @@ static int lfs_dir_commitcrc(lfs_t *lfs, struct lfs_commit *commit) {
}
// read erased state from next program unit
lfs_tag_t tag = 0xffffffff;
lfs_tag_t tag = LFS_BLOCK_NULL;
int err = lfs_bd_read(lfs,
NULL, &lfs->rcache, sizeof(tag),
commit->block, noff, &tag, sizeof(tag));
@@ -1274,7 +1276,7 @@ static int lfs_dir_commitcrc(lfs_t *lfs, struct lfs_commit *commit) {
commit->off += sizeof(tag)+lfs_tag_size(tag);
commit->ptag = tag ^ (reset << 31);
commit->crc = 0xffffffff; // reset crc for next "commit"
commit->crc = LFS_BLOCK_NULL; // reset crc for next "commit"
}
// flush buffers
@@ -1287,7 +1289,7 @@ static int lfs_dir_commitcrc(lfs_t *lfs, struct lfs_commit *commit) {
lfs_off_t off = commit->begin;
lfs_off_t noff = off1;
while (off < end) {
uint32_t crc = 0xffffffff;
uint32_t crc = LFS_BLOCK_NULL;
for (lfs_off_t i = off; i < noff+sizeof(uint32_t); i++) {
// leave it up to caching to make this efficient
uint8_t dat;
@@ -1341,10 +1343,10 @@ static int lfs_dir_alloc(lfs_t *lfs, lfs_mdir_t *dir) {
// set defaults
dir->off = sizeof(dir->rev);
dir->etag = 0xffffffff;
dir->etag = LFS_BLOCK_NULL;
dir->count = 0;
dir->tail[0] = 0xffffffff;
dir->tail[1] = 0xffffffff;
dir->tail[0] = LFS_BLOCK_NULL;
dir->tail[1] = LFS_BLOCK_NULL;
dir->erased = false;
dir->split = false;
@@ -1434,7 +1436,7 @@ static int lfs_dir_compact(lfs_t *lfs,
// find size
lfs_size_t size = 0;
int err = lfs_dir_traverse(lfs,
source, 0, 0xffffffff, attrs, attrcount, false,
source, 0, LFS_BLOCK_NULL, attrs, attrcount, false,
LFS_MKTAG(0x400, 0x3ff, 0),
LFS_MKTAG(LFS_TYPE_NAME, 0, 0),
begin, end, -begin,
@@ -1500,7 +1502,7 @@ static int lfs_dir_compact(lfs_t *lfs,
end = begin;
}
}
#if LFS_MIGRATE
#ifdef LFS_MIGRATE
} else if (lfs_pair_cmp(dir->pair, lfs->root) == 0 && lfs->lfs1) {
// we can't relocate our root during migrations, as this would
// cause the superblock to get updated, which would clobber v1
@@ -1526,8 +1528,8 @@ static int lfs_dir_compact(lfs_t *lfs,
struct lfs_commit commit = {
.block = dir->pair[1],
.off = 0,
.ptag = 0xffffffff,
.crc = 0xffffffff,
.ptag = LFS_BLOCK_NULL,
.crc = LFS_BLOCK_NULL,
.begin = 0,
.end = lfs->cfg->block_size - 8,
@@ -1556,7 +1558,7 @@ static int lfs_dir_compact(lfs_t *lfs,
// traverse the directory, this time writing out all unique tags
err = lfs_dir_traverse(lfs,
source, 0, 0xffffffff, attrs, attrcount, false,
source, 0, LFS_BLOCK_NULL, attrs, attrcount, false,
LFS_MKTAG(0x400, 0x3ff, 0),
LFS_MKTAG(LFS_TYPE_NAME, 0, 0),
begin, end, -begin,
@@ -1682,8 +1684,8 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir,
}
// calculate changes to the directory
lfs_tag_t deletetag = 0xffffffff;
lfs_tag_t createtag = 0xffffffff;
lfs_tag_t deletetag = LFS_BLOCK_NULL;
lfs_tag_t createtag = LFS_BLOCK_NULL;
for (int i = 0; i < attrcount; i++) {
if (lfs_tag_type3(attrs[i].tag) == LFS_TYPE_CREATE) {
createtag = attrs[i].tag;
@@ -1729,7 +1731,7 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir,
.block = dir->pair[0],
.off = dir->off,
.ptag = dir->etag,
.crc = 0xffffffff,
.crc = LFS_BLOCK_NULL,
.begin = dir->off,
.end = lfs->cfg->block_size - 8,
@@ -1813,8 +1815,8 @@ compact:
if (lfs_pair_cmp(d->m.pair, copy.pair) == 0) {
d->m = *dir;
if (d->id == lfs_tag_id(deletetag)) {
d->m.pair[0] = 0xffffffff;
d->m.pair[1] = 0xffffffff;
d->m.pair[0] = LFS_BLOCK_NULL;
d->m.pair[1] = LFS_BLOCK_NULL;
} else if (d->id > lfs_tag_id(deletetag)) {
d->id -= 1;
if (d->type == LFS_TYPE_DIR) {
@@ -2129,7 +2131,7 @@ static int lfs_ctz_find(lfs_t *lfs,
lfs_block_t head, lfs_size_t size,
lfs_size_t pos, lfs_block_t *block, lfs_off_t *off) {
if (size == 0) {
*block = 0xffffffff;
*block = LFS_BLOCK_NULL;
*off = 0;
return 0;
}
@@ -2327,6 +2329,7 @@ int lfs_file_opencfg(lfs_t *lfs, lfs_file_t *file,
file->cfg = cfg;
file->flags = flags | LFS_F_OPENED;
file->pos = 0;
file->off = 0;
file->cache.buffer = NULL;
// allocate entry for file if it doesn't exist
@@ -2426,7 +2429,7 @@ int lfs_file_opencfg(lfs_t *lfs, lfs_file_t *file,
if (lfs_tag_type3(tag) == LFS_TYPE_INLINESTRUCT) {
// load inline files
file->ctz.head = 0xfffffffe;
file->ctz.head = LFS_BLOCK_INLINE;
file->ctz.size = lfs_tag_size(tag);
file->flags |= LFS_F_INLINE;
file->cache.block = file->ctz.head;
@@ -2614,7 +2617,7 @@ static int lfs_file_flush(lfs_t *lfs, lfs_file_t *file) {
}
// keep our reference to the rcache in sync
if (lfs->rcache.block != 0xffffffff) {
if (lfs->rcache.block != LFS_BLOCK_NULL) {
lfs_cache_drop(lfs, &orig.cache);
lfs_cache_drop(lfs, &lfs->rcache);
}
@@ -2762,7 +2765,7 @@ lfs_ssize_t lfs_file_read(lfs_t *lfs, lfs_file_t *file,
return err;
}
} else {
file->block = 0xfffffffe;
file->block = LFS_BLOCK_INLINE;
file->off = file->pos;
}
@@ -2888,7 +2891,7 @@ lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file,
return err;
}
} else {
file->block = 0xfffffffe;
file->block = LFS_BLOCK_INLINE;
file->off = file->pos;
}
@@ -2978,6 +2981,7 @@ int lfs_file_truncate(lfs_t *lfs, lfs_file_t *file, lfs_off_t size) {
return LFS_ERR_INVAL;
}
lfs_off_t pos = file->pos;
lfs_off_t oldsize = lfs_file_size(lfs, file);
if (size < oldsize) {
// need to flush since directly changing metadata
@@ -3000,8 +3004,6 @@ int lfs_file_truncate(lfs_t *lfs, lfs_file_t *file, lfs_off_t size) {
file->ctz.size = size;
file->flags |= LFS_F_DIRTY | LFS_F_READING;
} else if (size > oldsize) {
lfs_off_t pos = file->pos;
// flush+seek if not already at end
if (file->pos != oldsize) {
int err = lfs_file_seek(lfs, file, 0, LFS_SEEK_END);
@@ -3019,6 +3021,7 @@ int lfs_file_truncate(lfs_t *lfs, lfs_file_t *file, lfs_off_t size) {
return res;
}
}
}
// restore pos
int err = lfs_file_seek(lfs, file, pos, LFS_SEEK_SET);
@@ -3026,7 +3029,6 @@ int lfs_file_truncate(lfs_t *lfs, lfs_file_t *file, lfs_off_t size) {
LFS_TRACE("lfs_file_truncate -> %d", err);
return err;
}
}
LFS_TRACE("lfs_file_truncate -> %d", 0);
return 0;
@@ -3374,7 +3376,7 @@ static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) {
LFS_ASSERT(lfs->cfg->block_size % lfs->cfg->cache_size == 0);
// check that the block size is large enough to fit ctz pointers
LFS_ASSERT(4*lfs_npw2(0xffffffff / (lfs->cfg->block_size-2*4))
LFS_ASSERT(4*lfs_npw2(LFS_BLOCK_NULL / (lfs->cfg->block_size-2*4))
<= lfs->cfg->block_size);
// block_cycles = 0 is no longer supported.
@@ -3412,10 +3414,10 @@ static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) {
lfs_cache_zero(lfs, &lfs->rcache);
lfs_cache_zero(lfs, &lfs->pcache);
// setup lookahead, must be multiple of 64-bits
// setup lookahead, must be multiple of 64-bits, 32-bit aligned
LFS_ASSERT(lfs->cfg->lookahead_size > 0);
LFS_ASSERT(lfs->cfg->lookahead_size % 8 == 0 &&
(uintptr_t)lfs->cfg->lookahead_buffer % 8 == 0);
(uintptr_t)lfs->cfg->lookahead_buffer % 4 == 0);
if (lfs->cfg->lookahead_buffer) {
lfs->free.buffer = lfs->cfg->lookahead_buffer;
} else {
@@ -3446,8 +3448,8 @@ static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) {
}
// setup default state
lfs->root[0] = 0xffffffff;
lfs->root[1] = 0xffffffff;
lfs->root[0] = LFS_BLOCK_NULL;
lfs->root[1] = LFS_BLOCK_NULL;
lfs->mlist = NULL;
lfs->seed = 0;
lfs->gstate = (struct lfs_gstate){0};
@@ -4250,7 +4252,7 @@ static int lfs1_dir_fetch(lfs_t *lfs,
continue;
}
uint32_t crc = 0xffffffff;
uint32_t crc = LFS_BLOCK_NULL;
lfs1_dir_tole32(&test);
lfs1_crc(&crc, &test, sizeof(test));
lfs1_dir_fromle32(&test);
@@ -4435,8 +4437,8 @@ static int lfs1_mount(lfs_t *lfs, struct lfs1 *lfs1,
}
lfs->lfs1 = lfs1;
lfs->lfs1->root[0] = 0xffffffff;
lfs->lfs1->root[1] = 0xffffffff;
lfs->lfs1->root[0] = LFS_BLOCK_NULL;
lfs->lfs1->root[1] = LFS_BLOCK_NULL;
// setup free lookahead
lfs->free.off = 0;
@@ -4685,7 +4687,7 @@ int lfs_migrate(lfs_t *lfs, const struct lfs_config *cfg) {
dir2.pair[1] = dir1.pair[1];
dir2.rev = dir1.d.rev;
dir2.off = sizeof(dir2.rev);
dir2.etag = 0xffffffff;
dir2.etag = LFS_BLOCK_NULL;
dir2.count = 0;
dir2.tail[0] = lfs->lfs1->root[0];
dir2.tail[1] = lfs->lfs1->root[1];

View File

@@ -107,6 +107,57 @@ scripts/test.py << TEST
lfs_unmount(&lfs) => 0;
TEST
echo "--- Write, truncate, and read ---"
scripts/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file, "sequence",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_TRUNC) => 0;
lfs_size_t size = lfs.cfg->cache_size;
lfs_size_t qsize = size / 4;
uint8_t *wb = buffer;
uint8_t *rb = buffer + size;
for (lfs_off_t j = 0; j < size; ++j) {
wb[j] = j;
}
/* Spread sequence over size */
lfs_file_write(&lfs, &file, wb, size) => size;
lfs_file_size(&lfs, &file) => size;
lfs_file_tell(&lfs, &file) => size;
lfs_file_seek(&lfs, &file, 0, LFS_SEEK_SET) => 0;
lfs_file_tell(&lfs, &file) => 0;
/* Chop off the last quarter */
lfs_size_t trunc = size - qsize;
lfs_file_truncate(&lfs, &file, trunc) => 0;
lfs_file_tell(&lfs, &file) => 0;
lfs_file_size(&lfs, &file) => trunc;
/* Read should produce first 3/4 */
lfs_file_read(&lfs, &file, rb, size) => trunc;
memcmp(rb, wb, trunc) => 0;
/* Move to 1/4 */
lfs_file_size(&lfs, &file) => trunc;
lfs_file_seek(&lfs, &file, qsize, LFS_SEEK_SET) => qsize;
lfs_file_tell(&lfs, &file) => qsize;
/* Chop to 1/2 */
trunc -= qsize;
lfs_file_truncate(&lfs, &file, trunc) => 0;
lfs_file_tell(&lfs, &file) => qsize;
lfs_file_size(&lfs, &file) => trunc;
/* Read should produce second quarter */
lfs_file_read(&lfs, &file, rb, size) => trunc - qsize;
memcmp(rb, wb + qsize, trunc - qsize) => 0;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Truncate and write ---"
scripts/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;