From c1fe64314c14e68d934ef3c7f682d9e2fdadd7c6 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Fri, 15 Sep 2023 14:50:47 -0500 Subject: [PATCH] Reworked how filesystem-level config is stored Now, instead of storing a single contiguous block of config data, config is stored as tagged metadata like any other attribute. This allows more flexibility towards adding/removing config in the future, without cluttering up the config with deprecated entries (see ATA's "IDENTIFY DEVICE" response). Most of the config entries are single leb128 limits on various integer types, with the exception of the magic string and version (major/minor pair). --- Note this also includes some semantic changes to the config: - Limits are stored as size-1. This avoid issues with integer overflow at extreme ranges. This was also adopted for block size (block limit) and block count (disk limit). This deviation between on-disk config and user-facing config risks confusion, but allows the potential for the full 2^31 range for these values. - The default cksum type, crc32c, has been changed to 0. Originally this was 2 to allow the type to map to the crc width for crc8, crc16, crc32c, crc64, etc. But dropping this idea and numbering checksums as they are implemented simplifies things. May come back to this. - Storing these configs as attributes opens up of the option of on-disk defaults when configs are missing. I'm being a bit conservative with this one, as it's not clear to me if we should prefer default configs (less code/storage, risk of untested config parsing) or prefer explicit on-disk configs. Currently the following have defaults since they seem the most obvious to me: - cksum type => defaults to crc32c - redund type => defaults to parity (TODO, should this default to no redund?) - utag_limit => defaults to 0x7f (no special tag decoding) - uattr_limit => defaults to block_limit (implicit) --- lfs.c | 676 ++++++++++++++++++++++++++------------------ lfs.h | 41 +-- scripts/dbgbtree.py | 36 ++- scripts/dbglfs.py | 281 +++++++++++++----- scripts/dbgmtree.py | 36 ++- scripts/dbgrbyd.py | 36 ++- 6 files changed, 715 insertions(+), 391 deletions(-) diff --git a/lfs.c b/lfs.c index 81b6f1fa..f5b75e18 100644 --- a/lfs.c +++ b/lfs.c @@ -578,8 +578,19 @@ static int lfsr_bd_erase(lfs_t *lfs, lfs_block_t block) { enum lfsr_tag_type { LFSR_TAG_NULL = 0x0000, - LFSR_TAG_SUPERMAGIC = 0x0003, - LFSR_TAG_SUPERCONFIG = 0x0004, + LFSR_TAG_CONFIG = 0x0000, + LFSR_TAG_MAGIC = 0x0003, + LFSR_TAG_VERSION = 0x0004, + LFSR_TAG_FLAGS = 0x0005, + LFSR_TAG_CKSUMTYPE = 0x0006, + LFSR_TAG_REDUNDTYPE = 0x0007, + LFSR_TAG_BLOCKLIMIT = 0x0008, + LFSR_TAG_DISKLIMIT = 0x0009, + LFSR_TAG_MLEAFLIMIT = 0x000a, + LFSR_TAG_SIZELIMIT = 0x000b, + LFSR_TAG_NAMELIMIT = 0x000c, + LFSR_TAG_UTAGLIMIT = 0x000d, + LFSR_TAG_UATTRLIMIT = 0x000e, LFSR_TAG_GSTATE = 0x0100, LFSR_TAG_GRM = 0x0100, @@ -6187,80 +6198,81 @@ static int lfsr_mtree_traversal_next(lfs_t *lfs, /// Superblock things /// -// These are all leb128s, but we can expect smaller encodings -// if we assume the version. +//// TODO rm? +//// These are all leb128s, but we can expect smaller encodings +//// if we assume the version. +//// +//// - 7-bit major_version => 1 byte leb128 (worst case) +//// - 7-bit minor_version => 1 byte leb128 (worst case) +//// - 7-bit cksum_type => 1 byte leb128 (worst case) +//// - 7-bit flags => 1 byte leb128 (worst case) +//// - 32-bit block_size => 5 byte leb128 (worst case) +//// - 32-bit block_count => 5 byte leb128 (worst case) +//// - 7-bit utag_limit => 1 byte leb128 (worst case) +//// - 32-bit mtree_limit => 5 byte leb128 (worst case) +//// - 32-bit attr_limit => 5 byte leb128 (worst case) +//// - 32-bit name_limit => 5 byte leb128 (worst case) +//// - 32-bit file_limit => 5 byte leb128 (worst case) +//// => 33 bytes total +//// +//#define LFSR_SUPERCONFIG_DSIZE (1+1+1+1+5+5+1+5+5+5+5) // -// - 7-bit major_version => 1 byte leb128 (worst case) -// - 7-bit minor_version => 1 byte leb128 (worst case) -// - 7-bit cksum_type => 1 byte leb128 (worst case) -// - 7-bit flags => 1 byte leb128 (worst case) -// - 32-bit block_size => 5 byte leb128 (worst case) -// - 32-bit block_count => 5 byte leb128 (worst case) -// - 7-bit utag_limit => 1 byte leb128 (worst case) -// - 32-bit mtree_limit => 5 byte leb128 (worst case) -// - 32-bit attr_limit => 5 byte leb128 (worst case) -// - 32-bit name_limit => 5 byte leb128 (worst case) -// - 32-bit file_limit => 5 byte leb128 (worst case) -// => 33 bytes total -// -#define LFSR_SUPERCONFIG_DSIZE (1+1+1+1+5+5+1+5+5+5+5) - -#define LFSR_DATA_FROMSUPERCONFIG(_lfs, _buffer) \ - lfsr_data_fromsuperconfig(_lfs, _buffer) - -static lfsr_data_t lfsr_data_fromsuperconfig(lfs_t *lfs, - uint8_t buffer[static LFSR_SUPERCONFIG_DSIZE]) { - // TODO most of these should also be in the lfs_config/lfs_t structs - - // note we take a shortcut for for single-byte leb128s, but these - // are still leb128s! the top bit must be zero! - - // on-disk major version - buffer[0] = LFS_DISK_VERSION_MAJOR; - // on-disk minor version - buffer[1] = LFS_DISK_VERSION_MINOR; - // on-disk cksum type - buffer[2] = 2; - // on-disk flags - buffer[3] = 0; - - // on-disk block size - lfs_ssize_t d = 4; - lfs_ssize_t d_ = lfs_toleb128(lfs->cfg->block_size, &buffer[d], 5); - LFS_ASSERT(d_ >= 0); - d += d_; - - // on-disk block count - d_ = lfs_toleb128(lfs->cfg->block_count, &buffer[d], 5); - LFS_ASSERT(d_ >= 0); - d += d_; - - // on-disk mleaf limit - d_ = lfs_toleb128(lfsr_mleafweight(lfs)-1, &buffer[d], 5); - LFS_ASSERT(d_ >= 0); - d += d_; - - // on-disk utag limit - buffer[d] = 0x7f; - d += 1; - - // on-disk attr limit - d_ = lfs_toleb128(0x7fffffff, &buffer[d], 5); - LFS_ASSERT(d_ >= 0); - d += d_; - - // on-disk name limit - d_ = lfs_toleb128(0xff, &buffer[d], 5); - LFS_ASSERT(d_ >= 0); - d += d_; - - // on-disk file limit - d_ = lfs_toleb128(0x7fffffff, &buffer[d], 5); - LFS_ASSERT(d_ >= 0); - d += d_; - - return LFSR_DATA_BUF(buffer, d); -} +//#define LFSR_DATA_FROMSUPERCONFIG(_lfs, _buffer) +// lfsr_data_fromsuperconfig(_lfs, _buffer) +// +//static lfsr_data_t lfsr_data_fromsuperconfig(lfs_t *lfs, +// uint8_t buffer[static LFSR_SUPERCONFIG_DSIZE]) { +// // TODO most of these should also be in the lfs_config/lfs_t structs +// +// // note we take a shortcut for for single-byte leb128s, but these +// // are still leb128s! the top bit must be zero! +// +// // on-disk major version +// buffer[0] = LFS_DISK_VERSION_MAJOR; +// // on-disk minor version +// buffer[1] = LFS_DISK_VERSION_MINOR; +// // on-disk cksum type +// buffer[2] = 2; +// // on-disk flags +// buffer[3] = 0; +// +// // on-disk block size +// lfs_ssize_t d = 4; +// lfs_ssize_t d_ = lfs_toleb128(lfs->cfg->block_size, &buffer[d], 5); +// LFS_ASSERT(d_ >= 0); +// d += d_; +// +// // on-disk block count +// d_ = lfs_toleb128(lfs->cfg->block_count, &buffer[d], 5); +// LFS_ASSERT(d_ >= 0); +// d += d_; +// +// // on-disk mleaf limit +// d_ = lfs_toleb128(lfsr_mleafweight(lfs)-1, &buffer[d], 5); +// LFS_ASSERT(d_ >= 0); +// d += d_; +// +// // on-disk utag limit +// buffer[d] = 0x7f; +// d += 1; +// +// // on-disk attr limit +// d_ = lfs_toleb128(0x7fffffff, &buffer[d], 5); +// LFS_ASSERT(d_ >= 0); +// d += d_; +// +// // on-disk name limit +// d_ = lfs_toleb128(0xff, &buffer[d], 5); +// LFS_ASSERT(d_ >= 0); +// d += d_; +// +// // on-disk file limit +// d_ = lfs_toleb128(0x7fffffff, &buffer[d], 5); +// LFS_ASSERT(d_ >= 0); +// d += d_; +// +// return LFSR_DATA_BUF(buffer, d); +//} /// Filesystem init functions /// @@ -6304,215 +6316,325 @@ static int lfsr_mountinited(lfs_t *lfs) { if (mdir->mid == -1) { // has magic string? lfsr_data_t data; - err = lfsr_mdir_lookup(lfs, mdir, -1, LFSR_TAG_SUPERMAGIC, + err = lfsr_mdir_lookup(lfs, mdir, -1, LFSR_TAG_MAGIC, NULL, &data); - if (err && err != LFS_ERR_NOENT) { + if (err) { + if (err == LFS_ERR_NOENT) { + LFS_ERROR("No littlefs magic found"); + return LFS_ERR_INVAL; + } return err; } - if (err != LFS_ERR_NOENT) { - lfs_scmp_t cmp = lfsr_data_cmp(lfs, &data, "littlefs", 8); - if (cmp < 0) { - return cmp; - } - - // treat corrupted magic as no magic - if (lfs_cmp(cmp) != 0) { - err = LFS_ERR_NOENT; - } + lfs_scmp_t cmp = lfsr_data_cmp(lfs, &data, "littlefs", 8); + if (cmp < 0) { + return cmp; } - if (err == LFS_ERR_NOENT) { + // treat corrupted magic as no magic + if (lfs_cmp(cmp) != 0) { LFS_ERROR("No littlefs magic found"); return LFS_ERR_INVAL; } - // lookup the superconfig - err = lfsr_mdir_lookup(lfs, mdir, -1, LFSR_TAG_SUPERCONFIG, + // check the disk version + err = lfsr_mdir_lookup(lfs, mdir, -1, LFSR_TAG_VERSION, + NULL, &data); + if (err) { + if (err == LFS_ERR_NOENT) { + LFS_ERROR("No littlefs version found"); + return LFS_ERR_INVAL; + } + return err; + } + + uint32_t major_version; + err = lfsr_data_readleb128(lfs, &data, (int32_t*)&major_version); + if (err && err != LFS_ERR_CORRUPT) { + return err; + } + if (err == LFS_ERR_CORRUPT) { + major_version = -1; + } + + uint32_t minor_version; + err = lfsr_data_readleb128(lfs, &data, (int32_t*)&minor_version); + if (err && err != LFS_ERR_CORRUPT) { + return err; + } + if (err == LFS_ERR_CORRUPT) { + minor_version = -1; + } + + if (major_version != LFS_DISK_VERSION_MAJOR + || minor_version > LFS_DISK_VERSION_MINOR) { + LFS_ERROR("Incompatible version v%"PRId32".%"PRId32 + " (!= v%"PRId32".%"PRId32")", + major_version, + minor_version, + LFS_DISK_VERSION_MAJOR, + LFS_DISK_VERSION_MINOR); + return LFS_ERR_INVAL; + } + + // check for any flags + err = lfsr_mdir_lookup(lfs, mdir, -1, LFSR_TAG_FLAGS, + NULL, &data); + if (err && err != LFS_ERR_NOENT) { + return err; + } + + uint32_t flags = 0; + if (err != LFS_ERR_NOENT) { + err = lfsr_data_readleb128(lfs, &data, (int32_t*)&flags); + if (err && err != LFS_ERR_CORRUPT) { + return err; + } + if (err == LFS_ERR_CORRUPT) { + flags = -1; + } + } + + if (flags != 0) { + LFS_ERROR("Incompatible flags 0x%"PRIx32, flags); + return LFS_ERR_INVAL; + } + + // check checksum type + err = lfsr_mdir_lookup(lfs, mdir, -1, LFSR_TAG_CKSUMTYPE, + NULL, &data); + if (err && err != LFS_ERR_NOENT) { + return err; + } + + uint32_t cksum_type = 0; + if (err != LFS_ERR_NOENT) { + err = lfsr_data_readleb128(lfs, &data, (int32_t*)&cksum_type); + if (err && err != LFS_ERR_CORRUPT) { + return err; + } + if (err == LFS_ERR_CORRUPT) { + cksum_type = -1; + } + } + + if (cksum_type != 0) { + LFS_ERROR("Incompatible cksum type 0x%"PRId32, cksum_type); + return LFS_ERR_INVAL; + } + + // check redundancy type + err = lfsr_mdir_lookup(lfs, mdir, -1, LFSR_TAG_REDUNDTYPE, + NULL, &data); + if (err && err != LFS_ERR_NOENT) { + return err; + } + + uint32_t redund_type = 0; + if (err != LFS_ERR_NOENT) { + err = lfsr_data_readleb128(lfs, &data, (int32_t*)&redund_type); + if (err && err != LFS_ERR_CORRUPT) { + return err; + } + if (err == LFS_ERR_CORRUPT) { + redund_type = -1; + } + } + + if (redund_type != 0) { + LFS_ERROR("Incompatible redund type 0x%"PRId32, redund_type); + return LFS_ERR_INVAL; + } + + // check block limit / block size + err = lfsr_mdir_lookup(lfs, mdir, -1, LFSR_TAG_BLOCKLIMIT, + NULL, &data); + if (err && err != LFS_ERR_NOENT) { + return err; + } + + uint32_t block_limit = 0; + if (err != LFS_ERR_NOENT) { + err = lfsr_data_readleb128(lfs, &data, (int32_t*)&block_limit); + if (err && err != LFS_ERR_CORRUPT) { + return err; + } + if (err == LFS_ERR_CORRUPT) { + block_limit = -1; + } + } + + if (block_limit != lfs->cfg->block_size-1) { + LFS_ERROR("Incompatible block size %"PRId32" " + "(!= %"PRId32")", + block_limit+1, + lfs->cfg->block_size); + return LFS_ERR_INVAL; + } + + // check disk limit / block count + err = lfsr_mdir_lookup(lfs, mdir, -1, LFSR_TAG_DISKLIMIT, + NULL, &data); + if (err && err != LFS_ERR_NOENT) { + return err; + } + + uint32_t disk_limit = 0; + if (err != LFS_ERR_NOENT) { + err = lfsr_data_readleb128(lfs, &data, (int32_t*)&disk_limit); + if (err && err != LFS_ERR_CORRUPT) { + return err; + } + if (err == LFS_ERR_CORRUPT) { + disk_limit = -1; + } + } + + if (disk_limit != lfs->cfg->block_count-1) { + LFS_ERROR("Incompatible block count %"PRId32" " + "(!= %"PRId32")", + disk_limit+1, + lfs->cfg->block_count); + return LFS_ERR_INVAL; + } + + // read the mleaf limit + err = lfsr_mdir_lookup(lfs, mdir, -1, LFSR_TAG_MLEAFLIMIT, + NULL, &data); + if (err) { + if (err == LFS_ERR_NOENT) { + LFS_ERROR("No mleaf limit found"); + return LFS_ERR_INVAL; + } + return err; + } + + uint32_t mleaf_limit; + err = lfsr_data_readleb128(lfs, &data, (int32_t*)&mleaf_limit); + if (err && err != LFS_ERR_CORRUPT) { + return err; + } + if (err == LFS_ERR_CORRUPT) { + mleaf_limit = -1; + } + + // we only support power-of-two mleaf weights, this unlikely to + // ever to change since mleaf weights are pretty arbitrary + if (lfs_popc(mleaf_limit+1) != 1) { + LFS_ERROR("Incompatible mleaf weight %"PRId32, mleaf_limit+1); + return LFS_ERR_INVAL; + } + + lfs->mleaf_bits = lfs_nlog2(mleaf_limit); + + // read the size limit + err = lfsr_mdir_lookup(lfs, mdir, -1, LFSR_TAG_SIZELIMIT, + NULL, &data); + if (err) { + if (err == LFS_ERR_NOENT) { + LFS_ERROR("No size limit found"); + return LFS_ERR_INVAL; + } + return err; + } + + uint32_t size_limit; + err = lfsr_data_readleb128(lfs, &data, (int32_t*)&size_limit); + if (err && err != LFS_ERR_CORRUPT) { + return err; + } + if (err == LFS_ERR_CORRUPT) { + size_limit = -1; + } + + if (size_limit > lfs->size_limit) { + LFS_ERROR("Incompatible size limit (%"PRId32" > %"PRId32")", + size_limit, + lfs->size_limit); + return LFS_ERR_INVAL; + } + + lfs->size_limit = size_limit; + + // read the name limit + err = lfsr_mdir_lookup(lfs, mdir, -1, LFSR_TAG_NAMELIMIT, + NULL, &data); + if (err) { + if (err == LFS_ERR_NOENT) { + LFS_ERROR("No name limit found"); + return LFS_ERR_INVAL; + } + return err; + } + + uint32_t name_limit; + err = lfsr_data_readleb128(lfs, &data, (int32_t*)&name_limit); + if (err && err != LFS_ERR_CORRUPT) { + return err; + } + if (err == LFS_ERR_CORRUPT) { + name_limit = -1; + } + + if (name_limit > lfs->name_limit) { + LFS_ERROR("Incompatible name limit (%"PRId32" > %"PRId32")", + name_limit, + lfs->name_limit); + return LFS_ERR_INVAL; + } + + lfs->name_limit = name_limit; + + // check the utag limit + err = lfsr_mdir_lookup(lfs, mdir, -1, LFSR_TAG_UTAGLIMIT, NULL, &data); if (err && err != LFS_ERR_NOENT) { return err; } if (err != LFS_ERR_NOENT) { - // check the major/minor version - int32_t major_version; - int32_t minor_version; - err = lfsr_data_readleb128(lfs, &data, &major_version); - // treat any leb128 overflows as out-of-range values + 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) { - err = lfsr_data_readleb128(lfs, &data, &minor_version); - // treat any leb128 overflows as out-of-range values - if (err && err != LFS_ERR_CORRUPT) { - return err; - } - } - - if (err == LFS_ERR_CORRUPT - || major_version != LFS_DISK_VERSION_MAJOR - || minor_version > LFS_DISK_VERSION_MINOR) { - LFS_ERROR("Incompatible version v%"PRId32".%"PRId32 - " (!= v%"PRId32".%"PRId32")", - (err ? -1 : major_version), - (err ? -1 : minor_version), - LFS_DISK_VERSION_MAJOR, - LFS_DISK_VERSION_MINOR); - return LFS_ERR_INVAL; + if (err == LFS_ERR_CORRUPT) { + utag_limit = -1; } - // check the on-disk cksum type - int32_t cksum_type; - err = lfsr_data_readleb128(lfs, &data, &cksum_type); - // treat any leb128 overflows as out-of-range values - if (err && err != LFS_ERR_CORRUPT) { - return err; - } - - if (err == LFS_ERR_CORRUPT || cksum_type != 2) { - LFS_ERROR("Incompatible cksum type 0x%"PRIx32 - " (!= 0x%"PRIx32")", - (err ? -1 : cksum_type), - 2); - return LFS_ERR_INVAL; - } - - // check for any on-disk flags - int32_t flags; - err = lfsr_data_readleb128(lfs, &data, &flags); - // treat any leb128 overflows as out-of-range values - if (err && err != LFS_ERR_CORRUPT) { - return err; - } - - if (err == LFS_ERR_CORRUPT || flags != 0) { - LFS_ERROR("Incompatible flags 0x%"PRIx32 - " (!= 0x%"PRIx32")", - (err ? -1 : flags), - 0); - return LFS_ERR_INVAL; - } - - // check the on-disk block size - // TODO actually use this - lfs_size_t block_size; - err = lfsr_data_readleb128(lfs, &data, (int32_t*)&block_size); - // treat any leb128 overflows as out-of-range values - if (err && err != LFS_ERR_CORRUPT) { - return err; - } - - if (err == LFS_ERR_CORRUPT - || block_size != lfs->cfg->block_size) { - LFS_ERROR("Incompatible block size 0x%"PRIx32 - " (!= 0x%"PRIx32")", - (err ? (lfs_size_t)-1 : block_size), - lfs->cfg->block_size); - return LFS_ERR_INVAL; - } - - // check the on-disk block count - // TODO actually use this - lfs_block_t block_count; - err = lfsr_data_readleb128(lfs, &data, (int32_t*)&block_count); - // treat any leb128 overflows as out-of-range values - if (err && err != LFS_ERR_CORRUPT) { - return err; - } - - if (err == LFS_ERR_CORRUPT - || block_count != lfs->cfg->block_count) { - LFS_ERROR("Incompatible block count 0x%"PRIx32 - " (!= 0x%"PRIx32")", - (err ? (lfs_block_t)-1 : block_count), - lfs->cfg->block_count); - return LFS_ERR_INVAL; - } - - // check the on-disk mtree limit - // TODO actually use this - lfsr_mid_t mleaf_limit; - err = lfsr_data_readleb128(lfs, &data, (int32_t*)&mleaf_limit); - // treat any leb128 overflows as out-of-range values - if (err && err != LFS_ERR_CORRUPT) { - return err; - } - - if (err == LFS_ERR_CORRUPT - || mleaf_limit != lfsr_mleafweight(lfs)-1) { - LFS_ERROR("Incompatible mleaf limit 0x%"PRIx32, - (err ? (lfsr_mid_t)-1 : mleaf_limit)); - return LFS_ERR_INVAL; - } - - // check the on-disk utag limit - // TODO actually use this - int32_t utag_limit; - err = lfsr_data_readleb128(lfs, &data, &utag_limit); - // treat any leb128 overflows as out-of-range values - if (err && err != LFS_ERR_CORRUPT) { - return err; - } - - if (err == LFS_ERR_CORRUPT || utag_limit != 0x7f) { - LFS_ERROR("Incompatible utag limit 0x%"PRIx32 - " (> 0x%"PRIx32")", - (err ? -1 : utag_limit), + // only 7-bit utags are supported + if (utag_limit != 0x7f) { + LFS_ERROR("Incompatible utag limit " + "(%"PRId32" != %"PRId32")", + utag_limit, 0x7f); return LFS_ERR_INVAL; } + } - // check the on-disk attr limit - // TODO actually use this - lfs_ssize_t attr_limit; - err = lfsr_data_readleb128(lfs, &data, &attr_limit); - // treat any leb128 overflows as out-of-range values + // check the uattr limit + err = lfsr_mdir_lookup(lfs, mdir, -1, LFSR_TAG_UATTRLIMIT, + NULL, &data); + if (err && err != LFS_ERR_NOENT) { + return err; + } + + if (err != LFS_ERR_NOENT) { + 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 || attr_limit != 0x7fffffff) { - LFS_ERROR("Incompatible attr limit 0x%"PRIx32 - " (> 0x%"PRIx32")", - (err ? -1 : attr_limit), - 0x7fffffff); - return LFS_ERR_INVAL; + if (err == LFS_ERR_CORRUPT) { + uattr_limit = -1; } - // check the on-disk name limit - // TODO actually use this - lfs_ssize_t name_limit; - err = lfsr_data_readleb128(lfs, &data, &name_limit); - // treat any leb128 overflows as out-of-range values - if (err && err != LFS_ERR_CORRUPT) { - return err; - } - - if (err == LFS_ERR_CORRUPT || name_limit != 0xff) { - LFS_ERROR("Incompatible name limit 0x%"PRIx32 - " (> 0x%"PRIx32")", - (err ? -1 : name_limit), - 0xff); - return LFS_ERR_INVAL; - } - - // check the on-disk file limit - // TODO actually use this - lfs_soff_t file_limit; - err = lfsr_data_readleb128(lfs, &data, &file_limit); - // treat any leb128 overflows as out-of-range values - if (err && err != LFS_ERR_CORRUPT) { - return err; - } - - if (err == LFS_ERR_CORRUPT || file_limit != 0x7fffffff) { - LFS_ERROR("Incompatible file limit 0x%"PRIx32 - " (> 0x%"PRIx32")", - (err ? -1 : file_limit), - 0x7fffffff); + // only >=block_size uattrs are (implicitly) supported + if (uattr_limit < lfs->cfg->block_size-1) { + LFS_ERROR("Incompatible uattr limit " + "(%"PRId32" < %"PRId32")", + uattr_limit, + lfs->cfg->block_size-1); return LFS_ERR_INVAL; } } @@ -6563,11 +6685,6 @@ static int lfsr_mountinited(lfs_t *lfs) { } static int lfsr_formatinited(lfs_t *lfs) { - // create superconfig - uint8_t superconfig_buf[LFSR_SUPERCONFIG_DSIZE]; - lfsr_data_t superconfig_data = lfsr_data_fromsuperconfig(lfs, - superconfig_buf); - for (int i = 0; i < 2; i++) { // write superblock to both rbyds in the root mroot to hopefully // avoid mounting an older filesystem on disk @@ -6588,11 +6705,18 @@ static int lfsr_formatinited(lfs_t *lfs) { // our initial superblock contains a couple things: // - our magic string, "littlefs" - // - the superconfig, format-time configuration + // - any format-time configuration // - the root's bookmark tag, which reserves did = 0 for the root err = lfsr_rbyd_commit(lfs, &rbyd, LFSR_ATTRS( - LFSR_ATTR(-1, SUPERMAGIC, 0, BUF("littlefs", 8)), - LFSR_ATTR(-1, SUPERCONFIG, 0, DATA(superconfig_data)), + LFSR_ATTR(-1, MAGIC, 0, BUF("littlefs", 8)), + LFSR_ATTR(-1, VERSION, 0, BUF(((const uint8_t[2]){ + LFS_DISK_VERSION_MAJOR, + LFS_DISK_VERSION_MINOR}), 2)), + LFSR_ATTR(-1, BLOCKLIMIT, 0, LEB128(lfs->cfg->block_size-1)), + LFSR_ATTR(-1, DISKLIMIT, 0, LEB128(lfs->cfg->block_count-1)), + LFSR_ATTR(-1, MLEAFLIMIT, 0, LEB128(lfsr_mleafweight(lfs)-1)), + LFSR_ATTR(-1, SIZELIMIT, 0, LEB128(0x7fffffff)), + LFSR_ATTR(-1, NAMELIMIT, 0, LEB128(0xff)), LFSR_ATTR(0, BOOKMARK, +1, NAME(0, NULL, 0)))); if (err) { return err; @@ -6794,7 +6918,7 @@ int lfsr_mkdir(lfs_t *lfs, const char *path) { } // check that name fits - if (name_size > lfs->name_max) { + if (name_size > lfs->name_limit) { return LFS_ERR_NAMETOOLONG; } @@ -7019,7 +7143,7 @@ int lfsr_rename(lfs_t *lfs, const char *old_path, const char *new_path) { // there are a few cases we need to watch out for if (!exists) { // check that name fits - if (new_name_size > lfs->name_max) { + if (new_name_size > lfs->name_limit) { return LFS_ERR_NAMETOOLONG; } @@ -10848,26 +10972,18 @@ static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) { lfs->lookahead.acked = 0; // check that the size limits are sane - LFS_ASSERT(lfs->cfg->name_max <= LFS_NAME_MAX); - lfs->name_max = lfs->cfg->name_max; - if (!lfs->name_max) { - lfs->name_max = LFS_NAME_MAX; + LFS_ASSERT(lfs->cfg->name_limit <= LFS_NAME_MAX); + lfs->name_limit = lfs->cfg->name_limit; + if (!lfs->name_limit) { + lfs->name_limit = LFS_NAME_MAX; } - LFS_ASSERT(lfs->cfg->file_max <= LFS_FILE_MAX); - lfs->file_max = lfs->cfg->file_max; - if (!lfs->file_max) { - lfs->file_max = LFS_FILE_MAX; + LFS_ASSERT(lfs->cfg->size_limit <= LFS_FILE_MAX); + lfs->size_limit = lfs->cfg->size_limit; + if (!lfs->size_limit) { + lfs->size_limit = LFS_FILE_MAX; } - LFS_ASSERT(lfs->cfg->attr_max <= LFS_ATTR_MAX); - lfs->attr_max = lfs->cfg->attr_max; - if (!lfs->attr_max) { - lfs->attr_max = LFS_ATTR_MAX; - } - - LFS_ASSERT(lfs->cfg->metadata_max <= lfs->cfg->block_size); - // setup default state lfs->root[0] = LFS_BLOCK_NULL; lfs->root[1] = LFS_BLOCK_NULL; diff --git a/lfs.h b/lfs.h index 99c4db53..1d439a8e 100644 --- a/lfs.h +++ b/lfs.h @@ -75,11 +75,12 @@ typedef int32_t lfsr_sdid_t; #define LFS_FILE_MAX 2147483647 #endif -// Maximum size of custom attributes in bytes, may be redefined, but there is -// no real benefit to using a smaller LFS_ATTR_MAX. Limited to <= 1022. -#ifndef LFS_ATTR_MAX -#define LFS_ATTR_MAX 1022 -#endif +// TODO rm me +//// Maximum size of custom attributes in bytes, may be redefined, but there is +//// no real benefit to using a smaller LFS_ATTR_MAX. Limited to <= 1022. +//#ifndef LFS_ATTR_MAX +//#define LFS_ATTR_MAX 1022 +//#endif // Possible error codes, these are negative to allow // valid positive return values @@ -261,23 +262,24 @@ struct lfs_config { // larger names except the size of the info struct which is controlled by // the LFS_NAME_MAX define. Defaults to LFS_NAME_MAX when zero. Stored in // superblock and must be respected by other littlefs drivers. - lfs_size_t name_max; + lfs_size_t name_limit; // Optional upper limit on files in bytes. No downside for larger files // but must be <= LFS_FILE_MAX. Defaults to LFS_FILE_MAX when zero. Stored // in superblock and must be respected by other littlefs drivers. - lfs_size_t file_max; + lfs_size_t size_limit; - // Optional upper limit on custom attributes in bytes. No downside for - // larger attributes size but must be <= LFS_ATTR_MAX. Defaults to - // LFS_ATTR_MAX when zero. - lfs_size_t attr_max; - - // Optional upper limit on total space given to metadata pairs in bytes. On - // devices with large blocks (e.g. 128kB) setting this to a low size (2-8kB) - // can help bound the metadata compaction time. Must be <= block_size. - // Defaults to block_size when zero. - lfs_size_t metadata_max; +// TODO rm me +// // Optional upper limit on custom attributes in bytes. No downside for +// // larger attributes size but must be <= LFS_ATTR_MAX. Defaults to +// // LFS_ATTR_MAX when zero. +// lfs_size_t attr_max; +// +// // Optional upper limit on total space given to metadata pairs in bytes. On +// // devices with large blocks (e.g. 128kB) setting this to a low size (2-8kB) +// // can help bound the metadata compaction time. Must be <= block_size. +// // Defaults to block_size when zero. +// lfs_size_t metadata_max; }; // File info structure @@ -505,9 +507,8 @@ typedef struct lfs { } lookahead; const struct lfs_config *cfg; - lfs_size_t name_max; - lfs_size_t file_max; - lfs_size_t attr_max; + lfs_size_t name_limit; + lfs_off_t size_limit; // begin lfsr things lfsr_grm_t grm; diff --git a/scripts/dbgbtree.py b/scripts/dbgbtree.py index 109cf35a..bc8a263a 100755 --- a/scripts/dbgbtree.py +++ b/scripts/dbgbtree.py @@ -9,8 +9,19 @@ import struct TAG_NULL = 0x0000 -TAG_SUPERMAGIC = 0x0003 -TAG_SUPERCONFIG = 0x0004 +TAG_CONFIG = 0x0000 +TAG_MAGIC = 0x0003 +TAG_VERSION = 0x0004 +TAG_FLAGS = 0x0005 +TAG_CKSUMTYPE = 0x0006 +TAG_REDUNDTYPE = 0x0007 +TAG_BLOCKLIMIT = 0x0008 +TAG_DISKLIMIT = 0x0009 +TAG_MLEAFLIMIT = 0x000a +TAG_SIZELIMIT = 0x000b +TAG_NAMELIMIT = 0x000c +TAG_UTAGLIMIT = 0x000d +TAG_UATTRLIMIT = 0x000e TAG_GSTATE = 0x0100 TAG_GRM = 0x0100 TAG_NAME = 0x0200 @@ -122,12 +133,21 @@ def tagrepr(tag, w, size, off=None): return 'null%s%s' % ( ' w%d' % w if w else '', ' %d' % size if size else '') - elif tag == TAG_SUPERMAGIC: - return 'supermagic%s %d' % ( - ' w%d' % w if w else '', - size) - elif tag == TAG_SUPERCONFIG: - return 'superconfig%s %d' % ( + elif (tag & 0xff00) == TAG_CONFIG: + return '%s%s %d' % ( + 'magic' if tag == TAG_MAGIC + else 'version' if tag == TAG_VERSION + else 'flags' if tag == TAG_FLAGS + else 'cksumtype' if tag == TAG_CKSUMTYPE + else 'redundtype' if tag == TAG_REDUNDTYPE + else 'blocklimit' if tag == TAG_BLOCKLIMIT + else 'disklimit' if tag == TAG_DISKLIMIT + else 'mleaflimit' if tag == TAG_MLEAFLIMIT + else 'sizelimit' if tag == TAG_SIZELIMIT + else 'namelimit' if tag == TAG_NAMELIMIT + else 'utaglimit' if tag == TAG_UTAGLIMIT + else 'uattrlimit' if tag == TAG_UATTRLIMIT + else 'config 0x%02x' % (tag & 0xff), ' w%d' % w if w else '', size) elif (tag & 0xff00) == TAG_GSTATE: diff --git a/scripts/dbglfs.py b/scripts/dbglfs.py index 3b5d403c..54788948 100755 --- a/scripts/dbglfs.py +++ b/scripts/dbglfs.py @@ -10,8 +10,19 @@ import struct TAG_NULL = 0x0000 -TAG_SUPERMAGIC = 0x0003 -TAG_SUPERCONFIG = 0x0004 +TAG_CONFIG = 0x0000 +TAG_MAGIC = 0x0003 +TAG_VERSION = 0x0004 +TAG_FLAGS = 0x0005 +TAG_CKSUMTYPE = 0x0006 +TAG_REDUNDTYPE = 0x0007 +TAG_BLOCKLIMIT = 0x0008 +TAG_DISKLIMIT = 0x0009 +TAG_MLEAFLIMIT = 0x000a +TAG_SIZELIMIT = 0x000b +TAG_NAMELIMIT = 0x000c +TAG_UTAGLIMIT = 0x000d +TAG_UATTRLIMIT = 0x000e TAG_GSTATE = 0x0100 TAG_GRM = 0x0100 TAG_NAME = 0x0200 @@ -132,12 +143,21 @@ def tagrepr(tag, w, size, off=None): return 'null%s%s' % ( ' w%d' % w if w else '', ' %d' % size if size else '') - elif tag == TAG_SUPERMAGIC: - return 'supermagic%s %d' % ( - ' w%d' % w if w else '', - size) - elif tag == TAG_SUPERCONFIG: - return 'superconfig%s %d' % ( + elif (tag & 0xff00) == TAG_CONFIG: + return '%s%s %d' % ( + 'magic' if tag == TAG_MAGIC + else 'version' if tag == TAG_VERSION + else 'flags' if tag == TAG_FLAGS + else 'cksumtype' if tag == TAG_CKSUMTYPE + else 'redundtype' if tag == TAG_REDUNDTYPE + else 'blocklimit' if tag == TAG_BLOCKLIMIT + else 'disklimit' if tag == TAG_DISKLIMIT + else 'mleaflimit' if tag == TAG_MLEAFLIMIT + else 'sizelimit' if tag == TAG_SIZELIMIT + else 'namelimit' if tag == TAG_NAMELIMIT + else 'utaglimit' if tag == TAG_UTAGLIMIT + else 'uattrlimit' if tag == TAG_UATTRLIMIT + else 'config 0x%02x' % (tag & 0xff), ' w%d' % w if w else '', size) elif (tag & 0xff00) == TAG_GSTATE: @@ -625,32 +645,164 @@ class Rbyd: break -# read the superconfig -def superconfig(mroot): - done, rid, tag, w, j, d, data, _ = mroot.lookup(-1, TAG_SUPERCONFIG) - if done or rid != -1 or tag != TAG_SUPERCONFIG: - return {} - j += d +# read the config +class Config: + def __init__(self, mroot=None): + # read the config from the mroot + self.config = {} + if mroot is not None: + tag = 0 + while True: + done, rid, tag, w, j, d, data, _ = mroot.lookup(-1, tag+0x1) + if done or rid != -1 or (tag & 0xff00) != TAG_CONFIG: + break + + self.config[tag] = (j+d, data) + + # accessors for known config + @ft.cached_property + def magic(self): + if TAG_MAGIC in self.config: + _, data = self.config[TAG_MAGIC] + return data + else: + return None + + @ft.cached_property + def version(self): + if TAG_VERSION in self.config: + _, data = self.config[TAG_VERSION] + d = 0 + major, d_ = fromleb128(data[d:]); d += d_ + minor, d_ = fromleb128(data[d:]); d += d_ + return (major, minor) + else: + return (None, None) + + @ft.cached_property + def flags(self): + if TAG_FLAGS in self.config: + _, data = self.config[TAG_FLAGS] + flags, _ = fromleb128(data) + return flags + else: + return None + + @ft.cached_property + def cksum_type(self): + if TAG_CKSUMTYPE in self.config: + _, data = self.config[TAG_CKSUMTYPE] + cksum_type, _ = fromleb128(data) + return cksum_type + else: + return None + + @ft.cached_property + def redund_type(self): + if TAG_REDUNDTYPE in self.config: + _, data = self.config[TAG_REDUNDTYPE] + redund_type, _ = fromleb128(data) + return redund_type + else: + return None + + @ft.cached_property + def block_limit(self): + if TAG_BLOCKLIMIT in self.config: + _, data = self.config[TAG_BLOCKLIMIT] + block_limit, _ = fromleb128(data) + return block_limit + else: + return None + + @ft.cached_property + def disk_limit(self): + if TAG_DISKLIMIT in self.config: + _, data = self.config[TAG_DISKLIMIT] + disk_limit, _ = fromleb128(data) + return disk_limit + else: + return None + + @ft.cached_property + def mleaf_limit(self): + if TAG_MLEAFLIMIT in self.config: + _, data = self.config[TAG_MLEAFLIMIT] + mleaf_limit, _ = fromleb128(data) + return mleaf_limit + else: + return None + + @ft.cached_property + def size_limit(self): + if TAG_SIZELIMIT in self.config: + _, data = self.config[TAG_SIZELIMIT] + size_limit, _ = fromleb128(data) + return size_limit + else: + return None + + @ft.cached_property + def name_limit(self): + if TAG_NAMELIMIT in self.config: + _, data = self.config[TAG_NAMELIMIT] + name_limit, _ = fromleb128(data) + return name_limit + 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 + + def repr(self): + def crepr(tag, data): + if tag == TAG_MAGIC: + return 'magic \"%s\"' % ''.join( + b if b >= ' ' and b <= '~' else '.' + for b in map(chr, self.magic)) + elif tag == TAG_VERSION: + return 'version v%d.%d' % self.version + elif tag == TAG_FLAGS: + return 'flags 0x%x' % self.flags + elif tag == TAG_CKSUMTYPE: + return 'cksum_type %d' % self.cksum_type + elif tag == TAG_REDUNDTYPE: + return 'redund_type %d' % self.redund_type + elif tag == TAG_BLOCKLIMIT: + return 'block_limit %d' % self.block_limit + elif tag == TAG_DISKLIMIT: + return 'disk_limit %d' % self.disk_limit + elif tag == TAG_MLEAFLIMIT: + return 'mleaf_limit %d' % self.mleaf_limit + elif tag == TAG_SIZELIMIT: + return 'size_limit %d' % self.size_limit + elif tag == TAG_NAMELIMIT: + return 'name_limit %d' % self.name_limit + elif tag == TAG_UTAGLIMIT: + return 'utag_limit %d' % self.utag_limit + elif tag == TAG_UATTRLIMIT: + return 'uattr_limit %d' % self.uattr_limit + else: + return 'config 0x%02x %d' % (tag, len(data)) + + for tag, (j, data) in sorted(self.config.items()): + yield crepr(tag, data), tag, j, data - config = co.OrderedDict() - d = 0 - def next(name): - nonlocal config, d - c, d_ = fromleb128(data[d:]) - config[name] = (c, j, data[d:d+d_]) - d += d_ - next('major_version') - next('minor_version') - next('csum_type') - next('flags') - next('block_size') - next('block_count') - next('mlimit') - next('utag_limit') - next('attr_limit') - next('name_limit') - next('file_limit') - return config # collect gstate class GState: @@ -695,25 +847,21 @@ class GState: mid % self.mleaf_weight)) return rms -def grepr(tag, data, mleaf_weight): - if tag == TAG_GRM: - d = 0 - count, d_ = fromleb128(data[d:]); d += d_ - rms = [] - if count <= 2: - for _ in range(count): - mid, d_ = fromleb128(data[d:]); d += d_ - rms.append(( - mid - (mid % mleaf_weight), - mid % mleaf_weight)) - return 'grm %s' % ( - 'none' if count == 0 - else ' '.join('%d.%d' % (mbid//mleaf_weight, rid) - for mbid, rid in rms) - if count <= 2 - else '0x%x' % count) - else: - return 'gstate 0x%02x %d' % (tag, len(data)) + def repr(self): + def grepr(tag, data): + if tag == TAG_GRM: + count, _ = fromleb128(data) + return 'grm %s' % ( + 'none' if count == 0 + else ' '.join('%d.%d' % (mbid//self.mleaf_weight, rid) + for mbid, rid in self.grm) + if count <= 2 + else '0x%x %d' % (count, len(data))) + else: + return 'gstate 0x%02x %d' % (tag, len(data)) + + for tag, data in sorted(self.gstate.items()): + yield grepr(tag, data), tag, data def frepr(mdir, rid, tag): if tag == TAG_BOOKMARK: @@ -770,14 +918,14 @@ def main(disk, mroots=None, *, # - find the actual mroot # - find the total weight # - are we corrupted? - # - collect superconfig + # - collect config # - collect gstate # - any missing or orphaned bookmark entries bweight = 0 rweight = 0 corrupted = False gstate = GState(mleaf_weight) - config = {} + config = Config() dir_dids = [(0, b'', -1, 0, None, -1, TAG_DID, 0)] bookmark_dids = [] @@ -792,8 +940,8 @@ def main(disk, mroots=None, *, rweight = max(rweight, mroot.weight) # yes we get gstate from all mroots gstate.xor(-1, 0, mroot) - # get the superconfig - config = superconfig(mroot) + # get the config + config = Config(mroot) # find any dids for rid, tag, w, j, d, data in mroot: @@ -942,8 +1090,8 @@ def main(disk, mroots=None, *, # print some information about the filesystem print('littlefs v%s.%s %s, rev %d, weight %d.%d' % ( - config.get('major_version', ('?',))[0], - config.get('minor_version', ('?',))[0], + config.version[0] if config.version[0] is not None else '?', + config.version[1] if config.version[1] is not None else '?', mroot.addr(), mroot.rev, bweight//mleaf_weight, 1*mleaf_weight)) # print header @@ -964,12 +1112,12 @@ def main(disk, mroots=None, *, 'data (truncated)' if not args.get('no_truncate') else '')) - # print superconfig? + # print config? if args.get('config'): - for i, (name, (c, j, data)) in enumerate(config.items()): + for i, (repr_, tag, j, data) in enumerate(config.repr()): print('%12s %-*s %s' % ( 'config:' if i == 0 else '', - w_width + 23, '%s %d' % (name, c), + w_width + 23, repr_, next(xxd(data, 8), '') if not args.get('no_truncate') else '')) @@ -979,7 +1127,7 @@ def main(disk, mroots=None, *, '', w_width, '', '%-22s%s' % ( - '%08x' % c, + '%04x %08x %07x' % (tag, 0, len(data)), ' %s' % ' '.join( '%08x' % fromle32( data[i*4 : min(i*4+4,len(data))]) @@ -998,11 +1146,10 @@ def main(disk, mroots=None, *, # print gstate? if args.get('gstate'): - for i, (tag, data) in enumerate(sorted(gstate.gstate.items())): + for i, (repr_, tag, data) in enumerate(gstate.repr()): print('%12s %-*s %s' % ( 'gstate:' if i == 0 else '', - w_width + 23, - grepr(tag, data, mleaf_weight), + w_width + 23, repr_, next(xxd(data, 8), '') if not args.get('no_truncate') else '')) @@ -1253,9 +1400,9 @@ if __name__ == "__main__": default='auto', help="When to use terminal colors. Defaults to 'auto'.") parser.add_argument( - '-c', '--config', '--superconfig', + '-c', '--config', action='store_true', - help="Show the on-disk superconfig.") + help="Show the on-disk config.") parser.add_argument( '-g', '--gstate', action='store_true', diff --git a/scripts/dbgmtree.py b/scripts/dbgmtree.py index aea7a6a0..b1545acf 100755 --- a/scripts/dbgmtree.py +++ b/scripts/dbgmtree.py @@ -9,8 +9,19 @@ import struct TAG_NULL = 0x0000 -TAG_SUPERMAGIC = 0x0003 -TAG_SUPERCONFIG = 0x0004 +TAG_CONFIG = 0x0000 +TAG_MAGIC = 0x0003 +TAG_VERSION = 0x0004 +TAG_FLAGS = 0x0005 +TAG_CKSUMTYPE = 0x0006 +TAG_REDUNDTYPE = 0x0007 +TAG_BLOCKLIMIT = 0x0008 +TAG_DISKLIMIT = 0x0009 +TAG_MLEAFLIMIT = 0x000a +TAG_SIZELIMIT = 0x000b +TAG_NAMELIMIT = 0x000c +TAG_UTAGLIMIT = 0x000d +TAG_UATTRLIMIT = 0x000e TAG_GSTATE = 0x0100 TAG_GRM = 0x0100 TAG_NAME = 0x0200 @@ -131,12 +142,21 @@ def tagrepr(tag, w, size, off=None): return 'null%s%s' % ( ' w%d' % w if w else '', ' %d' % size if size else '') - elif tag == TAG_SUPERMAGIC: - return 'supermagic%s %d' % ( - ' w%d' % w if w else '', - size) - elif tag == TAG_SUPERCONFIG: - return 'superconfig%s %d' % ( + elif (tag & 0xff00) == TAG_CONFIG: + return '%s%s %d' % ( + 'magic' if tag == TAG_MAGIC + else 'version' if tag == TAG_VERSION + else 'flags' if tag == TAG_FLAGS + else 'cksumtype' if tag == TAG_CKSUMTYPE + else 'redundtype' if tag == TAG_REDUNDTYPE + else 'blocklimit' if tag == TAG_BLOCKLIMIT + else 'disklimit' if tag == TAG_DISKLIMIT + else 'mleaflimit' if tag == TAG_MLEAFLIMIT + else 'sizelimit' if tag == TAG_SIZELIMIT + else 'namelimit' if tag == TAG_NAMELIMIT + else 'utaglimit' if tag == TAG_UTAGLIMIT + else 'uattrlimit' if tag == TAG_UATTRLIMIT + else 'config 0x%02x' % (tag & 0xff), ' w%d' % w if w else '', size) elif (tag & 0xff00) == TAG_GSTATE: diff --git a/scripts/dbgrbyd.py b/scripts/dbgrbyd.py index 584b36d8..290cc4f8 100755 --- a/scripts/dbgrbyd.py +++ b/scripts/dbgrbyd.py @@ -18,8 +18,19 @@ COLORS = [ TAG_NULL = 0x0000 -TAG_SUPERMAGIC = 0x0003 -TAG_SUPERCONFIG = 0x0004 +TAG_CONFIG = 0x0000 +TAG_MAGIC = 0x0003 +TAG_VERSION = 0x0004 +TAG_FLAGS = 0x0005 +TAG_CKSUMTYPE = 0x0006 +TAG_REDUNDTYPE = 0x0007 +TAG_BLOCKLIMIT = 0x0008 +TAG_DISKLIMIT = 0x0009 +TAG_MLEAFLIMIT = 0x000a +TAG_SIZELIMIT = 0x000b +TAG_NAMELIMIT = 0x000c +TAG_UTAGLIMIT = 0x000d +TAG_UATTRLIMIT = 0x000e TAG_GSTATE = 0x0100 TAG_GRM = 0x0100 TAG_NAME = 0x0200 @@ -123,12 +134,21 @@ def tagrepr(tag, w, size, off=None): return 'null%s%s' % ( ' w%d' % w if w else '', ' %d' % size if size else '') - elif tag == TAG_SUPERMAGIC: - return 'supermagic%s %d' % ( - ' w%d' % w if w else '', - size) - elif tag == TAG_SUPERCONFIG: - return 'superconfig%s %d' % ( + elif (tag & 0xff00) == TAG_CONFIG: + return '%s%s %d' % ( + 'magic' if tag == TAG_MAGIC + else 'version' if tag == TAG_VERSION + else 'flags' if tag == TAG_FLAGS + else 'cksumtype' if tag == TAG_CKSUMTYPE + else 'redundtype' if tag == TAG_REDUNDTYPE + else 'blocklimit' if tag == TAG_BLOCKLIMIT + else 'disklimit' if tag == TAG_DISKLIMIT + else 'mleaflimit' if tag == TAG_MLEAFLIMIT + else 'sizelimit' if tag == TAG_SIZELIMIT + else 'namelimit' if tag == TAG_NAMELIMIT + else 'utaglimit' if tag == TAG_UTAGLIMIT + else 'uattrlimit' if tag == TAG_UATTRLIMIT + else 'config 0x%02x' % (tag & 0xff), ' w%d' % w if w else '', size) elif (tag & 0xff00) == TAG_GSTATE: