blob: c0d21c26186211d4a262cfd85acd24f480b0eafd [file] [log] [blame]
package binary
import (
"bufio"
"errors"
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"cos.googlesource.com/cos/tools/src/cmd/cos_image_analyzer/internal/input"
"cos.googlesource.com/cos/tools/src/cmd/cos_image_analyzer/internal/utilities"
)
const cosGCSBucket = "cos-tools"
const kernelHeaderGCSObject = "kernel-headers.tgz"
const pathToKernelConfigs = "usr/src/linux-headers-4.19.112+/.config"
const pathToKernelCommandLine = "efi/boot/grub.cfg" // Located in partition 12 EFI
const kclImageName = "verified image A"
const startOfHashingKCL = "dm="
const pathToSysctlSettings = "/etc/sysctl.d/00-sysctl.conf" // Located in partition 3 Root-A
// getPartitionStructure returns the partition structure of .raw file
func getPartitionStructure(image *input.ImageInfo) error {
if image.TempDir == "" {
return nil
}
out, err := exec.Command("sudo", "sgdisk", "-p", image.DiskFile).Output()
if err != nil {
return fmt.Errorf("failed to call sgdisk -p %v: %v", image.DiskFile, err)
}
partitionFile := filepath.Join(image.TempDir, "partitions.txt")
if err := utilities.WriteToNewFile(partitionFile, string(out[:])); err != nil {
return fmt.Errorf("failed create file %v and write %v: %v", partitionFile, string(out[:]), err)
}
image.PartitionFile = partitionFile
return nil
}
// getKernelConfigs downloads the kernel configs for a build from GCS and stores
// it into the image's temporary directory
func getKernelConfigs(image *input.ImageInfo) error {
gcsObject := filepath.Join(image.BuildID, kernelHeaderGCSObject)
tarFile, err := utilities.GcsDowndload(cosGCSBucket, gcsObject, image.TempDir, kernelHeaderGCSObject)
if err != nil {
return fmt.Errorf("failed to download GCS object %v from bucket %v: %v", gcsObject, cosGCSBucket, err)
}
_, err = exec.Command("tar", "-xf", tarFile, "-C", image.TempDir).Output()
if err != nil {
return fmt.Errorf("failed to unzip %v into %v: %v", tarFile, image.TempDir, err)
}
image.KernelConfigsFile = filepath.Join(image.TempDir, pathToKernelConfigs)
return nil
}
// getKernelCommandLine gets the kernel command line from the image's partition 12 EFI
// located in the /efi/boot/grub.cfg file
func getKernelCommandLine(image *input.ImageInfo) error {
kclPath := filepath.Join(image.EFIPartition12, pathToKernelCommandLine)
kclFile, err := os.Open(kclPath)
if err != nil {
return fmt.Errorf("Failed to open file %v: %v", kclPath, err)
}
defer kclFile.Close()
foundKCL := false
scanner := bufio.NewScanner(kclFile)
for scanner.Scan() { // Scan file line by line for "verified Image A"
kcl := string(scanner.Text()[:])
if foundKCL {
if hashStart := strings.Index(kcl, startOfHashingKCL); hashStart >= 0 {
kcl = kcl[:hashStart] // Remove hash "dm='....'" from kcl
}
image.KernelCommandLine = strings.TrimSpace(kcl)
return nil
}
if strings.Contains(kcl, kclImageName) {
foundKCL = true
}
}
if scanner.Err() != nil {
return fmt.Errorf("Failed to scan file %v: %v", kclPath, scanner.Err())
}
return nil
}
// getSysctlSettings finds an image's Sysctrl settings file under
// the /etc/sysctrl.d/00-sysctl.conf
func getSysctlSettings(image *input.ImageInfo) error {
sysctlPath := filepath.Join(image.RootfsPartition3, pathToSysctlSettings)
image.SysctlSettingsFile = sysctlPath
return nil
}
// GetBinaryInfo finds relevant binary information for the COS image
func GetBinaryInfo(image *input.ImageInfo, flagInfo *input.FlagInfo) error {
if image.TempDir == "" {
return nil
}
if image.RootfsPartition3 != "" { // Get Version and BuildID
osReleaseMap, err := utilities.ReadFileToMap(image.RootfsPartition3+etcOSRelease, "=")
if err != nil {
return fmt.Errorf("Failed to read /etc/os-release file in rootfs of image %v : %v", image.TempDir, err)
}
var ok bool
if image.Version, ok = osReleaseMap["VERSION"]; !ok {
return errors.New("Error: \"Version\" field not found in /etc/os-release file")
}
if image.BuildID, ok = osReleaseMap["BUILD_ID"]; !ok {
return errors.New("Error: \"Build_ID\" field not found in /etc/os-release file")
}
}
if utilities.InArray("Partition-structure", flagInfo.BinaryTypesSelected) { // Get partition structure from "sgdisk -p"
if err := getPartitionStructure(image); err != nil {
return fmt.Errorf("failed to get partition structure for image %v: %v", image.TempDir, err)
}
}
if utilities.InArray("Kernel-configs", flagInfo.BinaryTypesSelected) { // Get kernel configs from gs://cos-tools/BuildID/kernel-headers.tgz
if err := getKernelConfigs(image); err != nil {
return fmt.Errorf("failed to get kernel configs for image %v: %v", image.TempDir, err)
}
}
if utilities.InArray("Kernel-command-line", flagInfo.BinaryTypesSelected) { // Get kernel command line from partition 12 EFI (efi/boot/grub.cfg)
if err := getKernelCommandLine(image); err != nil {
return fmt.Errorf("failed to get the kernel command line for image %v: %v", image.TempDir, err)
}
}
if utilities.InArray("Sysctl-settings", flagInfo.BinaryTypesSelected) { // Get Sysctl settings from /etc/sysctl.d/00-sysctl.conf
if err := getSysctlSettings(image); err != nil {
return fmt.Errorf("failed to get Sysctl-settings for image %v: %v", image.TempDir, err)
}
}
return nil
}