github.com/openflowlabs/storage@v1.12.13/pkg/chrootarchive/archive_test.go (about)

     1  package chrootarchive
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"hash/crc32"
     7  	"io"
     8  	"io/ioutil"
     9  	"os"
    10  	"path/filepath"
    11  	"runtime"
    12  	"strings"
    13  	"syscall"
    14  	"testing"
    15  	"time"
    16  
    17  	"github.com/containers/storage/pkg/archive"
    18  	"github.com/containers/storage/pkg/idtools"
    19  	"github.com/containers/storage/pkg/reexec"
    20  	"github.com/containers/storage/pkg/system"
    21  )
    22  
    23  func init() {
    24  	reexec.Init()
    25  }
    26  
    27  var chrootArchiver = NewArchiver(nil)
    28  
    29  func TarUntar(src, dst string) error {
    30  	return chrootArchiver.TarUntar(src, dst)
    31  }
    32  
    33  func CopyFileWithTar(src, dst string) (err error) {
    34  	return chrootArchiver.CopyFileWithTar(src, dst)
    35  }
    36  
    37  func UntarPath(src, dst string) error {
    38  	return chrootArchiver.UntarPath(src, dst)
    39  }
    40  
    41  func CopyWithTar(src, dst string) error {
    42  	return chrootArchiver.CopyWithTar(src, dst)
    43  }
    44  
    45  func TestChrootTarUntar(t *testing.T) {
    46  	tmpdir, err := ioutil.TempDir("", "storage-TestChrootTarUntar")
    47  	if err != nil {
    48  		t.Fatal(err)
    49  	}
    50  	defer os.RemoveAll(tmpdir)
    51  	src := filepath.Join(tmpdir, "src")
    52  	if err := system.MkdirAll(src, 0700, ""); err != nil {
    53  		t.Fatal(err)
    54  	}
    55  	if err := ioutil.WriteFile(filepath.Join(src, "toto"), []byte("hello toto"), 0644); err != nil {
    56  		t.Fatal(err)
    57  	}
    58  	if err := ioutil.WriteFile(filepath.Join(src, "lolo"), []byte("hello lolo"), 0644); err != nil {
    59  		t.Fatal(err)
    60  	}
    61  	stream, err := archive.Tar(src, archive.Uncompressed)
    62  	if err != nil {
    63  		t.Fatal(err)
    64  	}
    65  	dest := filepath.Join(tmpdir, "src")
    66  	if err := system.MkdirAll(dest, 0700, ""); err != nil {
    67  		t.Fatal(err)
    68  	}
    69  	if err := Untar(stream, dest, &archive.TarOptions{ExcludePatterns: []string{"lolo"}}); err != nil {
    70  		t.Fatal(err)
    71  	}
    72  }
    73  
    74  // gh#10426: Verify the fix for having a huge excludes list (like on `docker load` with large # of
    75  // local images)
    76  func TestChrootUntarWithHugeExcludesList(t *testing.T) {
    77  	tmpdir, err := ioutil.TempDir("", "storage-TestChrootUntarHugeExcludes")
    78  	if err != nil {
    79  		t.Fatal(err)
    80  	}
    81  	defer os.RemoveAll(tmpdir)
    82  	src := filepath.Join(tmpdir, "src")
    83  	if err := system.MkdirAll(src, 0700, ""); err != nil {
    84  		t.Fatal(err)
    85  	}
    86  	if err := ioutil.WriteFile(filepath.Join(src, "toto"), []byte("hello toto"), 0644); err != nil {
    87  		t.Fatal(err)
    88  	}
    89  	stream, err := archive.Tar(src, archive.Uncompressed)
    90  	if err != nil {
    91  		t.Fatal(err)
    92  	}
    93  	dest := filepath.Join(tmpdir, "dest")
    94  	if err := system.MkdirAll(dest, 0700, ""); err != nil {
    95  		t.Fatal(err)
    96  	}
    97  	options := &archive.TarOptions{}
    98  	//65534 entries of 64-byte strings ~= 4MB of environment space which should overflow
    99  	//on most systems when passed via environment or command line arguments
   100  	excludes := make([]string, 65534)
   101  	for i := 0; i < 65534; i++ {
   102  		excludes[i] = strings.Repeat(string(i), 64)
   103  	}
   104  	options.ExcludePatterns = excludes
   105  	if err := Untar(stream, dest, options); err != nil {
   106  		t.Fatal(err)
   107  	}
   108  }
   109  
   110  func TestChrootUntarEmptyArchive(t *testing.T) {
   111  	tmpdir, err := ioutil.TempDir("", "storage-TestChrootUntarEmptyArchive")
   112  	if err != nil {
   113  		t.Fatal(err)
   114  	}
   115  	defer os.RemoveAll(tmpdir)
   116  	if err := Untar(nil, tmpdir, nil); err == nil {
   117  		t.Fatal("expected error on empty archive")
   118  	}
   119  }
   120  
   121  func prepareSourceDirectory(numberOfFiles int, targetPath string, makeSymLinks bool) (int, error) {
   122  	fileData := []byte("fooo")
   123  	for n := 0; n < numberOfFiles; n++ {
   124  		fileName := fmt.Sprintf("file-%d", n)
   125  		if err := ioutil.WriteFile(filepath.Join(targetPath, fileName), fileData, 0700); err != nil {
   126  			return 0, err
   127  		}
   128  		if makeSymLinks {
   129  			if err := os.Symlink(filepath.Join(targetPath, fileName), filepath.Join(targetPath, fileName+"-link")); err != nil {
   130  				return 0, err
   131  			}
   132  		}
   133  	}
   134  	totalSize := numberOfFiles * len(fileData)
   135  	return totalSize, nil
   136  }
   137  
   138  func getHash(filename string) (uint32, error) {
   139  	stream, err := ioutil.ReadFile(filename)
   140  	if err != nil {
   141  		return 0, err
   142  	}
   143  	hash := crc32.NewIEEE()
   144  	hash.Write(stream)
   145  	return hash.Sum32(), nil
   146  }
   147  
   148  func compareDirectories(src string, dest string) error {
   149  	changes, err := archive.ChangesDirs(dest, &idtools.IDMappings{}, src, &idtools.IDMappings{})
   150  	if err != nil {
   151  		return err
   152  	}
   153  	if len(changes) > 0 {
   154  		return fmt.Errorf("Unexpected differences after untar: %v", changes)
   155  	}
   156  	return nil
   157  }
   158  
   159  func compareDirectoriesChown(src string, dest string, uid, gid int) error {
   160  	uidmap := []idtools.IDMap{{ContainerID: 0, HostID: uid, Size: 1}}
   161  	gidmap := []idtools.IDMap{{ContainerID: 0, HostID: gid, Size: 1}}
   162  	mappings := idtools.NewIDMappingsFromMaps(uidmap, gidmap)
   163  	changes, err := archive.ChangesDirs(dest, mappings, src, &idtools.IDMappings{})
   164  	if err != nil {
   165  		return err
   166  	}
   167  	if len(changes) > 0 {
   168  		return fmt.Errorf("Unexpected differences after untar: %v", changes)
   169  	}
   170  	return nil
   171  }
   172  
   173  func compareFiles(src string, dest string) error {
   174  	srcHash, err := getHash(src)
   175  	if err != nil {
   176  		return err
   177  	}
   178  	destHash, err := getHash(dest)
   179  	if err != nil {
   180  		return err
   181  	}
   182  	if srcHash != destHash {
   183  		return fmt.Errorf("%s is different from %s", src, dest)
   184  	}
   185  	return nil
   186  }
   187  
   188  func compareFilesChown(src string, dest string, uid, gid int) error {
   189  	if err := compareFiles(src, dest); err != nil {
   190  		return err
   191  	}
   192  	fi, err := os.Lstat(dest)
   193  	if err == nil {
   194  		statuid := fi.Sys().(*syscall.Stat_t).Uid
   195  		statgid := fi.Sys().(*syscall.Stat_t).Gid
   196  		if statuid != uint32(uid) || statgid != uint32(gid) {
   197  			return fmt.Errorf("%d:%d ownership on %s is different expected %d:%d", statuid, statgid, dest, uid, gid)
   198  		}
   199  	}
   200  	return err
   201  
   202  }
   203  
   204  func TestChrootTarUntarWithSymlink(t *testing.T) {
   205  	// TODO Windows: Figure out why this is failing
   206  	if runtime.GOOS == "windows" {
   207  		t.Skip("Failing on Windows")
   208  	}
   209  	tmpdir, err := ioutil.TempDir("", "storage-TestChrootTarUntarWithSymlink")
   210  	if err != nil {
   211  		t.Fatal(err)
   212  	}
   213  	defer os.RemoveAll(tmpdir)
   214  	src := filepath.Join(tmpdir, "src")
   215  	if err := system.MkdirAll(src, 0700, ""); err != nil {
   216  		t.Fatal(err)
   217  	}
   218  	if _, err := prepareSourceDirectory(10, src, false); err != nil {
   219  		t.Fatal(err)
   220  	}
   221  	dest := filepath.Join(tmpdir, "dest")
   222  	if err := TarUntar(src, dest); err != nil {
   223  		t.Fatal(err)
   224  	}
   225  	if err := compareDirectories(src, dest); err != nil {
   226  		t.Fatal(err)
   227  	}
   228  }
   229  
   230  func TestChrootCopyWithTar(t *testing.T) {
   231  	// TODO Windows: Figure out why this is failing
   232  	if runtime.GOOS == "windows" || runtime.GOOS == "solaris" {
   233  		t.Skip("Failing on Windows and Solaris")
   234  	}
   235  	tmpdir, err := ioutil.TempDir("", "storage-TestChrootCopyWithTar")
   236  	if err != nil {
   237  		t.Fatal(err)
   238  	}
   239  	defer os.RemoveAll(tmpdir)
   240  	src := filepath.Join(tmpdir, "src")
   241  	if err := system.MkdirAll(src, 0700, ""); err != nil {
   242  		t.Fatal(err)
   243  	}
   244  	if _, err := prepareSourceDirectory(10, src, true); err != nil {
   245  		t.Fatal(err)
   246  	}
   247  
   248  	// Copy directory
   249  	dest := filepath.Join(tmpdir, "dest")
   250  	if err := CopyWithTar(src, dest); err != nil {
   251  		t.Fatal(err)
   252  	}
   253  	if err := compareDirectories(src, dest); err != nil {
   254  		t.Fatal(err)
   255  	}
   256  
   257  	// Copy file
   258  	srcfile := filepath.Join(src, "file-1")
   259  	dest = filepath.Join(tmpdir, "destFile")
   260  	destfile := filepath.Join(dest, "file-1")
   261  	if err := CopyWithTar(srcfile, destfile); err != nil {
   262  		t.Fatal(err)
   263  	}
   264  	if err := compareFiles(srcfile, destfile); err != nil {
   265  		t.Fatal(err)
   266  	}
   267  
   268  	// Copy symbolic link
   269  	srcLinkfile := filepath.Join(src, "file-1-link")
   270  	dest = filepath.Join(tmpdir, "destSymlink")
   271  	destLinkfile := filepath.Join(dest, "file-1-link")
   272  	if err := CopyWithTar(srcLinkfile, destLinkfile); err != nil {
   273  		t.Fatal(err)
   274  	}
   275  	if err := compareFiles(srcLinkfile, destLinkfile); err != nil {
   276  		t.Fatal(err)
   277  	}
   278  }
   279  
   280  func TestChrootCopyWithTarAndChown(t *testing.T) {
   281  	// TODO Windows: Figure out why this is failing
   282  	if runtime.GOOS == "windows" || runtime.GOOS == "solaris" {
   283  		t.Skip("Failing on Windows and Solaris")
   284  	}
   285  	tmpdir, err := ioutil.TempDir("", "storage-TestChrootCopyWithTar")
   286  	if err != nil {
   287  		t.Fatal(err)
   288  	}
   289  	defer os.RemoveAll(tmpdir)
   290  	src := filepath.Join(tmpdir, "src")
   291  	if err := system.MkdirAll(src, 0700, ""); err != nil {
   292  		t.Fatal(err)
   293  	}
   294  	if _, err := prepareSourceDirectory(10, src, true); err != nil {
   295  		t.Fatal(err)
   296  	}
   297  	uid := 1000
   298  	gid := 1001
   299  	owner := idtools.IDPair{UID: uid, GID: gid}
   300  	idMap := idtools.IDMap{ContainerID: 0, HostID: 0, Size: 65536}
   301  	uidMap := []idtools.IDMap{idMap}
   302  	gidMap := []idtools.IDMap{idMap}
   303  	copyFunc := CopyWithTarAndChown(&owner, nil, uidMap, gidMap)
   304  	// Copy directory
   305  	dest := filepath.Join(tmpdir, "dest")
   306  	if err := copyFunc(src, dest); err != nil {
   307  		t.Fatal(err)
   308  	}
   309  	if err := compareDirectoriesChown(src, dest, uid, gid); err != nil {
   310  		t.Fatal(err)
   311  	}
   312  
   313  	// Copy file
   314  	srcfile := filepath.Join(src, "file-1")
   315  	dest = filepath.Join(tmpdir, "destFile")
   316  	destfile := filepath.Join(dest, "file-1")
   317  	if err := copyFunc(srcfile, destfile); err != nil {
   318  		t.Fatal(err)
   319  	}
   320  	if err := compareFilesChown(srcfile, destfile, uid, gid); err != nil {
   321  		t.Fatal(err)
   322  	}
   323  
   324  	// Copy symbolic link
   325  	srcLinkfile := filepath.Join(src, "file-1-link")
   326  	dest = filepath.Join(tmpdir, "destSymlink")
   327  	destLinkfile := filepath.Join(dest, "file-1-link")
   328  	if err := copyFunc(srcLinkfile, destLinkfile); err != nil {
   329  		t.Fatal(err)
   330  	}
   331  	if err := compareFilesChown(srcLinkfile, destLinkfile, uid, gid); err != nil {
   332  		t.Fatal(err)
   333  	}
   334  }
   335  
   336  func TestChrootCopyFileWithTar(t *testing.T) {
   337  	tmpdir, err := ioutil.TempDir("", "storage-TestChrootCopyFileWithTar")
   338  	if err != nil {
   339  		t.Fatal(err)
   340  	}
   341  	defer os.RemoveAll(tmpdir)
   342  	src := filepath.Join(tmpdir, "src")
   343  	if err := system.MkdirAll(src, 0700, ""); err != nil {
   344  		t.Fatal(err)
   345  	}
   346  	if _, err := prepareSourceDirectory(10, src, true); err != nil {
   347  		t.Fatal(err)
   348  	}
   349  
   350  	// Copy directory
   351  	dest := filepath.Join(tmpdir, "dest")
   352  	if err := CopyFileWithTar(src, dest); err == nil {
   353  		t.Fatal("Expected error on copying directory")
   354  	}
   355  
   356  	// Copy file
   357  	srcfile := filepath.Join(src, "file-1")
   358  	dest = filepath.Join(tmpdir, "destFile")
   359  	destfile := filepath.Join(dest, "file-1")
   360  	if err := CopyFileWithTar(srcfile, destfile); err != nil {
   361  		t.Fatal(err)
   362  	}
   363  	if err := compareFiles(srcfile, destfile); err != nil {
   364  		t.Fatal(err)
   365  	}
   366  
   367  	// Copy symbolic link
   368  	srcLinkfile := filepath.Join(src, "file-1-link")
   369  	dest = filepath.Join(tmpdir, "destSymlink")
   370  	destLinkfile := filepath.Join(dest, "file-1-link")
   371  	if err := CopyFileWithTar(srcLinkfile, destLinkfile); err != nil {
   372  		t.Fatal(err)
   373  	}
   374  	if err := compareFiles(srcLinkfile, destLinkfile); err != nil {
   375  		t.Fatal(err)
   376  	}
   377  }
   378  
   379  func TestChrootCopyFileWithTarAndChown(t *testing.T) {
   380  	tmpdir, err := ioutil.TempDir("", "storage-TestChrootCopyFileWithTar")
   381  	if err != nil {
   382  		t.Fatal(err)
   383  	}
   384  	defer os.RemoveAll(tmpdir)
   385  	src := filepath.Join(tmpdir, "src")
   386  	if err := system.MkdirAll(src, 0700, ""); err != nil {
   387  		t.Fatal(err)
   388  	}
   389  	if _, err := prepareSourceDirectory(10, src, true); err != nil {
   390  		t.Fatal(err)
   391  	}
   392  
   393  	uid := 1000
   394  	gid := 1001
   395  	owner := idtools.IDPair{UID: uid, GID: gid}
   396  	idMap := idtools.IDMap{ContainerID: 0, HostID: 0, Size: 65536}
   397  	uidMap := []idtools.IDMap{idMap}
   398  	gidMap := []idtools.IDMap{idMap}
   399  	copyFunc := CopyFileWithTarAndChown(&owner, nil, uidMap, gidMap)
   400  	// Copy directory
   401  	dest := filepath.Join(tmpdir, "dest")
   402  	if err := copyFunc(src, dest); err == nil {
   403  		t.Fatal("Expected error on copying directory")
   404  	}
   405  
   406  	// Copy file
   407  	srcfile := filepath.Join(src, "file-1")
   408  	dest = filepath.Join(tmpdir, "destFile")
   409  	destfile := filepath.Join(dest, "file-1")
   410  	if err := copyFunc(srcfile, destfile); err != nil {
   411  		t.Fatal(err)
   412  	}
   413  	if err := compareFilesChown(srcfile, destfile, uid, gid); err != nil {
   414  		t.Fatal(err)
   415  	}
   416  
   417  	// Copy symbolic link
   418  	srcLinkfile := filepath.Join(src, "file-1-link")
   419  	dest = filepath.Join(tmpdir, "destSymlink")
   420  	destLinkfile := filepath.Join(dest, "file-1-link")
   421  	if err := copyFunc(srcLinkfile, destLinkfile); err != nil {
   422  		t.Fatal(err)
   423  	}
   424  	if err := compareFilesChown(srcLinkfile, destLinkfile, uid, gid); err != nil {
   425  		t.Fatal(err)
   426  	}
   427  }
   428  
   429  func TestChrootUntarPath(t *testing.T) {
   430  	// TODO Windows: Figure out why this is failing
   431  	if runtime.GOOS == "windows" {
   432  		t.Skip("Failing on Windows")
   433  	}
   434  	tmpdir, err := ioutil.TempDir("", "storage-TestChrootUntarPath")
   435  	if err != nil {
   436  		t.Fatal(err)
   437  	}
   438  	defer os.RemoveAll(tmpdir)
   439  	src := filepath.Join(tmpdir, "src")
   440  	if err := system.MkdirAll(src, 0700, ""); err != nil {
   441  		t.Fatal(err)
   442  	}
   443  	if _, err := prepareSourceDirectory(10, src, false); err != nil {
   444  		t.Fatal(err)
   445  	}
   446  	dest := filepath.Join(tmpdir, "dest")
   447  	// Untar a directory
   448  	if err := UntarPath(src, dest); err == nil {
   449  		t.Fatal("Expected error on untaring a directory")
   450  	}
   451  
   452  	tarFunc := archive.TarPath(nil, nil)
   453  	// Untar a tar file
   454  	stream, err := tarFunc(src)
   455  	if err != nil {
   456  		t.Fatal(err)
   457  	}
   458  	buf := new(bytes.Buffer)
   459  	buf.ReadFrom(stream)
   460  	tarfile := filepath.Join(tmpdir, "src.tar")
   461  	if err := ioutil.WriteFile(tarfile, buf.Bytes(), 0644); err != nil {
   462  		t.Fatal(err)
   463  	}
   464  	if err := UntarPath(tarfile, dest); err != nil {
   465  		t.Fatal(err)
   466  	}
   467  	if err := compareDirectories(src, dest); err != nil {
   468  		t.Fatal(err)
   469  	}
   470  }
   471  
   472  func TestChrootUntarPathAndChown(t *testing.T) {
   473  	// TODO Windows: Figure out why this is failing
   474  	if runtime.GOOS == "windows" {
   475  		t.Skip("Failing on Windows")
   476  	}
   477  	tmpdir, err := ioutil.TempDir("", "storage-TestChrootUntarPath")
   478  	if err != nil {
   479  		t.Fatal(err)
   480  	}
   481  	defer os.RemoveAll(tmpdir)
   482  	src := filepath.Join(tmpdir, "src")
   483  	if err := system.MkdirAll(src, 0700, ""); err != nil {
   484  		t.Fatal(err)
   485  	}
   486  	if _, err := prepareSourceDirectory(10, src, false); err != nil {
   487  		t.Fatal(err)
   488  	}
   489  	dest := filepath.Join(tmpdir, "dest")
   490  
   491  	uid := 1000
   492  	gid := 1001
   493  	owner := idtools.IDPair{UID: uid, GID: gid}
   494  	idMap := idtools.IDMap{ContainerID: 0, HostID: 0, Size: 65536}
   495  	uidMap := []idtools.IDMap{idMap}
   496  	gidMap := []idtools.IDMap{idMap}
   497  	untarFunc := UntarPathAndChown(&owner, nil, uidMap, gidMap)
   498  	// Untar a directory
   499  	if err := untarFunc(src, dest); err == nil {
   500  		t.Fatal("Expected error on untaring a directory")
   501  	}
   502  
   503  	// Untar a tar file
   504  	tarFunc := archive.TarPath(nil, nil)
   505  	// Untar a tar file
   506  	stream, err := tarFunc(src)
   507  	if err != nil {
   508  		t.Fatal(err)
   509  	}
   510  
   511  	buf := new(bytes.Buffer)
   512  	buf.ReadFrom(stream)
   513  	tarfile := filepath.Join(tmpdir, "src.tar")
   514  	if err := ioutil.WriteFile(tarfile, buf.Bytes(), 0644); err != nil {
   515  		t.Fatal(err)
   516  	}
   517  	if err := untarFunc(tarfile, dest); err != nil {
   518  		t.Fatal(err)
   519  	}
   520  	if err := compareDirectoriesChown(src, dest, uid, gid); err != nil {
   521  		t.Fatal(err)
   522  	}
   523  }
   524  
   525  type slowEmptyTarReader struct {
   526  	size      int
   527  	offset    int
   528  	chunkSize int
   529  }
   530  
   531  // Read is a slow reader of an empty tar (like the output of "tar c --files-from /dev/null")
   532  func (s *slowEmptyTarReader) Read(p []byte) (int, error) {
   533  	time.Sleep(100 * time.Millisecond)
   534  	count := s.chunkSize
   535  	if len(p) < s.chunkSize {
   536  		count = len(p)
   537  	}
   538  	for i := 0; i < count; i++ {
   539  		p[i] = 0
   540  	}
   541  	s.offset += count
   542  	if s.offset > s.size {
   543  		return count, io.EOF
   544  	}
   545  	return count, nil
   546  }
   547  
   548  func TestChrootUntarEmptyArchiveFromSlowReader(t *testing.T) {
   549  	tmpdir, err := ioutil.TempDir("", "storage-TestChrootUntarEmptyArchiveFromSlowReader")
   550  	if err != nil {
   551  		t.Fatal(err)
   552  	}
   553  	defer os.RemoveAll(tmpdir)
   554  	dest := filepath.Join(tmpdir, "dest")
   555  	if err := system.MkdirAll(dest, 0700, ""); err != nil {
   556  		t.Fatal(err)
   557  	}
   558  	stream := &slowEmptyTarReader{size: 10240, chunkSize: 1024}
   559  	if err := Untar(stream, dest, nil); err != nil {
   560  		t.Fatal(err)
   561  	}
   562  }
   563  
   564  func TestChrootApplyEmptyArchiveFromSlowReader(t *testing.T) {
   565  	tmpdir, err := ioutil.TempDir("", "storage-TestChrootApplyEmptyArchiveFromSlowReader")
   566  	if err != nil {
   567  		t.Fatal(err)
   568  	}
   569  	defer os.RemoveAll(tmpdir)
   570  	dest := filepath.Join(tmpdir, "dest")
   571  	if err := system.MkdirAll(dest, 0700, ""); err != nil {
   572  		t.Fatal(err)
   573  	}
   574  	stream := &slowEmptyTarReader{size: 10240, chunkSize: 1024}
   575  	if _, err := ApplyLayer(dest, stream); err != nil {
   576  		t.Fatal(err)
   577  	}
   578  }
   579  
   580  func TestChrootApplyDotDotFile(t *testing.T) {
   581  	tmpdir, err := ioutil.TempDir("", "storage-TestChrootApplyDotDotFile")
   582  	if err != nil {
   583  		t.Fatal(err)
   584  	}
   585  	defer os.RemoveAll(tmpdir)
   586  	src := filepath.Join(tmpdir, "src")
   587  	if err := system.MkdirAll(src, 0700, ""); err != nil {
   588  		t.Fatal(err)
   589  	}
   590  	if err := ioutil.WriteFile(filepath.Join(src, "..gitme"), []byte(""), 0644); err != nil {
   591  		t.Fatal(err)
   592  	}
   593  	stream, err := archive.Tar(src, archive.Uncompressed)
   594  	if err != nil {
   595  		t.Fatal(err)
   596  	}
   597  	dest := filepath.Join(tmpdir, "dest")
   598  	if err := system.MkdirAll(dest, 0700, ""); err != nil {
   599  		t.Fatal(err)
   600  	}
   601  	if _, err := ApplyLayer(dest, stream); err != nil {
   602  		t.Fatal(err)
   603  	}
   604  }