Skip to content

Commit

Permalink
Handle read errors in alloc_cluster() to avoid "infinite" loop
Browse files Browse the repository at this point in the history
alloc_cluster() just ignores read errors, trying next cluster until it
either succeeds (finds an empty one) or runs out of clusters (after
checking all of them). A large volume may have quite a lot of clusters -
eg. a 16 GB SD card with a standard format has about 2 million clusters.
When a "persistent" read error happens during the alloc_cluster() (after
a successful mount operation) - for example a volume is physically
disconnected in a very inconvenient moment or the volume is/gets damaged
and all further reads fail - then this loop becomes practically
infinite. In one application we found a damaged SD card, for which reads
of first 600-700 blocks work perfectly fine, but any read beyond that
results in a SDIO interface timing-out (the card will not switch to
expected state within specified time). As the timeout for the operation
is ~100 ms, then the function would loop for over 2 days. The same card
just fails to work in a PC, where any read beyond first ~350 kB (which
is about 700 blocks) fails with an I/O error.

Fix this by returning from alloc_cluster() with an error when any read
operation fails.

Fixes dlbeer#15
  • Loading branch information
FreddieChopin committed Apr 24, 2023
1 parent e2a7466 commit ddd7cfe
Showing 1 changed file with 8 additions and 2 deletions.
10 changes: 8 additions & 2 deletions ufat.c
Original file line number Diff line number Diff line change
Expand Up @@ -642,15 +642,21 @@ static int alloc_cluster(struct ufat *uf, ufat_cluster_t *out,
for (i = 0; i < total; i++) {
const ufat_cluster_t idx = uf->alloc_ptr + 2;
ufat_cluster_t c;
int err;

uf->alloc_ptr = (uf->alloc_ptr + 1) % total;

/* Never use this cluster index in a FAT12 system */
if (idx == 0xff0 && uf->bpb.type == UFAT_TYPE_FAT12)
continue;

if (!ufat_read_fat(uf, idx, &c) && c == UFAT_CLUSTER_FREE) {
int err = ufat_write_fat(uf, idx, tail);
err = ufat_read_fat(uf, idx, &c);

if (err < 0)
return err;

if (c == UFAT_CLUSTER_FREE) {
err = ufat_write_fat(uf, idx, tail);

if (err < 0)
return err;
Expand Down

0 comments on commit ddd7cfe

Please sign in to comment.