Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 40 additions & 2 deletions lfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -503,7 +503,6 @@ static inline void lfs_superblock_tole32(lfs_superblock_t *superblock) {
}
#endif

#ifndef LFS_NO_ASSERT
static bool lfs_mlist_isopen(struct lfs_mlist *head,
struct lfs_mlist *node) {
for (struct lfs_mlist **p = &head; *p; p = &(*p)->next) {
Expand All @@ -514,7 +513,6 @@ static bool lfs_mlist_isopen(struct lfs_mlist *head,

return false;
}
#endif

static void lfs_mlist_remove(lfs_t *lfs, struct lfs_mlist *mlist) {
for (struct lfs_mlist **p = &lfs->mlist; *p; p = &(*p)->next) {
Expand Down Expand Up @@ -6318,6 +6316,46 @@ lfs_soff_t lfs_file_size(lfs_t *lfs, lfs_file_t *file) {
return res;
}

int lfs_file_movehandle(lfs_t *lfs, lfs_file_t *old_file,
lfs_file_t *new_file) {
int err = LFS_LOCK(lfs->cfg);
if (err) {
return err;
}
LFS_TRACE("lfs_file_movehandle(%p, %p, %p)",
(void*)lfs, (void*)old_file, (void*)new_file);
LFS_ASSERT(lfs_mlist_isopen(lfs->mlist, (struct lfs_mlist*)old_file));
LFS_ASSERT(!lfs_mlist_isopen(lfs->mlist, (struct lfs_mlist*)new_file));

*new_file = *old_file;
lfs_mlist_remove(lfs, (struct lfs_mlist*)old_file);
lfs_mlist_append(lfs, (struct lfs_mlist*)new_file);

err = LFS_ERR_OK;

LFS_TRACE("lfs_file_movehandle -> %d", err);
LFS_UNLOCK(lfs->cfg);
return err;
}

int lfs_file_ishandleopen(lfs_t *lfs, lfs_file_t *file) {
int err = LFS_LOCK(lfs->cfg);
if (err) {
return err;
}
LFS_TRACE("lfs_file_ishandleopen(%p, %p)", (void*)lfs, (void*)file);

if (lfs_mlist_isopen(lfs->mlist, (struct lfs_mlist*)file)) {
err = LFS_ERR_OK;
} else {
err = LFS_ERR_BADF;
}

LFS_TRACE("lfs_file_ishandleopen -> %d", err);
LFS_UNLOCK(lfs->cfg);
return err;
}

#ifndef LFS_READONLY
int lfs_mkdir(lfs_t *lfs, const char *path) {
int err = LFS_LOCK(lfs->cfg);
Expand Down
14 changes: 14 additions & 0 deletions lfs.h
Original file line number Diff line number Diff line change
Expand Up @@ -656,6 +656,20 @@ int lfs_file_rewind(lfs_t *lfs, lfs_file_t *file);
// Returns the size of the file, or a negative error code on failure.
lfs_soff_t lfs_file_size(lfs_t *lfs, lfs_file_t *file);

// Move a file handle
//
// littlefs's file handles are somewhat expensive to move. Try to avoid
// needing to move them. This allows moving a file handle from old_file
// to new_file when needed for abstraction. After this call, old_file is
// invalid.
//
// Returns a negative error code on failure.
int lfs_file_movehandle(lfs_t *lfs, lfs_file_t *old_file, lfs_file_t *new_file);

// Check if a given file handle is open
//
// Returns LFS_ERR_OK if the file handle is open, else LFS_ERR_BADF
int lfs_file_ishandleopen(lfs_t *lfs, lfs_file_t *file);

/// Directory operations ///

Expand Down
58 changes: 58 additions & 0 deletions tests/test_files.toml
Original file line number Diff line number Diff line change
Expand Up @@ -537,3 +537,61 @@ code = '''
}
lfs_unmount(&lfs) => 0;
'''

[cases.test_files_ishandleopen]
code = '''
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs_file_t file;

lfs_file_ishandleopen(&lfs, &file) => LFS_ERR_BADF;

lfs_file_open(&lfs, &file, "hello", LFS_O_CREAT | LFS_O_WRONLY) => 0;

lfs_file_ishandleopen(&lfs, &file) => 0;

lfs_file_close(&lfs, &file) => 0;

lfs_file_ishandleopen(&lfs, &file) => LFS_ERR_BADF;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I hadn't thought about the return value of lfs_file_ishandleopen, LFS_ERR_BADF is a good choice. It's also the first use of this error code in the codebase.

Humorously, LFS_ERR_BADF is removed in v3-alpha, but we can bring it back for this purpose.

LFS_ERR_BADF also makes sense for a theoretical LFS_UNTRUSTED_USER build, where file operations error if the file is not open. Some users have asked for such a feature, but it's been low priority. (I'm also hesitant because it isn't bulletproof, we can't provide the same guardrails around if a filesystem is mounted, for example.)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Humorously, LFS_ERR_BADF is removed in v3-alpha, but we can bring it back for this purpose.

That is hilarious! I was also debating LFS_ERR_INVAL, but LFS_ERR_BADF just felt right.


lfs_unmount(&lfs) => 0;
'''

[cases.test_files_movehandle]
defines.idx = [0, 1, 2]
code = '''
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs_file_t file[3];
lfs_file_open(&lfs, &file[0], "a", LFS_O_CREAT | LFS_O_WRONLY) => 0;
lfs_file_open(&lfs, &file[1], "b", LFS_O_CREAT | LFS_O_WRONLY) => 0;
lfs_file_open(&lfs, &file[2], "c", LFS_O_CREAT | LFS_O_WRONLY) => 0;

lfs_file_t file_new;
lfs_file_movehandle(&lfs, &file[idx], &file_new) => 0;

for (int i = 0; i < 3; i++) {
if (i == idx) {
continue;
}

lfs_file_ishandleopen(&lfs, &file[i]) => 0;
}
lfs_file_ishandleopen(&lfs, &file_new) => 0;

for (int i = 0; i < 3; i++) {
if (i == idx) {
continue;
}

lfs_file_close(&lfs, &file[i]) => 0;
lfs_file_ishandleopen(&lfs, &file[i]) => LFS_ERR_BADF;
}

lfs_file_close(&lfs, &file_new) => 0;
lfs_file_ishandleopen(&lfs, &file_new) => LFS_ERR_BADF;

lfs_unmount(&lfs) => 0;
'''