Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 32 additions & 8 deletions sshfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -2873,28 +2873,52 @@ static void sshfs_inc_modifver(void)
pthread_mutex_unlock(&sshfs.lock);
}

static int sshfs_getattr(const char *path, struct stat *stbuf,
struct fuse_file_info *fi);

/*
* SSH_FILEXFER_ATTR_ACMODTIME always sets both atime and mtime, so a time the
* caller wants left alone (UTIME_OMIT) has to be sent as its current value.
* Treating it as "now" makes an atime-only update (e.g. Finder recording that
* a file was previewed) overwrite the file's modification time.
*/
static time_t utimens_resolve(const struct timespec *ts, time_t cur, time_t now)
{
if (ts->tv_nsec == UTIME_OMIT)
return cur;
if (ts->tv_nsec == UTIME_NOW)
return now;
return ts->tv_sec;
}

static int sshfs_utimens(const char *path, const struct timespec tv[2],
struct fuse_file_info *fi)
{
(void) fi;
int err;
struct buffer buf;
struct sshfs_file *sf = NULL;
time_t asec = tv[0].tv_sec, msec = tv[1].tv_sec;
struct stat cur;
time_t now = time(NULL);
time_t asec, msec;

struct timeval now;
gettimeofday(&now, NULL);
if (asec == 0)
asec = now.tv_sec;
if (msec == 0)
msec = now.tv_sec;
if (tv[0].tv_nsec == UTIME_OMIT && tv[1].tv_nsec == UTIME_OMIT)
return 0;

if (fi != NULL) {
sf = get_sshfs_file(fi);
if (!sshfs_file_is_conn(sf))
return -EIO;
}

memset(&cur, 0, sizeof(cur));
if (tv[0].tv_nsec == UTIME_OMIT || tv[1].tv_nsec == UTIME_OMIT) {
err = sshfs_getattr(path, &cur, fi);
if (err)
return err;
}
asec = utimens_resolve(&tv[0], cur.st_atime, now);
msec = utimens_resolve(&tv[1], cur.st_mtime, now);

buf_init(&buf, 0);
if (sf == NULL)
buf_add_path(&buf, path);
Expand Down
18 changes: 18 additions & 0 deletions test/test_sshfs.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ def test_sshfs(
# file timestamps.
tst_utimens(mnt_dir, tol=1)
tst_utimens_now(mnt_dir)
tst_utimens_omit(mnt_dir)

tst_link(mnt_dir, cache_timeout)
tst_truncate_path(mnt_dir)
Expand Down Expand Up @@ -698,6 +699,23 @@ def tst_utimens_now(mnt_dir):
assert fstat.st_mtime != 0


def tst_utimens_omit(mnt_dir):
# Updating only the access time (UTIME_OMIT for mtime) must leave the
# modification time alone instead of setting it to "now".
fullname = pjoin(mnt_dir, name_generator())

fd = os.open(fullname, os.O_CREAT | os.O_RDWR)
os.close(fd)
old = int(os.lstat(fullname).st_mtime) - 1000
os.utime(fullname, (old, old))

subprocess.check_call(["touch", "-a", fullname])

fstat = os.lstat(fullname)
assert fstat.st_mtime == old
assert fstat.st_atime > old


def tst_passthrough(src_dir, mnt_dir, cache_timeout):
name = name_generator()
src_name = pjoin(src_dir, name)
Expand Down