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;