Commit 6b801d3b authored by Jakob Unterwurzacher's avatar Jakob Unterwurzacher

loopback: Utimens: deduplicate and fix time conversion code

1) Fix the "a" instead of "m" typo in loopbackFile.Utimens.
   Besides the incorrect behavoir, this causes a crash if "a"
   is nil.
   Obsoletes https://github.com/hanwen/go-fuse/pull/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.
parent 5e829bc7
...@@ -12,6 +12,7 @@ import ( ...@@ -12,6 +12,7 @@ import (
"os" "os"
"reflect" "reflect"
"syscall" "syscall"
"time"
"unsafe" "unsafe"
) )
...@@ -86,3 +87,21 @@ func init() { ...@@ -86,3 +87,21 @@ func init() {
log.Panicf("page size incorrect: %d", p) 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
}
...@@ -65,7 +65,6 @@ func (f *loopbackFile) Allocate(off uint64, sz uint64, mode uint32) fuse.Status ...@@ -65,7 +65,6 @@ func (f *loopbackFile) Allocate(off uint64, sz uint64, mode uint32) fuse.Status
return fuse.OK return fuse.OK
} }
const _UTIME_NOW = ((1 << 30) - 1)
const _UTIME_OMIT = ((1 << 30) - 2) const _UTIME_OMIT = ((1 << 30) - 2)
// timeToTimeval - Convert time.Time to syscall.Timeval // timeToTimeval - Convert time.Time to syscall.Timeval
......
...@@ -21,27 +21,11 @@ func (f *loopbackFile) Allocate(off uint64, sz uint64, mode uint32) fuse.Status ...@@ -21,27 +21,11 @@ func (f *loopbackFile) Allocate(off uint64, sz uint64, mode uint32) fuse.Status
return fuse.OK return fuse.OK
} }
const _UTIME_NOW = ((1 << 30) - 1)
const _UTIME_OMIT = ((1 << 30) - 2)
// Utimens - file handle based version of loopbackFileSystem.Utimens() // Utimens - file handle based version of loopbackFileSystem.Utimens()
func (f *loopbackFile) Utimens(a *time.Time, m *time.Time) fuse.Status { func (f *loopbackFile) Utimens(a *time.Time, m *time.Time) fuse.Status {
var ts [2]syscall.Timespec var ts [2]syscall.Timespec
ts[0] = fuse.UtimeToTimespec(a)
if a == nil { ts[1] = fuse.UtimeToTimespec(m)
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
}
f.lock.Lock() f.lock.Lock()
err := futimens(int(f.File.Fd()), &ts) err := futimens(int(f.File.Fd()), &ts)
f.lock.Unlock() f.lock.Unlock()
......
...@@ -39,27 +39,11 @@ func (fs *loopbackFileSystem) SetXAttr(name string, attr string, data []byte, fl ...@@ -39,27 +39,11 @@ func (fs *loopbackFileSystem) SetXAttr(name string, attr string, data []byte, fl
return fuse.ToStatus(err) return fuse.ToStatus(err)
} }
const _UTIME_NOW = ((1 << 30) - 1)
const _UTIME_OMIT = ((1 << 30) - 2)
// Utimens - path based version of loopbackFile.Utimens() // 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) { func (fs *loopbackFileSystem) Utimens(path string, a *time.Time, m *time.Time, context *fuse.Context) (code fuse.Status) {
var ts [2]syscall.Timespec var ts [2]syscall.Timespec
ts[0] = fuse.UtimeToTimespec(a)
if a == nil { ts[1] = fuse.UtimeToTimespec(m)
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
}
err := sysUtimensat(0, fs.GetPath(path), &ts, _AT_SYMLINK_NOFOLLOW) err := sysUtimensat(0, fs.GetPath(path), &ts, _AT_SYMLINK_NOFOLLOW)
return fuse.ToStatus(err) return fuse.ToStatus(err)
} }
...@@ -70,6 +70,41 @@ func TestNegativeTime(t *testing.T) { ...@@ -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) { func clearStatfs(s *syscall.Statfs_t) {
empty := syscall.Statfs_t{} empty := syscall.Statfs_t{}
s.Type = 0 s.Type = 0
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment