-
Notifications
You must be signed in to change notification settings - Fork 945
Add LFS_READONLY define, to allow smaller builds providing read-only mode #480
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
Hi @maximevince, thanks for creating a PR! Sorry being late in getting to this. This looks great.
I hadn't thought about this, it has the nice side-effect that it is easy to know which functions are read-only in the source code 👍 |
|
Also the CI was broken but should be fixed now. It should trigger on the next push to this branch. |
|
It's good to see support for read-only mode in littlefs. I did make a very similar patch to this (with the option called Will be good to see this merged! |
Alright, nice to hear from you @dpgeorge. If you see any differences between the parts of the code I have |
|
Just updated the PR with a new commit:
I am not entirely sure what should happen to |
|
Oh that is a good question. I'm going to say no to both, I don't see how they could be used in read-only mode. If there some strange use case we're missing, they are easy to add back into the read-only build. It would be harder to remove them later. |
| if ((flags & 3) != LFS_O_RDONLY) { | ||
| #ifdef LFS_READONLY | ||
| return LFS_ERR_ROFS; | ||
| #else |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would think that files can only ever be opened in LFS_O_RDONLY mode, so this entire if-block can just be excluded with the #if .
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's also another similar block down below in this function which could also be #if'd out.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually that's a good point, an LFS_ASSERT((flags & 3) == LFS_O_RDONLY) would work better here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not a big fan of "crashing" with an assert, instead of returning an error when a lfs_file_opencfg() is called with the wrong arguments, but I'll implement it that way, if you want.
Let's say an OS tries to write to a file mounted using the LittleFS filesystem, but LittleFS is compiled for read-only. Wouldn't it be more elegant to return an error indicating it's not possible to write, instead of asserting ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At the moment, it should be an assert for consistency (writing a file opened as rdonly currently asserts, for example).
Returning an error can be more graceful if there is an OS that understands that error, but for many littlefs applications this isn't the case. It's easy for the context of the error to get lost as "file open failed". Looking at POSIX errors, which littlefs follows, writing to an rdonly file only returns EINVAL. An assert takes you directly to the source of the problem, sort of like a poor-man's stack trace.
There's also a code cost for returning/handling errors, an assert can be dropped at compile time. Runtime checks can be added in the OS, but they can't be removed. This makes asserts preferred for conditions that should never happen unless there is a programmer mistake.
But you're not the first person to comment on this, maybe it would be worth adding a configuration option to change the use-error asserts to return errors. The next steps there would be to create a separate issue/PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alright, thanks for the detailed explanation. I can understand the reasoning indeed.
I have just pushed a new commit implementing the LFS_ASSERT for write attempts, as well as more code commented out, in case of LFS_READONLY.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One idea would be to also #if out the write-based constants, like LFS_O_WRONLY. Then it would be impossible to compile code that opened files for writing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, that's an even better idea! @dpgeorge with the great ideas
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, I just pushed another commit implementing this idea.
I had to make some extra #ifdef sections in other functions checking for these flags, so please carefully review that.
What I also did, is marking each #endif section with a comment indicating which #ifdef it is corresponding to.
|
This is now looking almost exactly the same as the patch I made. |
| #endif | ||
|
|
||
| // internally used flags | ||
| LFS_F_DIRTY = 0x010000, // File does not match storage |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it possible (and worth it) also #if'ing these constants like LFS_F_DIRTY which are not supposed to be used in read-only mode?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It was easy for LFS_F_DIRTY and LFS_F_WRITING.
However, I am not sure about LFS_F_ERRED.
It's documented as "An error occurred during write", but it has one non-obvious use at the end of int lfs_file_opencfg().
It seems you could end up there when e.g. lfs_malloc() fails, or when opening a file for read hits an LFS_ERR_NOENT (here) (among other possibilities).
Both of which cases do not seem to be strictly write-related.
So I conclude that this constant is needed in LFS_READONLY mode as well.
This is implemented in 35cc31e
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, it's not actually used in lfs_file_close but in the implicit lfs_file_sync. The purpose of the LFS_F_ERRED flag is to tell lfs_file_close to release resources without writing out to disk. lfs_file_opencfg immediately calls lfs_file_close if an error occurs.
You have the implicit lfs_file_sync removed in LFS_READONLY mode, so it should be safe to remove this line as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I only noticed this comment now. I have ifdefed LFS_F_ERRED out completely. (99c960b)
|
This looks great, thanks for this! Will bring this in on the next minor release. I added a Travis job, so we should see a read-only build size in the CI results now. |
Add LFS_READONLY define, to allow smaller builds providing read-only mode
This PR tries to solve #162 , by providing an
LFS_READONLYdefine.LittleFS seems to be preferred over SPIFFS in many places and projects right now.
However, for my bootloader / firmware upgrade use-case, the code size matters a lot.
A bootloader could do with a read-only version of the file system, which should be a lot smaller in size.
With SPIFFS I am able to get a read-only version without caching compiled to around 3 KB (compiled for Cortex-M0),
while the read-write version with caching weighs in at around 13 KB, which is comparable to LittleFS.
By defining out a bunch of write-related functionality, I was able to reduce the read-only version of LittleFS to around 5 KB for that same Cortex-M0 target, which seems acceptable for bootloader use.
I have separately
#ifdef'ed every function that is only needed for writing functionality.However, I'm not sure if this is the preferred way, of if you'd rather have a single
#ifdefaround the whole lump of code.Let me know if I can do anything to improve this PR.
Cheers and thanks for the awesome project! 👍