Skip to content

Commit 0d668b1

Browse files
authored
🐛 fstab discovery improvements (#5207)
* feat: VolumeMounter interface & mock * check: empty device name * feat: add PARTUUID support * feat: more reliable fstab structure matching * unit test: mountWithFstab * fix: handle subvolumes * lint * feat: handle pre-mounted rood part * fix: unknown platforms * prevent fsConn from overwrite * cleanup: use local CmdRunner instead of exposing CmdRunner at volumeMounter
1 parent e706d5f commit 0d668b1

File tree

10 files changed

+595
-60
lines changed

10 files changed

+595
-60
lines changed

Diff for: .gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,4 @@ providers/mock_plugin_interface.go
1919
providers/mock_schema.go
2020
providers-sdk/*/testutils/mockprovider/resources/*.resources.json
2121
!providers/core/resources/*.resources.json
22+
providers/os/connection/snapshot/mock_volumemounter.go

Diff for: Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -730,7 +730,7 @@ race/go:
730730
go test -race go.mondoo.com/cnquery/v11/explorer/scan
731731

732732
test/generate: prep/tools/mockgen
733-
go generate ./providers
733+
go generate ./providers/...
734734

735735
test/go: cnquery/generate test/generate test/go/plain
736736

Diff for: providers/os/connection/device/device_connection.go

+11-9
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package device
66
import (
77
"errors"
88
"runtime"
9+
"slices"
910
"strings"
1011

1112
"github.com/rs/zerolog/log"
@@ -129,19 +130,14 @@ func NewDeviceConnection(connId uint32, conf *inventory.Config, asset *inventory
129130

130131
res.partitions[block.MountPoint] = block
131132

132-
if asset.Platform != nil {
133-
log.Debug().Msg("device connection> asset already detected, skipping")
134-
continue
135-
}
136-
137133
if skipAssetDetection {
138134
log.Debug().Msg("device connection> skipping asset detection as requested")
139135
continue
140136
}
141137

142138
if fsConn, err := tryDetectAsset(connId, block, conf, asset); err != nil {
143139
log.Error().Err(err).Msg("partition did not return an asset, continuing")
144-
} else {
140+
} else if fsConn != nil {
145141
res.FileSystemConnection = fsConn
146142
}
147143
}
@@ -252,8 +248,6 @@ func tryDetectAsset(connId uint32, partition *snapshot.PartitionInfo, conf *inve
252248
return nil, errors.New("device connection> no platform detected")
253249
}
254250

255-
log.Debug().Str("scan_dir", partition.MountPoint).Msg("device connection> detected platform from device")
256-
asset.Platform = p
257251
if asset.Name == "" && fingerprint != nil {
258252
asset.Name = fingerprint.Name
259253
}
@@ -265,5 +259,13 @@ func tryDetectAsset(connId uint32, partition *snapshot.PartitionInfo, conf *inve
265259

266260
asset.Id = conf.Type
267261

268-
return fsConn, nil
262+
// volumes and partitions without a system on them would return an "unknown" platform
263+
// we don't want to overwrite the platform if a "proper" one was detected already
264+
// as well as we want to always have some platform to the asset
265+
if asset.Platform == nil || !slices.Contains([]string{"", "unknown"}, p.Name) {
266+
asset.Platform = p
267+
return fsConn, nil
268+
}
269+
270+
return nil, nil
269271
}

Diff for: providers/os/connection/device/linux/device_manager.go

+60-22
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ const (
3535
)
3636

3737
type LinuxDeviceManager struct {
38-
volumeMounter *snapshot.VolumeMounter
38+
volumeMounter snapshot.VolumeMounter
39+
cmdRunner *snapshot.LocalCommandRunner
3940
opts map[string]string
4041
}
4142

@@ -46,6 +47,7 @@ func NewLinuxDeviceManager(shell []string, opts map[string]string) (*LinuxDevice
4647

4748
return &LinuxDeviceManager{
4849
volumeMounter: snapshot.NewVolumeMounter(shell),
50+
cmdRunner: &snapshot.LocalCommandRunner{Shell: shell},
4951
opts: opts,
5052
}, nil
5153
}
@@ -161,7 +163,7 @@ func (d *LinuxDeviceManager) hintFSTypes(partitions []*snapshot.PartitionInfo) (
161163
}
162164

163165
func (d *LinuxDeviceManager) attemptFindFstab(dir string) ([]resources.FstabEntry, error) {
164-
cmd, err := d.volumeMounter.CmdRunner.RunCommand(fmt.Sprintf("find %s -type f -wholename '*/etc/fstab'", dir))
166+
cmd, err := d.cmdRunner.RunCommand(fmt.Sprintf("find %s -type f -wholename '*/etc/fstab'", dir))
165167
if err != nil {
166168
log.Error().Err(err).Msg("error searching for fstab")
167169
return nil, nil
@@ -281,46 +283,75 @@ func (d *LinuxDeviceManager) mountWithFstab(partitions []*snapshot.PartitionInfo
281283
return pathDepth(entries[i].Mountpoint) < pathDepth(entries[j].Mountpoint)
282284
})
283285

286+
rootScanDir := ""
284287
for _, entry := range entries {
285288
for i := range partitions {
286-
if !cmpPartition2Fstab(partitions[i], entry) {
289+
partition := partitions[i]
290+
mustAppend := false
291+
if !cmpPartition2Fstab(partition, entry) {
287292
continue
288293
}
289294

290-
if partitions[i].MountPoint == "" {
291-
partitions[i].MountOptions = entry.Options
292-
log.Debug().Str("device", partitions[i].Name).
293-
Strs("options", partitions[i].MountOptions).
294-
Msg("mounting partition")
295-
mnt, err := d.Mount(partitions[i])
296-
if err != nil {
297-
log.Error().Err(err).Str("device", partitions[i].Name).Msg("unable to mount partition")
298-
return partitions, err
295+
log.Debug().
296+
Str("device", partition.Name).
297+
Str("guest-mountpoint", entry.Mountpoint).
298+
Str("host-mountpouint", partition.MountPoint).
299+
Msg("partition matches fstab entry")
300+
301+
// if the partition is already mounted
302+
if partition.MountPoint != "" {
303+
mountedWithFstab := strings.HasPrefix(partition.MountPoint, rootScanDir)
304+
// mounted without fstab consideration, unmount it
305+
if rootScanDir == "" || !mountedWithFstab {
306+
log.Debug().Str("device", partition.Name).Msg("partition already mounted")
307+
// if the partition is mounted and is the root, we can keep it mounted
308+
if entry.Mountpoint == "/" {
309+
rootScanDir = partition.MountPoint
310+
continue
311+
}
312+
if err := d.volumeMounter.UmountP(partition); err != nil {
313+
log.Error().Err(err).Str("device", partition.Name).Msg("unable to unmount partition")
314+
continue
315+
}
316+
partition.MountPoint = ""
317+
} else if mountedWithFstab { // mounted with fstab, duplicate the partition (probably a subvolume)
318+
partitionCopy := *partition
319+
partition = &partitionCopy
320+
mustAppend = true
299321
}
300-
partitions[i].MountPoint = mnt
301-
302-
break
303322
}
304323

305-
// if partitions is already mounted, but there is an entry in fstab, we should register it and mount as subvolume
306-
partition := ptr.To(*partitions[i]) // copy the partition
324+
var scanDir *string
325+
if rootScanDir != "" {
326+
scanDir = ptr.To(path.Join(rootScanDir, entry.Mountpoint))
327+
}
307328
partition.MountOptions = entry.Options
329+
308330
log.Debug().Str("device", partition.Name).
309331
Strs("options", partition.MountOptions).
310-
Str("scan-dir", path.Join(partition.MountPoint, entry.Mountpoint)).
332+
Any("scan-dir", scanDir).
311333
Msg("mounting partition as subvolume")
312334
mnt, err := d.volumeMounter.MountP(&snapshot.MountPartitionDto{
313335
PartitionInfo: partition,
314-
ScanDir: ptr.To(path.Join(partition.MountPoint, entry.Mountpoint)),
336+
ScanDir: scanDir,
315337
})
316338
if err != nil {
317339
log.Error().Err(err).Str("device", partition.Name).Msg("unable to mount partition")
318340
return partitions, err
319341
}
342+
320343
partition.MountPoint = mnt
321-
partitions = append(partitions, partition)
344+
if entry.Mountpoint == "/" {
345+
rootScanDir = mnt
346+
}
322347

323-
break
348+
if mustAppend {
349+
partitions = append(partitions, partition)
350+
} else {
351+
partitions[i] = partition
352+
}
353+
354+
break // partition matched, no need to check the rest
324355
}
325356
}
326357
return partitions, nil
@@ -348,6 +379,8 @@ func cmpPartition2Fstab(partition *snapshot.PartitionInfo, entry resources.Fstab
348379
return partition.Uuid == parts[1]
349380
case "LABEL":
350381
return partition.Label == parts[1]
382+
case "PARTUUID":
383+
return partition.PartUuid == parts[1]
351384
default:
352385
log.Warn().Str("device", entry.Device).Msg("couldn't identify fstab device")
353386
return false
@@ -443,7 +476,12 @@ func (c *LinuxDeviceManager) identifyDeviceViaLun(lun int) ([]snapshot.BlockDevi
443476
}
444477

445478
func (c *LinuxDeviceManager) identifyViaDeviceName(deviceName string, mountAll bool, includeMounted bool) ([]*snapshot.PartitionInfo, error) {
446-
blockDevices, err := c.volumeMounter.CmdRunner.GetBlockDevices()
479+
if deviceName == "" {
480+
log.Warn().Msg("can't identify partition via device name, no device name provided")
481+
return []*snapshot.PartitionInfo{}, nil
482+
}
483+
484+
blockDevices, err := c.cmdRunner.GetBlockDevices()
447485
if err != nil {
448486
return nil, err
449487
}

0 commit comments

Comments
 (0)