github.com/olljanat/moby@v1.13.1/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  	"testing"
    14  	"time"
    15  
    16  	"github.com/docker/docker/pkg/archive"
    17  	"github.com/docker/docker/pkg/reexec"
    18  	"github.com/docker/docker/pkg/system"
    19  )
    20  
    21  func init() {
    22  	reexec.Init()
    23  }
    24  
    25  func TestChrootTarUntar(t *testing.T) {
    26  	tmpdir, err := ioutil.TempDir("", "docker-TestChrootTarUntar")
    27  	if err != nil {
    28  		t.Fatal(err)
    29  	}
    30  	defer os.RemoveAll(tmpdir)
    31  	src := filepath.Join(tmpdir, "src")
    32  	if err := system.MkdirAll(src, 0700); err != nil {
    33  		t.Fatal(err)
    34  	}
    35  	if err := ioutil.WriteFile(filepath.Join(src, "toto"), []byte("hello toto"), 0644); err != nil {
    36  		t.Fatal(err)
    37  	}
    38  	if err := ioutil.WriteFile(filepath.Join(src, "lolo"), []byte("hello lolo"), 0644); err != nil {
    39  		t.Fatal(err)
    40  	}
    41  	stream, err := archive.Tar(src, archive.Uncompressed)
    42  	if err != nil {
    43  		t.Fatal(err)
    44  	}
    45  	dest := filepath.Join(tmpdir, "src")
    46  	if err := system.MkdirAll(dest, 0700); err != nil {
    47  		t.Fatal(err)
    48  	}
    49  	if err := Untar(stream, dest, &archive.TarOptions{ExcludePatterns: []string{"lolo"}}); err != nil {
    50  		t.Fatal(err)
    51  	}
    52  }
    53  
    54  // gh#10426: Verify the fix for having a huge excludes list (like on `docker load` with large # of
    55  // local images)
    56  func TestChrootUntarWithHugeExcludesList(t *testing.T) {
    57  	tmpdir, err := ioutil.TempDir("", "docker-TestChrootUntarHugeExcludes")
    58  	if err != nil {
    59  		t.Fatal(err)
    60  	}
    61  	defer os.RemoveAll(tmpdir)
    62  	src := filepath.Join(tmpdir, "src")
    63  	if err := system.MkdirAll(src, 0700); err != nil {
    64  		t.Fatal(err)
    65  	}
    66  	if err := ioutil.WriteFile(filepath.Join(src, "toto"), []byte("hello toto"), 0644); err != nil {
    67  		t.Fatal(err)
    68  	}
    69  	stream, err := archive.Tar(src, archive.Uncompressed)
    70  	if err != nil {
    71  		t.Fatal(err)
    72  	}
    73  	dest := filepath.Join(tmpdir, "dest")
    74  	if err := system.MkdirAll(dest, 0700); err != nil {
    75  		t.Fatal(err)
    76  	}
    77  	options := &archive.TarOptions{}
    78  	//65534 entries of 64-byte strings ~= 4MB of environment space which should overflow
    79  	//on most systems when passed via environment or command line arguments
    80  	excludes := make([]string, 65534, 65534)
    81  	for i := 0; i < 65534; i++ {
    82  		excludes[i] = strings.Repeat(string(i), 64)
    83  	}
    84  	options.ExcludePatterns = excludes
    85  	if err := Untar(stream, dest, options); err != nil {
    86  		t.Fatal(err)
    87  	}
    88  }
    89  
    90  func TestChrootUntarEmptyArchive(t *testing.T) {
    91  	tmpdir, err := ioutil.TempDir("", "docker-TestChrootUntarEmptyArchive")
    92  	if err != nil {
    93  		t.Fatal(err)
    94  	}
    95  	defer os.RemoveAll(tmpdir)
    96  	if err := Untar(nil, tmpdir, nil); err == nil {
    97  		t.Fatal("expected error on empty archive")
    98  	}
    99  }
   100  
   101  func prepareSourceDirectory(numberOfFiles int, targetPath string, makeSymLinks bool) (int, error) {
   102  	fileData := []byte("fooo")
   103  	for n := 0; n < numberOfFiles; n++ {
   104  		fileName := fmt.Sprintf("file-%d", n)
   105  		if err := ioutil.WriteFile(filepath.Join(targetPath, fileName), fileData, 0700); err != nil {
   106  			return 0, err
   107  		}
   108  		if makeSymLinks {
   109  			if err := os.Symlink(filepath.Join(targetPath, fileName), filepath.Join(targetPath, fileName+"-link")); err != nil {
   110  				return 0, err
   111  			}
   112  		}
   113  	}
   114  	totalSize := numberOfFiles * len(fileData)
   115  	return totalSize, nil
   116  }
   117  
   118  func getHash(filename string) (uint32, error) {
   119  	stream, err := ioutil.ReadFile(filename)
   120  	if err != nil {
   121  		return 0, err
   122  	}
   123  	hash := crc32.NewIEEE()
   124  	hash.Write(stream)
   125  	return hash.Sum32(), nil
   126  }
   127  
   128  func compareDirectories(src string, dest string) error {
   129  	changes, err := archive.ChangesDirs(dest, src)
   130  	if err != nil {
   131  		return err
   132  	}
   133  	if len(changes) > 0 {
   134  		return fmt.Errorf("Unexpected differences after untar: %v", changes)
   135  	}
   136  	return nil
   137  }
   138  
   139  func compareFiles(src string, dest string) error {
   140  	srcHash, err := getHash(src)
   141  	if err != nil {
   142  		return err
   143  	}
   144  	destHash, err := getHash(dest)
   145  	if err != nil {
   146  		return err
   147  	}
   148  	if srcHash != destHash {
   149  		return fmt.Errorf("%s is different from %s", src, dest)
   150  	}
   151  	return nil
   152  }
   153  
   154  func TestChrootTarUntarWithSymlink(t *testing.T) {
   155  	// TODO Windows: Figure out why this is failing
   156  	if runtime.GOOS == "windows" {
   157  		t.Skip("Failing on Windows")
   158  	}
   159  	tmpdir, err := ioutil.TempDir("", "docker-TestChrootTarUntarWithSymlink")
   160  	if err != nil {
   161  		t.Fatal(err)
   162  	}
   163  	defer os.RemoveAll(tmpdir)
   164  	src := filepath.Join(tmpdir, "src")
   165  	if err := system.MkdirAll(src, 0700); err != nil {
   166  		t.Fatal(err)
   167  	}
   168  	if _, err := prepareSourceDirectory(10, src, false); err != nil {
   169  		t.Fatal(err)
   170  	}
   171  	dest := filepath.Join(tmpdir, "dest")
   172  	if err := TarUntar(src, dest); err != nil {
   173  		t.Fatal(err)
   174  	}
   175  	if err := compareDirectories(src, dest); err != nil {
   176  		t.Fatal(err)
   177  	}
   178  }
   179  
   180  func TestChrootCopyWithTar(t *testing.T) {
   181  	// TODO Windows: Figure out why this is failing
   182  	if runtime.GOOS == "windows" || runtime.GOOS == "solaris" {
   183  		t.Skip("Failing on Windows and Solaris")
   184  	}
   185  	tmpdir, err := ioutil.TempDir("", "docker-TestChrootCopyWithTar")
   186  	if err != nil {
   187  		t.Fatal(err)
   188  	}
   189  	defer os.RemoveAll(tmpdir)
   190  	src := filepath.Join(tmpdir, "src")
   191  	if err := system.MkdirAll(src, 0700); err != nil {
   192  		t.Fatal(err)
   193  	}
   194  	if _, err := prepareSourceDirectory(10, src, true); err != nil {
   195  		t.Fatal(err)
   196  	}
   197  
   198  	// Copy directory
   199  	dest := filepath.Join(tmpdir, "dest")
   200  	if err := CopyWithTar(src, dest); err != nil {
   201  		t.Fatal(err)
   202  	}
   203  	if err := compareDirectories(src, dest); err != nil {
   204  		t.Fatal(err)
   205  	}
   206  
   207  	// Copy file
   208  	srcfile := filepath.Join(src, "file-1")
   209  	dest = filepath.Join(tmpdir, "destFile")
   210  	destfile := filepath.Join(dest, "file-1")
   211  	if err := CopyWithTar(srcfile, destfile); err != nil {
   212  		t.Fatal(err)
   213  	}
   214  	if err := compareFiles(srcfile, destfile); err != nil {
   215  		t.Fatal(err)
   216  	}
   217  
   218  	// Copy symbolic link
   219  	srcLinkfile := filepath.Join(src, "file-1-link")
   220  	dest = filepath.Join(tmpdir, "destSymlink")
   221  	destLinkfile := filepath.Join(dest, "file-1-link")
   222  	if err := CopyWithTar(srcLinkfile, destLinkfile); err != nil {
   223  		t.Fatal(err)
   224  	}
   225  	if err := compareFiles(srcLinkfile, destLinkfile); err != nil {
   226  		t.Fatal(err)
   227  	}
   228  }
   229  
   230  func TestChrootCopyFileWithTar(t *testing.T) {
   231  	tmpdir, err := ioutil.TempDir("", "docker-TestChrootCopyFileWithTar")
   232  	if err != nil {
   233  		t.Fatal(err)
   234  	}
   235  	defer os.RemoveAll(tmpdir)
   236  	src := filepath.Join(tmpdir, "src")
   237  	if err := system.MkdirAll(src, 0700); err != nil {
   238  		t.Fatal(err)
   239  	}
   240  	if _, err := prepareSourceDirectory(10, src, true); err != nil {
   241  		t.Fatal(err)
   242  	}
   243  
   244  	// Copy directory
   245  	dest := filepath.Join(tmpdir, "dest")
   246  	if err := CopyFileWithTar(src, dest); err == nil {
   247  		t.Fatal("Expected error on copying directory")
   248  	}
   249  
   250  	// Copy file
   251  	srcfile := filepath.Join(src, "file-1")
   252  	dest = filepath.Join(tmpdir, "destFile")
   253  	destfile := filepath.Join(dest, "file-1")
   254  	if err := CopyFileWithTar(srcfile, destfile); err != nil {
   255  		t.Fatal(err)
   256  	}
   257  	if err := compareFiles(srcfile, destfile); err != nil {
   258  		t.Fatal(err)
   259  	}
   260  
   261  	// Copy symbolic link
   262  	srcLinkfile := filepath.Join(src, "file-1-link")
   263  	dest = filepath.Join(tmpdir, "destSymlink")
   264  	destLinkfile := filepath.Join(dest, "file-1-link")
   265  	if err := CopyFileWithTar(srcLinkfile, destLinkfile); err != nil {
   266  		t.Fatal(err)
   267  	}
   268  	if err := compareFiles(srcLinkfile, destLinkfile); err != nil {
   269  		t.Fatal(err)
   270  	}
   271  }
   272  
   273  func TestChrootUntarPath(t *testing.T) {
   274  	// TODO Windows: Figure out why this is failing
   275  	if runtime.GOOS == "windows" {
   276  		t.Skip("Failing on Windows")
   277  	}
   278  	tmpdir, err := ioutil.TempDir("", "docker-TestChrootUntarPath")
   279  	if err != nil {
   280  		t.Fatal(err)
   281  	}
   282  	defer os.RemoveAll(tmpdir)
   283  	src := filepath.Join(tmpdir, "src")
   284  	if err := system.MkdirAll(src, 0700); err != nil {
   285  		t.Fatal(err)
   286  	}
   287  	if _, err := prepareSourceDirectory(10, src, false); err != nil {
   288  		t.Fatal(err)
   289  	}
   290  	dest := filepath.Join(tmpdir, "dest")
   291  	// Untar a directory
   292  	if err := UntarPath(src, dest); err == nil {
   293  		t.Fatal("Expected error on untaring a directory")
   294  	}
   295  
   296  	// Untar a tar file
   297  	stream, err := archive.Tar(src, archive.Uncompressed)
   298  	if err != nil {
   299  		t.Fatal(err)
   300  	}
   301  	buf := new(bytes.Buffer)
   302  	buf.ReadFrom(stream)
   303  	tarfile := filepath.Join(tmpdir, "src.tar")
   304  	if err := ioutil.WriteFile(tarfile, buf.Bytes(), 0644); err != nil {
   305  		t.Fatal(err)
   306  	}
   307  	if err := UntarPath(tarfile, dest); err != nil {
   308  		t.Fatal(err)
   309  	}
   310  	if err := compareDirectories(src, dest); err != nil {
   311  		t.Fatal(err)
   312  	}
   313  }
   314  
   315  type slowEmptyTarReader struct {
   316  	size      int
   317  	offset    int
   318  	chunkSize int
   319  }
   320  
   321  // Read is a slow reader of an empty tar (like the output of "tar c --files-from /dev/null")
   322  func (s *slowEmptyTarReader) Read(p []byte) (int, error) {
   323  	time.Sleep(100 * time.Millisecond)
   324  	count := s.chunkSize
   325  	if len(p) < s.chunkSize {
   326  		count = len(p)
   327  	}
   328  	for i := 0; i < count; i++ {
   329  		p[i] = 0
   330  	}
   331  	s.offset += count
   332  	if s.offset > s.size {
   333  		return count, io.EOF
   334  	}
   335  	return count, nil
   336  }
   337  
   338  func TestChrootUntarEmptyArchiveFromSlowReader(t *testing.T) {
   339  	tmpdir, err := ioutil.TempDir("", "docker-TestChrootUntarEmptyArchiveFromSlowReader")
   340  	if err != nil {
   341  		t.Fatal(err)
   342  	}
   343  	defer os.RemoveAll(tmpdir)
   344  	dest := filepath.Join(tmpdir, "dest")
   345  	if err := system.MkdirAll(dest, 0700); err != nil {
   346  		t.Fatal(err)
   347  	}
   348  	stream := &slowEmptyTarReader{size: 10240, chunkSize: 1024}
   349  	if err := Untar(stream, dest, nil); err != nil {
   350  		t.Fatal(err)
   351  	}
   352  }
   353  
   354  func TestChrootApplyEmptyArchiveFromSlowReader(t *testing.T) {
   355  	tmpdir, err := ioutil.TempDir("", "docker-TestChrootApplyEmptyArchiveFromSlowReader")
   356  	if err != nil {
   357  		t.Fatal(err)
   358  	}
   359  	defer os.RemoveAll(tmpdir)
   360  	dest := filepath.Join(tmpdir, "dest")
   361  	if err := system.MkdirAll(dest, 0700); err != nil {
   362  		t.Fatal(err)
   363  	}
   364  	stream := &slowEmptyTarReader{size: 10240, chunkSize: 1024}
   365  	if _, err := ApplyLayer(dest, stream); err != nil {
   366  		t.Fatal(err)
   367  	}
   368  }
   369  
   370  func TestChrootApplyDotDotFile(t *testing.T) {
   371  	tmpdir, err := ioutil.TempDir("", "docker-TestChrootApplyDotDotFile")
   372  	if err != nil {
   373  		t.Fatal(err)
   374  	}
   375  	defer os.RemoveAll(tmpdir)
   376  	src := filepath.Join(tmpdir, "src")
   377  	if err := system.MkdirAll(src, 0700); err != nil {
   378  		t.Fatal(err)
   379  	}
   380  	if err := ioutil.WriteFile(filepath.Join(src, "..gitme"), []byte(""), 0644); err != nil {
   381  		t.Fatal(err)
   382  	}
   383  	stream, err := archive.Tar(src, archive.Uncompressed)
   384  	if err != nil {
   385  		t.Fatal(err)
   386  	}
   387  	dest := filepath.Join(tmpdir, "dest")
   388  	if err := system.MkdirAll(dest, 0700); err != nil {
   389  		t.Fatal(err)
   390  	}
   391  	if _, err := ApplyLayer(dest, stream); err != nil {
   392  		t.Fatal(err)
   393  	}
   394  }