github.com/zhuohuang-hust/src-cbuild@v0.0.0-20230105071821-c7aab3e7c840/mergeCode/runc/libcontainer/rootfs_linux.go (about) 1 // +build linux 2 3 package libcontainer 4 5 import ( 6 "fmt" 7 "io" 8 "io/ioutil" 9 "os" 10 "os/exec" 11 "path" 12 "path/filepath" 13 "strings" 14 "syscall" 15 "time" 16 17 "github.com/docker/docker/pkg/mount" 18 "github.com/docker/docker/pkg/symlink" 19 "github.com/mrunalp/fileutils" 20 "github.com/opencontainers/runc/libcontainer/cgroups" 21 "github.com/opencontainers/runc/libcontainer/configs" 22 "github.com/opencontainers/runc/libcontainer/label" 23 "github.com/opencontainers/runc/libcontainer/system" 24 libcontainerUtils "github.com/opencontainers/runc/libcontainer/utils" 25 ) 26 27 const defaultMountFlags = syscall.MS_NOEXEC | syscall.MS_NOSUID | syscall.MS_NODEV 28 29 // needsSetupDev returns true if /dev needs to be set up. 30 func needsSetupDev(config *configs.Config) bool { 31 for _, m := range config.Mounts { 32 if m.Device == "bind" && libcontainerUtils.CleanPath(m.Destination) == "/dev" { 33 return false 34 } 35 } 36 return true 37 } 38 39 // setupRootfs sets up the devices, mount points, and filesystems for use inside a 40 // new mount namespace. 41 func setupRootfs(config *configs.Config, console *linuxConsole, pipe io.ReadWriter) (err error) { 42 if err := prepareRoot(config); err != nil { 43 return newSystemErrorWithCause(err, "preparing rootfs") 44 } 45 46 setupDev := needsSetupDev(config) 47 for _, m := range config.Mounts { 48 for _, precmd := range m.PremountCmds { 49 if err := mountCmd(precmd); err != nil { 50 return newSystemErrorWithCause(err, "running premount command") 51 } 52 } 53 if err := mountToRootfs(m, config.Rootfs, config.MountLabel); err != nil { 54 return newSystemErrorWithCausef(err, "mounting %q to rootfs %q at %q", m.Source, config.Rootfs, m.Destination) 55 } 56 57 for _, postcmd := range m.PostmountCmds { 58 if err := mountCmd(postcmd); err != nil { 59 return newSystemErrorWithCause(err, "running postmount command") 60 } 61 } 62 } 63 if setupDev { 64 if err := createDevices(config); err != nil { 65 return newSystemErrorWithCause(err, "creating device nodes") 66 } 67 if err := setupPtmx(config, console); err != nil { 68 return newSystemErrorWithCause(err, "setting up ptmx") 69 } 70 if err := setupDevSymlinks(config.Rootfs); err != nil { 71 return newSystemErrorWithCause(err, "setting up /dev symlinks") 72 } 73 } 74 // Signal the parent to run the pre-start hooks. 75 // The hooks are run after the mounts are setup, but before we switch to the new 76 // root, so that the old root is still available in the hooks for any mount 77 // manipulations. 78 if err := syncParentHooks(pipe); err != nil { 79 return err 80 } 81 if err := syscall.Chdir(config.Rootfs); err != nil { 82 return newSystemErrorWithCausef(err, "changing dir to %q", config.Rootfs) 83 } 84 if config.NoPivotRoot { 85 err = msMoveRoot(config.Rootfs) 86 } else { 87 err = pivotRoot(config.Rootfs) 88 } 89 if err != nil { 90 return newSystemErrorWithCause(err, "jailing process inside rootfs") 91 } 92 if setupDev { 93 if err := reOpenDevNull(); err != nil { 94 return newSystemErrorWithCause(err, "reopening /dev/null inside container") 95 } 96 } 97 // remount dev as ro if specified 98 for _, m := range config.Mounts { 99 if libcontainerUtils.CleanPath(m.Destination) == "/dev" { 100 if m.Flags&syscall.MS_RDONLY != 0 { 101 if err := remountReadonly(m.Destination); err != nil { 102 return newSystemErrorWithCausef(err, "remounting %q as readonly", m.Destination) 103 } 104 } 105 break 106 } 107 } 108 // set rootfs ( / ) as readonly 109 if config.Readonlyfs { 110 if err := setReadonly(); err != nil { 111 return newSystemErrorWithCause(err, "setting rootfs as readonly") 112 } 113 } 114 syscall.Umask(0022) 115 return nil 116 } 117 118 func mountCmd(cmd configs.Command) error { 119 command := exec.Command(cmd.Path, cmd.Args[:]...) 120 command.Env = cmd.Env 121 command.Dir = cmd.Dir 122 if out, err := command.CombinedOutput(); err != nil { 123 return fmt.Errorf("%#v failed: %s: %v", cmd, string(out), err) 124 } 125 return nil 126 } 127 128 func mountToRootfs(m *configs.Mount, rootfs, mountLabel string) error { 129 var ( 130 dest = m.Destination 131 ) 132 if !strings.HasPrefix(dest, rootfs) { 133 dest = filepath.Join(rootfs, dest) 134 } 135 136 switch m.Device { 137 case "proc", "sysfs": 138 if err := os.MkdirAll(dest, 0755); err != nil { 139 return err 140 } 141 // Selinux kernels do not support labeling of /proc or /sys 142 return mountPropagate(m, rootfs, "") 143 case "mqueue": 144 if err := os.MkdirAll(dest, 0755); err != nil { 145 return err 146 } 147 if err := mountPropagate(m, rootfs, mountLabel); err != nil { 148 // older kernels do not support labeling of /dev/mqueue 149 if err := mountPropagate(m, rootfs, ""); err != nil { 150 return err 151 } 152 return label.SetFileLabel(dest, mountLabel) 153 } 154 return nil 155 case "tmpfs": 156 copyUp := m.Extensions&configs.EXT_COPYUP == configs.EXT_COPYUP 157 tmpDir := "" 158 stat, err := os.Stat(dest) 159 if err != nil { 160 if err := os.MkdirAll(dest, 0755); err != nil { 161 return err 162 } 163 } 164 if copyUp { 165 tmpDir, err = ioutil.TempDir("/tmp", "runctmpdir") 166 if err != nil { 167 return newSystemErrorWithCause(err, "tmpcopyup: failed to create tmpdir") 168 } 169 defer os.RemoveAll(tmpDir) 170 m.Destination = tmpDir 171 } 172 if err := mountPropagate(m, rootfs, mountLabel); err != nil { 173 return err 174 } 175 if copyUp { 176 if err := fileutils.CopyDirectory(dest, tmpDir); err != nil { 177 errMsg := fmt.Errorf("tmpcopyup: failed to copy %s to %s: %v", dest, tmpDir, err) 178 if err1 := syscall.Unmount(tmpDir, syscall.MNT_DETACH); err1 != nil { 179 return newSystemErrorWithCausef(err1, "tmpcopyup: %v: failed to unmount", errMsg) 180 } 181 return errMsg 182 } 183 if err := syscall.Mount(tmpDir, dest, "", syscall.MS_MOVE, ""); err != nil { 184 errMsg := fmt.Errorf("tmpcopyup: failed to move mount %s to %s: %v", tmpDir, dest, err) 185 if err1 := syscall.Unmount(tmpDir, syscall.MNT_DETACH); err1 != nil { 186 return newSystemErrorWithCausef(err1, "tmpcopyup: %v: failed to unmount", errMsg) 187 } 188 return errMsg 189 } 190 } 191 if stat != nil { 192 if err = os.Chmod(dest, stat.Mode()); err != nil { 193 return err 194 } 195 } 196 return nil 197 case "bind": 198 stat, err := os.Stat(m.Source) 199 if err != nil { 200 // error out if the source of a bind mount does not exist as we will be 201 // unable to bind anything to it. 202 return err 203 } 204 // ensure that the destination of the bind mount is resolved of symlinks at mount time because 205 // any previous mounts can invalidate the next mount's destination. 206 // this can happen when a user specifies mounts within other mounts to cause breakouts or other 207 // evil stuff to try to escape the container's rootfs. 208 if dest, err = symlink.FollowSymlinkInScope(filepath.Join(rootfs, m.Destination), rootfs); err != nil { 209 return err 210 } 211 if err := checkMountDestination(rootfs, dest); err != nil { 212 return err 213 } 214 // update the mount with the correct dest after symlinks are resolved. 215 m.Destination = dest 216 if err := createIfNotExists(dest, stat.IsDir()); err != nil { 217 return err 218 } 219 if err := mountPropagate(m, rootfs, mountLabel); err != nil { 220 return err 221 } 222 // bind mount won't change mount options, we need remount to make mount options effective. 223 // first check that we have non-default options required before attempting a remount 224 if m.Flags&^(syscall.MS_REC|syscall.MS_REMOUNT|syscall.MS_BIND) != 0 { 225 // only remount if unique mount options are set 226 if err := remount(m, rootfs); err != nil { 227 return err 228 } 229 } 230 231 if m.Relabel != "" { 232 if err := label.Validate(m.Relabel); err != nil { 233 return err 234 } 235 shared := label.IsShared(m.Relabel) 236 if err := label.Relabel(m.Source, mountLabel, shared); err != nil { 237 return err 238 } 239 } 240 case "cgroup": 241 binds, err := getCgroupMounts(m) 242 if err != nil { 243 return err 244 } 245 var merged []string 246 for _, b := range binds { 247 ss := filepath.Base(b.Destination) 248 if strings.Contains(ss, ",") { 249 merged = append(merged, ss) 250 } 251 } 252 tmpfs := &configs.Mount{ 253 Source: "tmpfs", 254 Device: "tmpfs", 255 Destination: m.Destination, 256 Flags: defaultMountFlags, 257 Data: "mode=755", 258 PropagationFlags: m.PropagationFlags, 259 } 260 if err := mountToRootfs(tmpfs, rootfs, mountLabel); err != nil { 261 return err 262 } 263 for _, b := range binds { 264 if err := mountToRootfs(b, rootfs, mountLabel); err != nil { 265 return err 266 } 267 } 268 for _, mc := range merged { 269 for _, ss := range strings.Split(mc, ",") { 270 // symlink(2) is very dumb, it will just shove the path into 271 // the link and doesn't do any checks or relative path 272 // conversion. Also, don't error out if the cgroup already exists. 273 if err := os.Symlink(mc, filepath.Join(rootfs, m.Destination, ss)); err != nil && !os.IsExist(err) { 274 return err 275 } 276 } 277 } 278 if m.Flags&syscall.MS_RDONLY != 0 { 279 // remount cgroup root as readonly 280 mcgrouproot := &configs.Mount{ 281 Source: m.Destination, 282 Device: "bind", 283 Destination: m.Destination, 284 Flags: defaultMountFlags | syscall.MS_RDONLY | syscall.MS_BIND, 285 } 286 if err := remount(mcgrouproot, rootfs); err != nil { 287 return err 288 } 289 } 290 default: 291 if err := os.MkdirAll(dest, 0755); err != nil { 292 return err 293 } 294 return mountPropagate(m, rootfs, mountLabel) 295 } 296 return nil 297 } 298 299 func getCgroupMounts(m *configs.Mount) ([]*configs.Mount, error) { 300 mounts, err := cgroups.GetCgroupMounts(false) 301 if err != nil { 302 return nil, err 303 } 304 305 cgroupPaths, err := cgroups.ParseCgroupFile("/proc/self/cgroup") 306 if err != nil { 307 return nil, err 308 } 309 310 var binds []*configs.Mount 311 312 for _, mm := range mounts { 313 dir, err := mm.GetThisCgroupDir(cgroupPaths) 314 if err != nil { 315 return nil, err 316 } 317 relDir, err := filepath.Rel(mm.Root, dir) 318 if err != nil { 319 return nil, err 320 } 321 binds = append(binds, &configs.Mount{ 322 Device: "bind", 323 Source: filepath.Join(mm.Mountpoint, relDir), 324 Destination: filepath.Join(m.Destination, strings.Join(mm.Subsystems, ",")), 325 Flags: syscall.MS_BIND | syscall.MS_REC | m.Flags, 326 PropagationFlags: m.PropagationFlags, 327 }) 328 } 329 330 return binds, nil 331 } 332 333 // checkMountDestination checks to ensure that the mount destination is not over the top of /proc. 334 // dest is required to be an abs path and have any symlinks resolved before calling this function. 335 func checkMountDestination(rootfs, dest string) error { 336 invalidDestinations := []string{ 337 "/proc", 338 } 339 // White list, it should be sub directories of invalid destinations 340 validDestinations := []string{ 341 // These entries can be bind mounted by files emulated by fuse, 342 // so commands like top, free displays stats in container. 343 "/proc/cpuinfo", 344 "/proc/diskstats", 345 "/proc/meminfo", 346 "/proc/stat", 347 "/proc/swaps", 348 "/proc/uptime", 349 "/proc/net/dev", 350 } 351 for _, valid := range validDestinations { 352 path, err := filepath.Rel(filepath.Join(rootfs, valid), dest) 353 if err != nil { 354 return err 355 } 356 if path == "." { 357 return nil 358 } 359 } 360 for _, invalid := range invalidDestinations { 361 path, err := filepath.Rel(filepath.Join(rootfs, invalid), dest) 362 if err != nil { 363 return err 364 } 365 if path == "." || !strings.HasPrefix(path, "..") { 366 return fmt.Errorf("%q cannot be mounted because it is located inside %q", dest, invalid) 367 } 368 } 369 return nil 370 } 371 372 func setupDevSymlinks(rootfs string) error { 373 var links = [][2]string{ 374 {"/proc/self/fd", "/dev/fd"}, 375 {"/proc/self/fd/0", "/dev/stdin"}, 376 {"/proc/self/fd/1", "/dev/stdout"}, 377 {"/proc/self/fd/2", "/dev/stderr"}, 378 } 379 // kcore support can be toggled with CONFIG_PROC_KCORE; only create a symlink 380 // in /dev if it exists in /proc. 381 if _, err := os.Stat("/proc/kcore"); err == nil { 382 links = append(links, [2]string{"/proc/kcore", "/dev/core"}) 383 } 384 for _, link := range links { 385 var ( 386 src = link[0] 387 dst = filepath.Join(rootfs, link[1]) 388 ) 389 if err := os.Symlink(src, dst); err != nil && !os.IsExist(err) { 390 return fmt.Errorf("symlink %s %s %s", src, dst, err) 391 } 392 } 393 return nil 394 } 395 396 // If stdin, stdout, and/or stderr are pointing to `/dev/null` in the parent's rootfs 397 // this method will make them point to `/dev/null` in this container's rootfs. This 398 // needs to be called after we chroot/pivot into the container's rootfs so that any 399 // symlinks are resolved locally. 400 func reOpenDevNull() error { 401 var stat, devNullStat syscall.Stat_t 402 file, err := os.OpenFile("/dev/null", os.O_RDWR, 0) 403 if err != nil { 404 return fmt.Errorf("Failed to open /dev/null - %s", err) 405 } 406 defer file.Close() 407 if err := syscall.Fstat(int(file.Fd()), &devNullStat); err != nil { 408 return err 409 } 410 for fd := 0; fd < 3; fd++ { 411 if err := syscall.Fstat(fd, &stat); err != nil { 412 return err 413 } 414 if stat.Rdev == devNullStat.Rdev { 415 // Close and re-open the fd. 416 if err := syscall.Dup3(int(file.Fd()), fd, 0); err != nil { 417 return err 418 } 419 } 420 } 421 return nil 422 } 423 424 // Create the device nodes in the container. 425 func createDevices(config *configs.Config) error { 426 useBindMount := system.RunningInUserNS() || config.Namespaces.Contains(configs.NEWUSER) 427 oldMask := syscall.Umask(0000) 428 for _, node := range config.Devices { 429 // containers running in a user namespace are not allowed to mknod 430 // devices so we can just bind mount it from the host. 431 if err := createDeviceNode(config.Rootfs, node, useBindMount); err != nil { 432 syscall.Umask(oldMask) 433 return err 434 } 435 } 436 syscall.Umask(oldMask) 437 return nil 438 } 439 440 func bindMountDeviceNode(dest string, node *configs.Device) error { 441 f, err := os.Create(dest) 442 if err != nil && !os.IsExist(err) { 443 return err 444 } 445 if f != nil { 446 f.Close() 447 } 448 return syscall.Mount(node.Path, dest, "bind", syscall.MS_BIND, "") 449 } 450 451 // Creates the device node in the rootfs of the container. 452 func createDeviceNode(rootfs string, node *configs.Device, bind bool) error { 453 dest := filepath.Join(rootfs, node.Path) 454 if err := os.MkdirAll(filepath.Dir(dest), 0755); err != nil { 455 return err 456 } 457 458 if bind { 459 return bindMountDeviceNode(dest, node) 460 } 461 if err := mknodDevice(dest, node); err != nil { 462 if os.IsExist(err) { 463 return nil 464 } else if os.IsPermission(err) { 465 return bindMountDeviceNode(dest, node) 466 } 467 return err 468 } 469 return nil 470 } 471 472 func mknodDevice(dest string, node *configs.Device) error { 473 fileMode := node.FileMode 474 switch node.Type { 475 case 'c': 476 fileMode |= syscall.S_IFCHR 477 case 'b': 478 fileMode |= syscall.S_IFBLK 479 default: 480 return fmt.Errorf("%c is not a valid device type for device %s", node.Type, node.Path) 481 } 482 if err := syscall.Mknod(dest, uint32(fileMode), node.Mkdev()); err != nil { 483 return err 484 } 485 return syscall.Chown(dest, int(node.Uid), int(node.Gid)) 486 } 487 488 func getMountInfo(mountinfo []*mount.Info, dir string) *mount.Info { 489 for _, m := range mountinfo { 490 if m.Mountpoint == dir { 491 return m 492 } 493 } 494 return nil 495 } 496 497 // Get the parent mount point of directory passed in as argument. Also return 498 // optional fields. 499 func getParentMount(rootfs string) (string, string, error) { 500 var path string 501 502 mountinfos, err := mount.GetMounts() 503 if err != nil { 504 return "", "", err 505 } 506 507 mountinfo := getMountInfo(mountinfos, rootfs) 508 if mountinfo != nil { 509 return rootfs, mountinfo.Optional, nil 510 } 511 512 path = rootfs 513 for { 514 path = filepath.Dir(path) 515 516 mountinfo = getMountInfo(mountinfos, path) 517 if mountinfo != nil { 518 return path, mountinfo.Optional, nil 519 } 520 521 if path == "/" { 522 break 523 } 524 } 525 526 // If we are here, we did not find parent mount. Something is wrong. 527 return "", "", fmt.Errorf("Could not find parent mount of %s", rootfs) 528 } 529 530 // Make parent mount private if it was shared 531 func rootfsParentMountPrivate(rootfs string) error { 532 sharedMount := false 533 534 parentMount, optionalOpts, err := getParentMount(rootfs) 535 if err != nil { 536 return err 537 } 538 539 optsSplit := strings.Split(optionalOpts, " ") 540 for _, opt := range optsSplit { 541 if strings.HasPrefix(opt, "shared:") { 542 sharedMount = true 543 break 544 } 545 } 546 547 // Make parent mount PRIVATE if it was shared. It is needed for two 548 // reasons. First of all pivot_root() will fail if parent mount is 549 // shared. Secondly when we bind mount rootfs it will propagate to 550 // parent namespace and we don't want that to happen. 551 if sharedMount { 552 return syscall.Mount("", parentMount, "", syscall.MS_PRIVATE, "") 553 } 554 555 return nil 556 } 557 558 func prepareRoot(config *configs.Config) error { 559 flag := syscall.MS_SLAVE | syscall.MS_REC 560 if config.RootPropagation != 0 { 561 flag = config.RootPropagation 562 } 563 if err := syscall.Mount("", "/", "", uintptr(flag), ""); err != nil { 564 return err 565 } 566 567 // Make parent mount private to make sure following bind mount does 568 // not propagate in other namespaces. Also it will help with kernel 569 // check pass in pivot_root. (IS_SHARED(new_mnt->mnt_parent)) 570 if err := rootfsParentMountPrivate(config.Rootfs); err != nil { 571 return err 572 } 573 574 return syscall.Mount(config.Rootfs, config.Rootfs, "bind", syscall.MS_BIND|syscall.MS_REC, "") 575 } 576 577 func setReadonly() error { 578 return syscall.Mount("/", "/", "bind", syscall.MS_BIND|syscall.MS_REMOUNT|syscall.MS_RDONLY|syscall.MS_REC, "") 579 } 580 581 func setupPtmx(config *configs.Config, console *linuxConsole) error { 582 ptmx := filepath.Join(config.Rootfs, "dev/ptmx") 583 if err := os.Remove(ptmx); err != nil && !os.IsNotExist(err) { 584 return err 585 } 586 if err := os.Symlink("pts/ptmx", ptmx); err != nil { 587 return fmt.Errorf("symlink dev ptmx %s", err) 588 } 589 if console != nil { 590 return console.mount(config.Rootfs, config.MountLabel) 591 } 592 return nil 593 } 594 595 // pivotRoot will call pivot_root such that rootfs becomes the new root 596 // filesystem, and everything else is cleaned up. 597 func pivotRoot(rootfs string) error { 598 // While the documentation may claim otherwise, pivot_root(".", ".") is 599 // actually valid. What this results in is / being the new root but 600 // /proc/self/cwd being the old root. Since we can play around with the cwd 601 // with pivot_root this allows us to pivot without creating directories in 602 // the rootfs. Shout-outs to the LXC developers for giving us this idea. 603 604 oldroot, err := syscall.Open("/", syscall.O_DIRECTORY|syscall.O_RDONLY, 0) 605 if err != nil { 606 return err 607 } 608 defer syscall.Close(oldroot) 609 610 newroot, err := syscall.Open(rootfs, syscall.O_DIRECTORY|syscall.O_RDONLY, 0) 611 if err != nil { 612 return err 613 } 614 defer syscall.Close(newroot) 615 616 // Change to the new root so that the pivot_root actually acts on it. 617 if err := syscall.Fchdir(newroot); err != nil { 618 return err 619 } 620 621 if err := syscall.PivotRoot(".", "."); err != nil { 622 return fmt.Errorf("pivot_root %s", err) 623 } 624 625 // Currently our "." is oldroot (according to the current kernel code). 626 // However, purely for safety, we will fchdir(oldroot) since there isn't 627 // really any guarantee from the kernel what /proc/self/cwd will be after a 628 // pivot_root(2). 629 630 if err := syscall.Fchdir(oldroot); err != nil { 631 return err 632 } 633 634 // Make oldroot rprivate to make sure our unmounts don't propagate to the 635 // host (and thus bork the machine). 636 if err := syscall.Mount("", ".", "", syscall.MS_PRIVATE|syscall.MS_REC, ""); err != nil { 637 return err 638 } 639 // Preform the unmount. MNT_DETACH allows us to unmount /proc/self/cwd. 640 if err := syscall.Unmount(".", syscall.MNT_DETACH); err != nil { 641 return err 642 } 643 644 // Switch back to our shiny new root. 645 if err := syscall.Chdir("/"); err != nil { 646 return fmt.Errorf("chdir / %s", err) 647 } 648 return nil 649 } 650 651 func msMoveRoot(rootfs string) error { 652 if err := syscall.Mount(rootfs, "/", "", syscall.MS_MOVE, ""); err != nil { 653 return err 654 } 655 if err := syscall.Chroot("."); err != nil { 656 return err 657 } 658 return syscall.Chdir("/") 659 } 660 661 // createIfNotExists creates a file or a directory only if it does not already exist. 662 func createIfNotExists(path string, isDir bool) error { 663 if _, err := os.Stat(path); err != nil { 664 if os.IsNotExist(err) { 665 if isDir { 666 return os.MkdirAll(path, 0755) 667 } 668 if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil { 669 return err 670 } 671 f, err := os.OpenFile(path, os.O_CREATE, 0755) 672 if err != nil { 673 return err 674 } 675 f.Close() 676 } 677 } 678 return nil 679 } 680 681 // remountReadonly will bind over the top of an existing path and ensure that it is read-only. 682 func remountReadonly(path string) error { 683 for i := 0; i < 5; i++ { 684 if err := syscall.Mount("", path, "", syscall.MS_REMOUNT|syscall.MS_RDONLY, ""); err != nil && !os.IsNotExist(err) { 685 switch err { 686 case syscall.EINVAL: 687 // Probably not a mountpoint, use bind-mount 688 if err := syscall.Mount(path, path, "", syscall.MS_BIND, ""); err != nil { 689 return err 690 } 691 return syscall.Mount(path, path, "", syscall.MS_BIND|syscall.MS_REMOUNT|syscall.MS_RDONLY|syscall.MS_REC|defaultMountFlags, "") 692 case syscall.EBUSY: 693 time.Sleep(100 * time.Millisecond) 694 continue 695 default: 696 return err 697 } 698 } 699 return nil 700 } 701 return fmt.Errorf("unable to mount %s as readonly max retries reached", path) 702 } 703 704 // maskPath masks the top of the specified path inside a container to avoid 705 // security issues from processes reading information from non-namespace aware 706 // mounts ( proc/kcore ). 707 // For files, maskPath bind mounts /dev/null over the top of the specified path. 708 // For directories, maskPath mounts read-only tmpfs over the top of the specified path. 709 func maskPath(path string) error { 710 if err := syscall.Mount("/dev/null", path, "", syscall.MS_BIND, ""); err != nil && !os.IsNotExist(err) { 711 if err == syscall.ENOTDIR { 712 return syscall.Mount("tmpfs", path, "tmpfs", syscall.MS_RDONLY, "") 713 } 714 return err 715 } 716 return nil 717 } 718 719 // writeSystemProperty writes the value to a path under /proc/sys as determined from the key. 720 // For e.g. net.ipv4.ip_forward translated to /proc/sys/net/ipv4/ip_forward. 721 func writeSystemProperty(key, value string) error { 722 keyPath := strings.Replace(key, ".", "/", -1) 723 return ioutil.WriteFile(path.Join("/proc/sys", keyPath), []byte(value), 0644) 724 } 725 726 func remount(m *configs.Mount, rootfs string) error { 727 var ( 728 dest = m.Destination 729 ) 730 if !strings.HasPrefix(dest, rootfs) { 731 dest = filepath.Join(rootfs, dest) 732 } 733 if err := syscall.Mount(m.Source, dest, m.Device, uintptr(m.Flags|syscall.MS_REMOUNT), ""); err != nil { 734 return err 735 } 736 return nil 737 } 738 739 // Do the mount operation followed by additional mounts required to take care 740 // of propagation flags. 741 func mountPropagate(m *configs.Mount, rootfs string, mountLabel string) error { 742 var ( 743 dest = m.Destination 744 data = label.FormatMountLabel(m.Data, mountLabel) 745 flags = m.Flags 746 ) 747 if libcontainerUtils.CleanPath(dest) == "/dev" { 748 flags &= ^syscall.MS_RDONLY 749 } 750 751 copyUp := m.Extensions&configs.EXT_COPYUP == configs.EXT_COPYUP 752 if !(copyUp || strings.HasPrefix(dest, rootfs)) { 753 dest = filepath.Join(rootfs, dest) 754 } 755 756 if err := syscall.Mount(m.Source, dest, m.Device, uintptr(flags), data); err != nil { 757 return err 758 } 759 760 for _, pflag := range m.PropagationFlags { 761 if err := syscall.Mount("", dest, "", uintptr(pflag), ""); err != nil { 762 return err 763 } 764 } 765 return nil 766 }