github.com/vvnotw/moby@v1.13.1/pkg/archive/diff_test.go (about)

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