Skip to content

Commit

Permalink
darwin, loopback: emulate UTIME_OMIT
Browse files Browse the repository at this point in the history
All but the newest OSX versions (xnu-4570.1.46, released 2017)
lack utimensat() and UTIME_OMIT, see:
https://github.com/apple/darwin-xnu/blame/0a798f6738bc1db01281fc08ae024145e84df927/bsd/sys/stat.h#L537

The UTIME_OMIT value is intepreted literally by darwin, resulting
in all files getting a 1970 timestamp
( rfjakob/gocryptfs#229 ).

Emulate UTIME_OMIT by filling in the missing values
using an extra GetAttr call.
  • Loading branch information
rfjakob committed Apr 29, 2018
1 parent a9ddcb8 commit 7e0d5cd
Showing 1 changed file with 20 additions and 17 deletions.
37 changes: 20 additions & 17 deletions fuse/pathfs/loopback_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,6 @@ import (
"github.com/hanwen/go-fuse/fuse"
)

const _UTIME_NOW = ((1 << 30) - 1)
const _UTIME_OMIT = ((1 << 30) - 2)

// timeToTimeval - Convert time.Time to syscall.Timeval
//
// Note: This does not use syscall.NsecToTimespec because
Expand All @@ -26,22 +23,28 @@ func timeToTimeval(t *time.Time) syscall.Timeval {
return tv
}

// OSX does not have the utimensat syscall neded to implement this properly.
// We do our best to emulate it using futimes.
// All but the newest OSX versions (xnu-4570.1.46, released 2017)
// lack utimensat() and UTIME_OMIT, see:
// https://github.com/apple/darwin-xnu/blame/0a798f6738bc1db01281fc08ae024145e84df927/bsd/sys/stat.h#L537
//
// We do our best to emulate using utimes() and extra GetAttr() calls.
func (fs *loopbackFileSystem) Utimens(path string, a *time.Time, m *time.Time, context *fuse.Context) fuse.Status {
tv := make([]syscall.Timeval, 2)
if a == nil {
tv[0].Usec = _UTIME_OMIT
} else {
tv[0] = timeToTimeval(a)
if a == nil || m == nil {
// Fill the missing value from the file
attr, status := fs.GetAttr(path, context)
if !status.Ok() {
return status
}
if a == nil {
a = time.Unix(attr.Atime, attr.Atimensec)
}
if m == nil {
m = time.Unix(attr.Mtime, attr.Mtimensec)
}
}

if m == nil {
tv[1].Usec = _UTIME_OMIT
} else {
tv[1] = timeToTimeval(m)
}

var tv [2]syscall.Timeval
tv[0] = timeToTimeval(a)
tv[1] = timeToTimeval(m)
err := syscall.Utimes(fs.GetPath(path), tv)
return fuse.ToStatus(err)
}

0 comments on commit 7e0d5cd

Please sign in to comment.