github.com/rumpl/bof@v23.0.0-rc.2+incompatible/pkg/chrootarchive/archive_test.go (about)

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