-
Notifications
You must be signed in to change notification settings - Fork 163
Description
When running F3 on macOS with exFAT-formatted devices, the tool may report systematic data corruption (typically exactly one cluster, e.g. 128 KB = 256 sectors) even on brand-new, healthy storage.
After extensive investigation, this appears to be a tool–OS–filesystem semantic mismatch, not actual media corruption.
This issue does not reproduce on:
-
APFS (macOS)
-
Linux (ext4 / exFAT)
-
Repeated runs on the same device (often disappears)
Observed Symptoms
Typical output from f3read on macOS + exFAT:
-
Exactly one missing or corrupted cluster
-
Often 128 KB (256 sectors)
-
Reported as corrupted or overwritten
-
More visible on HDDs or slower devices
Example:
Corrupted: 128.00 KB (256 sectors)
exfat fs default cluster = 128KiB (256 sectors)
Root Cause Analysis
static int bdev_write_blocks(struct device *dev, const char *buf,
uint64_t first_pos, uint64_t last_pos)
{
struct block_device *bdev = dev_bdev(dev);
const int block_order = dev_get_block_order(dev);
size_t length = (last_pos - first_pos + 1) << block_order;
off_t offset = first_pos << block_order;
off_t off_ret = lseek(bdev->fd, offset, SEEK_SET);
int rc;
if (off_ret < 0)
return - errno;
assert(off_ret == offset);
rc = write_all(bdev->fd, buf, length);
if (rc)
return rc;
rc = fsync(bdev->fd); // <----- Not ready sync in macOS with exfat
if (rc)
return rc;
return posix_fadvise(bdev->fd, 0, 0, POSIX_FADV_DONTNEED); // <----- Drop page cache but not ready sync data to storage, data lose...
}Key point
On macOS,
fsync()does NOT guarantee physical media persistence.
This is a documented semantic difference from Linux.
What F3 currently does (simplified)
write(...) fsync(fd) posix_fadvise(fd, 0, 0, POSIX_FADV_DONTNEED)
What happens on macOS + exFAT
-
write()populates the page cache -
fsync()flushes known dirty pages, but:- does not guarantee all delayed clusters are committed
-
The last cluster (commonly one exFAT cluster = 128 KB) may still be pending
-
POSIX_FADV_DONTNEEDaggressively discards page cache -
On subsequent
read():-
data is reloaded from disk
-
but the last cluster may contain:
-
old data
-
zeroes
-
uninitialized content
-
-
👉 Result: exactly one missing cluster, consistently aligned to exFAT cluster size.
This is not a block alignment issue, not an ioctl issue, and not a hardware failure.
Why APFS Does Not Show This Problem
APFS uses:
-
Copy-on-write
-
Transactional block allocation + data + metadata updates
-
fsync()commits the whole transaction
Therefore, F3’s assumption:
“fsync means data is safely persistent”
happens to hold on APFS, but does not hold on exFAT.
Why This Is Not an exFAT “Bug”
exFAT:
-
Has no journaling
-
Has weak ordering guarantees
-
Relies on cooperative cache behavior
macOS’s exFAT driver prioritizes performance and allows delayed allocation.
The behavior is legal under POSIX and macOS semantics.
Why This Is Dangerous
-
Users may falsely conclude:
-
“My new SD card is defective”
-
“The device has bad sectors”
-
-
Especially harmful for:
-
camera media
-
large-capacity cards
-
HDD-based storage
-
In reality, the data may never have been written incorrectly at all.
Recommended Fixes
Option 1 (Best): Use macOS-specific full sync
On macOS, replace fsync() with:
fcntl(fd, F_FULLFSYNC)
This guarantees data reaches physical media.
Option 2: Remove POSIX_FADV_DONTNEED on macOS + exFAT
Avoid dropping page cache immediately after fsync() on macOS.
Option 3: Document the limitation clearly
At minimum, warn users:
F3 results on macOS + exFAT may be unreliable unless F_FULLFSYNC is used.
Summary
-
This is not hardware corruption
-
This is not block misalignment
-
This is a filesystem + OS semantic mismatch
-
F3’s assumptions are valid on Linux and APFS
-
They are not valid on macOS + exFAT
I’m reporting this to prevent false negatives and unnecessary device returns.