keywords: correctly handle time keywords

Previously, the time generation code would inexplicably drop parts of
the nanotime -- potentially causing validation to succeed when it should
fail. This was probably do to a bug in the remainder logic, but instead
we should be using .Nanosecond() anyway.

After changing the time of a file with a test case like this:

    // Change the time to something known with nanosec != 0.
    chtime := time.Unix(100, 987654321)
    if err := os.Chtimes("somefile", chtime, chtime); err != nil {
	// panic
    }

timeKeywordFunc() would return the wrong value (time=100.000000021).
This fixes the issue and adds a test case.

Signed-off-by: Aleksa Sarai <asarai@suse.de>
This commit is contained in:
Aleksa Sarai 2016-11-01 17:27:39 +11:00
parent c5fc89d0f1
commit d0f7e8cb8b
No known key found for this signature in database
GPG Key ID: 9E18AA267DDB8DB4
2 changed files with 99 additions and 6 deletions

View File

@ -287,14 +287,12 @@ var (
}
}
tartimeKeywordFunc = func(path string, info os.FileInfo, r io.Reader) (string, error) {
return fmt.Sprintf("tar_time=%d.000000000", info.ModTime().Unix()), nil
return fmt.Sprintf("tar_time=%d.%9.9d", info.ModTime().Unix(), 0), nil
}
timeKeywordFunc = func(path string, info os.FileInfo, r io.Reader) (string, error) {
t := info.ModTime().UnixNano()
if t == 0 {
return "time=0.000000000", nil
}
return fmt.Sprintf("time=%d.%9.9d", (t / 1e9), (t % (t / 1e9))), nil
tSec := info.ModTime().Unix()
tNano := info.ModTime().Nanosecond()
return fmt.Sprintf("time=%d.%9.9d", tSec, tNano), nil
}
linkKeywordFunc = func(path string, info os.FileInfo, r io.Reader) (string, error) {
if sys, ok := info.Sys().(*tar.Header); ok {

95
keywords_test.go Normal file
View File

@ -0,0 +1,95 @@
package mtree
import (
"fmt"
"os"
"testing"
"time"
)
type fakeFileInfo struct {
mtime time.Time
}
func (ffi fakeFileInfo) Name() string {
// noop
return ""
}
func (ffi fakeFileInfo) Size() int64 {
// noop
return -1
}
func (ffi fakeFileInfo) Mode() os.FileMode {
// noop
return 0
}
func (ffi fakeFileInfo) ModTime() time.Time {
return ffi.mtime
}
func (ffi fakeFileInfo) IsDir() bool {
return ffi.Mode().IsDir()
}
func (ffi fakeFileInfo) Sys() interface{} {
// noop
return nil
}
func TestKeywordsTimeNano(t *testing.T) {
// We have to make sure that timeKeywordFunc always returns the correct
// formatting with regards to the nanotime.
for _, test := range []struct {
sec, nsec int64
}{
{1234, 123456789},
{5555, 987654321},
{1337, 100000000},
{8888, 999999999},
{144123582122, 1},
{857125628319, 0},
} {
mtime := time.Unix(test.sec, test.nsec)
expected := fmt.Sprintf("time=%d.%9.9d", test.sec, test.nsec)
got, err := timeKeywordFunc("", fakeFileInfo{
mtime: mtime,
}, nil)
if err != nil {
t.Errorf("unexpected error while parsing '%q': %q", mtime, err)
}
if expected != got {
t.Errorf("keyword didn't match, expected '%s' got '%s'", expected, got)
}
}
}
func TestKeywordsTimeTar(t *testing.T) {
// tartimeKeywordFunc always has nsec = 0.
for _, test := range []struct {
sec, nsec int64
}{
{1234, 123456789},
{5555, 987654321},
{1337, 100000000},
{8888, 999999999},
{144123582122, 1},
{857125628319, 0},
} {
mtime := time.Unix(test.sec, test.nsec)
expected := fmt.Sprintf("tar_time=%d.%9.9d", test.sec, 0)
got, err := tartimeKeywordFunc("", fakeFileInfo{
mtime: mtime,
}, nil)
if err != nil {
t.Errorf("unexpected error while parsing '%q': %q", mtime, err)
}
if expected != got {
t.Errorf("keyword didn't match, expected '%s' got '%s'", expected, got)
}
}
}