|
|
@@ -518,6 +518,24 @@ static void lfs_mlist_append(lfs_t *lfs, struct lfs_mlist *mlist) {
|
|
|
lfs->mlist = mlist;
|
|
|
}
|
|
|
|
|
|
+// some other filesystem operations
|
|
|
+static uint32_t lfs_fs_disk_version(lfs_t *lfs) {
|
|
|
+ if (lfs->cfg->disk_version) {
|
|
|
+ return lfs->cfg->disk_version;
|
|
|
+ } else {
|
|
|
+ return LFS_DISK_VERSION;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static uint16_t lfs_fs_disk_version_major(lfs_t *lfs) {
|
|
|
+ return 0xffff & (lfs_fs_disk_version(lfs) >> 16);
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+static uint16_t lfs_fs_disk_version_minor(lfs_t *lfs) {
|
|
|
+ return 0xffff & (lfs_fs_disk_version(lfs) >> 0);
|
|
|
+}
|
|
|
+
|
|
|
|
|
|
/// Internal operations predeclared here ///
|
|
|
#ifndef LFS_READONLY
|
|
|
@@ -1111,7 +1129,8 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs,
|
|
|
|
|
|
// next commit not yet programmed?
|
|
|
if (!lfs_tag_isvalid(tag)) {
|
|
|
- maybeerased = true;
|
|
|
+ // we only might be erased if the last tag was a crc
|
|
|
+ maybeerased = (lfs_tag_type2(ptag) == LFS_TYPE_CCRC);
|
|
|
break;
|
|
|
// out of range?
|
|
|
} else if (off + lfs_tag_dsize(tag) > lfs->cfg->block_size) {
|
|
|
@@ -1156,14 +1175,11 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs,
|
|
|
dir->tail[1] = temptail[1];
|
|
|
dir->split = tempsplit;
|
|
|
|
|
|
- // reset crc
|
|
|
+ // reset crc, hasfcrc
|
|
|
crc = 0xffffffff;
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
- // fcrc is only valid when last tag was a crc
|
|
|
- hasfcrc = false;
|
|
|
-
|
|
|
// crc the entry first, hopefully leaving it in the cache
|
|
|
err = lfs_bd_crc(lfs,
|
|
|
NULL, &lfs->rcache, lfs->cfg->block_size,
|
|
|
@@ -1257,20 +1273,30 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs,
|
|
|
|
|
|
// did we end on a valid commit? we may have an erased block
|
|
|
dir->erased = false;
|
|
|
- if (maybeerased && hasfcrc && dir->off % lfs->cfg->prog_size == 0) {
|
|
|
- // check for an fcrc matching the next prog's erased state, if
|
|
|
- // this failed most likely a previous prog was interrupted, we
|
|
|
- // need a new erase
|
|
|
- uint32_t fcrc_ = 0xffffffff;
|
|
|
- int err = lfs_bd_crc(lfs,
|
|
|
- NULL, &lfs->rcache, lfs->cfg->block_size,
|
|
|
- dir->pair[0], dir->off, fcrc.size, &fcrc_);
|
|
|
- if (err && err != LFS_ERR_CORRUPT) {
|
|
|
- return err;
|
|
|
- }
|
|
|
+ if (maybeerased && dir->off % lfs->cfg->prog_size == 0) {
|
|
|
+ // note versions < lfs2.1 did not have fcrc tags, if
|
|
|
+ // we're < lfs2.1 treat missing fcrc as erased data
|
|
|
+ //
|
|
|
+ // we don't strictly need to do this, but otherwise writing
|
|
|
+ // to lfs2.0 disks becomes very inefficient
|
|
|
+ if (lfs_fs_disk_version(lfs) < 0x00020001) {
|
|
|
+ dir->erased = true;
|
|
|
+
|
|
|
+ } else if (hasfcrc) {
|
|
|
+ // check for an fcrc matching the next prog's erased state, if
|
|
|
+ // this failed most likely a previous prog was interrupted, we
|
|
|
+ // need a new erase
|
|
|
+ uint32_t fcrc_ = 0xffffffff;
|
|
|
+ int err = lfs_bd_crc(lfs,
|
|
|
+ NULL, &lfs->rcache, lfs->cfg->block_size,
|
|
|
+ dir->pair[0], dir->off, fcrc.size, &fcrc_);
|
|
|
+ if (err && err != LFS_ERR_CORRUPT) {
|
|
|
+ return err;
|
|
|
+ }
|
|
|
|
|
|
- // found beginning of erased part?
|
|
|
- dir->erased = (fcrc_ == fcrc.crc);
|
|
|
+ // found beginning of erased part?
|
|
|
+ dir->erased = (fcrc_ == fcrc.crc);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
// synthetic move
|
|
|
@@ -1606,22 +1632,29 @@ static int lfs_dir_commitcrc(lfs_t *lfs, struct lfs_commit *commit) {
|
|
|
return err;
|
|
|
}
|
|
|
|
|
|
- // find the expected fcrc, don't bother avoiding a reread
|
|
|
- // of the eperturb, it should still be in our cache
|
|
|
- struct lfs_fcrc fcrc = {.size=lfs->cfg->prog_size, .crc=0xffffffff};
|
|
|
- err = lfs_bd_crc(lfs,
|
|
|
- NULL, &lfs->rcache, lfs->cfg->prog_size,
|
|
|
- commit->block, noff, fcrc.size, &fcrc.crc);
|
|
|
- if (err && err != LFS_ERR_CORRUPT) {
|
|
|
- return err;
|
|
|
- }
|
|
|
+ // unfortunately fcrcs break mdir fetching < lfs2.1, so only write
|
|
|
+ // these if we're a >= lfs2.1 filesystem
|
|
|
+ if (lfs_fs_disk_version(lfs) >= 0x00020001) {
|
|
|
+ // find the expected fcrc, don't bother avoiding a reread
|
|
|
+ // of the eperturb, it should still be in our cache
|
|
|
+ struct lfs_fcrc fcrc = {
|
|
|
+ .size = lfs->cfg->prog_size,
|
|
|
+ .crc = 0xffffffff
|
|
|
+ };
|
|
|
+ err = lfs_bd_crc(lfs,
|
|
|
+ NULL, &lfs->rcache, lfs->cfg->prog_size,
|
|
|
+ commit->block, noff, fcrc.size, &fcrc.crc);
|
|
|
+ if (err && err != LFS_ERR_CORRUPT) {
|
|
|
+ return err;
|
|
|
+ }
|
|
|
|
|
|
- lfs_fcrc_tole32(&fcrc);
|
|
|
- err = lfs_dir_commitattr(lfs, commit,
|
|
|
- LFS_MKTAG(LFS_TYPE_FCRC, 0x3ff, sizeof(struct lfs_fcrc)),
|
|
|
- &fcrc);
|
|
|
- if (err) {
|
|
|
- return err;
|
|
|
+ lfs_fcrc_tole32(&fcrc);
|
|
|
+ err = lfs_dir_commitattr(lfs, commit,
|
|
|
+ LFS_MKTAG(LFS_TYPE_FCRC, 0x3ff, sizeof(struct lfs_fcrc)),
|
|
|
+ &fcrc);
|
|
|
+ if (err) {
|
|
|
+ return err;
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -4052,6 +4085,13 @@ static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) {
|
|
|
lfs->cfg = cfg;
|
|
|
int err = 0;
|
|
|
|
|
|
+ // this driver only supports minor version < current minor version
|
|
|
+ LFS_ASSERT(!lfs->cfg->disk_version || (
|
|
|
+ (0xffff & (lfs->cfg->disk_version >> 16))
|
|
|
+ == LFS_DISK_VERSION_MAJOR
|
|
|
+ && (0xffff & (lfs->cfg->disk_version >> 0))
|
|
|
+ <= LFS_DISK_VERSION_MINOR));
|
|
|
+
|
|
|
// check that bool is a truthy-preserving type
|
|
|
//
|
|
|
// note the most common reason for this failure is a before-c99 compiler,
|
|
|
@@ -4209,7 +4249,7 @@ static int lfs_rawformat(lfs_t *lfs, const struct lfs_config *cfg) {
|
|
|
|
|
|
// write one superblock
|
|
|
lfs_superblock_t superblock = {
|
|
|
- .version = LFS_DISK_VERSION,
|
|
|
+ .version = lfs_fs_disk_version(lfs),
|
|
|
.block_size = lfs->cfg->block_size,
|
|
|
.block_count = lfs->cfg->block_count,
|
|
|
.name_max = lfs->name_max,
|
|
|
@@ -4307,12 +4347,14 @@ static int lfs_rawmount(lfs_t *lfs, const struct lfs_config *cfg) {
|
|
|
// check version
|
|
|
uint16_t major_version = (0xffff & (superblock.version >> 16));
|
|
|
uint16_t minor_version = (0xffff & (superblock.version >> 0));
|
|
|
- if ((major_version != LFS_DISK_VERSION_MAJOR ||
|
|
|
- minor_version > LFS_DISK_VERSION_MINOR)) {
|
|
|
+ if (major_version != lfs_fs_disk_version_major(lfs)
|
|
|
+ || minor_version > lfs_fs_disk_version_minor(lfs)) {
|
|
|
LFS_ERROR("Invalid version "
|
|
|
"v%"PRIu16".%"PRIu16" != v%"PRIu16".%"PRIu16,
|
|
|
- major_version, minor_version,
|
|
|
- LFS_DISK_VERSION_MAJOR, LFS_DISK_VERSION_MINOR);
|
|
|
+ major_version,
|
|
|
+ minor_version,
|
|
|
+ lfs_fs_disk_version_major(lfs),
|
|
|
+ lfs_fs_disk_version_minor(lfs));
|
|
|
err = LFS_ERR_INVAL;
|
|
|
goto cleanup;
|
|
|
}
|
|
|
@@ -4320,11 +4362,13 @@ static int lfs_rawmount(lfs_t *lfs, const struct lfs_config *cfg) {
|
|
|
// found older minor version? set an in-device only bit in the
|
|
|
// gstate so we know we need to rewrite the superblock before
|
|
|
// the first write
|
|
|
- if (minor_version < LFS_DISK_VERSION_MINOR) {
|
|
|
+ if (minor_version < lfs_fs_disk_version_minor(lfs)) {
|
|
|
LFS_DEBUG("Found older minor version "
|
|
|
"v%"PRIu16".%"PRIu16" < v%"PRIu16".%"PRIu16,
|
|
|
- major_version, minor_version,
|
|
|
- LFS_DISK_VERSION_MAJOR, LFS_DISK_VERSION_MINOR);
|
|
|
+ major_version,
|
|
|
+ minor_version,
|
|
|
+ lfs_fs_disk_version_major(lfs),
|
|
|
+ lfs_fs_disk_version_minor(lfs));
|
|
|
// note this bit is reserved on disk, so fetching more gstate
|
|
|
// will not interfere here
|
|
|
lfs_fs_prepsuperblock(lfs, true);
|
|
|
@@ -4424,7 +4468,7 @@ static int lfs_fs_rawstat(lfs_t *lfs, struct lfs_fsinfo *fsinfo) {
|
|
|
// if the superblock is up-to-date, we must be on the most recent
|
|
|
// minor version of littlefs
|
|
|
if (!lfs_gstate_needssuperblock(&lfs->gstate)) {
|
|
|
- fsinfo->disk_version = LFS_DISK_VERSION;
|
|
|
+ fsinfo->disk_version = lfs_fs_disk_version(lfs);
|
|
|
|
|
|
// otherwise we need to read the minor version on disk
|
|
|
} else {
|
|
|
@@ -4711,7 +4755,7 @@ static int lfs_fs_desuperblock(lfs_t *lfs) {
|
|
|
|
|
|
// write a new superblock
|
|
|
lfs_superblock_t superblock = {
|
|
|
- .version = LFS_DISK_VERSION,
|
|
|
+ .version = lfs_fs_disk_version(lfs),
|
|
|
.block_size = lfs->cfg->block_size,
|
|
|
.block_count = lfs->cfg->block_count,
|
|
|
.name_max = lfs->name_max,
|