github.com/demonoid81/moby@v0.0.0-20200517203328-62dd8e17c460/pkg/chrootarchive/archive_test.go (about)

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