Skip to content

Commit

Permalink
Added lfs_fs_stat for accessing configuration in the superblocks
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
geky committed Dec 7, 2022
1 parent 7e15e62 commit 0aa0c84
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 0 deletions.
29 changes: 29 additions & 0 deletions lfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 //////

Expand Down Expand Up @@ -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) {
Expand Down
32 changes: 32 additions & 0 deletions lfs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Expand Down
55 changes: 55 additions & 0 deletions tests/test_superblocks.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -181,7 +191,48 @@ 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;
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;
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;
Expand All @@ -190,6 +241,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;
Expand Down

0 comments on commit 0aa0c84

Please sign in to comment.