Merge pull request #2253 from restic/fix-2174

Make sure timestamps are valid
This commit is contained in:
Alexander Neumann 2019-04-25 09:14:43 +02:00
commit 870bc5108e
3 changed files with 81 additions and 17 deletions

View File

@ -0,0 +1,10 @@
Bugfix: Save files with invalid timestamps
When restic reads invalid timestamps (year is before 0000 or after 9999) it
refused to read and archive the file. We've changed the behavior and will now
save modified timestamps with the year set to either 0000 or 9999, the rest of
the timestamp stays the same, so the file will be saved (albeit with a bogus
timestamp).
https://github.com/restic/restic/issues/2174
https://github.com/restic/restic/issues/1173

View File

@ -332,24 +332,27 @@ func (node *Node) createFifoAt(path string) error {
return mkfifo(path, 0600)
}
// FixTime returns a time.Time which can safely be used to marshal as JSON. If
// the timestamp is ealier that year zero, the year is set to zero. In the same
// way, if the year is larger than 9999, the year is set to 9999. Other than
// the year nothing is changed.
func FixTime(t time.Time) time.Time {
switch {
case t.Year() < 0000:
return t.AddDate(-t.Year(), 0, 0)
case t.Year() > 9999:
return t.AddDate(-(t.Year() - 9999), 0, 0)
default:
return t
}
}
func (node Node) MarshalJSON() ([]byte, error) {
if node.ModTime.Year() < 0 || node.ModTime.Year() > 9999 {
err := errors.Errorf("node %v has invalid ModTime year %d: %v",
node.Path, node.ModTime.Year(), node.ModTime)
return nil, err
}
if node.ChangeTime.Year() < 0 || node.ChangeTime.Year() > 9999 {
err := errors.Errorf("node %v has invalid ChangeTime year %d: %v",
node.Path, node.ChangeTime.Year(), node.ChangeTime)
return nil, err
}
if node.AccessTime.Year() < 0 || node.AccessTime.Year() > 9999 {
err := errors.Errorf("node %v has invalid AccessTime year %d: %v",
node.Path, node.AccessTime.Year(), node.AccessTime)
return nil, err
}
// make sure invalid timestamps for mtime and atime are converted to
// something we can actually save.
node.ModTime = FixTime(node.ModTime)
node.AccessTime = FixTime(node.AccessTime)
node.ChangeTime = FixTime(node.ChangeTime)
type nodeJSON Node
nj := nodeJSON(node)

View File

@ -244,3 +244,54 @@ func AssertFsTimeEqual(t *testing.T, label string, nodeType string, t1 time.Time
rtest.Assert(t, equal, "%s: %s doesn't match (%v != %v)", label, nodeType, t1, t2)
}
func parseTimeNano(t testing.TB, s string) time.Time {
// 2006-01-02T15:04:05.999999999Z07:00
ts, err := time.Parse(time.RFC3339Nano, s)
if err != nil {
t.Fatalf("error parsing %q: %v", s, err)
}
return ts
}
func TestFixTime(t *testing.T) {
// load UTC location
utc, err := time.LoadLocation("")
if err != nil {
t.Fatal(err)
}
var tests = []struct {
src, want time.Time
}{
{
src: parseTimeNano(t, "2006-01-02T15:04:05.999999999+07:00"),
want: parseTimeNano(t, "2006-01-02T15:04:05.999999999+07:00"),
},
{
src: time.Date(0, 1, 2, 3, 4, 5, 6, utc),
want: parseTimeNano(t, "0000-01-02T03:04:05.000000006+00:00"),
},
{
src: time.Date(-2, 1, 2, 3, 4, 5, 6, utc),
want: parseTimeNano(t, "0000-01-02T03:04:05.000000006+00:00"),
},
{
src: time.Date(12345, 1, 2, 3, 4, 5, 6, utc),
want: parseTimeNano(t, "9999-01-02T03:04:05.000000006+00:00"),
},
{
src: time.Date(9999, 1, 2, 3, 4, 5, 6, utc),
want: parseTimeNano(t, "9999-01-02T03:04:05.000000006+00:00"),
},
}
for _, test := range tests {
t.Run("", func(t *testing.T) {
res := restic.FixTime(test.src)
if !res.Equal(test.want) {
t.Fatalf("wrong result for %v, want:\n %v\ngot:\n %v", test.src, test.want, res)
}
})
}
}