fix: keep mtime when utimens only updates atime (UTIME_OMIT) - #386
Open
Yike-Ye wants to merge 1 commit into
Open
fix: keep mtime when utimens only updates atime (UTIME_OMIT)#386Yike-Ye wants to merge 1 commit into
Yike-Ye wants to merge 1 commit into
Conversation
libfuse passes a time that should be left unchanged as
{ tv_sec = 0, tv_nsec = UTIME_OMIT }. Since f045211 ("Make utimens(NULL)
work correctly") sshfs_utimens() only looked at tv_sec and replaced 0 with
the current time, so an atime-only update overwrote the file's mtime on
the server. On macOS this happens whenever Finder previews a file with
Quick Look.
SSH_FILEXFER_ATTR_ACMODTIME always sets both times, so when one of them is
UTIME_OMIT fetch the current attributes and send its current value, map
UTIME_NOW to the current time, and skip the request if both are omitted.
Add a regression test that updates only the atime with touch -a.
Refs: libfuse#282
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Yike-Ye
force-pushed
the
fix/utimens-utime-omit
branch
from
September 11, 2026 08:24
1b828b6 to
5195b4c
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
On an sshfs mount, updating only a file's access time also changes its modification time on the server. On macOS this is very visible: pressing Space in Finder to Quick Look a file immediately changes its "Date Modified" (macfuse/macfuse#1157). The same bug was reported for FreeBSD (and macOS) in #282.
Minimal reproducer:
On a local file system mtime stays at 2020-01-01; on sshfs it becomes the current time.
Root cause
When only one of the two times is being changed, libfuse's high-level
fuse_lib_setattr()passes the other one as{ .tv_sec = 0, .tv_nsec = UTIME_OMIT }(and "now" as{ .tv_sec = 0, .tv_nsec = UTIME_NOW }).f045211 ("Make utimens(NULL) work correctly", #130, fixing #112) made
sshfs_utimens()treattv_sec == 0as "now". That fixedUTIME_NOW, butUTIME_OMITalso hastv_sec == 0, so an omitted time is replaced with the current time. BecauseSSH_FILEXFER_ATTR_ACMODTIMEalways sets both atime and mtime, the server's mtime is overwritten.Tracing the arguments (temporary debug print in
sshfs_utimens(), macOS 27 + macFUSE 5.3.3) forutimensat(AT_FDCWD, f, {{0, UTIME_NOW}, {0, UTIME_OMIT}}, 0):@bfleischer also noted in macfuse/macfuse#1157 that the LoopbackFS reference file system does not show this, which points at sshfs.
Fix
Decide by
tv_nsecinstead of guessing fromtv_sec:UTIME_OMIT: send the time's current value, read withsshfs_getattr()(only when a time is actually omitted)UTIME_NOW: send the current time (keeps the utimensat(NULL) not handled correctly #112 fix)tv_secSFTP v3 cannot set just one of the two times, so reading the current value first is unavoidable; a concurrent change to the omitted time between the read and the SETSTAT would be lost.
Testing
tst_utimens_omitintest/test_sshfs.py: set both times 1000 s in the past, runtouch -a, assert that mtime is unchanged.touch -apassesUTIME_OMITfor mtime viautimensat().fusermount), so I calledtst_utimens,tst_utimens_now,tst_utimens_omit,tst_createandtst_open_writedirectly against alocalhost:mount using the same cache options astest_sshfs(): with this patch 5/5 pass; current master fails onlytst_utimens_omit.utimensat()and viasetattrlist(ATTR_CMN_ACCTIME)now keeps mtime;touch,touch -m -t,touch -tand creating a new file behave as before (no 1970 timestamps).Fixes #282
Refs: #112, #130, macfuse/macfuse#1157
🤖 Generated with Claude Code