From d4317a1b0d3f01cf3d68ddbece9b9414a2916741 Mon Sep 17 00:00:00 2001 From: Derek McGowan Date: Thu, 14 Dec 2017 15:22:36 -0800 Subject: [PATCH] Fix parent directories not included in tar Signed-off-by: Derek McGowan --- archive/tar.go | 4 +- archive/tar_test.go | 126 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 129 insertions(+), 1 deletion(-) diff --git a/archive/tar.go b/archive/tar.go index c12d4f280..a649c5b45 100644 --- a/archive/tar.go +++ b/archive/tar.go @@ -381,8 +381,10 @@ func (cw *changeWriter) HandleChange(k fs.ChangeKind, p string, f os.FileInfo, e additionalLinks = cw.inodeRefs[inode] delete(cw.inodeRefs, inode) } - } else if k == fs.ChangeKindUnmodified { + } else if k == fs.ChangeKindUnmodified && !f.IsDir() { // Nothing to write to diff + // Unmodified directories should still be written to keep + // directory permissions correct on direct unpack return nil } diff --git a/archive/tar_test.go b/archive/tar_test.go index 6c3ae699b..276a47d00 100644 --- a/archive/tar_test.go +++ b/archive/tar_test.go @@ -666,6 +666,132 @@ func makeWriterToTarTest(wt WriterToTar, a fstest.Applier, validate func(string) } } +func TestDiffTar(t *testing.T) { + tests := []struct { + name string + validators []tarEntryValidator + a fstest.Applier + b fstest.Applier + }{ + { + name: "EmptyDiff", + validators: []tarEntryValidator{}, + a: fstest.Apply( + fstest.CreateDir("/etc/", 0755), + ), + b: fstest.Apply(), + }, + { + name: "ParentInclusion", + validators: []tarEntryValidator{ + dirEntry("d1/", 0755), + dirEntry("d1/d/", 0700), + dirEntry("d2/", 0770), + fileEntry("d2/f", []byte("ok"), 0644), + }, + a: fstest.Apply( + fstest.CreateDir("/d1/", 0755), + fstest.CreateDir("/d2/", 0770), + ), + b: fstest.Apply( + fstest.CreateDir("/d1/d", 0700), + fstest.CreateFile("/d2/f", []byte("ok"), 0644), + ), + }, + } + + for _, at := range tests { + t.Run(at.name, makeDiffTarTest(at.validators, at.a, at.b)) + } +} + +type tarEntryValidator func(*tar.Header, []byte) error + +func dirEntry(name string, mode int) tarEntryValidator { + return func(hdr *tar.Header, b []byte) error { + if hdr.Typeflag != tar.TypeDir { + return errors.New("not directory type") + } + if hdr.Name != name { + return errors.Errorf("wrong name %q, expected %q", hdr.Name, name) + } + if hdr.Mode != int64(mode) { + return errors.Errorf("wrong mode %o, expected %o", hdr.Mode, mode) + } + return nil + } +} + +func fileEntry(name string, expected []byte, mode int) tarEntryValidator { + return func(hdr *tar.Header, b []byte) error { + if hdr.Typeflag != tar.TypeReg { + return errors.New("not file type") + } + if hdr.Name != name { + return errors.Errorf("wrong name %q, expected %q", hdr.Name, name) + } + if hdr.Mode != int64(mode) { + return errors.Errorf("wrong mode %o, expected %o", hdr.Mode, mode) + } + if bytes.Compare(b, expected) != 0 { + return errors.Errorf("different file content") + } + return nil + } +} + +func makeDiffTarTest(validators []tarEntryValidator, a, b fstest.Applier) func(*testing.T) { + return func(t *testing.T) { + ad, err := ioutil.TempDir("", "test-make-diff-tar-") + if err != nil { + t.Fatalf("failed to create temp dir: %v", err) + } + defer os.RemoveAll(ad) + if err := a.Apply(ad); err != nil { + t.Fatalf("failed to apply a: %v", err) + } + + bd, err := ioutil.TempDir("", "test-make-diff-tar-") + if err != nil { + t.Fatalf("failed to create temp dir: %v", err) + } + defer os.RemoveAll(bd) + if err := fs.CopyDir(bd, ad); err != nil { + t.Fatalf("failed to copy dir: %v", err) + } + if err := b.Apply(bd); err != nil { + t.Fatalf("failed to apply b: %v", err) + } + + rc := Diff(context.Background(), ad, bd) + defer rc.Close() + + tr := tar.NewReader(rc) + for i := 0; ; i++ { + hdr, err := tr.Next() + if err != nil { + if err == io.EOF { + break + } + t.Fatalf("tar read error: %v", err) + } + var b []byte + if hdr.Typeflag == tar.TypeReg && hdr.Size > 0 { + b, err = ioutil.ReadAll(tr) + if err != nil { + t.Fatalf("tar read file error: %v", err) + } + } + if i >= len(validators) { + t.Fatal("no validator for entry") + } + if err := validators[i](hdr, b); err != nil { + t.Fatalf("tar entry[%d] validation fail: %#v", i, err) + } + } + } +} + type diffApplier struct{} func (d diffApplier) TestContext(ctx context.Context) (context.Context, func(), error) {