| package binary | |
| import ( | |
| "bufio" | |
| "errors" | |
| "fmt" | |
| "os" | |
| "os/exec" | |
| "path/filepath" | |
| "strings" | |
| "cos.googlesource.com/cos/tools.git/src/cmd/cos_image_analyzer/internal/input" | |
| "cos.googlesource.com/cos/tools.git/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, false) | |
| 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 | |
| } |