The following pull request was submitted through Github. It can be accessed and reviewed at: https://github.com/lxc/distrobuilder/pull/5
This e-mail was sent by the LXC bot, direct replies will not reach the author unless they happen to be subscribed to this list. === Description (from pull-request) === Signed-off-by: Thomas Hipp <thomas.h...@canonical.com>
From f89cebdc882a1d62a0614c02e5505ff5c80af31b Mon Sep 17 00:00:00 2001 From: Thomas Hipp <thomas.h...@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.h...@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 }
_______________________________________________ lxc-devel mailing list lxc-devel@lists.linuxcontainers.org http://lists.linuxcontainers.org/listinfo/lxc-devel