mirror of
https://github.com/restic/restic.git
synced 2024-12-25 17:27:25 +00:00
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)
|
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) {
|
func (node Node) MarshalJSON() ([]byte, error) {
|
||||||
if node.ModTime.Year() < 0 || node.ModTime.Year() > 9999 {
|
// make sure invalid timestamps for mtime and atime are converted to
|
||||||
err := errors.Errorf("node %v has invalid ModTime year %d: %v",
|
// something we can actually save.
|
||||||
node.Path, node.ModTime.Year(), node.ModTime)
|
node.ModTime = FixTime(node.ModTime)
|
||||||
return nil, err
|
node.AccessTime = FixTime(node.AccessTime)
|
||||||
}
|
node.ChangeTime = FixTime(node.ChangeTime)
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
type nodeJSON Node
|
type nodeJSON Node
|
||||||
nj := 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)
|
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