From 6236f460a4d459b4097a1775a17a1c2294086da9 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Tue, 25 Apr 2023 23:06:55 -0500 Subject: [PATCH] Added rough draft of the rest of superblock parsing --- lfs.c | 309 +++++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 220 insertions(+), 89 deletions(-) diff --git a/lfs.c b/lfs.c index 44e97387..236ffae7 100644 --- a/lfs.c +++ b/lfs.c @@ -4292,19 +4292,6 @@ static int lfsr_mdir_commit(lfs_t *lfs, lfsr_mdir_t *mdir, /// Superblock things /// -typedef struct lfsr_superconfig { - uint8_t major_version; - uint8_t minor_version; - uint8_t csum_type; - uint8_t flags; - lfs_size_t block_size; - lfs_off_t block_count; - uint8_t utag_limit; - lfs_size_t attr_limit; - lfs_size_t name_limit; - lfs_off_t file_limit; -} lfsr_superconfig_t; - // These are all leb128s, but we can expect smaller encodings // if we assume the version. // @@ -4323,46 +4310,56 @@ typedef struct lfsr_superconfig { #define LFSR_SUPERCONFIG_DSIZE (1+1+1+1+5+5+1+5+5+5) static lfs_ssize_t lfsr_superconfig_todisk(lfs_t *lfs, - const lfsr_superconfig_t *superconfig, uint8_t buffer[static LFSR_SUPERCONFIG_DSIZE]) { - // TODO wait, isn't this all derived from lfs_t? do we even need the - // lfsr_superconfig_t at all? - (void)lfs; - // shortcut the single-byte lebs - buffer[0] = superconfig->major_version; - buffer[1] = superconfig->minor_version; - buffer[2] = superconfig->csum_type; - buffer[3] = superconfig->flags; + // 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 csum 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(superconfig->block_size, &buffer[d], 5); + lfs_ssize_t d_ = lfs_toleb128(lfs->cfg->block_size, &buffer[d], 5); if (d_ < 0) { return d_; } d += d_; - d_ = lfs_toleb128(superconfig->block_count, &buffer[d], 5); + // on-disk block count + d_ = lfs_toleb128(lfs->cfg->block_count, &buffer[d], 5); if (d_ < 0) { return d_; } d += d_; - buffer[d] = superconfig->utag_limit; + // on-disk utag limit + buffer[d] = 0x7f; d += 1; - d_ = lfs_toleb128(superconfig->attr_limit, &buffer[d], 5); + // on-disk attr limit + d_ = lfs_toleb128(0x7fffffff, &buffer[d], 5); if (d_ < 0) { return d_; } d += d_; - d_ = lfs_toleb128(superconfig->name_limit, &buffer[d], 5); + // on-disk name limit + d_ = lfs_toleb128(0xff, &buffer[d], 5); if (d_ < 0) { return d_; } d += d_; - d_ = lfs_toleb128(superconfig->file_limit, &buffer[d], 5); + // on-disk file limit + d_ = lfs_toleb128(0x7fffffff, &buffer[d], 5); if (d_ < 0) { return d_; } @@ -4392,94 +4389,242 @@ static int lfsr_mountinited(lfs_t *lfs) { } // has magic string? - lfs_ssize_t id; - lfsr_tag_t tag; lfsr_data_t data; - err = lfsr_mdir_lookupnext(lfs, &mdir, -1, LFSR_TAG_SUPERMAGIC, - &id, &tag, NULL, &data); + err = lfsr_mdir_lookup(lfs, &mdir, -1, LFSR_TAG_SUPERMAGIC, &data); if (err && err != LFS_ERR_NOENT) { return err; } - if (err - || id != -1 - || tag != LFSR_TAG_SUPERMAGIC - || lfsr_data_size(data) != 8) { - LFS_ERROR("No littlefs magic found"); - return LFS_ERR_INVAL; + if (err != LFS_ERR_NOENT) { + int cmp; + err = lfsr_data_cmp(lfs, data, 0, "littlefs", 8, &cmp); + if (err) { + return err; + } + + // treat corrupted magic as no magic + if (cmp != 0) { + err = LFS_ERR_NOENT; + } } - int cmp; - err = lfsr_data_cmp(lfs, data, 0, "littlefs", 8, &cmp); - if (err) { - return err; - } - - if (cmp != 0) { + if (err == LFS_ERR_NOENT) { LFS_ERROR("No littlefs magic found"); return LFS_ERR_INVAL; } // lookup the superconfig - err = lfsr_mdir_lookupnext(lfs, &mdir, -1, LFSR_TAG_SUPERCONFIG, - &id, &tag, NULL, &data); + err = lfsr_mdir_lookup(lfs, &mdir, -1, LFSR_TAG_SUPERCONFIG, &data); if (err && err != LFS_ERR_NOENT) { return err; } - if (!(err - || id != -1 - || tag != LFSR_TAG_SUPERCONFIG)) { + if (err != LFS_ERR_NOENT) { // check the major/minor version uint32_t major_version; uint32_t minor_version; lfs_size_t d = 0; lfs_ssize_t d_ = lfsr_data_readleb128(lfs, data, d, &major_version); - if (d_ < 0) { - // treat leb128 overflow as an out-of-range value - if (d_ == LFS_ERR_CORRUPT) { - LFS_ERROR("Undecodable major version"); - return LFS_ERR_INVAL; - } + // treat any leb128 overflows as out-of-range values + if (d_ < 0 && d_ != LFS_ERR_CORRUPT) { return d_; } - d += d_; + if (d_ != LFS_ERR_CORRUPT) { + d += d_; - d_ = lfsr_data_readleb128(lfs, data, d, &minor_version); - if (d_ < 0) { - // treat leb128 overflow as an out-of-range value - if (d_ == LFS_ERR_CORRUPT) { - LFS_ERROR("Undecodable minor version"); - return LFS_ERR_INVAL; + d_ = lfsr_data_readleb128(lfs, data, d, &minor_version); + // treat any leb128 overflows as out-of-range values + if (d_ < 0 && d_ != LFS_ERR_CORRUPT) { + return d_; + } + if (d_ != LFS_ERR_CORRUPT) { + d += d_; } - return d_; } - if (major_version != LFS_DISK_VERSION_MAJOR + if (d_ == LFS_ERR_CORRUPT + || major_version != LFS_DISK_VERSION_MAJOR || minor_version > LFS_DISK_VERSION_MINOR) { - LFS_ERROR("Incompatible version v%"PRIu32".%"PRIu32, - major_version, minor_version); + LFS_ERROR("Incompatible version v%"PRIu32".%"PRIu32 + " (!= v%"PRIu32".%"PRIu32")", + (d_ == LFS_ERR_CORRUPT ? (uint32_t)-1 : major_version), + (d_ == LFS_ERR_CORRUPT ? (uint32_t)-1 : minor_version), + LFS_DISK_VERSION_MAJOR, + LFS_DISK_VERSION_MINOR); return LFS_ERR_INVAL; } - // TODO parse rest of the superblock + // check the on-disk csum type + uint32_t csum_type; + d_ = lfsr_data_readleb128(lfs, data, d, &csum_type); + // treat any leb128 overflows as out-of-range values + if (d_ < 0 && d_ != LFS_ERR_CORRUPT) { + return d_; + } + if (d_ != LFS_ERR_CORRUPT) { + d += d_; + } + + if (d_ == LFS_ERR_CORRUPT || csum_type != 2) { + LFS_ERROR("Incompatible csum type 0x%"PRIx32 + " (!= 0x%"PRIx32")", + (d_ == LFS_ERR_CORRUPT ? (uint32_t)-1 : csum_type), + 2); + return LFS_ERR_INVAL; + } + + // check for any on-disk flags + uint32_t flags; + d_ = lfsr_data_readleb128(lfs, data, d, &flags); + // treat any leb128 overflows as out-of-range values + if (d_ < 0 && d_ != LFS_ERR_CORRUPT) { + return d_; + } + if (d_ != LFS_ERR_CORRUPT) { + d += d_; + } + + if (d_ == LFS_ERR_CORRUPT || flags != 0) { + LFS_ERROR("Incompatible flags 0x%"PRIx32 + " (!= 0x%"PRIx32")", + (d_ == LFS_ERR_CORRUPT ? (uint32_t)-1 : flags), + 0); + return LFS_ERR_INVAL; + } + + // check the on-disk block size + // TODO actually use this + uint32_t block_size; + d_ = lfsr_data_readleb128(lfs, data, d, &block_size); + // treat any leb128 overflows as out-of-range values + if (d_ < 0 && d_ != LFS_ERR_CORRUPT) { + return d_; + } + if (d_ != LFS_ERR_CORRUPT) { + d += d_; + } + + if (d_ == LFS_ERR_CORRUPT || block_size != lfs->cfg->block_size) { + LFS_ERROR("Incompatible block size 0x%"PRIx32 + " (!= 0x%"PRIx32")", + (d_ == LFS_ERR_CORRUPT ? (uint32_t)-1 : block_size), + lfs->cfg->block_size); + return LFS_ERR_INVAL; + } + + // check the on-disk block count + // TODO actually use this + uint32_t block_count; + d_ = lfsr_data_readleb128(lfs, data, d, &block_count); + // treat any leb128 overflows as out-of-range values + if (d_ < 0 && d_ != LFS_ERR_CORRUPT) { + return d_; + } + if (d_ != LFS_ERR_CORRUPT) { + d += d_; + } + + if (d_ == LFS_ERR_CORRUPT || block_count != lfs->cfg->block_count) { + LFS_ERROR("Incompatible block count 0x%"PRIx32 + " (!= 0x%"PRIx32")", + (d_ == LFS_ERR_CORRUPT ? (uint32_t)-1 : block_count), + lfs->cfg->block_count); + return LFS_ERR_INVAL; + } + + // check the on-disk utag limit + // TODO actually use this + uint32_t utag_limit; + d_ = lfsr_data_readleb128(lfs, data, d, &utag_limit); + // treat any leb128 overflows as out-of-range values + if (d_ < 0 && d_ != LFS_ERR_CORRUPT) { + return d_; + } + if (d_ != LFS_ERR_CORRUPT) { + d += d_; + } + + if (d_ == LFS_ERR_CORRUPT || utag_limit != 0x7f) { + LFS_ERROR("Incompatible utag limit 0x%"PRIx32 + " (> 0x%"PRIx32")", + (d_ == LFS_ERR_CORRUPT ? (uint32_t)-1 : utag_limit), + 0x7f); + return LFS_ERR_INVAL; + } + + // check the on-disk attr limit + // TODO actually use this + uint32_t attr_limit; + d_ = lfsr_data_readleb128(lfs, data, d, &attr_limit); + // treat any leb128 overflows as out-of-range values + if (d_ < 0 && d_ != LFS_ERR_CORRUPT) { + return d_; + } + if (d_ != LFS_ERR_CORRUPT) { + d += d_; + } + + if (d_ == LFS_ERR_CORRUPT || attr_limit != 0x7fffffff) { + LFS_ERROR("Incompatible attr limit 0x%"PRIx32 + " (> 0x%"PRIx32")", + (d_ == LFS_ERR_CORRUPT ? (uint32_t)-1 : attr_limit), + 0x7fffffff); + return LFS_ERR_INVAL; + } + + // check the on-disk name limit + // TODO actually use this + uint32_t name_limit; + d_ = lfsr_data_readleb128(lfs, data, d, &name_limit); + // treat any leb128 overflows as out-of-range values + if (d_ < 0 && d_ != LFS_ERR_CORRUPT) { + return d_; + } + if (d_ != LFS_ERR_CORRUPT) { + d += d_; + } + + if (d_ == LFS_ERR_CORRUPT || name_limit != 0xff) { + LFS_ERROR("Incompatible name limit 0x%"PRIx32 + " (> 0x%"PRIx32")", + (d_ == LFS_ERR_CORRUPT ? (uint32_t)-1 : name_limit), + 0xff); + return LFS_ERR_INVAL; + } + + // check the on-disk file limit + // TODO actually use this + uint32_t file_limit; + d_ = lfsr_data_readleb128(lfs, data, d, &file_limit); + // treat any leb128 overflows as out-of-range values + if (d_ < 0 && d_ != LFS_ERR_CORRUPT) { + return d_; + } + if (d_ != LFS_ERR_CORRUPT) { + d += d_; + } + + if (d_ == LFS_ERR_CORRUPT || file_limit != 0x7fffffff) { + LFS_ERROR("Incompatible file limit 0x%"PRIx32 + " (> 0x%"PRIx32")", + (d_ == LFS_ERR_CORRUPT ? (uint32_t)-1 : file_limit), + 0x7fffffff); + return LFS_ERR_INVAL; + } } // lookup supermdir // // if we have a supermdir, this is actually a fake superblock and // we need to parse the next superblock in the chain - err = lfsr_mdir_lookupnext(lfs, &mdir, -1, LFSR_TAG_SUPERMDIR, - &id, &tag, NULL, &data); + err = lfsr_mdir_lookup(lfs, &mdir, -1, LFSR_TAG_SUPERMDIR, &data); if (err && err != LFS_ERR_NOENT) { return err; } - // no more supermdirs means we found our real superblock! - if (err - || id != -1 - || tag != LFSR_TAG_SUPERMDIR) { + // no more supermdirs means we found our real superblock + if (err == LFS_ERR_NOENT) { break; } @@ -4514,21 +4659,7 @@ int lfsr_unmount(lfs_t *lfs) { static int lfsr_formatinited(lfs_t *lfs) { uint8_t buf[LFSR_SUPERCONFIG_DSIZE]; - lfs_ssize_t d = lfsr_superconfig_todisk(lfs, - &(lfsr_superconfig_t){ - .major_version=LFS_DISK_VERSION_MAJOR, - .minor_version=LFS_DISK_VERSION_MINOR, - .csum_type=2, - .flags=0, - .block_size=lfs->cfg->block_size, - .block_count=lfs->cfg->block_count, - // TODO these should be defines - .utag_limit=0x7f, - .attr_limit=0x7fffffff, - .name_limit=0xff, - .file_limit=0x7fffffff, - }, - buf); + lfs_ssize_t d = lfsr_superconfig_todisk(lfs, buf); if (d < 0) { return d; }