Skip to content

Commit 2aac182

Browse files
Fix resolving device mapper related devices for non symlinks
Under certain circumstances files under `/dev/mapper` are not symlinks, but instead device nodes. This was causing issues, as the code was always expecting to find symlinks. A fix has been put into place, to resolve the given device node to a path to a device mapper device under `/dev`.
1 parent f0801ee commit 2aac182

File tree

3 files changed

+82
-4
lines changed

3 files changed

+82
-4
lines changed

cmd/agent/daemon/pipeline/storage_info_provider.go

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -520,17 +520,37 @@ func (s *SysfsStorageInfoProvider) getBackingDevices(device string) []string {
520520

521521
// getLVMDMDevice resolves LVM logical volumes to their dm device.
522522
// It handles the symlink chain: /dev/mapper/vg-lv → /dev/dm-X
523+
// It also handled block devices.
523524
func (s *SysfsStorageInfoProvider) getLVMDMDevice(device string) []string {
524525
if !strings.HasPrefix(device, "/dev/mapper/") {
525526
return nil
526527
}
527528
hostMapperPath := filepath.Join(s.hostRootPath, device)
528-
linkTarget, err := os.Readlink(hostMapperPath)
529-
if err != nil {
530-
s.log.Errorf("symlink resolution failed for %s: %v", device, err)
529+
530+
var stat unix.Stat_t
531+
if err := unix.Lstat(hostMapperPath, &stat); err != nil {
532+
s.log.Errorf("cannot read stat of %s: %v", hostMapperPath, err)
533+
return nil
534+
}
535+
536+
switch stat.Mode & unix.S_IFMT {
537+
// It is most common for devices under /dev/mapper to be symlinks, so check this first.
538+
case unix.S_IFLNK:
539+
linkTarget, err := os.Readlink(hostMapperPath)
540+
if err != nil {
541+
s.log.Errorf("symlink resolution failed for %s: %v", device, err)
542+
return nil
543+
}
544+
return []string{"/dev/" + filepath.Base(linkTarget)}
545+
546+
// Devices under /dev/mapper can also be block devices.
547+
case unix.S_IFBLK:
548+
return s.findDMDeviceByMajorMinor(hostMapperPath, stat.Rdev)
549+
550+
default:
551+
s.log.Errorf("unhandled file type for %s: %d", hostMapperPath, stat.Mode)
531552
return nil
532553
}
533-
return []string{"/dev/" + filepath.Base(linkTarget)}
534554
}
535555

536556
func (s *SysfsStorageInfoProvider) BuildBlockDeviceMetrics(timestamp time.Time) ([]BlockDeviceMetric, error) {
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
//go:build linux
2+
3+
package pipeline
4+
5+
import (
6+
"os"
7+
"path/filepath"
8+
"strings"
9+
10+
"golang.org/x/sys/unix"
11+
)
12+
13+
// findDMDeviceByMajorMinor searches for the corresponding /dev/dm-* device
14+
// with the same major:minor numbers as the given device Rdev.
15+
func (s *SysfsStorageInfoProvider) findDMDeviceByMajorMinor(hostMapperPath string, rdev uint64) []string {
16+
devDirPath := filepath.Join(s.hostRootPath, "/dev")
17+
entries, err := os.ReadDir(devDirPath)
18+
if err != nil {
19+
s.log.Errorf("failed to read /dev directory: %v", err)
20+
return nil
21+
}
22+
23+
targetMajor := unix.Major(rdev)
24+
targetMinor := unix.Minor(rdev)
25+
26+
for _, entry := range entries {
27+
// We only care about device mapper devices.
28+
if !strings.HasPrefix(entry.Name(), "dm-") {
29+
continue
30+
}
31+
32+
dmDevicePath := filepath.Join(devDirPath, entry.Name())
33+
var dmStat unix.Stat_t
34+
if err := unix.Stat(dmDevicePath, &dmStat); err != nil {
35+
continue
36+
}
37+
38+
// Check if it's a block device and has matching major:minor
39+
if dmStat.Mode&unix.S_IFBLK != 0 {
40+
dmMajor := unix.Major(dmStat.Rdev)
41+
dmMinor := unix.Minor(dmStat.Rdev)
42+
if dmMajor == targetMajor && dmMinor == targetMinor {
43+
return []string{"/dev/" + entry.Name()}
44+
}
45+
}
46+
}
47+
48+
s.log.Errorf("no matching /dev/dm-* device found for %s (major=%d, minor=%d)",
49+
hostMapperPath, targetMajor, targetMinor)
50+
return nil
51+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
//go:build !linux
2+
3+
package pipeline
4+
5+
func (s *SysfsStorageInfoProvider) findDMDeviceByMajorMinor(hostMapperPath string, rdev int32) []string {
6+
return nil
7+
}

0 commit comments

Comments
 (0)