diff --git a/chtimes.go b/chtimes.go new file mode 100644 index 000000000..b82666a91 --- /dev/null +++ b/chtimes.go @@ -0,0 +1,14 @@ +// Default implementation of Chtimes + +// +build !linux + +package main + +import ( + "os" + "time" +) + +func Chtimes(name string, atime time.Time, mtime time.Time) error { + return os.Chtimes(name, atime, mtime) +} diff --git a/chtimes_linux.go b/chtimes_linux.go new file mode 100644 index 000000000..0aeccdf78 --- /dev/null +++ b/chtimes_linux.go @@ -0,0 +1,78 @@ +// An implementation of Chtimes which preserves nanosecond precision under linux +// +// Should go in standard library, but here for now + +// +build linux + +package main + +import ( + "os" + "syscall" + "time" + "unsafe" +) + +// COPIED from syscall +// byteSliceFromString returns a NUL-terminated slice of bytes +// containing the text of s. If s contains a NUL byte at any +// location, it returns (nil, EINVAL). +func byteSliceFromString(s string) ([]byte, error) { + for i := 0; i < len(s); i++ { + if s[i] == 0 { + return nil, syscall.EINVAL + } + } + a := make([]byte, len(s)+1) + copy(a, s) + return a, nil +} + +// COPIED from syscall +// bytePtrFromString returns a pointer to a NUL-terminated array of +// bytes containing the text of s. If s contains a NUL byte at any +// location, it returns (nil, EINVAL). +func bytePtrFromString(s string) (*byte, error) { + a, err := byteSliceFromString(s) + if err != nil { + return nil, err + } + return &a[0], nil +} + +// COPIED from syscall auto generated code modified from utimes +func utimensat(dirfd int, path string, times *[2]syscall.Timespec) (err error) { + var _p0 *byte + _p0, err = bytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := syscall.Syscall(syscall.SYS_UTIMENSAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(times))) + if e1 != 0 { + err = e1 + } + return +} + +// FIXME needs defining properly! +const AT_FDCWD = -100 + +// COPIED from syscall and modified +//sys utimes(path string, times *[2]Timeval) (err error) +func Utimensat(dirfd int, path string, ts []syscall.Timespec) (err error) { + if len(ts) != 2 { + return syscall.EINVAL + } + return utimensat(dirfd, path, (*[2]syscall.Timespec)(unsafe.Pointer(&ts[0]))) +} + +// COPIED from syscall and modified +func Chtimes(name string, atime time.Time, mtime time.Time) error { + var utimes [2]syscall.Timespec + utimes[0] = syscall.NsecToTimespec(atime.UnixNano()) + utimes[1] = syscall.NsecToTimespec(mtime.UnixNano()) + if e := Utimensat(AT_FDCWD, name, utimes[0:]); e != nil { + return &os.PathError{"chtimes", name, e} + } + return nil +} diff --git a/notes.txt b/notes.txt index 556415f8e..9783e533c 100644 --- a/notes.txt +++ b/notes.txt @@ -48,40 +48,8 @@ Need to make directory objects otherwise can't upload an empty directory Make a fs.Errorf and count errors and log them at a different level -Seem to be able to read stats to ns precision (using struct stat -defined in asm-generic/stat.h) +Submit chtimes_linux.go to standard library + * Can also fix Futime which isn't using the syscall under linux + * Also for all the *bsd so likely they could be fixed too -https://www.kernel.org/doc/man-pages/online/pages/man2/stat.2.html - - but only write them to us precision. -This is confirmed by looking at the source for Chtimes - -func Chtimes(name string, atime time.Time, mtime time.Time) error { - var utimes [2]syscall.Timeval - atime_ns := atime.Unix()*1e9 + int64(atime.Nanosecond()) - mtime_ns := mtime.Unix()*1e9 + int64(mtime.Nanosecond()) - utimes[0] = syscall.NsecToTimeval(atime_ns) - utimes[1] = syscall.NsecToTimeval(mtime_ns) - if e := syscall.Utimes(name, utimes[0:]); e != nil { - return &PathError{"chtimes", name, e} - } - return nil -} - -And - -func NsecToTimeval(nsec int64) (tv Timeval) { - nsec += 999 // round up to microsecond - tv.Sec = nsec / 1e9 - tv.Usec = nsec % 1e9 / 1e3 - return -} - -Seems likely Go should be using - utimensat() and futimens() -Instead of utimes() - the syscall is defined already -SYS_UTIMENSAT -Also for all the *bsd so likely they could be fixed too - -Can also fix Futime which isn't using the syscall under linux diff --git a/swiftsync.go b/swiftsync.go index 6e1410958..7c99e0bef 100644 --- a/swiftsync.go +++ b/swiftsync.go @@ -77,7 +77,7 @@ func (fs *FsObject) md5sum() (string, error) { // Sets the modification time of the local fs object func (fs *FsObject) SetModTime(modTime time.Time) { - err := os.Chtimes(fs.path, modTime, modTime) + err := Chtimes(fs.path, modTime, modTime) if err != nil { fs.Debugf("Failed to set mtime on file: %s", err) }