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.
This commit is contained in:
Christopher Haster
2022-11-11 15:34:12 -06:00
parent 21e54ee458
commit c8bc6f96cd
3 changed files with 116 additions and 0 deletions
+29
View File
@@ -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) {
+32
View File
@@ -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
+55
View File
@@ -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;