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 }