From e60b110364688cf334ec68e073edf8d9cebc9fcb Mon Sep 17 00:00:00 2001 From: Kris Alder Date: Tue, 3 May 2022 11:05:34 -0700 Subject: pkg/build: refactor cuttlefish image code to embedFiles() Since most of the image mounting code is duplicated, we can instead extract it to an embedFiles() function that takes a callback. The Linux- or Android-specific code can be in the callback. --- pkg/build/android.go | 69 +++++++++++++++++++++++++++++++++++++++++--- pkg/build/android_linux.go | 67 ------------------------------------------ pkg/build/android_nolinux.go | 15 ---------- pkg/build/linux_linux.go | 13 ++++++++- pkg/build/linux_nolinux.go | 4 +++ 5 files changed, 81 insertions(+), 87 deletions(-) delete mode 100644 pkg/build/android_linux.go delete mode 100644 pkg/build/android_nolinux.go diff --git a/pkg/build/android.go b/pkg/build/android.go index 9fb38bd5c..cc4e041da 100644 --- a/pkg/build/android.go +++ b/pkg/build/android.go @@ -4,7 +4,11 @@ package build import ( + "archive/tar" + "compress/gzip" "fmt" + "io/ioutil" + "os" "path/filepath" "time" @@ -27,6 +31,40 @@ func (a android) runBuild(kernelDir, buildConfig string) error { return err } +func (a android) readCompiler(archivePath string) (string, error) { + f, err := os.Open(archivePath) + if err != nil { + return "", err + } + defer f.Close() + + gr, err := gzip.NewReader(f) + if err != nil { + return "", err + } + defer gr.Close() + + tr := tar.NewReader(gr) + + h, err := tr.Next() + for ; err == nil; h, err = tr.Next() { + if filepath.Base(h.Name) == "compile.h" { + bytes, err := ioutil.ReadAll(tr) + if err != nil { + return "", err + } + result := linuxCompilerRegexp.FindSubmatch(bytes) + if result == nil { + return "", fmt.Errorf("include/generated/compile.h does not contain build information") + } + + return string(result[1]), nil + } + } + + return "", fmt.Errorf("archive %s doesn't contain include/generated/compile.h", archivePath) +} + func (a android) build(params Params) (ImageDetails, error) { var details ImageDetails @@ -44,13 +82,37 @@ func (a android) build(params Params) (ImageDetails, error) { return details, fmt.Errorf("failed to build modules: %s", err) } - buildOutDir := filepath.Join(params.KernelDir, "out/dist") + buildOutDir := filepath.Join(params.KernelDir, "out", "dist") bzImage := filepath.Join(buildOutDir, "bzImage") vmlinux := filepath.Join(buildOutDir, "vmlinux") initramfs := filepath.Join(buildOutDir, "initramfs.img") - if err := buildCuttlefishImage(params, bzImage, vmlinux, initramfs); err != nil { - return details, fmt.Errorf("failed to build image: %s", err) + var err error + details.CompilerID, err = a.readCompiler(filepath.Join(buildOutDir, "kernel-headers.tar.gz")) + if err != nil { + return details, err + } + + if err := embedFiles(params, func(mountDir string) error { + homeDir := filepath.Join(mountDir, "root") + + if _, err := osutil.RunCmd(time.Hour, homeDir, "./fetchcvd"); err != nil { + return err + } + + if err := osutil.CopyFile(bzImage, filepath.Join(homeDir, "bzImage")); err != nil { + return err + } + if err := osutil.CopyFile(vmlinux, filepath.Join(homeDir, "vmlinux")); err != nil { + return err + } + if err := osutil.CopyFile(initramfs, filepath.Join(homeDir, "initramfs.img")); err != nil { + return err + } + + return nil + }); err != nil { + return details, err } if err := osutil.CopyFile(vmlinux, filepath.Join(params.OutputDir, "kernel")); err != nil { @@ -60,7 +122,6 @@ func (a android) build(params Params) (ImageDetails, error) { return details, err } - var err error details.Signature, err = elfBinarySignature(vmlinux) if err != nil { return details, fmt.Errorf("failed to generate signature: %s", err) diff --git a/pkg/build/android_linux.go b/pkg/build/android_linux.go deleted file mode 100644 index a6e4e086c..000000000 --- a/pkg/build/android_linux.go +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2022 syzkaller project authors. All rights reserved. -// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. - -package build - -import ( - "fmt" - "io/ioutil" - "os" - "path/filepath" - "time" - - "golang.org/x/sys/unix" - - "github.com/google/syzkaller/pkg/osutil" -) - -// buildCuttlefishImage mounts a disk image, fetches and installs a Cuttlefish emulator binary, -// and copies in the required kernel artifacts. -func buildCuttlefishImage(params Params, bzImage, vmlinux, initramfs string) error { - tempDir, err := ioutil.TempDir("", "syz-build") - if err != nil { - return err - } - defer os.RemoveAll(tempDir) - imageFile := filepath.Join(tempDir, "image") - if err := osutil.CopyFile(params.UserspaceDir, imageFile); err != nil { - return err - } - loop, loopFile, err := linuxSetupLoop(imageFile) - if err != nil { - return err - } - defer func() { - unix.IoctlGetInt(loop, unix.LOOP_CLR_FD) - unix.Close(loop) - }() - mountDir := filepath.Join(tempDir, "mnt") - if err := osutil.MkdirAll(mountDir); err != nil { - return err - } - if err := tryMount(loopFile+"p1", mountDir); err != nil { - return fmt.Errorf("mount(%vp1, %v) failed: %v", loopFile, mountDir, err) - } - defer unix.Unmount(mountDir, 0) - - imageHomeDir := filepath.Join(mountDir, "root") - if _, err := osutil.RunCmd(time.Hour, imageHomeDir, "./fetchcvd"); err != nil { - return fmt.Errorf("run fetch_cvd: %s", err) - } - - if err := osutil.CopyFile(bzImage, filepath.Join(imageHomeDir, "bzImage")); err != nil { - return err - } - if err := osutil.CopyFile(vmlinux, filepath.Join(imageHomeDir, "vmlinux")); err != nil { - return err - } - if err := osutil.CopyFile(initramfs, filepath.Join(imageHomeDir, "initramfs.img")); err != nil { - return err - } - - if err := unix.Unmount(mountDir, 0); err != nil { - return err - } - - return osutil.CopyFile(imageFile, filepath.Join(params.OutputDir, "image")) -} diff --git a/pkg/build/android_nolinux.go b/pkg/build/android_nolinux.go deleted file mode 100644 index 100d7669c..000000000 --- a/pkg/build/android_nolinux.go +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2022 syzkaller project authors. All rights reserved. -// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. - -//go:build !linux -// +build !linux - -package build - -import ( - "errors" -) - -func buildCuttlefishImage(params Params, bzImage, vmlinux, initramfs string) error { - return errors.New("building android cuttlefish image is only supported on linux") -} diff --git a/pkg/build/linux_linux.go b/pkg/build/linux_linux.go index 4a8270353..55f21a7ee 100644 --- a/pkg/build/linux_linux.go +++ b/pkg/build/linux_linux.go @@ -23,6 +23,17 @@ import ( // - cmdline file is not supported (should be moved to kernel config) // - the kernel is stored in the image in /vmlinuz file. func embedLinuxKernel(params Params, kernelPath string) error { + return embedFiles(params, func(mountDir string) error { + if err := copyKernel(mountDir, kernelPath); err != nil { + return err + } + return nil + }) +} + +// embedFiles mounts the disk image specified by params.UserspaceDir and then calls the given +// callback function which should copy files into the image as needed. +func embedFiles(params Params, callback func(mountDir string) error) error { if params.CmdlineFile != "" { return fmt.Errorf("cmdline file is not supported for linux images") } @@ -51,7 +62,7 @@ func embedLinuxKernel(params Params, kernelPath string) error { return fmt.Errorf("mount(%vp1, %v) failed: %v", loopFile, mountDir, err) } defer unix.Unmount(mountDir, 0) - if err := copyKernel(mountDir, kernelPath); err != nil { + if err := callback(mountDir); err != nil { return err } if params.SysctlFile != "" { diff --git a/pkg/build/linux_nolinux.go b/pkg/build/linux_nolinux.go index 0210627f5..03ea0da27 100644 --- a/pkg/build/linux_nolinux.go +++ b/pkg/build/linux_nolinux.go @@ -13,3 +13,7 @@ import ( func embedLinuxKernel(params Params, kernelPath string) error { return errors.New("building linux image is only supported on linux") } + +func embedFiles(params Params, callback func(mountDir string) error) error { + return errors.New("building linux image is only supported on linux") +} -- cgit mrf-deployment