| 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 |
| } |