What fsync actually promises
The man page says fsync() transfers all modified in-core data to the storage device. It does not say the data is on stable media, and that gap has undone more than one careful design.
Three layers, three promises
Data written by an application travels through the page cache, into the device's own cache, and finally onto the medium. write() reaches the first. fsync() reaches the second. Only the third survives a power loss, and it is the one the filesystem cannot see on its own.
A device with a volatile write cache will acknowledge the flush before the data is durable. The filesystem trusts that acknowledgement, because it has no way to verify it.
The barrier that is not there
Filesystems rely on a cache flush command to enforce ordering. If a device lies about having executed it, the ordering guarantees the filesystem assumes are simply not in effect, and a crash can leave a structure that was never valid on disk. This is why some drives are known to lose data on power failure despite a filesystem that is careful.
What to do about it
- Know whether your storage has a volatile cache, and whether it honours flush. On enterprise drives this is documented; on consumer drives it often is not.
- Batch your syncs. The cost of
fsync()is dominated by the flush round-trip; one sync for a group of writes is far cheaper than one each. - Do not use
fsync()as a substitute for a write barrier between two files. It orders one file's data, not the relationship between two. - Test the failure that matters. Pull power, do not just kill the process, and see what you actually get back.
The question is never whether fsync was called. It is whether the thing underneath it told the truth.