AltraMayor / AltraMayor/f3

macOS + exFAT: F3 produces false corruption due to fsync semantics and POSIX_FADV_DONTNEED

Open
#252 0 comments 2 reactions 0 assignees View on GitHub
Dominant language
C
Stars
3.4k
Forks
177
Avg merge
2d 15h
Merged PRs (30d)
2

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)

---

Image

## Root Cause Analysis

```c
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

1. `write()` populates the page cache

2. `fsync()` flushes _known dirty pages_, but:

- does **not** guarantee all delayed clusters are committed

3. The **last cluster** (commonly one exFAT cluster = 128 KB) may still be pending

4. `POSIX_FADV_DONTNEED` aggressively discards page cache

5. 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.

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.