forked from TrueCloudLab/restic
Merge pull request #2253 from restic/fix-2174
Make sure timestamps are valid
This commit is contained in:
commit
870bc5108e
3 changed files with 81 additions and 17 deletions
10
changelog/unreleased/issue-2174
Normal file
10
changelog/unreleased/issue-2174
Normal 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
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue