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