diff options
author | Brian Pugh <bnp117@gmail.com> | 2023-08-17 07:46:04 +0300 |
---|---|---|
committer | Brian Pugh <bnp117@gmail.com> | 2023-08-17 08:23:34 +0300 |
commit | 73285278b98e0b15a608325b3f7e4c5b8e0c6b75 (patch) | |
tree | 8b73f14a47a61dc51418748e258214ff88d3a267 | |
parent | 611c9b20db2b99faee261daa7cc9bbe175d3eaca (diff) |
refactor lfs_scan_for_state_updates and lfs_scan_for_superblock out of lfs_format
-rw-r--r-- | lfs.c | 312 |
1 files changed, 181 insertions, 131 deletions
@@ -4237,6 +4237,175 @@ static int lfs_deinit(lfs_t *lfs) { return 0; } +static int lfs_scan_for_superblock(lfs_t *lfs, lfs_superblock_t *superblock){ + // scan directory blocks for superblock and any global updates + lfs_mdir_t dir = {.tail = {0, 1}}; + lfs_block_t tortoise[2] = {LFS_BLOCK_NULL, LFS_BLOCK_NULL}; + lfs_size_t tortoise_i = 1; + lfs_size_t tortoise_period = 1; + while (!lfs_pair_isnull(dir.tail)) { + // detect cycles with Brent's algorithm + if (lfs_pair_issync(dir.tail, tortoise)) { + LFS_WARN("Cycle detected in tail list"); + return LFS_ERR_CORRUPT; + } + if (tortoise_i == tortoise_period) { + tortoise[0] = dir.tail[0]; + tortoise[1] = dir.tail[1]; + tortoise_i = 0; + tortoise_period *= 2; + } + tortoise_i += 1; + + // fetch next block in tail list + lfs_stag_t tag = lfs_dir_fetchmatch(lfs, &dir, dir.tail, + LFS_MKTAG(0x7ff, 0x3ff, 0), + LFS_MKTAG(LFS_TYPE_SUPERBLOCK, 0, 8), + NULL, + lfs_dir_find_match, &(struct lfs_dir_find_match){ + lfs, "littlefs", 8}); + if (tag < 0) { + return tag; + } + + // has superblock? + if (tag && !lfs_tag_isdelete(tag)) { + // update root + lfs->root[0] = dir.pair[0]; + lfs->root[1] = dir.pair[1]; + + // grab superblock + tag = lfs_dir_get(lfs, &dir, LFS_MKTAG(0x7ff, 0x3ff, 0), + LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, sizeof(*superblock)), + superblock); + if (tag < 0) { + return tag; + } + lfs_superblock_fromle32(superblock); + } + } + + return LFS_ERR_OK; +} + +static int lfs_scan_for_state_updates(lfs_t *lfs){ + int err; + // scan directory blocks for superblock and any global updates + lfs_mdir_t dir = {.tail = {0, 1}}; + lfs_block_t tortoise[2] = {LFS_BLOCK_NULL, LFS_BLOCK_NULL}; + lfs_size_t tortoise_i = 1; + lfs_size_t tortoise_period = 1; + while (!lfs_pair_isnull(dir.tail)) { + // detect cycles with Brent's algorithm + if (lfs_pair_issync(dir.tail, tortoise)) { + LFS_WARN("Cycle detected in tail list"); + return LFS_ERR_CORRUPT; + } + if (tortoise_i == tortoise_period) { + tortoise[0] = dir.tail[0]; + tortoise[1] = dir.tail[1]; + tortoise_i = 0; + tortoise_period *= 2; + } + tortoise_i += 1; + + // fetch next block in tail list + lfs_stag_t tag = lfs_dir_fetchmatch(lfs, &dir, dir.tail, + LFS_MKTAG(0x7ff, 0x3ff, 0), + LFS_MKTAG(LFS_TYPE_SUPERBLOCK, 0, 8), + NULL, + lfs_dir_find_match, &(struct lfs_dir_find_match){ + lfs, "littlefs", 8}); + if (tag < 0) { + return tag; + } + + // has gstate? + err = lfs_dir_getgstate(lfs, &dir, &lfs->gstate); + if (err) { + return err; + } + } + + return LFS_ERR_OK; +} + +static int lfs_validate_superblock(lfs_t *lfs, lfs_superblock_t *superblock){ + // check version + uint16_t major_version = (0xffff & (superblock->version >> 16)); + uint16_t minor_version = (0xffff & (superblock->version >> 0)); + 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_fs_disk_version_major(lfs), + lfs_fs_disk_version_minor(lfs)); + return LFS_ERR_INVAL; + } + + // 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_fs_disk_version_minor(lfs)) { + LFS_DEBUG("Found older minor version " + "v%"PRIu16".%"PRIu16" < v%"PRIu16".%"PRIu16, + 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); + } + + // check superblock configuration + if (superblock->name_max) { + if (superblock->name_max > lfs->name_max) { + LFS_ERROR("Unsupported name_max (%"PRIu32" > %"PRIu32")", + superblock->name_max, lfs->name_max); + return LFS_ERR_INVAL; + } + lfs->name_max = superblock->name_max; + } + + if (superblock->file_max) { + if (superblock->file_max > lfs->file_max) { + LFS_ERROR("Unsupported file_max (%"PRIu32" > %"PRIu32")", + superblock->file_max, lfs->file_max); + return LFS_ERR_INVAL; + } + lfs->file_max = superblock->file_max; + } + + if (superblock->attr_max) { + if (superblock->attr_max > lfs->attr_max) { + LFS_ERROR("Unsupported attr_max (%"PRIu32" > %"PRIu32")", + superblock->attr_max, lfs->attr_max); + return LFS_ERR_INVAL; + } + lfs->attr_max = superblock->attr_max; + } + + if (superblock->block_count != lfs->cfg->block_count) { + LFS_ERROR("Invalid block count (%"PRIu32" != %"PRIu32")", + superblock->block_count, lfs->cfg->block_count); + return LFS_ERR_INVAL; + } + + if (superblock->block_size != lfs->cfg->block_size) { + LFS_ERROR("Invalid block size (%"PRIu32" != %"PRIu32")", + superblock->block_size, lfs->cfg->block_size); + return LFS_ERR_INVAL; + } + + return LFS_ERR_OK; +} + + + + #ifndef LFS_READONLY static int lfs_rawformat(lfs_t *lfs, const struct lfs_config *cfg) { int err = 0; @@ -4309,139 +4478,20 @@ static int lfs_rawmount(lfs_t *lfs, const struct lfs_config *cfg) { return err; } - // scan directory blocks for superblock and any global updates - lfs_mdir_t dir = {.tail = {0, 1}}; - lfs_block_t tortoise[2] = {LFS_BLOCK_NULL, LFS_BLOCK_NULL}; - lfs_size_t tortoise_i = 1; - lfs_size_t tortoise_period = 1; - while (!lfs_pair_isnull(dir.tail)) { - // detect cycles with Brent's algorithm - if (lfs_pair_issync(dir.tail, tortoise)) { - LFS_WARN("Cycle detected in tail list"); - err = LFS_ERR_CORRUPT; - goto cleanup; - } - if (tortoise_i == tortoise_period) { - tortoise[0] = dir.tail[0]; - tortoise[1] = dir.tail[1]; - tortoise_i = 0; - tortoise_period *= 2; - } - tortoise_i += 1; - - // fetch next block in tail list - lfs_stag_t tag = lfs_dir_fetchmatch(lfs, &dir, dir.tail, - LFS_MKTAG(0x7ff, 0x3ff, 0), - LFS_MKTAG(LFS_TYPE_SUPERBLOCK, 0, 8), - NULL, - lfs_dir_find_match, &(struct lfs_dir_find_match){ - lfs, "littlefs", 8}); - if (tag < 0) { - err = tag; - goto cleanup; - } - - // has superblock? - if (tag && !lfs_tag_isdelete(tag)) { - // update root - lfs->root[0] = dir.pair[0]; - lfs->root[1] = dir.pair[1]; - - // grab superblock - lfs_superblock_t superblock; - tag = lfs_dir_get(lfs, &dir, LFS_MKTAG(0x7ff, 0x3ff, 0), - LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, sizeof(superblock)), - &superblock); - if (tag < 0) { - err = tag; - goto cleanup; - } - lfs_superblock_fromle32(&superblock); - - // check version - uint16_t major_version = (0xffff & (superblock.version >> 16)); - uint16_t minor_version = (0xffff & (superblock.version >> 0)); - 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_fs_disk_version_major(lfs), - lfs_fs_disk_version_minor(lfs)); - err = LFS_ERR_INVAL; - goto cleanup; - } - - // 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_fs_disk_version_minor(lfs)) { - LFS_DEBUG("Found older minor version " - "v%"PRIu16".%"PRIu16" < v%"PRIu16".%"PRIu16, - 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); - } - - // check superblock configuration - if (superblock.name_max) { - if (superblock.name_max > lfs->name_max) { - LFS_ERROR("Unsupported name_max (%"PRIu32" > %"PRIu32")", - superblock.name_max, lfs->name_max); - err = LFS_ERR_INVAL; - goto cleanup; - } - - lfs->name_max = superblock.name_max; - } - - if (superblock.file_max) { - if (superblock.file_max > lfs->file_max) { - LFS_ERROR("Unsupported file_max (%"PRIu32" > %"PRIu32")", - superblock.file_max, lfs->file_max); - err = LFS_ERR_INVAL; - goto cleanup; - } - - lfs->file_max = superblock.file_max; - } - - if (superblock.attr_max) { - if (superblock.attr_max > lfs->attr_max) { - LFS_ERROR("Unsupported attr_max (%"PRIu32" > %"PRIu32")", - superblock.attr_max, lfs->attr_max); - err = LFS_ERR_INVAL; - goto cleanup; - } - - lfs->attr_max = superblock.attr_max; - } - - if (superblock.block_count != lfs->cfg->block_count) { - LFS_ERROR("Invalid block count (%"PRIu32" != %"PRIu32")", - superblock.block_count, lfs->cfg->block_count); - err = LFS_ERR_INVAL; - goto cleanup; - } + lfs_superblock_t superblock; + err = lfs_scan_for_superblock(lfs, &superblock); + if(err) { + goto cleanup; + } - if (superblock.block_size != lfs->cfg->block_size) { - LFS_ERROR("Invalid block size (%"PRIu32" != %"PRIu32")", - superblock.block_size, lfs->cfg->block_size); - err = LFS_ERR_INVAL; - goto cleanup; - } - } + err = lfs_validate_superblock(lfs, &superblock); + if(err){ + return err; + } - // has gstate? - err = lfs_dir_getgstate(lfs, &dir, &lfs->gstate); - if (err) { - goto cleanup; - } + err = lfs_scan_for_state_updates(lfs); + if(err) { + goto cleanup; } // found superblock? |