Skip to content

Commit 843e3c6

Browse files
committed
Added sticky-bit for preventing file syncs after write errors
Short story, files are no longer committed to directories during file sync/close if the last write did not complete successfully. This avoids a set of interesting user-experience issues related to the end-of-life behaviour of the filesystem. As a filesystem approaches end-of-life, the chances of running into LFS_ERR_NOSPC grows rather quickly. Since this condition occurs after at the end of a devices life, it's likely that operating in these conditions hasn't been tested thoroughly. In the specific case of file-writes, you can hit an LFS_ERR_NOSPC after parts of the file have been written out. If the program simply continues and closes the file, the file is written out half completed. Since littlefs has a strong garuntee the prevents half-writes, it's unlikely this state of the file would be expected. To make things worse, since close is also responsible for memory cleanup, it's actually _impossible_ to continue working as it was without leaking memory. By prevent the file commits, end-of-life behaviour should at least retain a previous copy of the filesystem without any surprises.
1 parent 2612e1b commit 843e3c6

File tree

3 files changed

+28
-10
lines changed

3 files changed

+28
-10
lines changed

lfs.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1403,7 +1403,9 @@ int lfs_file_sync(lfs_t *lfs, lfs_file_t *file) {
14031403
return err;
14041404
}
14051405

1406-
if ((file->flags & LFS_F_DIRTY) && !lfs_pairisnull(file->pair)) {
1406+
if ((file->flags & LFS_F_DIRTY) &&
1407+
!(file->flags & LFS_F_ERRED) &&
1408+
!lfs_pairisnull(file->pair)) {
14071409
// update dir entry
14081410
lfs_dir_t cwd;
14091411
int err = lfs_dir_fetch(lfs, &cwd, file->pair);
@@ -1537,6 +1539,7 @@ lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file,
15371539
file->head, file->size,
15381540
file->pos-1, &file->block, &file->off);
15391541
if (err) {
1542+
file->flags |= LFS_F_ERRED;
15401543
return err;
15411544
}
15421545

@@ -1550,6 +1553,7 @@ lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file,
15501553
file->block, file->pos,
15511554
&file->block, &file->off);
15521555
if (err) {
1556+
file->flags |= LFS_F_ERRED;
15531557
return err;
15541558
}
15551559

@@ -1565,13 +1569,15 @@ lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file,
15651569
if (err == LFS_ERR_CORRUPT) {
15661570
goto relocate;
15671571
}
1572+
file->flags |= LFS_F_ERRED;
15681573
return err;
15691574
}
15701575

15711576
break;
15721577
relocate:
15731578
err = lfs_file_relocate(lfs, file);
15741579
if (err) {
1580+
file->flags |= LFS_F_ERRED;
15751581
return err;
15761582
}
15771583
}
@@ -1584,6 +1590,7 @@ lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file,
15841590
lfs_alloc_ack(lfs);
15851591
}
15861592

1593+
file->flags &= ~LFS_F_ERRED;
15871594
return size;
15881595
}
15891596

lfs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ enum lfs_open_flags {
7575
LFS_F_DIRTY = 0x10000, // File does not match storage
7676
LFS_F_WRITING = 0x20000, // File has been written since last flush
7777
LFS_F_READING = 0x40000, // File has been read since last flush
78+
LFS_F_ERRED = 0x80000, // An error occured during write
7879
};
7980

8081
// File seek flags

tests/test_alloc.sh

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ tests/test.py << TEST
121121
size = strlen("exhaustion");
122122
memcpy(buffer, "exhaustion", size);
123123
lfs_file_write(&lfs, &file[0], buffer, size) => size;
124+
lfs_file_sync(&lfs, &file[0]) => 0;
124125
125126
size = strlen("blahblahblahblah");
126127
memcpy(buffer, "blahblahblahblah", size);
@@ -142,6 +143,7 @@ tests/test.py << TEST
142143
lfs_mount(&lfs, &cfg) => 0;
143144
lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_RDONLY);
144145
size = strlen("exhaustion");
146+
lfs_file_size(&lfs, &file[0]) => size;
145147
lfs_file_read(&lfs, &file[0], buffer, size) => size;
146148
memcmp(buffer, "exhaustion", size) => 0;
147149
lfs_file_close(&lfs, &file[0]) => 0;
@@ -166,6 +168,7 @@ tests/test.py << TEST
166168
size = strlen("exhaustion");
167169
memcpy(buffer, "exhaustion", size);
168170
lfs_file_write(&lfs, &file[0], buffer, size) => size;
171+
lfs_file_sync(&lfs, &file[0]) => 0;
169172
170173
size = strlen("blahblahblahblah");
171174
memcpy(buffer, "blahblahblahblah", size);
@@ -187,6 +190,7 @@ tests/test.py << TEST
187190
lfs_mount(&lfs, &cfg) => 0;
188191
lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_RDONLY);
189192
size = strlen("exhaustion");
193+
lfs_file_size(&lfs, &file[0]) => size;
190194
lfs_file_read(&lfs, &file[0], buffer, size) => size;
191195
memcmp(buffer, "exhaustion", size) => 0;
192196
lfs_file_close(&lfs, &file[0]) => 0;
@@ -196,14 +200,14 @@ TEST
196200
echo "--- Dir exhaustion test ---"
197201
tests/test.py << TEST
198202
lfs_mount(&lfs, &cfg) => 0;
199-
lfs_stat(&lfs, "exhaustion", &info) => 0;
200-
lfs_size_t fullsize = info.size;
201203
lfs_remove(&lfs, "exhaustion") => 0;
202204
203205
lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT);
204206
size = strlen("blahblahblahblah");
205207
memcpy(buffer, "blahblahblahblah", size);
206-
for (lfs_size_t i = 0; i < fullsize - 2*512; i += size) {
208+
for (lfs_size_t i = 0;
209+
i < (cfg.block_count-6)*(cfg.block_size-8);
210+
i += size) {
207211
lfs_file_write(&lfs, &file[0], buffer, size) => size;
208212
}
209213
lfs_file_close(&lfs, &file[0]) => 0;
@@ -214,7 +218,11 @@ tests/test.py << TEST
214218
lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_APPEND);
215219
size = strlen("blahblahblahblah");
216220
memcpy(buffer, "blahblahblahblah", size);
217-
lfs_file_write(&lfs, &file[0], buffer, size) => size;
221+
for (lfs_size_t i = 0;
222+
i < (cfg.block_size-8);
223+
i += size) {
224+
lfs_file_write(&lfs, &file[0], buffer, size) => size;
225+
}
218226
lfs_file_close(&lfs, &file[0]) => 0;
219227
220228
lfs_mkdir(&lfs, "exhaustiondir") => LFS_ERR_NOSPC;
@@ -224,14 +232,14 @@ TEST
224232
echo "--- Chained dir exhaustion test ---"
225233
tests/test.py << TEST
226234
lfs_mount(&lfs, &cfg) => 0;
227-
lfs_stat(&lfs, "exhaustion", &info) => 0;
228-
lfs_size_t fullsize = info.size;
229-
230235
lfs_remove(&lfs, "exhaustion") => 0;
236+
231237
lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT);
232238
size = strlen("blahblahblahblah");
233239
memcpy(buffer, "blahblahblahblah", size);
234-
for (lfs_size_t i = 0; i < fullsize - 19*512; i += size) {
240+
for (lfs_size_t i = 0;
241+
i < (cfg.block_count-24)*(cfg.block_size-8);
242+
i += size) {
235243
lfs_file_write(&lfs, &file[0], buffer, size) => size;
236244
}
237245
lfs_file_close(&lfs, &file[0]) => 0;
@@ -247,7 +255,9 @@ tests/test.py << TEST
247255
lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT);
248256
size = strlen("blahblahblahblah");
249257
memcpy(buffer, "blahblahblahblah", size);
250-
for (lfs_size_t i = 0; i < fullsize - 20*512; i += size) {
258+
for (lfs_size_t i = 0;
259+
i < (cfg.block_count-26)*(cfg.block_size-8);
260+
i += size) {
251261
lfs_file_write(&lfs, &file[0], buffer, size) => size;
252262
}
253263
lfs_file_close(&lfs, &file[0]) => 0;

0 commit comments

Comments
 (0)