gitee.com/leisunstar/runtime@v0.0.0-20200521203717-5cef3e7b53f9/virtcontainers/mount_test.go (about)

     1  // Copyright (c) 2017 Intel Corporation
     2  //
     3  // SPDX-License-Identifier: Apache-2.0
     4  //
     5  
     6  package virtcontainers
     7  
     8  import (
     9  	"bytes"
    10  	"context"
    11  	"fmt"
    12  	"io/ioutil"
    13  	"os"
    14  	"os/exec"
    15  	"path/filepath"
    16  	"strconv"
    17  	"strings"
    18  	"syscall"
    19  	"testing"
    20  
    21  	ktu "github.com/kata-containers/runtime/pkg/katatestutils"
    22  	"github.com/stretchr/testify/assert"
    23  )
    24  
    25  const (
    26  	testDirMode = os.FileMode(0750)
    27  )
    28  
    29  var tc ktu.TestConstraint
    30  
    31  func init() {
    32  	tc = ktu.NewTestConstraint(false)
    33  }
    34  
    35  func TestIsSystemMount(t *testing.T) {
    36  	assert := assert.New(t)
    37  	tests := []struct {
    38  		mnt      string
    39  		expected bool
    40  	}{
    41  		{"/sys", true},
    42  		{"/sys/", true},
    43  		{"/sys//", true},
    44  		{"/sys/fs", true},
    45  		{"/sys/fs/", true},
    46  		{"/sys/fs/cgroup", true},
    47  		{"/sysfoo", false},
    48  		{"/home", false},
    49  		{"/dev/block/", false},
    50  		{"/mnt/dev/foo", false},
    51  	}
    52  
    53  	for _, test := range tests {
    54  		result := isSystemMount(test.mnt)
    55  		assert.Exactly(result, test.expected)
    56  	}
    57  }
    58  
    59  func TestIsHostDevice(t *testing.T) {
    60  	assert := assert.New(t)
    61  	tests := []struct {
    62  		mnt      string
    63  		expected bool
    64  	}{
    65  		{"/dev", true},
    66  		{"/dev/zero", true},
    67  		{"/dev/block", true},
    68  		{"/mnt/dev/block", false},
    69  	}
    70  
    71  	for _, test := range tests {
    72  		result := isHostDevice(test.mnt)
    73  		assert.Equal(result, test.expected)
    74  	}
    75  }
    76  
    77  func TestIsHostDeviceCreateFile(t *testing.T) {
    78  	assert := assert.New(t)
    79  	if tc.NotValid(ktu.NeedRoot()) {
    80  		t.Skip(ktu.TestDisabledNeedRoot)
    81  	}
    82  	// Create regular file in /dev
    83  
    84  	path := "/dev/foobar"
    85  	f, err := os.Create(path)
    86  	assert.NoError(err)
    87  	f.Close()
    88  
    89  	assert.False(isHostDevice(path))
    90  	assert.NoError(os.Remove(path))
    91  }
    92  
    93  func TestMajorMinorNumber(t *testing.T) {
    94  	assert := assert.New(t)
    95  	devices := []string{"/dev/zero", "/dev/net/tun"}
    96  
    97  	for _, device := range devices {
    98  		cmdStr := fmt.Sprintf("ls -l %s | awk '{print $5$6}'", device)
    99  		cmd := exec.Command("sh", "-c", cmdStr)
   100  		output, err := cmd.Output()
   101  		assert.NoError(err)
   102  
   103  		data := bytes.Split(output, []byte(","))
   104  		assert.False(len(data) < 2)
   105  
   106  		majorStr := strings.TrimSpace(string(data[0]))
   107  		minorStr := strings.TrimSpace(string(data[1]))
   108  
   109  		majorNo, err := strconv.Atoi(majorStr)
   110  		assert.NoError(err)
   111  
   112  		minorNo, err := strconv.Atoi(minorStr)
   113  		assert.NoError(err)
   114  
   115  		stat := syscall.Stat_t{}
   116  		err = syscall.Stat(device, &stat)
   117  		assert.NoError(err)
   118  
   119  		// Get major and minor numbers for the device itself. Note the use of stat.Rdev instead of Dev.
   120  		major := major(stat.Rdev)
   121  		minor := minor(stat.Rdev)
   122  
   123  		assert.Equal(minor, minorNo)
   124  		assert.Equal(major, majorNo)
   125  	}
   126  }
   127  
   128  func TestGetDeviceForPathRoot(t *testing.T) {
   129  	assert := assert.New(t)
   130  	dev, err := getDeviceForPath("/")
   131  	assert.NoError(err)
   132  
   133  	expected := "/"
   134  
   135  	assert.Equal(dev.mountPoint, expected)
   136  }
   137  
   138  func TestGetDeviceForPathValidMount(t *testing.T) {
   139  	assert := assert.New(t)
   140  	dev, err := getDeviceForPath("/proc")
   141  	assert.NoError(err)
   142  
   143  	expected := "/proc"
   144  
   145  	assert.Equal(dev.mountPoint, expected)
   146  }
   147  
   148  func TestGetDeviceForPathEmptyPath(t *testing.T) {
   149  	assert := assert.New(t)
   150  	_, err := getDeviceForPath("")
   151  	assert.Error(err)
   152  }
   153  
   154  func TestGetDeviceForPath(t *testing.T) {
   155  	assert := assert.New(t)
   156  
   157  	dev, err := getDeviceForPath("///")
   158  	assert.NoError(err)
   159  
   160  	assert.Equal(dev.mountPoint, "/")
   161  
   162  	_, err = getDeviceForPath("/../../.././././../.")
   163  	assert.NoError(err)
   164  
   165  	_, err = getDeviceForPath("/root/file with spaces")
   166  	assert.Error(err)
   167  }
   168  
   169  func TestGetDeviceForPathBindMount(t *testing.T) {
   170  	assert := assert.New(t)
   171  
   172  	if tc.NotValid(ktu.NeedRoot()) {
   173  		t.Skip(ktu.TestDisabledNeedRoot)
   174  	}
   175  
   176  	source := filepath.Join(testDir, "testDeviceDirSrc")
   177  	dest := filepath.Join(testDir, "testDeviceDirDest")
   178  	syscall.Unmount(dest, 0)
   179  	os.Remove(source)
   180  	os.Remove(dest)
   181  
   182  	err := os.MkdirAll(source, mountPerm)
   183  	assert.NoError(err)
   184  
   185  	defer os.Remove(source)
   186  
   187  	err = os.MkdirAll(dest, mountPerm)
   188  	assert.NoError(err)
   189  
   190  	defer os.Remove(dest)
   191  
   192  	err = bindMount(context.Background(), source, dest, false, "private")
   193  	assert.NoError(err)
   194  
   195  	defer syscall.Unmount(dest, syscall.MNT_DETACH)
   196  
   197  	destFile := filepath.Join(dest, "test")
   198  	_, err = os.Create(destFile)
   199  	assert.NoError(err)
   200  
   201  	defer os.Remove(destFile)
   202  
   203  	sourceDev, _ := getDeviceForPath(source)
   204  	destDev, _ := getDeviceForPath(destFile)
   205  
   206  	assert.Equal(sourceDev, destDev)
   207  }
   208  
   209  func TestIsDeviceMapper(t *testing.T) {
   210  	assert := assert.New(t)
   211  
   212  	// known major, minor for /dev/tty
   213  	major := 5
   214  	minor := 0
   215  
   216  	isDM, err := isDeviceMapper(major, minor)
   217  	assert.NoError(err)
   218  	assert.False(isDM)
   219  
   220  	// fake the block device format
   221  	blockFormatTemplate = "/sys/dev/char/%d:%d"
   222  	isDM, err = isDeviceMapper(major, minor)
   223  	assert.NoError(err)
   224  	assert.True(isDM)
   225  }
   226  
   227  func TestIsDockerVolume(t *testing.T) {
   228  	assert := assert.New(t)
   229  	path := "/var/lib/docker/volumes/00da1347c7cf4f15db35f/_data"
   230  	isDockerVolume := IsDockerVolume(path)
   231  	assert.True(isDockerVolume)
   232  
   233  	path = "/var/lib/testdir"
   234  	isDockerVolume = IsDockerVolume(path)
   235  	assert.False(isDockerVolume)
   236  }
   237  
   238  func TestIsEphemeralStorage(t *testing.T) {
   239  	assert := assert.New(t)
   240  	if tc.NotValid(ktu.NeedRoot()) {
   241  		t.Skip(ktu.TestDisabledNeedRoot)
   242  	}
   243  
   244  	dir, err := ioutil.TempDir(testDir, "foo")
   245  	assert.NoError(err)
   246  	defer os.RemoveAll(dir)
   247  
   248  	sampleEphePath := filepath.Join(dir, K8sEmptyDir, "tmp-volume")
   249  	err = os.MkdirAll(sampleEphePath, testDirMode)
   250  	assert.Nil(err)
   251  
   252  	err = syscall.Mount("tmpfs", sampleEphePath, "tmpfs", 0, "")
   253  	assert.NoError(err)
   254  	defer syscall.Unmount(sampleEphePath, 0)
   255  
   256  	isEphe := IsEphemeralStorage(sampleEphePath)
   257  	assert.True(isEphe)
   258  
   259  	isHostEmptyDir := Isk8sHostEmptyDir(sampleEphePath)
   260  	assert.False(isHostEmptyDir)
   261  
   262  	sampleEphePath = "/var/lib/kubelet/pods/366c3a75-4869-11e8-b479-507b9ddd5ce4/volumes/cache-volume"
   263  	isEphe = IsEphemeralStorage(sampleEphePath)
   264  	assert.False(isEphe)
   265  
   266  	isHostEmptyDir = Isk8sHostEmptyDir(sampleEphePath)
   267  	assert.False(isHostEmptyDir)
   268  }
   269  
   270  func TestBindMountInvalidSourceSymlink(t *testing.T) {
   271  	source := filepath.Join(testDir, "fooFile")
   272  	os.Remove(source)
   273  
   274  	err := bindMount(context.Background(), source, "", false, "private")
   275  	assert.Error(t, err)
   276  }
   277  
   278  func TestBindMountFailingMount(t *testing.T) {
   279  	source := filepath.Join(testDir, "fooLink")
   280  	fakeSource := filepath.Join(testDir, "fooFile")
   281  	os.Remove(source)
   282  	os.Remove(fakeSource)
   283  	assert := assert.New(t)
   284  
   285  	_, err := os.OpenFile(fakeSource, os.O_CREATE, mountPerm)
   286  	assert.NoError(err)
   287  
   288  	err = os.Symlink(fakeSource, source)
   289  	assert.NoError(err)
   290  
   291  	err = bindMount(context.Background(), source, "", false, "private")
   292  	assert.Error(err)
   293  }
   294  
   295  func TestBindMountSuccessful(t *testing.T) {
   296  	assert := assert.New(t)
   297  	if tc.NotValid(ktu.NeedRoot()) {
   298  		t.Skip(testDisabledAsNonRoot)
   299  	}
   300  
   301  	source := filepath.Join(testDir, "fooDirSrc")
   302  	dest := filepath.Join(testDir, "fooDirDest")
   303  	syscall.Unmount(dest, 0)
   304  	os.Remove(source)
   305  	os.Remove(dest)
   306  
   307  	err := os.MkdirAll(source, mountPerm)
   308  	assert.NoError(err)
   309  
   310  	err = os.MkdirAll(dest, mountPerm)
   311  	assert.NoError(err)
   312  
   313  	err = bindMount(context.Background(), source, dest, false, "private")
   314  	assert.NoError(err)
   315  
   316  	syscall.Unmount(dest, 0)
   317  }
   318  
   319  func TestBindMountReadonlySuccessful(t *testing.T) {
   320  	assert := assert.New(t)
   321  	if tc.NotValid(ktu.NeedRoot()) {
   322  		t.Skip(testDisabledAsNonRoot)
   323  	}
   324  
   325  	source := filepath.Join(testDir, "fooDirSrc")
   326  	dest := filepath.Join(testDir, "fooDirDest")
   327  	syscall.Unmount(dest, 0)
   328  	os.Remove(source)
   329  	os.Remove(dest)
   330  
   331  	err := os.MkdirAll(source, mountPerm)
   332  	assert.NoError(err)
   333  
   334  	err = os.MkdirAll(dest, mountPerm)
   335  	assert.NoError(err)
   336  
   337  	err = bindMount(context.Background(), source, dest, true, "private")
   338  	assert.NoError(err)
   339  
   340  	defer syscall.Unmount(dest, 0)
   341  
   342  	// should not be able to create file in read-only mount
   343  	destFile := filepath.Join(dest, "foo")
   344  	_, err = os.OpenFile(destFile, os.O_CREATE, mountPerm)
   345  	assert.Error(err)
   346  }
   347  
   348  func TestBindMountInvalidPgtypes(t *testing.T) {
   349  	assert := assert.New(t)
   350  	if tc.NotValid(ktu.NeedRoot()) {
   351  		t.Skip(testDisabledAsNonRoot)
   352  	}
   353  
   354  	source := filepath.Join(testDir, "fooDirSrc")
   355  	dest := filepath.Join(testDir, "fooDirDest")
   356  	syscall.Unmount(dest, 0)
   357  	os.Remove(source)
   358  	os.Remove(dest)
   359  
   360  	err := os.MkdirAll(source, mountPerm)
   361  	assert.NoError(err)
   362  
   363  	err = os.MkdirAll(dest, mountPerm)
   364  	assert.NoError(err)
   365  
   366  	err = bindMount(context.Background(), source, dest, false, "foo")
   367  	expectedErr := fmt.Sprintf("Wrong propagation type %s", "foo")
   368  	assert.EqualError(err, expectedErr)
   369  }
   370  
   371  // TestBindUnmountContainerRootfsENOENTNotError tests that if a file
   372  // or directory attempting to be unmounted doesn't exist, then it
   373  // is not considered an error
   374  func TestBindUnmountContainerRootfsENOENTNotError(t *testing.T) {
   375  	if os.Getuid() != 0 {
   376  		t.Skip("Test disabled as requires root user")
   377  	}
   378  	testMnt := "/tmp/test_mount"
   379  	sID := "sandIDTest"
   380  	cID := "contIDTest"
   381  	assert := assert.New(t)
   382  
   383  	// check to make sure the file doesn't exist
   384  	testPath := filepath.Join(testMnt, sID, cID, rootfsDir)
   385  	if _, err := os.Stat(testPath); !os.IsNotExist(err) {
   386  		assert.NoError(os.Remove(testPath))
   387  	}
   388  
   389  	err := bindUnmountContainerRootfs(context.Background(), testMnt, sID, cID)
   390  	assert.NoError(err)
   391  }
   392  
   393  func TestBindUnmountContainerRootfsRemoveRootfsDest(t *testing.T) {
   394  	assert := assert.New(t)
   395  	if tc.NotValid(ktu.NeedRoot()) {
   396  		t.Skip(ktu.TestDisabledNeedRoot)
   397  	}
   398  
   399  	sID := "sandIDTestRemoveRootfsDest"
   400  	cID := "contIDTestRemoveRootfsDest"
   401  
   402  	testPath := filepath.Join(testDir, sID, cID, rootfsDir)
   403  	syscall.Unmount(testPath, 0)
   404  	os.Remove(testPath)
   405  
   406  	err := os.MkdirAll(testPath, mountPerm)
   407  	assert.NoError(err)
   408  	defer os.RemoveAll(filepath.Join(testDir, sID))
   409  
   410  	bindUnmountContainerRootfs(context.Background(), testDir, sID, cID)
   411  
   412  	if _, err := os.Stat(testPath); err == nil {
   413  		t.Fatal("empty rootfs dest should be removed")
   414  	} else if !os.IsNotExist(err) {
   415  		t.Fatal(err)
   416  	}
   417  }