[native] Skip F_FULLFSYNC on macOS for network file systems#130779
[native] Skip F_FULLFSYNC on macOS for network file systems#130779adamsitnik with Copilot wants to merge 7 commits into
Conversation
Don't use F_FULLFSYNC on macOS for network file systems (NFS, SMB, CIFS, SMB2) as it may cause pending writes to be discarded. Use the same detection logic already used by the locking code (fstatfs + FileSystemNameSupportsLocking). Fixes #124722 Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
On fstatfs failure, return 1 (treat as network FS) to safely skip F_FULLFSYNC and avoid potential data loss. Also improve comment clarity on return value logic. Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
|
Azure Pipelines: Successfully started running 3 pipeline(s). 12 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
adamsitnik
left a comment
There was a problem hiding this comment.
@copilot please address my feedback
|
Tagging subscribers to this area: @dotnet/area-system-io |
Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
…pal_io.c Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
adamsitnik
left a comment
There was a problem hiding this comment.
@copilot address my feedback
…ckTheFile in managed code Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
|
Azure Pipelines: Successfully started running 3 pipeline(s). 12 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
…avior Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
| get | ||
| { | ||
| NullableBool useFullFsync = _useFullFsync; | ||
| if (useFullFsync == NullableBool.Undefined && !IsClosed && OperatingSystem.IsApplePlatform()) |
There was a problem hiding this comment.
SystemNative_FSync uses TARGET_OSX only.
| if (useFullFsync == NullableBool.Undefined && !IsClosed && OperatingSystem.IsApplePlatform()) | |
| if (useFullFsync == NullableBool.Undefined && !IsClosed && OperatingSystem.IsMacOS()) |
| NullableBool useFullFsync = _useFullFsync; | ||
| if (useFullFsync == NullableBool.Undefined && !IsClosed && OperatingSystem.IsApplePlatform()) | ||
| { | ||
| _useFullFsync = useFullFsync = Interop.Sys.FileSystemSupportsLocking(this, Interop.Sys.LockOperations.LOCK_SH, accessWrite: true) |
There was a problem hiding this comment.
The PR reuses SystemNative_FileSystemSupportsLocking as a proxy for "is this a network FS," but that function's contract is about locking and its result depends on lockOperation/accessWrite, so it can't simply be renamed to IsNetworkFileSystem. Consider extracting the shared network-FS detection (the FileSystemNameSupportsLocking nfs/cifs/smb/smb2 check) into a dedicated helper.
|
I tried to reproduce #124722 on a macOS client (macOS 26.5, arm64) connecting to a Samba server running in WSL2, but was not able to trigger the truncation. What I tried:
In every case all files came out at their exact expected size — 0 truncated. |
@sschultze is there something more specific required to reproduce the problem (#124722)? |
|
Workflow state for the Holistic Review Orchestrator. {
"version": 5,
"last_dispatched_commit": "f16793c0f1928285802c14f72b4d5e31e792e5c4",
"last_dispatched_base_ref": "main",
"last_dispatched_base_sha": "051c1fd3c13fea8c1e60b68d9c7f63c3173e5593",
"last_reviewed_commit": "f16793c0f1928285802c14f72b4d5e31e792e5c4",
"last_reviewed_base_ref": "main",
"last_reviewed_base_sha": "051c1fd3c13fea8c1e60b68d9c7f63c3173e5593",
"last_recorded_worker_run_id": "29688125118",
"review_attempt_commit": "",
"review_attempt_base_ref": "",
"review_attempt_count": 0,
"max_review_attempts": 5,
"review_history_format": "holistic-review-disclosure-v1",
"review_history": [
{
"commit": "f16793c0f1928285802c14f72b4d5e31e792e5c4",
"review_id": 4730810937
}
]
} |
There was a problem hiding this comment.
Holistic Review
Motivation: On macOS, F_FULLFSYNC issued against network file systems (NFS/SMB/CIFS/SMB2) can return success while silently discarding pending writes, causing undetected data loss. Because the existing fallback to fsync only triggers when F_FULLFSYNC fails, the corruption goes unnoticed (#124722). The PR proactively skips F_FULLFSYNC on file systems known to misbehave.
Approach: Rather than adding a new native file-system probe, the PR threads a useFullFSync flag from managed code into SystemNative_FSync. SafeFileHandle.UseFullFSync lazily computes and caches the decision by reusing the existing FileSystemSupportsLocking(LOCK_SH, accessWrite: true) probe — the same syscall/name-matching machinery already used to skip advisory locking on the identical set of network file systems (#44546, #53182). The probe is gated on OperatingSystem.IsApplePlatform() and only queried on first flush, so callers that never flush to disk pay no syscall. Native SystemNative_FSync now only attempts F_FULLFSYNC when the flag is set (still falling back to fsync on failure), and ignores the flag on non-Apple platforms via (void)useFullFSync. The p/invoke declaration, both WASM callhelper prototypes, and a doc-comment typo are updated to match. Reusing the proven locking probe keeps the network-FS list authoritative in one place.
Summary: A well-scoped, low-risk fix that reuses an established mechanism instead of duplicating file-system detection. Behavior on local file systems is unchanged (F_FULLFSYNC still attempted, fsync fallback preserved), and non-macOS platforms are unaffected since the flag is only consulted inside the TARGET_OSX block. The caching design correctly treats NullableBool.Undefined as "use full fsync," so a closed handle or a not-yet-probed handle stays on the durable path. The change is consistent with the concurrent CanLockTheFile/SystemNative_FileSystemSupportsLocking design and has already received a maintainer approval (LGTM from adamsitnik). I concur: LGTM.
Detailed Findings
No blocking issues found.
Minor (non-blocking): UseFullFSync gates the probe on OperatingSystem.IsApplePlatform(), which is true for macOS, iOS, tvOS, and Mac Catalyst, but the native F_FULLFSYNC path is compiled only under #ifdef TARGET_OSX. On non-macOS Apple platforms the useFullFSync argument is discarded via (void)useFullFSync, so the managed FileSystemSupportsLocking probe performs an fstatfs syscall (once per handle, on first flush) whose result is never used. This is harmless and only costs one cached syscall on iOS/tvOS/MacCatalyst; if you want to avoid it you could narrow the gate to OperatingSystem.IsMacOS() to match the native compilation, but this is not required for correctness.
Note
This review was generated by this repository's Holistic Review agentic workflow to complement the built-in Copilot review.
Generated by Holistic Review · 80.9 AIC · ⌖ 14.8 AIC · ⊞ 10K
|
Closing as we are unable to test it properly. The recommended fix is to report a bug directly in Samba (#124722 (comment)) |
On macOS,
F_FULLFSYNCon network file systems (NFS, SMB, CIFS, SMB2) can return success while silently discarding pending writes, causing data corruption. The existingfsyncfallback only triggers on failure, so the data loss goes undetected.Changes in
src/native/libs/System.Native/pal_io.c:IsNetworkFileSystem(int fileDescriptor)— macOS-only static helper that callsfstatfsand reuses the existingFileSystemNameSupportsLockinglogic to identify NFS/CIFS/SMB/SMB2 mounts. Conservatively returns1(is network FS) whenfstatfsfails, to skipF_FULLFSYNCand avoid data loss.SystemNative_FSyncto skipF_FULLFSYNCentirely whenIsNetworkFileSystemreturns true, going directly tofsync. Behavior on local file systems is unchanged.This mirrors the approach already used by
CanLockTheFile/SystemNative_FileSystemSupportsLocking, which skip advisory locking on the same set of network file systems for the same category of reasons.