go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/providers/os/connection/snapshot/blockdevices.go (about)

     1  // Copyright (c) Mondoo, Inc.
     2  // SPDX-License-Identifier: BUSL-1.1
     3  
     4  package snapshot
     5  
     6  import (
     7  	"errors"
     8  	"strings"
     9  
    10  	"github.com/rs/zerolog/log"
    11  )
    12  
    13  type blockDevices struct {
    14  	BlockDevices []blockDevice `json:"blockDevices,omitempty"`
    15  }
    16  
    17  type blockDevice struct {
    18  	Name       string        `json:"name,omitempty"`
    19  	FsType     string        `json:"fstype,omitempty"`
    20  	Label      string        `json:"label,omitempty"`
    21  	Uuid       string        `json:"uuid,omitempty"`
    22  	MountPoint string        `json:"mountpoint,omitempty"`
    23  	Children   []blockDevice `json:"children,omitempty"`
    24  }
    25  
    26  type fsInfo struct {
    27  	name   string
    28  	fstype string
    29  }
    30  
    31  func (blockEntries blockDevices) GetRootBlockEntry() (*fsInfo, error) {
    32  	log.Debug().Msg("get root block entry")
    33  	for i := range blockEntries.BlockDevices {
    34  		d := blockEntries.BlockDevices[i]
    35  		log.Debug().Str("name", d.Name).Interface("children", d.Children).Interface("mountpoint", d.MountPoint).Msg("found block device")
    36  		for i := range d.Children {
    37  			entry := d.Children[i]
    38  			if entry.IsNoBootVolume() {
    39  				devFsName := "/dev/" + entry.Name
    40  				return &fsInfo{name: devFsName, fstype: entry.FsType}, nil
    41  			}
    42  		}
    43  	}
    44  	return nil, errors.New("target volume not found on instance")
    45  }
    46  
    47  func (blockEntries blockDevices) GetBlockEntryByName(name string) (*fsInfo, error) {
    48  	log.Debug().Str("name", name).Msg("get matching block entry")
    49  	var secondName string
    50  	if strings.HasPrefix(name, "/dev/sd") {
    51  		// sdh and xvdh are interchangeable
    52  		end := strings.TrimPrefix(name, "/dev/sd")
    53  		secondName = "/dev/xvd" + end
    54  	}
    55  	for i := range blockEntries.BlockDevices {
    56  		d := blockEntries.BlockDevices[i]
    57  		log.Debug().Str("name", d.Name).Interface("children", d.Children).Interface("mountpoint", d.MountPoint).Msg("found block device")
    58  		fullDeviceName := "/dev/" + d.Name
    59  		if name != fullDeviceName { // check if the device name matches
    60  			if secondName == "" {
    61  				continue
    62  			}
    63  			if secondName != fullDeviceName { // check if the device name matches the second name option (sdh and xvdh are interchangeable)
    64  				continue
    65  			}
    66  		}
    67  		log.Debug().Msg("found match")
    68  		for i := range d.Children {
    69  			entry := d.Children[i]
    70  			if entry.IsNoBootVolumeAndUnmounted() {
    71  				devFsName := "/dev/" + entry.Name
    72  				return &fsInfo{name: devFsName, fstype: entry.FsType}, nil
    73  			}
    74  		}
    75  	}
    76  	return nil, errors.New("target volume not found on instance")
    77  }
    78  
    79  func (blockEntries blockDevices) GetUnnamedBlockEntry() (*fsInfo, error) {
    80  	fsInfo, err := blockEntries.GetUnmountedBlockEntry()
    81  	if err == nil && fsInfo != nil {
    82  		return fsInfo, nil
    83  	} else {
    84  		// if we get here, there was no non-root, non-mounted volume on the instance
    85  		// this is expected in the "no setup" case where we start an instance with the target
    86  		// volume attached and only that volume attached
    87  		fsInfo, err = blockEntries.GetRootBlockEntry()
    88  		if err == nil && fsInfo != nil {
    89  			return fsInfo, nil
    90  		}
    91  	}
    92  	return nil, errors.New("target volume not found on instance")
    93  }
    94  
    95  func (blockEntries blockDevices) GetUnmountedBlockEntry() (*fsInfo, error) {
    96  	log.Debug().Msg("get unmounted block entry")
    97  	for i := range blockEntries.BlockDevices {
    98  		d := blockEntries.BlockDevices[i]
    99  		log.Debug().Str("name", d.Name).Interface("children", d.Children).Interface("mountpoint", d.MountPoint).Msg("found block device")
   100  		if d.MountPoint != "" { // empty string means it is not mounted
   101  			continue
   102  		}
   103  		for i := range d.Children {
   104  			entry := d.Children[i]
   105  			if entry.IsNoBootVolumeAndUnmounted() {
   106  				devFsName := "/dev/" + entry.Name
   107  				return &fsInfo{name: devFsName, fstype: entry.FsType}, nil
   108  			}
   109  		}
   110  	}
   111  	return nil, errors.New("target volume not found on instance")
   112  }
   113  
   114  func (entry blockDevice) IsNoBootVolume() bool {
   115  	return entry.Uuid != "" && entry.FsType != "" && entry.FsType != "vfat" && entry.Label != "EFI"
   116  }
   117  
   118  func (entry blockDevice) IsNoBootVolumeAndUnmounted() bool {
   119  	return entry.IsNoBootVolume() && entry.MountPoint == ""
   120  }