diff --git a/node.go b/node.go index e607bc8b1..0e541d3ca 100644 --- a/node.go +++ b/node.go @@ -245,15 +245,15 @@ func (node Node) createSymlinkAt(path string) error { } func (node *Node) createDevAt(path string) error { - return syscall.Mknod(path, syscall.S_IFBLK|0600, int(node.Device)) + return mknod(path, syscall.S_IFBLK|0600, int(node.Device)) } func (node *Node) createCharDevAt(path string) error { - return syscall.Mknod(path, syscall.S_IFCHR|0600, int(node.Device)) + return mknod(path, syscall.S_IFCHR|0600, int(node.Device)) } func (node *Node) createFifoAt(path string) error { - return syscall.Mkfifo(path, 0600) + return mkfifo(path, 0600) } func (node Node) MarshalJSON() ([]byte, error) { @@ -381,9 +381,19 @@ func (node *Node) isNewer(path string, fi os.FileInfo) bool { return true } - extendedStat := fi.Sys().(*syscall.Stat_t) - inode := extendedStat.Ino - size := uint64(extendedStat.Size) + size := uint64(fi.Size()) + + extendedStat, ok := toStatT(fi.Sys()) + if !ok { + if node.ModTime != fi.ModTime() || + node.Size != size { + debug.Log("node.isNewer", "node %v is newer: timestamp, size or inode changed", path) + return true + } + return false + } + + inode := extendedStat.ino() if node.ModTime != fi.ModTime() || node.ChangeTime != changeTime(extendedStat) || @@ -397,11 +407,11 @@ func (node *Node) isNewer(path string, fi os.FileInfo) bool { return false } -func (node *Node) fillUser(stat *syscall.Stat_t) error { - node.UID = stat.Uid - node.GID = stat.Gid +func (node *Node) fillUser(stat statT) error { + node.UID = stat.uid() + node.GID = stat.gid() - username, err := lookupUsername(strconv.Itoa(int(stat.Uid))) + username, err := lookupUsername(strconv.Itoa(int(stat.uid()))) if err != nil { return errors.Annotate(err, "fillUser") } @@ -439,12 +449,12 @@ func lookupUsername(uid string) (string, error) { } func (node *Node) fillExtra(path string, fi os.FileInfo) error { - stat, ok := fi.Sys().(*syscall.Stat_t) + stat, ok := toStatT(fi.Sys()) if !ok { return nil } - node.Inode = uint64(stat.Ino) + node.Inode = uint64(stat.ino()) node.fillTimes(stat) @@ -456,15 +466,15 @@ func (node *Node) fillExtra(path string, fi os.FileInfo) error { switch node.Type { case "file": - node.Size = uint64(stat.Size) - node.Links = uint64(stat.Nlink) + node.Size = uint64(stat.size()) + node.Links = uint64(stat.nlink()) case "dir": case "symlink": node.LinkTarget, err = os.Readlink(path) case "dev": - node.Device = uint64(stat.Rdev) + node.Device = uint64(stat.rdev()) case "chardev": - node.Device = uint64(stat.Rdev) + node.Device = uint64(stat.rdev()) case "fifo": case "socket": default: @@ -473,3 +483,32 @@ func (node *Node) fillExtra(path string, fi os.FileInfo) error { return err } + +type statT interface { + dev() uint64 + ino() uint64 + nlink() uint64 + uid() uint32 + gid() uint32 + rdev() uint64 + size() int64 + atim() syscall.Timespec + mtim() syscall.Timespec + ctim() syscall.Timespec +} + +func mkfifo(path string, mode uint32) (err error) { + return mknod(path, mode|syscall.S_IFIFO, 0) +} + +func (node *Node) fillTimes(stat statT) { + ctim := stat.ctim() + atim := stat.atim() + node.ChangeTime = time.Unix(ctim.Unix()) + node.AccessTime = time.Unix(atim.Unix()) +} + +func changeTime(stat statT) time.Time { + ctim := stat.ctim() + return time.Unix(ctim.Unix()) +} diff --git a/node_darwin.go b/node_darwin.go index f63b41f13..adc980295 100644 --- a/node_darwin.go +++ b/node_darwin.go @@ -3,22 +3,16 @@ package restic import ( "os" "syscall" - "time" ) func (node *Node) OpenForReading() (*os.File, error) { return os.Open(node.path) } -func changeTime(stat *syscall.Stat_t) time.Time { - return time.Unix(stat.Ctimespec.Unix()) -} - -func (node *Node) fillTimes(stat *syscall.Stat_t) { - node.ChangeTime = time.Unix(stat.Ctimespec.Unix()) - node.AccessTime = time.Unix(stat.Atimespec.Unix()) -} - func (node Node) restoreSymlinkTimestamps(path string, utimes [2]syscall.Timespec) error { return nil } + +func (s statUnix) atim() syscall.Timespec { return s.Atimespec } +func (s statUnix) mtim() syscall.Timespec { return s.Mtimespec } +func (s statUnix) ctim() syscall.Timespec { return s.Ctimespec } diff --git a/node_freebsd.go b/node_freebsd.go index 231cf8db7..67bcdf3e9 100644 --- a/node_freebsd.go +++ b/node_freebsd.go @@ -3,22 +3,16 @@ package restic import ( "os" "syscall" - "time" ) func (node *Node) OpenForReading() (*os.File, error) { return os.OpenFile(node.path, os.O_RDONLY, 0) } -func (node *Node) fillTimes(stat *syscall.Stat_t) { - node.ChangeTime = time.Unix(stat.Ctimespec.Unix()) - node.AccessTime = time.Unix(stat.Atimespec.Unix()) -} - -func changeTime(stat *syscall.Stat_t) time.Time { - return time.Unix(stat.Ctimespec.Unix()) -} - func (node Node) restoreSymlinkTimestamps(path string, utimes [2]syscall.Timespec) error { return nil } + +func (s statUnix) atim() syscall.Timespec { return s.Atimespec } +func (s statUnix) mtim() syscall.Timespec { return s.Mtimespec } +func (s statUnix) ctim() syscall.Timespec { return s.Ctimespec } diff --git a/node_linux.go b/node_linux.go index 1043397a8..2304c13d5 100644 --- a/node_linux.go +++ b/node_linux.go @@ -4,7 +4,6 @@ import ( "os" "path/filepath" "syscall" - "time" "unsafe" "github.com/juju/errors" @@ -18,15 +17,6 @@ func (node *Node) OpenForReading() (*os.File, error) { return file, err } -func (node *Node) fillTimes(stat *syscall.Stat_t) { - node.ChangeTime = time.Unix(stat.Ctim.Unix()) - node.AccessTime = time.Unix(stat.Atim.Unix()) -} - -func changeTime(stat *syscall.Stat_t) time.Time { - return time.Unix(stat.Ctim.Unix()) -} - func (node Node) restoreSymlinkTimestamps(path string, utimes [2]syscall.Timespec) error { dir, err := os.Open(filepath.Dir(path)) defer dir.Close() @@ -65,3 +55,7 @@ func utimensat(dirfd int, path string, times *[2]syscall.Timespec, flags int) (e func utimesNanoAt(dirfd int, path string, ts [2]syscall.Timespec, flags int) (err error) { return utimensat(dirfd, path, (*[2]syscall.Timespec)(unsafe.Pointer(&ts[0])), flags) } + +func (s statUnix) atim() syscall.Timespec { return s.Atim } +func (s statUnix) mtim() syscall.Timespec { return s.Mtim } +func (s statUnix) ctim() syscall.Timespec { return s.Ctim } diff --git a/node_openbsd.go b/node_openbsd.go index 38f1d8441..feafe307e 100644 --- a/node_openbsd.go +++ b/node_openbsd.go @@ -3,7 +3,6 @@ package restic import ( "os" "syscall" - "time" ) func (node *Node) OpenForReading() (*os.File, error) { @@ -14,15 +13,10 @@ func (node *Node) OpenForReading() (*os.File, error) { return file, err } -func (node *Node) fillTimes(stat *syscall.Stat_t) { - node.ChangeTime = time.Unix(stat.Ctim.Unix()) - node.AccessTime = time.Unix(stat.Atim.Unix()) -} - -func changeTime(stat *syscall.Stat_t) time.Time { - return time.Unix(stat.Ctim.Unix()) -} - func (node Node) restoreSymlinkTimestamps(path string, utimes [2]syscall.Timespec) error { return nil } + +func (s statUnix) atim() syscall.Timespec { return s.Atim } +func (s statUnix) mtim() syscall.Timespec { return s.Mtim } +func (s statUnix) ctim() syscall.Timespec { return s.Ctim } diff --git a/node_unix.go b/node_unix.go new file mode 100644 index 000000000..cfbe7ec61 --- /dev/null +++ b/node_unix.go @@ -0,0 +1,28 @@ +// + +// +build dragonfly linux netbsd openbsd solaris darwin + +package restic + +var mknod = syscall.Mknod + +type statUnix syscall.Stat_t + +func toStatT(i interface{}) (statT, bool) { + if i == nil { + return nil, false + } + s, ok := i.(syscall.Stat_t) + if ok && s != nil { + return statUnix(*s), true + } + return nil, false +} + +func (s statUnix) dev() uint64 { return s.Dev } +func (s statUnix) ino() uint64 { return s.Ino } +func (s statUnix) nlink() uint64 { return s.Nlink } +func (s statUnix) uid() uint32 { return s.Uid } +func (s statUnix) gid() uint32 { return s.Gid } +func (s statUnix) rdev() uint64 { return s.Rdev } +func (s statUnix) size() int64 { return s.Size } diff --git a/node_windows.go b/node_windows.go new file mode 100644 index 000000000..c9cf9e854 --- /dev/null +++ b/node_windows.go @@ -0,0 +1,61 @@ +package restic + +import ( + "os" + "syscall" +) + +func (node *Node) OpenForReading() (*os.File, error) { + file, err := os.OpenFile(node.path, os.O_RDONLY, 0) + if os.IsPermission(err) { + return os.OpenFile(node.path, os.O_RDONLY, 0) + } + return file, err +} + +// mknod() creates a filesystem node (file, device +// special file, or named pipe) named pathname, with attributes +// specified by mode and dev. +var mknod = func(path string, mode uint32, dev int) (err error) { + panic("mknod not implemented") +} + +func (node Node) restoreSymlinkTimestamps(path string, utimes [2]syscall.Timespec) error { + return nil +} + +type statWin syscall.Win32FileAttributeData + +func toStatT(i interface{}) (statT, bool) { + if i == nil { + return nil, false + } + s, ok := i.(*syscall.Win32FileAttributeData) + if ok && s != nil { + return statWin(*s), true + } + return nil, false +} + +func (s statWin) dev() uint64 { return 0 } +func (s statWin) ino() uint64 { return 0 } +func (s statWin) nlink() uint64 { return 0 } +func (s statWin) uid() uint32 { return 0 } +func (s statWin) gid() uint32 { return 0 } +func (s statWin) rdev() uint64 { return 0 } + +func (s statWin) size() int64 { + return int64(s.FileSizeLow) | (int64(s.FileSizeHigh) << 32) +} + +func (s statWin) atim() syscall.Timespec { + return syscall.NsecToTimespec(s.LastAccessTime.Nanoseconds()) +} + +func (s statWin) mtim() syscall.Timespec { + return syscall.NsecToTimespec(s.LastWriteTime.Nanoseconds()) +} + +func (s statWin) ctim() syscall.Timespec { + return syscall.NsecToTimespec(s.CreationTime.Nanoseconds()) +}