[lxc-devel] [distrobuilder/master] distrobuilder: fix and restructure chroot code

monstermunchkin on Github lxc-bot at linuxcontainers.org
Mon Feb 12 11:06:06 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/20180212/d59c28d7/attachment.bin>
-------------- next part --------------
From f89cebdc882a1d62a0614c02e5505ff5c80af31b Mon Sep 17 00:00:00 2001
From: Thomas Hipp <thomas.hipp at canonical.com>
Date: Thu, 8 Feb 2018 15:38:53 +0100
Subject: [PATCH] distrobuilder: fix and restructure chroot code

Signed-off-by: Thomas Hipp <thomas.hipp at canonical.com>
---
 distrobuilder/chroot.go | 138 +++++++++++++++++++++++++++++++-----------------
 distrobuilder/main.go   |  14 +++--
 2 files changed, 100 insertions(+), 52 deletions(-)

diff --git a/distrobuilder/chroot.go b/distrobuilder/chroot.go
index 02d11ec..f0be5b7 100644
--- a/distrobuilder/chroot.go
+++ b/distrobuilder/chroot.go
@@ -4,80 +4,124 @@ import (
 	"fmt"
 	"os"
 	"path/filepath"
+	"regexp"
+	"strconv"
 	"syscall"
 
 	"github.com/lxc/distrobuilder/managers"
 	"github.com/lxc/distrobuilder/shared"
 )
 
-func prepareChroot(cacheDir string) ([]string, error) {
-	type Mount struct {
-		source string
-		target string
-		fstype string
-		flags  uintptr
-		data   string
+type chrootMount struct {
+	source  string
+	target  string
+	fstype  string
+	flags   uintptr
+	data    string
+	remove  bool
+	mounted bool
+	isDir   bool
+}
+
+func mountFilesystems(rootfs string, mounts []chrootMount) bool {
+	ok := true
+
+	for i, mount := range mounts {
+		if mount.isDir {
+			os.MkdirAll(filepath.Join(rootfs, mount.target), 0755)
+		} else {
+			os.Create(filepath.Join(rootfs, mount.target))
+		}
+		err := syscall.Mount(mount.source, filepath.Join(rootfs, mount.target),
+			mount.fstype, mount.flags, mount.data)
+		if err != nil {
+			fmt.Fprintf(os.Stderr, "Failed to mount '%s': %s\n", mount.source, err)
+			ok = false
+			break
+		}
+		mounts[i].mounted = true
 	}
 
-	var (
-		err      error
-		unmounts []string
-	)
+	return ok
+}
 
-	mounts := []Mount{
-		{filepath.Join(cacheDir, "rootfs"), "", "tmpfs", syscall.MS_BIND, ""},
-		{"proc", "proc", "proc", 0, ""},
-		{"sys", "sys", "sysfs", 0, ""},
-		{"udev", "dev", "devtmpfs", 0, ""},
-		{"shm", "/dev/shm", "tmpfs", 0, ""},
-		{"/dev/pts", "/dev/pts", "tmpfs", syscall.MS_BIND, ""},
-		{"run", "/run", "tmpfs", 0, ""},
-		{"tmp", "/tmp", "tmpfs", 0, ""},
+func unmountFilesystems(rootfs string, mounts []chrootMount) {
+	// unmount targets in reversed order
+	for i := len(mounts) - 1; i >= 0; i-- {
+		if mounts[i].mounted {
+			err := syscall.Unmount(filepath.Join(rootfs, mounts[i].target), 0)
+			if err != nil {
+				fmt.Fprintf(os.Stderr, "Failed to unmount '%s': %s\n", mounts[i].target, err)
+				continue
+			}
+			mounts[i].mounted = false
+
+			if mounts[i].remove {
+				os.RemoveAll(filepath.Join(rootfs, mounts[i].target))
+			}
+		}
 	}
+}
 
-	os.MkdirAll(filepath.Join(cacheDir, "rootfs", "dev", "pts"), 0755)
+func killChrootProcesses(rootfs string) error {
+	proc, err := os.Open(filepath.Join(rootfs, "proc"))
+	if err != nil {
+		return err
+	}
 
-	for _, mount := range mounts {
-		err = syscall.Mount(mount.source, filepath.Join(cacheDir, "rootfs", mount.target),
-			mount.fstype, mount.flags, mount.data)
-		if err != nil {
-			return unmounts, err
+	dirs, err := proc.Readdirnames(0)
+	if err != nil {
+		return err
+	}
+
+	for _, dir := range dirs {
+		match, _ := regexp.MatchString(`\d+`, dir)
+		if match {
+			link, _ := os.Readlink(filepath.Join(rootfs, "proc", dir, "root"))
+			if link == rootfs {
+				pid, _ := strconv.Atoi(dir)
+				syscall.Kill(pid, syscall.SIGKILL)
+			}
 		}
-		unmounts = append(unmounts, filepath.Join(cacheDir, "rootfs", mount.target))
 	}
 
-	return unmounts, nil
+	return nil
 }
 
-func setupChroot(cacheDir string) (func() error, error) {
+func setupChroot(rootfs string) (func() error, error) {
 	var err error
 
-	cwd, err := os.Getwd()
-	if err != nil {
-		return nil, err
+	mounts := []chrootMount{
+		{rootfs, "", "tmpfs", syscall.MS_BIND, "", false, false, true},
+		{"proc", "proc", "proc", 0, "", false, false, true},
+		{"sys", "sys", "sysfs", 0, "", false, false, true},
+		{"udev", "dev", "devtmpfs", 0, "", false, false, true},
+		{"shm", "/dev/shm", "tmpfs", 0, "", true, false, true},
+		{"/dev/pts", "/dev/pts", "tmpfs", syscall.MS_BIND, "", true, false, true},
+		{"run", "/run", "tmpfs", 0, "", false, false, true},
+		{"tmp", "/tmp", "tmpfs", 0, "", false, false, true},
+		{"/etc/resolv.conf", "/etc/resolv.conf", "", syscall.MS_BIND, "", false, false, false},
 	}
 
-	err = shared.Copy("/etc/resolv.conf", filepath.Join(cacheDir, "rootfs", "etc", "resolv.conf"))
+	cwd, err := os.Getwd()
 	if err != nil {
 		return nil, err
 	}
 
-	unmounts, err := prepareChroot(cacheDir)
-	if err != nil {
-		for i := len(unmounts) - 1; i >= 0; i-- {
-			syscall.Unmount(unmounts[i], 0)
-		}
-		return nil, err
+	ok := mountFilesystems(rootfs, mounts)
+	if !ok {
+		unmountFilesystems(rootfs, mounts)
+		return nil, fmt.Errorf("Failed to mount filesystems")
 	}
 
-	syscall.Mknod(filepath.Join(cacheDir, "rootfs", "dev", "null"), 1, 3)
+	syscall.Mknod(filepath.Join(rootfs, "dev", "null"), 1, 3)
 
 	root, err := os.Open("/")
 	if err != nil {
 		return nil, err
 	}
 
-	err = syscall.Chroot(filepath.Join(cacheDir, "rootfs"))
+	err = syscall.Chroot(rootfs)
 	if err != nil {
 		root.Close()
 		return nil, err
@@ -89,11 +133,6 @@ func setupChroot(cacheDir string) (func() error, error) {
 	}
 
 	return func() error {
-		// unmount targets in reversed order
-		for i := len(unmounts) - 1; i >= 0; i-- {
-			syscall.Unmount(unmounts[i], 0)
-		}
-
 		defer root.Close()
 
 		err = root.Chdir()
@@ -111,11 +150,16 @@ func setupChroot(cacheDir string) (func() error, error) {
 			return err
 		}
 
+		// This will kill all processes in the chroot and allow to cleanly
+		// unmount everything.
+		killChrootProcesses(rootfs)
+		unmountFilesystems(rootfs, mounts)
+
 		return nil
 	}, nil
 }
 
-func manageChroot(def shared.DefinitionPackages) error {
+func managePackages(def shared.DefinitionPackages) error {
 	var err error
 
 	manager := managers.Get(def.Manager)
diff --git a/distrobuilder/main.go b/distrobuilder/main.go
index c8187a4..f9fa2f4 100644
--- a/distrobuilder/main.go
+++ b/distrobuilder/main.go
@@ -52,6 +52,7 @@ import (
 	"fmt"
 	"io"
 	"os"
+	"path/filepath"
 
 	"github.com/lxc/distrobuilder/shared"
 	"github.com/lxc/distrobuilder/sources"
@@ -151,17 +152,20 @@ func run(c *cli.Context) error {
 		defer os.RemoveAll(c.GlobalString("cache-dir"))
 	}
 
-	exitFunc, err := setupChroot(c.GlobalString("cache-dir"))
+	// enter chroot
+	exitChroot, err := setupChroot(filepath.Join(c.GlobalString("cache-dir"), "rootfs"))
 	if err != nil {
-		return fmt.Errorf("Failed to chroot: %s", err)
+		return fmt.Errorf("Failed to setup chroot: %s", err)
 	}
-	defer exitFunc()
 
-	err = manageChroot(def.Packages)
+	err = managePackages(def.Packages)
 	if err != nil {
-		return fmt.Errorf("Failed to run setup: %s", err)
+		exitChroot()
+		return fmt.Errorf("Failed to manage packages: %s", err)
 	}
 
+	exitChroot()
+
 	return nil
 }
 


More information about the lxc-devel mailing list