k8s.io/kubernetes@v1.29.3/pkg/volume/util/subpath/subpath_linux_test.go (about) 1 //go:build linux 2 // +build linux 3 4 /* 5 Copyright 2014 The Kubernetes Authors. 6 7 Licensed under the Apache License, Version 2.0 (the "License"); 8 you may not use this file except in compliance with the License. 9 You may obtain a copy of the License at 10 11 http://www.apache.org/licenses/LICENSE-2.0 12 13 Unless required by applicable law or agreed to in writing, software 14 distributed under the License is distributed on an "AS IS" BASIS, 15 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 See the License for the specific language governing permissions and 17 limitations under the License. 18 */ 19 20 package subpath 21 22 import ( 23 "fmt" 24 "io/ioutil" 25 "net" 26 "os" 27 "path/filepath" 28 "reflect" 29 "strconv" 30 "syscall" 31 "testing" 32 33 "k8s.io/klog/v2" 34 "k8s.io/mount-utils" 35 ) 36 37 func TestSafeMakeDir(t *testing.T) { 38 defaultPerm := os.FileMode(0750) + os.ModeDir 39 maxPerm := os.FileMode(0777) + os.ModeDir 40 tests := []struct { 41 name string 42 // Function that prepares directory structure for the test under given 43 // base. 44 prepare func(base string) error 45 path string 46 checkPath string 47 perm os.FileMode 48 expectError bool 49 }{ 50 { 51 "directory-does-not-exist", 52 func(base string) error { 53 return nil 54 }, 55 "test/directory", 56 "test/directory", 57 defaultPerm, 58 false, 59 }, 60 { 61 "all-created-subpath-directory-with-permissions", 62 func(base string) error { 63 return nil 64 }, 65 "test/directory", 66 "test", 67 maxPerm, 68 false, 69 }, 70 { 71 "directory-with-sgid", 72 func(base string) error { 73 return nil 74 }, 75 "test/directory", 76 "test/directory", 77 os.FileMode(0777) + os.ModeDir + os.ModeSetgid, 78 false, 79 }, 80 { 81 "directory-with-suid", 82 func(base string) error { 83 return nil 84 }, 85 "test/directory", 86 "test/directory", 87 os.FileMode(0777) + os.ModeDir + os.ModeSetuid, 88 false, 89 }, 90 { 91 "directory-with-sticky-bit", 92 func(base string) error { 93 return nil 94 }, 95 "test/directory", 96 "test/directory", 97 os.FileMode(0777) + os.ModeDir + os.ModeSticky, 98 false, 99 }, 100 { 101 "directory-exists", 102 func(base string) error { 103 return os.MkdirAll(filepath.Join(base, "test/directory"), 0750) 104 }, 105 "test/directory", 106 "test/directory", 107 defaultPerm, 108 false, 109 }, 110 { 111 "create-base", 112 func(base string) error { 113 return nil 114 }, 115 "", 116 "", 117 defaultPerm, 118 false, 119 }, 120 { 121 "escape-base-using-dots", 122 func(base string) error { 123 return nil 124 }, 125 "..", 126 "", 127 defaultPerm, 128 true, 129 }, 130 { 131 "escape-base-using-dots-2", 132 func(base string) error { 133 return nil 134 }, 135 "test/../../..", 136 "", 137 defaultPerm, 138 true, 139 }, 140 { 141 "follow-symlinks", 142 func(base string) error { 143 if err := os.MkdirAll(filepath.Join(base, "destination"), defaultPerm); err != nil { 144 return err 145 } 146 return os.Symlink("destination", filepath.Join(base, "test")) 147 }, 148 "test/directory", 149 "destination/directory", 150 defaultPerm, 151 false, 152 }, 153 { 154 "follow-symlink-loop", 155 func(base string) error { 156 return os.Symlink("test", filepath.Join(base, "test")) 157 }, 158 "test/directory", 159 "", 160 defaultPerm, 161 true, 162 }, 163 { 164 "follow-symlink-multiple follow", 165 func(base string) error { 166 /* test1/dir points to test2 and test2/dir points to test1 */ 167 if err := os.MkdirAll(filepath.Join(base, "test1"), defaultPerm); err != nil { 168 return err 169 } 170 if err := os.MkdirAll(filepath.Join(base, "test2"), defaultPerm); err != nil { 171 return err 172 } 173 if err := os.Symlink(filepath.Join(base, "test2"), filepath.Join(base, "test1/dir")); err != nil { 174 return err 175 } 176 if err := os.Symlink(filepath.Join(base, "test1"), filepath.Join(base, "test2/dir")); err != nil { 177 return err 178 } 179 return nil 180 }, 181 "test1/dir/dir/dir/dir/dir/dir/dir/foo", 182 "test2/foo", 183 defaultPerm, 184 false, 185 }, 186 { 187 "danglink-symlink", 188 func(base string) error { 189 return os.Symlink("non-existing", filepath.Join(base, "test")) 190 }, 191 "test/directory", 192 "", 193 defaultPerm, 194 true, 195 }, 196 { 197 "non-directory", 198 func(base string) error { 199 return ioutil.WriteFile(filepath.Join(base, "test"), []byte{}, defaultPerm) 200 }, 201 "test/directory", 202 "", 203 defaultPerm, 204 true, 205 }, 206 { 207 "non-directory-final", 208 func(base string) error { 209 return ioutil.WriteFile(filepath.Join(base, "test"), []byte{}, defaultPerm) 210 }, 211 "test", 212 "", 213 defaultPerm, 214 true, 215 }, 216 { 217 "escape-with-relative-symlink", 218 func(base string) error { 219 if err := os.MkdirAll(filepath.Join(base, "dir"), defaultPerm); err != nil { 220 return err 221 } 222 if err := os.MkdirAll(filepath.Join(base, "exists"), defaultPerm); err != nil { 223 return err 224 } 225 return os.Symlink("../exists", filepath.Join(base, "dir/test")) 226 }, 227 "dir/test", 228 "", 229 defaultPerm, 230 false, 231 }, 232 { 233 "escape-with-relative-symlink-not-exists", 234 func(base string) error { 235 if err := os.MkdirAll(filepath.Join(base, "dir"), defaultPerm); err != nil { 236 return err 237 } 238 return os.Symlink("../not-exists", filepath.Join(base, "dir/test")) 239 }, 240 "dir/test", 241 "", 242 defaultPerm, 243 true, 244 }, 245 { 246 "escape-with-symlink", 247 func(base string) error { 248 return os.Symlink("/", filepath.Join(base, "test")) 249 }, 250 "test/directory", 251 "", 252 defaultPerm, 253 true, 254 }, 255 } 256 257 for i := range tests { 258 test := tests[i] 259 t.Run(test.name, func(t *testing.T) { 260 base, err := ioutil.TempDir("", "safe-make-dir-"+test.name+"-") 261 if err != nil { 262 t.Fatalf(err.Error()) 263 } 264 defer os.RemoveAll(base) 265 test.prepare(base) 266 pathToCreate := filepath.Join(base, test.path) 267 err = doSafeMakeDir(pathToCreate, base, test.perm) 268 if err != nil && !test.expectError { 269 t.Fatal(err) 270 } 271 if err != nil { 272 t.Logf("got error: %s", err) 273 } 274 if err == nil && test.expectError { 275 t.Fatalf("expected error, got none") 276 } 277 278 if test.checkPath != "" { 279 st, err := os.Stat(filepath.Join(base, test.checkPath)) 280 if err != nil { 281 t.Fatalf("cannot read path %s", test.checkPath) 282 } 283 actualMode := st.Mode() 284 if actualMode != test.perm { 285 if actualMode^test.perm == os.ModeSetgid && test.perm&os.ModeSetgid == 0 { 286 // when TMPDIR is a kubernetes emptydir, the sticky gid bit is set due to fsgroup 287 t.Logf("masking bit from %o", actualMode) 288 } else { 289 t.Errorf("expected permissions %o, got %o (%b)", test.perm, actualMode, test.perm^actualMode) 290 } 291 } 292 } 293 }) 294 } 295 } 296 297 func TestRemoveEmptyDirs(t *testing.T) { 298 defaultPerm := os.FileMode(0750) 299 tests := []struct { 300 name string 301 // Function that prepares directory structure for the test under given 302 // base. 303 prepare func(base string) error 304 // Function that validates directory structure after the test 305 validate func(base string) error 306 baseDir string 307 endDir string 308 expectError bool 309 }{ 310 { 311 name: "all-empty", 312 prepare: func(base string) error { 313 return os.MkdirAll(filepath.Join(base, "a/b/c"), defaultPerm) 314 }, 315 validate: func(base string) error { 316 return validateDirEmpty(filepath.Join(base, "a")) 317 }, 318 baseDir: "a", 319 endDir: "a/b/c", 320 expectError: false, 321 }, 322 { 323 name: "dir-not-empty", 324 prepare: func(base string) error { 325 if err := os.MkdirAll(filepath.Join(base, "a/b/c"), defaultPerm); err != nil { 326 return err 327 } 328 return os.Mkdir(filepath.Join(base, "a/b/d"), defaultPerm) 329 }, 330 validate: func(base string) error { 331 if err := validateDirNotExists(filepath.Join(base, "a/b/c")); err != nil { 332 return err 333 } 334 return validateDirExists(filepath.Join(base, "a/b")) 335 }, 336 baseDir: "a", 337 endDir: "a/b/c", 338 expectError: false, 339 }, 340 { 341 name: "path-not-within-base", 342 prepare: func(base string) error { 343 return os.MkdirAll(filepath.Join(base, "a/b/c"), defaultPerm) 344 }, 345 validate: func(base string) error { 346 return validateDirExists(filepath.Join(base, "a")) 347 }, 348 baseDir: "a", 349 endDir: "b/c", 350 expectError: true, 351 }, 352 { 353 name: "path-already-deleted", 354 prepare: func(base string) error { 355 return nil 356 }, 357 validate: func(base string) error { 358 return nil 359 }, 360 baseDir: "a", 361 endDir: "a/b/c", 362 expectError: false, 363 }, 364 { 365 name: "path-not-dir", 366 prepare: func(base string) error { 367 if err := os.MkdirAll(filepath.Join(base, "a/b"), defaultPerm); err != nil { 368 return err 369 } 370 return ioutil.WriteFile(filepath.Join(base, "a/b", "c"), []byte{}, defaultPerm) 371 }, 372 validate: func(base string) error { 373 if err := validateDirExists(filepath.Join(base, "a/b")); err != nil { 374 return err 375 } 376 return validateFileExists(filepath.Join(base, "a/b/c")) 377 }, 378 baseDir: "a", 379 endDir: "a/b/c", 380 expectError: true, 381 }, 382 } 383 384 for _, test := range tests { 385 klog.V(4).Infof("test %q", test.name) 386 base, err := ioutil.TempDir("", "remove-empty-dirs-"+test.name+"-") 387 if err != nil { 388 t.Fatalf(err.Error()) 389 } 390 if err = test.prepare(base); err != nil { 391 os.RemoveAll(base) 392 t.Fatalf("failed to prepare test %q: %v", test.name, err.Error()) 393 } 394 395 err = removeEmptyDirs(filepath.Join(base, test.baseDir), filepath.Join(base, test.endDir)) 396 if err != nil && !test.expectError { 397 t.Errorf("test %q failed: %v", test.name, err) 398 } 399 if err == nil && test.expectError { 400 t.Errorf("test %q failed: expected error, got success", test.name) 401 } 402 403 if err = test.validate(base); err != nil { 404 t.Errorf("test %q failed validation: %v", test.name, err) 405 } 406 407 os.RemoveAll(base) 408 } 409 } 410 411 func TestCleanSubPaths(t *testing.T) { 412 defaultPerm := os.FileMode(0750) 413 testVol := "vol1" 414 415 tests := []struct { 416 name string 417 // Function that prepares directory structure for the test under given 418 // base. 419 prepare func(base string) ([]mount.MountPoint, error) 420 // Function that validates directory structure after the test 421 validate func(base string) error 422 expectError bool 423 unmount func(path string) error 424 }{ 425 { 426 name: "not-exists", 427 prepare: func(base string) ([]mount.MountPoint, error) { 428 return nil, nil 429 }, 430 validate: func(base string) error { 431 return nil 432 }, 433 expectError: false, 434 }, 435 { 436 name: "subpath-not-mount", 437 prepare: func(base string) ([]mount.MountPoint, error) { 438 return nil, os.MkdirAll(filepath.Join(base, containerSubPathDirectoryName, testVol, "container1", "0"), defaultPerm) 439 }, 440 validate: func(base string) error { 441 return validateDirNotExists(filepath.Join(base, containerSubPathDirectoryName)) 442 }, 443 expectError: false, 444 }, 445 { 446 name: "subpath-file", 447 prepare: func(base string) ([]mount.MountPoint, error) { 448 path := filepath.Join(base, containerSubPathDirectoryName, testVol, "container1") 449 if err := os.MkdirAll(path, defaultPerm); err != nil { 450 return nil, err 451 } 452 return nil, ioutil.WriteFile(filepath.Join(path, "0"), []byte{}, defaultPerm) 453 }, 454 validate: func(base string) error { 455 return validateDirNotExists(filepath.Join(base, containerSubPathDirectoryName)) 456 }, 457 expectError: false, 458 }, 459 { 460 name: "subpath-container-not-dir", 461 prepare: func(base string) ([]mount.MountPoint, error) { 462 path := filepath.Join(base, containerSubPathDirectoryName, testVol) 463 if err := os.MkdirAll(path, defaultPerm); err != nil { 464 return nil, err 465 } 466 return nil, ioutil.WriteFile(filepath.Join(path, "container1"), []byte{}, defaultPerm) 467 }, 468 validate: func(base string) error { 469 return validateDirExists(filepath.Join(base, containerSubPathDirectoryName, testVol)) 470 }, 471 expectError: true, 472 }, 473 { 474 name: "subpath-multiple-container-not-dir", 475 prepare: func(base string) ([]mount.MountPoint, error) { 476 path := filepath.Join(base, containerSubPathDirectoryName, testVol) 477 if err := os.MkdirAll(filepath.Join(path, "container1"), defaultPerm); err != nil { 478 return nil, err 479 } 480 return nil, ioutil.WriteFile(filepath.Join(path, "container2"), []byte{}, defaultPerm) 481 }, 482 validate: func(base string) error { 483 path := filepath.Join(base, containerSubPathDirectoryName, testVol) 484 if err := validateDirNotExists(filepath.Join(path, "container1")); err != nil { 485 return err 486 } 487 return validateFileExists(filepath.Join(path, "container2")) 488 }, 489 expectError: true, 490 }, 491 { 492 name: "subpath-mount", 493 prepare: func(base string) ([]mount.MountPoint, error) { 494 path := filepath.Join(base, containerSubPathDirectoryName, testVol, "container1", "0") 495 if err := os.MkdirAll(path, defaultPerm); err != nil { 496 return nil, err 497 } 498 mounts := []mount.MountPoint{{Device: "/dev/sdb", Path: path}} 499 return mounts, nil 500 }, 501 validate: func(base string) error { 502 return validateDirNotExists(filepath.Join(base, containerSubPathDirectoryName)) 503 }, 504 }, 505 { 506 name: "subpath-mount-multiple", 507 prepare: func(base string) ([]mount.MountPoint, error) { 508 path := filepath.Join(base, containerSubPathDirectoryName, testVol, "container1", "0") 509 path2 := filepath.Join(base, containerSubPathDirectoryName, testVol, "container1", "1") 510 path3 := filepath.Join(base, containerSubPathDirectoryName, testVol, "container2", "1") 511 if err := os.MkdirAll(path, defaultPerm); err != nil { 512 return nil, err 513 } 514 if err := os.MkdirAll(path2, defaultPerm); err != nil { 515 return nil, err 516 } 517 if err := os.MkdirAll(path3, defaultPerm); err != nil { 518 return nil, err 519 } 520 mounts := []mount.MountPoint{ 521 {Device: "/dev/sdb", Path: path}, 522 {Device: "/dev/sdb", Path: path3}, 523 } 524 return mounts, nil 525 }, 526 validate: func(base string) error { 527 return validateDirNotExists(filepath.Join(base, containerSubPathDirectoryName)) 528 }, 529 }, 530 { 531 name: "subpath-mount-multiple-vols", 532 prepare: func(base string) ([]mount.MountPoint, error) { 533 path := filepath.Join(base, containerSubPathDirectoryName, testVol, "container1", "0") 534 path2 := filepath.Join(base, containerSubPathDirectoryName, "vol2", "container1", "1") 535 if err := os.MkdirAll(path, defaultPerm); err != nil { 536 return nil, err 537 } 538 if err := os.MkdirAll(path2, defaultPerm); err != nil { 539 return nil, err 540 } 541 mounts := []mount.MountPoint{ 542 {Device: "/dev/sdb", Path: path}, 543 } 544 return mounts, nil 545 }, 546 validate: func(base string) error { 547 baseSubdir := filepath.Join(base, containerSubPathDirectoryName) 548 if err := validateDirNotExists(filepath.Join(baseSubdir, testVol)); err != nil { 549 return err 550 } 551 return validateDirExists(baseSubdir) 552 }, 553 }, 554 { 555 name: "subpath-with-files", 556 prepare: func(base string) ([]mount.MountPoint, error) { 557 containerPath := filepath.Join(base, containerSubPathDirectoryName, testVol, "container1") 558 if err := os.MkdirAll(containerPath, defaultPerm); err != nil { 559 return nil, err 560 } 561 562 file0 := filepath.Join(containerPath, "0") 563 if err := ioutil.WriteFile(file0, []byte{}, defaultPerm); err != nil { 564 return nil, err 565 } 566 567 dir1 := filepath.Join(containerPath, "1") 568 if err := os.MkdirAll(filepath.Join(dir1, "my-dir-1"), defaultPerm); err != nil { 569 return nil, err 570 } 571 572 dir2 := filepath.Join(containerPath, "2") 573 if err := os.MkdirAll(filepath.Join(dir2, "my-dir-2"), defaultPerm); err != nil { 574 return nil, err 575 } 576 577 file3 := filepath.Join(containerPath, "3") 578 if err := ioutil.WriteFile(file3, []byte{}, defaultPerm); err != nil { 579 return nil, err 580 } 581 582 mounts := []mount.MountPoint{ 583 {Device: "/dev/sdb", Path: file0}, 584 {Device: "/dev/sdc", Path: dir1}, 585 {Device: "/dev/sdd", Path: dir2}, 586 {Device: "/dev/sde", Path: file3}, 587 } 588 return mounts, nil 589 }, 590 unmount: func(mountpath string) error { 591 err := filepath.Walk(mountpath, func(path string, info os.FileInfo, _ error) error { 592 if path == mountpath { 593 // Skip top level directory 594 return nil 595 } 596 597 if err := os.Remove(path); err != nil { 598 return err 599 } 600 return filepath.SkipDir 601 }) 602 if err != nil { 603 return fmt.Errorf("error processing %s: %s", mountpath, err) 604 } 605 606 return nil 607 }, 608 validate: func(base string) error { 609 return validateDirNotExists(filepath.Join(base, containerSubPathDirectoryName)) 610 }, 611 }, 612 } 613 614 for _, test := range tests { 615 klog.V(4).Infof("test %q", test.name) 616 base, err := ioutil.TempDir("", "clean-subpaths-"+test.name+"-") 617 if err != nil { 618 t.Fatalf(err.Error()) 619 } 620 mounts, err := test.prepare(base) 621 if err != nil { 622 os.RemoveAll(base) 623 t.Fatalf("failed to prepare test %q: %v", test.name, err.Error()) 624 } 625 626 fm := mount.NewFakeMounter(mounts) 627 fm.UnmountFunc = test.unmount 628 629 err = doCleanSubPaths(fm, base, testVol) 630 if err != nil && !test.expectError { 631 t.Errorf("test %q failed: %v", test.name, err) 632 } 633 if err == nil && test.expectError { 634 t.Errorf("test %q failed: expected error, got success", test.name) 635 } 636 if err = test.validate(base); err != nil { 637 t.Errorf("test %q failed validation: %v", test.name, err) 638 } 639 640 os.RemoveAll(base) 641 } 642 } 643 644 var ( 645 testVol = "vol1" 646 testPod = "pod0" 647 testContainer = "container0" 648 testSubpath = 1 649 ) 650 651 func setupFakeMounter(testMounts []string) *mount.FakeMounter { 652 mounts := []mount.MountPoint{} 653 for _, mountPoint := range testMounts { 654 mounts = append(mounts, mount.MountPoint{Device: "/foo", Path: mountPoint}) 655 } 656 return mount.NewFakeMounter(mounts) 657 } 658 659 func getTestPaths(base string) (string, string) { 660 return filepath.Join(base, testVol), 661 filepath.Join(base, testPod, containerSubPathDirectoryName, testVol, testContainer, strconv.Itoa(testSubpath)) 662 } 663 664 func TestBindSubPath(t *testing.T) { 665 defaultPerm := os.FileMode(0750) 666 667 tests := []struct { 668 name string 669 // Function that prepares directory structure for the test under given 670 // base. 671 prepare func(base string) ([]string, string, string, error) 672 expectError bool 673 }{ 674 { 675 name: "subpath-dir", 676 prepare: func(base string) ([]string, string, string, error) { 677 volpath, _ := getTestPaths(base) 678 subpath := filepath.Join(volpath, "dir0") 679 return nil, volpath, subpath, os.MkdirAll(subpath, defaultPerm) 680 }, 681 expectError: false, 682 }, 683 { 684 name: "subpath-dir-symlink", 685 prepare: func(base string) ([]string, string, string, error) { 686 volpath, _ := getTestPaths(base) 687 subpath := filepath.Join(volpath, "dir0") 688 if err := os.MkdirAll(subpath, defaultPerm); err != nil { 689 return nil, "", "", err 690 } 691 subpathLink := filepath.Join(volpath, "dirLink") 692 return nil, volpath, subpath, os.Symlink(subpath, subpathLink) 693 }, 694 expectError: false, 695 }, 696 { 697 name: "subpath-file", 698 prepare: func(base string) ([]string, string, string, error) { 699 volpath, _ := getTestPaths(base) 700 subpath := filepath.Join(volpath, "file0") 701 if err := os.MkdirAll(volpath, defaultPerm); err != nil { 702 return nil, "", "", err 703 } 704 return nil, volpath, subpath, ioutil.WriteFile(subpath, []byte{}, defaultPerm) 705 }, 706 expectError: false, 707 }, 708 { 709 name: "subpath-not-exists", 710 prepare: func(base string) ([]string, string, string, error) { 711 volpath, _ := getTestPaths(base) 712 subpath := filepath.Join(volpath, "file0") 713 return nil, volpath, subpath, nil 714 }, 715 expectError: true, 716 }, 717 { 718 name: "subpath-outside", 719 prepare: func(base string) ([]string, string, string, error) { 720 volpath, _ := getTestPaths(base) 721 subpath := filepath.Join(volpath, "dir0") 722 if err := os.MkdirAll(volpath, defaultPerm); err != nil { 723 return nil, "", "", err 724 } 725 return nil, volpath, subpath, os.Symlink(base, subpath) 726 }, 727 expectError: true, 728 }, 729 { 730 name: "subpath-symlink-child-outside", 731 prepare: func(base string) ([]string, string, string, error) { 732 volpath, _ := getTestPaths(base) 733 subpathDir := filepath.Join(volpath, "dir0") 734 subpath := filepath.Join(subpathDir, "child0") 735 if err := os.MkdirAll(subpathDir, defaultPerm); err != nil { 736 return nil, "", "", err 737 } 738 return nil, volpath, subpath, os.Symlink(base, subpath) 739 }, 740 expectError: true, 741 }, 742 { 743 name: "subpath-child-outside-exists", 744 prepare: func(base string) ([]string, string, string, error) { 745 volpath, _ := getTestPaths(base) 746 subpathDir := filepath.Join(volpath, "dir0") 747 child := filepath.Join(base, "child0") 748 subpath := filepath.Join(subpathDir, "child0") 749 if err := os.MkdirAll(volpath, defaultPerm); err != nil { 750 return nil, "", "", err 751 } 752 // touch file outside 753 if err := ioutil.WriteFile(child, []byte{}, defaultPerm); err != nil { 754 return nil, "", "", err 755 } 756 757 // create symlink for subpath dir 758 return nil, volpath, subpath, os.Symlink(base, subpathDir) 759 }, 760 expectError: true, 761 }, 762 { 763 name: "subpath-child-outside-not-exists", 764 prepare: func(base string) ([]string, string, string, error) { 765 volpath, _ := getTestPaths(base) 766 subpathDir := filepath.Join(volpath, "dir0") 767 subpath := filepath.Join(subpathDir, "child0") 768 if err := os.MkdirAll(volpath, defaultPerm); err != nil { 769 return nil, "", "", err 770 } 771 // create symlink for subpath dir 772 return nil, volpath, subpath, os.Symlink(base, subpathDir) 773 }, 774 expectError: true, 775 }, 776 { 777 name: "subpath-child-outside-exists-middle-dir-symlink", 778 prepare: func(base string) ([]string, string, string, error) { 779 volpath, _ := getTestPaths(base) 780 subpathDir := filepath.Join(volpath, "dir0") 781 symlinkDir := filepath.Join(subpathDir, "linkDir0") 782 child := filepath.Join(base, "child0") 783 subpath := filepath.Join(symlinkDir, "child0") 784 if err := os.MkdirAll(subpathDir, defaultPerm); err != nil { 785 return nil, "", "", err 786 } 787 // touch file outside 788 if err := ioutil.WriteFile(child, []byte{}, defaultPerm); err != nil { 789 return nil, "", "", err 790 } 791 792 // create symlink for middle dir 793 return nil, volpath, subpath, os.Symlink(base, symlinkDir) 794 }, 795 expectError: true, 796 }, 797 { 798 name: "subpath-backstepping", 799 prepare: func(base string) ([]string, string, string, error) { 800 volpath, _ := getTestPaths(base) 801 subpath := filepath.Join(volpath, "dir0") 802 symlinkBase := filepath.Join(volpath, "..") 803 if err := os.MkdirAll(volpath, defaultPerm); err != nil { 804 return nil, "", "", err 805 } 806 807 // create symlink for subpath 808 return nil, volpath, subpath, os.Symlink(symlinkBase, subpath) 809 }, 810 expectError: true, 811 }, 812 { 813 name: "subpath-mountdir-already-exists", 814 prepare: func(base string) ([]string, string, string, error) { 815 volpath, subpathMount := getTestPaths(base) 816 if err := os.MkdirAll(subpathMount, defaultPerm); err != nil { 817 return nil, "", "", err 818 } 819 820 subpath := filepath.Join(volpath, "dir0") 821 return nil, volpath, subpath, os.MkdirAll(subpath, defaultPerm) 822 }, 823 expectError: false, 824 }, 825 { 826 name: "subpath-mount-already-exists", 827 prepare: func(base string) ([]string, string, string, error) { 828 volpath, subpathMount := getTestPaths(base) 829 mounts := []string{subpathMount} 830 if err := os.MkdirAll(subpathMount, defaultPerm); err != nil { 831 return nil, "", "", err 832 } 833 834 subpath := filepath.Join(volpath, "dir0") 835 return mounts, volpath, subpath, os.MkdirAll(subpath, defaultPerm) 836 }, 837 expectError: false, 838 }, 839 { 840 name: "mount-unix-socket", 841 prepare: func(base string) ([]string, string, string, error) { 842 volpath, subpathMount := getTestPaths(base) 843 mounts := []string{subpathMount} 844 if err := os.MkdirAll(volpath, defaultPerm); err != nil { 845 return nil, "", "", err 846 } 847 848 socketFile, socketCreateError := createSocketFile(volpath) 849 850 return mounts, volpath, socketFile, socketCreateError 851 }, 852 expectError: false, 853 }, 854 { 855 name: "subpath-mounting-fifo", 856 prepare: func(base string) ([]string, string, string, error) { 857 volpath, subpathMount := getTestPaths(base) 858 mounts := []string{subpathMount} 859 if err := os.MkdirAll(volpath, defaultPerm); err != nil { 860 return nil, "", "", err 861 } 862 863 testFifo := filepath.Join(volpath, "mount_test.fifo") 864 err := syscall.Mkfifo(testFifo, 0) 865 return mounts, volpath, testFifo, err 866 }, 867 expectError: false, 868 }, 869 } 870 871 for _, test := range tests { 872 klog.V(4).Infof("test %q", test.name) 873 base, err := ioutil.TempDir("", "bind-subpath-"+test.name+"-") 874 if err != nil { 875 t.Fatalf(err.Error()) 876 } 877 878 mounts, volPath, subPath, err := test.prepare(base) 879 if err != nil { 880 os.RemoveAll(base) 881 t.Fatalf("failed to prepare test %q: %v", test.name, err.Error()) 882 } 883 884 fm := setupFakeMounter(mounts) 885 886 subpath := Subpath{ 887 VolumeMountIndex: testSubpath, 888 Path: subPath, 889 VolumeName: testVol, 890 VolumePath: volPath, 891 PodDir: filepath.Join(base, "pod0"), 892 ContainerName: testContainer, 893 } 894 895 _, subpathMount := getTestPaths(base) 896 bindPathTarget, err := doBindSubPath(fm, subpath) 897 if test.expectError { 898 if err == nil { 899 t.Errorf("test %q failed: expected error, got success", test.name) 900 } 901 if bindPathTarget != "" { 902 t.Errorf("test %q failed: expected empty bindPathTarget, got %v", test.name, bindPathTarget) 903 } 904 if err = validateDirNotExists(subpathMount); err != nil { 905 t.Errorf("test %q failed: %v", test.name, err) 906 } 907 } 908 if !test.expectError { 909 if err != nil { 910 t.Errorf("test %q failed: %v", test.name, err) 911 } 912 if bindPathTarget != subpathMount { 913 t.Errorf("test %q failed: expected bindPathTarget %v, got %v", test.name, subpathMount, bindPathTarget) 914 } 915 if err = validateFileExists(subpathMount); err != nil { 916 t.Errorf("test %q failed: %v", test.name, err) 917 } 918 } 919 920 os.RemoveAll(base) 921 } 922 } 923 924 func TestSubpath_PrepareSafeSubpath(t *testing.T) { 925 //complete code 926 defaultPerm := os.FileMode(0750) 927 928 tests := []struct { 929 name string 930 // Function that prepares directory structure for the test under given 931 // base. 932 prepare func(base string) ([]string, string, string, error) 933 expectError bool 934 expectAction []mount.FakeAction 935 mountExists bool 936 }{ 937 { 938 name: "subpath-mount-already-exists-with-mismatching-mount", 939 prepare: func(base string) ([]string, string, string, error) { 940 volpath, subpathMount := getTestPaths(base) 941 mounts := []string{subpathMount} 942 if err := os.MkdirAll(subpathMount, defaultPerm); err != nil { 943 return nil, "", "", err 944 } 945 946 subpath := filepath.Join(volpath, "dir0") 947 return mounts, volpath, subpath, os.MkdirAll(subpath, defaultPerm) 948 }, 949 expectError: false, 950 expectAction: []mount.FakeAction{{Action: "unmount"}}, 951 mountExists: false, 952 }, 953 { 954 name: "subpath-mount-already-exists-with-samefile", 955 prepare: func(base string) ([]string, string, string, error) { 956 volpath, subpathMount := getTestPaths(base) 957 mounts := []string{subpathMount} 958 subpathMountRoot := filepath.Dir(subpathMount) 959 960 if err := os.MkdirAll(subpathMountRoot, defaultPerm); err != nil { 961 return nil, "", "", err 962 } 963 targetFile, err := os.Create(subpathMount) 964 if err != nil { 965 return nil, "", "", err 966 } 967 defer targetFile.Close() 968 969 if err := os.MkdirAll(volpath, defaultPerm); err != nil { 970 return nil, "", "", err 971 } 972 subpath := filepath.Join(volpath, "file0") 973 // using hard link to simulate bind mounts 974 err = os.Link(subpathMount, subpath) 975 if err != nil { 976 return nil, "", "", err 977 } 978 return mounts, volpath, subpath, nil 979 }, 980 expectError: false, 981 expectAction: []mount.FakeAction{}, 982 mountExists: true, 983 }, 984 } 985 for _, test := range tests { 986 klog.V(4).Infof("test %q", test.name) 987 base, err := ioutil.TempDir("", "bind-subpath-"+test.name+"-") 988 if err != nil { 989 t.Fatalf(err.Error()) 990 } 991 defer os.RemoveAll(base) 992 993 mounts, volPath, subPath, err := test.prepare(base) 994 if err != nil { 995 os.RemoveAll(base) 996 t.Fatalf("failed to prepare test %q: %v", test.name, err.Error()) 997 } 998 999 fm := setupFakeMounter(mounts) 1000 1001 subpath := Subpath{ 1002 VolumeMountIndex: testSubpath, 1003 Path: subPath, 1004 VolumeName: testVol, 1005 VolumePath: volPath, 1006 PodDir: filepath.Join(base, "pod0"), 1007 ContainerName: testContainer, 1008 } 1009 1010 _, subpathMount := getTestPaths(base) 1011 bindMountExists, bindPathTarget, err := prepareSubpathTarget(fm, subpath) 1012 1013 if bindMountExists != test.mountExists { 1014 t.Errorf("test %q failed: expected bindMountExists %v, got %v", test.name, test.mountExists, bindMountExists) 1015 } 1016 1017 logActions := fm.GetLog() 1018 if len(test.expectAction) == 0 && len(logActions) > 0 { 1019 t.Errorf("test %q failed: expected no actions, got %v", test.name, logActions) 1020 } 1021 1022 if len(test.expectAction) > 0 { 1023 foundMatchingAction := false 1024 testAction := test.expectAction[0] 1025 for _, action := range logActions { 1026 if action.Action == testAction.Action { 1027 foundMatchingAction = true 1028 break 1029 } 1030 } 1031 if !foundMatchingAction { 1032 t.Errorf("test %q failed: expected action %q, got %v", test.name, testAction.Action, logActions) 1033 } 1034 } 1035 1036 if test.expectError { 1037 if err == nil { 1038 t.Errorf("test %q failed: expected error, got success", test.name) 1039 } 1040 if bindPathTarget != "" { 1041 t.Errorf("test %q failed: expected empty bindPathTarget, got %v", test.name, bindPathTarget) 1042 } 1043 if err = validateDirNotExists(subpathMount); err != nil { 1044 t.Errorf("test %q failed: %v", test.name, err) 1045 } 1046 } 1047 if !test.expectError { 1048 if err != nil { 1049 t.Errorf("test %q failed: %v", test.name, err) 1050 } 1051 if bindPathTarget != subpathMount { 1052 t.Errorf("test %q failed: expected bindPathTarget %v, got %v", test.name, subpathMount, bindPathTarget) 1053 } 1054 if err = validateFileExists(subpathMount); err != nil { 1055 t.Errorf("test %q failed: %v", test.name, err) 1056 } 1057 } 1058 } 1059 } 1060 1061 func TestSafeOpen(t *testing.T) { 1062 defaultPerm := os.FileMode(0750) 1063 1064 tests := []struct { 1065 name string 1066 // Function that prepares directory structure for the test under given 1067 // base. 1068 prepare func(base string) error 1069 path string 1070 expectError bool 1071 }{ 1072 { 1073 "directory-does-not-exist", 1074 func(base string) error { 1075 return nil 1076 }, 1077 "test/directory", 1078 true, 1079 }, 1080 { 1081 "directory-exists", 1082 func(base string) error { 1083 return os.MkdirAll(filepath.Join(base, "test/directory"), 0750) 1084 }, 1085 "test/directory", 1086 false, 1087 }, 1088 { 1089 "escape-base-using-dots", 1090 func(base string) error { 1091 return nil 1092 }, 1093 "..", 1094 true, 1095 }, 1096 { 1097 "escape-base-using-dots-2", 1098 func(base string) error { 1099 return os.MkdirAll(filepath.Join(base, "test"), 0750) 1100 }, 1101 "test/../../..", 1102 true, 1103 }, 1104 { 1105 "symlink", 1106 func(base string) error { 1107 if err := os.MkdirAll(filepath.Join(base, "destination"), defaultPerm); err != nil { 1108 return err 1109 } 1110 return os.Symlink("destination", filepath.Join(base, "test")) 1111 }, 1112 "test", 1113 true, 1114 }, 1115 { 1116 "symlink-nested", 1117 func(base string) error { 1118 if err := os.MkdirAll(filepath.Join(base, "dir1/dir2"), defaultPerm); err != nil { 1119 return err 1120 } 1121 return os.Symlink("dir1", filepath.Join(base, "dir1/dir2/test")) 1122 }, 1123 "test", 1124 true, 1125 }, 1126 { 1127 "symlink-loop", 1128 func(base string) error { 1129 return os.Symlink("test", filepath.Join(base, "test")) 1130 }, 1131 "test", 1132 true, 1133 }, 1134 { 1135 "symlink-not-exists", 1136 func(base string) error { 1137 return os.Symlink("non-existing", filepath.Join(base, "test")) 1138 }, 1139 "test", 1140 true, 1141 }, 1142 { 1143 "non-directory", 1144 func(base string) error { 1145 return ioutil.WriteFile(filepath.Join(base, "test"), []byte{}, defaultPerm) 1146 }, 1147 "test/directory", 1148 true, 1149 }, 1150 { 1151 "non-directory-final", 1152 func(base string) error { 1153 return ioutil.WriteFile(filepath.Join(base, "test"), []byte{}, defaultPerm) 1154 }, 1155 "test", 1156 false, 1157 }, 1158 { 1159 "escape-with-relative-symlink", 1160 func(base string) error { 1161 if err := os.MkdirAll(filepath.Join(base, "dir"), defaultPerm); err != nil { 1162 return err 1163 } 1164 if err := os.MkdirAll(filepath.Join(base, "exists"), defaultPerm); err != nil { 1165 return err 1166 } 1167 return os.Symlink("../exists", filepath.Join(base, "dir/test")) 1168 }, 1169 "dir/test", 1170 true, 1171 }, 1172 { 1173 "escape-with-relative-symlink-not-exists", 1174 func(base string) error { 1175 if err := os.MkdirAll(filepath.Join(base, "dir"), defaultPerm); err != nil { 1176 return err 1177 } 1178 return os.Symlink("../not-exists", filepath.Join(base, "dir/test")) 1179 }, 1180 "dir/test", 1181 true, 1182 }, 1183 { 1184 "escape-with-symlink", 1185 func(base string) error { 1186 return os.Symlink("/", filepath.Join(base, "test")) 1187 }, 1188 "test", 1189 true, 1190 }, 1191 { 1192 "mount-unix-socket", 1193 func(base string) error { 1194 socketFile, socketError := createSocketFile(base) 1195 1196 if socketError != nil { 1197 return fmt.Errorf("error preparing socket file %s with %w", socketFile, socketError) 1198 } 1199 return nil 1200 }, 1201 "mt.sock", 1202 false, 1203 }, 1204 { 1205 "mounting-unix-socket-in-middle", 1206 func(base string) error { 1207 testSocketFile, socketError := createSocketFile(base) 1208 1209 if socketError != nil { 1210 return fmt.Errorf("error preparing socket file %s with %w", testSocketFile, socketError) 1211 } 1212 return nil 1213 }, 1214 "mt.sock/bar", 1215 true, 1216 }, 1217 } 1218 1219 for _, test := range tests { 1220 klog.V(4).Infof("test %q", test.name) 1221 base, err := ioutil.TempDir("", "safe-open-"+test.name+"-") 1222 if err != nil { 1223 t.Fatalf(err.Error()) 1224 } 1225 1226 test.prepare(base) 1227 pathToCreate := filepath.Join(base, test.path) 1228 fd, err := doSafeOpen(pathToCreate, base) 1229 if err != nil && !test.expectError { 1230 t.Errorf("test %q: %s", test.name, err) 1231 } 1232 if err != nil { 1233 klog.Infof("got error: %s", err) 1234 } 1235 if err == nil && test.expectError { 1236 t.Errorf("test %q: expected error, got none", test.name) 1237 } 1238 1239 syscall.Close(fd) 1240 os.RemoveAll(base) 1241 } 1242 } 1243 1244 func createSocketFile(socketDir string) (string, error) { 1245 testSocketFile := filepath.Join(socketDir, "mt.sock") 1246 1247 // Switch to volume path and create the socket file 1248 // socket file can not have length of more than 108 character 1249 // and hence we must use relative path 1250 oldDir, _ := os.Getwd() 1251 1252 err := os.Chdir(socketDir) 1253 if err != nil { 1254 return "", err 1255 } 1256 defer func() { 1257 os.Chdir(oldDir) 1258 }() 1259 _, socketCreateError := net.Listen("unix", "mt.sock") 1260 return testSocketFile, socketCreateError 1261 } 1262 1263 func TestFindExistingPrefix(t *testing.T) { 1264 defaultPerm := os.FileMode(0750) 1265 tests := []struct { 1266 name string 1267 // Function that prepares directory structure for the test under given 1268 // base. 1269 prepare func(base string) error 1270 path string 1271 expectedPath string 1272 expectedDirs []string 1273 expectError bool 1274 }{ 1275 { 1276 "directory-does-not-exist", 1277 func(base string) error { 1278 return nil 1279 }, 1280 "directory", 1281 "", 1282 []string{"directory"}, 1283 false, 1284 }, 1285 { 1286 "directory-exists", 1287 func(base string) error { 1288 return os.MkdirAll(filepath.Join(base, "test/directory"), 0750) 1289 }, 1290 "test/directory", 1291 "test/directory", 1292 []string{}, 1293 false, 1294 }, 1295 { 1296 "follow-symlinks", 1297 func(base string) error { 1298 if err := os.MkdirAll(filepath.Join(base, "destination/directory"), defaultPerm); err != nil { 1299 return err 1300 } 1301 return os.Symlink("destination", filepath.Join(base, "test")) 1302 }, 1303 "test/directory", 1304 "test/directory", 1305 []string{}, 1306 false, 1307 }, 1308 { 1309 "follow-symlink-loop", 1310 func(base string) error { 1311 return os.Symlink("test", filepath.Join(base, "test")) 1312 }, 1313 "test/directory", 1314 "", 1315 nil, 1316 true, 1317 }, 1318 { 1319 "follow-symlink-multiple follow", 1320 func(base string) error { 1321 /* test1/dir points to test2 and test2/dir points to test1 */ 1322 if err := os.MkdirAll(filepath.Join(base, "test1"), defaultPerm); err != nil { 1323 return err 1324 } 1325 if err := os.MkdirAll(filepath.Join(base, "test2"), defaultPerm); err != nil { 1326 return err 1327 } 1328 if err := os.Symlink(filepath.Join(base, "test2"), filepath.Join(base, "test1/dir")); err != nil { 1329 return err 1330 } 1331 if err := os.Symlink(filepath.Join(base, "test1"), filepath.Join(base, "test2/dir")); err != nil { 1332 return err 1333 } 1334 return nil 1335 }, 1336 "test1/dir/dir/foo/bar", 1337 "test1/dir/dir", 1338 []string{"foo", "bar"}, 1339 false, 1340 }, 1341 { 1342 "danglink-symlink", 1343 func(base string) error { 1344 return os.Symlink("non-existing", filepath.Join(base, "test")) 1345 }, 1346 // OS returns IsNotExist error both for dangling symlink and for 1347 // non-existing directory. 1348 "test/directory", 1349 "", 1350 []string{"test", "directory"}, 1351 false, 1352 }, 1353 { 1354 "with-fifo-in-middle", 1355 func(base string) error { 1356 testFifo := filepath.Join(base, "mount_test.fifo") 1357 return syscall.Mkfifo(testFifo, 0) 1358 }, 1359 "mount_test.fifo/directory", 1360 "", 1361 nil, 1362 true, 1363 }, 1364 } 1365 1366 for _, test := range tests { 1367 klog.V(4).Infof("test %q", test.name) 1368 base, err := ioutil.TempDir("", "find-prefix-"+test.name+"-") 1369 if err != nil { 1370 t.Fatalf(err.Error()) 1371 } 1372 test.prepare(base) 1373 path := filepath.Join(base, test.path) 1374 existingPath, dirs, err := findExistingPrefix(base, path) 1375 if err != nil && !test.expectError { 1376 t.Errorf("test %q: %s", test.name, err) 1377 } 1378 if err != nil { 1379 klog.Infof("got error: %s", err) 1380 } 1381 if err == nil && test.expectError { 1382 t.Errorf("test %q: expected error, got none", test.name) 1383 } 1384 1385 fullExpectedPath := filepath.Join(base, test.expectedPath) 1386 if existingPath != fullExpectedPath { 1387 t.Errorf("test %q: expected path %q, got %q", test.name, fullExpectedPath, existingPath) 1388 } 1389 if !reflect.DeepEqual(dirs, test.expectedDirs) { 1390 t.Errorf("test %q: expected dirs %v, got %v", test.name, test.expectedDirs, dirs) 1391 } 1392 os.RemoveAll(base) 1393 } 1394 } 1395 1396 func validateDirEmpty(dir string) error { 1397 files, err := ioutil.ReadDir(dir) 1398 if err != nil { 1399 return err 1400 } 1401 1402 if len(files) != 0 { 1403 return fmt.Errorf("directory %q is not empty", dir) 1404 } 1405 return nil 1406 } 1407 1408 func validateDirExists(dir string) error { 1409 _, err := ioutil.ReadDir(dir) 1410 if err != nil { 1411 return err 1412 } 1413 return nil 1414 } 1415 1416 func validateDirNotExists(dir string) error { 1417 _, err := ioutil.ReadDir(dir) 1418 if os.IsNotExist(err) { 1419 return nil 1420 } 1421 if err != nil { 1422 return err 1423 } 1424 return fmt.Errorf("dir %q still exists", dir) 1425 } 1426 1427 func validateFileExists(file string) error { 1428 if _, err := os.Stat(file); err != nil { 1429 return err 1430 } 1431 return nil 1432 }