Adopted different strategy for hypothetical future configs

Instead of writing every possible config that has the potential to be
useful in the future, stick to just writing the configs that we know are
useful, and error if we see any configs we don't understand.

This prevents unnecessary config bloat, while still allowing configs to
be introduced in a backwards compatible way in the future.

Currently unknown configs are treated as a mount error, but in theory
you could still try to read the filesystem, just with potentially
corrupted data. Maybe this could be behind some sort of "FORCE" mount
flag. littlefs must never write to the filesystem if it finds unknown
configs.

---

This also creates a curious case for the hole in our tag encoding
previously taken up by the OCOMPATFLAGS config. We can query for any
config > SIZELIMIT with lookupnext, but the OCOMPATFLAGS flag would need
an extra lookup which just isn't worth it.

Instead I'm just adding OCOMPATFLAGS back in. To support OCOMPATFLAGS
littlefs has to do literally nothing, so this is really more of a
documentation change. And who know, maybe OCOMPATFLAGS will have some
weird use case in the future...
This commit is contained in:
Christopher Haster
2023-12-08 14:03:56 -06:00
parent 337bdf61ae
commit 6ccd9eb598
6 changed files with 31 additions and 295 deletions

182
lfs.c
View File

@@ -586,18 +586,13 @@ enum lfsr_tag {
LFSR_TAG_CONFIG = 0x0000,
LFSR_TAG_MAGIC = 0x0003,
LFSR_TAG_VERSION = 0x0004,
LFSR_TAG_OCOMPATFLAGS = 0x0005,
LFSR_TAG_RCOMPATFLAGS = 0x0006,
LFSR_TAG_WCOMPATFLAGS = 0x0007,
LFSR_TAG_BLOCKSIZE = 0x0008,
LFSR_TAG_BLOCKCOUNT = 0x0009,
LFSR_TAG_NAMELIMIT = 0x000a,
LFSR_TAG_SIZELIMIT = 0x000b,
LFSR_TAG_UTAGLIMIT = 0x000c,
LFSR_TAG_UATTRLIMIT = 0x000d,
LFSR_TAG_STAGLIMIT = 0x000e,
LFSR_TAG_SATTRLIMIT = 0x000f,
LFSR_TAG_MDIRLIMIT = 0x0010,
LFSR_TAG_MTREELIMIT = 0x0011,
// global-state tags
LFSR_TAG_GDELTA = 0x0100,
@@ -7912,171 +7907,18 @@ static int lfsr_mountmroot(lfs_t *lfs, const lfsr_mdir_t *mroot) {
lfs->size_limit = size_limit;
// check the utag limit
err = lfsr_mdir_lookup(lfs, mroot, -1, LFSR_TAG_UTAGLIMIT,
NULL, &data);
// check for unknown configs
lfsr_tag_t tag;
err = lfsr_mdir_lookupnext(lfs, mroot, -1, LFSR_TAG_SIZELIMIT+1,
&tag, NULL);
if (err && err != LFS_ERR_NOENT) {
return err;
}
if (err != LFS_ERR_NOENT) {
uint32_t utag_limit;
err = lfsr_data_readleb128(lfs, &data, (int32_t*)&utag_limit);
if (err && err != LFS_ERR_CORRUPT) {
return err;
}
if (err == LFS_ERR_CORRUPT) {
utag_limit = -1;
}
// only 7-bit utags are supported
if (utag_limit != 0x7f) {
LFS_ERROR("Incompatible utag limit (%"PRId32" != %"PRId32")",
utag_limit,
0x7f);
return LFS_ERR_INVAL;
}
}
// read the uattr limit
err = lfsr_mdir_lookup(lfs, mroot, -1, LFSR_TAG_UATTRLIMIT,
NULL, &data);
if (err) {
if (err == LFS_ERR_NOENT) {
LFS_ERROR("No uattr limit found");
return LFS_ERR_INVAL;
}
return err;
}
uint32_t uattr_limit;
err = lfsr_data_readleb128(lfs, &data, (int32_t*)&uattr_limit);
if (err && err != LFS_ERR_CORRUPT) {
return err;
}
if (err == LFS_ERR_CORRUPT) {
uattr_limit = -1;
}
if (uattr_limit > lfs->uattr_limit) {
LFS_ERROR("Incompatible uattr limit (%"PRId32" > %"PRId32")",
uattr_limit,
lfs->uattr_limit);
return LFS_ERR_INVAL;
}
lfs->uattr_limit = uattr_limit;
// check the stag limit
err = lfsr_mdir_lookup(lfs, mroot, -1, LFSR_TAG_STAGLIMIT,
NULL, &data);
if (err && err != LFS_ERR_NOENT) {
return err;
}
if (err != LFS_ERR_NOENT) {
uint32_t stag_limit;
err = lfsr_data_readleb128(lfs, &data, (int32_t*)&stag_limit);
if (err && err != LFS_ERR_CORRUPT) {
return err;
}
if (err == LFS_ERR_CORRUPT) {
stag_limit = -1;
}
// only 7-bit stags are supported
if (stag_limit != 0x7f) {
LFS_ERROR("Incompatible stag limit (%"PRId32" != %"PRId32")",
stag_limit,
0x7f);
return LFS_ERR_INVAL;
}
}
// read the sattr limit
err = lfsr_mdir_lookup(lfs, mroot, -1, LFSR_TAG_SATTRLIMIT,
NULL, &data);
if (err) {
if (err == LFS_ERR_NOENT) {
LFS_ERROR("No sattr limit found");
return LFS_ERR_INVAL;
}
return err;
}
uint32_t sattr_limit;
err = lfsr_data_readleb128(lfs, &data, (int32_t*)&sattr_limit);
if (err && err != LFS_ERR_CORRUPT) {
return err;
}
if (err == LFS_ERR_CORRUPT) {
sattr_limit = -1;
}
if (sattr_limit > lfs->sattr_limit) {
LFS_ERROR("Incompatible sattr limit (%"PRId32" > %"PRId32")",
sattr_limit,
lfs->sattr_limit);
return LFS_ERR_INVAL;
}
lfs->sattr_limit = sattr_limit;
// read the mdir limit
err = lfsr_mdir_lookup(lfs, mroot, -1, LFSR_TAG_MDIRLIMIT,
NULL, &data);
if (err) {
if (err == LFS_ERR_NOENT) {
LFS_ERROR("No mdir limit found");
return LFS_ERR_INVAL;
}
return err;
}
uint32_t mdir_limit;
err = lfsr_data_readleb128(lfs, &data, (int32_t*)&mdir_limit);
if (err && err != LFS_ERR_CORRUPT) {
return err;
}
if (err == LFS_ERR_CORRUPT) {
mdir_limit = -1;
}
// we only support power-of-two-minus-one mdir limits, this is
// unlikely to ever to change since mdir limits are arbitrary
if (lfs_popc(mdir_limit+1) != 1) {
LFS_ERROR("Incompatible mdir limit %"PRId32,
mdir_limit);
return LFS_ERR_INVAL;
}
lfs->mbits = lfs_nlog2(mdir_limit+1);
// read the mtree limit
err = lfsr_mdir_lookup(lfs, mroot, -1, LFSR_TAG_MTREELIMIT,
NULL, &data);
if (err) {
if (err == LFS_ERR_NOENT) {
LFS_ERROR("No mtree limit found");
return LFS_ERR_INVAL;
}
return err;
}
uint32_t mtree_limit;
err = lfsr_data_readleb128(lfs, &data, (int32_t*)&mtree_limit);
if (err && err != LFS_ERR_CORRUPT) {
return err;
}
if (err == LFS_ERR_CORRUPT) {
mtree_limit = -1;
}
// TODO should we actually be doing something with mtree_limit?
if (mtree_limit != lfs->size_limit) {
LFS_ERROR("Incompatible mtree limit (%"PRId32" != %"PRId32")",
mtree_limit,
LFS_FILE_MAX);
if (err != LFS_ERR_NOENT
&& lfsr_tag_suptype(tag) == LFSR_TAG_CONFIG) {
LFS_ERROR("Unknown config 0x%04"PRIx16,
tag);
return LFS_ERR_INVAL;
}
@@ -8234,12 +8076,6 @@ static int lfsr_formatinited(lfs_t *lfs) {
LFSR_ATTR(-1, BLOCKCOUNT, 0, LEB128(lfs->cfg->block_count-1)),
LFSR_ATTR(-1, NAMELIMIT, 0, LEB128(lfs->name_limit)),
LFSR_ATTR(-1, SIZELIMIT, 0, LEB128(lfs->size_limit)),
LFSR_ATTR(-1, UTAGLIMIT, 0, LEB128(0x7f)),
LFSR_ATTR(-1, UATTRLIMIT, 0, LEB128(lfs->uattr_limit)),
LFSR_ATTR(-1, STAGLIMIT, 0, LEB128(0x7f)),
LFSR_ATTR(-1, SATTRLIMIT, 0, LEB128(lfs->sattr_limit)),
LFSR_ATTR(-1, MDIRLIMIT, 0, LEB128(lfsr_mweight(lfs)-1)),
LFSR_ATTR(-1, MTREELIMIT, 0, LEB128(lfs->size_limit)),
LFSR_ATTR(0, BOOKMARK, +1, LEB128(0))));
if (err) {
return err;

View File

@@ -14,18 +14,13 @@ TAG_NULL = 0x0000
TAG_CONFIG = 0x0000
TAG_MAGIC = 0x0003
TAG_VERSION = 0x0004
TAG_OCOMPATFLAGS = 0x0005
TAG_RCOMPATFLAGS = 0x0006
TAG_WCOMPATFLAGS = 0x0007
TAG_BLOCKSIZE = 0x0008
TAG_BLOCKCOUNT = 0x0009
TAG_NAMELIMIT = 0x000a
TAG_SIZELIMIT = 0x000b
TAG_UTAGLIMIT = 0x000c
TAG_UATTRLIMIT = 0x000d
TAG_STAGLIMIT = 0x000e
TAG_SATTRLIMIT = 0x000f
TAG_MDIRLIMIT = 0x0010
TAG_MTREELIMIT = 0x0011
TAG_GDELTA = 0x0100
TAG_GRMDELTA = 0x0100
TAG_NAME = 0x0200

View File

@@ -12,18 +12,13 @@ TAG_NULL = 0x0000
TAG_CONFIG = 0x0000
TAG_MAGIC = 0x0003
TAG_VERSION = 0x0004
TAG_OCOMPATFLAGS = 0x0005
TAG_RCOMPATFLAGS = 0x0006
TAG_WCOMPATFLAGS = 0x0007
TAG_BLOCKSIZE = 0x0008
TAG_BLOCKCOUNT = 0x0009
TAG_NAMELIMIT = 0x000a
TAG_SIZELIMIT = 0x000b
TAG_UTAGLIMIT = 0x000c
TAG_UATTRLIMIT = 0x000d
TAG_STAGLIMIT = 0x000e
TAG_SATTRLIMIT = 0x000f
TAG_MDIRLIMIT = 0x0010
TAG_MTREELIMIT = 0x0011
TAG_GDELTA = 0x0100
TAG_GRMDELTA = 0x0100
TAG_NAME = 0x0200
@@ -168,18 +163,13 @@ def tagrepr(tag, w, size, off=None):
'shrub' if tag & TAG_SHRUB else '',
'magic' if (tag & 0xfff) == TAG_MAGIC
else 'version' if (tag & 0xfff) == TAG_VERSION
else 'ocompatflags' if (tag & 0xfff) == TAG_OCOMPATFLAGS
else 'rcompatflags' if (tag & 0xfff) == TAG_RCOMPATFLAGS
else 'wcompatflags' if (tag & 0xfff) == TAG_WCOMPATFLAGS
else 'blocksize' if (tag & 0xfff) == TAG_BLOCKSIZE
else 'blockcount' if (tag & 0xfff) == TAG_BLOCKCOUNT
else 'sizelimit' if (tag & 0xfff) == TAG_SIZELIMIT
else 'namelimit' if (tag & 0xfff) == TAG_NAMELIMIT
else 'utaglimit' if (tag & 0xfff) == TAG_UTAGLIMIT
else 'uattrlimit' if (tag & 0xfff) == TAG_UATTRLIMIT
else 'staglimit' if (tag & 0xfff) == TAG_STAGLIMIT
else 'sattrlimit' if (tag & 0xfff) == TAG_SATTRLIMIT
else 'mdirlimit' if (tag & 0xfff) == TAG_MDIRLIMIT
else 'mtreelimit' if (tag & 0xfff) == TAG_MTREELIMIT
else 'config 0x%02x' % (tag & 0xff),
' w%d' % w if w else '',
size)

View File

@@ -13,18 +13,13 @@ TAG_NULL = 0x0000
TAG_CONFIG = 0x0000
TAG_MAGIC = 0x0003
TAG_VERSION = 0x0004
TAG_OCOMPATFLAGS = 0x0005
TAG_RCOMPATFLAGS = 0x0006
TAG_WCOMPATFLAGS = 0x0007
TAG_BLOCKSIZE = 0x0008
TAG_BLOCKCOUNT = 0x0009
TAG_NAMELIMIT = 0x000a
TAG_SIZELIMIT = 0x000b
TAG_UTAGLIMIT = 0x000c
TAG_UATTRLIMIT = 0x000d
TAG_STAGLIMIT = 0x000e
TAG_SATTRLIMIT = 0x000f
TAG_MDIRLIMIT = 0x0010
TAG_MTREELIMIT = 0x0011
TAG_GDELTA = 0x0100
TAG_GRMDELTA = 0x0100
TAG_NAME = 0x0200
@@ -197,18 +192,13 @@ def tagrepr(tag, w, size, off=None):
'shrub' if tag & TAG_SHRUB else '',
'magic' if (tag & 0xfff) == TAG_MAGIC
else 'version' if (tag & 0xfff) == TAG_VERSION
else 'ocompatflags' if (tag & 0xfff) == TAG_OCOMPATFLAGS
else 'rcompatflags' if (tag & 0xfff) == TAG_RCOMPATFLAGS
else 'wcompatflags' if (tag & 0xfff) == TAG_WCOMPATFLAGS
else 'blocksize' if (tag & 0xfff) == TAG_BLOCKSIZE
else 'blockcount' if (tag & 0xfff) == TAG_BLOCKCOUNT
else 'sizelimit' if (tag & 0xfff) == TAG_SIZELIMIT
else 'namelimit' if (tag & 0xfff) == TAG_NAMELIMIT
else 'utaglimit' if (tag & 0xfff) == TAG_UTAGLIMIT
else 'uattrlimit' if (tag & 0xfff) == TAG_UATTRLIMIT
else 'staglimit' if (tag & 0xfff) == TAG_STAGLIMIT
else 'sattrlimit' if (tag & 0xfff) == TAG_SATTRLIMIT
else 'mdirlimit' if (tag & 0xfff) == TAG_MDIRLIMIT
else 'mtreelimit' if (tag & 0xfff) == TAG_MTREELIMIT
else 'config 0x%02x' % (tag & 0xff),
' w%d' % w if w else '',
size)
@@ -1033,6 +1023,14 @@ class Config:
else:
return (None, None)
@ft.cached_property
def ocompatflags(self):
if TAG_OCOMPATFLAGS in self.config:
_, data = self.config[TAG_OCOMPATFLAGS]
return data
else:
return None
@ft.cached_property
def rcompatflags(self):
if TAG_RCOMPATFLAGS in self.config:
@@ -1085,60 +1083,6 @@ class Config:
else:
return None
@ft.cached_property
def utag_limit(self):
if TAG_UTAGLIMIT in self.config:
_, data = self.config[TAG_UTAGLIMIT]
utag_limit, _ = fromleb128(data)
return utag_limit
else:
return None
@ft.cached_property
def uattr_limit(self):
if TAG_UATTRLIMIT in self.config:
_, data = self.config[TAG_UATTRLIMIT]
uattr_limit, _ = fromleb128(data)
return uattr_limit
else:
return None
@ft.cached_property
def stag_limit(self):
if TAG_STAGLIMIT in self.config:
_, data = self.config[TAG_STAGLIMIT]
stag_limit, _ = fromleb128(data)
return stag_limit
else:
return None
@ft.cached_property
def sattr_limit(self):
if TAG_SATTRLIMIT in self.config:
_, data = self.config[TAG_SATTRLIMIT]
sattr_limit, _ = fromleb128(data)
return sattr_limit
else:
return None
@ft.cached_property
def mdir_limit(self):
if TAG_MDIRLIMIT in self.config:
_, data = self.config[TAG_MDIRLIMIT]
mdir_limit, _ = fromleb128(data)
return mdir_limit
else:
return None
@ft.cached_property
def mtree_limit(self):
if TAG_MTREELIMIT in self.config:
_, data = self.config[TAG_MTREELIMIT]
mtree_limit, _ = fromleb128(data)
return mtree_limit
else:
return None
def repr(self):
def crepr(tag, data):
if tag == TAG_MAGIC:
@@ -1147,6 +1091,9 @@ class Config:
for b in map(chr, self.magic))
elif tag == TAG_VERSION:
return 'version v%d.%d' % self.version
elif tag == TAG_OCOMPATFLAGS:
return 'ocompatflags 0x%s' % ''.join(
'%02x' % f for f in reversed(self.ocompatflags))
elif tag == TAG_RCOMPATFLAGS:
return 'rcompatflags 0x%s' % ''.join(
'%02x' % f for f in reversed(self.rcompatflags))
@@ -1161,18 +1108,6 @@ class Config:
return 'sizelimit %d' % self.size_limit
elif tag == TAG_NAMELIMIT:
return 'namelimit %d' % self.name_limit
elif tag == TAG_UTAGLIMIT:
return 'utaglimit %d' % self.utag_limit
elif tag == TAG_UATTRLIMIT:
return 'uattrlimit %d' % self.uattr_limit
elif tag == TAG_STAGLIMIT:
return 'staglimit %d' % self.stag_limit
elif tag == TAG_SATTRLIMIT:
return 'sattrlimit %d' % self.sattr_limit
elif tag == TAG_MDIRLIMIT:
return 'mdirlimit %d' % self.mdir_limit
elif tag == TAG_MTREELIMIT:
return 'mtreelimit %d' % self.mtree_limit
else:
return 'config 0x%02x %d' % (tag, len(data))
@@ -1976,9 +1911,9 @@ def main(disk, mroots=None, *,
# print gdeltas?
if args.get('gdelta'):
for mbid, mw, mdir, j, d, data in gstate.gdelta[tag]:
print('%s{%s}: %*s %-*s %s%s' % (
print('%s%12s %*s %-*s %s%s' % (
'\x1b[90m' if color else '',
','.join('%04x' % block
'{%s}:' % ','.join('%04x' % block
for block in it.chain([mdir.block],
mdir.redund_blocks)),
2*w_width+1, '%d.%d' % (mbid//mleaf_weight, -1),

View File

@@ -12,18 +12,13 @@ TAG_NULL = 0x0000
TAG_CONFIG = 0x0000
TAG_MAGIC = 0x0003
TAG_VERSION = 0x0004
TAG_OCOMPATFLAGS = 0x0005
TAG_RCOMPATFLAGS = 0x0006
TAG_WCOMPATFLAGS = 0x0007
TAG_BLOCKSIZE = 0x0008
TAG_BLOCKCOUNT = 0x0009
TAG_NAMELIMIT = 0x000a
TAG_SIZELIMIT = 0x000b
TAG_UTAGLIMIT = 0x000c
TAG_UATTRLIMIT = 0x000d
TAG_STAGLIMIT = 0x000e
TAG_SATTRLIMIT = 0x000f
TAG_MDIRLIMIT = 0x0010
TAG_MTREELIMIT = 0x0011
TAG_GDELTA = 0x0100
TAG_GRMDELTA = 0x0100
TAG_NAME = 0x0200
@@ -183,18 +178,13 @@ def tagrepr(tag, w, size, off=None):
'shrub' if tag & TAG_SHRUB else '',
'magic' if (tag & 0xfff) == TAG_MAGIC
else 'version' if (tag & 0xfff) == TAG_VERSION
else 'ocompatflags' if (tag & 0xfff) == TAG_OCOMPATFLAGS
else 'rcompatflags' if (tag & 0xfff) == TAG_RCOMPATFLAGS
else 'wcompatflags' if (tag & 0xfff) == TAG_WCOMPATFLAGS
else 'blocksize' if (tag & 0xfff) == TAG_BLOCKSIZE
else 'blockcount' if (tag & 0xfff) == TAG_BLOCKCOUNT
else 'sizelimit' if (tag & 0xfff) == TAG_SIZELIMIT
else 'namelimit' if (tag & 0xfff) == TAG_NAMELIMIT
else 'utaglimit' if (tag & 0xfff) == TAG_UTAGLIMIT
else 'uattrlimit' if (tag & 0xfff) == TAG_UATTRLIMIT
else 'staglimit' if (tag & 0xfff) == TAG_STAGLIMIT
else 'sattrlimit' if (tag & 0xfff) == TAG_SATTRLIMIT
else 'mdirlimit' if (tag & 0xfff) == TAG_MDIRLIMIT
else 'mtreelimit' if (tag & 0xfff) == TAG_MTREELIMIT
else 'config 0x%02x' % (tag & 0xff),
' w%d' % w if w else '',
size)

View File

@@ -21,18 +21,13 @@ TAG_NULL = 0x0000
TAG_CONFIG = 0x0000
TAG_MAGIC = 0x0003
TAG_VERSION = 0x0004
TAG_OCOMPATFLAGS = 0x0005
TAG_RCOMPATFLAGS = 0x0006
TAG_WCOMPATFLAGS = 0x0007
TAG_BLOCKSIZE = 0x0008
TAG_BLOCKCOUNT = 0x0009
TAG_NAMELIMIT = 0x000a
TAG_SIZELIMIT = 0x000b
TAG_UTAGLIMIT = 0x000c
TAG_UATTRLIMIT = 0x000d
TAG_STAGLIMIT = 0x000e
TAG_SATTRLIMIT = 0x000f
TAG_MDIRLIMIT = 0x0010
TAG_MTREELIMIT = 0x0011
TAG_GDELTA = 0x0100
TAG_GRMDELTA = 0x0100
TAG_NAME = 0x0200
@@ -170,18 +165,13 @@ def tagrepr(tag, w, size, off=None):
'shrub' if tag & TAG_SHRUB else '',
'magic' if (tag & 0xfff) == TAG_MAGIC
else 'version' if (tag & 0xfff) == TAG_VERSION
else 'ocompatflags' if (tag & 0xfff) == TAG_OCOMPATFLAGS
else 'rcompatflags' if (tag & 0xfff) == TAG_RCOMPATFLAGS
else 'wcompatflags' if (tag & 0xfff) == TAG_WCOMPATFLAGS
else 'blocksize' if (tag & 0xfff) == TAG_BLOCKSIZE
else 'blockcount' if (tag & 0xfff) == TAG_BLOCKCOUNT
else 'sizelimit' if (tag & 0xfff) == TAG_SIZELIMIT
else 'namelimit' if (tag & 0xfff) == TAG_NAMELIMIT
else 'utaglimit' if (tag & 0xfff) == TAG_UTAGLIMIT
else 'uattrlimit' if (tag & 0xfff) == TAG_UATTRLIMIT
else 'staglimit' if (tag & 0xfff) == TAG_STAGLIMIT
else 'sattrlimit' if (tag & 0xfff) == TAG_SATTRLIMIT
else 'mdirlimit' if (tag & 0xfff) == TAG_MDIRLIMIT
else 'mtreelimit' if (tag & 0xfff) == TAG_MTREELIMIT
else 'config 0x%02x' % (tag & 0xff),
' w%d' % w if w else '',
size)