[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