github.com/docker/docker@v299999999.0.0-20200612211812-aaf470eca7b5+incompatible/pkg/archive/diff_test.go (about)

     1  package archive // import "github.com/docker/docker/pkg/archive"
     2  
     3  import (
     4  	"archive/tar"
     5  	"io"
     6  	"io/ioutil"
     7  	"os"
     8  	"path/filepath"
     9  	"reflect"
    10  	"testing"
    11  
    12  	"github.com/docker/docker/pkg/ioutils"
    13  )
    14  
    15  func TestApplyLayerInvalidFilenames(t *testing.T) {
    16  	for i, headers := range [][]*tar.Header{
    17  		{
    18  			{
    19  				Name:     "../victim/dotdot",
    20  				Typeflag: tar.TypeReg,
    21  				Mode:     0644,
    22  			},
    23  		},
    24  		{
    25  			{
    26  				// Note the leading slash
    27  				Name:     "/../victim/slash-dotdot",
    28  				Typeflag: tar.TypeReg,
    29  				Mode:     0644,
    30  			},
    31  		},
    32  	} {
    33  		if err := testBreakout("applylayer", "docker-TestApplyLayerInvalidFilenames", headers); err != nil {
    34  			t.Fatalf("i=%d. %v", i, err)
    35  		}
    36  	}
    37  }
    38  
    39  func TestApplyLayerInvalidHardlink(t *testing.T) {
    40  	for i, headers := range [][]*tar.Header{
    41  		{ // try reading victim/hello (../)
    42  			{
    43  				Name:     "dotdot",
    44  				Typeflag: tar.TypeLink,
    45  				Linkname: "../victim/hello",
    46  				Mode:     0644,
    47  			},
    48  		},
    49  		{ // try reading victim/hello (/../)
    50  			{
    51  				Name:     "slash-dotdot",
    52  				Typeflag: tar.TypeLink,
    53  				// Note the leading slash
    54  				Linkname: "/../victim/hello",
    55  				Mode:     0644,
    56  			},
    57  		},
    58  		{ // try writing victim/file
    59  			{
    60  				Name:     "loophole-victim",
    61  				Typeflag: tar.TypeLink,
    62  				Linkname: "../victim",
    63  				Mode:     0755,
    64  			},
    65  			{
    66  				Name:     "loophole-victim/file",
    67  				Typeflag: tar.TypeReg,
    68  				Mode:     0644,
    69  			},
    70  		},
    71  		{ // try reading victim/hello (hardlink, symlink)
    72  			{
    73  				Name:     "loophole-victim",
    74  				Typeflag: tar.TypeLink,
    75  				Linkname: "../victim",
    76  				Mode:     0755,
    77  			},
    78  			{
    79  				Name:     "symlink",
    80  				Typeflag: tar.TypeSymlink,
    81  				Linkname: "loophole-victim/hello",
    82  				Mode:     0644,
    83  			},
    84  		},
    85  		{ // Try reading victim/hello (hardlink, hardlink)
    86  			{
    87  				Name:     "loophole-victim",
    88  				Typeflag: tar.TypeLink,
    89  				Linkname: "../victim",
    90  				Mode:     0755,
    91  			},
    92  			{
    93  				Name:     "hardlink",
    94  				Typeflag: tar.TypeLink,
    95  				Linkname: "loophole-victim/hello",
    96  				Mode:     0644,
    97  			},
    98  		},
    99  		{ // Try removing victim directory (hardlink)
   100  			{
   101  				Name:     "loophole-victim",
   102  				Typeflag: tar.TypeLink,
   103  				Linkname: "../victim",
   104  				Mode:     0755,
   105  			},
   106  			{
   107  				Name:     "loophole-victim",
   108  				Typeflag: tar.TypeReg,
   109  				Mode:     0644,
   110  			},
   111  		},
   112  	} {
   113  		if err := testBreakout("applylayer", "docker-TestApplyLayerInvalidHardlink", headers); err != nil {
   114  			t.Fatalf("i=%d. %v", i, err)
   115  		}
   116  	}
   117  }
   118  
   119  func TestApplyLayerInvalidSymlink(t *testing.T) {
   120  	for i, headers := range [][]*tar.Header{
   121  		{ // try reading victim/hello (../)
   122  			{
   123  				Name:     "dotdot",
   124  				Typeflag: tar.TypeSymlink,
   125  				Linkname: "../victim/hello",
   126  				Mode:     0644,
   127  			},
   128  		},
   129  		{ // try reading victim/hello (/../)
   130  			{
   131  				Name:     "slash-dotdot",
   132  				Typeflag: tar.TypeSymlink,
   133  				// Note the leading slash
   134  				Linkname: "/../victim/hello",
   135  				Mode:     0644,
   136  			},
   137  		},
   138  		{ // try writing victim/file
   139  			{
   140  				Name:     "loophole-victim",
   141  				Typeflag: tar.TypeSymlink,
   142  				Linkname: "../victim",
   143  				Mode:     0755,
   144  			},
   145  			{
   146  				Name:     "loophole-victim/file",
   147  				Typeflag: tar.TypeReg,
   148  				Mode:     0644,
   149  			},
   150  		},
   151  		{ // try reading victim/hello (symlink, symlink)
   152  			{
   153  				Name:     "loophole-victim",
   154  				Typeflag: tar.TypeSymlink,
   155  				Linkname: "../victim",
   156  				Mode:     0755,
   157  			},
   158  			{
   159  				Name:     "symlink",
   160  				Typeflag: tar.TypeSymlink,
   161  				Linkname: "loophole-victim/hello",
   162  				Mode:     0644,
   163  			},
   164  		},
   165  		{ // try reading victim/hello (symlink, hardlink)
   166  			{
   167  				Name:     "loophole-victim",
   168  				Typeflag: tar.TypeSymlink,
   169  				Linkname: "../victim",
   170  				Mode:     0755,
   171  			},
   172  			{
   173  				Name:     "hardlink",
   174  				Typeflag: tar.TypeLink,
   175  				Linkname: "loophole-victim/hello",
   176  				Mode:     0644,
   177  			},
   178  		},
   179  		{ // try removing victim directory (symlink)
   180  			{
   181  				Name:     "loophole-victim",
   182  				Typeflag: tar.TypeSymlink,
   183  				Linkname: "../victim",
   184  				Mode:     0755,
   185  			},
   186  			{
   187  				Name:     "loophole-victim",
   188  				Typeflag: tar.TypeReg,
   189  				Mode:     0644,
   190  			},
   191  		},
   192  	} {
   193  		if err := testBreakout("applylayer", "docker-TestApplyLayerInvalidSymlink", headers); err != nil {
   194  			t.Fatalf("i=%d. %v", i, err)
   195  		}
   196  	}
   197  }
   198  
   199  func TestApplyLayerWhiteouts(t *testing.T) {
   200  	wd, err := ioutil.TempDir("", "graphdriver-test-whiteouts")
   201  	if err != nil {
   202  		return
   203  	}
   204  	defer os.RemoveAll(wd)
   205  
   206  	base := []string{
   207  		".baz",
   208  		"bar/",
   209  		"bar/bax",
   210  		"bar/bay/",
   211  		"baz",
   212  		"foo/",
   213  		"foo/.abc",
   214  		"foo/.bcd/",
   215  		"foo/.bcd/a",
   216  		"foo/cde/",
   217  		"foo/cde/def",
   218  		"foo/cde/efg",
   219  		"foo/fgh",
   220  		"foobar",
   221  	}
   222  
   223  	type tcase struct {
   224  		change, expected []string
   225  	}
   226  
   227  	tcases := []tcase{
   228  		{
   229  			base,
   230  			base,
   231  		},
   232  		{
   233  			[]string{
   234  				".bay",
   235  				".wh.baz",
   236  				"foo/",
   237  				"foo/.bce",
   238  				"foo/.wh..wh..opq",
   239  				"foo/cde/",
   240  				"foo/cde/efg",
   241  			},
   242  			[]string{
   243  				".bay",
   244  				".baz",
   245  				"bar/",
   246  				"bar/bax",
   247  				"bar/bay/",
   248  				"foo/",
   249  				"foo/.bce",
   250  				"foo/cde/",
   251  				"foo/cde/efg",
   252  				"foobar",
   253  			},
   254  		},
   255  		{
   256  			[]string{
   257  				".bay",
   258  				".wh..baz",
   259  				".wh.foobar",
   260  				"foo/",
   261  				"foo/.abc",
   262  				"foo/.wh.cde",
   263  				"bar/",
   264  			},
   265  			[]string{
   266  				".bay",
   267  				"bar/",
   268  				"bar/bax",
   269  				"bar/bay/",
   270  				"foo/",
   271  				"foo/.abc",
   272  				"foo/.bce",
   273  			},
   274  		},
   275  		{
   276  			[]string{
   277  				".abc",
   278  				".wh..wh..opq",
   279  				"foobar",
   280  			},
   281  			[]string{
   282  				".abc",
   283  				"foobar",
   284  			},
   285  		},
   286  	}
   287  
   288  	for i, tc := range tcases {
   289  		l, err := makeTestLayer(tc.change)
   290  		if err != nil {
   291  			t.Fatal(err)
   292  		}
   293  
   294  		_, err = UnpackLayer(wd, l, nil)
   295  		if err != nil {
   296  			t.Fatal(err)
   297  		}
   298  		err = l.Close()
   299  		if err != nil {
   300  			t.Fatal(err)
   301  		}
   302  
   303  		paths, err := readDirContents(wd)
   304  		if err != nil {
   305  			t.Fatal(err)
   306  		}
   307  
   308  		if !reflect.DeepEqual(tc.expected, paths) {
   309  			t.Fatalf("invalid files for layer %d: expected %q, got %q", i, tc.expected, paths)
   310  		}
   311  	}
   312  
   313  }
   314  
   315  func makeTestLayer(paths []string) (rc io.ReadCloser, err error) {
   316  	tmpDir, err := ioutil.TempDir("", "graphdriver-test-mklayer")
   317  	if err != nil {
   318  		return
   319  	}
   320  	defer func() {
   321  		if err != nil {
   322  			os.RemoveAll(tmpDir)
   323  		}
   324  	}()
   325  	for _, p := range paths {
   326  		// Source files are always in Unix format. But we use filepath on
   327  		// creation to be platform agnostic.
   328  		if p[len(p)-1] == '/' {
   329  			if err = os.MkdirAll(filepath.Join(tmpDir, p), 0700); err != nil {
   330  				return
   331  			}
   332  		} else {
   333  			if err = ioutil.WriteFile(filepath.Join(tmpDir, p), nil, 0600); err != nil {
   334  				return
   335  			}
   336  		}
   337  	}
   338  	archive, err := Tar(tmpDir, Uncompressed)
   339  	if err != nil {
   340  		return
   341  	}
   342  	return ioutils.NewReadCloserWrapper(archive, func() error {
   343  		err := archive.Close()
   344  		os.RemoveAll(tmpDir)
   345  		return err
   346  	}), nil
   347  }
   348  
   349  func readDirContents(root string) ([]string, error) {
   350  	var files []string
   351  	err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
   352  		if err != nil {
   353  			return err
   354  		}
   355  		if path == root {
   356  			return nil
   357  		}
   358  		rel, err := filepath.Rel(root, path)
   359  		if err != nil {
   360  			return err
   361  		}
   362  		if info.IsDir() {
   363  			rel = rel + string(filepath.Separator)
   364  		}
   365  		// Append in Unix semantics
   366  		files = append(files, filepath.ToSlash(rel))
   367  		return nil
   368  	})
   369  	if err != nil {
   370  		return nil, err
   371  	}
   372  	return files, nil
   373  }