Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🧹 Add new methods for detecting and mounting in the volume mounter. #4101

Merged
merged 2 commits into from
May 24, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions providers/os/connection/device/device_connection.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ func NewDeviceConnection(connId uint32, conf *inventory.Config, asset *inventory
}
log.Debug().Str("manager", manager.Name()).Msg("device manager created")

blocks, err := manager.IdentifyBlockDevice(conf.Options)
blocks, err := manager.IdentifyMountTargets(conf.Options)
if err != nil {
return nil, err
}
Expand All @@ -56,7 +56,7 @@ func NewDeviceConnection(connId uint32, conf *inventory.Config, asset *inventory
return nil, errors.New("internal>blocks size is not equal to 1.")
}
block := blocks[0]
log.Debug().Str("device", block.DeviceName).Msg("identified block for mounting")
log.Debug().Str("name", block.Name).Str("type", block.FsType).Msg("identified partition for mounting")

res := &DeviceConnection{
Connection: plugin.NewConnection(connId, asset),
Expand Down
12 changes: 9 additions & 3 deletions providers/os/connection/device/device_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,17 @@

package device

import "go.mondoo.com/cnquery/v11/providers/os/connection/device/shared"
import (
"go.mondoo.com/cnquery/v11/providers/os/connection/snapshot"
)

type DeviceManager interface {
// Name returns the name of the device manager
Name() string
IdentifyBlockDevice(opts map[string]string) ([]shared.MountInfo, error)
Mount(mi shared.MountInfo) (string, error)
// IdentifyMountTargets returns a list of partitions that match the given options and can be mounted
IdentifyMountTargets(opts map[string]string) ([]*snapshot.PartitionInfo, error)
// Mounts the partition and returns the directory it was mounted to
Mount(pi *snapshot.PartitionInfo) (string, error)
// UnmountAndClose unmounts the partitions from the specified dirs and closes the device manager
UnmountAndClose()
}
72 changes: 30 additions & 42 deletions providers/os/connection/device/linux/device_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (

"github.com/cockroachdb/errors"
"github.com/rs/zerolog/log"
"go.mondoo.com/cnquery/v11/providers/os/connection/device/shared"
"go.mondoo.com/cnquery/v11/providers/os/connection/snapshot"
)

Expand Down Expand Up @@ -37,7 +36,7 @@ func (d *LinuxDeviceManager) Name() string {
return "linux"
}

func (d *LinuxDeviceManager) IdentifyBlockDevice(opts map[string]string) ([]shared.MountInfo, error) {
func (d *LinuxDeviceManager) IdentifyMountTargets(opts map[string]string) ([]*snapshot.PartitionInfo, error) {
if err := validateOpts(opts); err != nil {
return nil, err
}
Expand All @@ -46,20 +45,26 @@ func (d *LinuxDeviceManager) IdentifyBlockDevice(opts map[string]string) ([]shar
if err != nil {
return nil, err
}
return d.identifyViaLun(lun)
pi, err := d.identifyViaLun(lun)
if err != nil {
return nil, err
}
return []*snapshot.PartitionInfo{pi}, nil
}

return d.identifyViaDeviceName(opts[DeviceName])
pi, err := d.identifyViaDeviceName(opts[DeviceName])
if err != nil {
return nil, err
}
return []*snapshot.PartitionInfo{pi}, nil
}

func (d *LinuxDeviceManager) Mount(mi shared.MountInfo) (string, error) {
// TODO: we should make the volume mounter return the scan dir from Mount()
// TODO: use the mount info to mount the volume
err := d.volumeMounter.Mount()
func (d *LinuxDeviceManager) Mount(pi *snapshot.PartitionInfo) (string, error) {
scanDir, err := d.volumeMounter.MountP(pi)
if err != nil {
return "", err
}
return d.volumeMounter.ScanDir, nil
return scanDir, nil
}

func (d *LinuxDeviceManager) UnmountAndClose() {
Expand Down Expand Up @@ -92,7 +97,7 @@ func validateOpts(opts map[string]string) error {
return nil
}

func (c *LinuxDeviceManager) identifyViaLun(lun int) ([]shared.MountInfo, error) {
func (c *LinuxDeviceManager) identifyViaLun(lun int) (*snapshot.PartitionInfo, error) {
scsiDevices, err := c.listScsiDevices()
if err != nil {
return nil, err
Expand All @@ -104,43 +109,26 @@ func (c *LinuxDeviceManager) identifyViaLun(lun int) ([]shared.MountInfo, error)
return nil, errors.New("no matching scsi devices found")
}

var target string
// if we have exactly one device present at the LUN we can directly point the volume mounter towards it
if len(filteredScsiDevices) == 1 {
return []shared.MountInfo{{DeviceName: filteredScsiDevices[0].VolumePath}}, nil
}

// we have multiple devices at the same LUN. we find the first non-mounted block devices in that list
blockDevices, err := c.volumeMounter.CmdRunner.GetBlockDevices()
if err != nil {
return nil, err
}
target, err := findMatchingDeviceByBlock(filteredScsiDevices, blockDevices)
if err != nil {
return nil, err
}
c.volumeMounter.VolumeAttachmentLoc = target
return []shared.MountInfo{{DeviceName: target}}, nil
}

func (c *LinuxDeviceManager) identifyViaDeviceName(deviceName string) ([]shared.MountInfo, error) {
blockDevices, err := c.volumeMounter.CmdRunner.GetBlockDevices()
if err != nil {
return nil, err
}
// this is a best-effort approach, we try to find the first unmounted block device as we don't have the device name
if deviceName == "" {
fsInfo, err := blockDevices.GetUnmountedBlockEntry()
target = filteredScsiDevices[0].VolumePath
} else {
// we have multiple devices at the same LUN. we find the first non-mounted block devices in that list
blockDevices, err := c.volumeMounter.CmdRunner.GetBlockDevices()
if err != nil {
return nil, err
}
target, err = findMatchingDeviceByBlock(filteredScsiDevices, blockDevices)
if err != nil {
return nil, err
}
c.volumeMounter.VolumeAttachmentLoc = deviceName
return []shared.MountInfo{{DeviceName: fsInfo.Name}}, nil
}

fsInfo, err := blockDevices.GetBlockEntryByName(deviceName)
if err != nil {
return nil, err
}
c.volumeMounter.VolumeAttachmentLoc = deviceName
return []shared.MountInfo{{DeviceName: fsInfo.Name}}, nil
return c.volumeMounter.GetDeviceForMounting(target)
}

func (c *LinuxDeviceManager) identifyViaDeviceName(deviceName string) (*snapshot.PartitionInfo, error) {
// GetDeviceForMounting also supports passing in empty strings, in that case we do a best-effort guess
return c.volumeMounter.GetDeviceForMounting(deviceName)
}
38 changes: 19 additions & 19 deletions providers/os/connection/snapshot/blockdevices.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ type BlockDevice struct {
FsUse string `json:"fsuse%,omitempty"`
}

type fsInfo struct {
type PartitionInfo struct {
Name string
FsType string
}
Expand Down Expand Up @@ -55,7 +55,7 @@ func (cmdRunner *LocalCommandRunner) GetBlockDevices() (*BlockDevices, error) {
return blockEntries, nil
}

func (blockEntries BlockDevices) GetRootBlockEntry() (*fsInfo, error) {
func (blockEntries BlockDevices) GetRootBlockEntry() (*PartitionInfo, error) {
log.Debug().Msg("get root block entry")
for i := range blockEntries.BlockDevices {
d := blockEntries.BlockDevices[i]
Expand All @@ -64,14 +64,14 @@ func (blockEntries BlockDevices) GetRootBlockEntry() (*fsInfo, error) {
entry := d.Children[i]
if entry.IsNoBootVolume() {
devFsName := "/dev/" + entry.Name
return &fsInfo{Name: devFsName, FsType: entry.FsType}, nil
return &PartitionInfo{Name: devFsName, FsType: entry.FsType}, nil
}
}
}
return nil, errors.New("target volume not found on instance")
}

func (blockEntries BlockDevices) GetBlockEntryByName(name string) (*fsInfo, error) {
func (blockEntries BlockDevices) GetBlockEntryByName(name string) (*PartitionInfo, error) {
log.Debug().Str("name", name).Msg("get matching block entry")
var secondName string
if strings.HasPrefix(name, "/dev/sd") {
Expand All @@ -91,57 +91,57 @@ func (blockEntries BlockDevices) GetBlockEntryByName(name string) (*fsInfo, erro
continue
}
}
log.Debug().Msg("found match")
log.Debug().Str("location", d.Name).Msg("found match")
for i := range d.Children {
entry := d.Children[i]
if entry.IsNotBootOrRootVolumeAndUnmounted() {
devFsName := "/dev/" + entry.Name
return &fsInfo{Name: devFsName, FsType: entry.FsType}, nil
return &PartitionInfo{Name: devFsName, FsType: entry.FsType}, nil
}
}
}
return nil, errors.New("target volume not found on instance")
}

func (blockEntries BlockDevices) GetUnnamedBlockEntry() (*fsInfo, error) {
fsInfo, err := blockEntries.GetUnmountedBlockEntry()
if err == nil && fsInfo != nil {
return fsInfo, nil
func (blockEntries BlockDevices) GetUnnamedBlockEntry() (*PartitionInfo, error) {
pInfo, err := blockEntries.GetUnmountedBlockEntry()
if err == nil && pInfo != nil {
return pInfo, nil
} else {
// if we get here, there was no non-root, non-mounted volume on the instance
// this is expected in the "no setup" case where we start an instance with the target
// volume attached and only that volume attached
fsInfo, err = blockEntries.GetRootBlockEntry()
if err == nil && fsInfo != nil {
return fsInfo, nil
pInfo, err = blockEntries.GetRootBlockEntry()
if err == nil && pInfo != nil {
return pInfo, nil
}
}
return nil, errors.New("target volume not found on instance")
}

func (blockEntries BlockDevices) GetUnmountedBlockEntry() (*fsInfo, error) {
func (blockEntries BlockDevices) GetUnmountedBlockEntry() (*PartitionInfo, error) {
log.Debug().Msg("get unmounted block entry")
for i := range blockEntries.BlockDevices {
d := blockEntries.BlockDevices[i]
log.Debug().Str("name", d.Name).Interface("children", d.Children).Interface("mountpoint", d.MountPoint).Msg("found block device")
if d.MountPoint != "" { // empty string means it is not mounted
continue
}
if fsinfo := findVolume(d.Children); fsinfo != nil {
return fsinfo, nil
if pInfo := findVolume(d.Children); pInfo != nil {
return pInfo, nil
}
}
return nil, errors.New("target volume not found on instance")
}

func findVolume(children []BlockDevice) *fsInfo {
var fs *fsInfo
func findVolume(children []BlockDevice) *PartitionInfo {
var fs *PartitionInfo
for i := range children {
entry := children[i]
if entry.IsNotBootOrRootVolumeAndUnmounted() {
// we are NOT searching for the root volume here, so we can exclude the "sda" and "xvda" volumes
devFsName := "/dev/" + entry.Name
fs = &fsInfo{Name: devFsName, FsType: entry.FsType}
fs = &PartitionInfo{Name: devFsName, FsType: entry.FsType}
}
}
return fs
Expand Down
40 changes: 20 additions & 20 deletions providers/os/connection/snapshot/blockdevices_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,40 +21,40 @@ func TestGetMatchingBlockEntryByName(t *testing.T) {
{Name: "sdx", Children: []BlockDevice{{Uuid: "12346", FsType: "xfs", Label: "ROOT", Name: "sdh1"}, {Uuid: "12345", FsType: "", Label: "EFI"}}},
}...)

realFsInfo, err := blockEntries.GetBlockEntryByName("/dev/sdx")
realPartitionInfo, err := blockEntries.GetBlockEntryByName("/dev/sdx")
require.Nil(t, err)
require.Equal(t, fsInfo{FsType: "xfs", Name: "/dev/sdh1"}, *realFsInfo)
require.Equal(t, PartitionInfo{FsType: "xfs", Name: "/dev/sdh1"}, *realPartitionInfo)

blockEntries = BlockDevices{BlockDevices: []BlockDevice{RootDevice}}
blockEntries.BlockDevices = append(blockEntries.BlockDevices, []BlockDevice{
{Name: "nvme0n1", Children: []BlockDevice{{Uuid: "12345", FsType: "xfs", Label: "ROOT", Name: "nvmd1n1"}, {Uuid: "12345", FsType: "", Label: "EFI"}}},
{Name: "xvdx", Children: []BlockDevice{{Uuid: "12346", FsType: "xfs", Label: "ROOT", Name: "xvdh1"}, {Uuid: "12345", FsType: "", Label: "EFI"}}},
}...)

realFsInfo, err = blockEntries.GetBlockEntryByName("/dev/sdx")
realPartitionInfo, err = blockEntries.GetBlockEntryByName("/dev/sdx")
require.Nil(t, err)
require.Equal(t, fsInfo{FsType: "xfs", Name: "/dev/xvdh1"}, *realFsInfo)
require.Equal(t, PartitionInfo{FsType: "xfs", Name: "/dev/xvdh1"}, *realPartitionInfo)

blockEntries = BlockDevices{BlockDevices: []BlockDevice{RootDevice}}
blockEntries.BlockDevices = append(blockEntries.BlockDevices, []BlockDevice{
{Name: "nvme0n1", Children: []BlockDevice{{Uuid: "12345", FsType: "xfs", Label: "ROOT", Name: "nvmd1n1"}, {Uuid: "12345", FsType: "", Label: "EFI"}}},
{Name: "xvdh", Children: []BlockDevice{{Uuid: "12346", FsType: "xfs", Label: "ROOT", Name: "xvdh1"}, {Uuid: "12345", FsType: "", Label: "EFI"}}},
}...)

realFsInfo, err = blockEntries.GetBlockEntryByName("/dev/xvdh")
realPartitionInfo, err = blockEntries.GetBlockEntryByName("/dev/xvdh")
require.Nil(t, err)
require.Equal(t, fsInfo{FsType: "xfs", Name: "/dev/xvdh1"}, *realFsInfo)
require.Equal(t, PartitionInfo{FsType: "xfs", Name: "/dev/xvdh1"}, *realPartitionInfo)

blockEntries = BlockDevices{BlockDevices: []BlockDevice{RootDevice}}
blockEntries.BlockDevices = append(blockEntries.BlockDevices, []BlockDevice{
{Name: "nvme0n1", Children: []BlockDevice{{Uuid: "12345", FsType: "xfs", Label: "ROOT", Name: "nvmd1n1"}, {Uuid: "12345", FsType: "", Label: "EFI"}}},
}...)

realFsInfo, err = blockEntries.GetBlockEntryByName("/dev/sdh")
_, err = blockEntries.GetBlockEntryByName("/dev/sdh")
require.Error(t, err)

blockEntries = BlockDevices{BlockDevices: []BlockDevice{RootDevice}}
realFsInfo, err = blockEntries.GetBlockEntryByName("/dev/sdh")
_, err = blockEntries.GetBlockEntryByName("/dev/sdh")
require.Error(t, err)
}

Expand All @@ -63,16 +63,16 @@ func TestGetNonRootBlockEntry(t *testing.T) {
blockEntries.BlockDevices = append(blockEntries.BlockDevices, []BlockDevice{
{Name: "nvme0n1", Children: []BlockDevice{{Uuid: "12345", FsType: "xfs", Label: "ROOT", Name: "nvmd1n1"}, {Uuid: "12345", FsType: "", Label: "EFI"}}},
}...)
realFsInfo, err := blockEntries.GetUnmountedBlockEntry()
realPartitionInfo, err := blockEntries.GetUnmountedBlockEntry()
require.Nil(t, err)
require.Equal(t, fsInfo{FsType: "xfs", Name: "/dev/nvmd1n1"}, *realFsInfo)
require.Equal(t, PartitionInfo{FsType: "xfs", Name: "/dev/nvmd1n1"}, *realPartitionInfo)
}

func TestGetRootBlockEntry(t *testing.T) {
blockEntries := BlockDevices{BlockDevices: []BlockDevice{RootDevice}}
realFsInfo, err := blockEntries.GetRootBlockEntry()
realPartitionInfo, err := blockEntries.GetRootBlockEntry()
require.Nil(t, err)
require.Equal(t, fsInfo{FsType: "xfs", Name: "/dev/sda1"}, *realFsInfo)
require.Equal(t, PartitionInfo{FsType: "xfs", Name: "/dev/sda1"}, *realPartitionInfo)
}

func TestGetRootBlockEntryRhel8(t *testing.T) {
Expand All @@ -83,13 +83,13 @@ func TestGetRootBlockEntryRhel8(t *testing.T) {
err = json.Unmarshal(data, &blockEntries)
require.NoError(t, err)

rootFsInfo, err := blockEntries.GetRootBlockEntry()
rootPartitionInfo, err := blockEntries.GetRootBlockEntry()
require.NoError(t, err)
require.Equal(t, fsInfo{FsType: "xfs", Name: "/dev/sda2"}, *rootFsInfo)
require.Equal(t, PartitionInfo{FsType: "xfs", Name: "/dev/sda2"}, *rootPartitionInfo)

rootFsInfo, err = blockEntries.GetUnnamedBlockEntry()
rootPartitionInfo, err = blockEntries.GetUnnamedBlockEntry()
require.NoError(t, err)
require.Equal(t, fsInfo{FsType: "xfs", Name: "/dev/sdc2"}, *rootFsInfo)
require.Equal(t, PartitionInfo{FsType: "xfs", Name: "/dev/sdc2"}, *rootPartitionInfo)
}

func TestGetRootBlockEntryRhelNoLabels(t *testing.T) {
Expand All @@ -100,13 +100,13 @@ func TestGetRootBlockEntryRhelNoLabels(t *testing.T) {
err = json.Unmarshal(data, &blockEntries)
require.NoError(t, err)

rootFsInfo, err := blockEntries.GetRootBlockEntry()
rootPartitionInfo, err := blockEntries.GetRootBlockEntry()
require.NoError(t, err)
require.Equal(t, fsInfo{FsType: "xfs", Name: "/dev/sda2"}, *rootFsInfo)
require.Equal(t, PartitionInfo{FsType: "xfs", Name: "/dev/sda2"}, *rootPartitionInfo)

rootFsInfo, err = blockEntries.GetUnnamedBlockEntry()
rootPartitionInfo, err = blockEntries.GetUnnamedBlockEntry()
require.NoError(t, err)
require.Equal(t, fsInfo{FsType: "ext4", Name: "/dev/sdb1"}, *rootFsInfo)
require.Equal(t, PartitionInfo{FsType: "ext4", Name: "/dev/sdb1"}, *rootPartitionInfo)
}

func TestAttachedBlockEntry(t *testing.T) {
Expand Down
Loading
Loading