github.com/apcera/util@v0.0.0-20180322191801-7a50bc84ee48/tarhelper/tar_test.go (about)

     1  // Copyright 2013-2016 Apcera Inc. All rights reserved.
     2  
     3  package tarhelper
     4  
     5  import (
     6  	"archive/tar"
     7  	"bytes"
     8  	"fmt"
     9  	"io"
    10  	"io/ioutil"
    11  	"os"
    12  	"path"
    13  	"testing"
    14  	"time"
    15  
    16  	tt "github.com/apcera/util/testtool"
    17  )
    18  
    19  func makeTestDir(t *testing.T) string {
    20  	testHelper := tt.StartTest(t)
    21  	//defer testHelper.FinishTest()
    22  
    23  	cwd, err := os.Getwd()
    24  	tt.TestExpectSuccess(t, err)
    25  	testHelper.AddTestFinalizer(func() {
    26  		tt.TestExpectSuccess(t, os.Chdir(cwd))
    27  	})
    28  	dir := testHelper.TempDir()
    29  	tt.TestExpectSuccess(t, os.Chdir(dir))
    30  	mode := os.FileMode(0755)
    31  	os.Mkdir(cwd, mode) //Don't care about return value.  For some reason CWD is not created by go test on all systems.
    32  	tt.TestExpectSuccess(t, os.Mkdir("a", mode))
    33  	tt.TestExpectSuccess(t, os.Mkdir("a/b", mode))
    34  	tt.TestExpectSuccess(t, os.Mkdir("a/b/c", mode))
    35  	tt.TestExpectSuccess(t, os.Mkdir("a/b/c/d", mode))
    36  	tt.TestExpectSuccess(t, os.Mkdir("a/b/i", mode))
    37  	tt.TestExpectSuccess(t, os.Mkdir("a/b/i/j", mode))
    38  	tt.TestExpectSuccess(t, ioutil.WriteFile("a/b/c/d/e", []byte{}, mode))
    39  	tt.TestExpectSuccess(t, ioutil.WriteFile("a/b/c/f", []byte{}, mode))
    40  	tt.TestExpectSuccess(t, ioutil.WriteFile("a/b/g", []byte{}, mode))
    41  	tt.TestExpectSuccess(t, ioutil.WriteFile("a/b/i/j/k", []byte{}, mode))
    42  	tt.TestExpectSuccess(t, os.Symlink("/bin/bash", "a/b/bash"))
    43  	tt.TestExpectSuccess(t, os.Symlink("../i", "a/b/c/l"))
    44  	tt.TestExpectSuccess(t, os.Symlink("g", "a/b/h"))
    45  	tt.TestExpectSuccess(t, os.Symlink("k", "a/b/i/j/l"))
    46  	tt.TestExpectSuccess(t, os.Symlink("../../g", "a/b/i/j/m"))
    47  	return dir
    48  }
    49  
    50  func TestTarSimple(t *testing.T) {
    51  	testHelper := tt.StartTest(t)
    52  	defer testHelper.FinishTest()
    53  
    54  	w := bytes.NewBufferString("")
    55  	tw := NewTar(w, makeTestDir(t))
    56  	tt.TestExpectSuccess(t, tw.Archive())
    57  }
    58  
    59  func TestTarHooks(t *testing.T) {
    60  	testHelper := tt.StartTest(t)
    61  	defer testHelper.FinishTest()
    62  
    63  	w := bytes.NewBufferString("")
    64  	tw := NewTar(w, makeTestDir(t))
    65  	prefixRan := false
    66  	suffixRan := false
    67  	tw.PrefixHook = func(archive *tar.Writer) error {
    68  		prefixRan = true
    69  		return nil
    70  	}
    71  	tw.SuffixHook = func(archive *tar.Writer) error {
    72  		suffixRan = true
    73  		return nil
    74  	}
    75  	tt.TestExpectSuccess(t, tw.Archive())
    76  	tt.TestTrue(t, prefixRan)
    77  	tt.TestTrue(t, suffixRan)
    78  }
    79  
    80  func TestTarVirtualPath(t *testing.T) {
    81  	testHelper := tt.StartTest(t)
    82  	defer testHelper.FinishTest()
    83  
    84  	w := bytes.NewBufferString("")
    85  	tw := NewTar(w, makeTestDir(t))
    86  	tw.VirtualPath = "foo"
    87  	tt.TestExpectSuccess(t, tw.Archive())
    88  }
    89  
    90  func TestExcludeRootPath(t *testing.T) {
    91  	testHelper := tt.StartTest(t)
    92  	defer testHelper.FinishTest()
    93  
    94  	w := bytes.NewBufferString("")
    95  	tw := NewTar(w, makeTestDir(t))
    96  	tw.ExcludeRootPath = true
    97  	tt.TestEqual(t, tw.excludeRootPath("./"), true)
    98  	tt.TestExpectSuccess(t, tw.Archive())
    99  
   100  	archive := tar.NewReader(w)
   101  	rootHeader := ""
   102  	for {
   103  		header, err := archive.Next()
   104  		if err == io.EOF {
   105  			break
   106  		}
   107  		if header.Name == "./" {
   108  			rootHeader = header.Name
   109  		}
   110  	}
   111  
   112  	tt.TestNotEqual(t, rootHeader, "./")
   113  }
   114  
   115  func TestIncludeRootPath(t *testing.T) {
   116  	testHelper := tt.StartTest(t)
   117  	defer testHelper.FinishTest()
   118  
   119  	w := bytes.NewBufferString("")
   120  	tw := NewTar(w, makeTestDir(t))
   121  	tw.ExcludeRootPath = false
   122  	tt.TestEqual(t, tw.excludeRootPath("./"), false)
   123  	tt.TestExpectSuccess(t, tw.Archive())
   124  
   125  	archive := tar.NewReader(w)
   126  	rootHeader := ""
   127  	for {
   128  		header, err := archive.Next()
   129  		if err == io.EOF {
   130  			break
   131  		}
   132  		if header.Name == "./" {
   133  			rootHeader = header.Name
   134  		}
   135  	}
   136  
   137  	tt.TestEqual(t, rootHeader, "./")
   138  }
   139  
   140  func TestPathExclusion(t *testing.T) {
   141  	testHelper := tt.StartTest(t)
   142  	defer testHelper.FinishTest()
   143  
   144  	type testcase struct {
   145  		RE       string // e.g. "p.*h"
   146  		Path     string // e.g. "path"
   147  		Expected map[string]bool
   148  	}
   149  
   150  	testcases := []testcase{
   151  		{
   152  			RE: "simple", Path: "simple",
   153  			Expected: map[string]bool{
   154  				"simple":                      true,
   155  				"/simple":                     true,
   156  				"simple/":                     true,
   157  				"/simple/":                    true,
   158  				"/before/simple":              true,
   159  				"/three/levels/before/simple": true,
   160  			},
   161  		}, {
   162  			RE: "/simple", Path: "simple",
   163  			Expected: map[string]bool{
   164  				"/simple": true, "/simple/": true,
   165  			},
   166  		}, {
   167  			RE:       "slash/",
   168  			Path:     "slash",
   169  			Expected: map[string]bool{},
   170  		}, {
   171  			RE:       "/simple/",
   172  			Path:     "simple",
   173  			Expected: map[string]bool{},
   174  		}, {
   175  			RE:   "sim.*-RE",
   176  			Path: "simple-RE",
   177  			Expected: map[string]bool{
   178  				"simple-RE":                      true,
   179  				"/simple-RE":                     true,
   180  				"simple-RE/":                     true,
   181  				"/simple-RE/":                    true,
   182  				"/before/simple-RE":              true,
   183  				"/three/levels/before/simple-RE": true,
   184  				"simp-middle-le-RE":              true,
   185  			},
   186  		}, {
   187  			RE:   "simple-RE.*",
   188  			Path: "simple-RE",
   189  			Expected: map[string]bool{
   190  				"simple-RE":                      true,
   191  				"/simple-RE":                     true,
   192  				"simple-RE/":                     true,
   193  				"/simple-RE/":                    true,
   194  				"/before/simple-RE":              true,
   195  				"/three/levels/before/simple-RE": true,
   196  				"simple-RE-after":                true,
   197  			},
   198  		}, {
   199  			RE:   "/simple-RE.*",
   200  			Path: "simple-RE",
   201  			Expected: map[string]bool{
   202  				"/simple-RE":                    true,
   203  				"/simple-RE/":                   true,
   204  				"/simple-RE/after":              true,
   205  				"/simple-RE/three/levels/after": true,
   206  			},
   207  		},
   208  	}
   209  
   210  	// test the "empty exclusion list" cases
   211  	w := bytes.NewBufferString("")
   212  	tw := NewTar(w, makeTestDir(t))
   213  	tt.TestEqual(t, tw.shouldBeExcluded("/any/thing", false), false)
   214  	tw.ExcludePath("")
   215  	tt.TestEqual(t, tw.shouldBeExcluded("/any/thing", false), false)
   216  
   217  	// test these cases on new instances of Tar object to avoid any
   218  	// possible side effects/conflicts
   219  
   220  	for _, tc := range testcases {
   221  		w = bytes.NewBufferString("")
   222  		tw = NewTar(w, makeTestDir(t))
   223  		tw.ExcludePath(tc.RE)
   224  
   225  		stdPaths := []string{
   226  			tc.Path,
   227  			"/" + tc.Path,
   228  			tc.Path + "/",
   229  			"/" + tc.Path + "/",
   230  			"/before/" + tc.Path,
   231  			"/" + tc.Path + "/after",
   232  			"/before/" + tc.Path + "/after",
   233  			"/three/levels/before/" + tc.Path,
   234  			"/" + tc.Path + "/three/levels/after",
   235  			"before-" + tc.Path,
   236  			tc.Path + "-after",
   237  			"before-" + tc.Path + "-after",
   238  			tc.Path[:len(tc.Path)/2] + "-middle-" + tc.Path[len(tc.Path)/2:],
   239  		}
   240  
   241  		for _, path := range stdPaths {
   242  			tt.TestEqual(t, tw.shouldBeExcluded(path, false), tc.Expected[path],
   243  				fmt.Sprintf("Path:%q, tc:%v", path, tc))
   244  			delete(tc.Expected, path)
   245  		}
   246  
   247  		for path, exp := range tc.Expected {
   248  			tt.TestEqual(t, tw.shouldBeExcluded(path, false), exp)
   249  		}
   250  	}
   251  
   252  	// This should return nil for these paths as they are excluded.
   253  	// An extra check that processEntry indeed bails on excluded items
   254  	w = bytes.NewBufferString("")
   255  	tw = NewTar(w, makeTestDir(t))
   256  	tw.ExcludePath("/one.*")
   257  	tw.ExcludePath("/two/two/.*")
   258  	tw.ExcludePath("/three/three/three.*")
   259  	var fi staticFileInfo
   260  	tt.TestExpectSuccess(t, tw.processEntry("/one/something", fi, []string{}))
   261  	tt.TestExpectSuccess(t, tw.processEntry("/two/two/something", fi, []string{}))
   262  	tt.TestExpectSuccess(t, tw.processEntry("/three/three/three-something", fi, []string{}))
   263  }
   264  
   265  func TestTarIDMapping(t *testing.T) {
   266  	testHelper := tt.StartTest(t)
   267  	defer testHelper.FinishTest()
   268  
   269  	// set up our mapping funcs
   270  	uidFuncCalled := false
   271  	gidFuncCalled := false
   272  	uidMappingFunc := func(uid int) (int, error) {
   273  		uidFuncCalled = true
   274  		return 0, nil
   275  	}
   276  	gidMappingFunc := func(gid int) (int, error) {
   277  		gidFuncCalled = true
   278  		return 0, nil
   279  	}
   280  
   281  	// set up our untar and use the test tar helper
   282  	w := bytes.NewBufferString("")
   283  	tw := NewTar(w, makeTestDir(t))
   284  	tw.IncludeOwners = true
   285  	tw.OwnerMappingFunc = uidMappingFunc
   286  	tw.GroupMappingFunc = gidMappingFunc
   287  	tt.TestExpectSuccess(t, tw.Archive())
   288  
   289  	// untar it and verify all of the uid/gids are 0
   290  	archive := tar.NewReader(w)
   291  	for {
   292  		header, err := archive.Next()
   293  		if err == io.EOF {
   294  			break
   295  		}
   296  		tt.TestExpectSuccess(t, err)
   297  		tt.TestEqual(t, header.Uid, 0)
   298  		tt.TestEqual(t, header.Gid, 0)
   299  	}
   300  }
   301  
   302  func TestSymlinkOptDereferenceLinkToFile(t *testing.T) {
   303  	testHelper := tt.StartTest(t)
   304  	defer testHelper.FinishTest()
   305  
   306  	cwd, err := os.Getwd()
   307  	tt.TestExpectSuccess(t, err)
   308  	testHelper.AddTestFinalizer(func() {
   309  		tt.TestExpectSuccess(t, os.Chdir(cwd))
   310  	})
   311  
   312  	dir := testHelper.TempDir()
   313  	tt.TestExpectSuccess(t, os.Chdir(dir))
   314  	mode := os.FileMode(0755)
   315  	tt.TestExpectSuccess(t, os.Mkdir("a", mode))
   316  	tt.TestExpectSuccess(t, os.Mkdir("a/b", mode))
   317  	tt.TestExpectSuccess(t, os.Mkdir("a/b/c", mode))
   318  	tt.TestExpectSuccess(t, os.Mkdir("a/b/c/d", mode))
   319  	tt.TestExpectSuccess(t, os.Mkdir("a/b/i", mode))
   320  	tt.TestExpectSuccess(t, ioutil.WriteFile("a/b/i/j", []byte{'t', 'e', 's', 't'}, mode))
   321  	tt.TestExpectSuccess(t, os.Symlink("/bin/bash", "a/b/bash"))
   322  	tt.TestExpectSuccess(t, os.Symlink("../i/j", "a/b/c/lj"))
   323  	w := bytes.NewBufferString("")
   324  	tw := NewTar(w, dir)
   325  	tw.UserOptions |= c_DEREF
   326  	tt.TestExpectSuccess(t, tw.Archive())
   327  
   328  	extractionPath := path.Join(dir, "pkg")
   329  	err = os.MkdirAll(extractionPath, 0755)
   330  	tt.TestExpectSuccess(t, err)
   331  
   332  	// extract
   333  	r := bytes.NewReader(w.Bytes())
   334  	u := NewUntar(r, extractionPath)
   335  	u.AbsoluteRoot = dir
   336  	tt.TestExpectSuccess(t, u.Extract())
   337  
   338  	dirExists := func(name string) {
   339  		f, err := os.Stat(path.Join(extractionPath, name))
   340  		tt.TestExpectSuccess(t, err)
   341  		tt.TestEqual(t, true, f.IsDir())
   342  	}
   343  
   344  	sameFileContents := func(f1 string, f2 string) {
   345  		b1, err := ioutil.ReadFile(f1)
   346  		tt.TestExpectSuccess(t, err)
   347  
   348  		b2, err := ioutil.ReadFile(f2)
   349  		tt.TestExpectSuccess(t, err)
   350  		tt.TestEqual(t, b1, b2)
   351  	}
   352  
   353  	// Verify dirs a, a/b, a/b/c, a/b/c/d
   354  	dirExists("./a")
   355  	dirExists("./a/b")
   356  	dirExists("./a/b/c")
   357  	dirExists("./a/b/c/d")
   358  	dirExists("./a/b/i")
   359  
   360  	// Verify a/b/bash and /bin/bash are same
   361  	sameFileContents(path.Join(extractionPath, "./a/b/bash"), "/bin/bash")
   362  
   363  	// Verify that a/b/i/j and a/b/c/lj contents are same
   364  	sameFileContents(path.Join(extractionPath, "./a/b/i/j"), path.Join(extractionPath, "./a/b/c/lj"))
   365  }
   366  
   367  func TestSymlinkOptDereferenceLinkToDir(t *testing.T) {
   368  	testHelper := tt.StartTest(t)
   369  	defer testHelper.FinishTest()
   370  
   371  	cwd, err := os.Getwd()
   372  	tt.TestExpectSuccess(t, err)
   373  	testHelper.AddTestFinalizer(func() {
   374  		tt.TestExpectSuccess(t, os.Chdir(cwd))
   375  	})
   376  
   377  	dir := testHelper.TempDir()
   378  	tt.TestExpectSuccess(t, os.Chdir(dir))
   379  	mode := os.FileMode(0755)
   380  	tt.TestExpectSuccess(t, os.Mkdir("a", mode))
   381  	tt.TestExpectSuccess(t, os.Mkdir("a/b", mode))
   382  	tt.TestExpectSuccess(t, os.Mkdir("a/b/c", mode))
   383  	tt.TestExpectSuccess(t, os.Mkdir("a/b/c/d", mode))
   384  	tt.TestExpectSuccess(t, os.Mkdir("a/b/i", mode))
   385  	tt.TestExpectSuccess(t, ioutil.WriteFile("a/b/i/j", []byte{'t', 'e', 's', 't'}, mode))
   386  	tt.TestExpectSuccess(t, os.Symlink("/bin/bash", "a/b/bash"))
   387  	tt.TestExpectSuccess(t, os.Symlink("../i", "a/b/c/l"))
   388  	w := bytes.NewBufferString("")
   389  	tw := NewTar(w, dir)
   390  	tw.UserOptions |= c_DEREF
   391  	tt.TestExpectSuccess(t, tw.Archive())
   392  
   393  	extractionPath := path.Join(dir, "pkg")
   394  	err = os.MkdirAll(extractionPath, 0755)
   395  	tt.TestExpectSuccess(t, err)
   396  
   397  	// extract
   398  	r := bytes.NewReader(w.Bytes())
   399  	u := NewUntar(r, extractionPath)
   400  	u.AbsoluteRoot = dir
   401  	tt.TestExpectSuccess(t, u.Extract())
   402  
   403  	dirExists := func(name string) {
   404  		f, err := os.Stat(path.Join(extractionPath, name))
   405  		tt.TestExpectSuccess(t, err)
   406  		tt.TestEqual(t, true, f.IsDir())
   407  	}
   408  
   409  	sameFileContents := func(f1 string, f2 string) {
   410  		b1, err := ioutil.ReadFile(f1)
   411  		tt.TestExpectSuccess(t, err)
   412  
   413  		b2, err := ioutil.ReadFile(f2)
   414  		tt.TestExpectSuccess(t, err)
   415  		tt.TestEqual(t, b1, b2)
   416  	}
   417  
   418  	// Verify dirs a, a/b, a/b/c, a/b/c/d
   419  	dirExists("./a")
   420  	dirExists("./a/b")
   421  	dirExists("./a/b/c")
   422  	dirExists("./a/b/c/d")
   423  	dirExists("./a/b/i")
   424  
   425  	// Verify a/b/bash and /bin/bash are same
   426  	sameFileContents(path.Join(extractionPath, "./a/b/bash"), "/bin/bash")
   427  
   428  	// Verify that a/b/i/j and a/b/c/l/j contents are same
   429  	sameFileContents(path.Join(extractionPath, "./a/b/i/j"), path.Join(extractionPath, "./a/b/c/l/j"))
   430  }
   431  
   432  func TestSymlinkOptDereferenceCircular(t *testing.T) {
   433  	testHelper := tt.StartTest(t)
   434  	defer testHelper.FinishTest()
   435  
   436  	cwd, err := os.Getwd()
   437  	tt.TestExpectSuccess(t, err)
   438  	testHelper.AddTestFinalizer(func() {
   439  		tt.TestExpectSuccess(t, os.Chdir(cwd))
   440  	})
   441  
   442  	dir := testHelper.TempDir()
   443  	tt.TestExpectSuccess(t, os.Chdir(dir))
   444  	mode := os.FileMode(0755)
   445  	tt.TestExpectSuccess(t, os.Mkdir("a", mode))
   446  	tt.TestExpectSuccess(t, os.Mkdir("a/b", mode))
   447  	tt.TestExpectSuccess(t, os.Mkdir("a/b/c", mode))
   448  	tt.TestExpectSuccess(t, os.Mkdir("a/b/c/d", mode))
   449  	tt.TestExpectSuccess(t, os.Mkdir("a/b/i", mode))
   450  	tt.TestExpectSuccess(t, ioutil.WriteFile("a/b/i/j", []byte{'t', 'e', 's', 't'}, mode))
   451  	tt.TestExpectSuccess(t, os.Symlink("/bin/bash", "a/b/bash"))
   452  	tt.TestExpectSuccess(t, os.Symlink(dir+"/a/b/c/l", "a/b/i/ll"))
   453  	tt.TestExpectSuccess(t, os.Symlink("../i", "a/b/c/l"))
   454  	w := bytes.NewBufferString("")
   455  	tw := NewTar(w, dir)
   456  	tw.UserOptions |= c_DEREF
   457  	tt.TestExpectSuccess(t, tw.Archive())
   458  
   459  	extractionPath := path.Join(dir, "pkg")
   460  	err = os.MkdirAll(extractionPath, 0755)
   461  	tt.TestExpectSuccess(t, err)
   462  
   463  	// extract
   464  	r := bytes.NewReader(w.Bytes())
   465  	u := NewUntar(r, extractionPath)
   466  	u.AbsoluteRoot = dir
   467  	tt.TestExpectSuccess(t, u.Extract())
   468  
   469  	fileExists := func(name string) {
   470  		_, err := os.Stat(path.Join(extractionPath, name))
   471  		tt.TestExpectSuccess(t, err)
   472  	}
   473  
   474  	dirExists := func(name string) {
   475  		f, err := os.Stat(path.Join(extractionPath, name))
   476  		tt.TestExpectSuccess(t, err)
   477  		tt.TestEqual(t, true, f.IsDir())
   478  	}
   479  
   480  	sameFileContents := func(f1 string, f2 string) {
   481  		b1, err := ioutil.ReadFile(f1)
   482  		tt.TestExpectSuccess(t, err)
   483  
   484  		b2, err := ioutil.ReadFile(f2)
   485  		tt.TestExpectSuccess(t, err)
   486  		tt.TestEqual(t, b1, b2)
   487  	}
   488  
   489  	// Verify dirs a, a/b, a/b/c, a/b/c/d
   490  	dirExists("./a")
   491  	dirExists("./a/b")
   492  	dirExists("./a/b/c")
   493  	dirExists("./a/b/c/d")
   494  	dirExists("./a/b/i")
   495  
   496  	// Verify that the file 'j' exists in both a/b/i and a/b/c/l
   497  	fileExists("./a/b/i/j")
   498  	fileExists("./a/b/c/l/j")
   499  
   500  	// Verify a/b/bash
   501  	sameFileContents(path.Join(extractionPath, "./a/b/bash"), "/bin/bash")
   502  
   503  	// Verify that a/b/i/j and a/b/c/l/j contents are same
   504  	sameFileContents(path.Join(extractionPath, "./a/b/i/j"), path.Join(extractionPath, "./a/b/c/l/j"))
   505  
   506  	// Verify that the circular symbolic link a/b/i/ll does not exis
   507  	_, err = os.Stat(path.Join(extractionPath, "./a/b/i/ll"))
   508  	tt.TestEqual(t, true, os.IsNotExist(err))
   509  }
   510  
   511  func TestSymlinkOptDereferenceCircularToRoot(t *testing.T) {
   512  	testHelper := tt.StartTest(t)
   513  	defer testHelper.FinishTest()
   514  
   515  	cwd, err := os.Getwd()
   516  	tt.TestExpectSuccess(t, err)
   517  	testHelper.AddTestFinalizer(func() {
   518  		tt.TestExpectSuccess(t, os.Chdir(cwd))
   519  	})
   520  
   521  	dir := testHelper.TempDir()
   522  	tt.TestExpectSuccess(t, os.Chdir(dir))
   523  	mode := os.FileMode(0755)
   524  	tt.TestExpectSuccess(t, os.Mkdir("a", mode))
   525  	tt.TestExpectSuccess(t, os.Mkdir("a/b", mode))
   526  	tt.TestExpectSuccess(t, os.Mkdir("a/b/c", mode))
   527  	tt.TestExpectSuccess(t, os.Mkdir("a/b/c/d", mode))
   528  	tt.TestExpectSuccess(t, os.Mkdir("a/b/i", mode))
   529  	tt.TestExpectSuccess(t, ioutil.WriteFile("a/b/i/j", []byte{'t', 'e', 's', 't'}, mode))
   530  	tt.TestExpectSuccess(t, os.Symlink("/bin/bash", "a/b/bash"))
   531  	tt.TestExpectSuccess(t, os.Symlink(dir+"/a", "a/b/i/ll"))
   532  	w := bytes.NewBufferString("")
   533  	tw := NewTar(w, dir)
   534  	tw.UserOptions |= c_DEREF
   535  	tt.TestExpectSuccess(t, tw.Archive())
   536  
   537  	extractionPath := path.Join(dir, "pkg")
   538  	err = os.MkdirAll(extractionPath, 0755)
   539  	tt.TestExpectSuccess(t, err)
   540  
   541  	// extract
   542  	r := bytes.NewReader(w.Bytes())
   543  	u := NewUntar(r, extractionPath)
   544  	u.AbsoluteRoot = dir
   545  	tt.TestExpectSuccess(t, u.Extract())
   546  
   547  	fileExists := func(name string) {
   548  		_, err := os.Stat(path.Join(extractionPath, name))
   549  		tt.TestExpectSuccess(t, err)
   550  	}
   551  
   552  	dirExists := func(name string) {
   553  		f, err := os.Stat(path.Join(extractionPath, name))
   554  		tt.TestExpectSuccess(t, err)
   555  		tt.TestEqual(t, true, f.IsDir())
   556  	}
   557  
   558  	sameFileContents := func(f1 string, f2 string) {
   559  		b1, err := ioutil.ReadFile(f1)
   560  		tt.TestExpectSuccess(t, err)
   561  
   562  		b2, err := ioutil.ReadFile(f2)
   563  		tt.TestExpectSuccess(t, err)
   564  		tt.TestEqual(t, b1, b2)
   565  	}
   566  
   567  	// Verify dirs a, a/b, a/b/c, a/b/c/d
   568  	dirExists("./a")
   569  	dirExists("./a/b")
   570  	dirExists("./a/b/c")
   571  	dirExists("./a/b/c/d")
   572  	dirExists("./a/b/i")
   573  
   574  	// Verify that the file 'j' exists in a/b/i
   575  	fileExists("./a/b/i/j")
   576  
   577  	// Verify a/b/bash
   578  	sameFileContents(path.Join(extractionPath, "./a/b/bash"), "/bin/bash")
   579  
   580  	// Verify that the circular symbolic link a/b/i/ll does not exist
   581  	_, err = os.Stat(path.Join(extractionPath, "./a/b/i/ll"))
   582  	tt.TestEqual(t, true, os.IsNotExist(err))
   583  }
   584  
   585  func TestTarPointedToFile(t *testing.T) {
   586  	testHelper := tt.StartTest(t)
   587  	defer testHelper.FinishTest()
   588  
   589  	dir := testHelper.TempDir()
   590  	apath := path.Join(dir, "a")
   591  
   592  	// write the file, then read it the same way that we'll validate it
   593  	tt.TestExpectSuccess(t, ioutil.WriteFile(apath, []byte("hello world"), os.FileMode(0644)))
   594  	contents, err := ioutil.ReadFile(apath)
   595  	tt.TestExpectSuccess(t, err)
   596  	tt.TestEqual(t, string(contents), "hello world")
   597  
   598  	// tar the file
   599  	w := bytes.NewBufferString("")
   600  	tw := NewTar(w, apath)
   601  	tt.TestExpectSuccess(t, tw.Archive())
   602  
   603  	// should then also be able to untar it
   604  	dir = testHelper.TempDir()
   605  	u := NewUntar(w, dir)
   606  	u.AbsoluteRoot = dir
   607  	tt.TestExpectSuccess(t, u.Extract())
   608  
   609  	// stat it, ensure it exists and is a file, not a directory
   610  	stat, err := os.Stat(path.Join(dir, "a"))
   611  	tt.TestExpectSuccess(t, err)
   612  	tt.TestEqual(t, stat.IsDir(), false, "should be a file, not a directory")
   613  
   614  	// read the contents to verify
   615  	contents, err = ioutil.ReadFile(path.Join(dir, "a"))
   616  	tt.TestExpectSuccess(t, err)
   617  	tt.TestEqual(t, string(contents), "hello world")
   618  }
   619  
   620  func TestTarPreserveSetuid(t *testing.T) {
   621  	testHelper := tt.StartTest(t)
   622  	defer testHelper.FinishTest()
   623  
   624  	dir := testHelper.TempDir()
   625  	apath := path.Join(dir, "a")
   626  
   627  	err := ioutil.WriteFile(apath, []byte("hello world"), os.FileMode(0644))
   628  	tt.TestExpectSuccess(t, err)
   629  
   630  	err = os.Chmod(apath, os.FileMode(0644)|os.ModeSetuid)
   631  	tt.TestExpectSuccess(t, err)
   632  
   633  	w := bytes.NewBufferString("")
   634  	tw := NewTar(w, apath)
   635  	err = tw.Archive()
   636  	tt.TestExpectSuccess(t, err)
   637  
   638  	dir = testHelper.TempDir()
   639  	u := NewUntar(w, dir)
   640  	u.AbsoluteRoot = dir
   641  	err = u.Extract()
   642  	tt.TestExpectSuccess(t, err)
   643  
   644  	stat, err := os.Stat(path.Join(dir, "a"))
   645  	tt.TestExpectSuccess(t, err)
   646  	tt.TestEqual(t, stat.Mode()&os.ModeSetuid != 0, true, "must have setuid bit")
   647  }
   648  
   649  func TestTarCustomHandler(t *testing.T) {
   650  	testHelper := tt.StartTest(t)
   651  	defer testHelper.FinishTest()
   652  
   653  	w := bytes.NewBufferString("")
   654  	tw := NewTar(w, makeTestDir(t))
   655  	tw.CustomHandlers = []TarCustomHandler{
   656  		func(fullpath string, fi os.FileInfo, header *tar.Header) (bool, error) {
   657  			if header.Name == "a/b/i/j/m" {
   658  				header.Name = "a/b/i/j/n"
   659  				header.Size = 0
   660  				return true, nil
   661  			}
   662  			return false, nil
   663  		},
   664  	}
   665  	tt.TestExpectSuccess(t, tw.Archive())
   666  
   667  	archive := tar.NewReader(w)
   668  	hasRenamedFile := false
   669  	for {
   670  		header, err := archive.Next()
   671  		if err == io.EOF {
   672  			break
   673  		}
   674  		if header.Name == "a/b/i/j/m" {
   675  			tt.Fatalf(t, "The \"a/b/i/j/m\" file should have been omitted")
   676  		}
   677  		if header.Name == "a/b/i/j/n" {
   678  			hasRenamedFile = true
   679  		}
   680  	}
   681  
   682  	tt.TestEqual(t, hasRenamedFile, true, "The tar file did not include the renamed file")
   683  }
   684  
   685  type staticFileInfo struct{}
   686  
   687  func (m staticFileInfo) Name() string       { return "foo" }
   688  func (m staticFileInfo) Size() int64        { return 1 }
   689  func (m staticFileInfo) Mode() os.FileMode  { return 7777 }
   690  func (m staticFileInfo) ModTime() time.Time { return time.Now() }
   691  func (m staticFileInfo) IsDir() bool        { return false }
   692  func (m staticFileInfo) Sys() interface{}   { return nil }