github.com/blixtra/rkt@v0.8.1-0.20160204105720-ab0d1add1a43/pkg/tar/tar_test.go (about)

     1  // Copyright 2014 The rkt Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package tar
    16  
    17  import (
    18  	"archive/tar"
    19  	"fmt"
    20  	"io"
    21  	"io/ioutil"
    22  	"os"
    23  	"path/filepath"
    24  	"runtime"
    25  	"syscall"
    26  	"testing"
    27  	"time"
    28  
    29  	"github.com/coreos/rkt/pkg/multicall"
    30  	"github.com/coreos/rkt/pkg/sys"
    31  	"github.com/coreos/rkt/pkg/uid"
    32  )
    33  
    34  func init() {
    35  	multicall.MaybeExec()
    36  }
    37  
    38  type testTarEntry struct {
    39  	header   *tar.Header
    40  	contents string
    41  }
    42  
    43  func newTestTar(entries []*testTarEntry) (string, error) {
    44  	t, err := ioutil.TempFile("", "test-tar")
    45  	if err != nil {
    46  		return "", err
    47  	}
    48  	defer t.Close()
    49  	tw := tar.NewWriter(t)
    50  	for _, entry := range entries {
    51  		// Add default mode
    52  		if entry.header.Mode == 0 {
    53  			if entry.header.Typeflag == tar.TypeDir {
    54  				entry.header.Mode = 0755
    55  			} else {
    56  				entry.header.Mode = 0644
    57  			}
    58  		}
    59  		// Add calling user uid and gid or tests will fail
    60  		entry.header.Uid = os.Getuid()
    61  		entry.header.Gid = os.Getgid()
    62  		if err := tw.WriteHeader(entry.header); err != nil {
    63  			return "", err
    64  		}
    65  		if _, err := io.WriteString(tw, entry.contents); err != nil {
    66  			return "", err
    67  		}
    68  	}
    69  	if err := tw.Close(); err != nil {
    70  		return "", err
    71  	}
    72  	return t.Name(), nil
    73  }
    74  
    75  type fileInfo struct {
    76  	path     string
    77  	typeflag byte
    78  	size     int64
    79  	contents string
    80  	mode     os.FileMode
    81  }
    82  
    83  func fileInfoSliceToMap(slice []*fileInfo) map[string]*fileInfo {
    84  	fim := make(map[string]*fileInfo, len(slice))
    85  	for _, fi := range slice {
    86  		fim[fi.path] = fi
    87  	}
    88  	return fim
    89  }
    90  
    91  func checkExpectedFiles(dir string, expectedFiles map[string]*fileInfo) error {
    92  	files := make(map[string]*fileInfo)
    93  	err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
    94  		fm := info.Mode()
    95  		if path == dir {
    96  			return nil
    97  		}
    98  		relpath, err := filepath.Rel(dir, path)
    99  		if err != nil {
   100  			return err
   101  		}
   102  		switch {
   103  		case fm.IsRegular():
   104  			files[relpath] = &fileInfo{path: relpath, typeflag: tar.TypeReg, size: info.Size(), mode: info.Mode().Perm()}
   105  		case info.IsDir():
   106  			files[relpath] = &fileInfo{path: relpath, typeflag: tar.TypeDir, mode: info.Mode().Perm()}
   107  		case fm&os.ModeSymlink != 0:
   108  			files[relpath] = &fileInfo{path: relpath, typeflag: tar.TypeSymlink, mode: info.Mode()}
   109  		default:
   110  			return fmt.Errorf("file mode not handled: %v", fm)
   111  		}
   112  
   113  		return nil
   114  	})
   115  	if err != nil {
   116  		return err
   117  	}
   118  
   119  	// Set defaults for not specified expected file mode
   120  	for _, ef := range expectedFiles {
   121  		if ef.mode == 0 {
   122  			if ef.typeflag == tar.TypeDir {
   123  				ef.mode = 0755
   124  			} else {
   125  				ef.mode = 0644
   126  			}
   127  		}
   128  	}
   129  
   130  	for _, ef := range expectedFiles {
   131  		_, ok := files[ef.path]
   132  		if !ok {
   133  			return fmt.Errorf("Expected file %q not in files", ef.path)
   134  		}
   135  
   136  	}
   137  
   138  	for _, file := range files {
   139  		ef, ok := expectedFiles[file.path]
   140  		if !ok {
   141  			return fmt.Errorf("file %q not in expectedFiles", file.path)
   142  		}
   143  		if ef.typeflag != file.typeflag {
   144  			return fmt.Errorf("file %q: file type differs: wanted: %d, got: %d", file.path, ef.typeflag, file.typeflag)
   145  		}
   146  		if ef.typeflag == tar.TypeReg {
   147  			if ef.size != file.size {
   148  				return fmt.Errorf("file %q: size differs: wanted %d, wanted: %d", file.path, ef.size, file.size)
   149  			}
   150  			if ef.contents != "" {
   151  				buf, err := ioutil.ReadFile(filepath.Join(dir, file.path))
   152  				if err != nil {
   153  					return fmt.Errorf("unexpected error: %v", err)
   154  				}
   155  				if string(buf) != ef.contents {
   156  					return fmt.Errorf("unexpected contents, wanted: %s, got: %s", ef.contents, buf)
   157  				}
   158  
   159  			}
   160  		}
   161  		// Check modes but ignore symlinks
   162  		if ef.mode != file.mode && ef.typeflag != tar.TypeSymlink {
   163  			return fmt.Errorf("file %q: mode differs: wanted %#o, got: %#o", file.path, ef.mode, file.mode)
   164  		}
   165  
   166  	}
   167  	return nil
   168  }
   169  
   170  func TestExtractTarFolders(t *testing.T) {
   171  	if !sys.HasChrootCapability() {
   172  		t.Skipf("chroot capability not available. Disabling test.")
   173  	}
   174  	testExtractTarFolders(t, extractTarHelper)
   175  }
   176  func TestExtractTarFoldersInsecure(t *testing.T) {
   177  	testExtractTarFolders(t, extractTarInsecureHelper)
   178  }
   179  func testExtractTarFolders(t *testing.T, extractTar func(io.Reader, string) error) {
   180  	entries := []*testTarEntry{
   181  		{
   182  			contents: "foo",
   183  			header: &tar.Header{
   184  				Name: "deep/folder/foo.txt",
   185  				Size: 3,
   186  			},
   187  		},
   188  		{
   189  			header: &tar.Header{
   190  				Name:     "deep/folder/",
   191  				Typeflag: tar.TypeDir,
   192  				Mode:     int64(0747),
   193  			},
   194  		},
   195  		{
   196  			contents: "bar",
   197  			header: &tar.Header{
   198  				Name: "deep/folder/bar.txt",
   199  				Size: 3,
   200  			},
   201  		},
   202  		{
   203  			header: &tar.Header{
   204  				Name:     "deep/folder2/symlink.txt",
   205  				Typeflag: tar.TypeSymlink,
   206  				Linkname: "deep/folder/foo.txt",
   207  			},
   208  		},
   209  		{
   210  			header: &tar.Header{
   211  				Name:     "deep/folder2/",
   212  				Typeflag: tar.TypeDir,
   213  				Mode:     int64(0747),
   214  			},
   215  		},
   216  		{
   217  			contents: "bar",
   218  			header: &tar.Header{
   219  				Name: "deep/folder2/bar.txt",
   220  				Size: 3,
   221  			},
   222  		},
   223  		{
   224  			header: &tar.Header{
   225  				Name:     "deep/deep/folder",
   226  				Typeflag: tar.TypeDir,
   227  				Mode:     int64(0755),
   228  			},
   229  		},
   230  		{
   231  			header: &tar.Header{
   232  				Name:     "deep/deep/",
   233  				Typeflag: tar.TypeDir,
   234  				Mode:     int64(0747),
   235  			},
   236  		},
   237  	}
   238  
   239  	testTarPath, err := newTestTar(entries)
   240  	if err != nil {
   241  		t.Errorf("unexpected error: %v", err)
   242  	}
   243  	defer os.Remove(testTarPath)
   244  	containerTar, err := os.Open(testTarPath)
   245  	if err != nil {
   246  		t.Errorf("unexpected error: %v", err)
   247  	}
   248  	defer containerTar.Close()
   249  	tmpdir, err := ioutil.TempDir("", "rkt-temp-dir")
   250  	if err != nil {
   251  		t.Errorf("unexpected error: %v", err)
   252  	}
   253  	os.RemoveAll(tmpdir)
   254  	err = os.MkdirAll(tmpdir, 0755)
   255  	if err != nil {
   256  		t.Errorf("unexpected error: %v", err)
   257  	}
   258  	defer os.RemoveAll(tmpdir)
   259  	err = extractTar(containerTar, tmpdir)
   260  	if err != nil {
   261  		t.Errorf("unexpected error: %v", err)
   262  	}
   263  	matches, err := filepath.Glob(filepath.Join(tmpdir, "deep/folder/*.txt"))
   264  	if err != nil {
   265  		t.Errorf("unexpected error: %v", err)
   266  	}
   267  	if len(matches) != 2 {
   268  		t.Errorf("unexpected number of files found: %d, wanted 2", len(matches))
   269  	}
   270  	matches, err = filepath.Glob(filepath.Join(tmpdir, "deep/folder2/*.txt"))
   271  	if err != nil {
   272  		t.Errorf("unexpected error: %v", err)
   273  	}
   274  	if len(matches) != 2 {
   275  		t.Errorf("unexpected number of files found: %d, wanted 2", len(matches))
   276  	}
   277  
   278  	dirInfo, err := os.Lstat(filepath.Join(tmpdir, "deep/folder"))
   279  	if err != nil {
   280  		t.Errorf("unexpected error: %v", err)
   281  	} else if dirInfo.Mode().Perm() != os.FileMode(0747) {
   282  		t.Errorf("unexpected dir mode: %s", dirInfo.Mode())
   283  	}
   284  	dirInfo, err = os.Lstat(filepath.Join(tmpdir, "deep/deep"))
   285  	if err != nil {
   286  		t.Errorf("unexpected error: %v", err)
   287  	} else if dirInfo.Mode().Perm() != os.FileMode(0747) {
   288  		t.Errorf("unexpected dir mode: %s", dirInfo.Mode())
   289  	}
   290  }
   291  
   292  func TestExtractTarFileToBuf(t *testing.T) {
   293  	entries := []*testTarEntry{
   294  		{
   295  			header: &tar.Header{
   296  				Name:     "folder/",
   297  				Typeflag: tar.TypeDir,
   298  				Mode:     int64(0747),
   299  			},
   300  		},
   301  		{
   302  			contents: "foo",
   303  			header: &tar.Header{
   304  				Name: "folder/foo.txt",
   305  				Size: 3,
   306  			},
   307  		},
   308  		{
   309  			contents: "bar",
   310  			header: &tar.Header{
   311  				Name: "folder/bar.txt",
   312  				Size: 3,
   313  			},
   314  		},
   315  		{
   316  			header: &tar.Header{
   317  				Name:     "folder/symlink.txt",
   318  				Typeflag: tar.TypeSymlink,
   319  				Linkname: "folder/foo.txt",
   320  			},
   321  		},
   322  	}
   323  	testTarPath, err := newTestTar(entries)
   324  	if err != nil {
   325  		t.Fatalf("unexpected error: %v", err)
   326  	}
   327  	defer os.Remove(testTarPath)
   328  	containerTar1, err := os.Open(testTarPath)
   329  	if err != nil {
   330  		t.Fatalf("unexpected error: %v", err)
   331  	}
   332  	defer containerTar1.Close()
   333  
   334  	tr := tar.NewReader(containerTar1)
   335  	buf, err := extractFileFromTar(tr, "folder/foo.txt")
   336  	if err != nil {
   337  		t.Errorf("unexpected error: %v", err)
   338  	}
   339  	if string(buf) != "foo" {
   340  		t.Errorf("unexpected contents, wanted: %s, got: %s", "foo", buf)
   341  	}
   342  
   343  	containerTar2, err := os.Open(testTarPath)
   344  	if err != nil {
   345  		t.Errorf("unexpected error: %v", err)
   346  	}
   347  	defer containerTar2.Close()
   348  	tr = tar.NewReader(containerTar2)
   349  	buf, err = extractFileFromTar(tr, "folder/symlink.txt")
   350  	if err == nil {
   351  		t.Errorf("expected error")
   352  	}
   353  }
   354  
   355  func TestExtractTarPWL(t *testing.T) {
   356  	if !sys.HasChrootCapability() {
   357  		t.Skipf("chroot capability not available. Disabling test.")
   358  	}
   359  	testExtractTarPWL(t, extractTarHelperPWL)
   360  }
   361  func TestExtractTarPWLInsecure(t *testing.T) {
   362  	testExtractTarPWL(t, extractTarInsecureHelperPWL)
   363  }
   364  func testExtractTarPWL(t *testing.T, extractTar func(rdr io.Reader, target string, pwl PathWhitelistMap) error) {
   365  	entries := []*testTarEntry{
   366  		{
   367  			header: &tar.Header{
   368  				Name:     "folder/",
   369  				Typeflag: tar.TypeDir,
   370  				Mode:     int64(0747),
   371  			},
   372  		},
   373  		{
   374  			contents: "foo",
   375  			header: &tar.Header{
   376  				Name: "folder/foo.txt",
   377  				Size: 3,
   378  			},
   379  		},
   380  		{
   381  			contents: "bar",
   382  			header: &tar.Header{
   383  				Name: "folder/bar.txt",
   384  				Size: 3,
   385  			},
   386  		},
   387  		{
   388  			header: &tar.Header{
   389  				Name:     "folder/symlink.txt",
   390  				Typeflag: tar.TypeSymlink,
   391  				Linkname: "folder/foo.txt",
   392  			},
   393  		},
   394  	}
   395  	testTarPath, err := newTestTar(entries)
   396  	if err != nil {
   397  		t.Errorf("unexpected error: %v", err)
   398  	}
   399  	defer os.Remove(testTarPath)
   400  	containerTar, err := os.Open(testTarPath)
   401  	if err != nil {
   402  		t.Errorf("unexpected error: %v", err)
   403  	}
   404  	defer containerTar.Close()
   405  	tmpdir, err := ioutil.TempDir("", "rkt-temp-dir")
   406  	if err != nil {
   407  		t.Errorf("unexpected error: %v", err)
   408  	}
   409  	defer os.RemoveAll(tmpdir)
   410  
   411  	pwl := make(PathWhitelistMap)
   412  	pwl["folder/foo.txt"] = struct{}{}
   413  	err = extractTar(containerTar, tmpdir, pwl)
   414  	if err != nil {
   415  		t.Errorf("unexpected error: %v", err)
   416  	}
   417  	matches, err := filepath.Glob(filepath.Join(tmpdir, "folder/*.txt"))
   418  	if err != nil {
   419  		t.Errorf("unexpected error: %v", err)
   420  	}
   421  	if len(matches) != 1 {
   422  		t.Errorf("unexpected number of files found: %d, wanted 1", len(matches))
   423  	}
   424  }
   425  
   426  func TestExtractTarOverwrite(t *testing.T) {
   427  	if !sys.HasChrootCapability() {
   428  		t.Skipf("chroot capability not available. Disabling test.")
   429  	}
   430  	testExtractTarOverwrite(t, extractTarHelper)
   431  }
   432  func TestExtractTarOverwriteInsecure(t *testing.T) {
   433  	testExtractTarOverwrite(t, extractTarInsecureHelper)
   434  }
   435  func testExtractTarOverwrite(t *testing.T, extractTar func(io.Reader, string) error) {
   436  	tmpdir, err := ioutil.TempDir("", "rkt-temp-dir")
   437  	if err != nil {
   438  		t.Fatalf("unexpected error: %v", err)
   439  	}
   440  	defer os.RemoveAll(tmpdir)
   441  
   442  	entries := []*testTarEntry{
   443  		{
   444  			contents: "hello",
   445  			header: &tar.Header{
   446  				Name: "hello.txt",
   447  				Size: 5,
   448  			},
   449  		},
   450  		{
   451  			header: &tar.Header{
   452  				Name:     "afolder",
   453  				Typeflag: tar.TypeDir,
   454  			},
   455  		},
   456  		{
   457  			contents: "hello",
   458  			header: &tar.Header{
   459  				Name: "afolder/hello.txt",
   460  				Size: 5,
   461  			},
   462  		},
   463  		{
   464  			contents: "hello",
   465  			header: &tar.Header{
   466  				Name: "afile",
   467  				Size: 5,
   468  			},
   469  		},
   470  		{
   471  			header: &tar.Header{
   472  				Name:     "folder01",
   473  				Typeflag: tar.TypeDir,
   474  			},
   475  		},
   476  		{
   477  			contents: "hello",
   478  			header: &tar.Header{
   479  				Name: "folder01/file01",
   480  				Size: 5,
   481  			},
   482  		},
   483  		{
   484  			contents: "hello",
   485  			header: &tar.Header{
   486  				Name: "filesymlinked",
   487  				Size: 5,
   488  			},
   489  		},
   490  		{
   491  			header: &tar.Header{
   492  				Name:     "linktofile",
   493  				Linkname: "filesymlinked",
   494  				Typeflag: tar.TypeSymlink,
   495  			},
   496  		},
   497  
   498  		{
   499  			header: &tar.Header{
   500  				Name:     "dirsymlinked",
   501  				Typeflag: tar.TypeDir,
   502  			},
   503  		},
   504  		{
   505  			header: &tar.Header{
   506  				Name:     "linktodir",
   507  				Linkname: "dirsymlinked",
   508  				Typeflag: tar.TypeSymlink,
   509  			},
   510  		},
   511  	}
   512  
   513  	testTarPath, err := newTestTar(entries)
   514  	if err != nil {
   515  		t.Fatalf("unexpected error: %v", err)
   516  	}
   517  	defer os.Remove(testTarPath)
   518  	containerTar1, err := os.Open(testTarPath)
   519  	if err != nil {
   520  		t.Fatalf("unexpected error: %v", err)
   521  	}
   522  	defer containerTar1.Close()
   523  	err = extractTar(containerTar1, tmpdir)
   524  	if err != nil {
   525  		t.Fatalf("unexpected error: %v", err)
   526  	}
   527  
   528  	// Now overwrite:
   529  	// a file with a new file
   530  	// a dir with a file
   531  	entries = []*testTarEntry{
   532  		{
   533  			contents: "newhello",
   534  			header: &tar.Header{
   535  				Name: "hello.txt",
   536  				Size: 8,
   537  			},
   538  		},
   539  		// Now this is a file
   540  		{
   541  			contents: "nowafile",
   542  			header: &tar.Header{
   543  				Name:     "afolder",
   544  				Typeflag: tar.TypeReg,
   545  				Size:     8,
   546  			},
   547  		},
   548  		// Now this is a dir
   549  		{
   550  			header: &tar.Header{
   551  				Name:     "afile",
   552  				Typeflag: tar.TypeDir,
   553  			},
   554  		},
   555  		// Overwrite symlink to a file with a regular file
   556  		// the linked file shouldn't be removed
   557  		{
   558  			contents: "filereplacingsymlink",
   559  			header: &tar.Header{
   560  				Name:     "linktofile",
   561  				Typeflag: tar.TypeReg,
   562  				Size:     20,
   563  			},
   564  		},
   565  		// Overwrite symlink to a dir with a regular file
   566  		// the linked directory and all its contents shouldn't be
   567  		// removed
   568  		{
   569  			contents: "filereplacingsymlink",
   570  			header: &tar.Header{
   571  				Name:     "linktodir",
   572  				Typeflag: tar.TypeReg,
   573  				Size:     20,
   574  			},
   575  		},
   576  		// folder01 already exists and shouldn't be removed (keeping folder01/file01)
   577  		{
   578  			header: &tar.Header{
   579  				Name:     "folder01",
   580  				Typeflag: tar.TypeDir,
   581  				Mode:     int64(0755),
   582  			},
   583  		},
   584  		{
   585  			contents: "hello",
   586  			header: &tar.Header{
   587  				Name: "folder01/file02",
   588  				Size: 5,
   589  				Mode: int64(0644),
   590  			},
   591  		},
   592  	}
   593  	testTarPath, err = newTestTar(entries)
   594  	if err != nil {
   595  		t.Errorf("unexpected error: %v", err)
   596  	}
   597  	defer os.Remove(testTarPath)
   598  	containerTar2, err := os.Open(testTarPath)
   599  	if err != nil {
   600  		t.Errorf("unexpected error: %v", err)
   601  	}
   602  	defer containerTar2.Close()
   603  	err = extractTar(containerTar2, tmpdir)
   604  
   605  	expectedFiles := []*fileInfo{
   606  		&fileInfo{path: "hello.txt", typeflag: tar.TypeReg, size: 8, contents: "newhello"},
   607  		&fileInfo{path: "linktofile", typeflag: tar.TypeReg, size: 20},
   608  		&fileInfo{path: "linktodir", typeflag: tar.TypeReg, size: 20},
   609  		&fileInfo{path: "afolder", typeflag: tar.TypeReg, size: 8},
   610  		&fileInfo{path: "dirsymlinked", typeflag: tar.TypeDir},
   611  		&fileInfo{path: "afile", typeflag: tar.TypeDir},
   612  		&fileInfo{path: "filesymlinked", typeflag: tar.TypeReg, size: 5},
   613  		&fileInfo{path: "folder01", typeflag: tar.TypeDir},
   614  		&fileInfo{path: "folder01/file01", typeflag: tar.TypeReg, size: 5},
   615  		&fileInfo{path: "folder01/file02", typeflag: tar.TypeReg, size: 5},
   616  	}
   617  
   618  	err = checkExpectedFiles(tmpdir, fileInfoSliceToMap(expectedFiles))
   619  	if err != nil {
   620  		t.Errorf("unexpected error: %v", err)
   621  	}
   622  }
   623  
   624  func TestExtractTarTimes(t *testing.T) {
   625  	if !sys.HasChrootCapability() {
   626  		t.Skipf("chroot capability not available. Disabling test.")
   627  	}
   628  	testExtractTarTimes(t, extractTarHelper)
   629  }
   630  func TestExtractTarTimesInsecure(t *testing.T) {
   631  	testExtractTarTimes(t, extractTarInsecureHelper)
   632  }
   633  func testExtractTarTimes(t *testing.T, extractTar func(io.Reader, string) error) {
   634  	// Do not set ns as tar has second precision
   635  	time1 := time.Unix(100000, 0)
   636  	time2 := time.Unix(200000, 0)
   637  	time3 := time.Unix(300000, 0)
   638  	entries := []*testTarEntry{
   639  		{
   640  			header: &tar.Header{
   641  				Name:     "folder/",
   642  				Typeflag: tar.TypeDir,
   643  				Mode:     int64(0747),
   644  				ModTime:  time1,
   645  			},
   646  		},
   647  		{
   648  			contents: "foo",
   649  			header: &tar.Header{
   650  				Name:    "folder/foo.txt",
   651  				Size:    3,
   652  				ModTime: time2,
   653  			},
   654  		},
   655  		{
   656  			header: &tar.Header{
   657  				Name:     "folder/symlink.txt",
   658  				Typeflag: tar.TypeSymlink,
   659  				Linkname: "folder/foo.txt",
   660  				ModTime:  time3,
   661  			},
   662  		},
   663  	}
   664  
   665  	testTarPath, err := newTestTar(entries)
   666  	if err != nil {
   667  		t.Errorf("unexpected error: %v", err)
   668  	}
   669  	defer os.Remove(testTarPath)
   670  	containerTar, err := os.Open(testTarPath)
   671  	if err != nil {
   672  		t.Errorf("unexpected error: %v", err)
   673  	}
   674  	defer containerTar.Close()
   675  	tmpdir, err := ioutil.TempDir("", "rkt-temp-dir")
   676  	if err != nil {
   677  		t.Errorf("unexpected error: %v", err)
   678  	}
   679  	os.RemoveAll(tmpdir)
   680  	err = os.MkdirAll(tmpdir, 0755)
   681  	if err != nil {
   682  		t.Errorf("unexpected error: %v", err)
   683  	}
   684  	err = extractTar(containerTar, tmpdir)
   685  	if err != nil {
   686  		t.Errorf("unexpected error: %v", err)
   687  	}
   688  	err = checkTime(filepath.Join(tmpdir, "folder/"), time1)
   689  	if err != nil {
   690  		t.Errorf("unexpected error: %v", err)
   691  	}
   692  	err = checkTime(filepath.Join(tmpdir, "folder/foo.txt"), time2)
   693  	if err != nil {
   694  		t.Errorf("unexpected error: %v", err)
   695  	}
   696  
   697  	//Check only (by now) on linux
   698  	if runtime.GOOS == "linux" {
   699  		err = checkTime(filepath.Join(tmpdir, "folder/symlink.txt"), time3)
   700  		if err != nil {
   701  			t.Errorf("unexpected error: %v", err)
   702  		}
   703  	}
   704  }
   705  
   706  func checkTime(path string, time time.Time) error {
   707  	info, err := os.Lstat(path)
   708  	if err != nil {
   709  		return err
   710  	}
   711  
   712  	if info.ModTime() != time {
   713  		return fmt.Errorf("%s: info.ModTime: %s, different from expected time: %s", path, info.ModTime(), time)
   714  	}
   715  	return nil
   716  }
   717  
   718  func TestExtractTarHardLink(t *testing.T) {
   719  	if !sys.HasChrootCapability() {
   720  		t.Skipf("chroot capability not available. Disabling test.")
   721  	}
   722  	testExtractTarHardLink(t, extractTarHelper)
   723  }
   724  func TestExtractTarHardLinkInsecure(t *testing.T) {
   725  	testExtractTarHardLink(t, extractTarInsecureHelper)
   726  }
   727  func testExtractTarHardLink(t *testing.T, extractTar func(io.Reader, string) error) {
   728  	entries := []*testTarEntry{
   729  		{
   730  			contents: "foo",
   731  			header: &tar.Header{
   732  				Name: "/foo.txt",
   733  				Size: 3,
   734  			},
   735  		},
   736  		{
   737  			header: &tar.Header{
   738  				Name:     "foolink",
   739  				Typeflag: tar.TypeLink,
   740  				Linkname: "/foo.txt",
   741  			},
   742  		},
   743  	}
   744  
   745  	testTarPath, err := newTestTar(entries)
   746  	if err != nil {
   747  		t.Errorf("unexpected error: %v", err)
   748  	}
   749  	defer os.Remove(testTarPath)
   750  	containerTar, err := os.Open(testTarPath)
   751  	if err != nil {
   752  		t.Errorf("unexpected error: %v", err)
   753  	}
   754  	defer containerTar.Close()
   755  	tmpdir, err := ioutil.TempDir("", "rkt-temp-dir")
   756  	if err != nil {
   757  		t.Errorf("unexpected error: %v", err)
   758  	}
   759  	os.RemoveAll(tmpdir)
   760  	err = os.MkdirAll(tmpdir, 0755)
   761  	if err != nil {
   762  		t.Errorf("unexpected error: %v", err)
   763  	}
   764  	defer os.RemoveAll(tmpdir)
   765  	err = extractTar(containerTar, tmpdir)
   766  	if err != nil {
   767  		t.Errorf("unexpected error: %v", err)
   768  	}
   769  	var origFile syscall.Stat_t
   770  	err = syscall.Stat(filepath.Join(tmpdir, "/foo.txt"), &origFile)
   771  	if err != nil {
   772  		t.Errorf("unexpected error: %v", err)
   773  	}
   774  	var linkedFile syscall.Stat_t
   775  	err = syscall.Stat(filepath.Join(tmpdir, "/foolink"), &linkedFile)
   776  	if err != nil {
   777  		t.Errorf("unexpected error: %v", err)
   778  	}
   779  	if origFile.Nlink != 2 {
   780  		t.Errorf("wrong number of nlinks on original file, expecting: 2, actual: %d", origFile.Nlink)
   781  	}
   782  	if linkedFile.Nlink != 2 {
   783  		t.Errorf("wrong number of nlinks on linked file, expecting: 2, actual: %d", origFile.Nlink)
   784  	}
   785  	if origFile.Ino != linkedFile.Ino {
   786  		t.Errorf("original file and linked file have different inodes")
   787  	}
   788  }
   789  
   790  func extractTarHelper(rdr io.Reader, target string) error {
   791  	return extractTarHelperPWL(rdr, target, nil)
   792  }
   793  
   794  func extractTarHelperPWL(rdr io.Reader, target string, pwl PathWhitelistMap) error {
   795  	return ExtractTar(rdr, target, false, uid.NewBlankUidRange(), pwl)
   796  }
   797  
   798  func extractTarInsecureHelper(rdr io.Reader, target string) error {
   799  	return extractTarInsecureHelperPWL(rdr, target, nil)
   800  }
   801  
   802  func extractTarInsecureHelperPWL(rdr io.Reader, target string, pwl PathWhitelistMap) error {
   803  	editor, err := NewUidShiftingFilePermEditor(uid.NewBlankUidRange())
   804  	if err != nil {
   805  		return err
   806  	}
   807  	return ExtractTarInsecure(tar.NewReader(rdr), target, true, pwl, editor)
   808  }