Fixed data corruption with multiple write handles
Multiple write handles in littlefs has always been a bit confusing (hopefully improving in littlefs3), but I didn't realize it could lead to corrupted data. The problem, as noted by Ictogan1, is that syncs to related file handles ignores the LFS_F_DIRTY flag. If you open a file twice, and write to one handle, littlefs doesn't realize the other handle it out-of-date. This may not seem like a problem, but then littlefs is happy to reallocate those (still referenced) blocks, leading to data corruption: open(a, "quiche.txt") write(a) sync(a) // syncs a's contents open(b, "quiche.txt") truncate(b) sync(b) // syncs b's contents write(b) // may allocate from a rewind(a) read(a) // potentially corrupted --- What we want is to set LFS_F_DIRTY in all other file handles during lfs_file_sync, but doing so would force those file handles to sync during close. That would be even more confusing (not to mention backwards incompatible). In theory setting both LFS_F_DIRTY + LFS_F_ERRED could work, but that would prevent implicit syncs of writes that haven't actually errored: open(a, "quiche.txt") write(a) open(b, "quiche.txt") write(b) close(b) // syncs b's contents close(a) // should sync a's contents So, instead, as a somewhat clunky workaround, a new flag: LFS_F_DUSTY, which indicates a file does not match storage, but should not be synced during close. --- It's worth noting this is already fixed in littlefs3, which includes a more rigorous, and hopefully easier to use sync model. But in the meantime, this should at least prevent the loss of data. Added test_alloc_multihandle and test_alloc_multihandle_reuse to prevent a regression, test_alloc_multihandle_reuse does reproduce the bug. Found and reproduced by Ictogan1
This commit is contained in:
@@ -135,14 +135,15 @@ enum lfs_open_flags {
|
||||
|
||||
// internally used flags
|
||||
#ifndef LFS_READONLY
|
||||
LFS_F_DIRTY = 0x010000, // File does not match storage
|
||||
LFS_F_WRITING = 0x020000, // File has been written since last flush
|
||||
LFS_F_DIRTY = 0x00010000, // File does not match storage due to write
|
||||
LFS_F_DUSTY = 0x00020000, // File does not match storage due to desync
|
||||
LFS_F_WRITING = 0x00040000, // File has been written since last flush
|
||||
#endif
|
||||
LFS_F_READING = 0x040000, // File has been read since last flush
|
||||
LFS_F_READING = 0x00080000, // File has been read since last flush
|
||||
#ifndef LFS_READONLY
|
||||
LFS_F_ERRED = 0x080000, // An error occurred during write
|
||||
LFS_F_ERRED = 0x00100000, // An error occurred during write
|
||||
#endif
|
||||
LFS_F_INLINE = 0x100000, // Currently inlined in directory entry
|
||||
LFS_F_INLINE = 0x01000000, // Currently inlined in directory entry
|
||||
};
|
||||
|
||||
// File seek flags
|
||||
|
||||
Reference in New Issue
Block a user