Skip to content

Commit

Permalink
Examine file ctime when checking if files have changed.
Browse files Browse the repository at this point in the history
  • Loading branch information
Courtney Bane committed Mar 20, 2019
1 parent 7b8d1dc commit 3e98d90
Show file tree
Hide file tree
Showing 7 changed files with 40 additions and 1 deletion.
8 changes: 8 additions & 0 deletions changelog/unreleased/issue-2179
@@ -0,0 +1,8 @@
Bugfix: Use ctime when checking for file changes

Previously, restic only checked a file's mtime (along with other non-timestamp
data) to decide if a file has changed. This could cause it to not notice changes
if something edits a file and then resets the timestamp. Restic now also checks
the ctime, so any modification to a file should be noticed.

https://github.com/restic/restic/issues/2179
7 changes: 6 additions & 1 deletion internal/archiver/archiver.go
Expand Up @@ -451,8 +451,13 @@ func fileChanged(fi os.FileInfo, node *restic.Node) bool {
return true
}

// check size
// check status change timestamp
extFI := fs.ExtendedStat(fi)
if !extFI.ChangeTime.Equal(node.ChangeTime) {
return true
}

// check size
if uint64(fi.Size()) != node.Size || uint64(extFI.Size) != node.Size {
return true
}
Expand Down
21 changes: 21 additions & 0 deletions internal/archiver/archiver_test.go
@@ -1,6 +1,7 @@
package archiver

import (
"bytes"
"context"
"io/ioutil"
"os"
Expand Down Expand Up @@ -574,6 +575,26 @@ func TestFileChanged(t *testing.T) {
save(t, filename, defaultContent)
},
},
{
Name: "new-content-same-timestamp",
Modify: func(t testing.TB, filename string) {
fi, _ := os.Stat(filename)
extFI := fs.ExtendedStat(fi)
save(t, filename, bytes.ToUpper(defaultContent))
sleep()
ts := []syscall.Timespec{
{
Sec: extFI.AccessTime.Unix(),
Nsec: int64(extFI.AccessTime.Nanosecond()),
},
{
Sec: extFI.ModTime.Unix(),
Nsec: int64(extFI.ModTime.Nanosecond()),
},
}
syscall.UtimesNano(filename, ts)
},
},
{
Name: "other-content",
Modify: func(t testing.TB, filename string) {
Expand Down
1 change: 1 addition & 0 deletions internal/fs/stat.go
Expand Up @@ -22,6 +22,7 @@ type ExtendedFileInfo struct {

AccessTime time.Time // last access time stamp
ModTime time.Time // last (content) modification time stamp
ChangeTime time.Time // last status change time stamp
}

// ExtendedStat returns an ExtendedFileInfo constructed from the os.FileInfo.
Expand Down
1 change: 1 addition & 0 deletions internal/fs/stat_bsd.go
Expand Up @@ -30,6 +30,7 @@ func extendedStat(fi os.FileInfo) ExtendedFileInfo {

AccessTime: time.Unix(s.Atimespec.Unix()),
ModTime: time.Unix(s.Mtimespec.Unix()),
ChangeTime: time.Unix(s.Ctimespec.Unix()),
}

return extFI
Expand Down
1 change: 1 addition & 0 deletions internal/fs/stat_unix.go
Expand Up @@ -30,6 +30,7 @@ func extendedStat(fi os.FileInfo) ExtendedFileInfo {

AccessTime: time.Unix(s.Atim.Unix()),
ModTime: time.Unix(s.Mtim.Unix()),
ChangeTime: time.Unix(s.Ctim.Unix()),
}

return extFI
Expand Down
2 changes: 2 additions & 0 deletions internal/fs/stat_windows.go
Expand Up @@ -27,5 +27,7 @@ func extendedStat(fi os.FileInfo) ExtendedFileInfo {
mtime := syscall.NsecToTimespec(s.LastWriteTime.Nanoseconds())
extFI.ModTime = time.Unix(mtime.Unix())

extFI.ChangeTime = extFI.ModTime

return extFI
}

0 comments on commit 3e98d90

Please sign in to comment.