mirror of
https://github.com/kairos-io/immucore.git
synced 2025-09-16 15:09:12 +00:00
🐛 Fix chroot unmounting (#80)
Signed-off-by: Itxaka <itxaka.garcia@spectrocloud.com>
This commit is contained in:
@@ -36,17 +36,14 @@ type Chroot struct {
|
|||||||
func NewChroot(path string) *Chroot {
|
func NewChroot(path string) *Chroot {
|
||||||
return &Chroot{
|
return &Chroot{
|
||||||
path: path,
|
path: path,
|
||||||
defaultMounts: []string{"/dev", "/proc", "/sys", "/run", "/tmp"},
|
defaultMounts: []string{
|
||||||
|
"/sys", "/dev", "/dev/pts", "/dev/shm", "/proc", "/tmp",
|
||||||
|
"/run/rootfsbase", "/run/initramfs/live", "/run",
|
||||||
|
},
|
||||||
activeMounts: []string{},
|
activeMounts: []string{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChrootedCallback runs the given callback in a chroot environment.
|
|
||||||
func ChrootedCallback(path string, bindMounts map[string]string, callback func() error) error {
|
|
||||||
chroot := NewChroot(path)
|
|
||||||
return chroot.RunCallback(callback)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prepare will mount the defaultMounts as bind mounts, to be ready when we run chroot.
|
// Prepare will mount the defaultMounts as bind mounts, to be ready when we run chroot.
|
||||||
func (c *Chroot) Prepare() error {
|
func (c *Chroot) Prepare() error {
|
||||||
var err error
|
var err error
|
||||||
@@ -55,20 +52,35 @@ func (c *Chroot) Prepare() error {
|
|||||||
return errors.New("there are already active mountpoints for this instance")
|
return errors.New("there are already active mountpoints for this instance")
|
||||||
}
|
}
|
||||||
|
|
||||||
defer func() {
|
|
||||||
if err != nil {
|
|
||||||
c.Close()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
for _, mnt := range c.defaultMounts {
|
for _, mnt := range c.defaultMounts {
|
||||||
mountPoint := filepath.Join(c.path, mnt)
|
mountPoint := filepath.Join(c.path, mnt)
|
||||||
|
if _, err := os.Stat(mnt); os.IsNotExist(err) {
|
||||||
|
// Source doesnt exist, skip it
|
||||||
|
Log.Debug().Str("what", mnt).Msg("Source does not exists, not mounting in chroot")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
err = CreateIfNotExists(mountPoint)
|
err = CreateIfNotExists(mountPoint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Log.Err(err).Str("what", mountPoint).Msg("Creating dir")
|
Log.Err(err).Str("what", mountPoint).Msg("Creating dir")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = syscall.Mount(mnt, mountPoint, "bind", syscall.MS_BIND|syscall.MS_REC, "")
|
// Don't mount /sys, /dev or /run as MS_REC as this brings a lot of submounts for cgroups and such and those are not needed
|
||||||
|
// and prevents up from cleaning up the chroot afterwards
|
||||||
|
// For example you can also have a cdrom device mounted under /dev/sr0 or /dev/cdrom and we dont know how to find it and mark it private
|
||||||
|
switch {
|
||||||
|
case mnt == "/sys", mnt == "/dev", mnt == "/run":
|
||||||
|
err = syscall.Mount(mnt, mountPoint, "", syscall.MS_BIND, "")
|
||||||
|
default:
|
||||||
|
err = syscall.Mount(mnt, mountPoint, "", syscall.MS_BIND|syscall.MS_REC, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
Log.Err(err).Str("where", mountPoint).Str("what", mnt).Msg("Mounting chroot bind")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// "remount" with private so unmount events do not propagate
|
||||||
|
err = syscall.Mount("", mountPoint, "", syscall.MS_PRIVATE, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Log.Err(err).Str("where", mountPoint).Str("what", mnt).Msg("Mounting chroot bind")
|
Log.Err(err).Str("where", mountPoint).Str("what", mnt).Msg("Mounting chroot bind")
|
||||||
return err
|
return err
|
||||||
@@ -82,6 +94,9 @@ func (c *Chroot) Prepare() error {
|
|||||||
// Close will unmount all active mounts created in Prepare on reverse order.
|
// Close will unmount all active mounts created in Prepare on reverse order.
|
||||||
func (c *Chroot) Close() error {
|
func (c *Chroot) Close() error {
|
||||||
failures := []string{}
|
failures := []string{}
|
||||||
|
Log.Debug().Strs("activeMounts", c.activeMounts).Msg("Closing chroot")
|
||||||
|
// Something mounts this due to selinux, so we need to try to manually unmount and ignore any errors
|
||||||
|
_ = syscall.Unmount(filepath.Join(c.path, "/sys/fs/selinux"), 0)
|
||||||
for len(c.activeMounts) > 0 {
|
for len(c.activeMounts) > 0 {
|
||||||
curr := c.activeMounts[len(c.activeMounts)-1]
|
curr := c.activeMounts[len(c.activeMounts)-1]
|
||||||
Log.Debug().Str("what", curr).Msg("Unmounting from chroot")
|
Log.Debug().Str("what", curr).Msg("Unmounting from chroot")
|
||||||
@@ -131,12 +146,9 @@ func (c *Chroot) RunCallback(callback func() error) (err error) {
|
|||||||
Log.Err(err).Msg("Can't mount default mounts")
|
Log.Err(err).Msg("Can't mount default mounts")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func(c *Chroot) {
|
||||||
tmpErr := c.Close()
|
err = c.Close()
|
||||||
if err == nil {
|
}(c)
|
||||||
err = tmpErr
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
}
|
||||||
// Change to new dir before running chroot!
|
// Change to new dir before running chroot!
|
||||||
err = syscall.Chdir(c.path)
|
err = syscall.Chdir(c.path)
|
||||||
|
@@ -95,7 +95,6 @@ func (s *State) RunStageOp(stage string) func(context.Context) error {
|
|||||||
}
|
}
|
||||||
output, err := utils.SH(cmd)
|
output, err := utils.SH(cmd)
|
||||||
internalUtils.Log.Info().Msg("Running rootfs stage")
|
internalUtils.Log.Info().Msg("Running rootfs stage")
|
||||||
internalUtils.Log.Info().Msg(output)
|
|
||||||
f, ferr := os.Create(filepath.Join(constants.LogDir, "rootfs_stage.log"))
|
f, ferr := os.Create(filepath.Join(constants.LogDir, "rootfs_stage.log"))
|
||||||
if ferr == nil {
|
if ferr == nil {
|
||||||
_, _ = f.WriteString(output)
|
_, _ = f.WriteString(output)
|
||||||
@@ -104,10 +103,9 @@ func (s *State) RunStageOp(stage string) func(context.Context) error {
|
|||||||
return err
|
return err
|
||||||
case "initramfs":
|
case "initramfs":
|
||||||
// Not sure if it will work under UKI where the s.Rootdir is the current root already
|
// Not sure if it will work under UKI where the s.Rootdir is the current root already
|
||||||
|
internalUtils.Log.Info().Msg("Running initramfs stage")
|
||||||
chroot := internalUtils.NewChroot(s.Rootdir)
|
chroot := internalUtils.NewChroot(s.Rootdir)
|
||||||
output, err := chroot.Run(cmd)
|
output, err := chroot.Run(cmd)
|
||||||
internalUtils.Log.Info().Msg("Running initramfs stage")
|
|
||||||
internalUtils.Log.Info().Msg(output)
|
|
||||||
f, ferr := os.Create(filepath.Join(constants.LogDir, "initramfs_stage.log"))
|
f, ferr := os.Create(filepath.Join(constants.LogDir, "initramfs_stage.log"))
|
||||||
if ferr == nil {
|
if ferr == nil {
|
||||||
_, _ = f.WriteString(output)
|
_, _ = f.WriteString(output)
|
||||||
|
Reference in New Issue
Block a user