diff options
| author | Dmitry Vyukov <dvyukov@google.com> | 2021-01-29 13:02:28 +0100 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2021-01-29 15:48:16 +0100 |
| commit | d0407197ba7cea0b5cdeb67012180fb5d87f0e19 (patch) | |
| tree | 34ecb531a55a96f4ec88a5088d21108e9136542d /pkg | |
| parent | 7de5d7d8442f187ab8eb59a4df93863f1390398a (diff) | |
pkg/build: add a new way of building linux images
Add a new, simpler procedure:
working bootable image is accepted as an input
and we only replace the kernel in it.
This just looks much saner and the right way to do it
(instead of assembling the image from bits and shelling out
to mkfs, fdisk, grub, etc). The new way also works inside of
containers much better.
Diffstat (limited to 'pkg')
| -rw-r--r-- | pkg/build/linux.go | 14 | ||||
| -rw-r--r-- | pkg/build/linux_linux.go | 104 | ||||
| -rw-r--r-- | pkg/build/linux_nolinux.go | 14 |
3 files changed, 125 insertions, 7 deletions
diff --git a/pkg/build/linux.go b/pkg/build/linux.go index f08455c39..be2341311 100644 --- a/pkg/build/linux.go +++ b/pkg/build/linux.go @@ -31,20 +31,20 @@ func (linux linux) build(params *Params) error { return err } kernelPath := filepath.Join(params.KernelDir, filepath.FromSlash(kernelBin(params.TargetArch))) - if fileInfo, err := os.Stat(params.UserspaceDir); err == nil && !fileInfo.IsDir() && params.VMType == "qemu" { + if fileInfo, err := os.Stat(params.UserspaceDir); err == nil && fileInfo.IsDir() { + // The old way of assembling the image from userspace dir. + // It should be removed once all syzbot instances are switched. + return linux.createImage(params, kernelPath) + } + if params.VMType == "qemu" { // If UserspaceDir is a file (image) and we use qemu, we just copy image and kernel to the output dir // assuming that qemu will use injected kernel boot. In this mode we also assume password/key-less ssh. - // In future it would be good to switch to accepting complete disk image always and just replacing - // kernel in it for GCE VMs (assembling image from userspace files in createImage isn't reasonable). if err := osutil.CopyFile(kernelPath, filepath.Join(params.OutputDir, "kernel")); err != nil { return err } return osutil.CopyFile(params.UserspaceDir, filepath.Join(params.OutputDir, "image")) } - if err := linux.createImage(params, kernelPath); err != nil { - return err - } - return nil + return embedLinuxKernel(params, kernelPath) } func (linux linux) sign(params *Params) (string, error) { diff --git a/pkg/build/linux_linux.go b/pkg/build/linux_linux.go new file mode 100644 index 000000000..05a1036e0 --- /dev/null +++ b/pkg/build/linux_linux.go @@ -0,0 +1,104 @@ +// Copyright 2021 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" + "syscall" + "unsafe" + + "github.com/google/syzkaller/pkg/osutil" + "golang.org/x/sys/unix" +) + +// embedLinuxKernel copies a new kernel into an existing disk image. +// There are several assumptions about the image: +// - the image is ext4 (may be inferred from image name if necessary, e.g. "image.btrfs") +// - the data is on partition 1 (we could see what partitions we got and use the last one) +// - ssh works without password (we don't copy the key) +// - 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 { + if params.CmdlineFile != "" { + return fmt.Errorf("cmdline file is not supported for linux images") + } + 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 := unix.Mount(loopFile+"p1", mountDir, "ext4", 0, ""); err != nil { + return err + } + defer unix.Unmount(mountDir, 0) + if err := osutil.CopyFile(kernelPath, filepath.Join(mountDir, "vmlinuz")); err != nil { + return err + } + if params.SysctlFile != "" { + if err := osutil.CopyFile(params.SysctlFile, filepath.Join(mountDir, "etc", "sysctl.conf")); err != nil { + return err + } + } + if err := unix.Unmount(mountDir, 0); err != nil { + return err + } + return osutil.CopyFile(imageFile, filepath.Join(params.OutputDir, "image")) +} + +func linuxSetupLoop(imageFile string) (int, string, error) { + image, err := unix.Open(imageFile, unix.O_RDWR, 0) + if err != nil { + return 0, "", fmt.Errorf("failed to open %v: %v", imageFile, err) + } + defer unix.Close(image) + loopControl, err := unix.Open("/dev/loop-control", unix.O_RDWR, 0) + if err != nil { + return 0, "", fmt.Errorf("failed to open /dev/loop-control: %v", err) + } + defer unix.Close(loopControl) + loopIndex, err := unix.IoctlRetInt(loopControl, unix.LOOP_CTL_GET_FREE) + if err != nil { + return 0, "", fmt.Errorf("LOOP_CTL_GET_FREE failed: %v", err) + } + loopFile := fmt.Sprintf("/dev/loop%v", loopIndex) + loop, err := unix.Open(loopFile, unix.O_RDWR, 0) + if err != nil { + return 0, "", fmt.Errorf("failed to open %v: %v", loopFile, err) + } + if err := unix.IoctlSetInt(loop, unix.LOOP_SET_FD, image); err != nil { + unix.Close(loop) + return 0, "", fmt.Errorf("LOOP_SET_FD failed: %v", err) + } + info := &unix.LoopInfo64{ + Flags: unix.LO_FLAGS_PARTSCAN, + } + for i := 0; i < len(imageFile); i++ { + info.File_name[i] = imageFile[i] + } + if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, uintptr(loop), unix.LOOP_SET_STATUS64, + uintptr(unsafe.Pointer(info))); err != 0 { + unix.Close(loop) + return 0, "", fmt.Errorf("LOOP_SET_STATUS64 failed: %v", err) + } + return loop, loopFile, nil +} diff --git a/pkg/build/linux_nolinux.go b/pkg/build/linux_nolinux.go new file mode 100644 index 000000000..1bffbeaae --- /dev/null +++ b/pkg/build/linux_nolinux.go @@ -0,0 +1,14 @@ +// Copyright 2021 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. + +// +build !linux + +package build + +import ( + "errors" +) + +func embedLinuxKernel(params *Params, kernelPath string) error { + return errors.New("building linux image is only supported on linux") +} |
