Skip to content

Commit 6b801d3

Browse files
committed
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 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.
1 parent 5e829bc commit 6b801d3

File tree

5 files changed

+58
-37
lines changed

5 files changed

+58
-37
lines changed

fuse/misc.go

+19
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"os"
1313
"reflect"
1414
"syscall"
15+
"time"
1516
"unsafe"
1617
)
1718

@@ -86,3 +87,21 @@ func init() {
8687
log.Panicf("page size incorrect: %d", p)
8788
}
8889
}
90+
91+
const _UTIME_OMIT = ((1 << 30) - 2)
92+
93+
// UtimeToTimespec converts a "Time" pointer as passed to Utimens to a
94+
// "Timespec" that can be passed to the utimensat syscall.
95+
// A nil pointer is converted to the special UTIME_OMIT value.
96+
func UtimeToTimespec(t *time.Time) (ts syscall.Timespec) {
97+
if t == nil {
98+
ts.Nsec = _UTIME_OMIT
99+
} else {
100+
ts = syscall.NsecToTimespec(t.UnixNano())
101+
// Go bug https://github.com/golang/go/issues/12777
102+
if ts.Nsec < 0 {
103+
ts.Nsec = 0
104+
}
105+
}
106+
return ts
107+
}

fuse/nodefs/files_darwin.go

-1
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,6 @@ func (f *loopbackFile) Allocate(off uint64, sz uint64, mode uint32) fuse.Status
6565
return fuse.OK
6666
}
6767

68-
const _UTIME_NOW = ((1 << 30) - 1)
6968
const _UTIME_OMIT = ((1 << 30) - 2)
7069

7170
// timeToTimeval - Convert time.Time to syscall.Timeval

fuse/nodefs/files_linux.go

+2-18
Original file line numberDiff line numberDiff line change
@@ -21,27 +21,11 @@ func (f *loopbackFile) Allocate(off uint64, sz uint64, mode uint32) fuse.Status
2121
return fuse.OK
2222
}
2323

24-
const _UTIME_NOW = ((1 << 30) - 1)
25-
const _UTIME_OMIT = ((1 << 30) - 2)
26-
2724
// Utimens - file handle based version of loopbackFileSystem.Utimens()
2825
func (f *loopbackFile) Utimens(a *time.Time, m *time.Time) fuse.Status {
2926
var ts [2]syscall.Timespec
30-
31-
if a == nil {
32-
ts[0].Nsec = _UTIME_OMIT
33-
} else {
34-
ts[0] = syscall.NsecToTimespec(a.UnixNano())
35-
ts[0].Nsec = 0
36-
}
37-
38-
if m == nil {
39-
ts[1].Nsec = _UTIME_OMIT
40-
} else {
41-
ts[1] = syscall.NsecToTimespec(a.UnixNano())
42-
ts[1].Nsec = 0
43-
}
44-
27+
ts[0] = fuse.UtimeToTimespec(a)
28+
ts[1] = fuse.UtimeToTimespec(m)
4529
f.lock.Lock()
4630
err := futimens(int(f.File.Fd()), &ts)
4731
f.lock.Unlock()

fuse/pathfs/loopback_linux.go

+2-18
Original file line numberDiff line numberDiff line change
@@ -39,27 +39,11 @@ func (fs *loopbackFileSystem) SetXAttr(name string, attr string, data []byte, fl
3939
return fuse.ToStatus(err)
4040
}
4141

42-
const _UTIME_NOW = ((1 << 30) - 1)
43-
const _UTIME_OMIT = ((1 << 30) - 2)
44-
4542
// Utimens - path based version of loopbackFile.Utimens()
4643
func (fs *loopbackFileSystem) Utimens(path string, a *time.Time, m *time.Time, context *fuse.Context) (code fuse.Status) {
4744
var ts [2]syscall.Timespec
48-
49-
if a == nil {
50-
ts[0].Nsec = _UTIME_OMIT
51-
} else {
52-
ts[0] = syscall.NsecToTimespec(a.UnixNano())
53-
ts[0].Nsec = 0
54-
}
55-
56-
if m == nil {
57-
ts[1].Nsec = _UTIME_OMIT
58-
} else {
59-
ts[1] = syscall.NsecToTimespec(m.UnixNano())
60-
ts[1].Nsec = 0
61-
}
62-
45+
ts[0] = fuse.UtimeToTimespec(a)
46+
ts[1] = fuse.UtimeToTimespec(m)
6347
err := sysUtimensat(0, fs.GetPath(path), &ts, _AT_SYMLINK_NOFOLLOW)
6448
return fuse.ToStatus(err)
6549
}

fuse/test/loopback_linux_test.go

+35
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,41 @@ func TestNegativeTime(t *testing.T) {
7070
}
7171
}
7272

73+
// Setting nanoseconds should work for dates after 1970
74+
func TestUtimesNano(t *testing.T) {
75+
tc := NewTestCase(t)
76+
defer tc.Cleanup()
77+
78+
path := tc.mountFile
79+
err := ioutil.WriteFile(path, []byte("xyz"), 0600)
80+
if err != nil {
81+
t.Fatal(err)
82+
}
83+
ts := make([]syscall.Timespec, 2)
84+
// atime
85+
ts[0].Sec = 1
86+
ts[0].Nsec = 2
87+
// mtime
88+
ts[1].Sec = 3
89+
ts[1].Nsec = 4
90+
err = syscall.UtimesNano(path, ts)
91+
if err != nil {
92+
t.Fatal(err)
93+
}
94+
95+
var st syscall.Stat_t
96+
err = syscall.Stat(path, &st)
97+
if err != nil {
98+
t.Fatal(err)
99+
}
100+
if st.Atim != ts[0] {
101+
t.Errorf("Wrong atime: %v, want: %v", st.Atim, ts[0])
102+
}
103+
if st.Mtim != ts[1] {
104+
t.Errorf("Wrong mtime: %v, want: %v", st.Mtim, ts[1])
105+
}
106+
}
107+
73108
func clearStatfs(s *syscall.Statfs_t) {
74109
empty := syscall.Statfs_t{}
75110
s.Type = 0

0 commit comments

Comments
 (0)