From c8bc6f96cda13e78d502fae1ce212328fb921c77 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Fri, 11 Nov 2022 15:34:12 -0600 Subject: [PATCH] Added lfs_fs_stat for accessing configuration in the superblocks This is necessary for accessing format-specific information stored in the superblock after mounting. As is common in other filesystems this also provides the filesystem usage. However collecting the filesystem usage is more expensive in littlefs than other filesystem (and less accurate because of CoW), so this may need to be cached after mount in the future. --- lfs.c | 29 +++++++++++++++++++ lfs.h | 32 +++++++++++++++++++++ tests/test_superblocks.toml | 55 +++++++++++++++++++++++++++++++++++++ 3 files changed, 116 insertions(+) diff --git a/lfs.c b/lfs.c index cf44f95f..38d09273 100644 --- a/lfs.c +++ b/lfs.c @@ -4825,6 +4825,21 @@ static lfs_ssize_t lfs_fs_rawsize(lfs_t *lfs) { return size; } +static int lfs_fs_rawstat(lfs_t *lfs, struct lfs_fsinfo *fsinfo) { + lfs_ssize_t usage = lfs_fs_rawsize(lfs); + if (usage < 0) { + return usage; + } + + fsinfo->block_size = lfs->block_size; + fsinfo->block_count = lfs->block_count; + fsinfo->block_usage = usage; + fsinfo->name_max = lfs->name_max; + fsinfo->file_max = lfs->file_max; + fsinfo->attr_max = lfs->attr_max; + return 0; +} + #ifdef LFS_MIGRATE ////// Migration from littelfs v1 below this ////// @@ -5948,6 +5963,20 @@ int lfs_dir_rewind(lfs_t *lfs, lfs_dir_t *dir) { return err; } +int lfs_fs_stat(lfs_t *lfs, struct lfs_fsinfo *fsinfo) { + int err = LFS_LOCK(lfs->cfg); + if (err) { + return err; + } + LFS_TRACE("lfs_fs_stat(%p, %p)", (void*)lfs, (void*)fsinfo); + + err = lfs_fs_rawstat(lfs, fsinfo); + + LFS_TRACE("lfs_fs_stat -> %d", err); + LFS_UNLOCK(lfs->cfg); + return err; +} + lfs_ssize_t lfs_fs_size(lfs_t *lfs) { int err = LFS_LOCK(lfs->cfg); if (err) { diff --git a/lfs.h b/lfs.h index d919aabc..bbe64efe 100644 --- a/lfs.h +++ b/lfs.h @@ -301,6 +301,33 @@ struct lfs_info { char name[LFS_NAME_MAX+1]; }; +// Filesystem info structure +// +// Some of these can also be found in lfs_config, but the values here respect +// what was stored in the superblock during lfs_format. +struct lfs_fsinfo { + // Size of a logical block in bytes. + lfs_size_t block_size; + + // Number of logical blocks on the block device. + lfs_size_t block_count; + + // Number of blocks in use, this is the same as lfs_fs_size. + // + // Note: Result is best effort. If files share COW structures, the returned + // size may be larger than the filesystem actually is. + lfs_size_t block_usage; + + // Upper limit on the length of file names in bytes. + lfs_size_t name_max; + + // Upper limit on the size of files in bytes. + lfs_size_t file_max; + + // Upper limit on the size of custom attributes in bytes. + lfs_size_t attr_max; +}; + // Custom attribute structure, used to describe custom attributes // committed atomically during file writes. struct lfs_attr { @@ -683,6 +710,11 @@ int lfs_dir_rewind(lfs_t *lfs, lfs_dir_t *dir); /// Filesystem-level filesystem operations +// Find info about the filesystem +// +// Fills out the fsinfo structure. Returns a negative error code on failure. +int lfs_fs_stat(lfs_t *lfs, struct lfs_fsinfo *fsinfo); + // Finds the current size of the filesystem // // Note: Result is best effort. If files share COW structures, the returned diff --git a/tests/test_superblocks.toml b/tests/test_superblocks.toml index 2625ecc3..1e34b3f2 100644 --- a/tests/test_superblocks.toml +++ b/tests/test_superblocks.toml @@ -157,7 +157,13 @@ code = ''' lfs_format(&lfs, cfg) => 0; cfg->block_size = 0; + lfs_mount(&lfs, cfg) => 0; + struct lfs_fsinfo fsinfo; + lfs_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.block_size == BLOCK_SIZE); + assert(fsinfo.block_count == BLOCK_COUNT); + lfs_file_t file; lfs_file_open(&lfs, &file, "test", LFS_O_CREAT | LFS_O_EXCL | LFS_O_WRONLY) => 0; @@ -166,6 +172,10 @@ code = ''' lfs_unmount(&lfs) => 0; lfs_mount(&lfs, cfg) => 0; + lfs_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.block_size == BLOCK_SIZE); + assert(fsinfo.block_count == BLOCK_COUNT); + lfs_file_open(&lfs, &file, "test", LFS_O_RDONLY) => 0; uint8_t buffer[256]; lfs_file_read(&lfs, &file, buffer, sizeof(buffer)) => 6; @@ -181,7 +191,13 @@ code = ''' lfs_format(&lfs, cfg) => 0; cfg->block_count = 0; + lfs_mount(&lfs, cfg) => 0; + struct lfs_fsinfo fsinfo; + lfs_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.block_size == BLOCK_SIZE); + assert(fsinfo.block_count == BLOCK_COUNT); + lfs_file_t file; lfs_file_open(&lfs, &file, "test", LFS_O_CREAT | LFS_O_EXCL | LFS_O_WRONLY) => 0; @@ -190,6 +206,45 @@ code = ''' lfs_unmount(&lfs) => 0; lfs_mount(&lfs, cfg) => 0; + lfs_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.block_size == BLOCK_SIZE); + assert(fsinfo.block_count == BLOCK_COUNT); + + lfs_file_open(&lfs, &file, "test", LFS_O_RDONLY) => 0; + uint8_t buffer[256]; + lfs_file_read(&lfs, &file, buffer, sizeof(buffer)) => 6; + lfs_file_close(&lfs, &file) => 0; + assert(memcmp(buffer, "hello!", 6) == 0); + lfs_unmount(&lfs) => 0; +''' + +# mount with unknown block_size+block_count +[cases.test_superblocks_unknown_block_size_count] +code = ''' + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + + cfg->block_size = 0; + cfg->block_count = 0; + + lfs_mount(&lfs, cfg) => 0; + struct lfs_fsinfo fsinfo; + lfs_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.block_size == BLOCK_SIZE); + assert(fsinfo.block_count == BLOCK_COUNT); + + lfs_file_t file; + lfs_file_open(&lfs, &file, "test", + LFS_O_CREAT | LFS_O_EXCL | LFS_O_WRONLY) => 0; + lfs_file_write(&lfs, &file, "hello!", 6) => 6; + lfs_file_close(&lfs, &file) => 0; + lfs_unmount(&lfs) => 0; + + lfs_mount(&lfs, cfg) => 0; + lfs_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.block_size == BLOCK_SIZE); + assert(fsinfo.block_count == BLOCK_COUNT); + lfs_file_open(&lfs, &file, "test", LFS_O_RDONLY) => 0; uint8_t buffer[256]; lfs_file_read(&lfs, &file, buffer, sizeof(buffer)) => 6;