From 6ccd9eb5986d9c5afb8fe4648d697f56cd7b2422 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Fri, 8 Dec 2023 14:03:56 -0600 Subject: [PATCH] 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... --- lfs.c | 182 +++----------------------------------------- scripts/dbgbmap.py | 7 +- scripts/dbgbtree.py | 14 +--- scripts/dbglfs.py | 95 ++++------------------- scripts/dbgmtree.py | 14 +--- scripts/dbgrbyd.py | 14 +--- 6 files changed, 31 insertions(+), 295 deletions(-) diff --git a/lfs.c b/lfs.c index cdc5767b..e6025d1d 100644 --- a/lfs.c +++ b/lfs.c @@ -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; diff --git a/scripts/dbgbmap.py b/scripts/dbgbmap.py index de2611fb..8f464633 100755 --- a/scripts/dbgbmap.py +++ b/scripts/dbgbmap.py @@ -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 diff --git a/scripts/dbgbtree.py b/scripts/dbgbtree.py index 24bbbd6a..ffb4875d 100755 --- a/scripts/dbgbtree.py +++ b/scripts/dbgbtree.py @@ -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) diff --git a/scripts/dbglfs.py b/scripts/dbglfs.py index b09e4909..b064c002 100755 --- a/scripts/dbglfs.py +++ b/scripts/dbglfs.py @@ -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), diff --git a/scripts/dbgmtree.py b/scripts/dbgmtree.py index 0e4fa231..dce964c3 100755 --- a/scripts/dbgmtree.py +++ b/scripts/dbgmtree.py @@ -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) diff --git a/scripts/dbgrbyd.py b/scripts/dbgrbyd.py index cca01f77..447d62fd 100755 --- a/scripts/dbgrbyd.py +++ b/scripts/dbgrbyd.py @@ -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)