0b89e86cd1
This removes all of the special handling code for both TarCheck() and Check() so that everything now uses the new (generic) Compare() code. In addition, the tests had to be modified to reflect the new classes of errors. Signed-off-by: Aleksa Sarai <asarai@suse.de>
371 lines
8.1 KiB
Go
371 lines
8.1 KiB
Go
package mtree
|
|
|
|
import (
|
|
"archive/tar"
|
|
"bytes"
|
|
"io"
|
|
"io/ioutil"
|
|
"os"
|
|
"syscall"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func ExampleStreamer() {
|
|
fh, err := os.Open("./testdata/test.tar")
|
|
if err != nil {
|
|
// handle error ...
|
|
}
|
|
str := NewTarStreamer(fh, nil)
|
|
if err := extractTar("/tmp/dir", str); err != nil {
|
|
// handle error ...
|
|
}
|
|
|
|
dh, err := str.Hierarchy()
|
|
if err != nil {
|
|
// handle error ...
|
|
}
|
|
|
|
res, err := Check("/tmp/dir/", dh, nil)
|
|
if err != nil {
|
|
// handle error ...
|
|
}
|
|
if len(res) > 0 {
|
|
// handle validation issue ...
|
|
}
|
|
}
|
|
func extractTar(root string, tr io.Reader) error {
|
|
return nil
|
|
}
|
|
|
|
func TestTar(t *testing.T) {
|
|
/*
|
|
data, err := makeTarStream()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
buf := bytes.NewBuffer(data)
|
|
str := NewTarStreamer(buf, append(DefaultKeywords, "sha1"))
|
|
*/
|
|
/*
|
|
// open empty folder and check size.
|
|
fh, err := os.Open("./testdata/empty")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
log.Println(fh.Stat())
|
|
fh.Close() */
|
|
fh, err := os.Open("./testdata/test.tar")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
str := NewTarStreamer(fh, append(DefaultKeywords, "sha1"))
|
|
|
|
if _, err := io.Copy(ioutil.Discard, str); err != nil && err != io.EOF {
|
|
t.Fatal(err)
|
|
}
|
|
if err := str.Close(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer fh.Close()
|
|
|
|
// get DirectoryHierarcy struct from walking the tar archive
|
|
tdh, err := str.Hierarchy()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if tdh == nil {
|
|
t.Fatal("expected a DirectoryHierarchy struct, but got nil")
|
|
}
|
|
|
|
fh, err = os.Create("./testdata/test.mtree")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer os.Remove("./testdata/test.mtree")
|
|
|
|
// put output of tar walk into test.mtree
|
|
_, err = tdh.WriteTo(fh)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
fh.Close()
|
|
|
|
// now simulate gomtree -T testdata/test.tar -f testdata/test.mtree
|
|
fh, err = os.Open("./testdata/test.mtree")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer fh.Close()
|
|
|
|
dh, err := ParseSpec(fh)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
res, err := TarCheck(tdh, dh, append(DefaultKeywords, "sha1"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// print any failures, and then call t.Fatal once all failures/extra/missing
|
|
// are outputted
|
|
if len(res) > 0 {
|
|
for _, delta := range res {
|
|
t.Error(delta)
|
|
}
|
|
t.Fatal("unexpected errors")
|
|
}
|
|
}
|
|
|
|
// This test checks how gomtree handles archives that were created
|
|
// with multiple directories, i.e, archives created with something like:
|
|
// `tar -cvf some.tar dir1 dir2 dir3 dir4/dir5 dir6` ... etc.
|
|
// The testdata of collection.tar resemble such an archive. the `collection` folder
|
|
// is the contents of `collection.tar` extracted
|
|
func TestArchiveCreation(t *testing.T) {
|
|
fh, err := os.Open("./testdata/collection.tar")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
str := NewTarStreamer(fh, []string{"sha1"})
|
|
|
|
if _, err := io.Copy(ioutil.Discard, str); err != nil && err != io.EOF {
|
|
t.Fatal(err)
|
|
}
|
|
if err := str.Close(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer fh.Close()
|
|
|
|
// get DirectoryHierarcy struct from walking the tar archive
|
|
tdh, err := str.Hierarchy()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Test the tar manifest against the actual directory
|
|
res, err := Check("./testdata/collection", tdh, []string{"sha1"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if len(res) > 0 {
|
|
for _, delta := range res {
|
|
t.Error(delta)
|
|
}
|
|
t.Fatal("unexpected errors")
|
|
}
|
|
|
|
// Test the tar manifest against itself
|
|
res, err = TarCheck(tdh, tdh, []string{"sha1"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(res) > 0 {
|
|
for _, delta := range res {
|
|
t.Error(delta)
|
|
}
|
|
t.Fatal("unexpected errors")
|
|
}
|
|
|
|
// Validate the directory manifest against the archive
|
|
dh, err := Walk("./testdata/collection", nil, []string{"sha1"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
res, err = TarCheck(tdh, dh, []string{"sha1"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(res) > 0 {
|
|
for _, delta := range res {
|
|
t.Error(delta)
|
|
}
|
|
t.Fatal("unexpected errors")
|
|
}
|
|
}
|
|
|
|
// Now test a tar file that was created with just the path to a file. In this
|
|
// test case, the traversal and creation of "placeholder" directories are
|
|
// evaluated. Also, The fact that this archive contains a single entry, yet the
|
|
// entry is associated with a file that has parent directories, means that the
|
|
// "." directory should be the lowest sub-directory under which `file` is contained.
|
|
func TestTreeTraversal(t *testing.T) {
|
|
fh, err := os.Open("./testdata/traversal.tar")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
str := NewTarStreamer(fh, DefaultTarKeywords)
|
|
|
|
if _, err = io.Copy(ioutil.Discard, str); err != nil && err != io.EOF {
|
|
t.Fatal(err)
|
|
}
|
|
if err = str.Close(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
fh.Close()
|
|
tdh, err := str.Hierarchy()
|
|
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
res, err := TarCheck(tdh, tdh, []string{"sha1"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(res) > 0 {
|
|
for _, delta := range res {
|
|
t.Error(delta)
|
|
}
|
|
t.Fatal("unexpected errors")
|
|
}
|
|
|
|
// top-level "." directory will contain contents of traversal.tar
|
|
res, err = Check("./testdata/.", tdh, []string{"sha1"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(res) > 0 {
|
|
var failed bool
|
|
for _, delta := range res {
|
|
// We only care about missing or modified files.
|
|
// The original test was written using the old check code.
|
|
if delta.Type() != Extra {
|
|
failed = true
|
|
t.Error(delta)
|
|
}
|
|
}
|
|
if failed {
|
|
t.Fatal("unexpected errors")
|
|
}
|
|
}
|
|
|
|
// Now test an archive that requires placeholder directories, i.e, there are
|
|
// no headers in the archive that are associated with the actual directory name
|
|
fh, err = os.Open("./testdata/singlefile.tar")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
str = NewTarStreamer(fh, DefaultTarKeywords)
|
|
if _, err = io.Copy(ioutil.Discard, str); err != nil && err != io.EOF {
|
|
t.Fatal(err)
|
|
}
|
|
if err = str.Close(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
tdh, err = str.Hierarchy()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Implied top-level "." directory will contain the contents of singlefile.tar
|
|
res, err = Check("./testdata/.", tdh, []string{"sha1"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(res) > 0 {
|
|
var failed bool
|
|
for _, delta := range res {
|
|
// We only care about missing or modified files.
|
|
// The original test was written using the old check code.
|
|
if delta.Type() != Extra {
|
|
failed = true
|
|
t.Error(delta)
|
|
}
|
|
}
|
|
if failed {
|
|
t.Fatal("unexpected errors")
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestHardlinks(t *testing.T) {
|
|
fh, err := os.Open("./testdata/hardlinks.tar")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
str := NewTarStreamer(fh, append(DefaultTarKeywords, "nlink"))
|
|
|
|
if _, err = io.Copy(ioutil.Discard, str); err != nil && err != io.EOF {
|
|
t.Fatal(err)
|
|
}
|
|
if err = str.Close(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
fh.Close()
|
|
tdh, err := str.Hierarchy()
|
|
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
foundnlink := false
|
|
for _, e := range tdh.Entries {
|
|
if e.Type == RelativeType {
|
|
for _, kv := range e.Keywords {
|
|
if KeyVal(kv).Keyword() == "nlink" {
|
|
foundnlink = true
|
|
if KeyVal(kv).Value() != "3" {
|
|
t.Errorf("expected to have 3 hardlinks for %s", e.Name)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if !foundnlink {
|
|
t.Errorf("nlink expected to be evaluated")
|
|
}
|
|
}
|
|
|
|
type fakeFile struct {
|
|
Name, Body string
|
|
Mode int64
|
|
Type byte
|
|
Sec, Nsec int64
|
|
Xattrs map[string]string
|
|
}
|
|
|
|
// minimal tar archive that mimics what is in ./testdata/test.tar
|
|
var minimalFiles = []fakeFile{
|
|
{"x/", "", 0755, '5', 0, 0, nil},
|
|
{"x/files", "howdy\n", 0644, '0', 0, 0, nil},
|
|
}
|
|
|
|
func makeTarStream(ff []fakeFile) ([]byte, error) {
|
|
buf := new(bytes.Buffer)
|
|
|
|
// Create a new tar archive.
|
|
tw := tar.NewWriter(buf)
|
|
|
|
// Add some files to the archive.
|
|
for _, file := range ff {
|
|
hdr := &tar.Header{
|
|
Name: file.Name,
|
|
Uid: syscall.Getuid(),
|
|
Gid: syscall.Getgid(),
|
|
Mode: file.Mode,
|
|
Typeflag: file.Type,
|
|
Size: int64(len(file.Body)),
|
|
ModTime: time.Unix(file.Sec, file.Nsec),
|
|
AccessTime: time.Unix(file.Sec, file.Nsec),
|
|
ChangeTime: time.Unix(file.Sec, file.Nsec),
|
|
Xattrs: file.Xattrs,
|
|
}
|
|
if err := tw.WriteHeader(hdr); err != nil {
|
|
return nil, err
|
|
}
|
|
if len(file.Body) > 0 {
|
|
if _, err := tw.Write([]byte(file.Body)); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
}
|
|
// Make sure to check the error on Close.
|
|
if err := tw.Close(); err != nil {
|
|
return nil, err
|
|
}
|
|
return buf.Bytes(), nil
|
|
}
|