[lxc-devel] [distrobuilder/master] chroot: Fix mount logic and symlink handling
stgraber on Github
lxc-bot at linuxcontainers.org
Wed Feb 21 18:32:32 UTC 2018
A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 362 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20180221/67bbd08c/attachment.bin>
-------------- next part --------------
From 3ed3b15cf7951948b7b8a7e335e4c9c68b236481 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Wed, 21 Feb 2018 13:31:48 -0500
Subject: [PATCH] chroot: Fix mount logic and symlink handling
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
distrobuilder/chroot.go | 74 +++++++++++++++++++++++++++++++++++++++++--------
1 file changed, 63 insertions(+), 11 deletions(-)
diff --git a/distrobuilder/chroot.go b/distrobuilder/chroot.go
index f055d73..f859dcb 100644
--- a/distrobuilder/chroot.go
+++ b/distrobuilder/chroot.go
@@ -22,15 +22,19 @@ type chrootMount struct {
}
func setupMounts(rootfs string, mounts []chrootMount) error {
+ // Create a temporary mount path
err := os.MkdirAll(filepath.Join(rootfs, ".distrobuilder"), 0700)
if err != nil {
return err
}
for i, mount := range mounts {
+ // Target path
tmpTarget := filepath.Join(rootfs, ".distrobuilder", fmt.Sprintf("%d", i))
+
+ // Create the target mountpoint
if mount.isDir {
- err := os.MkdirAll(tmpTarget, 0755)
+ err := os.Mkdir(tmpTarget, 0755)
if err != nil {
return err
}
@@ -41,6 +45,7 @@ func setupMounts(rootfs string, mounts []chrootMount) error {
}
}
+ // Mount to the temporary path
err := syscall.Mount(mount.source, tmpTarget, mount.fstype, mount.flags, mount.data)
if err != nil {
return fmt.Errorf("Failed to mount '%s': %s", mount.source, err)
@@ -52,25 +57,59 @@ func setupMounts(rootfs string, mounts []chrootMount) error {
func moveMounts(mounts []chrootMount) error {
for i, mount := range mounts {
+ // Source path
tmpSource := filepath.Join("/", ".distrobuilder", fmt.Sprintf("%d", i))
+
+ // Resolve symlinks
+ target := mount.target
+ for {
+ // Get information on current target
+ fi, err := os.Lstat(target)
+ if err != nil {
+ break
+ }
+
+ // If not a symlink, we're done
+ if fi.Mode()&os.ModeSymlink == 0 {
+ break
+ }
+
+ // If a symlink, resolve it
+ newTarget, err := os.Readlink(target)
+ if err != nil {
+ break
+ }
+
+ target = newTarget
+ }
+
+ // Create parent paths if missing
+ err := os.MkdirAll(filepath.Dir(target), 0755)
+ if err != nil {
+ return err
+ }
+
+ // Create target path
if mount.isDir {
- err := os.MkdirAll(mount.target, 0755)
+ err = os.MkdirAll(target, 0755)
if err != nil {
return err
}
} else {
- _, err := os.Create(mount.target)
+ _, err = os.Create(target)
if err != nil {
return err
}
}
- err := syscall.Mount(tmpSource, mount.target, "", syscall.MS_MOVE, "")
+ // Move the mount to its destination
+ err = syscall.Mount(tmpSource, target, "", syscall.MS_MOVE, "")
if err != nil {
return fmt.Errorf("Failed to mount '%s': %s", mount.source, err)
}
}
+ // Cleanup our temporary path
err := os.RemoveAll(filepath.Join("/", ".distrobuilder"))
if err != nil {
return err
@@ -81,6 +120,7 @@ func moveMounts(mounts []chrootMount) error {
}
func killChrootProcesses(rootfs string) error {
+ // List all files under /proc
proc, err := os.Open(filepath.Join(rootfs, "proc"))
if err != nil {
return err
@@ -91,6 +131,7 @@ func killChrootProcesses(rootfs string) error {
return err
}
+ // Get all processes and kill them
for _, dir := range dirs {
match, _ := regexp.MatchString(`\d+`, dir)
if match {
@@ -106,10 +147,14 @@ func killChrootProcesses(rootfs string) error {
}
func setupChroot(rootfs string) (func() error, error) {
- var err error
+ // Mount the rootfs
+ err := syscall.Mount(rootfs, rootfs, "", syscall.MS_BIND, "")
+ if err != nil {
+ return nil, fmt.Errorf("Failed to mount '%s': %s", rootfs, err)
+ }
+ // Setup all other needed mounts
mounts := []chrootMount{
- {rootfs, "/", "", syscall.MS_BIND, "", true},
{"none", "/proc", "proc", 0, "", true},
{"none", "/sys", "sysfs", 0, "", true},
{"/dev", "/dev", "", syscall.MS_BIND, "", true},
@@ -118,21 +163,24 @@ func setupChroot(rootfs string) (func() error, error) {
{"/etc/resolv.conf", "/etc/resolv.conf", "", syscall.MS_BIND, "", false},
}
- cwd, err := os.Getwd()
+ // Keep a reference to the host rootfs and cwd
+ root, err := os.Open("/")
if err != nil {
return nil, err
}
- err = setupMounts(rootfs, mounts)
+ cwd, err := os.Getwd()
if err != nil {
- return nil, fmt.Errorf("Failed to mount filesystems: %v", err)
+ return nil, err
}
- root, err := os.Open("/")
+ // Setup all needed mounts in a temporary location
+ err = setupMounts(rootfs, mounts)
if err != nil {
- return nil, err
+ return nil, fmt.Errorf("Failed to mount filesystems: %v", err)
}
+ // Chroot into the container's rootfs
err = syscall.Chroot(rootfs)
if err != nil {
root.Close()
@@ -144,6 +192,7 @@ func setupChroot(rootfs string) (func() error, error) {
return nil, err
}
+ // Move all the mounts into place
err = moveMounts(mounts)
if err != nil {
return nil, err
@@ -152,6 +201,7 @@ func setupChroot(rootfs string) (func() error, error) {
return func() error {
defer root.Close()
+ // Switch back to the host rootfs
err = root.Chdir()
if err != nil {
return err
@@ -170,6 +220,8 @@ func setupChroot(rootfs string) (func() error, error) {
// This will kill all processes in the chroot and allow to cleanly
// unmount everything.
killChrootProcesses(rootfs)
+
+ // And now unmount the entire tree
syscall.Unmount(rootfs, syscall.MNT_DETACH)
return nil
More information about the lxc-devel
mailing list