Skip to content

Commit

Permalink
loopback: Utimens: deduplicate and fix time conversion code
Browse files Browse the repository at this point in the history
1) Fix the "a" instead of "m" typo in loopbackFile.Utimens.
   Besides the incorrect behavoir, this causes a crash if "a"
   is nil.
   Obsoletes hanwen#100 .

2) Enable nanosecond resolution for dates after 1970.
   syscall.NsecToTimespec is broken for dates before 1970 but
   works fine otherwise, so let's keep the nanoseconds there.

3) Deduplicate the time conversion code in nodefs and paths into
   the new function fuse.UtimeToTimespec.

4) Add a test case.
  • Loading branch information
rfjakob committed Oct 24, 2016
1 parent 5e829bc commit 6b801d3
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 37 deletions.
19 changes: 19 additions & 0 deletions fuse/misc.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"os"
"reflect"
"syscall"
"time"
"unsafe"
)

Expand Down Expand Up @@ -86,3 +87,21 @@ func init() {
log.Panicf("page size incorrect: %d", p)
}
}

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

// UtimeToTimespec converts a "Time" pointer as passed to Utimens to a
// "Timespec" that can be passed to the utimensat syscall.
// A nil pointer is converted to the special UTIME_OMIT value.
func UtimeToTimespec(t *time.Time) (ts syscall.Timespec) {
if t == nil {
ts.Nsec = _UTIME_OMIT
} else {
ts = syscall.NsecToTimespec(t.UnixNano())
// Go bug https://github.com/golang/go/issues/12777
if ts.Nsec < 0 {
ts.Nsec = 0
}
}
return ts
}
1 change: 0 additions & 1 deletion fuse/nodefs/files_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ func (f *loopbackFile) Allocate(off uint64, sz uint64, mode uint32) fuse.Status
return fuse.OK
}

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

// timeToTimeval - Convert time.Time to syscall.Timeval
Expand Down
20 changes: 2 additions & 18 deletions fuse/nodefs/files_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,27 +21,11 @@ func (f *loopbackFile) Allocate(off uint64, sz uint64, mode uint32) fuse.Status
return fuse.OK
}

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

// Utimens - file handle based version of loopbackFileSystem.Utimens()
func (f *loopbackFile) Utimens(a *time.Time, m *time.Time) fuse.Status {
var ts [2]syscall.Timespec

if a == nil {
ts[0].Nsec = _UTIME_OMIT
} else {
ts[0] = syscall.NsecToTimespec(a.UnixNano())
ts[0].Nsec = 0
}

if m == nil {
ts[1].Nsec = _UTIME_OMIT
} else {
ts[1] = syscall.NsecToTimespec(a.UnixNano())
ts[1].Nsec = 0
}

ts[0] = fuse.UtimeToTimespec(a)
ts[1] = fuse.UtimeToTimespec(m)
f.lock.Lock()
err := futimens(int(f.File.Fd()), &ts)
f.lock.Unlock()
Expand Down
20 changes: 2 additions & 18 deletions fuse/pathfs/loopback_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,27 +39,11 @@ func (fs *loopbackFileSystem) SetXAttr(name string, attr string, data []byte, fl
return fuse.ToStatus(err)
}

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

// Utimens - path based version of loopbackFile.Utimens()
func (fs *loopbackFileSystem) Utimens(path string, a *time.Time, m *time.Time, context *fuse.Context) (code fuse.Status) {
var ts [2]syscall.Timespec

if a == nil {
ts[0].Nsec = _UTIME_OMIT
} else {
ts[0] = syscall.NsecToTimespec(a.UnixNano())
ts[0].Nsec = 0
}

if m == nil {
ts[1].Nsec = _UTIME_OMIT
} else {
ts[1] = syscall.NsecToTimespec(m.UnixNano())
ts[1].Nsec = 0
}

ts[0] = fuse.UtimeToTimespec(a)
ts[1] = fuse.UtimeToTimespec(m)
err := sysUtimensat(0, fs.GetPath(path), &ts, _AT_SYMLINK_NOFOLLOW)
return fuse.ToStatus(err)
}
35 changes: 35 additions & 0 deletions fuse/test/loopback_linux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,41 @@ func TestNegativeTime(t *testing.T) {
}
}

// Setting nanoseconds should work for dates after 1970
func TestUtimesNano(t *testing.T) {
tc := NewTestCase(t)
defer tc.Cleanup()

path := tc.mountFile
err := ioutil.WriteFile(path, []byte("xyz"), 0600)
if err != nil {
t.Fatal(err)
}
ts := make([]syscall.Timespec, 2)
// atime
ts[0].Sec = 1
ts[0].Nsec = 2
// mtime
ts[1].Sec = 3
ts[1].Nsec = 4
err = syscall.UtimesNano(path, ts)
if err != nil {
t.Fatal(err)
}

var st syscall.Stat_t
err = syscall.Stat(path, &st)
if err != nil {
t.Fatal(err)
}
if st.Atim != ts[0] {
t.Errorf("Wrong atime: %v, want: %v", st.Atim, ts[0])
}
if st.Mtim != ts[1] {
t.Errorf("Wrong mtime: %v, want: %v", st.Mtim, ts[1])
}
}

func clearStatfs(s *syscall.Statfs_t) {
empty := syscall.Statfs_t{}
s.Type = 0
Expand Down

0 comments on commit 6b801d3

Please sign in to comment.