Skip to content

Commit

Permalink
fix(init): switch_root implementation (#49)
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewrynhard authored Apr 19, 2018
1 parent 4c9a810 commit b614179
Show file tree
Hide file tree
Showing 4 changed files with 246 additions and 113 deletions.
14 changes: 5 additions & 9 deletions initramfs/src/init/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import (
"github.com/autonomy/dianemo/initramfs/src/init/pkg/constants"
"github.com/autonomy/dianemo/initramfs/src/init/pkg/handlers"
"github.com/autonomy/dianemo/initramfs/src/init/pkg/mount"
"github.com/autonomy/dianemo/initramfs/src/init/pkg/mount/cgroups"
"github.com/autonomy/dianemo/initramfs/src/init/pkg/process"
"github.com/autonomy/dianemo/initramfs/src/init/pkg/switchroot"
"github.com/autonomy/dianemo/initramfs/src/init/pkg/userdata"
Expand Down Expand Up @@ -45,21 +44,18 @@ func init() {
func main() {
defer hang()
if !*switchRoot {
// Mount the initial file systems.
if err := mount.Mount(); err != nil {
// Read the block devices and populate the mount point definitions.
if err := mount.Init(constants.NewRoot); err != nil {
panic(err)
}
// Move the initial file systems to the new root.
if err := mount.Move(); err != nil {
panic(err)
}
// Mount the cgroups file systems to the new root.
if err := cgroups.Mount(); err != nil {
// Unmount the ROOT and DATA block devices
if err := mount.Unmount(); err != nil {
panic(err)
}
// Perform the equivalent of switch_root.
// See https://github.com/karelzak/util-linux/blob/master/sys-utils/switch_root.c
if err := switchroot.Switch(); err != nil {
if err := switchroot.Switch(constants.NewRoot); err != nil {
panic(err)
}
}
Expand Down
8 changes: 4 additions & 4 deletions initramfs/src/init/pkg/mount/cgroups/cgroups.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"os"
"path"

"github.com/autonomy/dianemo/initramfs/src/init/pkg/constants"
"golang.org/x/sys/unix"
)

Expand All @@ -33,8 +32,9 @@ Mount creates the following file systems:
cgroup /sys/fs/cgroup/freezer cgroup defaults 0 0
cgroup /sys/fs/cgroup/cpuset cgroup defaults 0 0
*/
func Mount() error {
target := path.Join(constants.NewRoot, "/sys/fs/cgroup")
func Mount(s string) error {

target := path.Join(s, "/sys/fs/cgroup")
if err := os.MkdirAll(target, os.ModeDir); err != nil {
return fmt.Errorf("failed to create %s: %s", target, err.Error())
}
Expand All @@ -59,7 +59,7 @@ func Mount() error {
"cpuset",
}
for _, c := range cgroups {
p := path.Join(constants.NewRoot, fmt.Sprintf("/sys/fs/cgroup/%s", c))
p := path.Join(s, fmt.Sprintf("/sys/fs/cgroup/%s", c))
if err := os.MkdirAll(p, os.ModeDir); err != nil {
return fmt.Errorf("failed to create %s: %s", p, err.Error())
}
Expand Down
231 changes: 137 additions & 94 deletions initramfs/src/init/pkg/mount/mount.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"os"
"path"
"sync"

"github.com/autonomy/dianemo/initramfs/src/init/pkg/blkid"
"github.com/autonomy/dianemo/initramfs/src/init/pkg/constants"
Expand All @@ -12,8 +13,8 @@ import (
)

type (
// Filesystem represents a linux file system.
Filesystem struct {
// MountPoint represents a linux mount point.
MountPoint struct {
source string
target string
fstype string
Expand All @@ -32,116 +33,158 @@ type (
)

var (
filesystems = []*Filesystem{
{
"none",
"/dev",
"devtmpfs",
unix.MS_NOSUID,
"",
},
{
"none",
"/proc",
"proc",
unix.MS_NOSUID | unix.MS_NOEXEC | unix.MS_NODEV,
"",
},
{
"none",
"/sys",
"sysfs",
unix.MS_NOSUID | unix.MS_NOEXEC | unix.MS_NODEV,
"",
},
{
"none",
"/run",
"tmpfs",
0,
"",
},
{
"none",
"/tmp",
"tmpfs",
0,
"",
},
instance struct {
special map[string]*MountPoint
blockdevices map[string]*MountPoint
}

once sync.Once

special = map[string]*MountPoint{
"dev": {"devtmpfs", "/dev", "devtmpfs", unix.MS_NOSUID, "mode=0755"},
"proc": {"proc", "/proc", "proc", unix.MS_NOSUID | unix.MS_NOEXEC | unix.MS_NODEV, ""},
"sys": {"sysfs", "/sys", "sysfs", unix.MS_NOSUID | unix.MS_NOEXEC | unix.MS_NODEV, ""},
"run": {"tmpfs", "/run", "tmpfs", 0, ""},
"tmp": {"tmpfs", "/tmp", "tmpfs", 0, ""},
}
)

/*
Mount creates the following file systems:
devtmpfs /dev devtmpfs nosuid 0 0
proc /proc proc nosuid,noexec,nodev 0 0
sysfs /sys sysfs nosuid,noexec,nodev 0 0
tmpfs /run tmpfs defaults 0 0
tmpfs /tmp tmpfs defaults 0 0
*/
func Mount() error {
for _, m := range filesystems {
if err := os.MkdirAll(m.target, os.ModeDir); err != nil {
return fmt.Errorf("failed to create %s: %s", m.target, err.Error())
// Init initializes the mount points.
func Init(s string) error {
once.Do(func() {
instance = struct {
special map[string]*MountPoint
blockdevices map[string]*MountPoint
}{
special,
map[string]*MountPoint{},
}
})

for _, mountpoint := range instance.special {
if err := os.MkdirAll(mountpoint.target, os.ModeDir); err != nil {
return fmt.Errorf("create %s: %s", mountpoint.target, err.Error())
}
if err := unix.Mount(mountpoint.source, mountpoint.target, mountpoint.fstype, mountpoint.flags, mountpoint.data); err != nil {
return fmt.Errorf("mount %s: %s", mountpoint.target, err.Error())
}
}

probed, err := probe()
if err != nil {
return fmt.Errorf("probe block devices: %s", err.Error())
}
for _, b := range probed {
mountpoint := &MountPoint{
source: b.dev,
fstype: b.TYPE,
flags: unix.MS_NOATIME,
data: "",
}
switch b.LABEL {
case constants.ROOTLabel:
mountpoint.target = s
case constants.DATALabel:
mountpoint.target = path.Join(s, "var")
}

if err := os.MkdirAll(mountpoint.target, os.ModeDir); err != nil {
return fmt.Errorf("create %s: %s", mountpoint.target, err.Error())
}
if err := unix.Mount(m.source, m.target, m.fstype, m.flags, m.data); err != nil {
return fmt.Errorf("failed to mount %s: %s", m.target, err.Error())
if err := unix.Mount(mountpoint.source, mountpoint.target, mountpoint.fstype, mountpoint.flags, mountpoint.data); err != nil {
return fmt.Errorf("mount %s: %s", mountpoint.target, err.Error())
}

instance.blockdevices[b.LABEL] = mountpoint
}

return nil
}

/*
Move moves the following file systems to the new root:
devtmpfs /dev devtmpfs nosuid 0 0
proc /proc proc nosuid,noexec,nodev 0 0
sysfs /sys sysfs nosuid,noexec,nodev 0 0
tmpfs /run tmpfs defaults 0 0
tmpfs /tmp tmpfs defaults 0 0
*/
func Move() error {
if err := os.MkdirAll(constants.NewRoot, os.ModeDir); err != nil {
// Move moves the mount points created in Init, to the new root.
func Move(s string) error {
if err := os.MkdirAll(s, os.ModeDir); err != nil {
return err
}

blockDevices, err := probe()
if err != nil {
return fmt.Errorf("failed to probe block devices: %s", err.Error())
}
for _, b := range blockDevices {
switch b.LABEL {
case constants.ROOTLabel:
if err := unix.Mount(b.dev, constants.NewRoot, b.TYPE, unix.MS_RDONLY|unix.MS_NOATIME, ""); err != nil {
return fmt.Errorf("mount %s: %s", constants.NewRoot, err.Error())
}
// See http://man7.org/linux/man-pages/man2/mount.2.html
// MS_SHARED
// Make this mount point shared. Mount and unmount events
// immediately under this mount point will propagate to the other
// mount points that are members of this mount's peer group.
// Propagation here means that the same mount or unmount will
// automatically occur under all of the other mount points in the
// peer group. Conversely, mount and unmount events that take
// place under peer mount points will propagate to this mount
// point.
// https://github.com/kubernetes/kubernetes/issues/61058
if err := unix.Mount("", constants.NewRoot, "", unix.MS_SHARED, ""); err != nil {
return fmt.Errorf("mount shared %s: %s", constants.NewRoot, err.Error())
// Move the special mounts to the new root.
for label, mountpoint := range instance.special {
target := path.Join(s, mountpoint.target)
if err := unix.Mount(mountpoint.target, target, "", unix.MS_MOVE, ""); err != nil {
return fmt.Errorf("move mount point %s to %s: %s", mountpoint.target, target, err.Error())
}
if label == "dev" {
mountpoint = &MountPoint{"devpts", path.Join(s, "/dev/pts"), "devpts", unix.MS_NOSUID | unix.MS_NOEXEC, "ptmxmode=000,mode=620,gid=5"}
if err := os.MkdirAll(mountpoint.target, os.ModeDir); err != nil {
return fmt.Errorf("create %s: %s", mountpoint.target, err.Error())
}
case constants.DATALabel:
target := path.Join(constants.NewRoot, "var")
if err := unix.Mount(b.dev, target, b.TYPE, unix.MS_NOATIME, ""); err != nil {
return fmt.Errorf("mount %s: %s", target, err.Error())
if err := unix.Mount(mountpoint.source, mountpoint.target, mountpoint.fstype, mountpoint.flags, mountpoint.data); err != nil {
return fmt.Errorf("mount %s: %s", mountpoint.target, err.Error())
}
}
}

// Move the existing file systems to the new root.
for _, m := range filesystems {
t := path.Join(constants.NewRoot, m.target)
if err := unix.Mount(m.target, t, "", unix.MS_MOVE, ""); err != nil {
return fmt.Errorf("failed to mount %s: %s", t, err.Error())
return nil
}

// Finalize moves the mount points created in Init, to the new root.
func Finalize(s string) error {
if err := unix.Mount(s, "/", "", unix.MS_MOVE, ""); err != nil {
return err
}

return nil
}

// Mount moves the mount points created in Init, to the new root.
func Mount(s string) error {
if err := os.MkdirAll(s, os.ModeDir); err != nil {
return err
}

mountpoint, ok := instance.blockdevices[constants.ROOTLabel]
if ok {
mountpoint.flags = unix.MS_RDONLY | unix.MS_NOATIME
if err := unix.Mount(mountpoint.source, mountpoint.target, mountpoint.fstype, mountpoint.flags, mountpoint.data); err != nil {
return fmt.Errorf("mount %s: %s", mountpoint.target, err.Error())
}
// MS_SHARED:
// Make this mount point shared. Mount and unmount events
// immediately under this mount point will propagate to the
// other mount points that are members of this mount's peer
// group. Propagation here means that the same mount or
// unmount will automatically occur under all of the other
// mount points in the peer group. Conversely, mount and
// unmount events that take place under peer mount points
// will propagate to this mount point.
// See http://man7.org/linux/man-pages/man2/mount.2.html
// https://github.com/kubernetes/kubernetes/issues/61058
if err := unix.Mount("", mountpoint.target, "", unix.MS_SHARED, ""); err != nil {
return fmt.Errorf("mount %s as shared: %s", mountpoint.target, err.Error())
}
}
mountpoint, ok = instance.blockdevices[constants.DATALabel]
if ok {
if err := unix.Mount(mountpoint.source, mountpoint.target, mountpoint.fstype, mountpoint.flags, mountpoint.data); err != nil {
return fmt.Errorf("mount %s: %s", mountpoint.target, err.Error())
}
}

return nil
}

// Unmount unmounts the ROOT and DATA block devices.
func Unmount() error {
mountpoint, ok := instance.blockdevices[constants.DATALabel]
if ok {
if err := unix.Unmount(mountpoint.target, 0); err != nil {
return fmt.Errorf("unmount mount point %s: %s", mountpoint.target, err.Error())
}
}
mountpoint, ok = instance.blockdevices[constants.ROOTLabel]
if ok {
if err := unix.Unmount(mountpoint.target, 0); err != nil {
return fmt.Errorf("unmount mount point %s: %s", mountpoint.target, err.Error())
}
}

Expand Down
Loading

0 comments on commit b614179

Please sign in to comment.