blob: 504fdb1b5df1f8890e5989882fe6c7c8f2915fea [file] [log] [blame]
package binary
import (
"fmt"
"io/ioutil"
"os"
"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"
)
// Global variables
var (
// Command-line path strings
// /etc is the OS configurations directory
etc = "/etc/"
// /etc/os-release is the file describing COS versioning
etcOSRelease = "/etc/os-release"
)
// Differences is a Intermediate Struct used to store all binary differences
// Field names are pre-defined in parse_input.go and will be cross-checked with -binary flag.
type Differences struct {
Version []string
BuildID []string
Rootfs string
KernelCommandLine string
Stateful string
PartitionStructure string
SysctlSettings string
OSConfigs map[string]string
KernelConfigs string
}
// versionDiff calculates the Version difference of two images
func (binaryDiff *Differences) versionDiff(image1, image2 *input.ImageInfo) {
if image1.Version != image2.Version {
binaryDiff.Version = []string{image1.Version, image2.Version}
}
}
// buildDiff calculates the BuildID difference of two images
func (binaryDiff *Differences) buildDiff(image1, image2 *input.ImageInfo) {
if image1.BuildID != image2.BuildID {
binaryDiff.BuildID = []string{image1.BuildID, image2.BuildID}
}
}
// rootfsDiff calculates the Root FS difference of two images
func (binaryDiff *Differences) rootfsDiff(image1, image2 *input.ImageInfo, flagInfo *input.FlagInfo) error {
compressedRoots, err := ioutil.ReadFile(flagInfo.CompressRootfsFile)
if err != nil {
return fmt.Errorf("failed to convert file %v to slice: %v", flagInfo.CompressRootfsFile, err)
}
compressedRootsSlice := strings.Split(string(compressedRoots), "\n")
rootfsDiff, err := directoryDiff(image1.RootfsPartition3, image2.RootfsPartition3, "rootfs", flagInfo.Verbose, compressedRootsSlice)
if err != nil {
return fmt.Errorf("fail to find rootfs difference: %v", err)
}
binaryDiff.Rootfs = rootfsDiff
return nil
}
// osConfigDiff calculates the OsConfig difference of two images
func (binaryDiff *Differences) osConfigDiff(image1, image2 *input.ImageInfo, flagInfo *input.FlagInfo) error {
if err := findOSConfigs(image1, image2, binaryDiff); err != nil {
return fmt.Errorf("failed to find OS Configs: %v", err)
}
for etcEntryName, diff := range binaryDiff.OSConfigs {
etcEntryPath := filepath.Join(etc, etcEntryName)
if diff != "" {
uniqueEntryPath := filepath.Join(diff+"/rootfs/", etcEntryPath)
info, err := os.Stat(uniqueEntryPath)
if err != nil {
return fmt.Errorf("failed to get info on file %v: %v", uniqueEntryPath, err)
}
if info.IsDir() {
binaryDiff.OSConfigs[etcEntryName] = diff + " has unique directory " + etcEntryPath
} else {
binaryDiff.OSConfigs[etcEntryName] = diff + " has unique file " + etcEntryPath
}
} else {
compressedRoots, err := ioutil.ReadFile(flagInfo.CompressRootfsFile)
if err != nil {
return fmt.Errorf("failed to convert file %v to slice: %v", flagInfo.CompressRootfsFile, err)
}
compressedRootsSlice := strings.Split(string(compressedRoots), "\n")
osConfigDiff, err := directoryDiff(filepath.Join(image1.RootfsPartition3, etcEntryPath), filepath.Join(image2.RootfsPartition3, etcEntryPath), "rootfs", flagInfo.Verbose, compressedRootsSlice)
if err != nil {
return fmt.Errorf("fail to find difference on /etc/ entry %v: %v", etcEntryName, err)
}
binaryDiff.OSConfigs[etcEntryName] = osConfigDiff
}
}
return nil
}
// statefulDiff calculates the stateful partition difference of two images
func (binaryDiff *Differences) statefulDiff(image1, image2 *input.ImageInfo, flagInfo *input.FlagInfo) error {
compressedStateful, err := ioutil.ReadFile(flagInfo.CompressStatefulFile)
if err != nil {
return fmt.Errorf("failed to convert file %v to slice: %v", flagInfo.CompressStatefulFile, err)
}
compressedStatefulSlice := strings.Split(string(compressedStateful), "\n")
statefulDiff, err := directoryDiff(image1.StatePartition1, image2.StatePartition1, "stateful", flagInfo.Verbose, compressedStatefulSlice)
if err != nil {
return fmt.Errorf("failed to diff %v and %v: %v", image1.StatePartition1, image2.StatePartition1, err)
}
binaryDiff.Stateful = statefulDiff
return nil
}
// partitionStructureDiff calculates the Version difference of two images
func (binaryDiff *Differences) partitionStructureDiff(image1, image2 *input.ImageInfo) error {
if image2.TempDir != "" {
partitionStructureDiff, err := pureDiff(image1.PartitionFile, image2.PartitionFile)
if err != nil {
return fmt.Errorf("fail to find Partition Structure difference: %v", err)
}
binaryDiff.PartitionStructure = partitionStructureDiff
} else {
image1Structure, err := ioutil.ReadFile(image1.PartitionFile)
if err != nil {
return fmt.Errorf("failed to convert file %v to string: %v", image1.PartitionFile, err)
}
binaryDiff.PartitionStructure = string(image1Structure)
}
return nil
}
// FormatVersionDiff returns a formated string of the version difference
func (binaryDiff *Differences) FormatVersionDiff() string {
if len(binaryDiff.Version) == 2 {
if binaryDiff.Version[1] != "" {
return "-----Version-----\n< " + binaryDiff.Version[0] + "\n> " + binaryDiff.Version[1] + "\n\n"
}
return "-----Version-----\n" + binaryDiff.Version[0] + "\n\n"
}
return ""
}
// FormatBuildIDDiff returns a formated string of the build difference
func (binaryDiff *Differences) FormatBuildIDDiff() string {
if len(binaryDiff.BuildID) == 2 {
if binaryDiff.BuildID[1] != "" {
return "-----BuildID-----\n< " + binaryDiff.BuildID[0] + "\n> " + binaryDiff.BuildID[1] + "\n\n"
}
return "-----BuildID-----\n" + binaryDiff.BuildID[0] + "\n\n"
}
return ""
}
// FormatRootfsDiff returns a formated string of the rootfs difference
func (binaryDiff *Differences) FormatRootfsDiff() string {
if binaryDiff.Rootfs != "" {
return "-----RootFS-----\n " + binaryDiff.Rootfs + "\n\n"
}
return ""
}
// FormatStatefulDiff returns a formated string of the stateful partition difference
func (binaryDiff *Differences) FormatStatefulDiff() string {
if binaryDiff.Stateful != "" {
return "-----Stateful Partition-----\n " + binaryDiff.Stateful + "\n\n"
}
return ""
}
// FormatOSConfigDiff returns a formated string of the OS Config difference
func (binaryDiff *Differences) FormatOSConfigDiff() string {
if len(binaryDiff.OSConfigs) > 0 {
osConfigDifference := "-----OS Configurations-----\n"
for etcEntryName, diff := range binaryDiff.OSConfigs {
if diff != "" {
osConfigDifference += etcEntryName + "\n" + diff + "\n\n"
}
}
return osConfigDifference
}
return ""
}
// FormatPartitionStructureDiff returns a formated string of the partition structure difference
func (binaryDiff *Differences) FormatPartitionStructureDiff() string {
if binaryDiff.PartitionStructure != "" {
return "-----Partition Structure-----\n " + binaryDiff.PartitionStructure + "\n\n"
}
return ""
}
// Diff is a tool that finds all binary differences of two COS images
// (COS version, rootfs, kernel command line, stateful partition, ...)
// Input:
// (*ImageInfo) image1 - A struct that will store binary info for image1
// (*ImageInfo) image2 - A struct that will store binary info for image2
// (*FlagInfo) flagInfo - A struct that holds input preference from the user
// Output:
// (*Differences) BinaryDiff - A struct that will store the binary differences
func Diff(image1, image2 *input.ImageInfo, flagInfo *input.FlagInfo) (*Differences, error) {
BinaryDiff := &Differences{}
if utilities.InArray("Version", flagInfo.BinaryTypesSelected) {
BinaryDiff.versionDiff(image1, image2)
}
if utilities.InArray("BuildID", flagInfo.BinaryTypesSelected) {
BinaryDiff.buildDiff(image1, image2)
}
if utilities.InArray("Partition-structure", flagInfo.BinaryTypesSelected) {
if err := BinaryDiff.partitionStructureDiff(image1, image2); err != nil {
return BinaryDiff, fmt.Errorf("Failed to get Partition-structure difference: %v", err)
}
}
if image2.TempDir != "" {
if utilities.InArray("Rootfs", flagInfo.BinaryTypesSelected) {
if err := BinaryDiff.rootfsDiff(image1, image2, flagInfo); err != nil {
return BinaryDiff, fmt.Errorf("Failed to get Roofs difference: %v", err)
}
}
if utilities.InArray("OS-config", flagInfo.BinaryTypesSelected) {
if err := BinaryDiff.osConfigDiff(image1, image2, flagInfo); err != nil {
return BinaryDiff, fmt.Errorf("Failed to get OS-config difference: %v", err)
}
}
if utilities.InArray("Stateful-partition", flagInfo.BinaryTypesSelected) {
if err := BinaryDiff.statefulDiff(image1, image2, flagInfo); err != nil {
return BinaryDiff, fmt.Errorf("Failed to get Stateful-partition difference: %v", err)
}
}
}
return BinaryDiff, nil
}