From ddd7cfe1c77f1df88ffbfd405b84c64ee774950e Mon Sep 17 00:00:00 2001 From: Freddie Chopin Date: Mon, 24 Apr 2023 13:13:22 +0200 Subject: [PATCH] Handle read errors in alloc_cluster() to avoid "infinite" loop 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 #15 --- ufat.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/ufat.c b/ufat.c index a79cea0..ae3576f 100644 --- a/ufat.c +++ b/ufat.c @@ -642,6 +642,7 @@ 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; @@ -649,8 +650,13 @@ static int alloc_cluster(struct ufat *uf, ufat_cluster_t *out, 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;